# -*- coding: utf-8 -*- from odoo.addons.account.tests.common import AccountTestInvoicingCommon from odoo.exceptions import ValidationError from odoo.tests import tagged from odoo import fields, Command from odoo.tests.common import Form from odoo.tools.safe_eval import datetime @tagged('post_install', '-at_install') class TestAccountPaymentTerms(AccountTestInvoicingCommon): @classmethod def setUpClass(cls, chart_template_ref=None): super().setUpClass(chart_template_ref=chart_template_ref) cls.pay_term_today = cls.env['account.payment.term'].create({ 'name': 'Today', 'line_ids': [ (0, 0, { 'value_amount': 100, 'value': 'percent', 'nb_days': 0, }), ], }) cls.pay_term_net_30_days = cls.env['account.payment.term'].create({ 'name': 'Net 30 days', 'line_ids': [ (0, 0, { 'value_amount': 100, 'value': 'percent', 'nb_days': 30, }), ], }) cls.pay_term_60_days = cls.env['account.payment.term'].create({ 'name': '60 days two lines', 'line_ids': [ (0, 0, { 'value_amount': 30, 'value': 'percent', 'nb_days': 15, }), (0, 0, { 'value_amount': 70, 'value': 'percent', 'nb_days': 45, }), ], }) cls.pay_term_30_days = cls.env['account.payment.term'].create({ 'name': '60 days two lines', 'line_ids': [ (0, 0, { 'value_amount': 100, 'value': 'percent', 'nb_days': 15, }), ], }) cls.invoice = cls.init_invoice('out_refund', products=cls.product_a+cls.product_b) cls.pay_term_a = cls.env['account.payment.term'].create({ 'name': "turlututu", 'early_discount': True, 'discount_percentage': 10, 'discount_days': 1, 'line_ids': [ Command.create({ 'value': 'percent', 'value_amount': 100, 'nb_days': 2, }), ], }) cls.pay_term_b = cls.env['account.payment.term'].create({ 'name': "tralala", 'line_ids': [ Command.create({ 'value': 'percent', 'value_amount': 50, 'nb_days': 2, }), Command.create({ 'value': 'percent', 'value_amount': 50, 'nb_days': 4, }), ], }) def assertPaymentTerm(self, pay_term, invoice_date, dates): with Form(self.invoice) as move_form: move_form.invoice_payment_term_id = pay_term move_form.invoice_date = invoice_date self.assertEqual( self.invoice.line_ids.filtered( lambda l: l.account_id == self.company_data['default_account_receivable'] ).sorted(key=lambda r: r.date_maturity).mapped('date_maturity'), [fields.Date.from_string(date) for date in dates], ) def test_payment_term(self): self.assertPaymentTerm(self.pay_term_today, '2019-01-01', ['2019-01-01']) self.assertPaymentTerm(self.pay_term_today, '2019-01-15', ['2019-01-15']) self.assertPaymentTerm(self.pay_term_today, '2019-01-31', ['2019-01-31']) self.assertPaymentTerm(self.pay_term_net_30_days, '2022-01-01', ['2022-01-31']) self.assertPaymentTerm(self.pay_term_net_30_days, '2022-01-15', ['2022-02-14']) self.assertPaymentTerm(self.pay_term_net_30_days, '2022-01-31', ['2022-03-02']) self.assertPaymentTerm(self.pay_term_60_days, '2022-01-01', ['2022-01-16', '2022-02-15']) self.assertPaymentTerm(self.pay_term_60_days, '2022-01-15', ['2022-01-30', '2022-03-01']) self.assertPaymentTerm(self.pay_term_60_days, '2022-01-31', ['2022-02-15', '2022-03-17']) def test_wrong_payment_term(self): with self.assertRaises(ValidationError): self.env['account.payment.term'].create({ 'name': 'Wrong Payment Term', 'line_ids': [ (0, 0, { 'value': 'percent', 'value_amount': 50, }), ], }) def test_payment_term_compute_method_with_cash_discount(self): self.pay_term_a.early_pay_discount_computation = 'included' computed_term_a = self.pay_term_a._compute_terms( fields.Date.from_string('2016-01-01'), self.env.company.currency_id, self.env.company, 150.0, 150.0, 1000.0, 1000.0, 1.0, ) self.assertDictEqual( { 'total_amount': computed_term_a.get("total_amount"), 'discount_balance': computed_term_a.get("discount_balance"), 'line_ids': computed_term_a.get("line_ids"), }, #What should be obtained { 'total_amount': 1150.0, 'discount_balance': 1035.0, 'line_ids': [{ 'date': datetime.date(2016, 1, 3), 'company_amount': 1150.0, 'foreign_amount': 151.0, }], }, ) def test_payment_term_compute_method_without_cash_discount(self): computed_term_b = self.pay_term_b._compute_terms( fields.Date.from_string('2016-01-01'), self.env.company.currency_id, self.env.company, 150.0, 150.0, 1000.0, 1000.0, 1.0, ) self.assertDictEqual( { 'total_amount': computed_term_b.get("total_amount"), 'discount_balance': computed_term_b.get("discount_balance"), 'line_ids': computed_term_b.get("line_ids"), }, # What should be obtained { 'total_amount': 1150.0, 'discount_balance': 0, 'line_ids': [{ 'date': datetime.date(2016, 1, 3), 'company_amount': 575.0, 'foreign_amount': 75.5, }, { 'date': datetime.date(2016, 1, 5), 'company_amount': 575.0, 'foreign_amount': 75.5, }], }, ) def test_payment_term_compute_method_early_excluded(self): self.pay_term_a.early_pay_discount_computation = 'excluded' computed_term_a = self.pay_term_a._compute_terms( fields.Date.from_string('2016-01-01'), self.env.company.currency_id, self.env.company, 150.0, 150.0, 1000.0, 1000.0, 1.0, ) self.assertDictEqual( { 'total_amount': computed_term_a.get("total_amount"), 'discount_balance': computed_term_a.get("discount_balance"), 'line_ids': computed_term_a.get("line_ids"), }, # What should be obtained { 'total_amount': 1150.0, 'discount_balance': 1050.0, 'line_ids': [{ 'date': datetime.date(2016, 1, 3), 'company_amount': 1150.0, 'foreign_amount': 151.0, }], }, ) def test_payment_term_residual_amount_on_last_line_with_fixed_amount_multi_currency(self): pay_term = self.env['account.payment.term'].create({ 'name': "test_payment_term_residual_amount_on_last_line", 'line_ids': [ Command.create({ 'value_amount': 50, 'value': 'percent', 'nb_days': 0, }), Command.create({ 'value_amount': 50, 'value': 'percent', 'nb_days': 0, }), Command.create({ 'value_amount': 0.02, 'value': 'fixed', 'nb_days': 0, }), ], }) computed_term = pay_term._compute_terms( fields.Date.from_string('2016-01-01'), self.currency_data['currency'], self.env.company, 0.0, 0.0, 1.0, 0.04, 0.09, ) self.assertEqual( [ ( self.currency_data['currency'].round(l['foreign_amount']), self.company_data['currency'].round(l['company_amount']), ) for l in computed_term['line_ids'] ], [(0.045, 0.02), (0.045, 0.02), (0.0, 0.0)], ) def test_payment_term_residual_amount_on_last_line(self): pay_term = self.env['account.payment.term'].create({ 'name': "test_payment_term_residual_amount_on_last_line", 'line_ids': [ Command.create({ 'value_amount': 50, 'value': 'percent', 'nb_days': 0, }), Command.create({ 'value_amount': 50, 'value': 'percent', 'nb_days': 0, }), ], }) computed_term = pay_term._compute_terms( fields.Date.from_string('2016-01-01'), self.env.company.currency_id, self.env.company, 0.0, 0.0, 1.0, 0.03, 0.03, ) self.assertEqual( [self.env.company.currency_id.round(l['foreign_amount']) for l in computed_term['line_ids']], [0.02, 0.01], ) def test_payment_term_last_balance_line_with_fixed(self): pay_term = self.env['account.payment.term'].create({ 'name': 'test_payment_term_last_balance_line_with_fixed', 'line_ids': [ Command.create({ 'value_amount': 70, 'value': 'percent', 'nb_days': 0, }), Command.create({ 'value_amount': 200, 'value': 'fixed', 'nb_days': 0, }), Command.create({ 'value_amount': 30, 'value': 'percent', 'nb_days': 0, }), ] }) computed_term = pay_term._compute_terms( fields.Date.from_string('2016-01-01'), self.env.company.currency_id, self.env.company, 0.0, 0.0, 1.0, 1000.0, 1000.0, ) self.assertEqual( [self.env.company.currency_id.round(l['foreign_amount']) for l in computed_term['line_ids']], [700.0, 200.0, 100.0], ) def test_payment_term_last_balance_line_with_fixed_negative(self): pay_term = self.env['account.payment.term'].create({ 'name': 'test_payment_term_last_balance_line_with_fixed_negative', 'line_ids': [ Command.create({ 'value_amount': 70, 'value': 'percent', 'nb_days': 0, }), Command.create({ 'value_amount': 500, 'value': 'fixed', 'nb_days': 0, }), Command.create({ 'value_amount': 30, 'value': 'percent', 'nb_days': 0, }), ] }) computed_term = pay_term._compute_terms( fields.Date.from_string('2016-01-01'), self.env.company.currency_id, self.env.company, 0.0, 0.0, 1.0, 1000.0, 1000.0, ) self.assertEqual( [self.env.company.currency_id.round(l['foreign_amount']) for l in computed_term['line_ids']], [700.0, 500.0, -200.0], ) def test_payment_term_last_balance_line_with_fixed_negative_fixed(self): pay_term = self.env['account.payment.term'].create({ 'name': 'test_payment_term_last_balance_line_with_fixed_negative_fixed', 'line_ids': [ Command.create({ 'value_amount': 70, 'value': 'percent', 'nb_days': 0, }), Command.create({ 'value_amount': 500, 'value': 'fixed', 'nb_days': 0, }), Command.create({ 'value_amount': 30, 'value': 'percent', 'nb_days': 0, }), Command.create({ 'value_amount': 200, 'value': 'fixed', 'nb_days': 0, }), ] }) computed_term = pay_term._compute_terms( fields.Date.from_string('2016-01-01'), self.env.company.currency_id, self.env.company, 0.0, 0.0, 1.0, 1000.0, 1000.0, ) self.assertEqual( [self.env.company.currency_id.round(l['foreign_amount']) for l in computed_term['line_ids']], [700.0, 500.0, 300.0, -500.0], ) def test_payment_term_percent_round_calculation(self): """ the sum function might not sum the floating numbers properly if there are a lot of lines with floating numbers so this test verifies the round function changes """ self.env['account.payment.term'].create({ 'name': "test_payment_term_percent_round_calculation", 'line_ids': [ Command.create({'value_amount': 50, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 1.66, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 16.8, 'value': 'percent', 'nb_days': 0, }), ], }) def test_payment_term_labels(self): # create a payment term with 40% now, 30% in 30 days and 30% in 60 days multiple_installment_term = self.env['account.payment.term'].create({ 'name': "test_payment_term_labels", 'line_ids': [ Command.create({'value_amount': 40, 'value': 'percent', 'nb_days': 0, }), Command.create({'value_amount': 30, 'value': 'percent', 'nb_days': 30, }), Command.create({'value_amount': 30, 'value': 'percent', 'nb_days': 60, }), ], }) # create immediate payment term immediate_term = self.env['account.payment.term'].create({ 'name': 'Immediate', 'line_ids': [ Command.create({'value_amount': 100, 'value': 'percent', 'nb_days': 0, }), ], }) # create an invoice with immediate payment term invoice = self.init_invoice('out_invoice', products=self.product_a) invoice.invoice_payment_term_id = immediate_term # check the payment term labels invoice_terms = invoice.line_ids.filtered(lambda l: l.display_type == 'payment_term') self.assertEqual(invoice_terms[0].name, '') # change the payment term to the multiple installment term invoice.invoice_payment_term_id = multiple_installment_term invoice_terms = invoice.line_ids.filtered(lambda l: l.display_type == 'payment_term').sorted('date_maturity') self.assertEqual(invoice_terms[0].name, 'installment #1') self.assertEqual(invoice_terms[0].debit, invoice.amount_total * 0.4) self.assertEqual(invoice_terms[1].name, 'installment #2') self.assertEqual(invoice_terms[1].debit, invoice.amount_total * 0.3) self.assertEqual(invoice_terms[2].name, 'installment #3') self.assertEqual(invoice_terms[2].debit, invoice.amount_total * 0.3)