# Copyright (C) 2025: Criptomart (https://criptomart.net) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from odoo.tests import tagged from odoo.tests.common import TransactionCase @tagged("post_install", "-at_install") class TestAccountMove(TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() # Create a partner cls.partner = cls.env["res.partner"].create({ "name": "Test Customer", "email": "customer@test.com", }) # Create a product cls.product = cls.env["product.product"].create({ "name": "Test Product Invoice", "type": "consu", "list_price": 200.0, "standard_price": 100.0, }) # Create tax cls.tax = cls.env["account.tax"].create({ "name": "Test Tax 10%", "amount": 10.0, "amount_type": "percent", "type_tax_use": "sale", }) # Create an invoice cls.invoice = cls.env["account.move"].create({ "move_type": "out_invoice", "partner_id": cls.partner.id, "invoice_date": "2026-01-01", }) # Create invoice line cls.invoice_line = cls.env["account.move.line"].create({ "move_id": cls.invoice.id, "product_id": cls.product.id, "quantity": 5, "price_unit": 200.0, "discount1": 10.0, "discount2": 5.0, "discount3": 2.0, "tax_ids": [(6, 0, [cls.tax.id])], }) def test_invoice_line_discount_readonly(self): """Test that discount field is readonly in invoice lines""" field = self.invoice_line._fields["discount"] self.assertTrue(field.readonly, "Discount field should be readonly in invoice lines") def test_invoice_line_write_with_explicit_discounts(self): """Test writing invoice line with explicit discounts""" self.invoice_line.write({ "discount": 30.0, # Should be ignored "discount1": 15.0, "discount2": 10.0, "discount3": 5.0, }) self.assertEqual(self.invoice_line.discount1, 15.0) self.assertEqual(self.invoice_line.discount2, 10.0) self.assertEqual(self.invoice_line.discount3, 5.0) def test_invoice_line_legacy_discount(self): """Test legacy discount behavior in invoice lines""" self.invoice_line.write({ "discount": 20.0, }) # Should map to discount1 and reset others self.assertEqual(self.invoice_line.discount1, 20.0) self.assertEqual(self.invoice_line.discount2, 0.0) self.assertEqual(self.invoice_line.discount3, 0.0) def test_invoice_line_price_calculation(self): """Test that price subtotal is calculated correctly with triple discount""" self.invoice_line.write({ "discount1": 10.0, "discount2": 5.0, "discount3": 0.0, }) # Base: 5 * 200 = 1000 # After 10% discount: 900 # After 5% discount: 855 expected_subtotal = 5 * 200 * 0.9 * 0.95 self.assertAlmostEqual( self.invoice_line.price_subtotal, expected_subtotal, places=2 ) def test_multiple_invoice_lines(self): """Test multiple invoice lines with different discounts""" line2 = self.env["account.move.line"].create({ "move_id": self.invoice.id, "product_id": self.product.id, "quantity": 3, "price_unit": 150.0, "discount1": 20.0, "discount2": 10.0, "discount3": 5.0, "tax_ids": [(6, 0, [self.tax.id])], }) # Verify both lines have correct discounts self.assertEqual(self.invoice_line.discount1, 10.0) self.assertEqual(line2.discount1, 20.0) self.assertEqual(line2.discount2, 10.0) self.assertEqual(line2.discount3, 5.0) def test_invoice_line_update_quantity(self): """Test updating quantity doesn't affect discounts""" initial_discount1 = self.invoice_line.discount1 initial_discount2 = self.invoice_line.discount2 self.invoice_line.write({ "quantity": 10, }) # Discounts should remain unchanged self.assertEqual(self.invoice_line.discount1, initial_discount1) self.assertEqual(self.invoice_line.discount2, initial_discount2) # Quantity should be updated self.assertEqual(self.invoice_line.quantity, 10) def test_invoice_line_update_price(self): """Test updating price doesn't affect discounts""" initial_discount1 = self.invoice_line.discount1 self.invoice_line.write({ "price_unit": 250.0, }) # Discount should remain unchanged self.assertEqual(self.invoice_line.discount1, initial_discount1) # Price should be updated self.assertEqual(self.invoice_line.price_unit, 250.0) def test_invoice_with_zero_discounts(self): """Test invoice line with all zero discounts""" self.invoice_line.write({ "discount1": 0.0, "discount2": 0.0, "discount3": 0.0, }) # All discounts should be zero self.assertEqual(self.invoice_line.discount, 0.0) self.assertEqual(self.invoice_line.discount1, 0.0) self.assertEqual(self.invoice_line.discount2, 0.0) self.assertEqual(self.invoice_line.discount3, 0.0) # Subtotal should be quantity * price expected = 5 * 200 self.assertEqual(self.invoice_line.price_subtotal, expected) def test_invoice_line_combined_operations(self): """Test combined operations on invoice line""" # Update multiple fields at once self.invoice_line.write({ "quantity": 8, "price_unit": 180.0, "discount1": 12.0, "discount2": 6.0, "discount3": 0.0, # Reset discount3 explicitly }) # All fields should be updated correctly self.assertEqual(self.invoice_line.quantity, 8) self.assertEqual(self.invoice_line.price_unit, 180.0) self.assertEqual(self.invoice_line.discount1, 12.0) self.assertEqual(self.invoice_line.discount2, 6.0) self.assertEqual(self.invoice_line.discount3, 0.0) # Calculate expected subtotal: 8 * 180 * (1-0.12) * (1-0.06) expected = 8 * 180 * 0.88 * 0.94 self.assertAlmostEqual( self.invoice_line.price_subtotal, expected, places=2 )