# Copyright 2024 Criptomart # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import fields from odoo.exceptions import UserError from odoo.tests import tagged from odoo.addons.account.tests.common import AccountTestInvoicingCommon @tagged("post_install", "-at_install") class TestPurchaseRappel(AccountTestInvoicingCommon): @classmethod def setUpClass(cls, chart_template_ref=None): super().setUpClass(chart_template_ref=chart_template_ref) cls.vendor_rappel = cls.env["res.partner"].create( { "name": "Vendor with Rappel", "rappel_commission": True, "rappel_commission_rate": 5.0, "property_account_receivable_id": cls.company_data[ "default_account_receivable" ].id, "property_account_payable_id": cls.company_data[ "default_account_payable" ].id, } ) cls.vendor_no_rappel = cls.env["res.partner"].create( { "name": "Vendor without Rappel", "rappel_commission": False, "property_account_receivable_id": cls.company_data[ "default_account_receivable" ].id, "property_account_payable_id": cls.company_data[ "default_account_payable" ].id, } ) cls.vendor_zero_rate = cls.env["res.partner"].create( { "name": "Vendor Zero Rate", "rappel_commission": True, "rappel_commission_rate": 0.0, "property_account_receivable_id": cls.company_data[ "default_account_receivable" ].id, "property_account_payable_id": cls.company_data[ "default_account_payable" ].id, } ) cls.rappel_product = cls.env["product.product"].create( { "name": "Rappel Commission Product", "type": "service", "list_price": 100.0, "property_account_income_id": cls.company_data[ "default_account_revenue" ].id, "taxes_id": [(6, 0, [cls.tax_sale_a.id])], } ) cls.rappel_journal = cls.company_data["default_journal_sale"] cls.company_data["company"].write( { "rappel_journal_id": cls.rappel_journal.id, "rappel_product_id": cls.rappel_product.id, } ) def _create_vendor_bill(self, partner, amount=1000.0, post=True, ref=None): move = self.init_invoice( "in_invoice", partner=partner, products=self.product_a, post=False, ) move.invoice_line_ids[0].price_unit = amount if ref is not None: move.ref = ref if post: move.action_post() return move def _create_vendor_refund(self, partner, amount=200.0, post=True, ref=None): move = self.init_invoice( "in_refund", partner=partner, products=self.product_a, post=False, ) move.invoice_line_ids[0].price_unit = amount if ref is not None: move.ref = ref if post: move.action_post() return move def test_rappel_pending_on_create(self): bill_rappel = self._create_vendor_bill(self.vendor_rappel) self.assertTrue(bill_rappel.rappel_pending) bill_no_rappel = self._create_vendor_bill(self.vendor_no_rappel) self.assertFalse(bill_no_rappel.rappel_pending) def test_rappel_pending_onchange(self): move = self.env["account.move"].new( { "move_type": "in_invoice", "partner_id": self.vendor_no_rappel.id, "invoice_date": fields.Date.from_string("2019-01-01"), } ) move._onchange_partner_id_rappel() self.assertFalse(move.rappel_pending) move.partner_id = self.vendor_rappel move._onchange_partner_id_rappel() self.assertTrue(move.rappel_pending) def test_wizard_default_get_valid(self): bill = self._create_vendor_bill(self.vendor_rappel) wizard = ( self.env["account.move.rappel.wizard"] .with_context(active_ids=bill.ids) .create({}) ) self.assertIn(bill, wizard.move_ids) self.assertEqual(wizard.journal_id, self.rappel_journal) self.assertEqual(wizard.product_id, self.rappel_product) def test_wizard_default_get_no_valid(self): bill = self._create_vendor_bill(self.vendor_rappel, post=False) with self.assertRaises(UserError): self.env["account.move.rappel.wizard"].with_context( active_ids=bill.ids ).create({}) def test_wizard_default_get_no_rappel(self): bill = self._create_vendor_bill(self.vendor_no_rappel) with self.assertRaises(UserError): self.env["account.move.rappel.wizard"].with_context( active_ids=bill.ids ).create({}) def test_wizard_default_get_not_pending(self): bill = self._create_vendor_bill(self.vendor_rappel) bill.rappel_pending = False with self.assertRaises(UserError): self.env["account.move.rappel.wizard"].with_context( active_ids=bill.ids ).create({}) def test_wizard_create_invoice(self): bill = self._create_vendor_bill(self.vendor_rappel, amount=2000.0) self.assertTrue(bill.rappel_pending) wizard = ( self.env["account.move.rappel.wizard"] .with_context(active_ids=bill.ids) .create({}) ) action = wizard.action_create_rappel_invoices() rappel_invoice = self.env["account.move"].browse(action["res_id"]) self.assertEqual(rappel_invoice.move_type, "out_invoice") self.assertEqual(rappel_invoice.partner_id, self.vendor_rappel) self.assertEqual(len(rappel_invoice.invoice_line_ids), 1) expected_amount = 2000.0 * 5.0 / 100.0 self.assertEqual(rappel_invoice.invoice_line_ids[0].price_unit, expected_amount) self.assertFalse(bill.rappel_pending) def test_wizard_grouping(self): bill1 = self._create_vendor_bill(self.vendor_rappel, amount=1000.0) vendor_rappel_2 = self.env["res.partner"].create( { "name": "Vendor Rappel 2", "rappel_commission": True, "rappel_commission_rate": 10.0, "property_account_receivable_id": self.company_data[ "default_account_receivable" ].id, "property_account_payable_id": self.company_data[ "default_account_payable" ].id, } ) bill2 = self._create_vendor_bill(vendor_rappel_2, amount=2000.0) wizard = ( self.env["account.move.rappel.wizard"] .with_context(active_ids=(bill1 + bill2).ids) .create({}) ) action = wizard.action_create_rappel_invoices() invoices = self.env["account.move"].search(action.get("domain", [])) self.assertEqual(len(invoices), 2) invoice_vendor_1 = invoices.filtered( lambda m: m.partner_id == self.vendor_rappel ) self.assertEqual( invoice_vendor_1.invoice_line_ids[0].price_unit, 1000.0 * 5.0 / 100.0, ) invoice_vendor_2 = invoices.filtered(lambda m: m.partner_id == vendor_rappel_2) self.assertEqual( invoice_vendor_2.invoice_line_ids[0].price_unit, 2000.0 * 10.0 / 100.0, ) self.assertFalse(bill1.rappel_pending) self.assertFalse(bill2.rappel_pending) def test_wizard_no_journal(self): bill = self._create_vendor_bill(self.vendor_rappel) wizard = self.env["account.move.rappel.wizard"].new( { "move_ids": [(6, 0, bill.ids)], "journal_id": False, "product_id": self.rappel_product.id, "company_id": self.env.company.id, } ) with self.assertRaises(UserError): wizard.action_create_rappel_invoices() def test_wizard_no_product(self): bill = self._create_vendor_bill(self.vendor_rappel) wizard = self.env["account.move.rappel.wizard"].new( { "move_ids": [(6, 0, bill.ids)], "journal_id": self.rappel_journal.id, "product_id": False, "company_id": self.env.company.id, } ) with self.assertRaises(UserError): wizard.action_create_rappel_invoices() def test_wizard_zero_rate(self): bill = self._create_vendor_bill(self.vendor_zero_rate, amount=1000.0) wizard = ( self.env["account.move.rappel.wizard"] .with_context(active_ids=bill.ids) .create({}) ) with self.assertRaises(UserError): wizard.action_create_rappel_invoices() def test_wizard_vendor_ref_in_description(self): bill_with_ref = self._create_vendor_bill( self.vendor_rappel, amount=1000.0, ref="VENDOR-REF-001" ) wizard = ( self.env["account.move.rappel.wizard"] .with_context(active_ids=bill_with_ref.ids) .create({}) ) action = wizard.action_create_rappel_invoices() rappel_invoice = self.env["account.move"].browse(action["res_id"]) line_name = rappel_invoice.invoice_line_ids[0].name self.assertIn("VENDOR-REF-001", line_name) self.assertNotIn(bill_with_ref.name, line_name) bill_without_ref = self._create_vendor_bill( self.vendor_rappel, amount=1000.0, ref=None ) wizard2 = ( self.env["account.move.rappel.wizard"] .with_context(active_ids=bill_without_ref.ids) .create({}) ) action2 = wizard2.action_create_rappel_invoices() rappel_invoice2 = self.env["account.move"].browse(action2["res_id"]) line_name2 = rappel_invoice2.invoice_line_ids[0].name self.assertIn(bill_without_ref.name, line_name2) def test_wizard_includes_in_refund(self): bill = self._create_vendor_bill(self.vendor_rappel, amount=1000.0) refund = self._create_vendor_refund(self.vendor_rappel, amount=200.0) self.assertTrue(refund.rappel_pending) wizard = ( self.env["account.move.rappel.wizard"] .with_context(active_ids=(bill + refund).ids) .create({}) ) action = wizard.action_create_rappel_invoices() rappel_invoice = self.env["account.move"].browse(action["res_id"]) self.assertEqual(len(rappel_invoice.invoice_line_ids), 2) amounts = sorted(rappel_invoice.invoice_line_ids.mapped("price_unit")) self.assertEqual(amounts, [-10.0, 50.0]) self.assertFalse(bill.rappel_pending) self.assertFalse(refund.rappel_pending) def test_wizard_refund_subtracts_commission(self): bill = self._create_vendor_bill(self.vendor_rappel, amount=1000.0) refund = self._create_vendor_refund(self.vendor_rappel, amount=200.0) wizard = ( self.env["account.move.rappel.wizard"] .with_context(active_ids=(bill + refund).ids) .create({}) ) action = wizard.action_create_rappel_invoices() rappel_invoice = self.env["account.move"].browse(action["res_id"]) self.assertEqual(len(rappel_invoice.invoice_line_ids), 2) line_by_sign = { line.price_unit > 0: line for line in rappel_invoice.invoice_line_ids } self.assertEqual(line_by_sign[True].price_unit, 50.0) self.assertEqual(line_by_sign[False].price_unit, -10.0) self.assertEqual(rappel_invoice.amount_untaxed, 40.0) self.assertIn("Refund", line_by_sign[False].name) def test_rappel_pending_set_on_refund_create(self): refund = self._create_vendor_refund( self.vendor_rappel, amount=200.0, post=False ) self.assertTrue(refund.rappel_pending) refund_no_rappel = self._create_vendor_refund( self.vendor_no_rappel, amount=200.0, post=False ) self.assertFalse(refund_no_rappel.rappel_pending)