diff --git a/l10n_us_hr_payroll/models/browsable_object.py b/l10n_us_hr_payroll/models/browsable_object.py index 17f29c3f..bed067fd 100644 --- a/l10n_us_hr_payroll/models/browsable_object.py +++ b/l10n_us_hr_payroll/models/browsable_object.py @@ -76,12 +76,35 @@ class Payslips(BrowsableObject): FROM hr_payslip as hp, hr_payslip_line as pl WHERE hp.employee_id = %s AND hp.state = 'done' AND hp.{sum_field} >= %s AND hp.date_to <= %s AND hp.id = pl.slip_id AND pl.code = %s""".format(sum_field=sum_field) + # Original (non-recursive) + # self.__browsable_query_category = """ + # SELECT sum(case when hp.credit_note is not True then (pl.total) else (-pl.total) end) + # FROM hr_payslip as hp, hr_payslip_line as pl, hr_salary_rule_category as rc + # WHERE hp.employee_id = %s AND hp.state = 'done' + # AND hp.{sum_field} >= %s AND hp.date_to <= %s AND hp.id = pl.slip_id + # AND rc.id = pl.category_id AND rc.code = %s""".format(sum_field=sum_field) + + # Hibou Recursive version self.__browsable_query_category = """ + WITH RECURSIVE + category_by_code as ( + SELECT id + FROM hr_salary_rule_category + WHERE code = %s + ), + category_ids as ( + SELECT COALESCE((SELECT id FROM category_by_code), -1) AS id + UNION ALL + SELECT rc.id + FROM hr_salary_rule_category AS rc + JOIN category_ids AS rcs ON rcs.id = rc.parent_id + ) + SELECT sum(case when hp.credit_note is not True then (pl.total) else (-pl.total) end) - FROM hr_payslip as hp, hr_payslip_line as pl, hr_salary_rule_category as rc + FROM hr_payslip as hp, hr_payslip_line as pl WHERE hp.employee_id = %s AND hp.state = 'done' AND hp.{sum_field} >= %s AND hp.date_to <= %s AND hp.id = pl.slip_id - AND rc.id = pl.category_id AND rc.code = %s""".format(sum_field=sum_field) + AND pl.category_id in (SELECT id from category_ids)""".format(sum_field=sum_field) def sum(self, code, from_date, to_date=None): if to_date is None: @@ -101,7 +124,10 @@ class Payslips(BrowsableObject): self.env['hr.payslip.line'].flush(['total', 'slip_id', 'category_id']) self.env['hr.salary.rule.category'].flush(['code']) - self.env.cr.execute(self.__browsable_query_category, (self.employee_id, from_date, to_date, code)) + # standard version + # self.env.cr.execute(self.__browsable_query_category, (self.employee_id, from_date, to_date, code)) + # recursive category version + self.env.cr.execute(self.__browsable_query_category, (code, self.employee_id, from_date, to_date)) res = self.env.cr.fetchone() return res and res[0] or 0.0 diff --git a/l10n_us_hr_payroll/models/federal/fed_941.py b/l10n_us_hr_payroll/models/federal/fed_941.py index c154bca7..009a93c6 100644 --- a/l10n_us_hr_payroll/models/federal/fed_941.py +++ b/l10n_us_hr_payroll/models/federal/fed_941.py @@ -12,17 +12,17 @@ def fica_wage(payslip, categories): """ wage = categories.GROSS - wage -= categories.ALW_FICA_EXEMPT + \ - categories.ALW_FIT_FICA_EXEMPT + \ - categories.ALW_FIT_FICA_FUTA_EXEMPT + \ - categories.ALW_FICA_FUTA_EXEMPT + less_exempt = categories.ALW_FICA_EXEMPT + \ + categories.ALW_FIT_FICA_EXEMPT + \ + categories.ALW_FIT_FICA_FUTA_EXEMPT + \ + categories.ALW_FICA_FUTA_EXEMPT - wage += categories.DED_FICA_EXEMPT + \ - categories.DED_FIT_FICA_EXEMPT + \ - categories.DED_FIT_FICA_FUTA_EXEMPT + \ - categories.DED_FICA_FUTA_EXEMPT - - return wage + plus_exempt = categories.DED_FICA_EXEMPT + \ + categories.DED_FIT_FICA_EXEMPT + \ + categories.DED_FIT_FICA_FUTA_EXEMPT + \ + categories.DED_FICA_FUTA_EXEMPT + # _logger.info('fica wage GROSS: %0.2f less exempt ALW: %0.2f plus exempt DED: %0.2f' % (wage, less_exempt, plus_exempt)) + return wage - less_exempt + plus_exempt def fica_wage_ytd(payslip, categories): @@ -34,18 +34,19 @@ def fica_wage_ytd(payslip, categories): year = payslip.dict.get_year() ytd_wage = payslip.sum_category('GROSS', str(year) + '-01-01', str(year+1) + '-01-01') - ytd_wage -= payslip.sum_category('ALW_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \ - payslip.sum_category('ALW_FIT_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \ - payslip.sum_category('ALW_FIT_FICA_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \ - payslip.sum_category('ALW_FICA_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + less_exempt = payslip.sum_category('ALW_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \ + payslip.sum_category('ALW_FIT_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \ + payslip.sum_category('ALW_FIT_FICA_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \ + payslip.sum_category('ALW_FICA_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') - ytd_wage += payslip.sum_category('DED_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \ - payslip.sum_category('DED_FIT_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \ - payslip.sum_category('DED_FIT_FICA_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \ - payslip.sum_category('DED_FICA_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + plus_exempt = payslip.sum_category('DED_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \ + payslip.sum_category('DED_FIT_FICA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \ + payslip.sum_category('DED_FIT_FICA_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') + \ + payslip.sum_category('DED_FICA_FUTA_EXEMPT', str(year) + '-01-01', str(year+1) + '-01-01') - ytd_wage += payslip.contract_id.external_wages - return ytd_wage + external_wages = payslip.dict.contract_id.external_wages + # _logger.info('fica ytd wage GROSS: %0.2f less exempt ALW: %0.2f plus exempt DED: %0.2f plus external: %0.2f' % (ytd_wage, less_exempt, plus_exempt, external_wages)) + return ytd_wage - less_exempt + plus_exempt + external_wages def ee_us_941_fica_ss(payslip, categories, worked_days, inputs): diff --git a/l10n_us_hr_payroll/tests/test_special.py b/l10n_us_hr_payroll/tests/test_special.py index 430af0ea..688761ec 100644 --- a/l10n_us_hr_payroll/tests/test_special.py +++ b/l10n_us_hr_payroll/tests/test_special.py @@ -63,3 +63,57 @@ elif ytd_rule == 0.0: payslip.compute_sheet() cats = self._getCategories(payslip) self.assertEqual(cats['test_sum_behavior'], 0.0) + + def test_recursive_salary_rule_category(self): + # self.debug = True + us_structure = self.env.ref('l10n_us_hr_payroll.hr_payroll_structure') + # In this scenario, you are in rule code that will check for the category + # and a subcategory will also + alw_category = self.env.ref('hr_payroll.ALW') + ded_category = self.env.ref('hr_payroll.DED') + test_category = self.env['hr.salary.rule.category'].create({ + 'name': 'Special ALW', + 'code': 'ALW_SPECIAL_RECURSIVE', + 'parent_id': alw_category.id, + }) + test_special_alw = self.env['hr.salary.rule'].create({ + 'name': 'Flat amount 200', + 'code': 'ALW_SPECIAL_RECURSIVE', + 'category_id': test_category.id, + 'condition_select': 'none', + 'amount_select': 'fix', + 'amount_fix': 200.0, + 'struct_id': us_structure.id, + }) + test_recursion = self.env['hr.salary.rule'].create({ + 'name': 'Actual Test Behavior', + 'code': 'RECURSION_TEST', + 'category_id': ded_category.id, + 'condition_select': 'none', + 'amount_select': 'code', + 'amount_python_compute': """ +# this rule will always be the total of the ALW category and YTD ALW category +result = categories.ALW +year = payslip.dict.get_year() +result += payslip.sum_category('ALW', str(year) + '-01-01', str(year+1) + '-01-01') + """, + 'sequence': 101, + 'struct_id': us_structure.id, + }) + + salary = 80000.0 + employee = self._createEmployee() + contract = self._createContract(employee, wage=salary, schedule_pay='bi-weekly') + payslip = self._createPayslip(employee, '2020-01-01', '2020-01-14') + payslip.compute_sheet() + cats = self._getCategories(payslip) + rules = self._getRules(payslip) + self.assertEqual(rules['RECURSION_TEST'], 200.0) + process_payslip(payslip) + + payslip = self._createPayslip(employee, '2020-01-15', '2020-01-27') + payslip.compute_sheet() + cats = self._getCategories(payslip) + rules = self._getRules(payslip) + # two hundred is in the YTD ALW + self.assertEqual(rules['RECURSION_TEST'], 200.0 + 200.0)