diff options
| author | oco-odoo <oco@odoo.com> | 2020-01-16 18:04:26 +0000 |
|---|---|---|
| committer | oco-odoo <oco@odoo.com> | 2020-01-16 18:04:26 +0000 |
| commit | 8e4158af810bcf475214946fa64d3b0ce4d3b26d (patch) | |
| tree | bee0b38b9179a025c0f23d2ed1b445a647bbf986 /addons | |
| parent | 1a7c21328f551ee0ec904606209d642a84071446 (diff) | |
[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) <qdp@openerp.com>
Diffstat (limited to 'addons')
27 files changed, 160 insertions, 127 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 <strong>${format_amount(object.amount_total, object.currency_id)}</strong> 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 @@ <field name="invoice_partner_display_name" invisible="context.get('default_type') not in ('out_invoice', 'out_refund','out_receipt')" groups="base.group_user" string="Customer" /> <field name="invoice_date" optional="show" invisible="context.get('default_type') not in ('in_invoice', 'in_refund','in_receipt')" string="Bill Date"/> <field name="invoice_date" optional="show" invisible="context.get('default_type') not in ('out_invoice', 'out_refund','out_receipt')" string="Invoice Date"/> - <field name="invoice_origin" optional="show" string="Source Document"/> + <field name="invoice_origin" optional="hide" string="Source Document"/> <field name="invoice_payment_ref" optional="hide" invisible="context.get('default_type') in ('out_invoice', 'out_refund','out_receipt')"/> - <field name="invoice_user_id" optional="show" invisible="context.get('default_type') not in ('out_invoice', 'out_refund','out_receipt')" string="Sales Person"/> + <field name="invoice_user_id" optional="hide" invisible="context.get('default_type') not in ('out_invoice', 'out_refund','out_receipt')" string="Sales Person"/> <field name="company_id" groups="base.group_multi_company" options="{'no_create': True}" optional="show"/> <field name="invoice_date_due" optional="show"/> <field name="amount_untaxed_signed" string="Tax Excluded" sum="Total" optional="show"/> @@ -410,8 +410,8 @@ <field name="amount_residual_signed" string="Amount Due" sum="Amount Due" optional="show"/> <field name="currency_id" invisible="1"/> <field name="company_currency_id" invisible="1"/> - <field name="state" optional="show"/> - <field name="invoice_payment_state" optional="hide"/> + <field name="state" optional="hide"/> + <field name="payment_state"/> <field name="type" invisible="context.get('default_type', True)"/> </tree> </field> @@ -485,7 +485,7 @@ <!-- Register Payment (only invoices / receipts) --> <button name="action_invoice_register_payment" id="account_invoice_payment_btn" type="object" class="oe_highlight" - attrs="{'invisible': ['|', '|', ('state', '!=', 'posted'), ('invoice_payment_state', '!=', 'not_paid'), ('type', 'not in', ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt'))]}" + attrs="{'invisible': ['|', '|', ('state', '!=', 'posted'), ('payment_state', 'not in', ('not_paid', 'partial')), ('type', 'not in', ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt'))]}" string="Register Payment" groups="account.group_account_invoice"/> <!-- Preview (only customer invoices) --> @@ -494,7 +494,7 @@ <!-- Reverse --> <button name="%(action_view_account_move_reversal)d" string="Reverse Entry" type="action" groups="account.group_account_invoice" - attrs="{'invisible': ['|', ('type', '!=', 'entry'), ('state', '!=', 'posted')]}"/> + attrs="{'invisible': ['|', ('type', '!=', 'entry'), '|', ('state', '!=', 'posted'), ('payment_state', '=', 'reversed')]}"/> <button name="action_reverse" string='Add Credit Note' type='object' groups="account.group_account_invoice" attrs="{'invisible': ['|', ('type', 'not in', ('out_invoice', 'in_invoice')), ('state', '!=', 'posted')]}"/> @@ -516,12 +516,12 @@ </div> <div groups="account.group_account_invoice" class="alert alert-info" role="alert" style="margin-bottom:0px;" - attrs="{'invisible': ['|', '|', ('type', 'not in', ('out_invoice', 'out_refund')), ('invoice_has_outstanding', '=', False), ('invoice_payment_state', '!=', 'not_paid')]}"> + attrs="{'invisible': ['|', '|', ('type', 'not in', ('out_invoice', 'out_refund')), ('invoice_has_outstanding', '=', False), ('payment_state', 'not in', ('not_paid', 'partial'))]}"> You have <bold><a class="alert-link" href="#outstanding" role="button">outstanding payments</a></bold> for this customer. You can allocate them to mark this invoice as paid. </div> <div groups="account.group_account_invoice" class="alert alert-info" role="alert" style="margin-bottom:0px;" - attrs="{'invisible': ['|', '|', ('type', 'not in', ('in_invoice', 'in_refund')), ('invoice_has_outstanding', '=', False), ('invoice_payment_state', '!=', 'not_paid')]}"> + attrs="{'invisible': ['|', '|', ('type', 'not in', ('in_invoice', 'in_refund')), ('invoice_has_outstanding', '=', False), ('payment_state', 'not in', ('not_paid', 'partial'))]}"> You have <bold><a class="alert-link" href="#outstanding" role="button">outstanding debits</a></bold> for this supplier. You can allocate them to mark this bill as paid. </div> <sheet> @@ -537,14 +537,19 @@ <!-- Payment status for invoices / receipts --> <widget name="web_ribbon" title="Paid" - attrs="{'invisible': ['|', ('invoice_payment_state', '!=', 'paid'), ('type', 'not in', ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt'))]}"/> + attrs="{'invisible': ['|', ('payment_state', '!=', 'paid'), ('type', 'not in', ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt'))]}"/> <widget name="web_ribbon" title="In Payment" - attrs="{'invisible': ['|', ('invoice_payment_state', '!=', 'in_payment'), ('type', 'not in', ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt'))]}"/> + attrs="{'invisible': ['|', ('payment_state', '!=', 'in_payment'), ('type', 'not in', ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt'))]}"/> + <widget name="web_ribbon" title="Partial" + attrs="{'invisible': ['|', ('payment_state', '!=', 'partial'), ('type', 'not in', ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt'))]}"/> + <widget name="web_ribbon" title="Reversed" + bg_color="bg-danger" + attrs="{'invisible': [('payment_state', '!=', 'reversed')]}"/> <!-- Invisible fields --> <field name="id" invisible="1"/> <field name="type" invisible="1"/> - <field name="invoice_payment_state" invisible="1" force_save="1"/> + <field name="payment_state" invisible="1" force_save="1"/> <field name="invoice_filter_type_domain" invisible="1"/> <field name="company_currency_id" invisible="1"/> <field name="commercial_partner_id" invisible="1"/> @@ -1038,6 +1043,8 @@ <filter string="Unposted" name="unposted" domain="[('state', '=', 'draft')]" help="Unposted Journal Entries"/> <filter string="Posted" name="posted" domain="[('state', '=', 'posted')]" help="Posted Journal Entries"/> <separator/> + <filter string="Reversed" name="reversed" domain="[('payment_state', '=', 'reversed')]"/> + <separator/> <filter string="To Check" name="to_check" domain="[('to_check', '=', True)]"/> <separator/> <filter string="Sales" name="sales" domain="[('journal_id.type', '=', 'sale')]" context="{'default_journal_type': 'sale'}"/> @@ -1077,13 +1084,15 @@ <filter domain="[('invoice_user_id', '=', uid)]" name="myinvoices" help="My Invoices"/> <separator/> <filter name="draft" string="Draft" domain="[('state','=','draft')]"/> - <filter name="unpaid" string="Posted" domain="[('state', '=', 'posted'), ('invoice_payment_state', '=', 'not_paid')]"/> + <filter name="unpaid" string="Posted" domain="[('state', '=', 'posted'), ('payment_state', 'in', ('not_paid', 'partial'))]"/> <separator/> <filter string="To Check" name="to_check" domain="[('to_check', '=', True)]"/> <separator/> - <filter name="in_payment" string="In Payment" domain="[('invoice_payment_state', '=', 'in_payment')]"/> - <filter name="paid" string="Paid" domain="[('invoice_payment_state', '=', 'paid')]"/> - <filter name="late" string="Overdue" domain="['&', ('invoice_date_due', '<', time.strftime('%%Y-%%m-%%d')), ('state', '=', 'posted'), ('invoice_payment_state', '=', 'not_paid')]" help="Overdue invoices, maturity date passed"/> + <filter name="in_payment" string="In Payment" domain="[('payment_state', '=', 'in_payment')]"/> + <filter name="partial" string="Partially Paid" domain="[('payment_state', '=', 'partial')]"/> + <filter name="paid" string="Paid" domain="[('payment_state', '=', 'paid')]"/> + <filter name="reversed" string="Reversed" domain="[('payment_state', '=', 'reversed')]"/> + <filter name="late" string="Overdue" domain="['&', ('invoice_date_due', '<', time.strftime('%%Y-%%m-%%d')), ('state', '=', 'posted'), ('payment_state', 'in', ('not_paid', 'partial'))]" help="Overdue invoices, maturity date passed"/> <separator/> <filter name="invoice_date" string="Invoice Date" date="invoice_date"/> <filter name="due_date" string="Due Date" date="invoice_date_due"/> diff --git a/addons/account_payment/views/account_portal_templates.xml b/addons/account_payment/views/account_portal_templates.xml index 0300c85645b..e30c5e61394 100644 --- a/addons/account_payment/views/account_portal_templates.xml +++ b/addons/account_payment/views/account_portal_templates.xml @@ -7,7 +7,7 @@ <td class="text-center"> <t t-set="tx_ids" t-value="invoice.transaction_ids.filtered(lambda tx: tx.state in ('pending', 'authorized', 'done'))"/> <t t-set="pending_manual_txs" t-value="tx_ids.filtered(lambda tx: tx.state == 'pending' and tx.acquirer_id.provider in ('transfer', 'manual'))"/> - <a t-if="invoice.state == 'posted' and invoice.invoice_payment_state == 'not_paid' and invoice.amount_total and invoice.type == 'out_invoice' and (pending_manual_txs or not tx_ids)" + <a t-if="invoice.state == 'posted' and invoice.payment_state in ('not_paid', 'partial') and invoice.amount_total and invoice.type == 'out_invoice' and (pending_manual_txs or not tx_ids)" t-att-href="invoice.get_portal_url(anchor='portal_pay')" title="Pay Now" aria-label="Pay now" class="btn btn-sm btn-primary" role="button"> <i class="fa fa-arrow-circle-right"/><span class='d-none d-md-inline'> Pay Now</span> </a> @@ -16,7 +16,7 @@ <xpath expr="//t[@t-foreach='invoices']/tr/td[hasclass('tx_status')]" position="replace"> <t t-set="last_tx" t-value="invoice.get_portal_last_transaction()"/> <td class="tx_status text-center"> - <t t-if="invoice.state == 'posted' and invoice.invoice_payment_state == 'not_paid' and (last_tx.state not in ['pending', 'authorized', 'done', 'cancel'] or (last_tx.state == 'pending' and last_tx.acquirer_id.provider in ('transfer', 'manual')))"> + <t t-if="invoice.state == 'posted' and invoice.payment_state in ('not_paid', 'partial') and (last_tx.state not in ['pending', 'authorized', 'done', 'cancel'] or (last_tx.state == 'pending' and last_tx.acquirer_id.provider in ('transfer', 'manual')))"> <span class="badge badge-pill badge-info"><i class="fa fa-fw fa-clock-o"></i><span class="d-none d-md-inline"> Waiting for Payment</span></span> </t> <t t-if="invoice.state == 'posted' and last_tx.state == 'authorized'"> @@ -25,7 +25,7 @@ <t t-if="invoice.state == 'posted' and last_tx.state == 'pending' and last_tx.acquirer_id.provider not in ('transfer', 'manual')"> <span class="badge badge-pill badge-warning"><span class="d-none d-md-inline"> Pending</span></span> </t> - <t t-if="invoice.state == 'posted' and invoice.invoice_payment_state == 'paid' or last_tx.state == 'done'"> + <t t-if="invoice.state == 'posted' and invoice.payment_state == 'paid' or last_tx.state == 'done'"> <span class="badge badge-pill badge-success"><i class="fa fa-fw fa-check"></i><span class="d-none d-md-inline"> Paid</span></span> </t> <t t-if="invoice.state == 'cancel'"> @@ -36,7 +36,7 @@ </template> <template id="portal_invoice_payment" name="Invoice Payment"> - <div class="row" t-if="not tx_ids and invoice.state == 'posted' and invoice.invoice_payment_state == 'not_paid' and invoice.amount_total" id="portal_pay"> + <div class="row" t-if="not tx_ids and invoice.state == 'posted' and invoice.payment_state in ('not_paid', 'partial') and invoice.amount_total" id="portal_pay"> <div class="modal fade" id="pay_with" role="dialog"> <div class="modal-dialog"> <div class="modal-content"> @@ -71,14 +71,14 @@ <t t-set="tx_ids" t-value="invoice.transaction_ids.filtered(lambda tx: tx.state in ('pending', 'authorized', 'done'))"/> <t t-set="pending_manual_txs" t-value="tx_ids.filtered(lambda tx: tx.state == 'pending' and tx.acquirer_id.provider in ('transfer', 'manual'))"/> <div> - <a href="#" t-if="invoice.state == 'posted' and invoice.invoice_payment_state == 'not_paid' and invoice.amount_total and invoice.type == 'out_invoice' and (pending_manual_txs or not tx_ids)" + <a href="#" t-if="invoice.state == 'posted' and invoice.payment_state in ('not_paid', 'partial') and invoice.amount_total and invoice.type == 'out_invoice' and (pending_manual_txs or not tx_ids)" class="btn btn-primary btn-block mb-2" data-toggle="modal" data-target="#pay_with"> <i class="fa fa-fw fa-arrow-circle-right"/> Pay Now </a> - <div t-if="tx_ids and not pending_manual_txs and invoice.invoice_payment_state != 'paid'" class="alert alert-info py-1 mb-2" > + <div t-if="tx_ids and not pending_manual_txs and invoice.payment_state != 'paid'" class="alert alert-info py-1 mb-2" > <i class="fa fa-fw fa-check-circle"/> Pending </div> - <div t-if="invoice.invoice_payment_state == 'paid'" class="alert alert-success py-1 mb-2" > + <div t-if="invoice.payment_state == 'paid'" class="alert alert-success py-1 mb-2" > <i class="fa fa-fw fa-check-circle"/> Paid </div> </div> @@ -91,7 +91,7 @@ </t> </div> <t t-set="tx_ids" t-value="invoice.transaction_ids.filtered(lambda tx: tx.state in ('authorized', 'done'))"/> - <div t-if="not tx_ids and invoice.state == 'posted' and invoice.invoice_payment_state == 'not_paid' and invoice.amount_total" id="portal_pay"> + <div t-if="not tx_ids and invoice.state == 'posted' and invoice.payment_state in ('not_paid', 'partial') and invoice.amount_total" id="portal_pay"> <div t-if="pms or acquirers" id="payment_method"> <t t-call="account_payment.portal_invoice_payment"/> </div> @@ -136,7 +136,7 @@ <b>Communication: </b><span t-esc='invoice.ref'/> </div> </t> - <t t-if="success == 'pay_invoice' and invoice.invoice_payment_state == 'paid'"> + <t t-if="success == 'pay_invoice' and invoice.payment_state == 'paid'"> Done, your online payment has been successfully processed. Thank you for your order. </t> </xpath> diff --git a/addons/l10n_be_invoice_bba/data/mail_template_data.xml b/addons/l10n_be_invoice_bba/data/mail_template_data.xml index 66aa134c8e4..1599a31d186 100644 --- a/addons/l10n_be_invoice_bba/data/mail_template_data.xml +++ b/addons/l10n_be_invoice_bba/data/mail_template_data.xml @@ -10,7 +10,7 @@ (${object.partner_id.parent_id.name}) % endif <br /><br /> - Here is, in attachment, your + Here is, in attachment, your % if object.name: invoice <strong>${object.name}</strong> % else: @@ -21,7 +21,7 @@ % endif amounting in <strong>${format_amount(object.amount_total, object.currency_id)}</strong> 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/l10n_ch/views/account_invoice_view.xml b/addons/l10n_ch/views/account_invoice_view.xml index d86c8e8bece..115a546fd98 100644 --- a/addons/l10n_ch/views/account_invoice_view.xml +++ b/addons/l10n_ch/views/account_invoice_view.xml @@ -20,7 +20,7 @@ ('type', 'not in', ('out_invoice', 'out_refund')), ('l10n_ch_isr_sent', '=', True), ('state', '!=', 'posted'), - ('invoice_payment_state', '!=', 'not_paid'), + ('payment_state', 'not in', ('not_paid', 'partial')), ('l10n_ch_currency_name', 'not in', ['EUR', 'CHF'])]}" class="oe_highlight" groups="base.group_user" @@ -36,7 +36,7 @@ ('type', 'not in', ('out_invoice', 'out_refund')), ('l10n_ch_isr_sent', '=', False), ('state', '!=', 'posted'), - ('invoice_payment_state', '!=', 'not_paid'), + ('payment_state', 'not in', ('not_paid', 'partial')), ('l10n_ch_currency_name', 'not in', ['EUR', 'CHF'])]}" groups="base.group_user" /> diff --git a/addons/l10n_cl/views/account_move_view.xml b/addons/l10n_cl/views/account_move_view.xml index 685d8cbde60..6266b080f17 100644 --- a/addons/l10n_cl/views/account_move_view.xml +++ b/addons/l10n_cl/views/account_move_view.xml @@ -38,7 +38,7 @@ <field name="currency_id" invisible="1"/> <field name="company_currency_id" invisible="1"/> <field name="state" optional="show"/> - <field name="invoice_payment_state" optional="hide"/> + <field name="payment_state" optional="hide"/> <field name="type" invisible="context.get('default_type', True)"/> </tree> </field> diff --git a/addons/membership/models/membership.py b/addons/membership/models/membership.py index fede4462625..bdfa9a201a4 100644 --- a/addons/membership/models/membership.py +++ b/addons/membership/models/membership.py @@ -44,7 +44,7 @@ class MembershipLine(models.Model): @api.depends('account_invoice_id.state', 'account_invoice_id.amount_residual', - 'account_invoice_id.invoice_payment_state') + 'account_invoice_id.payment_state') def _compute_state(self): """Compute the state lines """ if not self: @@ -59,7 +59,7 @@ class MembershipLine(models.Model): reverse_map = dict(self._cr.fetchall()) for line in self: move_state = line.account_invoice_id.state - payment_state = line.account_invoice_id.invoice_payment_state + payment_state = line.account_invoice_id.payment_state line.state = 'none' if move_state == 'draft': @@ -72,7 +72,7 @@ class MembershipLine(models.Model): line.state = 'paid' elif payment_state == 'in_payment': line.state = 'paid' - elif payment_state == 'not_paid': + elif payment_state in ('not_paid', 'partial'): line.state = 'invoiced' elif move_state == 'cancel': line.state = 'canceled' diff --git a/addons/membership/models/partner.py b/addons/membership/models/partner.py index 6d1f6dd0de5..3078a39bc88 100644 --- a/addons/membership/models/partner.py +++ b/addons/membership/models/partner.py @@ -39,7 +39,7 @@ class Partner(models.Model): @api.depends('member_lines.account_invoice_line', 'member_lines.account_invoice_line.move_id.state', - 'member_lines.account_invoice_line.move_id.invoice_payment_state', + 'member_lines.account_invoice_line.move_id.payment_state', 'member_lines.account_invoice_line.move_id.partner_id', 'free_member', 'member_lines.date_to', 'member_lines.date_from', diff --git a/addons/payment/views/account_invoice_views.xml b/addons/payment/views/account_invoice_views.xml index 7a224ce2e92..1253e0382a7 100644 --- a/addons/payment/views/account_invoice_views.xml +++ b/addons/payment/views/account_invoice_views.xml @@ -10,7 +10,7 @@ The user must capture/void the authorized transactions before registering a new payment. --> <xpath expr="//button[@id='account_invoice_payment_btn']" position="attributes"> - <attribute name="attrs">{'invisible': ['|', '|', '|', ('state', '!=', 'posted'), ('invoice_payment_state', '!=', 'not_paid'), ('type', 'not in', ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt')), ('authorized_transaction_ids', '!=', [])]}</attribute> + <attribute name="attrs">{'invisible': ['|', '|', '|', ('state', '!=', 'posted'), ('payment_state', 'not in', ('partial', 'not_paid')), ('type', 'not in', ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt')), ('authorized_transaction_ids', '!=', [])]}</attribute> </xpath> <xpath expr="//button[@id='account_invoice_payment_btn']" position="after"> <field name="authorized_transaction_ids" invisible="1"/> diff --git a/addons/point_of_sale/models/account_move.py b/addons/point_of_sale/models/account_move.py index 5223a4738d7..b218dfa5b79 100644 --- a/addons/point_of_sale/models/account_move.py +++ b/addons/point_of_sale/models/account_move.py @@ -21,7 +21,7 @@ class AccountMove(models.Model): super(AccountMove, self)._compute_amount() pos_invoices = self.filtered(lambda i: i.type in ['out_invoice', 'out_refund'] and i.pos_order_ids) for invoice in pos_invoices: - invoice.invoice_payment_state = 'paid' + invoice.payment_state = 'paid' class AccountMoveLine(models.Model): _inherit = 'account.move.line' diff --git a/addons/product_margin/models/product_product.py b/addons/product_margin/models/product_product.py index 3f5c607c987..79a81009217 100644 --- a/addons/product_margin/models/product_product.py +++ b/addons/product_margin/models/product_product.py @@ -90,21 +90,21 @@ class ProductProduct(models.Model): res[val.id]['date_to'] = date_to res[val.id]['invoice_state'] = invoice_state states = () - invoice_payment_states = () + payment_states = () if invoice_state == 'paid': states = ('posted',) - invoice_payment_states = ('paid',) + payment_states = ('paid',) elif invoice_state == 'open_paid': states = ('posted',) - invoice_payment_states = ('not_paid', 'paid') + payment_states = ('not_paid', 'paid') elif invoice_state == 'draft_open_paid': states = ('posted', 'draft') - invoice_payment_states = ('not_paid', 'paid') + payment_states = ('not_paid', 'paid') company_id = self.env.company.id #Cost price is calculated afterwards as it is a property self.env['account.move.line'].flush(['price_unit', 'quantity', 'balance', 'product_id', 'display_type']) - self.env['account.move'].flush(['state', 'invoice_payment_state', 'type', 'invoice_date', 'company_id']) + self.env['account.move'].flush(['state', 'payment_state', 'type', 'invoice_date', 'company_id']) self.env['product.template'].flush(['list_price']) sqlstr = """ WITH currency_rate AS ({}) @@ -124,14 +124,14 @@ class ProductProduct(models.Model): (cr.date_end IS NULL OR cr.date_end > COALESCE(i.invoice_date, NOW()))) WHERE l.product_id = %s AND i.state IN %s - AND i.invoice_payment_state IN %s + AND i.payment_state IN %s AND i.type IN %s AND i.invoice_date BETWEEN %s AND %s AND i.company_id = %s AND l.display_type IS NULL """.format(self.env['res.currency']._select_companies_rates()) invoice_types = ('out_invoice', 'in_refund') - self.env.cr.execute(sqlstr, (val.id, states, invoice_payment_states, invoice_types, date_from, date_to, company_id)) + self.env.cr.execute(sqlstr, (val.id, states, payment_states, invoice_types, date_from, date_to, company_id)) result = self.env.cr.fetchall()[0] res[val.id]['sale_avg_price'] = result[0] and result[0] or 0.0 res[val.id]['sale_num_invoiced'] = result[1] and result[1] or 0.0 @@ -139,7 +139,7 @@ class ProductProduct(models.Model): res[val.id]['sale_expected'] = result[3] and result[3] or 0.0 res[val.id]['sales_gap'] = res[val.id]['sale_expected'] - res[val.id]['turnover'] invoice_types = ('in_invoice', 'out_refund') - self.env.cr.execute(sqlstr, (val.id, states, invoice_payment_states, invoice_types, date_from, date_to, company_id)) + self.env.cr.execute(sqlstr, (val.id, states, payment_states, invoice_types, date_from, date_to, company_id)) result = self.env.cr.fetchall()[0] res[val.id]['purchase_avg_price'] = result[0] and result[0] or 0.0 res[val.id]['purchase_num_invoiced'] = result[1] and result[1] or 0.0 diff --git a/addons/purchase_stock/tests/test_anglo_saxon_valuation_reconciliation.py b/addons/purchase_stock/tests/test_anglo_saxon_valuation_reconciliation.py index 01554c62905..114f8d9a5e1 100644 --- a/addons/purchase_stock/tests/test_anglo_saxon_valuation_reconciliation.py +++ b/addons/purchase_stock/tests/test_anglo_saxon_valuation_reconciliation.py @@ -138,7 +138,8 @@ class TestValuationReconciliation(ValuationReconciliationTestCommon): refund_invoice = self.env['account.move'].browse(refund_invoice_wiz.reverse_moves()['res_id']) # Check the result - self.assertTrue(invoice.invoice_payment_state == refund_invoice.invoice_payment_state == 'paid'), "Invoice and refund should both be in 'Paid' state" + self.assertEqual(invoice.payment_state, 'reversed', "Invoice should be in 'reversed' state") + self.assertEqual(refund_invoice.payment_state, 'paid', "Refund should be in 'paid' state") self.check_reconciliation(refund_invoice, return_pick) def test_multiple_shipments_invoices(self): diff --git a/addons/sale/models/sales_team.py b/addons/sale/models/sales_team.py index 8bec5cbf10b..9f2d58adde1 100644 --- a/addons/sale/models/sales_team.py +++ b/addons/sale/models/sales_team.py @@ -80,7 +80,7 @@ class CrmTeam(models.Model): FROM account_move move LEFT JOIN account_move_line line ON line.move_id = move.id WHERE move.type IN ('out_invoice', 'out_refund', 'in_invoice', 'in_refund') - AND move.invoice_payment_state IN ('in_payment', 'paid') + AND move.payment_state IN ('in_payment', 'paid') AND move.state = 'posted' AND move.team_id IN %s AND move.date BETWEEN %s AND %s @@ -96,7 +96,7 @@ class CrmTeam(models.Model): data_map = dict((v[0], v[1]) for v in self._cr.fetchall()) for team in self: team.invoiced = data_map.get(team.id, 0.0) - + def _graph_get_model(self): if self._context.get('in_sales_app'): return 'sale.report' diff --git a/addons/sale/tests/test_sale_refund.py b/addons/sale/tests/test_sale_refund.py index 3bd9d45850a..4e6af956bbd 100644 --- a/addons/sale/tests/test_sale_refund.py +++ b/addons/sale/tests/test_sale_refund.py @@ -199,7 +199,7 @@ class TestSaleToInvoice(TestCommonSaleNoChart): # Check invoice's type and number self.assertEqual(invoice_refund.type, 'out_refund', 'The last created invoiced should be a customer invoice') - self.assertEqual(invoice_refund.invoice_payment_state, 'paid', 'Last Customer creadit note should be in paid state') + self.assertEqual(invoice_refund.payment_state, 'paid', 'Last Customer creadit note should be in paid state') self.assertEqual(self.sale_order.invoice_count, 2, "The SO should have 3 related invoices: the original, the refund, and the new one") self.assertEqual(len(self.sale_order.invoice_ids.filtered(lambda inv: inv.type == 'out_refund')), 1, "The SO should be linked to only one refund") self.assertEqual(len(self.sale_order.invoice_ids.filtered(lambda inv: inv.type == 'out_invoice')), 1, "The SO should be linked to only one customer invoices") diff --git a/addons/sale/tests/test_sale_transaction.py b/addons/sale/tests/test_sale_transaction.py index 6a4c93c5a85..935b07789cf 100644 --- a/addons/sale/tests/test_sale_transaction.py +++ b/addons/sale/tests/test_sale_transaction.py @@ -47,7 +47,7 @@ class TestSaleTransaction(AccountTestCommon): invoice = self.order._create_invoices() invoice.post() - self.assertEqual(invoice.invoice_payment_state, 'paid') + self.assertEqual(invoice.payment_state, 'paid') def test_sale_transaction_mismatch(self): """Test that a transaction for the incorrect amount does not validate the SO.""" diff --git a/addons/sale_stock/tests/test_anglo_saxon_valuation_reconciliation.py b/addons/sale_stock/tests/test_anglo_saxon_valuation_reconciliation.py index 038990def81..a88ec316542 100644 --- a/addons/sale_stock/tests/test_anglo_saxon_valuation_reconciliation.py +++ b/addons/sale_stock/tests/test_anglo_saxon_valuation_reconciliation.py @@ -157,10 +157,8 @@ class TestValuationReconciliation(ValuationReconciliationTestCommon): 'refund_method': 'cancel', }) refund_invoice = self.env['account.move'].browse(refund_invoice_wiz.reverse_moves()['res_id']) - self.assertTrue( - invoice.invoice_payment_state == refund_invoice.invoice_payment_state == 'paid', - "Invoice and refund should both be in 'Paid' state" - ) + self.assertEqual(invoice.payment_state, 'reversed', "Invoice should be in 'reversed' state.") + self.assertEqual(refund_invoice.payment_state, 'paid', "Refund should be in 'paid' state.") self.check_reconciliation(refund_invoice, return_pick, operation='sale') def test_multiple_shipments_invoices(self): diff --git a/addons/website_sale_digital/controllers/main.py b/addons/website_sale_digital/controllers/main.py index e4ddceb5f79..f3899b92e10 100644 --- a/addons/website_sale_digital/controllers/main.py +++ b/addons/website_sale_digital/controllers/main.py @@ -36,7 +36,7 @@ class WebsiteSaleDigital(CustomerPortal): if not 'sale_order' in response.qcontext: return response order = response.qcontext['sale_order'] - invoiced_lines = request.env['account.move.line'].sudo().search([('move_id', 'in', order.invoice_ids.ids), ('move_id.invoice_payment_state', '=', 'paid')]) + invoiced_lines = request.env['account.move.line'].sudo().search([('move_id', 'in', order.invoice_ids.ids), ('move_id.payment_state', '=', 'paid')]) products = invoiced_lines.mapped('product_id') | order.order_line.filtered(lambda r: not r.price_subtotal).mapped('product_id') if not order.amount_total: # in that case, we should add all download links to the products diff --git a/addons/website_sale_digital/models/account_invoice.py b/addons/website_sale_digital/models/account_invoice.py index 5d5e350912e..e75b42d4a4a 100644 --- a/addons/website_sale_digital/models/account_invoice.py +++ b/addons/website_sale_digital/models/account_invoice.py @@ -14,7 +14,7 @@ class AccountInvoiceLine(models.Model): # Get paid invoices purchases = self.sudo().search_read( domain=[ - ('move_id.invoice_payment_state', '=', 'paid'), + ('move_id.payment_state', '=', 'paid'), ('move_id.partner_id', '=', partner.id), ('product_id', '!=', False), ], |
