From 75c6917b4eb78e656876f057658e8ea0b35d6154 Mon Sep 17 00:00:00 2001 From: "Dylan Kiss (dyki)" Date: Mon, 26 Jun 2023 15:12:22 +0000 Subject: [PATCH] [FIX] account,sale: split partner credit from sales and invoices Since 7bd93cc64b582cdd559a6b6f855c1950a992df68, we add the amounts to invoice from sales order to the credit limit warning. When looking at the credit amount on a partner from an accounting perspective, it is not correct though that sales orders to invoice are considered as well. In this change we split the credit amount from invoices and from sales orders to invoice. On the partner we only show the credit amount from invoices. In other places we add both. task-3375260 closes odoo/odoo#128158 X-original-commit: 94eae9b73995dbf2175ecee65548ace5a382f5f8 Signed-off-by: John Laterre (jol) Signed-off-by: Dylan Kiss (dyki) --- addons/account/i18n/account.pot | 50 +++++++++++++++---- addons/account/models/account_move.py | 48 +++++++++++++----- addons/account/models/partner.py | 9 ++++ addons/sale/models/res_partner.py | 8 +-- addons/sale/models/sale_order.py | 5 +- .../tests/test_sale_order_down_payment.py | 12 +++-- 6 files changed, 99 insertions(+), 33 deletions(-) diff --git a/addons/account/i18n/account.pot b/addons/account/i18n/account.pot index cef086a7d04..bc02aff6450 100644 --- a/addons/account/i18n/account.pot +++ b/addons/account/i18n/account.pot @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server saas~16.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-05-30 05:08+0000\n" -"PO-Revision-Date: 2023-05-30 05:08+0000\n" +"POT-Creation-Date: 2023-07-04 14:39+0000\n" +"PO-Revision-Date: 2023-07-04 14:39+0000\n" "Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -85,6 +85,13 @@ msgstr "" msgid "%(amount)s due %(date)s" msgstr "" +#. module: account +#. odoo-python +#: code:addons/account/models/account_move.py:0 +#, python-format +msgid "%(partner_name)s has reached its credit limit of: %(credit_limit)s" +msgstr "" + #. module: account #: model_terms:ir.ui.view,arch_db:account.account_automatic_entry_wizard_form msgid "%(" @@ -159,15 +166,6 @@ msgstr "" msgid "%s Incl. Taxes" msgstr "" -#. module: account -#. odoo-python -#: code:addons/account/models/account_move.py:0 -#, python-format -msgid "" -"%s has reached its Credit Limit of: %s\n" -"Total amount due" -msgstr "" - #. module: account #. odoo-python #: code:addons/account/models/account_move_line.py:0 @@ -13737,6 +13735,36 @@ msgstr "" msgid "Total Signed" msgstr "" +#. module: account +#. odoo-python +#: code:addons/account/models/account_move.py:0 +#, python-format +msgid "" +"Total amount due (including sales orders and this document): " +"%(total_credit)s" +msgstr "" + +#. module: account +#. odoo-python +#: code:addons/account/models/account_move.py:0 +#, python-format +msgid "Total amount due (including sales orders): %(total_credit)s" +msgstr "" + +#. module: account +#. odoo-python +#: code:addons/account/models/account_move.py:0 +#, python-format +msgid "Total amount due (including this document): %(total_credit)s" +msgstr "" + +#. module: account +#. odoo-python +#: code:addons/account/models/account_move.py:0 +#, python-format +msgid "Total amount due: %(total_credit)s" +msgstr "" + #. module: account #: model:ir.model.fields,help:account.field_account_automatic_entry_wizard__total_amount msgid "Total amount impacted by the automatic entry." diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 8a63563c8dc..63a3f6f6f2d 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -1428,31 +1428,55 @@ class AccountMove(models.Model): move.company_id.account_use_credit_limit if show_warning: move.partner_credit_warning = self._build_credit_warning_message( - move, move.partner_credit, move.tax_totals['amount_total'] > 0.0) + move, + current_amount=move.tax_totals['amount_total'], + exclude_current=True, + ) @api.depends('partner_id') def _compute_partner_credit(self): for move in self: move.partner_credit = move.partner_id.commercial_partner_id.credit - def _build_credit_warning_message(self, record, updated_credit, include): + def _build_credit_warning_message(self, record, current_amount=0.0, exclude_current=False): """ Build the warning message that will be displayed in a yellow banner on top of the current record if the partner exceeds a credit limit (set on the company or the partner itself). :param record: The record where the warning will appear (Invoice, Sales Order...). - :param updated_credit (float): The partner's updated credit limit including the current record. - :param include (bool): Whether the current record's amount is included in the warning message. + :param current_amount (float): The partner's outstanding credit amount from the current document. + :param exclude_current (bool): Whether to exclude `current_amount` from the credit to invoice. :return (str): The warning message to be showed. """ partner_id = record.partner_id.commercial_partner_id - if not partner_id.credit_limit or updated_credit <= partner_id.credit_limit: + credit_to_invoice = max(partner_id.credit_to_invoice - (current_amount if exclude_current else 0), 0) + total_credit = partner_id.credit + credit_to_invoice + current_amount + if not partner_id.credit_limit or total_credit <= partner_id.credit_limit: return '' - msg = _('%s has reached its Credit Limit of: %s\nTotal amount due', - partner_id.name, - formatLang(self.env, partner_id.credit_limit, currency_obj=record.company_id.currency_id)) - if include: - msg += _(' (including this document)') - msg += ': %s' % formatLang(self.env, updated_credit, currency_obj=record.company_id.currency_id) - return msg + msg = _( + '%(partner_name)s has reached its credit limit of: %(credit_limit)s', + partner_name=partner_id.name, + credit_limit=formatLang(self.env, partner_id.credit_limit, currency_obj=record.company_id.currency_id) + ) + total_credit_formatted = formatLang(self.env, total_credit, currency_obj=record.company_id.currency_id) + if credit_to_invoice > 0 and current_amount > 0: + return msg + '\n' + _( + 'Total amount due (including sales orders and this document): %(total_credit)s', + total_credit=total_credit_formatted + ) + elif credit_to_invoice > 0: + return msg + '\n' + _( + 'Total amount due (including sales orders): %(total_credit)s', + total_credit=total_credit_formatted + ) + elif current_amount > 0: + return msg + '\n' + _( + 'Total amount due (including this document): %(total_credit)s', + total_credit=total_credit_formatted + ) + else: + return msg + '\n' + _( + 'Total amount due: %(total_credit)s', + total_credit=total_credit_formatted + ) @api.depends('journal_id.type', 'company_id') def _compute_quick_edit_mode(self): diff --git a/addons/account/models/partner.py b/addons/account/models/partner.py index b66bd0cd7f1..e9a5a47e11c 100644 --- a/addons/account/models/partner.py +++ b/addons/account/models/partner.py @@ -358,6 +358,11 @@ class ResPartner(models.Model): remaining.debit = False remaining.credit = False + @api.depends_context('company') + def _compute_credit_to_invoice(self): + # To be overridden in Sales + self.credit_to_invoice = False + def _asset_difference_search(self, account_type, operator, operand): if operator not in ('<', '=', '>', '>=', '<='): return [] @@ -486,6 +491,10 @@ class ResPartner(models.Model): credit = fields.Monetary(compute='_credit_debit_get', search=_credit_search, string='Total Receivable', help="Total amount this customer owes you.", groups='account.group_account_invoice,account.group_account_readonly') + credit_to_invoice = fields.Monetary( + compute='_compute_credit_to_invoice', + groups='account.group_account_invoice,account.group_account_readonly' + ) credit_limit = fields.Float( string='Credit Limit', help='Credit limit specific to this partner.', groups='account.group_account_invoice,account.group_account_readonly', diff --git a/addons/sale/models/res_partner.py b/addons/sale/models/res_partner.py index f10da7aa9d5..51e2f63ca94 100644 --- a/addons/sale/models/res_partner.py +++ b/addons/sale/models/res_partner.py @@ -54,12 +54,14 @@ class ResPartner(models.Model): action["domain"] = [("partner_id", "in", all_child.ids)] return action - def _credit_debit_get(self): - super()._credit_debit_get() + def _compute_credit_to_invoice(self): + # EXTENDS 'account' + super()._compute_credit_to_invoice() domain = [('partner_id', 'in', self.ids), ('state', '=', 'sale')] group = self.env['sale.order']._read_group(domain, ['partner_id'], ['amount_to_invoice:sum']) for partner, amount_to_invoice_sum in group: - partner.credit += amount_to_invoice_sum + partner.credit_to_invoice += amount_to_invoice_sum + def unlink(self): # Unlink draft/cancelled SO so that the partner can be removed from database diff --git a/addons/sale/models/sale_order.py b/addons/sale/models/sale_order.py index 887d1a0e984..271035cf813 100644 --- a/addons/sale/models/sale_order.py +++ b/addons/sale/models/sale_order.py @@ -632,9 +632,10 @@ class SaleOrder(models.Model): show_warning = order.state in ('draft', 'sent') and \ order.company_id.account_use_credit_limit if show_warning: - updated_credit = order.partner_id.commercial_partner_id.credit + (order.amount_total * order.currency_rate) order.partner_credit_warning = self.env['account.move']._build_credit_warning_message( - order, updated_credit, order.amount_total > 0.0) + order, + current_amount=(order.amount_total * order.currency_rate), + ) @api.depends('order_line.tax_id', 'order_line.price_unit', 'amount_total', 'amount_untaxed', 'currency_id') def _compute_tax_totals(self): diff --git a/addons/sale/tests/test_sale_order_down_payment.py b/addons/sale/tests/test_sale_order_down_payment.py index e5a4c2c1a8a..fb0c6f62119 100644 --- a/addons/sale/tests/test_sale_order_down_payment.py +++ b/addons/sale/tests/test_sale_order_down_payment.py @@ -432,13 +432,15 @@ class TestSaleOrderDownPayment(TestSaleCommon): })] }) - # Check that partner_a's credit is 0.0. + # Check that partner_a's credit and credit_to_invoice is 0.0. self.assertEqual(self.partner_a.credit, 0.0) + self.assertEqual(self.partner_a.credit_to_invoice, 0.0) - # Make sure partner_a's credit includes the newly confirmed SO. + # Make sure partner_a's credit_to_invoice includes the newly confirmed SO. sale_order.action_confirm() - self.partner_a.invalidate_recordset(['credit']) - self.assertEqual(self.partner_a.credit, 1000.0) + self.partner_a.invalidate_recordset(['credit', 'credit_to_invoice']) + self.assertEqual(self.partner_a.credit, 0.0) + self.assertEqual(self.partner_a.credit_to_invoice, 1000.0) # Create a 50% down payment invoice. self.env['sale.advance.payment.inv'].with_context({ @@ -465,7 +467,7 @@ class TestSaleOrderDownPayment(TestSaleCommon): invoice.invoice_line_ids.quantity = 3 self.assertEqual( invoice.partner_credit_warning, - "partner_a has reached its Credit Limit of: $\xa01,000.00\n" + "partner_a has reached its credit limit of: $\xa01,000.00\n" "Total amount due (including this document): $\xa01,500.00" )