From 0177afc69ffae173185be885c6604650f80d084b Mon Sep 17 00:00:00 2001 From: Saran440 Date: Tue, 27 Jul 2021 13:23:00 +0700 Subject: [PATCH 01/15] [14.0][ADD] hr_expense_payment (cherry picked from commit 958f0483ef96ca924c91b21cebf2ebf21180f179) --- hr_expense_payment/README.rst | 82 ++++ hr_expense_payment/__init__.py | 5 + hr_expense_payment/__manifest__.py | 16 + hr_expense_payment/hooks.py | 19 + hr_expense_payment/models/__init__.py | 5 + hr_expense_payment/models/account_payment.py | 19 + hr_expense_payment/models/hr_expense.py | 63 +++ hr_expense_payment/models/hr_expense_sheet.py | 26 ++ hr_expense_payment/readme/CONTRIBUTORS.rst | 1 + hr_expense_payment/readme/DESCRIPTION.rst | 2 + .../static/description/icon.png | Bin 0 -> 9455 bytes .../static/description/index.html | 422 ++++++++++++++++++ hr_expense_payment/tests/__init__.py | 3 + .../tests/test_hr_expense_payment.py | 91 ++++ hr_expense_payment/wizard/__init__.py | 3 + .../wizard/account_payment_register.py | 30 ++ 16 files changed, 787 insertions(+) create mode 100644 hr_expense_payment/README.rst create mode 100644 hr_expense_payment/__init__.py create mode 100644 hr_expense_payment/__manifest__.py create mode 100644 hr_expense_payment/hooks.py create mode 100644 hr_expense_payment/models/__init__.py create mode 100644 hr_expense_payment/models/account_payment.py create mode 100644 hr_expense_payment/models/hr_expense.py create mode 100644 hr_expense_payment/models/hr_expense_sheet.py create mode 100644 hr_expense_payment/readme/CONTRIBUTORS.rst create mode 100644 hr_expense_payment/readme/DESCRIPTION.rst create mode 100644 hr_expense_payment/static/description/icon.png create mode 100644 hr_expense_payment/static/description/index.html create mode 100644 hr_expense_payment/tests/__init__.py create mode 100644 hr_expense_payment/tests/test_hr_expense_payment.py create mode 100644 hr_expense_payment/wizard/__init__.py create mode 100644 hr_expense_payment/wizard/account_payment_register.py diff --git a/hr_expense_payment/README.rst b/hr_expense_payment/README.rst new file mode 100644 index 000000000..b82c96946 --- /dev/null +++ b/hr_expense_payment/README.rst @@ -0,0 +1,82 @@ +================== +HR Expense Payment +================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fhr--expense-lightgray.png?logo=github + :target: https://github.com/OCA/hr-expense/tree/14.0/hr_expense_payment + :alt: OCA/hr-expense +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/hr-expense-14-0/hr-expense-14-0-hr_expense_payment + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/289/14.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allow payment link to expense. +After you register payment on expense sheet, it will link between expense sheet and payment. + +**Table of contents** + +.. contents:: + :local: + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Ecosoft + +Contributors +~~~~~~~~~~~~ + +* Saran Lim. + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-Saran440| image:: https://github.com/Saran440.png?size=40px + :target: https://github.com/Saran440 + :alt: Saran440 + +Current `maintainer `__: + +|maintainer-Saran440| + +This module is part of the `OCA/hr-expense `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/hr_expense_payment/__init__.py b/hr_expense_payment/__init__.py new file mode 100644 index 000000000..c127efb6a --- /dev/null +++ b/hr_expense_payment/__init__.py @@ -0,0 +1,5 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models +from . import wizard +from .hooks import post_init_hook diff --git a/hr_expense_payment/__manifest__.py b/hr_expense_payment/__manifest__.py new file mode 100644 index 000000000..a0d156b95 --- /dev/null +++ b/hr_expense_payment/__manifest__.py @@ -0,0 +1,16 @@ +# Copyright 2019 Tecnativa - Ernesto Tejeda +# Copyright 2021 Ecosoft Co., Ltd (http://ecosoft.co.th/) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "HR Expense Payment", + "version": "14.0.1.0.0", + "category": "Human Resources", + "author": "Tecnativa, Ecosoft, Odoo Community Association (OCA)", + "license": "AGPL-3", + "website": "https://github.com/OCA/hr-expense", + "depends": ["hr_expense"], + "data": [], + "installable": True, + "post_init_hook": "post_init_hook", +} diff --git a/hr_expense_payment/hooks.py b/hr_expense_payment/hooks.py new file mode 100644 index 000000000..997bf6842 --- /dev/null +++ b/hr_expense_payment/hooks.py @@ -0,0 +1,19 @@ +# Copyright 2019 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import SUPERUSER_ID, api + + +def post_init_hook(cr, registry): + """ Trying to fill the source expense sheet in payments """ + with api.Environment.manage(): + env = api.Environment(cr, SUPERUSER_ID, {}) + sheets = env["hr.expense.sheet"].search([("payment_mode", "=", "own_account")]) + for sheet in sheets: + amls = sheet.account_move_id.mapped("line_ids") + reconcile = amls.mapped("full_reconcile_id") + aml_payment = reconcile.mapped("reconciled_line_ids").filtered( + lambda r: r not in amls + ) + payment = aml_payment.mapped("payment_id") + payment.write({"expense_sheet_ids": sheet.ids}) diff --git a/hr_expense_payment/models/__init__.py b/hr_expense_payment/models/__init__.py new file mode 100644 index 000000000..e9fa1d92d --- /dev/null +++ b/hr_expense_payment/models/__init__.py @@ -0,0 +1,5 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import account_payment +from . import hr_expense +from . import hr_expense_sheet diff --git a/hr_expense_payment/models/account_payment.py b/hr_expense_payment/models/account_payment.py new file mode 100644 index 000000000..0edcaea31 --- /dev/null +++ b/hr_expense_payment/models/account_payment.py @@ -0,0 +1,19 @@ +# Copyright 2019 Tecnativa - Ernesto Tejeda +# Copyright 2021 Ecosoft Co., Ltd (http://ecosoft.co.th/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class AccountPayment(models.Model): + _inherit = "account.payment" + + expense_sheet_ids = fields.Many2many( + comodel_name="hr.expense.sheet", + relation="payment_expense_sheet_rel", + column1="payment_id", + column2="sheet_id", + string="Expense sheet", + readonly=True, + copy=False, + ) diff --git a/hr_expense_payment/models/hr_expense.py b/hr_expense_payment/models/hr_expense.py new file mode 100644 index 000000000..ad0830489 --- /dev/null +++ b/hr_expense_payment/models/hr_expense.py @@ -0,0 +1,63 @@ +# Copyright 2019 Tecnativa - Ernesto Tejeda +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import models + + +class HrExpense(models.Model): + _inherit = "hr.expense" + + def _prepare_payment_expense_company( + self, + payment_methods, + journal, + different_currency, + journal_currency, + total_amount_currency, + ): + self.ensure_one() + commercial_partner_id = ( + self.employee_id.sudo().address_home_id.commercial_partner_id + ) + payment_dict = { + "date": self.date, + "amount": total_amount_currency, + "payment_type": "outbound", + "partner_type": "supplier", + "ref": self.name, + "journal_id": journal.id, + "currency_id": self.currency_id.id + if different_currency + else journal_currency.id, + "partner_id": commercial_partner_id.id, + "payment_method_id": payment_methods and payment_methods[0].id or False, + "expense_sheet_ids": self.sheet_id.ids, + } + return payment_dict + + def action_move_create(self): + move_group_by_sheet = super().action_move_create() + payment_list = [] + for expense in self: + if expense.payment_mode == "company_account": + total_amount_currency = expense.total_amount + different_currency = ( + expense.currency_id != expense.company_id.currency_id + ) + journal = expense.sheet_id.bank_journal_id + journal_currency = journal.currency_id or journal.company_id.currency_id + payment_methods = journal.outbound_payment_method_ids + # prepare payment dict value for case paid by company + payment_dict = expense._prepare_payment_expense_company( + payment_methods, + journal, + different_currency, + journal_currency, + total_amount_currency, + ) + payment_list.append(payment_dict) + # create payment and auto post from expense paid by company + if payment_list: + payment = self.env["account.payment"].create(payment_list) + payment.action_post() + return move_group_by_sheet diff --git a/hr_expense_payment/models/hr_expense_sheet.py b/hr_expense_payment/models/hr_expense_sheet.py new file mode 100644 index 000000000..4acab3067 --- /dev/null +++ b/hr_expense_payment/models/hr_expense_sheet.py @@ -0,0 +1,26 @@ +# Copyright 2019 Tecnativa - Ernesto Tejeda +# Copyright 2021 Ecosoft Co., Ltd (http://ecosoft.co.th/) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class HrExpenseSheet(models.Model): + _inherit = "hr.expense.sheet" + + payment_ids = fields.Many2many( + comodel_name="account.payment", + relation="payment_expense_sheet_rel", + column1="sheet_id", + column2="payment_id", + string="Payment", + readonly=True, + copy=False, + ) + + def action_register_payment(self): + """ Send context when you register payment from expense sheet """ + action = super().action_register_payment() + if self._name == "hr.expense.sheet": + action["context"].update({"expense_sheet_ids": self.ids}) + return action diff --git a/hr_expense_payment/readme/CONTRIBUTORS.rst b/hr_expense_payment/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..cc6b23102 --- /dev/null +++ b/hr_expense_payment/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Saran Lim. diff --git a/hr_expense_payment/readme/DESCRIPTION.rst b/hr_expense_payment/readme/DESCRIPTION.rst new file mode 100644 index 000000000..2bfb486f3 --- /dev/null +++ b/hr_expense_payment/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This module allow payment link to expense. +After you register payment on expense sheet, it will link between expense sheet and payment. diff --git a/hr_expense_payment/static/description/icon.png b/hr_expense_payment/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/hr_expense_payment/static/description/index.html b/hr_expense_payment/static/description/index.html new file mode 100644 index 000000000..e6349f8e0 --- /dev/null +++ b/hr_expense_payment/static/description/index.html @@ -0,0 +1,422 @@ + + + + + + +HR Expense Payment + + + +
+

HR Expense Payment

+ + +

Beta License: AGPL-3 OCA/hr-expense Translate me on Weblate Try me on Runbot

+

This module allow payment link to expense. +After you register payment on expense sheet, it will link between expense sheet and payment.

+

Table of contents

+ +
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Ecosoft
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainer:

+

Saran440

+

This module is part of the OCA/hr-expense project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/hr_expense_payment/tests/__init__.py b/hr_expense_payment/tests/__init__.py new file mode 100644 index 000000000..36579e552 --- /dev/null +++ b/hr_expense_payment/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import test_hr_expense_payment diff --git a/hr_expense_payment/tests/test_hr_expense_payment.py b/hr_expense_payment/tests/test_hr_expense_payment.py new file mode 100644 index 000000000..b24ae55c3 --- /dev/null +++ b/hr_expense_payment/tests/test_hr_expense_payment.py @@ -0,0 +1,91 @@ +# Copyright 2019 Tecnativa - Ernesto Tejeda +# Copyright 2021 Ecosoft Co., Ltd (http://ecosoft.co.th/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests.common import Form, TransactionCase + +from ..hooks import post_init_hook + + +class TestHrExpensePayment(TransactionCase): + def setUp(self): + super().setUp() + self.account_payment_register = self.env["account.payment.register"] + self.payment_journal = self.env["account.journal"].search( + [("type", "in", ["cash", "bank"])], limit=1 + ) + + company = self.env.ref("base.main_company") + self.expense_journal = self.env["account.journal"].create( + { + "name": "Purchase Journal - Test", + "code": "HRTPJ", + "type": "purchase", + "company_id": company.id, + } + ) + + self.expense_sheet = self.env["hr.expense.sheet"].create( + { + "employee_id": self.ref("hr.employee_admin"), + "name": "Expense test", + "journal_id": self.expense_journal.id, + } + ) + self.expense_sheet.approve_expense_sheets() + + self.expense = self.env["hr.expense"].create( + { + "name": "Expense test", + "employee_id": self.ref("hr.employee_admin"), + "product_id": self.ref("hr_expense.air_ticket"), + "unit_amount": 1, + "quantity": 10, + "sheet_id": self.expense_sheet.id, + } + ) + + def _get_payment_wizard(self, expense_sheet): + action = expense_sheet.action_register_payment() + ctx = action.get("context") + with Form( + self.account_payment_register.with_context(ctx), + view="account.view_account_payment_register_form", + ) as f: + f.journal_id = self.payment_journal + f.amount = self.expense_sheet.total_amount + register_payment = f.save() + return register_payment + + def test_post_init_hook(self): + self.expense_sheet.action_sheet_move_create() + payment_wizard = self._get_payment_wizard(self.expense_sheet) + payment_wizard.action_create_payments() + + payment = self.expense_sheet.payment_ids + + self.assertEqual(len(payment), 1) + self.assertEqual(len(payment.expense_sheet_ids), 1) + + payment.expense_sheet_ids = False + # Recompute many2one + payment = self.expense_sheet.payment_ids + + self.assertFalse(payment) + self.assertFalse(payment.expense_sheet_ids) + post_init_hook(self.env.cr, self.registry) + + self.assertEqual(len(self.expense_sheet.payment_ids), 1) + + def test_get_payment_vals(self): + self.expense_sheet.action_sheet_move_create() + payment_wizard = self._get_payment_wizard(self.expense_sheet) + self.assertFalse(self.expense_sheet.payment_ids) + payment_wizard.action_create_payments() + self.assertEqual(len(self.expense_sheet.payment_ids), 1) + + def test_action_sheet_move_create(self): + self.expense.payment_mode = "company_account" + self.assertFalse(self.expense_sheet.payment_ids) + self.expense_sheet.action_sheet_move_create() + self.assertEqual(len(self.expense_sheet.payment_ids), 1) diff --git a/hr_expense_payment/wizard/__init__.py b/hr_expense_payment/wizard/__init__.py new file mode 100644 index 000000000..09e22cb07 --- /dev/null +++ b/hr_expense_payment/wizard/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import account_payment_register diff --git a/hr_expense_payment/wizard/account_payment_register.py b/hr_expense_payment/wizard/account_payment_register.py new file mode 100644 index 000000000..dc4f40e1f --- /dev/null +++ b/hr_expense_payment/wizard/account_payment_register.py @@ -0,0 +1,30 @@ +# Copyright 2019 Tecnativa - Ernesto Tejeda +# Copyright 2020 Ecosoft Co., Ltd (https://ecosoft.co.th/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import models + + +class AccountPaymentRegister(models.TransientModel): + _inherit = "account.payment.register" + + def _create_payment_vals_from_wizard(self): + payment_vals = super()._create_payment_vals_from_wizard() + expense_sheet_ids = self._context.get("expense_sheet_ids", False) + if expense_sheet_ids: + payment_vals.update(expense_sheet_ids=expense_sheet_ids) + return payment_vals + + def _create_payment_vals_from_batch(self, batch_result): + payment_vals = super()._create_payment_vals_from_batch(batch_result) + expense_sheet_ids = self._context.get("expense_sheet_ids", False) + if expense_sheet_ids: + move_line_ids = self.env["account.move.line"].browse( + batch_result["lines"].ids + ) + sheet_ids = self.env["hr.expense.sheet"].browse(expense_sheet_ids) + sheet_id = sheet_ids.filtered( + lambda l: l.account_move_id.id == move_line_ids.move_id.id + ) + payment_vals.update(expense_sheet_ids=sheet_id.ids) + return payment_vals From 2aea7a0205bff14b8bbc799f0a986ebb4abb9046 Mon Sep 17 00:00:00 2001 From: Saran440 Date: Tue, 27 Jul 2021 13:28:28 +0700 Subject: [PATCH 02/15] [ENH] hr_expense_cancel depend on hr_expense_payment (cherry picked from commit 1fb90c168ca022fc6f027c04ae41f68821148d07) --- hr_expense_payment/README.rst | 9 +-- hr_expense_payment/hooks.py | 2 +- .../i18n/hr_expense_payment.pot | 68 +++++++++++++++++++ hr_expense_payment/models/hr_expense.py | 3 + hr_expense_payment/models/hr_expense_sheet.py | 2 +- .../static/description/index.html | 5 +- .../tests/test_hr_expense_payment.py | 9 +++ 7 files changed, 85 insertions(+), 13 deletions(-) create mode 100644 hr_expense_payment/i18n/hr_expense_payment.pot diff --git a/hr_expense_payment/README.rst b/hr_expense_payment/README.rst index b82c96946..d15fae650 100644 --- a/hr_expense_payment/README.rst +++ b/hr_expense_payment/README.rst @@ -49,6 +49,7 @@ Credits Authors ~~~~~~~ +* Tecnativa * Ecosoft Contributors @@ -69,14 +70,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -.. |maintainer-Saran440| image:: https://github.com/Saran440.png?size=40px - :target: https://github.com/Saran440 - :alt: Saran440 - -Current `maintainer `__: - -|maintainer-Saran440| - This module is part of the `OCA/hr-expense `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/hr_expense_payment/hooks.py b/hr_expense_payment/hooks.py index 997bf6842..adf207181 100644 --- a/hr_expense_payment/hooks.py +++ b/hr_expense_payment/hooks.py @@ -5,7 +5,7 @@ def post_init_hook(cr, registry): - """ Trying to fill the source expense sheet in payments """ + """Trying to fill the source expense sheet in payments""" with api.Environment.manage(): env = api.Environment(cr, SUPERUSER_ID, {}) sheets = env["hr.expense.sheet"].search([("payment_mode", "=", "own_account")]) diff --git a/hr_expense_payment/i18n/hr_expense_payment.pot b/hr_expense_payment/i18n/hr_expense_payment.pot new file mode 100644 index 000000000..a7d848668 --- /dev/null +++ b/hr_expense_payment/i18n/hr_expense_payment.pot @@ -0,0 +1,68 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * hr_expense_payment +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: hr_expense_payment +#: model:ir.model.fields,field_description:hr_expense_payment.field_account_payment__display_name +#: model:ir.model.fields,field_description:hr_expense_payment.field_account_payment_register__display_name +#: model:ir.model.fields,field_description:hr_expense_payment.field_hr_expense__display_name +#: model:ir.model.fields,field_description:hr_expense_payment.field_hr_expense_sheet__display_name +msgid "Display Name" +msgstr "" + +#. module: hr_expense_payment +#: model:ir.model,name:hr_expense_payment.model_hr_expense +msgid "Expense" +msgstr "" + +#. module: hr_expense_payment +#: model:ir.model,name:hr_expense_payment.model_hr_expense_sheet +msgid "Expense Report" +msgstr "" + +#. module: hr_expense_payment +#: model:ir.model.fields,field_description:hr_expense_payment.field_account_payment__expense_sheet_ids +msgid "Expense sheet" +msgstr "" + +#. module: hr_expense_payment +#: model:ir.model.fields,field_description:hr_expense_payment.field_account_payment__id +#: model:ir.model.fields,field_description:hr_expense_payment.field_account_payment_register__id +#: model:ir.model.fields,field_description:hr_expense_payment.field_hr_expense__id +#: model:ir.model.fields,field_description:hr_expense_payment.field_hr_expense_sheet__id +msgid "ID" +msgstr "" + +#. module: hr_expense_payment +#: model:ir.model.fields,field_description:hr_expense_payment.field_account_payment____last_update +#: model:ir.model.fields,field_description:hr_expense_payment.field_account_payment_register____last_update +#: model:ir.model.fields,field_description:hr_expense_payment.field_hr_expense____last_update +#: model:ir.model.fields,field_description:hr_expense_payment.field_hr_expense_sheet____last_update +msgid "Last Modified on" +msgstr "" + +#. module: hr_expense_payment +#: model:ir.model.fields,field_description:hr_expense_payment.field_hr_expense_sheet__payment_ids +msgid "Payment" +msgstr "" + +#. module: hr_expense_payment +#: model:ir.model,name:hr_expense_payment.model_account_payment +msgid "Payments" +msgstr "" + +#. module: hr_expense_payment +#: model:ir.model,name:hr_expense_payment.model_account_payment_register +msgid "Register Payment" +msgstr "" diff --git a/hr_expense_payment/models/hr_expense.py b/hr_expense_payment/models/hr_expense.py index ad0830489..d80514042 100644 --- a/hr_expense_payment/models/hr_expense.py +++ b/hr_expense_payment/models/hr_expense.py @@ -38,6 +38,9 @@ def _prepare_payment_expense_company( def action_move_create(self): move_group_by_sheet = super().action_move_create() payment_list = [] + # you can skip create payment from expense paid by company_account + if self.env.context.get("skip_create_payment_company_account", False): + return move_group_by_sheet for expense in self: if expense.payment_mode == "company_account": total_amount_currency = expense.total_amount diff --git a/hr_expense_payment/models/hr_expense_sheet.py b/hr_expense_payment/models/hr_expense_sheet.py index 4acab3067..a5f657e79 100644 --- a/hr_expense_payment/models/hr_expense_sheet.py +++ b/hr_expense_payment/models/hr_expense_sheet.py @@ -19,7 +19,7 @@ class HrExpenseSheet(models.Model): ) def action_register_payment(self): - """ Send context when you register payment from expense sheet """ + """Send context when you register payment from expense sheet""" action = super().action_register_payment() if self._name == "hr.expense.sheet": action["context"].update({"expense_sheet_ids": self.ids}) diff --git a/hr_expense_payment/static/description/index.html b/hr_expense_payment/static/description/index.html index e6349f8e0..8fb2e7e56 100644 --- a/hr_expense_payment/static/description/index.html +++ b/hr_expense_payment/static/description/index.html @@ -3,7 +3,7 @@ - + HR Expense Payment -
-

HR Expense Payment

+
+ + +Odoo Community Association + +
+

HR Expense Payment

-

Beta License: AGPL-3 OCA/hr-expense Translate me on Weblate Try me on Runboat

-

This module allow payment link to expense. After you register payment on -expense sheet, it will link between expense sheet and payment.

+

Beta License: AGPL-3 OCA/hr-expense Translate me on Weblate Try me on Runboat

+

This module links each employee expense to the payment(s) that settled +it. When you Register Payment on a posted employee-paid expense, the +resulting payment back-links to the source expense, and vice versa. A +post-install hook backfills the link for payments made before the module +was installed.

Table of contents

    @@ -385,24 +393,24 @@

    HR Expense Payment

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -feedback.

+feedback.

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Tecnativa
  • Ecosoft
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -421,10 +429,11 @@

Maintainers

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

-

This module is part of the OCA/hr-expense project on GitHub.

+

This module is part of the OCA/hr-expense project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
diff --git a/setup/_metapackage/pyproject.toml b/setup/_metapackage/pyproject.toml deleted file mode 100644 index 58db04949..000000000 --- a/setup/_metapackage/pyproject.toml +++ /dev/null @@ -1,11 +0,0 @@ -[project] -name = "odoo-addons-oca-hr-expense" -version = "18.0.20250109.0" -dependencies = [ - "odoo-addon-hr_expense_payment==18.0.*", -] -classifiers=[ - "Programming Language :: Python", - "Framework :: Odoo", - "Framework :: Odoo :: 18.0", -] From 093ce9c87bf7fc490adbaba1b305a6a4afe271d5 Mon Sep 17 00:00:00 2001 From: Don Kendall Date: Fri, 19 Jun 2026 15:11:35 -0400 Subject: [PATCH 15/15] [MIG] hr_expense_payment: Migration to 19.0 hr.expense.sheet was removed in 19.0; retarget the payment register to hr.expense. --- hr_expense_payment/__manifest__.py | 2 +- hr_expense_payment/hooks.py | 14 ++-- hr_expense_payment/models/__init__.py | 2 +- hr_expense_payment/models/account_payment.py | 10 +-- hr_expense_payment/models/hr_expense.py | 29 +++++++++ hr_expense_payment/models/hr_expense_sheet.py | 26 -------- .../tests/test_hr_expense_payment.py | 65 ++++++++++--------- .../wizard/account_payment_register.py | 20 ++++-- 8 files changed, 92 insertions(+), 76 deletions(-) create mode 100644 hr_expense_payment/models/hr_expense.py delete mode 100644 hr_expense_payment/models/hr_expense_sheet.py diff --git a/hr_expense_payment/__manifest__.py b/hr_expense_payment/__manifest__.py index 2b60855be..e0adbc7d1 100644 --- a/hr_expense_payment/__manifest__.py +++ b/hr_expense_payment/__manifest__.py @@ -4,7 +4,7 @@ { "name": "HR Expense Payment", - "version": "18.0.1.0.0", + "version": "19.0.1.0.0", "category": "Human Resources", "author": "Tecnativa, Ecosoft, Odoo Community Association (OCA)", "license": "AGPL-3", diff --git a/hr_expense_payment/hooks.py b/hr_expense_payment/hooks.py index cdd39fb54..09237d225 100644 --- a/hr_expense_payment/hooks.py +++ b/hr_expense_payment/hooks.py @@ -3,13 +3,17 @@ def post_init_hook(env): - """Trying to fill the source expense sheet in payments""" - sheets = env["hr.expense.sheet"].search([("payment_mode", "=", "own_account")]) - for sheet in sheets: - amls = sheet.account_move_ids.mapped("line_ids") + """Backfill payment_ids on expenses paid before the module was installed, + via the expense move's reconciliation.""" + expenses = env["hr.expense"].search( + [("payment_mode", "=", "own_account"), ("account_move_id", "!=", False)] + ) + for expense in expenses: + amls = expense.account_move_id.line_ids reconcile = amls.mapped("full_reconcile_id") aml_payment = reconcile.mapped("reconciled_line_ids").filtered( lambda r, amls=amls: r not in amls ) payment = aml_payment.mapped("payment_id") - payment.write({"expense_sheet_ids": sheet.ids}) + if payment: + payment.write({"expense_ids": [(4, expense.id)]}) diff --git a/hr_expense_payment/models/__init__.py b/hr_expense_payment/models/__init__.py index c7667a3fa..82e1cb11c 100644 --- a/hr_expense_payment/models/__init__.py +++ b/hr_expense_payment/models/__init__.py @@ -1,4 +1,4 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from . import account_payment -from . import hr_expense_sheet +from . import hr_expense diff --git a/hr_expense_payment/models/account_payment.py b/hr_expense_payment/models/account_payment.py index 0edcaea31..0ddc832b4 100644 --- a/hr_expense_payment/models/account_payment.py +++ b/hr_expense_payment/models/account_payment.py @@ -8,12 +8,12 @@ class AccountPayment(models.Model): _inherit = "account.payment" - expense_sheet_ids = fields.Many2many( - comodel_name="hr.expense.sheet", - relation="payment_expense_sheet_rel", + expense_ids = fields.Many2many( + comodel_name="hr.expense", + relation="payment_hr_expense_rel", column1="payment_id", - column2="sheet_id", - string="Expense sheet", + column2="expense_id", + string="Expenses", readonly=True, copy=False, ) diff --git a/hr_expense_payment/models/hr_expense.py b/hr_expense_payment/models/hr_expense.py new file mode 100644 index 000000000..bced26fd1 --- /dev/null +++ b/hr_expense_payment/models/hr_expense.py @@ -0,0 +1,29 @@ +# Copyright 2019 Tecnativa - Ernesto Tejeda +# Copyright 2021 Ecosoft Co., Ltd (http://ecosoft.co.th/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class HrExpense(models.Model): + _inherit = "hr.expense" + + payment_ids = fields.Many2many( + comodel_name="account.payment", + relation="payment_hr_expense_rel", + column1="expense_id", + column2="payment_id", + string="Payments", + readonly=True, + copy=False, + ) + + def action_pay(self): + """Pass the expense ids to the payment wizard so created payments + back-link here.""" + action = super().action_pay() + if action and isinstance(action, dict): + ctx = dict(action.get("context") or {}) + ctx["hr_expense_ids"] = self.ids + action["context"] = ctx + return action diff --git a/hr_expense_payment/models/hr_expense_sheet.py b/hr_expense_payment/models/hr_expense_sheet.py deleted file mode 100644 index 59462a687..000000000 --- a/hr_expense_payment/models/hr_expense_sheet.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2019 Tecnativa - Ernesto Tejeda -# Copyright 2021 Ecosoft Co., Ltd (http://ecosoft.co.th/) -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). - -from odoo import fields, models - - -class HrExpenseSheet(models.Model): - _inherit = "hr.expense.sheet" - - payment_ids = fields.Many2many( - comodel_name="account.payment", - relation="payment_expense_sheet_rel", - column1="sheet_id", - column2="payment_id", - string="Payment", - readonly=True, - copy=False, - ) - - def action_register_payment(self): - """Send context when you register payment from expense sheet""" - action = super().action_register_payment() - if self._name == "hr.expense.sheet": - action["context"].update({"expense_sheet_ids": self.ids}) - return action diff --git a/hr_expense_payment/tests/test_hr_expense_payment.py b/hr_expense_payment/tests/test_hr_expense_payment.py index 636b7159d..23e1fb369 100644 --- a/hr_expense_payment/tests/test_hr_expense_payment.py +++ b/hr_expense_payment/tests/test_hr_expense_payment.py @@ -15,38 +15,41 @@ class TestHrExpensePayment(TestExpenseCommon): @classmethod def setUpClass(cls): super().setUpClass() - # Create expense + sheet + approve - cls.expense = cls.create_expense(cls) - res = cls.expense.action_submit_expenses() - cls.expense_sheet = cls.env[res["res_model"]].browse(res["res_id"]) - cls.expense_sheet.action_submit_sheet() - cls.expense_sheet.action_approve_expense_sheets() + # Build an employee-paid expense, submit + approve + post (= receipt). + cls.expense = cls.create_expenses({"payment_mode": "own_account"}) + cls.expense.action_submit() + cls.expense.action_approve() + cls.post_expenses_with_wizard(cls.expense) def _get_payment_wizard(self): - res = self.expense_sheet.action_register_payment() - register_form = Form(self.env[res["res_model"]].with_context(**res["context"])) - register_form.journal_id = self.company_data["default_journal_bank"] - register_form.amount = self.expense_sheet.total_amount - return register_form.save() - - def test_post_init_hook(self): - self.expense_sheet.action_sheet_move_post() - payment_wizard = self._get_payment_wizard() - payment_wizard.action_create_payments() - payment = self.expense_sheet.payment_ids + action = self.expense.action_pay() + wizard_form = Form( + self.env[action["res_model"]].with_context(**action["context"]) + ) + wizard_form.journal_id = self.company_data["default_journal_bank"] + wizard_form.amount = self.expense.total_amount + return wizard_form.save() + + def test_action_pay_links_payment_back_to_expense(self): + """After action_pay → create_payment, the resulting payment's + expense_ids includes the source expense and the expense's + payment_ids includes the new payment.""" + self.assertFalse(self.expense.payment_ids) + wizard = self._get_payment_wizard() + wizard.action_create_payments() + self.assertEqual(len(self.expense.payment_ids), 1) + payment = self.expense.payment_ids + self.assertIn(self.expense, payment.expense_ids) + + def test_post_init_hook_backfills_legacy_payments(self): + """post_init_hook walks reconciliation to recover the back-link for + payments that pre-date the module install.""" + wizard = self._get_payment_wizard() + wizard.action_create_payments() + payment = self.expense.payment_ids self.assertEqual(len(payment), 1) - self.assertEqual(len(payment.expense_sheet_ids), 1) - payment.expense_sheet_ids = False - # Recompute many2one - payment = self.expense_sheet.payment_ids - self.assertFalse(payment) - self.assertFalse(payment.expense_sheet_ids) + # Wipe the back-link, then re-run the hook. + payment.expense_ids = False + self.assertFalse(self.expense.payment_ids) post_init_hook(self.env) - self.assertEqual(len(self.expense_sheet.payment_ids), 1) - - def test_get_payment_vals(self): - self.expense_sheet.action_sheet_move_post() - payment_wizard = self._get_payment_wizard() - self.assertFalse(self.expense_sheet.payment_ids) - payment_wizard.action_create_payments() - self.assertEqual(len(self.expense_sheet.payment_ids), 1) + self.assertEqual(len(self.expense.payment_ids), 1) diff --git a/hr_expense_payment/wizard/account_payment_register.py b/hr_expense_payment/wizard/account_payment_register.py index 7381fd9ae..3f53c5b4b 100644 --- a/hr_expense_payment/wizard/account_payment_register.py +++ b/hr_expense_payment/wizard/account_payment_register.py @@ -10,15 +10,21 @@ class AccountPaymentRegister(models.TransientModel): def _create_payment_vals_from_wizard(self, batch_result): payment_vals = super()._create_payment_vals_from_wizard(batch_result) - expense_sheet_ids = self._context.get("expense_sheet_ids", False) - if expense_sheet_ids: - payment_vals.update(expense_sheet_ids=expense_sheet_ids) + expense_ids = self.env.context.get("hr_expense_ids") + if expense_ids: + payment_vals["expense_ids"] = [(6, 0, expense_ids)] return payment_vals def _create_payment_vals_from_batch(self, batch_result): payment_vals = super()._create_payment_vals_from_batch(batch_result) - expense_sheet_ids = self._context.get("expense_sheet_ids", False) - if expense_sheet_ids: - moves = self.env["account.move"].browse(batch_result["lines"].move_id.ids) - payment_vals.update(expense_sheet_ids=moves.mapped("expense_sheet_id").ids) + expense_ids = self.env.context.get("hr_expense_ids") + if expense_ids: + # action_pay path: use the caller-supplied ids. + payment_vals["expense_ids"] = [(6, 0, expense_ids)] + return payment_vals + # Otherwise derive from the batch's moves (payment registered move-side). + moves = self.env["account.move"].browse(batch_result["lines"].move_id.ids) + expenses = moves.expense_ids + if expenses: + payment_vals["expense_ids"] = [(6, 0, expenses.ids)] return payment_vals