diff --git a/advance_salary/__init__.py b/advance_salary/__init__.py new file mode 100644 index 000000000..03ebafd51 --- /dev/null +++ b/advance_salary/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2015-TODAY Cybrosys Technologies(). +# Author: Sreejith P() +# you can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# It is forbidden to publish, distribute, sublicense, or sell copies +# of the Software or modified copies of the Software. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. +# If not, see . +# +############################################################################## + +import models diff --git a/advance_salary/__openerp__.py b/advance_salary/__openerp__.py new file mode 100644 index 000000000..5dfcec1d4 --- /dev/null +++ b/advance_salary/__openerp__.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2015-TODAY Cybrosys Technologies(). +# Author: Sreejith P() +# you can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# It is forbidden to publish, distribute, sublicense, or sell copies +# of the Software or modified copies of the Software. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. +# If not, see . +# +############################################################################## + +{ + 'name': "Advance Salary", + 'version': "8.0.1.0.0", + 'author': 'Cybrosys Techno Solutions', + 'company': 'Cybrosys Techno Solutions', + 'summary': 'Advance salary payment option to the Employee.', + 'website': 'https://www.cybrosys.com', + 'license': "AGPL-3", + 'category': "Human resources", + 'depends': ['hr', 'hr_payroll', 'hr_contract'], + 'data': [ + "security/ir.model.access.csv", + "views/salary_structure_view.xml", + "views/salary_advance_menu.xml", + "views/advance_rule_menu.xml", + "views/journal_entry.xml", + ], + 'installable': True, + 'active': False, + 'images': ['static/description/banner.jpg'], + 'auto_install': False, +} diff --git a/advance_salary/models/__init__.py b/advance_salary/models/__init__.py new file mode 100644 index 000000000..937966ac7 --- /dev/null +++ b/advance_salary/models/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2015-TODAY Cybrosys Technologies(). +# Author: Sreejith P() +# you can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# It is forbidden to publish, distribute, sublicense, or sell copies +# of the Software or modified copies of the Software. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. +# If not, see . +# +############################################################################## + +import salary_structure +import hr_advance_payslip +import salary_advance diff --git a/advance_salary/models/hr_advance_payslip.py b/advance_salary/models/hr_advance_payslip.py new file mode 100644 index 000000000..307c930fc --- /dev/null +++ b/advance_salary/models/hr_advance_payslip.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +from datetime import datetime +from openerp import models + + +class SalaryRuleInput(models.Model): + _inherit = 'hr.payslip' + + def get_inputs(self, cr, uid, contract_ids, date_from, date_to, context=None): + res = super(SalaryRuleInput, self).get_inputs(cr, uid, contract_ids, date_from, date_to, context=None) + contract_obj = self.pool.get('hr.contract') + emp_id = contract_obj.browse(cr, uid, contract_ids[0], context=context).employee_id.name + adv_salary = self.pool.get('salary.advance').search(cr, uid, [('employee_id', '=', emp_id)]) + for each_employee in adv_salary: + current_date = datetime.strptime(date_from, '%Y-%m-%d').date().month + date = self.pool.get('salary.advance').browse(cr, uid, each_employee, context).date + existing_date = datetime.strptime(date, '%Y-%m-%d').date().month + if current_date == existing_date: + adv_browse = self.pool.get('salary.advance').browse(cr, uid, each_employee, context) + state = adv_browse.state + amount = adv_browse.advance + for result in res: + if state == 'approved' and amount != 0 and result.get('code') == 'SAR': + result['amount'] = amount + return res diff --git a/advance_salary/models/salary_advance.py b/advance_salary/models/salary_advance.py new file mode 100644 index 000000000..22ef40888 --- /dev/null +++ b/advance_salary/models/salary_advance.py @@ -0,0 +1,234 @@ +# -*- coding: utf-8 -*- +from datetime import datetime +from openerp import models, fields, api, _ +from openerp.osv import osv +from openerp.exceptions import Warning + + +def _employee_get(obj, cr, uid, context=None): + if context is None: + context = {} + ids = obj.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)], context=context) + if ids: + return ids[0] + return False + + +def _get_currency(self, cr, uid, context=None): + user = self.pool.get('res.users').browse(cr, uid, [uid], context=context)[0] + return user.company_id.currency_id.id + + +class SalaryAdvancePayment(models.Model): + _name = "salary.advance" + name = fields.Char(string='Name', readonly=True, select=True, default=lambda self: 'Adv/') + employee_id = fields.Many2one('hr.employee', string='Employee', required=True, default='_employee_get') + date = fields.Date(string='Date', required=True, default=lambda self: fields.Date.today()) + reason = fields.Text(string='Reason') + currency_id = fields.Many2one('res.currency', string='Currency', required=True, + default='_get_currency') + company_id = fields.Many2one('res.company', string='Company', required=True, + default=lambda self: self.env.user.company_id) + advance = fields.Float(string='Advance', required=True) + payment_method = fields.Many2one('account.journal', string='Payment Method', required=True) + exceed_condition = fields.Boolean(string='Exceed than maximum') + department = fields.Many2one('hr.department', string='Department') + state = fields.Selection([('draft', 'Draft'), + ('approved', 'Approved'), + ('cancel', 'Cancel')], string='Status') + + @api.onchange('currency_id') + def onchange_currency_id(self, currency_id=False, company_id=False): + res = {'value': {'journal_id': False}} + journal_ids = self.pool.get('account.journal').search(self._cr, self._uid, [('type', '=', 'purchase'), + ('currency', '=', currency_id), + ('company_id', '=', company_id)]) + if journal_ids: + res['value']['journal_id'] = journal_ids[0] + return res + + @api.onchange('company_id') + def onchange_company_id(self): + company = self.company_id + domain = [('company_id.id', '=', company.id), ] + result = { + 'domain': { + 'payment_method': domain, + }, + + } + return result + + def onchange_employee_id(self, cr, uid, ids, employee_id, context=None): + emp_obj = self.pool.get('hr.employee') + department_id = False + if employee_id: + employee = emp_obj.browse(cr, uid, employee_id, context=context) + department_id = employee.department_id.id + return {'value': {'department': department_id}} + + @api.model + def create(self, vals): + vals['name'] = self.env['ir.sequence'].get('adv') + emp_id = vals.get('employee_id') + contract_obj = self.env['hr.contract'] + emp_obj = self.env['hr.employee'] + search_contract = contract_obj.search([('employee_id', '=', emp_id)]) + address = emp_obj.browse([emp_id]).address_home_id + if not address.id: + raise osv.except_osv('Error!', 'Define home address for employee') + salary_advance_search = self.search([('employee_id', '=', emp_id)]) + for each_advance in salary_advance_search: + current_month = datetime.strptime(vals.get('date'), '%Y-%m-%d').date().month + existing_month = datetime.strptime(each_advance.date, '%Y-%m-%d').date().month + if current_month == existing_month or current_month < existing_month: + raise osv.except_osv('Error!', 'Advance can be requested once in a month') + if not search_contract: + raise osv.except_osv('Error!', 'Define a contract for the employee') + for each_contract in search_contract: + struct_id = each_contract.struct_id + if not struct_id.max_percent or not struct_id.advance_date: + raise osv.except_osv('Error!', 'Max percentage or advance days are not provided') + adv = vals.get('advance') + amt = (each_contract.struct_id.max_percent * each_contract.wage) / 100 + if adv > each_contract.wage: + raise osv.except_osv('Error!', 'Advance amount is greater than Wage') + if adv > amt and vals.get('exceed_condition') == False: + raise osv.except_osv('Error!', 'Advance amount is greater than allotted') + vals.update({'state': 'draft'}) + res_id = super(SalaryAdvancePayment, self).create(vals) + return res_id + + @api.multi + def write(self, vals): + emp_id = self.employee_id.id + date = self.date + advance = self.advance + if 'employee_id' in vals: + emp_id = vals.get('employee_id') + if 'date' in vals: + date = vals.get('date') + if 'advance' in vals: + advance = vals.get('advance') + contract = self.env['hr.contract'] + search_contract = contract.search([('employee_id', '=', emp_id)]) + emp_obj = self.env['hr.employee'] + address = emp_obj.browse([emp_id]).address_home_id + if not address.id: + raise osv.except_osv('Error!', 'Define home address for employee') + salary_advance_search = self.search([('employee_id', '=', emp_id)]) + for each_advance in salary_advance_search: + current_month = datetime.strptime(date, '%Y-%m-%d').date().month + existing_month = datetime.strptime(each_advance.date, '%Y-%m-%d').date().month + if each_advance.id != self.id and (current_month == existing_month or current_month < existing_month): + raise osv.except_osv('Error!', 'Advance can be requested once in a month') + if not search_contract: + raise osv.except_osv('Error!', 'Define a contract for the employee') + for each_contract in search_contract: + if not each_contract.struct_id.max_percent or not each_contract.struct_id.advance_date: + raise osv.except_osv('Error!', 'Max percentage or advance days are not provided') + amt = (each_contract.struct_id.max_percent * each_contract.wage) / 100 + if advance > each_contract.wage: + raise osv.except_osv('Error!', 'Advance amount is greater than Wage') + if advance > amt and vals.get('exceed_condition') == False: + raise osv.except_osv('Error!', 'Advance amount is greater than allotted') + super(SalaryAdvancePayment, self).write(vals) + return True + + def compute_advance_totals(self, account_move_lines): + total = 0.0 + for i in account_move_lines: + total -= i['price'] + return total, account_move_lines + + def line_get_convert(self, x, part, date): + partner_id = self.env['res.partner']._find_accounting_partner(part).id + res = { + 'date_maturity': x.get('date_maturity', False), + 'partner_id': partner_id, + 'name': x.get('name'), + 'date': date, + 'debit': x['price'] > 0 and x['price'], + 'credit': x['price'] < 0 and -x['price'], + 'account_id': x['account_id'], + 'analytic_lines': x.get('analytic_lines', False), + 'amount_currency': x['price'] > 0 and abs(x.get('amount_currency', False)) or -abs( + x.get('amount_currency', False)), + 'currency_id': x.get('currency_id', False), + 'ref': x.get('ref', False), + 'product_id': x.get('product_id', False), + 'product_uom_id': x.get('uos_id', False), + 'analytic_account_id': x.get('account_analytic_id', False), + } + return res + + def account_adv_get(self, salary_adv_obj): + return self.env['account.move'].account_move_prepare(salary_adv_obj.journal.id, date=False, + ref=self.employee_id.name, company_id=False) + + @api.one + def approve(self): + self.employee_id.write({'advance_amount': self.advance}) + move_obj = self.env['account.move'] + company_id = self.env['res.users'].browse(self._uid).company_id + salary_adv_obj = self.env['advance.rules'].search([('company_id', '=', company_id.id)]) + if not salary_adv_obj: + raise Warning( + _("No Salary Advance Rule Defined For the Login User Company.")) + + else: + move_id = move_obj.create(self.account_adv_get(salary_adv_obj)) + acc_debit = salary_adv_obj.debit.id + adv_line = { + 'type': 'src', + 'name': 'Salary Advance', + 'price_unit': self.advance, + 'price': self.advance, + 'account_id': acc_debit, + 'currency_id': self.currency_id.id, + } + total, adv_line = self.compute_advance_totals([adv_line]) + credit_acc = self.payment_method.default_credit_account_id.id + adv_line.append({ + 'type': 'dest', + 'name': 'Salary Advance', + 'price': total, + 'account_id': credit_acc, + 'ref': self.ids[0] + }) + journal = move_id.journal_id + company_currency = self.env.user.company_id.currency_id.id + total, total_currency, adv_line = self.compute_expense_totals(self, company_currency, self.name, adv_line) + lines = map(lambda x: (0, 0, self.line_get_convert(x, self.employee_id.address_home_id, self.date)), + adv_line) + if journal.entry_posted: + move_obj.button_validate(move_id.id) + move_id.write({'line_id': lines}) + self.write({'state': 'approved'}) + + def compute_expense_totals(self, cr, uid, adv, company_currency, ref, account_move_lines, context=None): + cur_obj = self.pool.get('res.currency') + total = 0.0 + total_currency = 0.0 + for i in account_move_lines: + if adv.currency_id.id != company_currency: + i['currency_id'] = adv.currency_id.id + i['amount_currency'] = i['price'] + i['price'] = cur_obj.compute(cr, uid, adv.currency_id.id, + company_currency, i['price'], + context=context) + else: + i['amount_currency'] = False + i['currency_id'] = False + total -= i['price'] + total_currency -= i['amount_currency'] or i['price'] + return total, total_currency, account_move_lines + + @api.one + def cancel(self): + self.write({'state': 'cancel'}) + + +class EmployeeAdvance(models.Model): + _inherit = 'hr.employee' + advance_amount = fields.Float("Advance Amount") diff --git a/advance_salary/models/salary_structure.py b/advance_salary/models/salary_structure.py new file mode 100644 index 000000000..8378677cb --- /dev/null +++ b/advance_salary/models/salary_structure.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +from openerp import models, fields, api +from openerp.osv import osv + + +class SalaryStructure(models.Model): + _inherit = 'hr.payroll.structure' + max_percent = fields.Integer(string='Max.Salary Advance Percentage') + advance_date = fields.Integer(string='Salary Advance-After days') + + +class AdvanceRule(models.Model): + _name = "advance.rules" + name = fields.Char(string='Name', required=True) + debit = fields.Many2one('account.account', string='Debit Account', domain="[('type','=','other')]", required=True) + credit = fields.Many2one('account.account', string='Credit Account', domain="[('type','=','other')]", required=True) + journal = fields.Many2one('account.journal', string='Journal', required=True) + company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.user.company_id) + analytic_journal = fields.Many2one('account.analytic.journal', string='Analytic Journal') + + @api.model + def create(self, vals): + company_id = vals.get('company_id') + advance_rule_search = self.search([('company_id', '=', company_id)]) + if advance_rule_search: + raise osv.except_osv('Error!', 'Advance rule for this Company already exist') + res_id = super(AdvanceRule, self).create(vals) + return res_id + + @api.multi + def write(self, vals): + company_id = self.company_id + if 'company_id' in vals: + company_id = vals.get('company_id') + advance_rule_search = self.search([('company_id', '=', company_id)]) + if advance_rule_search and advance_rule_search.id != self.id: + raise osv.except_osv('Error!', 'Advance rule for this Company already exist') + super(AdvanceRule, self).write(vals) + return True diff --git a/advance_salary/security/ir.model.access.csv b/advance_salary/security/ir.model.access.csv new file mode 100644 index 000000000..4e5a1b465 --- /dev/null +++ b/advance_salary/security/ir.model.access.csv @@ -0,0 +1,7 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_salary_advance,access_advance,model_salary_advance,,1,1,1,1 +access_hr_contract,hr.contract,hr_contract.model_hr_contract,base.group_hr_user,1,0,0,0 +access_hr_salary,hr.salary,hr_payroll.model_hr_payroll_structure,base.group_hr_user,1,0,0,0 +access_hr_contract_user,hr.contract,hr_contract.model_hr_contract,base.group_user,1,0,0,0 +access_hr_salary_user,hr.salary,hr_payroll.model_hr_payroll_structure,base.group_user,1,0,0,0 +access_advance_rule,access_rule,model_advance_rules,base.group_hr_manager,1,1,1,1 diff --git a/advance_salary/static/description/adv_salary.png b/advance_salary/static/description/adv_salary.png new file mode 100644 index 000000000..ec825a356 Binary files /dev/null and b/advance_salary/static/description/adv_salary.png differ diff --git a/advance_salary/static/description/banner.jpg b/advance_salary/static/description/banner.jpg new file mode 100644 index 000000000..19a8ae23f Binary files /dev/null and b/advance_salary/static/description/banner.jpg differ diff --git a/advance_salary/static/description/cybro_logo.png b/advance_salary/static/description/cybro_logo.png new file mode 100644 index 000000000..bb309114c Binary files /dev/null and b/advance_salary/static/description/cybro_logo.png differ diff --git a/advance_salary/static/description/employee_form.png b/advance_salary/static/description/employee_form.png new file mode 100644 index 000000000..e01581950 Binary files /dev/null and b/advance_salary/static/description/employee_form.png differ diff --git a/advance_salary/static/description/icon.png b/advance_salary/static/description/icon.png new file mode 100644 index 000000000..e2539cec4 Binary files /dev/null and b/advance_salary/static/description/icon.png differ diff --git a/advance_salary/static/description/index.html b/advance_salary/static/description/index.html new file mode 100644 index 000000000..b0149d795 --- /dev/null +++ b/advance_salary/static/description/index.html @@ -0,0 +1,113 @@ +
+
+

Advance salary

+

This module will help you to provide advanced salary for the employee.

+ +

Author : Cybrosys Techno Solutions , www.cybrosys.com

+
+

Features:

+
    +
  •    Adds a salary rule
  • +
  •    Menu for advance salary configuration.
  • +
+
+
+
+ +
+
+
+

Overview

+

+ A module that provides provision to pay advance salary to the employee. +

+
+
+
+ +
+
+

Salary structure

+
+

+ ☛ Add "salary advance rule" to the salary structure.
+ ☛ Provide limit of advance salary.
+ ☛ Provide the minimum days to give advance salary
+

+
+ +
+
+
+
+ +
+
+

Advance salary configuration

+
+

+ ☛ Define a Advance salary rule. In case of multi company keep separate config for each company
+ +

+
+ +
+
+
+
+ +
+
+

Advance salary

+
+

+ ☛ Select Employee.
+ ☛ Select Payment method.
+ ☛ Give the amount on Advance field.
+

+
+ +
+
+
+
+ +
+
+

Employee Form

+
+

+ ☛ Select Home address.
+

+
+ +
+
+
+
+ +
+

Need Any Help?

+ +
diff --git a/advance_salary/static/description/salary_config.png b/advance_salary/static/description/salary_config.png new file mode 100644 index 000000000..828bf578c Binary files /dev/null and b/advance_salary/static/description/salary_config.png differ diff --git a/advance_salary/static/description/salary_structure.png b/advance_salary/static/description/salary_structure.png new file mode 100644 index 000000000..e82201a51 Binary files /dev/null and b/advance_salary/static/description/salary_structure.png differ diff --git a/advance_salary/views/advance_rule_menu.xml b/advance_salary/views/advance_rule_menu.xml new file mode 100644 index 000000000..7163407ef --- /dev/null +++ b/advance_salary/views/advance_rule_menu.xml @@ -0,0 +1,32 @@ + + + + + Advance rule + advance.rules + form + + + advance.rule.form + advance.rules + +
+ + + + + + + + + + + + + +
+
+
+ +
+
\ No newline at end of file diff --git a/advance_salary/views/journal_entry.xml b/advance_salary/views/journal_entry.xml new file mode 100644 index 000000000..acaf6a399 --- /dev/null +++ b/advance_salary/views/journal_entry.xml @@ -0,0 +1,18 @@ + + + + + account_move_voucher_inherit_view + account.move + + + + Invoice No + + + Customer/Supplier + + + + + diff --git a/advance_salary/views/salary_advance_menu.xml b/advance_salary/views/salary_advance_menu.xml new file mode 100644 index 000000000..2fa24b716 --- /dev/null +++ b/advance_salary/views/salary_advance_menu.xml @@ -0,0 +1,69 @@ + + + + + Advance + adv + + + Advance_Num + adv + Adv + 3 + + + Salary Advance + salary.advance + form + + + salary.advance.form + salary.advance + +
+
+
+ +
+

+ +

+
+ + + + + + + + + + + + + + + +
+
+
+
+ + salary.advance.tree + salary.advance + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/advance_salary/views/salary_structure_view.xml b/advance_salary/views/salary_structure_view.xml new file mode 100644 index 000000000..390147728 --- /dev/null +++ b/advance_salary/views/salary_structure_view.xml @@ -0,0 +1,32 @@ + + + + + + SAR + Advance Salary + + code + result = -(inputs.SAR.amount) + + + + + SAR + Salary Advance + + + + + salary.structure.form + hr.payroll.structure + + + + + + + + + + \ No newline at end of file