From fcc49c788f6e24bb35caee890194649adf61fbac Mon Sep 17 00:00:00 2001 From: sed-odoo Date: Thu, 3 Jan 2019 09:06:03 +0000 Subject: [PATCH] [IMP] l10n_be_hr_payroll: Add 'Individual account' report Purpose ======= Add the possibility to generate the 'Compte Individuel' (or 'Individual Account') based on the belgian payslip for a specific year. This report aggregates the payslip lines by month, by quarter and for the entire year. closes odoo/odoo#30487 --- addons/hr_payroll/models/hr_salary_rule.py | 2 +- addons/l10n_be_hr_payroll/__init__.py | 2 + addons/l10n_be_hr_payroll/__manifest__.py | 4 + .../data/report_paperformat.xml | 18 ++ addons/l10n_be_hr_payroll/report/__init__.py | 4 + .../report/hr_individual_account_reports.py | 88 +++++++ .../report/hr_individual_account_reports.xml | 18 ++ .../hr_individual_account_templates.xml | 240 ++++++++++++++++++ addons/l10n_be_hr_payroll/wizard/__init__.py | 4 + .../l10n_be_individual_account_wizard.py | 28 ++ ...10n_be_individual_account_wizard_views.xml | 31 +++ .../data/l10n_be_hr_payroll_account_demo.xml | 2 +- 12 files changed, 439 insertions(+), 2 deletions(-) create mode 100644 addons/l10n_be_hr_payroll/data/report_paperformat.xml create mode 100644 addons/l10n_be_hr_payroll/report/__init__.py create mode 100644 addons/l10n_be_hr_payroll/report/hr_individual_account_reports.py create mode 100644 addons/l10n_be_hr_payroll/report/hr_individual_account_reports.xml create mode 100644 addons/l10n_be_hr_payroll/report/hr_individual_account_templates.xml create mode 100644 addons/l10n_be_hr_payroll/wizard/__init__.py create mode 100644 addons/l10n_be_hr_payroll/wizard/l10n_be_individual_account_wizard.py create mode 100644 addons/l10n_be_hr_payroll/wizard/l10n_be_individual_account_wizard_views.xml diff --git a/addons/hr_payroll/models/hr_salary_rule.py b/addons/hr_payroll/models/hr_salary_rule.py index 1b20df39fa5..81062738d5c 100644 --- a/addons/hr_payroll/models/hr_salary_rule.py +++ b/addons/hr_payroll/models/hr_salary_rule.py @@ -57,7 +57,7 @@ class HrPayrollStructure(models.Model): all_rules = [] for struct in self: all_rules += struct.rule_ids._recursive_search_of_rules() - return all_rules + return list(set(all_rules)) @api.multi def _get_parent_structure(self): diff --git a/addons/l10n_be_hr_payroll/__init__.py b/addons/l10n_be_hr_payroll/__init__.py index 08c9d97f4fd..34a7c37d5db 100644 --- a/addons/l10n_be_hr_payroll/__init__.py +++ b/addons/l10n_be_hr_payroll/__init__.py @@ -2,3 +2,5 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import models +from . import report +from . import wizard diff --git a/addons/l10n_be_hr_payroll/__manifest__.py b/addons/l10n_be_hr_payroll/__manifest__.py index 544d781c0b1..56b00fd8431 100644 --- a/addons/l10n_be_hr_payroll/__manifest__.py +++ b/addons/l10n_be_hr_payroll/__manifest__.py @@ -25,7 +25,11 @@ Belgian Payroll Rules. 'views/reports.xml', 'views/l10n_be_hr_payroll_view.xml', 'data/l10n_be_hr_payroll_data.xml', + 'data/report_paperformat.xml', 'views/res_config_settings_views.xml', + 'wizard/l10n_be_individual_account_wizard_views.xml', + 'report/hr_individual_account_reports.xml', + 'report/hr_individual_account_templates.xml', ], 'demo':[ 'data/l10n_be_hr_payroll_demo.xml' diff --git a/addons/l10n_be_hr_payroll/data/report_paperformat.xml b/addons/l10n_be_hr_payroll/data/report_paperformat.xml new file mode 100644 index 00000000000..10fbda50914 --- /dev/null +++ b/addons/l10n_be_hr_payroll/data/report_paperformat.xml @@ -0,0 +1,18 @@ + + + + Individual Account + + custom + 297 + 210 + Portrait + 30 + 23 + 5 + 5 + + 20 + 90 + + diff --git a/addons/l10n_be_hr_payroll/report/__init__.py b/addons/l10n_be_hr_payroll/report/__init__.py new file mode 100644 index 00000000000..a1b89235ace --- /dev/null +++ b/addons/l10n_be_hr_payroll/report/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import hr_individual_account_reports diff --git a/addons/l10n_be_hr_payroll/report/hr_individual_account_reports.py b/addons/l10n_be_hr_payroll/report/hr_individual_account_reports.py new file mode 100644 index 00000000000..9eb6dee2d58 --- /dev/null +++ b/addons/l10n_be_hr_payroll/report/hr_individual_account_reports.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from collections import OrderedDict + +from odoo import api, models, _ +from odoo.fields import Datetime +from odoo.exceptions import UserError + + +class IndividualAccountReport(models.AbstractModel): + _name = 'report.l10n_be_hr_payroll.report_individual_account' + _description = 'Individual Account Report' + + def _get_report_data(self, data): + year = data['year'] + employees = self.env['hr.employee'].browse(data['employee_ids']) + + payslips = self.env['hr.payslip'].search([ + ('employee_id', 'in', employees.ids), + ('state', '=', 'done'), + ('date_from', '>=', Datetime.now().replace(month=1, day=1, year=year)), + ('date_from', '<=', Datetime.now().replace(month=12, day=31, year=year)), + '|', + ('struct_id.country_id', '=', False), + ('struct_id.country_id', '=', self.env.ref('base.be').id), + ]) + lines = payslips.mapped('line_ids') + payslip_rules = [(rule.code, rule.sequence) for rule in lines.mapped('salary_rule_id')] + payslip_rules = sorted(payslip_rules, key=lambda x: x[1]) + worked_days = payslips.mapped('worked_days_line_ids') + + result = { + employee: { + 'rules': OrderedDict( + (rule[0], { + 'year': {'name': False, 'total': 0}, + 'month': {m: {'name': False, 'total': 0} for m in range(12)}, + 'quarter': {q: {'name': False, 'total': 0} for q in range(4)} + }) for rule in payslip_rules), + 'worked_days': { + code: { + 'year': {'name': False, 'number_of_days': 0, 'number_of_hours': 0}, + 'month': {m: {'name': False, 'number_of_days': 0, 'number_of_hours': 0} for m in range(12)}, + 'quarter': {q: {'name': False, 'number_of_days': 0, 'number_of_hours': 0} for q in range(4)} + } for code in worked_days.mapped('code') + } + } for employee in employees + } + + for line in lines: + rule = result[line.employee_id]['rules'][line.salary_rule_id.code] + month = line.slip_id.date_from.month - 1 + rule['month'][month]['name'] = line.name + rule['month'][month]['total'] += line.total + rule['quarter'][(month) // 3]['name'] = line.name + rule['quarter'][(month) // 3]['total'] += line.total + rule['year']['name'] = line.name + rule['year']['total'] += line.total + + rule['month'][month]['total'] = round(rule['month'][month]['total'], 2) + rule['quarter'][(month) // 3]['total'] = round(rule['quarter'][(month) // 3]['total'], 2) + rule['year']['total'] = round(rule['year']['total'], 2) + + for worked_day in worked_days: + work = result[worked_day.payslip_id.employee_id]['worked_days'][worked_day.code] + month = worked_day.payslip_id.date_from.month - 1 + + work['month'][month]['name'] = worked_day.name + work['month'][month]['number_of_days'] += worked_day.number_of_days + work['month'][month]['number_of_hours'] += worked_day.number_of_hours + work['quarter'][(month) // 3]['name'] = worked_day.name + work['quarter'][(month) // 3]['number_of_days'] += worked_day.number_of_days + work['quarter'][(month) // 3]['number_of_hours'] += worked_day.number_of_hours + work['year']['name'] = worked_day.name + work['year']['number_of_days'] += worked_day.number_of_days + work['year']['number_of_hours'] += worked_day.number_of_hours + + return { + 'year': year, + 'employee_data': result + } + + @api.model + def _get_report_values(self, docids, data=None): + if self.env.user.company_id.country_id != self.env.ref('base.be'): + raise UserError(_("You must be logged into a Belgian company to print the individual account.")) + return {'report_data': self._get_report_data(data)} diff --git a/addons/l10n_be_hr_payroll/report/hr_individual_account_reports.xml b/addons/l10n_be_hr_payroll/report/hr_individual_account_reports.xml new file mode 100644 index 00000000000..0ab5aebdf00 --- /dev/null +++ b/addons/l10n_be_hr_payroll/report/hr_individual_account_reports.xml @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/addons/l10n_be_hr_payroll/report/hr_individual_account_templates.xml b/addons/l10n_be_hr_payroll/report/hr_individual_account_templates.xml new file mode 100644 index 00000000000..8ca44b8cbe2 --- /dev/null +++ b/addons/l10n_be_hr_payroll/report/hr_individual_account_templates.xml @@ -0,0 +1,240 @@ + + + + + + diff --git a/addons/l10n_be_hr_payroll/wizard/__init__.py b/addons/l10n_be_hr_payroll/wizard/__init__.py new file mode 100644 index 00000000000..075f972922b --- /dev/null +++ b/addons/l10n_be_hr_payroll/wizard/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import l10n_be_individual_account_wizard diff --git a/addons/l10n_be_hr_payroll/wizard/l10n_be_individual_account_wizard.py b/addons/l10n_be_hr_payroll/wizard/l10n_be_individual_account_wizard.py new file mode 100644 index 00000000000..106f4024260 --- /dev/null +++ b/addons/l10n_be_hr_payroll/wizard/l10n_be_individual_account_wizard.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import datetime + +from odoo import fields, models + + +class L10nBeIndividualAccountWizard(models.TransientModel): + _name = 'l10n_be.individual.account.wizard' + _description = 'HR Individual Account Report By Employee' + + def _get_selection(self): + current_year = datetime.datetime.now().year + return [(str(i), i) for i in range(1990, current_year + 1)] + + year = fields.Selection( + selection='_get_selection', string='Year', required=True, + default=lambda x: str(datetime.datetime.now().year - 1)) + + def print_report(self): + self.ensure_one() + active_ids = self.env.context.get('active_ids', []) + data = { + 'employee_ids': active_ids, + 'year': int(self.year) + } + return self.env.ref('l10n_be_hr_payroll.action_report_individual_account').report_action(active_ids, data=data) diff --git a/addons/l10n_be_hr_payroll/wizard/l10n_be_individual_account_wizard_views.xml b/addons/l10n_be_hr_payroll/wizard/l10n_be_individual_account_wizard_views.xml new file mode 100644 index 00000000000..556041d0ded --- /dev/null +++ b/addons/l10n_be_hr_payroll/wizard/l10n_be_individual_account_wizard_views.xml @@ -0,0 +1,31 @@ + + + + l10n_be.individual.account.wizard.form + l10n_be.individual.account.wizard + +
+ + + + + +
+
+
+
+
+ + + Individual Accounts + ir.actions.act_window + l10n_be.individual.account.wizard + form + form + new + + report + +
diff --git a/addons/l10n_be_hr_payroll_account/data/l10n_be_hr_payroll_account_demo.xml b/addons/l10n_be_hr_payroll_account/data/l10n_be_hr_payroll_account_demo.xml index 2a8d28e2292..da32883888c 100644 --- a/addons/l10n_be_hr_payroll_account/data/l10n_be_hr_payroll_account_demo.xml +++ b/addons/l10n_be_hr_payroll_account/data/l10n_be_hr_payroll_account_demo.xml @@ -39,7 +39,7 @@ - Sick keave + Sick leave LEAVE200 2 16