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 @@
+
+
+
+
+
+
+
+
+
+ Individual Account Report for year
+
+
+
+
+
+ | Company Information |
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+
+
+ | Name |
+ |
+ Designation |
+ |
+
+
+ | Address |
+
+
+ |
+
+
+ | Email |
+ |
+ Identification No |
+ |
+
+
+ | Bank Account |
+ |
+
+
+ | Registration Number |
+ |
+
+
+
+
+ | Name |
+ JAN |
+ FEB |
+ MARS |
+ APR |
+ MAY |
+ JUNE |
+ JUL |
+ AUG |
+ SEP |
+ OCT |
+ NOV |
+ DEC |
+
+
+ | Worked Days |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+ |
+
+
+ / |
+
+
+
+
+ | hours |
+
+
+ |
+
+
+ / |
+
+
+
+
+
+
+ | Salary Computation |
+ |
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+ |
+
+
+ / |
+
+
+
+
+
+
+
+
+ | Name |
+ Quarter 1 |
+ Quarter 2 |
+ Quarter 3 |
+ Quarter 4 |
+ Total Year |
+
+
+ | Worked Days |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+ |
+
+
+ / |
+
+
+
+ |
+
+
+ / |
+
+
+
+ | hours |
+
+
+ |
+
+
+ / |
+
+
+
+ |
+
+
+ / |
+
+
+
+
+
+
+ | Salary Computation |
+ |
+
+
+
+
+
+
+
+
+
+
+
+ |
+
+
+ |
+
+
+ / |
+
+
+
+ |
+
+
+ / |
+
+
+
+
+
+
+
+
+
+
+
+
+
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