diff --git a/account_invoice_alternate_payer/README.rst b/account_invoice_alternate_payer/README.rst new file mode 100644 index 00000000000..94f513422d0 --- /dev/null +++ b/account_invoice_alternate_payer/README.rst @@ -0,0 +1,93 @@ +=============================== +Account Invoice Alternate Payer +=============================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:8e7efb8082dae27a79a5ccf141d6363c61a66a547c4700ecf9658a0725221ccd + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Faccount--invoicing-lightgray.png?logo=github + :target: https://github.com/OCA/account-invoicing/tree/17.0/account_invoice_alternate_payer + :alt: OCA/account-invoicing +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/account-invoicing-17-0/account-invoicing-17-0-account_invoice_alternate_payer + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/account-invoicing&target_branch=17.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows to enter an alternative payer in the customer invoice +or vendor bill. + +This allows, that we pay or expect to get paid by another partner other +than the main partner. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +Access to the invoice and change the Alternate Payor/Payee of the +invoice. + +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 `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Eficent + +Contributors +------------ + +- Eficent Business and IT Consulting Services, S.L. + (https://www.eficent.com) + + - Jordi Ballester Alomar + +- Auneor Conseil (https://www.auneor-conseil.fr) + + - Alpha Oumar Sandaly DIALLO + +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. + +This module is part of the `OCA/account-invoicing `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/account_invoice_alternate_payer/__init__.py b/account_invoice_alternate_payer/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/account_invoice_alternate_payer/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/account_invoice_alternate_payer/__manifest__.py b/account_invoice_alternate_payer/__manifest__.py new file mode 100644 index 00000000000..1efc8dbc370 --- /dev/null +++ b/account_invoice_alternate_payer/__manifest__.py @@ -0,0 +1,14 @@ +# Copyright 2018 Eficent Business and IT Consulting Services, S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "Account Invoice Alternate Payer", + "summary": "Set a alternate payor/payee in invoices", + "version": "17.0.1.0.0", + "license": "AGPL-3", + "category": "Accounting", + "author": "Eficent,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/account-invoicing", + "depends": ["account"], + "data": ["views/account_move_views.xml"], +} diff --git a/account_invoice_alternate_payer/i18n/account_invoice_alternate_payer.pot b/account_invoice_alternate_payer/i18n/account_invoice_alternate_payer.pot new file mode 100644 index 00000000000..867c16a51cf --- /dev/null +++ b/account_invoice_alternate_payer/i18n/account_invoice_alternate_payer.pot @@ -0,0 +1,61 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_invoice_alternate_payer +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-09 13:16+0000\n" +"PO-Revision-Date: 2024-12-09 13:16+0000\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: account_invoice_alternate_payer +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_bank_statement_line__alternate_payer_id +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_move__alternate_payer_id +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_payment__alternate_payer_id +msgid "Alternate Payer" +msgstr "" + +#. module: account_invoice_alternate_payer +#: model:ir.model.fields,help:account_invoice_alternate_payer.field_account_bank_statement_line__alternate_payer_id +#: model:ir.model.fields,help:account_invoice_alternate_payer.field_account_move__alternate_payer_id +#: model:ir.model.fields,help:account_invoice_alternate_payer.field_account_payment__alternate_payer_id +msgid "" +"If set, this will be the partner that we expect to pay or to be paid by. If " +"not set, the payor is by default the commercial" +msgstr "" + +#. module: account_invoice_alternate_payer +#: model:ir.model,name:account_invoice_alternate_payer.model_account_move +msgid "Journal Entry" +msgstr "" + +#. module: account_invoice_alternate_payer +#: model:ir.model,name:account_invoice_alternate_payer.model_account_move_line +msgid "Journal Item" +msgstr "" + +#. module: account_invoice_alternate_payer +#. odoo-python +#: code:addons/account_invoice_alternate_payer/models/account_move.py:0 +#, python-format +msgid "Outstanding credits" +msgstr "" + +#. module: account_invoice_alternate_payer +#. odoo-python +#: code:addons/account_invoice_alternate_payer/models/account_move.py:0 +#, python-format +msgid "Outstanding debits" +msgstr "" + +#. module: account_invoice_alternate_payer +#: model:ir.model,name:account_invoice_alternate_payer.model_account_payment +msgid "Payments" +msgstr "" diff --git a/account_invoice_alternate_payer/i18n/es.po b/account_invoice_alternate_payer/i18n/es.po new file mode 100644 index 00000000000..2717e8f6717 --- /dev/null +++ b/account_invoice_alternate_payer/i18n/es.po @@ -0,0 +1,92 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_invoice_alternate_payer +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-03-23 09:36+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: none\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: account_invoice_alternate_payer +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_bank_statement_line__alternate_payer_id +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_move__alternate_payer_id +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_payment__alternate_payer_id +#: model_terms:ir.ui.view,arch_db:account_invoice_alternate_payer.view_move_form +msgid "Alternate Payer" +msgstr "Pagador Alterno" + +#. module: account_invoice_alternate_payer +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_move__display_name +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_move_line__display_name +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_payment__display_name +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_payment_register__display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: account_invoice_alternate_payer +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_move__id +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_move_line__id +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_payment__id +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_payment_register__id +msgid "ID" +msgstr "ID" + +#. module: account_invoice_alternate_payer +#: model:ir.model.fields,help:account_invoice_alternate_payer.field_account_bank_statement_line__alternate_payer_id +#: model:ir.model.fields,help:account_invoice_alternate_payer.field_account_move__alternate_payer_id +#: model:ir.model.fields,help:account_invoice_alternate_payer.field_account_payment__alternate_payer_id +msgid "" +"If set, this will be the partner that we expect to pay or to be paid by. If " +"not set, the payor is by default the commercial" +msgstr "" +"Si se establece, será el socio que esperamos que pague o que nos pague. Si " +"no se establece, el pagador será por defecto el comercial" + +#. module: account_invoice_alternate_payer +#: model:ir.model,name:account_invoice_alternate_payer.model_account_move +msgid "Journal Entry" +msgstr "Asiento contable" + +#. module: account_invoice_alternate_payer +#: model:ir.model,name:account_invoice_alternate_payer.model_account_move_line +msgid "Journal Item" +msgstr "Apunte contable" + +#. module: account_invoice_alternate_payer +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_move____last_update +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_move_line____last_update +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_payment____last_update +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_payment_register____last_update +msgid "Last Modified on" +msgstr "Última modificación el" + +#. module: account_invoice_alternate_payer +#: code:addons/account_invoice_alternate_payer/models/account_move.py:0 +#, python-format +msgid "Outstanding credits" +msgstr "Créditos pendientes" + +#. module: account_invoice_alternate_payer +#: code:addons/account_invoice_alternate_payer/models/account_move.py:0 +#, python-format +msgid "Outstanding debits" +msgstr "Débitos pendientes" + +#. module: account_invoice_alternate_payer +#: model:ir.model,name:account_invoice_alternate_payer.model_account_payment +msgid "Payments" +msgstr "Pagos" + +#. module: account_invoice_alternate_payer +#: model:ir.model,name:account_invoice_alternate_payer.model_account_payment_register +msgid "Register Payment" +msgstr "Registrar pago" diff --git a/account_invoice_alternate_payer/i18n/fr.po b/account_invoice_alternate_payer/i18n/fr.po new file mode 100644 index 00000000000..2b4a346edee --- /dev/null +++ b/account_invoice_alternate_payer/i18n/fr.po @@ -0,0 +1,62 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_invoice_alternate_payer +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-09 13:13+0000\n" +"PO-Revision-Date: 2024-12-09 13:13+0000\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: account_invoice_alternate_payer +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_bank_statement_line__alternate_payer_id +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_move__alternate_payer_id +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_payment__alternate_payer_id +msgid "Alternate Payer" +msgstr "Payeur" + +#. module: account_invoice_alternate_payer +#: model:ir.model.fields,help:account_invoice_alternate_payer.field_account_bank_statement_line__alternate_payer_id +#: model:ir.model.fields,help:account_invoice_alternate_payer.field_account_move__alternate_payer_id +#: model:ir.model.fields,help:account_invoice_alternate_payer.field_account_payment__alternate_payer_id +msgid "" +"If set, this will be the partner that we expect to pay or to be paid by. If " +"not set, the payor is by default the commercial" +msgstr "S'il est défini, il s'agira du partenaire que nous prévoyons de payer ou par lequel nous prévoyons d'être payés." +"S'il n'est pas défini, le payeur est par défaut le partenaire commercial." + +#. module: account_invoice_alternate_payer +#: model:ir.model,name:account_invoice_alternate_payer.model_account_move +msgid "Journal Entry" +msgstr "Pièce comptable" + +#. module: account_invoice_alternate_payer +#: model:ir.model,name:account_invoice_alternate_payer.model_account_move_line +msgid "Journal Item" +msgstr "Écriture comptable" + +#. module: account_invoice_alternate_payer +#. odoo-python +#: code:addons/account_invoice_alternate_payer/models/account_move.py:0 +#, python-format +msgid "Outstanding credits" +msgstr "crédits en circulation" + +#. module: account_invoice_alternate_payer +#. odoo-python +#: code:addons/account_invoice_alternate_payer/models/account_move.py:0 +#, python-format +msgid "Outstanding debits" +msgstr "Débits en circulation" + +#. module: account_invoice_alternate_payer +#: model:ir.model,name:account_invoice_alternate_payer.model_account_payment +msgid "Payments" +msgstr "Paiements" diff --git a/account_invoice_alternate_payer/i18n/it.po b/account_invoice_alternate_payer/i18n/it.po new file mode 100644 index 00000000000..472600e2f07 --- /dev/null +++ b/account_invoice_alternate_payer/i18n/it.po @@ -0,0 +1,70 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_invoice_alternate_payer +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-04-04 12:37+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: account_invoice_alternate_payer +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_bank_statement_line__alternate_payer_id +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_move__alternate_payer_id +#: model:ir.model.fields,field_description:account_invoice_alternate_payer.field_account_payment__alternate_payer_id +#: model_terms:ir.ui.view,arch_db:account_invoice_alternate_payer.view_move_form +msgid "Alternate Payer" +msgstr "Pagatore alternativo" + +#. module: account_invoice_alternate_payer +#: model:ir.model.fields,help:account_invoice_alternate_payer.field_account_bank_statement_line__alternate_payer_id +#: model:ir.model.fields,help:account_invoice_alternate_payer.field_account_move__alternate_payer_id +#: model:ir.model.fields,help:account_invoice_alternate_payer.field_account_payment__alternate_payer_id +msgid "" +"If set, this will be the partner that we expect to pay or to be paid by. If " +"not set, the payor is by default the commercial" +msgstr "" +"Se impostato, questo sarà il partner che ci si aspetta che paghi o da " +"pagare. Se non impostato, il pagatore è in modo predefinito il commerciale" + +#. module: account_invoice_alternate_payer +#: model:ir.model,name:account_invoice_alternate_payer.model_account_move +msgid "Journal Entry" +msgstr "Registrazione contabile" + +#. module: account_invoice_alternate_payer +#: model:ir.model,name:account_invoice_alternate_payer.model_account_move_line +msgid "Journal Item" +msgstr "Movimento contabile" + +#. module: account_invoice_alternate_payer +#. odoo-python +#: code:addons/account_invoice_alternate_payer/models/account_move.py:0 +#, python-format +msgid "Outstanding credits" +msgstr "Crediti scaduti" + +#. module: account_invoice_alternate_payer +#. odoo-python +#: code:addons/account_invoice_alternate_payer/models/account_move.py:0 +#, python-format +msgid "Outstanding debits" +msgstr "Debiti scaduti" + +#. module: account_invoice_alternate_payer +#: model:ir.model,name:account_invoice_alternate_payer.model_account_payment +msgid "Payments" +msgstr "Pagamenti" + +#. module: account_invoice_alternate_payer +#: model:ir.model,name:account_invoice_alternate_payer.model_account_payment_register +msgid "Register Payment" +msgstr "Registra pagamento" diff --git a/account_invoice_alternate_payer/models/__init__.py b/account_invoice_alternate_payer/models/__init__.py new file mode 100644 index 00000000000..11623d4e532 --- /dev/null +++ b/account_invoice_alternate_payer/models/__init__.py @@ -0,0 +1,2 @@ +from . import account_move +from . import account_payment diff --git a/account_invoice_alternate_payer/models/account_move.py b/account_invoice_alternate_payer/models/account_move.py new file mode 100644 index 00000000000..5041878e638 --- /dev/null +++ b/account_invoice_alternate_payer/models/account_move.py @@ -0,0 +1,160 @@ +# Copyright 2018 Eficent Business and IT Consulting Services, S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from contextlib import contextmanager + +from odoo import _, api, fields, models + + +class AccountMove(models.Model): + _inherit = "account.move" + + alternate_payer_id = fields.Many2one( + "res.partner", + string="Alternate Payer", + help="If set, this will be the partner that we expect to pay or to " + "be paid by. If not set, the payor is by default the " + "commercial", + ) + + @api.depends("commercial_partner_id", "alternate_payer_id") + def _compute_bank_partner_id(self): + super_moves = self.filtered( + lambda r: not r.alternate_payer_id or not r.is_outbound() + ) + for move in self - super_moves: + if move.is_outbound() and move.alternate_payer_id: + move.bank_partner_id = move.alternate_payer_id + return super( + AccountMove, + super_moves, + )._compute_bank_partner_id() + + @api.onchange("alternate_payer_id") + def _onchange_alternate_payer_id(self): + return self._onchange_partner_id() + + @contextmanager + def _sync_dynamic_line( + self, + existing_key_fname, + needed_vals_fname, + needed_dirty_fname, + line_type, + container, + ): + records_with_alternate_player = container.get("records").filtered( + lambda x: x.alternate_payer_id + ) + with super()._sync_dynamic_line( + existing_key_fname, + needed_vals_fname, + needed_dirty_fname, + line_type, + container, + ): + if line_type == "payment_term" and records_with_alternate_player: + for invoice in container.get("records").filtered( + lambda x: x.alternate_payer_id + ): + payment_term_lines = invoice.line_ids.filtered( + lambda x: x.display_type == "payment_term" + ) + payment_term_lines.write( + { + "partner_id": invoice.alternate_payer_id.id, + } + ) + yield + + def _compute_payments_widget_to_reconcile_info(self): + super_moves = self.filtered(lambda r: not r.alternate_payer_id) + for move in self - super_moves: + move.invoice_outstanding_credits_debits_widget = False + move.invoice_has_outstanding = False + + if ( + move.state != "posted" + or move.payment_state not in ("not_paid", "partial") + or not move.is_invoice(include_receipts=True) + ): + continue + + pay_term_lines = move.line_ids.filtered( + lambda line: line.account_id.account_type + in ("asset_receivable", "liability_payable") + ) + + domain = [ + ("account_id", "in", pay_term_lines.account_id.ids), + ("parent_state", "=", "posted"), + ("partner_id", "=", move.alternate_payer_id.id), + ("reconciled", "=", False), + "|", + ("amount_residual", "!=", 0.0), + ("amount_residual_currency", "!=", 0.0), + ] + + payments_widget_vals = { + "outstanding": True, + "content": [], + "move_id": move.id, + } + + if move.is_inbound(): + domain.append(("balance", "<", 0.0)) + payments_widget_vals["title"] = _("Outstanding credits") + else: + domain.append(("balance", ">", 0.0)) + payments_widget_vals["title"] = _("Outstanding debits") + + for line in self.env["account.move.line"].search(domain): + if line.currency_id == move.currency_id: + # Same foreign currency. + amount = abs(line.amount_residual_currency) + else: + # Different foreign currencies. + amount = line.company_currency_id._convert( + abs(line.amount_residual), + move.currency_id, + move.company_id, + line.date, + ) + + if move.currency_id.is_zero(amount): + continue + + payments_widget_vals["content"].append( + { + "journal_name": line.ref or line.move_id.name, + "amount": amount, + "currency_id": move.currency_id.id, + "id": line.id, + "move_id": line.move_id.id, + "date": fields.Date.to_string(line.date), + "account_payment_id": line.payment_id.id, + } + ) + + if not payments_widget_vals["content"]: + continue + + move.invoice_outstanding_credits_debits_widget = payments_widget_vals + move.invoice_has_outstanding = True + return super( + AccountMove, super_moves + )._compute_payments_widget_to_reconcile_info() + + +class AccountMoveLine(models.Model): + _inherit = "account.move.line" + + def write(self, values): + if "partner_id" in values and len(values.keys()) == 1: + lines_to_skip = self.filtered( + lambda x: x.move_id.alternate_payer_id + and x.display_type == "payment_term" + and x.partner_id == x.move_id.alternate_payer_id + ) + return super(AccountMoveLine, self - lines_to_skip).write(values) + return super().write(values) diff --git a/account_invoice_alternate_payer/models/account_payment.py b/account_invoice_alternate_payer/models/account_payment.py new file mode 100644 index 00000000000..9650e13d025 --- /dev/null +++ b/account_invoice_alternate_payer/models/account_payment.py @@ -0,0 +1,23 @@ +# Copyright 2018 Eficent Business and IT Consulting Services, S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, models + + +class AccountPayment(models.Model): + _inherit = "account.payment" + + @api.model + def default_get(self, default_fields): + rec = super().default_get(default_fields) + if self.env.context.get("active_model") != "account.move": + return rec + active_ids = self._context.get("active_ids") + invoices = ( + self.env["account.move"] + .browse(active_ids) + .filtered(lambda move: move.is_invoice(include_receipts=True)) + ) + if invoices and invoices[0].alternate_payer_id: + rec.update({"partner_id": invoices[0].alternate_payer_id.id}) + return rec diff --git a/account_invoice_alternate_payer/pyproject.toml b/account_invoice_alternate_payer/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/account_invoice_alternate_payer/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/account_invoice_alternate_payer/readme/CONTRIBUTORS.md b/account_invoice_alternate_payer/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..0c7a5685810 --- /dev/null +++ b/account_invoice_alternate_payer/readme/CONTRIBUTORS.md @@ -0,0 +1,5 @@ +- Eficent Business and IT Consulting Services, S.L. () + - Jordi Ballester Alomar <\> + +- Auneor Conseil (https://www.auneor-conseil.fr) + - Alpha Oumar Sandaly DIALLO <\> diff --git a/account_invoice_alternate_payer/readme/DESCRIPTION.md b/account_invoice_alternate_payer/readme/DESCRIPTION.md new file mode 100644 index 00000000000..2d362ace97f --- /dev/null +++ b/account_invoice_alternate_payer/readme/DESCRIPTION.md @@ -0,0 +1,5 @@ +This module allows to enter an alternative payer in the customer invoice +or vendor bill. + +This allows, that we pay or expect to get paid by another partner other +than the main partner. diff --git a/account_invoice_alternate_payer/readme/USAGE.md b/account_invoice_alternate_payer/readme/USAGE.md new file mode 100644 index 00000000000..04f2c4aec0a --- /dev/null +++ b/account_invoice_alternate_payer/readme/USAGE.md @@ -0,0 +1,2 @@ +Access to the invoice and change the Alternate Payor/Payee of the +invoice. diff --git a/account_invoice_alternate_payer/static/description/icon.png b/account_invoice_alternate_payer/static/description/icon.png new file mode 100644 index 00000000000..3a0328b516c Binary files /dev/null and b/account_invoice_alternate_payer/static/description/icon.png differ diff --git a/account_invoice_alternate_payer/static/description/index.html b/account_invoice_alternate_payer/static/description/index.html new file mode 100644 index 00000000000..7d84c6e4e4d --- /dev/null +++ b/account_invoice_alternate_payer/static/description/index.html @@ -0,0 +1,440 @@ + + + + + +Account Invoice Alternate Payer + + + +
+

Account Invoice Alternate Payer

+ + +

Beta License: AGPL-3 OCA/account-invoicing Translate me on Weblate Try me on Runboat

+

This module allows to enter an alternative payer in the customer invoice +or vendor bill.

+

This allows, that we pay or expect to get paid by another partner other +than the main partner.

+

Table of contents

+ +
+

Usage

+

Access to the invoice and change the Alternate Payor/Payee of the +invoice.

+
+
+

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.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Eficent
  • +
+
+
+

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.

+

This module is part of the OCA/account-invoicing project on GitHub.

+

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

+
+
+
+ + diff --git a/account_invoice_alternate_payer/tests/__init__.py b/account_invoice_alternate_payer/tests/__init__.py new file mode 100644 index 00000000000..d7555d02125 --- /dev/null +++ b/account_invoice_alternate_payer/tests/__init__.py @@ -0,0 +1 @@ +from . import test_account_invoice diff --git a/account_invoice_alternate_payer/tests/test_account_invoice.py b/account_invoice_alternate_payer/tests/test_account_invoice.py new file mode 100644 index 00000000000..ace60ef5217 --- /dev/null +++ b/account_invoice_alternate_payer/tests/test_account_invoice.py @@ -0,0 +1,189 @@ +# Copyright 2018 Eficent Business and IT Consulting Services, S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests import Form, tagged + +from odoo.addons.account.tests.common import AccountTestInvoicingCommon + + +@tagged("post_install", "-at_install") +class TestAccountInvoiceAlternateCommercialPartner(AccountTestInvoicingCommon): + @classmethod + def setUpClass(cls, chart_template_ref=None): + super().setUpClass(chart_template_ref=chart_template_ref) + cls.in_invoice = cls.init_invoice("in_invoice", products=cls.product_b) + cls.out_invoice = cls.init_invoice("out_invoice", products=cls.product_a) + cls.in_invoice_02 = cls.init_invoice("in_invoice", products=cls.product_a) + cls.out_invoice_02 = cls.init_invoice("out_invoice", products=cls.product_b) + cls.alternate_partner = cls.env["res.partner"].create( + { + "name": "Alternate Payer", + "property_payment_term_id": cls.pay_terms_a.id, + "property_supplier_payment_term_id": cls.pay_terms_a.id, + "property_account_receivable_id": cls.company_data[ + "default_account_receivable" + ].id, + "property_account_payable_id": cls.company_data[ + "default_account_payable" + ].id, + "company_id": False, + } + ) + + cls.payment_method_manual_out = cls.env.ref( + "account.account_payment_method_manual_out" + ) + cls.payment_method_manual_in = cls.env.ref( + "account.account_payment_method_manual_in" + ) + cls.bank_journal_euro = cls.env["account.journal"].create( + {"name": "Bank", "type": "bank", "code": "BNK67"} + ) + cls.bank_account = cls.env["account.account"].create( + { + "name": "Demo Bank account", + "code": "demobankaccount01", + "account_type": "asset_cash", + } + ) + + def test_01_onchange_out_invoice(self): + with Form(self.out_invoice) as form: + form.alternate_payer_id = self.alternate_partner + self.out_invoice = form.save() + self.assertEqual( + self.out_invoice.bank_partner_id, self.out_invoice.company_id.partner_id + ) + + def test_01_1_post_out_invoice(self): + with Form(self.out_invoice) as form: + form.alternate_payer_id = self.alternate_partner + self.out_invoice_posted = form.save() + self.out_invoice_posted.action_post() + self.assertEqual( + self.out_invoice_posted.line_ids.filtered( + lambda r: r.display_type == "payment_term" + ).mapped("partner_id"), + self.alternate_partner, + ) + self.assertEqual( + self.out_invoice_posted.line_ids.filtered( + lambda r: r.display_type != "payment_term" + ).mapped("partner_id"), + self.out_invoice_posted.partner_id, + ) + self.assertEqual( + self.out_invoice_posted.bank_partner_id, + self.out_invoice_posted.company_id.partner_id, + ) + + def test_02_onchange_in_invoice(self): + with Form(self.in_invoice) as form: + form.alternate_payer_id = self.alternate_partner + self.in_invoice = form.save() + self.assertEqual(self.in_invoice.bank_partner_id, self.alternate_partner) + + def test_02_1_post_in_invoice(self): + with Form(self.in_invoice) as form: + form.alternate_payer_id = self.alternate_partner + self.in_invoice_posted = form.save() + self.in_invoice_posted.action_post() + self.assertEqual( + self.in_invoice_posted.line_ids.filtered( + lambda r: r.display_type == "payment_term" + ).mapped("partner_id"), + self.alternate_partner, + ) + self.assertEqual( + self.in_invoice_posted.line_ids.filtered( + lambda r: r.display_type != "payment_term" + ).mapped("partner_id"), + self.out_invoice.partner_id, + ) + self.assertEqual(self.in_invoice_posted.bank_partner_id, self.alternate_partner) + + def test_03_payment_out_invoice(self): + with Form(self.out_invoice) as form: + form.alternate_payer_id = self.alternate_partner + self.out_invoice = form.save() + self.out_invoice.action_post() + records = self.out_invoice + ctx = {"active_model": records._name, "active_ids": records.ids} + payment = ( + self.env["account.payment"] + .with_context(**ctx) + .create( + { + "payment_method_id": self.payment_method_manual_out.id, + "journal_id": self.bank_journal_euro.id, + } + ) + ) + self.assertEqual(payment.partner_id, self.alternate_partner) + + def test_04_payment_in_invoice(self): + with Form(self.in_invoice) as form: + form.alternate_payer_id = self.alternate_partner + self.in_invoice = form.save() + self.in_invoice.action_post() + records = self.in_invoice + ctx = {"active_model": records._name, "active_ids": records.ids} + payment = ( + self.env["account.payment"] + .with_context(**ctx) + .create( + { + "payment_method_id": self.payment_method_manual_in.id, + "journal_id": self.bank_journal_euro.id, + } + ) + ) + self.assertEqual(payment.partner_id, self.alternate_partner) + + def test_05_payment_out_invoices(self): + with Form(self.out_invoice) as form: + form.alternate_payer_id = self.alternate_partner + self.out_invoice = form.save() + self.out_invoice.action_post() + with Form(self.out_invoice_02) as form: + form.alternate_payer_id = self.alternate_partner + self.out_invoice_02 = form.save() + self.out_invoice_02.action_post() + records = self.out_invoice | self.out_invoice_02 + ctx = {"active_model": records._name, "active_ids": records.ids} + payments = ( + self.env["account.payment"] + .with_context(**ctx) + .create( + { + "payment_method_id": self.payment_method_manual_out.id, + "journal_id": self.bank_journal_euro.id, + } + ) + ) + for payment in payments: + self.assertEqual(payment.partner_id, self.alternate_partner) + + def test_06_payment_in_invoices(self): + with Form(self.in_invoice) as form: + form.alternate_payer_id = self.alternate_partner + self.in_invoice = form.save() + self.in_invoice.action_post() + with Form(self.in_invoice_02) as form: + form.alternate_payer_id = self.alternate_partner + self.in_invoice_02 = form.save() + self.in_invoice_02.action_post() + records = self.in_invoice | self.in_invoice_02 + ctx = {"active_model": records._name, "active_ids": records.ids} + payments = ( + self.env["account.payment"] + .with_context(**ctx) + .create( + { + "payment_method_id": self.payment_method_manual_out.id, + "journal_id": self.bank_journal_euro.id, + } + ) + ) + for payment in payments: + self.assertEqual(payment.partner_id, self.alternate_partner) diff --git a/account_invoice_alternate_payer/views/account_move_views.xml b/account_invoice_alternate_payer/views/account_move_views.xml new file mode 100644 index 00000000000..1e495e994ff --- /dev/null +++ b/account_invoice_alternate_payer/views/account_move_views.xml @@ -0,0 +1,21 @@ + + + + account.move.supplier.form + account.move + + + + + + + +