From f5310d21c4a54adf05262c1df4e4a9e0778fc55c Mon Sep 17 00:00:00 2001 From: Victor Feyens Date: Thu, 11 Aug 2022 12:47:03 +0000 Subject: [PATCH] [FIX] sale_margin: forbid manual cost specification Considering recent recurring bug reports and the fact that bridge modules now automatically compute the cost considering logistics, timesheets & expense flows, the ability to manually edit the cost on a sale.order.line is now disabled. In most cases, manually specified costs are automatically reset because of stock/timesheets/expenses added dependencies on the purchase_price field. Also adapt existing tests and use the new Sale tests common. closes odoo/odoo#97977 Signed-off-by: Victor Feyens (vfe) --- addons/sale_margin/models/sale_order_line.py | 4 +- addons/sale_margin/tests/test_sale_margin.py | 224 +++++++------------ 2 files changed, 88 insertions(+), 140 deletions(-) diff --git a/addons/sale_margin/models/sale_order_line.py b/addons/sale_margin/models/sale_order_line.py index 8e03988ceac..0130ec81451 100644 --- a/addons/sale_margin/models/sale_order_line.py +++ b/addons/sale_margin/models/sale_order_line.py @@ -13,8 +13,8 @@ class SaleOrderLine(models.Model): margin_percent = fields.Float( "Margin (%)", compute='_compute_margin', store=True, groups="base.group_user", precompute=True) purchase_price = fields.Float( - string='Cost', compute="_compute_purchase_price", - digits='Product Price', store=True, readonly=False, precompute=True, + string="Cost", compute="_compute_purchase_price", + digits='Product Price', store=True, precompute=True, groups="base.group_user") @api.depends('product_id', 'company_id', 'currency_id', 'product_uom') diff --git a/addons/sale_margin/tests/test_sale_margin.py b/addons/sale_margin/tests/test_sale_margin.py index 69047e7580c..cb1e254bad8 100644 --- a/addons/sale_margin/tests/test_sale_margin.py +++ b/addons/sale_margin/tests/test_sale_margin.py @@ -1,154 +1,102 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo.tests import common -from datetime import datetime +from odoo.fields import Command +from odoo.addons.sale.tests.common import SaleCommon -class TestSaleMargin(common.TransactionCase): - - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.SaleOrder = cls.env['sale.order'] - - cls.product_uom_id = cls.env.ref('uom.product_uom_unit').id - cls.product = cls.env['product.product'].create({'name': 'Individual Workplace'}) - cls.product_id = cls.product.id - cls.partner_id = cls.env['res.partner'].create({'name': 'A test partner'}).id - cls.partner_invoice_address_id = cls.env['res.partner'].create({ - 'name': 'A test partner address', - 'parent_id': cls.partner_id, - }).id - cls.pricelist = cls.env.ref('product.list0') - cls.pricelist_id = cls.pricelist.id +class TestSaleMargin(SaleCommon): def test_sale_margin(self): """ Test the sale_margin module in Odoo. """ - self.pricelist.currency_id = self.env.company.currency_id self.product.standard_price = 700.0 - sale_order_so11 = self.SaleOrder.create({ - 'date_order': datetime.today(), - 'name': 'Test_SO011', - 'order_line': [ - (0, 0, { - 'name': '[CARD] Individual Workplace', - 'price_unit': 1000.0, - 'product_uom': self.product_uom_id, - 'product_uom_qty': 10.0, - 'state': 'draft', - 'product_id': self.product_id}), - (0, 0, { - 'name': 'Line without product_uom', - 'price_unit': 1000.0, - 'product_uom_qty': 10.0, - 'state': 'draft', - 'product_id': self.product_id})], - 'partner_id': self.partner_id, - 'partner_invoice_id': self.partner_invoice_address_id, - 'partner_shipping_id': self.partner_invoice_address_id, - 'pricelist_id': self.pricelist_id}) - # Confirm the sales order. - sale_order_so11.action_confirm() - # Verify that margin field gets bind with the value. - self.assertEqual(sale_order_so11.margin, 6000.00, "Sales order profit should be 6000.00") - self.assertEqual(sale_order_so11.margin_percent, 0.3, "Sales order margin should be 30%") - sale_order_so11.order_line[1].purchase_price = 800 - self.assertEqual(sale_order_so11.margin, 5000.00, "Sales order margin should be 5000.00") + order = self.empty_order - def test_sale_margin1(self): + order.order_line = [ + Command.create({ + 'price_unit': 1000.0, + 'product_uom_qty': 10.0, + 'product_id': self.product.id, + }), + ] + # Confirm the sales order. + order.action_confirm() + # Verify that margin field gets bind with the value. + self.assertEqual(order.margin, 3000.00, "Sales order profit should be 6000.00") + self.assertEqual(order.margin_percent, 0.3, "Sales order margin should be 30%") + + def test_negative_margin(self): """ Test the margin when sales price is less then cost.""" - sale_order_so12 = self.SaleOrder.create({ - 'date_order': datetime.today(), - 'name': 'Test_SO012', - 'order_line': [ - (0, 0, { - 'name': '[CARD] Individual Workplace', - 'purchase_price': 40.0, - 'price_unit': 20.0, - 'product_uom': self.product_uom_id, - 'product_uom_qty': 1.0, - 'state': 'draft', - 'product_id': self.product_id}), - (0, 0, { - 'name': 'Line without product_uom', - 'price_unit': -100.0, - 'purchase_price': 0.0, - 'product_uom_qty': 1.0, - 'state': 'draft', - 'product_id': self.product_id})], - 'partner_id': self.partner_id, - 'partner_invoice_id': self.partner_invoice_address_id, - 'partner_shipping_id': self.partner_invoice_address_id, - 'pricelist_id': self.pricelist_id}) - # Confirm the sales order. - sale_order_so12.action_confirm() - # Verify that margin field of Sale Order Lines gets bind with the value. - self.assertEqual(sale_order_so12.order_line[0].margin, -20.00, "Sales order profit should be -20.00") - self.assertEqual(sale_order_so12.order_line[0].margin_percent, -1, "Sales order margin percentage should be -100%") - self.assertEqual(sale_order_so12.order_line[1].margin, -100.00, "Sales order profit should be -100.00") - self.assertEqual(sale_order_so12.order_line[1].margin_percent, 1.00, "Sales order margin should be 100% when the cost is zero and price defined") - # Verify that margin field gets bind with the value. - self.assertEqual(sale_order_so12.margin, -120.00, "Sales order margin should be -120.00") - self.assertEqual(sale_order_so12.margin_percent, 1.5, "Sales order margin should be 150%") + order = self.empty_order + self.service_product.standard_price = 40.0 - def test_sale_margin2(self): + order.order_line = [ + Command.create({ + 'price_unit': 20.0, + 'product_uom_qty': 1.0, + 'state': 'draft', + 'product_id': self.service_product.id, + }), + Command.create({ + 'price_unit': -100.0, + 'purchase_price': 0.0, + 'product_uom_qty': 1.0, + 'state': 'draft', + 'product_id': self.product.id, + }), + ] + # Confirm the sales order. + order.action_confirm() + # Verify that margin field of Sale Order Lines gets bind with the value. + self.assertEqual(order.order_line[0].margin, -20.00, "Sales order profit should be -20.00") + self.assertEqual(order.order_line[0].margin_percent, -1, "Sales order margin percentage should be -100%") + self.assertEqual(order.order_line[1].margin, -100.00, "Sales order profit should be -100.00") + self.assertEqual(order.order_line[1].margin_percent, 1.00, "Sales order margin should be 100% when the cost is zero and price defined") + # Verify that margin field gets bind with the value. + self.assertEqual(order.margin, -120.00, "Sales order margin should be -120.00") + self.assertEqual(order.margin_percent, 1.5, "Sales order margin should be 150%") + + def test_margin_no_cost(self): """ Test the margin when cost is 0 margin percentage should always be 100%.""" - sale_order_so13 = self.SaleOrder.create({ - 'date_order': datetime.today(), - 'name': 'Test_SO013', - 'order_line': [ - (0, 0, { - 'name': '[CARD] Individual Workplace', - 'purchase_price': 0.0, - 'price_unit': 70.0, - 'product_uom': self.product_uom_id, - 'product_uom_qty': 1.0, - 'state': 'draft', - 'product_id': self.product_id})], - 'partner_id': self.partner_id, - 'partner_invoice_id': self.partner_invoice_address_id, - 'partner_shipping_id': self.partner_invoice_address_id, - 'pricelist_id': self.pricelist_id}) - # Verify that margin field of Sale Order Lines gets bind with the value. - self.assertEqual(sale_order_so13.order_line[0].margin, 70.00, "Sales order profit should be 70.00") - self.assertEqual(sale_order_so13.order_line[0].margin_percent, 1.0, "Sales order margin percentage should be 100.00") - # Verify that margin field gets bind with the value. - self.assertEqual(sale_order_so13.margin, 70.00, "Sales order profit should be 70.00") - self.assertEqual(sale_order_so13.margin_percent, 1.00, "Sales order margin percentage should be 100.00") + order = self.empty_order + order.order_line = [Command.create({ + 'product_id': self.product.id, + 'price_unit': 70.0, + 'product_uom_qty': 1.0, + })] - def test_sale_margin3(self): - """ Test the margin and margin percentage when product with multiple quantity""" - sale_order_so14 = self.SaleOrder.create({ - 'date_order': datetime.today(), - 'name': 'Test_SO014', - 'order_line': [ - (0, 0, { - 'name': '[CARD] Individual Workplace', - 'purchase_price': 50.0, - 'price_unit': 100.0, - 'product_uom': self.product_uom_id, - 'product_uom_qty': 3.0, - 'state': 'draft', - 'product_id': self.product_id}), - (0, 0, { - 'name': 'Line without product_uom', - 'price_unit': -50.0, - 'purchase_price': 0.0, - 'product_uom_qty': 1.0, - 'state': 'draft', - 'product_id': self.product_id})], - 'partner_id': self.partner_id, - 'partner_invoice_id': self.partner_invoice_address_id, - 'partner_shipping_id': self.partner_invoice_address_id, - 'pricelist_id': self.pricelist_id}) - # Confirm the sales order. - sale_order_so14.action_confirm() # Verify that margin field of Sale Order Lines gets bind with the value. - self.assertEqual(sale_order_so14.order_line[0].margin, 150.00, "Sales order profit should be 150.00") - self.assertEqual(sale_order_so14.order_line[0].margin_percent, 0.5, "Sales order margin should be 100%") - self.assertEqual(sale_order_so14.order_line[1].margin, -50.00, "Sales order profit should be -50.00") - self.assertEqual(sale_order_so14.order_line[1].margin_percent, 1.0, "Sales order margin should be 100%") + self.assertEqual(order.order_line[0].margin, 70.00, "Sales order profit should be 70.00") + self.assertEqual(order.order_line[0].margin_percent, 1.0, "Sales order margin percentage should be 100.00") # Verify that margin field gets bind with the value. - self.assertEqual(sale_order_so14.margin, 100.00, "Sales order profit should be 100.00") - self.assertEqual(sale_order_so14.margin_percent, 0.4, "Sales order margin should be 40%") + self.assertEqual(order.margin, 70.00, "Sales order profit should be 70.00") + self.assertEqual(order.margin_percent, 1.00, "Sales order margin percentage should be 100.00") + + def test_margin_considering_product_qty(self): + """ Test the margin and margin percentage when product with multiple quantity""" + order = self.empty_order + self.service_product.standard_price = 50.0 + + order.order_line = [ + Command.create({ + 'price_unit': 100.0, + 'product_uom_qty': 3.0, + 'product_id': self.service_product.id, + }), + Command.create({ + 'price_unit': -50.0, + 'product_uom_qty': 1.0, + 'product_id': self.product.id, + }), + ] + + # Confirm the sales order. + order.action_confirm() + # Verify that margin field of Sale Order Lines gets bind with the value. + self.assertEqual(order.order_line[0].margin, 150.00, "Sales order profit should be 150.00") + self.assertEqual(order.order_line[0].margin_percent, 0.5, "Sales order margin should be 100%") + self.assertEqual(order.order_line[1].margin, -50.00, "Sales order profit should be -50.00") + self.assertEqual(order.order_line[1].margin_percent, 1.0, "Sales order margin should be 100%") + # Verify that margin field gets bind with the value. + self.assertEqual(order.margin, 100.00, "Sales order profit should be 100.00") + self.assertEqual(order.margin_percent, 0.4, "Sales order margin should be 40%")