From 55814321c288547782f8217b0207fdaddfa579a4 Mon Sep 17 00:00:00 2001 From: Jared Kipe Date: Tue, 5 Nov 2019 10:52:59 -0800 Subject: [PATCH] MIG `hr_payroll_payment` for Odoo 13 and improve Pay your Payroll ================ Hibou's Payroll Payments modifies, and abstracts, the way that the accounting for payslips is generated. In stock Odoo 13, journal entries are grouped by account and name, but has no linking to partners. On the Payroll Journal, you can now select optional journal entry creation with the options: - Original: Stock Implementation - Grouped: Lines are grouped by account and partner. The slip line names will be comma separated in the line name. - Payslip: Lines are grouped by account and partner, as above, but a single journal entry will be created per payslip. Adds configuration on how you would pay your employees on the Payroll journal. e.g. You write a "check" from "Bank" Adds button on payslip and payslip batch to generate payment for the employee's payable portion. When paying on a batch, a "Batch Payment" will be generated and linked to the whole payslip run. Adds Accounting Date field on Batch to use when creating slips with the batch's date. Tested ------ Passes original Payroll Accounting tests and additional ones for gouping behavior. --- hr_payroll_payment/README.md | 25 + hr_payroll_payment/__init__.py | 5 +- hr_payroll_payment/__manifest__.py | 40 +- .../hr_payroll_register_payment.py | 149 ----- .../hr_payroll_register_payment.xml | 87 --- hr_payroll_payment/models/__init__.py | 5 + hr_payroll_payment/models/account.py | 19 + hr_payroll_payment/models/hr_payslip.py | 556 ++++++++++++++++++ hr_payroll_payment/models/hr_payslip_patch.py | 9 + .../static/description/icon.png | Bin 0 -> 7980 bytes hr_payroll_payment/tests/__init__.py | 3 + .../tests/test_hr_payroll_account.py | 44 ++ hr_payroll_payment/views/account_views.xml | 18 + hr_payroll_payment/views/hr_payslip_views.xml | 63 ++ hr_payroll_payment/wizard/__init__.py | 3 + .../hr_payroll_payslips_by_employees.py | 13 + 16 files changed, 797 insertions(+), 242 deletions(-) create mode 100644 hr_payroll_payment/README.md delete mode 100755 hr_payroll_payment/hr_payroll_register_payment.py delete mode 100755 hr_payroll_payment/hr_payroll_register_payment.xml create mode 100644 hr_payroll_payment/models/__init__.py create mode 100644 hr_payroll_payment/models/account.py create mode 100644 hr_payroll_payment/models/hr_payslip.py create mode 100644 hr_payroll_payment/models/hr_payslip_patch.py create mode 100644 hr_payroll_payment/static/description/icon.png create mode 100644 hr_payroll_payment/tests/__init__.py create mode 100644 hr_payroll_payment/tests/test_hr_payroll_account.py create mode 100644 hr_payroll_payment/views/account_views.xml create mode 100644 hr_payroll_payment/views/hr_payslip_views.xml create mode 100644 hr_payroll_payment/wizard/__init__.py create mode 100644 hr_payroll_payment/wizard/hr_payroll_payslips_by_employees.py diff --git a/hr_payroll_payment/README.md b/hr_payroll_payment/README.md new file mode 100644 index 00000000..bf2c362e --- /dev/null +++ b/hr_payroll_payment/README.md @@ -0,0 +1,25 @@ +Pay your Payroll +================ + +Hibou's Payroll Payments modifies, and abstracts, the way that the accounting for payslips is generated. + +In stock Odoo 13, journal entries are grouped by account and name, but has no linking to partners. + +On the Payroll Journal, you can now select optional journal entry creation with the options: + +- Original: Stock Implementation +- Grouped: Lines are grouped by account and partner. The slip line names will be comma separated in the line name. +- Payslip: Lines are grouped by account and partner, as above, but a single journal entry will be created per payslip. + +Adds configuration on how you would pay your employees on the Payroll journal. e.g. You write a "check" from "Bank" + +Adds button on payslip and payslip batch to generate payment for the employee's payable portion. + +When paying on a batch, a "Batch Payment" will be generated and linked to the whole payslip run. + +Adds Accounting Date field on Batch to use when creating slips with the batch's date. + +Tested +------ + +Passes original Payroll Accounting tests and additional ones for gouping behavior. \ No newline at end of file diff --git a/hr_payroll_payment/__init__.py b/hr_payroll_payment/__init__.py index 6c77970b..c7120225 100755 --- a/hr_payroll_payment/__init__.py +++ b/hr_payroll_payment/__init__.py @@ -1,3 +1,4 @@ -# -*- coding: utf-8 -*- +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. -from . import hr_payroll_register_payment +from . import models +from . import wizard diff --git a/hr_payroll_payment/__manifest__.py b/hr_payroll_payment/__manifest__.py index d4cecf39..15411b4a 100755 --- a/hr_payroll_payment/__manifest__.py +++ b/hr_payroll_payment/__manifest__.py @@ -1,18 +1,50 @@ +# Part of Hibou Suite Professional. See LICENSE_PROFESSIONAL file for full copyright and licensing details. + { 'name': 'Payroll Payments', 'author': 'Hibou Corp. ', - 'version': '12.0.1.0.0', + 'version': '13.0.1.0.0', 'category': 'Human Resources', 'sequence': 95, 'summary': 'Register payments for Payroll Payslips', 'description': """ -Adds the ability to register a payment on a payslip. +Pay your Payroll +================ + +Hibou's Payroll Payments modifies, and abstracts, the way that the accounting for payslips is generated. + +In stock Odoo 13, journal entries are grouped by account and name, but has no linking to partners. + +On the Payroll Journal, you can now select optional journal entry creation with the options: + +- Original: Stock Implementation +- Grouped: Lines are grouped by account and partner. The slip line names will be comma separated in the line name. +- Payslip: Lines are grouped by account and partner, as above, but a single journal entry will be created per payslip. + +Adds configuration on how you would pay your employees on the Payroll journal. e.g. You write a "check" from "Bank" + +Adds button on payslip and payslip batch to generate payment for the employee's payable portion. + +When paying on a batch, a "Batch Payment" will be generated and linked to the whole payslip run. + +Adds Accounting Date field on Batch to use when creating slips with the batch's date. + +Tested +------ + +Passes original Payroll Accounting tests and additional ones for gouping behavior. """, 'website': 'https://hibou.io/', - 'depends': ['hr_payroll_account', 'payment'], + 'depends': [ + 'hr_payroll_account', + 'account_batch_payment', + ], 'data': [ - 'hr_payroll_register_payment.xml', + #'wizard/hr_payroll_register_payment_views.xml', + 'views/account_views.xml', + 'views/hr_payslip_views.xml', ], 'installable': True, 'application': False, + 'license': 'OPL-1', } diff --git a/hr_payroll_payment/hr_payroll_register_payment.py b/hr_payroll_payment/hr_payroll_register_payment.py deleted file mode 100755 index 63bb640a..00000000 --- a/hr_payroll_payment/hr_payroll_register_payment.py +++ /dev/null @@ -1,149 +0,0 @@ -from odoo import api, fields, models, _ -from odoo.exceptions import ValidationError - - -class HrPayslip(models.Model): - _inherit = 'hr.payslip' - - @api.multi - @api.depends('move_id', 'move_id.line_ids.full_reconcile_id') - def _is_paid(self): - for payslip in self: - payslip.is_paid = ( - payslip.move_id and len(payslip.move_id.line_ids.filtered(lambda l: ( - l.partner_id.id == payslip.employee_id.address_home_id.id and - l.account_id.internal_type == 'payable' and - not l.reconciled - ))) == 0 - ) - - is_paid = fields.Boolean(string="Has been Paid", compute='_is_paid', store=True) - - -class HrPayrollRegisterPaymentWizard(models.TransientModel): - - _name = "hr.payroll.register.payment.wizard" - _description = "Hr Payroll Register Payment wizard" - - @api.model - def _default_partner_id(self): - context = dict(self._context or {}) - active_ids = context.get('active_ids', []) - payslip = self.env['hr.payslip'].browse(active_ids) - return payslip.employee_id.address_home_id.id - - @api.model - def _default_amount(self): - context = dict(self._context or {}) - active_ids = context.get('active_ids', []) - payslip = self.env['hr.payslip'].browse(active_ids) - amount = -sum(payslip.move_id.line_ids.filtered(lambda l: ( - l.account_id.internal_type == 'payable' - and l.partner_id.id == payslip.employee_id.address_home_id.id - and not l.reconciled) - ).mapped('balance')) - return amount - - @api.model - def _default_communication(self): - context = dict(self._context or {}) - active_ids = context.get('active_ids', []) - payslip = self.env['hr.payslip'].browse(active_ids) - return payslip.number - - partner_id = fields.Many2one('res.partner', string='Partner', required=True, default=_default_partner_id) - journal_id = fields.Many2one('account.journal', string='Payment Method', required=True, domain=[('type', 'in', ('bank', 'cash'))]) - company_id = fields.Many2one('res.company', related='journal_id.company_id', string='Company', readonly=True, required=True) - payment_method_id = fields.Many2one('account.payment.method', string='Payment Type', required=True) - payment_method_code = fields.Char(related='payment_method_id.code', - help="Technical field used to adapt the interface to the payment type selected.", readonly=True) - payment_transaction_id = fields.Many2one('payment.transaction', string="Payment Transaction") - payment_token_id = fields.Many2one('payment.token', string="Saved payment token", - domain=[('acquirer_id.capture_manually', '=', False)], - help="Note that tokens from acquirers set to only authorize transactions (instead of capturing the amount) are not available.") - amount = fields.Monetary(string='Payment Amount', required=True, default=_default_amount) - currency_id = fields.Many2one('res.currency', string='Currency', required=True, default=lambda self: self.env.user.company_id.currency_id) - payment_date = fields.Date(string='Payment Date', default=fields.Date.context_today, required=True) - communication = fields.Char(string='Memo', default=_default_communication) - hide_payment_method = fields.Boolean(compute='_compute_hide_payment_method', - help="Technical field used to hide the payment method if the selected journal has only one available which is 'manual'") - - @api.onchange('partner_id') - def _onchange_partner_id(self): - res = {} - if self.partner_id: - partners = self.partner_id | self.partner_id.commercial_partner_id | self.partner_id.commercial_partner_id.child_ids - res['domain'] = { - 'payment_token_id': [('partner_id', 'in', partners.ids), ('acquirer_id.capture_manually', '=', False)]} - - return res - - @api.onchange('payment_method_id', 'journal_id') - def _onchange_payment_method(self): - if self.payment_method_code == 'electronic': - self.payment_token_id = self.env['payment.token'].search( - [('partner_id', '=', self.partner_id.id), ('acquirer_id.capture_manually', '=', False)], limit=1) - else: - self.payment_token_id = False - - @api.one - @api.constrains('amount') - def _check_amount(self): - if not self.amount > 0.0: - raise ValidationError('The payment amount must be strictly positive.') - - @api.one - @api.depends('journal_id') - def _compute_hide_payment_method(self): - if not self.journal_id: - self.hide_payment_method = True - return - journal_payment_methods = self.journal_id.outbound_payment_method_ids - self.hide_payment_method = len(journal_payment_methods) == 1 and journal_payment_methods[0].code == 'manual' - - @api.onchange('journal_id') - def _onchange_journal(self): - if self.journal_id: - # Set default payment method (we consider the first to be the default one) - payment_methods = self.journal_id.outbound_payment_method_ids - self.payment_method_id = payment_methods and payment_methods[0] or False - # Set payment method domain (restrict to methods enabled for the journal and to selected payment type) - return {'domain': {'payment_method_id': [('payment_type', '=', 'outbound'), ('id', 'in', payment_methods.ids)]}} - return {} - - @api.multi - def payroll_post_payment(self): - self.ensure_one() - context = dict(self._context or {}) - active_ids = context.get('active_ids', []) - payslip = self.env['hr.payslip'].browse(active_ids) - - # Create payment and post it - payment = self.env['account.payment'].create({ - 'partner_type': 'supplier', - 'payment_type': 'outbound', - 'partner_id': self.partner_id.id, - 'journal_id': self.journal_id.id, - 'company_id': self.company_id.id, - 'payment_method_id': self.payment_method_id.id, - 'amount': self.amount, - 'currency_id': self.currency_id.id, - 'payment_date': self.payment_date, - 'communication': self.communication, - 'payment_transaction_id': self.payment_transaction_id.id if self.payment_transaction_id else False, - 'payment_token_id': self.payment_token_id.id if self.payment_token_id else False, - }) - payment.post() - - # Reconcile the payment and the payroll, i.e. lookup on the payable account move lines - account_move_lines_to_reconcile = self.env['account.move.line'] - for line in payment.move_line_ids: - if line.account_id.internal_type == 'payable': - account_move_lines_to_reconcile |= line - for line in payslip.move_id.line_ids: - if line.account_id.internal_type == 'payable' and line.partner_id.id == self.partner_id.id and not line.reconciled: - account_move_lines_to_reconcile |= line - - account_move_lines_to_reconcile.reconcile() - - return {'type': 'ir.actions.act_window_close'} diff --git a/hr_payroll_payment/hr_payroll_register_payment.xml b/hr_payroll_payment/hr_payroll_register_payment.xml deleted file mode 100755 index 8e8d5a60..00000000 --- a/hr_payroll_payment/hr_payroll_register_payment.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - hr.payslip.select - hr.payslip - 20 - - - - - - - - - - - - - hr.payroll.register.payment.wizard.form - hr.payroll.register.payment.wizard - -
- - -
-

Draft Payment

-
- - - - - - - - - - - - - - - -
-
-
-
-
-
- - - - Register Payment - hr.payroll.register.payment.wizard - form - form - - new - {'default_payment_type': 'inbound'} - [('partner_type', '=', 'customer')] - - - - - hr.payslip.form - hr.payslip - - - -