From 8e4158af810bcf475214946fa64d3b0ce4d3b26d Mon Sep 17 00:00:00 2001 From: oco-odoo Date: Thu, 16 Jan 2020 18:04:26 +0000 Subject: [PATCH] [IMP] account: add 'partial' and 'reversed' payment states to invoices - 'partial' payment state corresponds to invoices whose payable/receivable move line has been partially reconciled with some other line. - 'reversed' payment state corresponds to entries that have been cancelled by the creation of a single reverse entry (using the dedicated button on the form view). This state can be set on invoice as well as on regular entries. => To stay consistent with the naming conventions, this commit also renames invoice_payment_state field to payment_state, since it's no longer only applicable on invoices. closes odoo/odoo#41723 Related: odoo/enterprise#7202 Signed-off-by: Quentin De Paoli (qdp) --- addons/account/data/mail_template_data.xml | 2 +- .../models/account_journal_dashboard.py | 10 +-- addons/account/models/account_move.py | 67 ++++++++++++------- addons/account/models/account_payment.py | 2 +- .../account/report/account_invoice_report.py | 6 +- .../tests/test_account_move_in_invoice.py | 12 ++-- .../tests/test_account_move_out_invoice.py | 12 ++-- .../test_bank_statement_reconciliation.py | 2 +- addons/account/tests/test_payment.py | 18 ++--- addons/account/tests/test_reconciliation.py | 38 +++++------ addons/account/views/account_move_views.xml | 39 ++++++----- .../views/account_portal_templates.xml | 18 ++--- .../data/mail_template_data.xml | 4 +- addons/l10n_ch/views/account_invoice_view.xml | 4 +- addons/l10n_cl/views/account_move_view.xml | 2 +- addons/membership/models/membership.py | 6 +- addons/membership/models/partner.py | 2 +- .../payment/views/account_invoice_views.xml | 2 +- addons/point_of_sale/models/account_move.py | 2 +- .../product_margin/models/product_product.py | 16 ++--- ...st_anglo_saxon_valuation_reconciliation.py | 3 +- addons/sale/models/sales_team.py | 4 +- addons/sale/tests/test_sale_refund.py | 2 +- addons/sale/tests/test_sale_transaction.py | 2 +- ...st_anglo_saxon_valuation_reconciliation.py | 6 +- .../website_sale_digital/controllers/main.py | 2 +- .../models/account_invoice.py | 2 +- 27 files changed, 159 insertions(+), 126 deletions(-) diff --git a/addons/account/data/mail_template_data.xml b/addons/account/data/mail_template_data.xml index 2d30ca90879..c3fcb7f9d7d 100644 --- a/addons/account/data/mail_template_data.xml +++ b/addons/account/data/mail_template_data.xml @@ -32,7 +32,7 @@ % endif amounting in ${format_amount(object.amount_total, object.currency_id)} from ${object.company_id.name}. - % if object.invoice_payment_state == 'paid': + % if object.payment_state == 'paid': This invoice is already paid. % else: Please remit payment at your earliest convenience. diff --git a/addons/account/models/account_journal_dashboard.py b/addons/account/models/account_journal_dashboard.py index 5bc4eff5e4a..449e6f36b58 100644 --- a/addons/account/models/account_journal_dashboard.py +++ b/addons/account/models/account_journal_dashboard.py @@ -208,7 +208,7 @@ class account_journal(models.Model): FROM account_move move WHERE move.journal_id = %(journal_id)s AND move.state = 'posted' - AND move.invoice_payment_state = 'not_paid' + AND move.payment_state in ('not_paid', 'partial') AND move.type IN %(invoice_types)s ''', { 'invoice_types': tuple(self.env['account.move'].get_invoice_types(True)), @@ -251,7 +251,7 @@ class account_journal(models.Model): #TODO need to check if all invoices are in the same currency than the journal!!!! elif self.type in ['sale', 'purchase']: title = _('Bills to pay') if self.type == 'purchase' else _('Invoices owed to you') - self.env['account.move'].flush(['amount_residual', 'currency_id', 'type', 'invoice_date', 'company_id', 'journal_id', 'date', 'state', 'invoice_payment_state']) + self.env['account.move'].flush(['amount_residual', 'currency_id', 'type', 'invoice_date', 'company_id', 'journal_id', 'date', 'state', 'payment_state']) (query, query_args) = self._get_open_bills_to_pay_query() self.env.cr.execute(query, query_args) @@ -273,7 +273,7 @@ class account_journal(models.Model): WHERE journal_id = %s AND date <= %s AND state = 'posted' - AND invoice_payment_state = 'not_paid' + AND payment_state in ('not_paid', 'partial') AND type IN ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt'); ''' self.env.cr.execute(query, (self.id, today)) @@ -331,7 +331,7 @@ class account_journal(models.Model): FROM account_move move WHERE move.journal_id = %(journal_id)s AND move.state = 'posted' - AND move.invoice_payment_state = 'not_paid' + AND move.payment_state in ('not_paid', 'partial') AND move.type IN ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt'); ''', {'journal_id': self.id}) @@ -351,7 +351,7 @@ class account_journal(models.Model): FROM account_move move WHERE move.journal_id = %(journal_id)s AND move.state = 'draft' - AND move.invoice_payment_state = 'not_paid' + AND move.payment_state in ('not_paid', 'partial') AND move.type IN ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt'); ''', {'journal_id': self.id}) diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index ddf79aac7e2..bccb3c04242 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -156,6 +156,14 @@ class AccountMove(models.Model): amount_by_group = fields.Binary(string="Tax amount by group", compute='_compute_invoice_taxes_by_group', help='Edit Tax amounts if you encounter rouding issues.') + payment_state = fields.Selection(selection=[ + ('not_paid', 'Not Paid'), + ('in_payment', 'In Payment'), + ('paid', 'Paid'), + ('partial', 'Partially Paid'), + ('reversed', 'Reversed'),], + string='Payment', store=True, readonly=True, copy=False, tracking=True, + compute='_compute_amount') # ==== Cash basis feature fields ==== tax_cash_basis_rec_id = fields.Many2one( @@ -186,12 +194,6 @@ class AccountMove(models.Model): default=lambda self: self.env.user) user_id = fields.Many2one(string='User', related='invoice_user_id', help='Technical field used to fit the generic behavior in mail templates.') - invoice_payment_state = fields.Selection(selection=[ - ('not_paid', 'Not Paid'), - ('in_payment', 'In Payment'), - ('paid', 'Paid')], - string='Payment', store=True, readonly=True, copy=False, tracking=True, - compute='_compute_amount') invoice_date = fields.Date(string='Invoice/Bill Date', readonly=True, index=True, copy=False, states={'draft': [('readonly', False)]}, default=_get_default_invoice_date) @@ -945,7 +947,8 @@ class AccountMove(models.Model): 'line_ids.amount_currency', 'line_ids.amount_residual', 'line_ids.amount_residual_currency', - 'line_ids.payment_id.state') + 'line_ids.payment_id.state', + 'line_ids.full_reconcile_id') def _compute_amount(self): invoice_ids = [move.id for move in self if move.id and move.is_invoice(include_receipts=True)] self.env['account.payment'].flush(['state']) @@ -976,6 +979,7 @@ class AccountMove(models.Model): total_untaxed_currency = 0.0 total_tax = 0.0 total_tax_currency = 0.0 + total_to_pay = 0.0 total_residual = 0.0 total_residual_currency = 0.0 total = 0.0 @@ -1003,6 +1007,7 @@ class AccountMove(models.Model): total_currency += line.amount_currency elif line.account_id.user_type_id.type in ('receivable', 'payable'): # Residual amount. + total_to_pay += line.balance total_residual += line.amount_residual total_residual_currency += line.amount_residual_currency else: @@ -1025,18 +1030,30 @@ class AccountMove(models.Model): move.amount_residual_signed = total_residual currency = len(currencies) == 1 and currencies.pop() or move.company_id.currency_id - is_paid = currency and currency.is_zero(move.amount_residual) or not move.amount_residual - # Compute 'invoice_payment_state'. - if move.type == 'entry': - move.invoice_payment_state = False - elif move.state == 'posted' and is_paid: - if move.id in in_payment_set: - move.invoice_payment_state = 'in_payment' - else: - move.invoice_payment_state = 'paid' - else: - move.invoice_payment_state = 'not_paid' + # Compute 'payment_state'. + new_pmt_state = 'not_paid' if move.type != 'entry' else False + + if move.is_invoice(include_receipts=True) and move.state == 'posted': + + if currency.is_zero(move.amount_residual): + if move.id in in_payment_set: + new_pmt_state = 'in_payment' + else: + new_pmt_state = 'paid' + elif currency.compare_amounts(total_to_pay, total_residual) != 0: + new_pmt_state = 'partial' + + if new_pmt_state == 'paid' and move.type in ('in_invoice', 'out_invoice', 'entry'): + reverse_type = move.type == 'in_invoice' and 'in_refund' or move.type == 'out_invoice' and 'out_refund' or 'entry' + reverse_moves = self.env['account.move'].search([('reversed_entry_id', '=', move.id), ('state', '=', 'posted'), ('type', '=', reverse_type)]) + + # We only set 'reversed' state in cas of 1 to 1 full reconciliation with a reverse entry; otherwise, we use the regular 'paid' state + reverse_moves_full_recs = reverse_moves.mapped('line_ids.full_reconcile_id') + if reverse_moves_full_recs.mapped('reconciled_line_ids.move_id').filtered(lambda x: x not in (reverse_moves + reverse_moves_full_recs.mapped('exchange_move_id'))) == move: + new_pmt_state = 'reversed' + + move.payment_state = new_pmt_state def _inverse_amount_total(self): for move in self: @@ -1075,12 +1092,12 @@ class AccountMove(models.Model): def _compute_has_matching_suspense_amount(self): for r in self: res = False - if r.state == 'posted' and r.is_invoice() and r.invoice_payment_state == 'not_paid': + if r.state == 'posted' and r.is_invoice() and r.payment_state == 'not_paid': domain = r._get_domain_matching_suspense_moves() #there are more than one but less than 5 suspense moves matching the residual amount if (0 < self.env['account.move.line'].search_count(domain) < 5): domain2 = [ - ('invoice_payment_state', '=', 'not_paid'), + ('payment_state', '=', 'not_paid'), ('state', '=', 'posted'), ('amount_residual', '=', r.amount_residual), ('type', '=', r.type)] @@ -1168,7 +1185,7 @@ class AccountMove(models.Model): move.invoice_outstanding_credits_debits_widget = json.dumps(False) move.invoice_has_outstanding = False - if move.state != 'posted' or move.invoice_payment_state != 'not_paid' or not move.is_invoice(include_receipts=True): + if move.state != 'posted' or move.payment_state != 'not_paid' or not move.is_invoice(include_receipts=True): continue pay_term_line_ids = move.line_ids.filtered(lambda line: line.account_id.user_type_id.type in ('receivable', 'payable')) @@ -1557,12 +1574,12 @@ class AccountMove(models.Model): moves = super(AccountMove, self).create(vals_list) # Trigger 'action_invoice_paid' when the invoice is directly paid at its creation. - moves.filtered(lambda move: move.is_invoice(include_receipts=True) and move.invoice_payment_state in ('paid', 'in_payment')).action_invoice_paid() + moves.filtered(lambda move: move.is_invoice(include_receipts=True) and move.payment_state in ('paid', 'in_payment')).action_invoice_paid() return moves def write(self, vals): - not_paid_invoices = self.filtered(lambda move: move.is_invoice(include_receipts=True) and move.invoice_payment_state not in ('paid', 'in_payment')) + not_paid_invoices = self.filtered(lambda move: move.is_invoice(include_receipts=True) and move.payment_state not in ('paid', 'in_payment')) for move in self: if (move.restrict_mode_hash_table and move.state == "posted" and set(vals).intersection(INTEGRITY_HASH_MOVE_FIELDS)): @@ -1606,7 +1623,7 @@ class AccountMove(models.Model): self._check_balanced() # Trigger 'action_invoice_paid' when the invoice becomes paid after a write. - not_paid_invoices.filtered(lambda move: move.invoice_payment_state in ('paid', 'in_payment')).action_invoice_paid() + not_paid_invoices.filtered(lambda move: move.payment_state in ('paid', 'in_payment')).action_invoice_paid() return res @@ -1646,7 +1663,7 @@ class AccountMove(models.Model): if not self.is_invoice(include_receipts=True): return super(AccountMove, self)._track_subtype(init_values) - if 'invoice_payment_state' in init_values and self.invoice_payment_state == 'paid': + if 'payment_state' in init_values and self.payment_state == 'paid': return self.env.ref('account.mt_invoice_paid') elif 'state' in init_values and self.state == 'posted' and self.is_sale_document(include_receipts=True): return self.env.ref('account.mt_invoice_validated') diff --git a/addons/account/models/account_payment.py b/addons/account/models/account_payment.py index 3f267845633..53e6ce51c71 100644 --- a/addons/account/models/account_payment.py +++ b/addons/account/models/account_payment.py @@ -735,7 +735,7 @@ class payment_register(models.TransientModel): invoices = self.env['account.move'].browse(active_ids) # Check all invoices are open - if any(invoice.state != 'posted' or invoice.invoice_payment_state != 'not_paid' or not invoice.is_invoice() for invoice in invoices): + if any(invoice.state != 'posted' or invoice.payment_state not in ('not_paid', 'partial') or not invoice.is_invoice() for invoice in invoices): raise UserError(_("You can only register payments for open invoices")) # Check all invoices are inbound or all invoices are outbound outbound_list = [invoice.is_outbound() for invoice in invoices] diff --git a/addons/account/report/account_invoice_report.py b/addons/account/report/account_invoice_report.py index 1a4f443b91f..e9e960872fd 100644 --- a/addons/account/report/account_invoice_report.py +++ b/addons/account/report/account_invoice_report.py @@ -32,7 +32,7 @@ class AccountInvoiceReport(models.Model): ('posted', 'Open'), ('cancel', 'Cancelled') ], string='Invoice Status', readonly=True) - invoice_payment_state = fields.Selection(selection=[ + payment_state = fields.Selection(selection=[ ('not_paid', 'Not Paid'), ('in_payment', 'In Payment'), ('paid', 'paid') @@ -92,7 +92,7 @@ class AccountInvoiceReport(models.Model): move.partner_id, move.invoice_user_id, move.fiscal_position_id, - move.invoice_payment_state, + move.payment_state, move.invoice_date, move.invoice_date_due, move.invoice_payment_term_id, @@ -154,7 +154,7 @@ class AccountInvoiceReport(models.Model): move.partner_id, move.invoice_user_id, move.fiscal_position_id, - move.invoice_payment_state, + move.payment_state, move.invoice_date, move.invoice_date_due, move.invoice_payment_term_id, diff --git a/addons/account/tests/test_account_move_in_invoice.py b/addons/account/tests/test_account_move_in_invoice.py index a92109b40aa..2a4539ffc0a 100644 --- a/addons/account/tests/test_account_move_in_invoice.py +++ b/addons/account/tests/test_account_move_in_invoice.py @@ -1147,6 +1147,7 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon): reversal = move_reversal.reverse_moves() reverse_move = self.env['account.move'].browse(reversal['res_id']) + self.assertEqual(self.invoice.payment_state, 'not_paid', "Refunding with a draft credit note should keep the invoice 'not_paid'.") self.assertInvoiceValues(reverse_move, [ { **self.product_line_vals_1, @@ -1181,7 +1182,7 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon): 'date': move_reversal.date, 'state': 'draft', 'ref': 'Reversal of: %s, %s' % (self.invoice.name, move_reversal.reason), - 'invoice_payment_state': 'not_paid', + 'payment_state': 'not_paid', }) move_reversal = self.env['account.move.reversal'].with_context(active_model="account.move", active_ids=self.invoice.ids).create({ @@ -1192,6 +1193,7 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon): reversal = move_reversal.reverse_moves() reverse_move = self.env['account.move'].browse(reversal['res_id']) + self.assertEqual(self.invoice.payment_state, 'reversed', "After cancelling it with a reverse invoice, an invoice should be in 'reversed' state.") self.assertInvoiceValues(reverse_move, [ { **self.product_line_vals_1, @@ -1226,7 +1228,7 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon): 'date': move_reversal.date, 'state': 'posted', 'ref': 'Reversal of: %s, %s' % (self.invoice.name, move_reversal.reason), - 'invoice_payment_state': 'paid', + 'payment_state': 'paid', }) def test_in_invoice_create_refund_multi_currency(self): @@ -1249,6 +1251,7 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon): reversal = move_reversal.reverse_moves() reverse_move = self.env['account.move'].browse(reversal['res_id']) + self.assertEqual(self.invoice.payment_state, 'not_paid', "Refunding with a draft credit note should keep the invoice 'not_paid'.") self.assertInvoiceValues(reverse_move, [ { **self.product_line_vals_1, @@ -1294,7 +1297,7 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon): 'date': move_reversal.date, 'state': 'draft', 'ref': 'Reversal of: %s, %s' % (self.invoice.name, move_reversal.reason), - 'invoice_payment_state': 'not_paid', + 'payment_state': 'not_paid', }) move_reversal = self.env['account.move.reversal'].with_context(active_model="account.move", active_ids=self.invoice.ids).create({ @@ -1305,6 +1308,7 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon): reversal = move_reversal.reverse_moves() reverse_move = self.env['account.move'].browse(reversal['res_id']) + self.assertEqual(self.invoice.payment_state, 'reversed', "After cancelling it with a reverse invoice, an invoice should be in 'reversed' state.") self.assertInvoiceValues(reverse_move, [ { **self.product_line_vals_1, @@ -1350,7 +1354,7 @@ class TestAccountMoveInInvoiceOnchanges(AccountTestInvoicingCommon): 'date': move_reversal.date, 'state': 'posted', 'ref': 'Reversal of: %s, %s' % (self.invoice.name, move_reversal.reason), - 'invoice_payment_state': 'paid', + 'payment_state': 'paid', }) def test_in_invoice_create_1(self): diff --git a/addons/account/tests/test_account_move_out_invoice.py b/addons/account/tests/test_account_move_out_invoice.py index b047a3a2fd3..bd3a717185e 100644 --- a/addons/account/tests/test_account_move_out_invoice.py +++ b/addons/account/tests/test_account_move_out_invoice.py @@ -1274,6 +1274,7 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon): reversal = move_reversal.reverse_moves() reverse_move = self.env['account.move'].browse(reversal['res_id']) + self.assertEqual(self.invoice.payment_state, 'not_paid', "Refunding with a draft credit note should keep the invoice 'not_paid'.") self.assertInvoiceValues(reverse_move, [ { **self.product_line_vals_1, @@ -1309,7 +1310,7 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon): 'date': move_reversal.date, 'state': 'draft', 'ref': 'Reversal of: %s, %s' % (self.invoice.name, move_reversal.reason), - 'invoice_payment_state': 'not_paid', + 'payment_state': 'not_paid', }) move_reversal = self.env['account.move.reversal'].with_context(active_model="account.move", active_ids=self.invoice.ids).create({ @@ -1320,6 +1321,7 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon): reversal = move_reversal.reverse_moves() reverse_move = self.env['account.move'].browse(reversal['res_id']) + self.assertEqual(self.invoice.payment_state, 'reversed', "After cancelling it with a reverse invoice, an invoice should be in 'reversed' state.") self.assertInvoiceValues(reverse_move, [ { **self.product_line_vals_1, @@ -1354,7 +1356,7 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon): 'date': move_reversal.date, 'state': 'posted', 'ref': 'Reversal of: %s, %s' % (self.invoice.name, move_reversal.reason), - 'invoice_payment_state': 'paid', + 'payment_state': 'paid', }) def test_out_invoice_create_refund_multi_currency(self): @@ -1377,6 +1379,7 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon): reversal = move_reversal.reverse_moves() reverse_move = self.env['account.move'].browse(reversal['res_id']) + self.assertEqual(self.invoice.payment_state, 'not_paid', "Refunding with a draft credit note should keep the invoice 'not_paid'.") self.assertInvoiceValues(reverse_move, [ { **self.product_line_vals_1, @@ -1422,7 +1425,7 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon): 'date': move_reversal.date, 'state': 'draft', 'ref': 'Reversal of: %s, %s' % (self.invoice.name, move_reversal.reason), - 'invoice_payment_state': 'not_paid', + 'payment_state': 'not_paid', }) move_reversal = self.env['account.move.reversal'].with_context(active_model="account.move", active_ids=self.invoice.ids).create({ @@ -1433,6 +1436,7 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon): reversal = move_reversal.reverse_moves() reverse_move = self.env['account.move'].browse(reversal['res_id']) + self.assertEqual(self.invoice.payment_state, 'reversed', "After cancelling it with a reverse invoice, an invoice should be in 'reversed' state.") self.assertInvoiceValues(reverse_move, [ { **self.product_line_vals_1, @@ -1478,7 +1482,7 @@ class TestAccountMoveOutInvoiceOnchanges(AccountTestInvoicingCommon): 'date': move_reversal.date, 'state': 'posted', 'ref': 'Reversal of: %s, %s' % (self.invoice.name, move_reversal.reason), - 'invoice_payment_state': 'paid', + 'payment_state': 'paid', }) def test_out_invoice_create_1(self): diff --git a/addons/account/tests/test_bank_statement_reconciliation.py b/addons/account/tests/test_bank_statement_reconciliation.py index 37590f44646..a6782594c07 100644 --- a/addons/account/tests/test_bank_statement_reconciliation.py +++ b/addons/account/tests/test_bank_statement_reconciliation.py @@ -49,7 +49,7 @@ class TestBankStatementReconciliation(AccountTestCommon): self.assertTrue(rcv_mv_line.reconciled) self.assertTrue(counterpart_mv_line.reconciled) self.assertEqual(counterpart_mv_line.matched_credit_ids, rcv_mv_line.matched_debit_ids) - self.assertEqual(rcv_mv_line.move_id.invoice_payment_state, 'paid', "The related invoice's state should now be 'paid'") + self.assertEqual(rcv_mv_line.move_id.payment_state, 'paid', "The related invoice's state should now be 'paid'") def test_reconcile_with_write_off(self): pass diff --git a/addons/account/tests/test_payment.py b/addons/account/tests/test_payment.py index bfc85405b83..1c9da598ffd 100644 --- a/addons/account/tests/test_payment.py +++ b/addons/account/tests/test_payment.py @@ -122,7 +122,7 @@ class TestPayment(AccountTestCommon): self.assertAlmostEqual(payment.amount, 300) self.assertEqual(payment.state, 'posted') self.assertEqual(payment.state, 'posted') - self.assertEqual(inv_1.invoice_payment_state, 'paid') + self.assertEqual(inv_1.payment_state, 'paid') rec_line = payment.move_line_ids.filtered(lambda line: line.account_id.user_type_id.type in ('receivable', 'payable')) self.assertRecordValues(payment.move_line_ids.sorted('credit'), [ @@ -249,7 +249,7 @@ class TestPayment(AccountTestCommon): {'account_id': self.account_payable.id, 'debit': 8.65, 'credit': 0.0, 'amount_currency': 13.22, 'currency_id': self.currency_usd_id}, ]) self.assertTrue(receivable_line.full_reconcile_id) - self.assertEqual(invoice.invoice_payment_state, 'paid') + self.assertEqual(invoice.payment_state, 'paid') # Use case: # Company is in EUR, create a vendor bill for 25 EUR and register payment of 25 USD. @@ -277,7 +277,7 @@ class TestPayment(AccountTestCommon): {'account_id': self.account_receivable.id, 'debit': 0.0, 'credit': 8.65, 'amount_currency': -13.22, 'currency_id': self.currency_usd_id}, ]) self.assertTrue(payable_line.full_reconcile_id) - self.assertEqual(invoice.invoice_payment_state, 'paid') + self.assertEqual(invoice.payment_state, 'paid') def test_payment_and_writeoff_out_refund(self): # Use case: @@ -304,7 +304,7 @@ class TestPayment(AccountTestCommon): {'account_id': self.account_eur.id, 'debit': 0.0, 'credit': 90.0, 'amount_currency': 0.0, 'currency_id': False}, {'account_id': self.account_payable.id, 'debit': 0.0, 'credit': 10.0, 'amount_currency': 0.0, 'currency_id': False}, ]) - self.assertEqual(invoice.invoice_payment_state, 'paid') + self.assertEqual(invoice.payment_state, 'paid') def test_payment_and_writeoff_in_other_currency_2(self): # Use case: @@ -351,7 +351,7 @@ class TestPayment(AccountTestCommon): ]) #check the invoice status - self.assertEqual(invoice.invoice_payment_state, 'paid') + self.assertEqual(invoice.payment_state, 'paid') def test_payment_and_writeoff_in_other_currency_3(self): # Use case related in revision 20935462a0cabeb45480ce70114ff2f4e91eaf79 @@ -397,7 +397,7 @@ class TestPayment(AccountTestCommon): # Check the invoice status and the full reconciliation: the difference on the receivable account # should have been completed by an exchange rate difference entry - self.assertEqual(invoice.invoice_payment_state, 'paid') + self.assertEqual(invoice.payment_state, 'paid') self.assertTrue(receivable_line.full_reconcile_id) def test_post_at_bank_reconciliation_payment(self): @@ -441,7 +441,7 @@ class TestPayment(AccountTestCommon): invoice = self.create_invoice(amount=53, partner=self.partner_agrolait.id) (payment_one.move_line_ids + payment_two.move_line_ids + invoice.line_ids).filtered(lambda x: x.account_id.user_type_id.type == 'receivable').reconcile() - self.assertEqual(invoice.invoice_payment_state, 'in_payment', "Invoice should be in 'in payment' state") + self.assertEqual(invoice.payment_state, 'in_payment', "Invoice should be in 'in payment' state") # Match the first payment with a bank statement line bank_statement_one = self.reconcile(payment_one.move_line_ids.filtered(lambda x: x.account_id.user_type_id.type == 'liquidity'), 42) @@ -450,7 +450,7 @@ class TestPayment(AccountTestCommon): self.assertEqual(payment_one.mapped('move_line_ids.move_id.state'), ['posted'], "After bank reconciliation, payment one's account.move should be posted.") self.assertEqual(payment_one.mapped('move_line_ids.move_id.date'), stmt_line_date_one, "After bank reconciliation, payment one's account.move should share the same date as the bank statement.") self.assertEqual([payment_one.payment_date], stmt_line_date_one, "After bank reconciliation, payment one should share the same date as the bank statement.") - self.assertEqual(invoice.invoice_payment_state, 'in_payment', "The invoice should still be 'in payment', not all its payments are reconciled with a statement") + self.assertEqual(invoice.payment_state, 'in_payment', "The invoice should still be 'in payment', not all its payments are reconciled with a statement") # Match the second payment with a bank statement line bank_statement_two = self.reconcile(payment_two.move_line_ids.filtered(lambda x: x.account_id.user_type_id.type == 'liquidity'), 42) @@ -461,7 +461,7 @@ class TestPayment(AccountTestCommon): self.assertEqual([payment_two.payment_date], stmt_line_date_two, "After bank reconciliation, payment two should share the same date as the bank statement.") # The invoice should now be paid - self.assertEqual(invoice.invoice_payment_state, 'paid', "Invoice should be in 'paid' state after having reconciled the two payments with a bank statement") + self.assertEqual(invoice.payment_state, 'paid', "Invoice should be in 'paid' state after having reconciled the two payments with a bank statement") def test_payment_draft_keep_name(self): payment = self.payment_model.create({ diff --git a/addons/account/tests/test_reconciliation.py b/addons/account/tests/test_reconciliation.py index e0959e39d97..8d5c5500416 100644 --- a/addons/account/tests/test_reconciliation.py +++ b/addons/account/tests/test_reconciliation.py @@ -179,7 +179,7 @@ class TestReconciliationExec(TestAccountReconciliationCommon): ]) # The invoice should be paid, as the payments totally cover its total - self.assertEqual(move.invoice_payment_state, 'paid', 'The invoice should be paid by now') + self.assertEqual(move.payment_state, 'paid', 'The invoice should be paid by now') self.assertTrue(receivable_line.reconciled, 'The invoice should be totally reconciled') self.assertTrue(receivable_line.full_reconcile_id, 'The invoice should have a full reconcile number') self.assertEqual(receivable_line.amount_residual, 0, 'The invoice should be totally reconciled') @@ -772,7 +772,7 @@ class TestReconciliationExec(TestAccountReconciliationCommon): credit_aml = payment.move_line_ids.filtered('credit') inv.js_assign_outstanding_line(credit_aml.id) - self.assertTrue(inv.invoice_payment_state == 'paid', 'The invoice should be paid') + self.assertTrue(inv.payment_state == 'paid', 'The invoice should be paid') exchange_reconcile = payment.move_line_ids.mapped('full_reconcile_id') exchange_move = exchange_reconcile.exchange_move_id @@ -853,9 +853,9 @@ class TestReconciliationExec(TestAccountReconciliationCommon): 'invoice_ids': [(6, 0, invoice_cust_1.ids)], }) payment.post() - # We expect at this point that the invoice should still be open, + # We expect at this point that the invoice should still be open, in 'partial' state, # because they owe us still 50 CC. - self.assertEqual(invoice_cust_1.invoice_payment_state, 'not_paid', 'Invoice is in status %s' % invoice_cust_1.state) + self.assertEqual(invoice_cust_1.payment_state, 'partial', 'Invoice is in status %s' % invoice_cust_1.state) def test_multiple_term_reconciliation_opw_1906665(self): '''Test that when registering a payment to an invoice with multiple @@ -1686,7 +1686,7 @@ class TestReconciliationExec(TestAccountReconciliationCommon): # Second Payment payment1 = self.make_payment(invoice, journal, 0.01) self.assertEqual(invoice.amount_residual, 0) - self.assertEqual(invoice.invoice_payment_state, 'paid') + self.assertEqual(invoice.payment_state, 'paid') self.assertTrue(tax_waiting_line.reconciled) move_caba1 = tax_waiting_line.matched_debit_ids.mapped('debit_move_id').mapped('move_id').filtered(lambda m: m != move_caba0) @@ -1816,8 +1816,8 @@ class TestReconciliationExec(TestAccountReconciliationCommon): self.assertEqual(inv1_receivable.full_reconcile_id, pay_receivable.full_reconcile_id) self.assertEqual(inv1_receivable.full_reconcile_id, move_balance_receiv.full_reconcile_id) - self.assertEqual(inv1.invoice_payment_state, 'paid') - self.assertEqual(inv2.invoice_payment_state, 'paid') + self.assertEqual(inv1.payment_state, 'paid') + self.assertEqual(inv2.payment_state, 'paid') def test_inv_refund_foreign_payment_writeoff_domestic3(self): """ @@ -1888,8 +1888,8 @@ class TestReconciliationExec(TestAccountReconciliationCommon): exchange_rcv = inv1_receivable.full_reconcile_id.exchange_move_id.line_ids.filtered(lambda l: l.account_id.internal_type == 'receivable') self.assertEqual(exchange_rcv.amount_currency, 0.01) - self.assertEqual(inv1.invoice_payment_state, 'paid') - self.assertEqual(inv2.invoice_payment_state, 'paid') + self.assertEqual(inv1.payment_state, 'paid') + self.assertEqual(inv2.payment_state, 'paid') def test_inv_refund_foreign_payment_writeoff_domestic4(self): """ @@ -1969,8 +1969,8 @@ class TestReconciliationExec(TestAccountReconciliationCommon): self.assertFalse(inv1_receivable.full_reconcile_id.exchange_move_id) - self.assertEqual(inv1.invoice_payment_state, 'paid') - self.assertEqual(inv2.invoice_payment_state, 'paid') + self.assertEqual(inv1.payment_state, 'paid') + self.assertEqual(inv2.payment_state, 'paid') def test_inv_refund_foreign_payment_writeoff_domestic5(self): """ @@ -2037,8 +2037,8 @@ class TestReconciliationExec(TestAccountReconciliationCommon): self.assertFalse(inv1_receivable.full_reconcile_id.exchange_move_id) - self.assertEqual(inv1.invoice_payment_state, 'paid') - self.assertEqual(inv2.invoice_payment_state, 'paid') + self.assertEqual(inv1.payment_state, 'paid') + self.assertEqual(inv2.payment_state, 'paid') def test_inv_refund_foreign_payment_writeoff_domestic6(self): """ @@ -2101,8 +2101,8 @@ class TestReconciliationExec(TestAccountReconciliationCommon): self.assertFalse(inv1_receivable.full_reconcile_id.exchange_move_id) - self.assertEqual(inv1.invoice_payment_state, 'paid') - self.assertEqual(inv2.invoice_payment_state, 'paid') + self.assertEqual(inv1.payment_state, 'paid') + self.assertEqual(inv2.payment_state, 'paid') def test_inv_refund_foreign_payment_writeoff_domestic6bis(self): """ @@ -2176,8 +2176,8 @@ class TestReconciliationExec(TestAccountReconciliationCommon): self.assertFalse(inv1_receivable.full_reconcile_id.exchange_move_id) - self.assertEqual(inv1.invoice_payment_state, 'paid') - self.assertEqual(inv2.invoice_payment_state, 'paid') + self.assertEqual(inv1.payment_state, 'paid') + self.assertEqual(inv2.payment_state, 'paid') def test_inv_refund_foreign_payment_writeoff_domestic7(self): """ @@ -2235,7 +2235,7 @@ class TestReconciliationExec(TestAccountReconciliationCommon): self.assertFalse(inv1_receivable.full_reconcile_id.exchange_move_id) - self.assertEqual(inv1.invoice_payment_state, 'paid') + self.assertEqual(inv1.payment_state, 'paid') def test_inv_refund_foreign_payment_writeoff_domestic8(self): """ @@ -2298,4 +2298,4 @@ class TestReconciliationExec(TestAccountReconciliationCommon): self.assertFalse(inv1_receivable.full_reconcile_id.exchange_move_id) - self.assertEqual(inv1.invoice_payment_state, 'paid') + self.assertEqual(inv1.payment_state, 'paid') diff --git a/addons/account/views/account_move_views.xml b/addons/account/views/account_move_views.xml index 342b035523d..79f67ea7d8e 100644 --- a/addons/account/views/account_move_views.xml +++ b/addons/account/views/account_move_views.xml @@ -399,9 +399,9 @@ - + - + @@ -410,8 +410,8 @@ - - + + @@ -485,7 +485,7 @@