diff --git a/sale_margin_delivered/__manifest__.py b/sale_margin_delivered/__manifest__.py
index e8682ada..d056f316 100644
--- a/sale_margin_delivered/__manifest__.py
+++ b/sale_margin_delivered/__manifest__.py
@@ -2,7 +2,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Sale Margin Delivered",
- "version": "16.0.1.0.5",
+ "version": "16.0.2.0.0",
"author": "Tecnativa, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/margin-analysis",
"category": "Sales",
diff --git a/sale_margin_delivered/migrations/16.0.2.0.0/post-migration.py b/sale_margin_delivered/migrations/16.0.2.0.0/post-migration.py
new file mode 100644
index 00000000..64d5bf8a
--- /dev/null
+++ b/sale_margin_delivered/migrations/16.0.2.0.0/post-migration.py
@@ -0,0 +1,15 @@
+import logging
+
+_logger = logging.getLogger(__name__)
+
+
+def migrate(cr, version=None):
+ """Store margin delivered precentage as a fraction of 1"""
+ cr.execute(
+ """
+ UPDATE sale_order_line
+ SET margin_delivered_percent = margin_delivered_percent / 100
+ WHERE margin_delivered_percent > 0
+ """
+ )
+ _logger.info("Updated %d records", cr.rowcount)
diff --git a/sale_margin_delivered/models/sale_margin.py b/sale_margin_delivered/models/sale_margin.py
index 50ceb550..29682716 100644
--- a/sale_margin_delivered/models/sale_margin.py
+++ b/sale_margin_delivered/models/sale_margin.py
@@ -22,7 +22,7 @@ class SaleOrderLine(models.Model):
help="Margin percent between the Unit Price with discounts and "
"Delivered Unit Cost.\n\n"
"Formula: ((Unit Price with Discounts - Average Unit Cost of "
- "delivered products) / Unit Price with Discounts) * 100.0",
+ "delivered products) / Unit Price with Discounts)",
)
purchase_price_delivery = fields.Float(
compute="_compute_margin_delivered",
@@ -72,6 +72,14 @@ def _compute_margin_delivered(self):
self.margin_delivered_percent = 0.0
self.purchase_price_delivery = 0.0
for line in self.filtered("qty_delivered"):
+ # we need to compute the price reduce to avoid rounding issues
+ # the one stored in the line is rounded to the product price precision
+ price_reduce_taxexcl = (
+ line.price_subtotal / line.product_uom_qty
+ if line.product_uom_qty
+ else 0.0
+ )
+
if line.product_id.type != "product":
currency = line.order_id.pricelist_id.currency_id
price = line.purchase_price
@@ -91,13 +99,11 @@ def _compute_margin_delivered(self):
)
# Inverse qty_delivered because outgoing quantities are negative
line.margin_delivered = -qty_delivered * (
- line.price_reduce - line.purchase_price_delivery
+ price_reduce_taxexcl - line.purchase_price_delivery
)
# compute percent margin based on delivered quantities or ordered
# quantities
- if line.price_reduce:
+ if price_reduce_taxexcl:
line.margin_delivered_percent = (
- (line.price_reduce - line.purchase_price_delivery)
- / line.price_reduce
- * 100.0
- )
+ price_reduce_taxexcl - line.purchase_price_delivery
+ ) / price_reduce_taxexcl
diff --git a/sale_margin_delivered/static/description/index.html b/sale_margin_delivered/static/description/index.html
index ed03cc6b..746e3daa 100644
--- a/sale_margin_delivered/static/description/index.html
+++ b/sale_margin_delivered/static/description/index.html
@@ -8,10 +8,11 @@
/*
:Author: David Goodger (goodger@python.org)
-:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
+:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
+Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
@@ -274,7 +275,7 @@
margin-left: 2em ;
margin-right: 2em }
-pre.code .ln { color: grey; } /* line numbers */
+pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
@@ -300,7 +301,7 @@
span.pre {
white-space: pre }
-span.problematic {
+span.problematic, pre.problematic {
color: red }
span.section-subtitle {
@@ -452,7 +453,9 @@
This module is maintained by the OCA.
-
+
+
+
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
diff --git a/sale_margin_delivered/tests/test_sale_margin_delivered.py b/sale_margin_delivered/tests/test_sale_margin_delivered.py
index bd0c5f9c..bd9ebb59 100644
--- a/sale_margin_delivered/tests/test_sale_margin_delivered.py
+++ b/sale_margin_delivered/tests/test_sale_margin_delivered.py
@@ -91,7 +91,7 @@ def test_sale_margin_delivered(self):
picking._action_done()
order_line = sale_order.order_line[:1]
self.assertEqual(order_line.margin_delivered, 30.0)
- self.assertEqual(order_line.margin_delivered_percent, 50.0)
+ self.assertEqual(order_line.margin_delivered_percent, 0.5)
self.assertEqual(order_line.purchase_price_delivery, order_line.purchase_price)
def test_sale_margin_delivered_excess(self):
@@ -104,7 +104,7 @@ def test_sale_margin_delivered_excess(self):
picking._action_done()
order_line = sale_order.order_line[:1]
self.assertEqual(order_line.margin_delivered, 120.0)
- self.assertEqual(order_line.margin_delivered_percent, 50.0)
+ self.assertEqual(order_line.margin_delivered_percent, 0.5)
self.assertEqual(order_line.purchase_price_delivery, order_line.purchase_price)
def test_sale_margin_zero(self):
@@ -142,7 +142,7 @@ def test_sale_margin_delivered_return_to_refund(self):
picking_return._action_done()
order_line = sale_order.order_line[:1]
self.assertEqual(order_line.margin_delivered, 30.0)
- self.assertEqual(order_line.margin_delivered_percent, 50.0)
+ self.assertEqual(order_line.margin_delivered_percent, 0.5)
self.assertEqual(order_line.purchase_price_delivery, order_line.purchase_price)
def test_sale_margin_delivered_return_to_refund_excess(self):
@@ -155,7 +155,7 @@ def test_sale_margin_delivered_return_to_refund_excess(self):
picking_return._action_done()
order_line = sale_order.order_line[:1]
self.assertEqual(order_line.margin_delivered, 90.0)
- self.assertEqual(order_line.margin_delivered_percent, 50.0)
+ self.assertEqual(order_line.margin_delivered_percent, 0.5)
self.assertEqual(order_line.purchase_price_delivery, order_line.purchase_price)
def test_sale_margin_delivered_return_no_refund(self):
@@ -168,7 +168,7 @@ def test_sale_margin_delivered_return_no_refund(self):
picking_return._action_done()
order_line = sale_order.order_line[:1]
self.assertEqual(order_line.margin_delivered, 60.0)
- self.assertEqual(order_line.margin_delivered_percent, 50.0)
+ self.assertEqual(order_line.margin_delivered_percent, 0.5)
self.assertEqual(order_line.purchase_price_delivery, order_line.purchase_price)
def test_sale_margin_delivered_return_no_refund_excess(self):
@@ -181,5 +181,40 @@ def test_sale_margin_delivered_return_no_refund_excess(self):
picking_return._action_done()
order_line = sale_order.order_line[:1]
self.assertEqual(order_line.margin_delivered, 120.0)
- self.assertEqual(order_line.margin_delivered_percent, 50.0)
+ self.assertEqual(order_line.margin_delivered_percent, 0.5)
+ self.assertEqual(order_line.purchase_price_delivery, order_line.purchase_price)
+
+ def test_sale_margin_delivered_precision(self):
+ self.product.standard_price = 10.30
+ self.product.list_price = 20.17
+ sale_order = self._new_sale_order()
+ sale_order.order_line[:1].discount = 17.0
+ sale_order.action_confirm()
+ picking = sale_order.picking_ids
+ picking.action_assign()
+ picking.move_line_ids.qty_done = 6.0
+ picking._action_done()
+ order_line = sale_order.order_line[:1]
+ # price_subtotal is rounded
+ self.assertEqual(order_line.price_subtotal, 100.45)
+ # the unit reduce price will be computed as 100.45 / 6 = 16.741666666666667
+ # it should not be rounded to 16.74
+ # margin_delivered: round(6 * ((100.45 /6) - 10.30)) != round(6 * (16.74 - 10.30))
+ self.assertEqual(order_line.margin_delivered, 38.65)
+ self.assertAlmostEqual(order_line.margin_delivered_percent, 0.38476854156296)
+ self.assertEqual(order_line.purchase_price_delivery, order_line.purchase_price)
+
+ def test_sale_margin_no_cost(self):
+ self.product.standard_price = 0.0
+ self.product.list_price = 20
+ sale_order = self._new_sale_order()
+ sale_order.action_confirm()
+ picking = sale_order.picking_ids
+ picking.action_assign()
+ picking.move_line_ids.qty_done = 6.0
+ picking._action_done()
+ order_line = sale_order.order_line[:1]
+ # price_subtotal is rounded
+ self.assertEqual(order_line.margin_delivered, 120)
+ self.assertAlmostEqual(order_line.margin_delivered_percent, 1)
self.assertEqual(order_line.purchase_price_delivery, order_line.purchase_price)
diff --git a/sale_margin_delivered/views/sale_margin_delivered_view.xml b/sale_margin_delivered/views/sale_margin_delivered_view.xml
index 696a40c3..b07662ac 100644
--- a/sale_margin_delivered/views/sale_margin_delivered_view.xml
+++ b/sale_margin_delivered/views/sale_margin_delivered_view.xml
@@ -6,7 +6,7 @@
sale.order
-
+
diff --git a/sale_margin_delivered_dropshipping/tests/test_sale_margin_delivered_dropship.py b/sale_margin_delivered_dropshipping/tests/test_sale_margin_delivered_dropship.py
index 296d1d88..b986bfab 100644
--- a/sale_margin_delivered_dropshipping/tests/test_sale_margin_delivered_dropship.py
+++ b/sale_margin_delivered_dropshipping/tests/test_sale_margin_delivered_dropship.py
@@ -48,5 +48,5 @@ def test_sale_margin_delivered_dropship(self):
picking_return._action_done()
order_line = sale_order.order_line[:1]
self.assertEqual(order_line.margin_delivered, 30.0)
- self.assertEqual(order_line.margin_delivered_percent, 50.0)
+ self.assertEqual(order_line.margin_delivered_percent, 0.5)
self.assertEqual(order_line.purchase_price_delivery, order_line.purchase_price)