changeset b28d22ff345e in modules/sale_supply_drop_shipment:default
details: 
https://hg.tryton.org/modules/sale_supply_drop_shipment?cmd=changeset&node=b28d22ff345e
description:
        Recompute cost price of drop shipment

        Since issue9962 the unit price of the supplier move can be updated 
later so we
        must recompute the cost price of the customer moves.

        issue11295
        review364951003
diffstat:

 CHANGELOG                                    |   1 +
 __init__.py                                  |   3 +-
 product.py                                   |  29 ++++++++++++++++
 stock.py                                     |  50 ++++++++++++++++++++-------
 tests/scenario_sale_supply_drop_shipment.rst |  38 ++++++++++++++++++++-
 5 files changed, 106 insertions(+), 15 deletions(-)

diffs (225 lines):

diff -r 6a2afcc843a5 -r b28d22ff345e CHANGELOG
--- a/CHANGELOG Wed Mar 09 00:57:17 2022 +0100
+++ b/CHANGELOG Sat Mar 26 12:15:13 2022 +0100
@@ -1,3 +1,4 @@
+* Recompute cost price of drop shipment
 * Add support for Python 3.10
 * Remove support for Python 3.6
 
diff -r 6a2afcc843a5 -r b28d22ff345e __init__.py
--- a/__init__.py       Wed Mar 09 00:57:17 2022 +0100
+++ b/__init__.py       Sat Mar 26 12:15:13 2022 +0100
@@ -3,7 +3,7 @@
 
 from trytond.pool import Pool
 
-from . import party, purchase, sale, stock
+from . import party, product, purchase, sale, stock
 
 
 def register():
@@ -22,6 +22,7 @@
         purchase.Purchase,
         purchase.Line,
         purchase.ProductSupplier,
+        product.Product,
         module='sale_supply_drop_shipment', type_='model')
     Pool.register(
         purchase.RequestCreatePurchase,
diff -r 6a2afcc843a5 -r b28d22ff345e product.py
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/product.py        Sat Mar 26 12:15:13 2022 +0100
@@ -0,0 +1,29 @@
+# This file is part of Tryton.  The COPYRIGHT file at the top level of
+# this repository contains the full copyright notices and license terms.
+from trytond.pool import Pool, PoolMeta
+from trytond.tools import grouped_slice
+
+
+class Product(metaclass=PoolMeta):
+    __name__ = 'product.product'
+
+    @classmethod
+    def recompute_cost_price(cls, products, start=None):
+        pool = Pool()
+        Move = pool.get('stock.move')
+        Shipment = pool.get('stock.shipment.drop')
+        shipments = set()
+        for sub_products in grouped_slice(products):
+            domain = [
+                ('unit_price_updated', '=', True),
+                cls._domain_moves_cost(),
+                ('product', 'in', [p.id for p in sub_products]),
+                ('shipment', 'like', 'stock.shipment.drop,%'),
+                ]
+            if start:
+                domain.append(('effective_date', '>=', start))
+            for move in Move.search(domain, order=[]):
+                shipments.add(move.shipment)
+        shipments = Shipment.browse(list(shipments))
+        Shipment.set_cost(shipments)
+        super().recompute_cost_price(products, start=start)
diff -r 6a2afcc843a5 -r b28d22ff345e stock.py
--- a/stock.py  Wed Mar 09 00:57:17 2022 +0100
+++ b/stock.py  Sat Mar 26 12:15:13 2022 +0100
@@ -443,7 +443,6 @@
         to_save = []
         for shipment in shipments:
             product_qty = defaultdict(int)
-            product_cost = defaultdict(int)
 
             for c_move in shipment.customer_moves:
                 if c_move.state == 'cancelled':
@@ -456,10 +455,6 @@
             for s_move in shipment.supplier_moves:
                 if s_move.state == 'cancelled':
                     continue
-                internal_quantity = Decimal(str(s_move.internal_quantity))
-                product_cost[s_move.product] += (
-                    s_move.get_cost_price() * internal_quantity)
-
                 quantity = UoM.compute_qty(
                     s_move.uom, s_move.quantity, s_move.product.default_uom,
                     round=False)
@@ -488,15 +483,8 @@
                 new_customer_move.unit_price = unit_price
                 to_save.append(new_customer_move)
 
-            for product, cost in product_cost.items():
-                qty = Decimal(str(s_product_qty[product]))
-                if qty:
-                    product_cost[product] = round_price(cost / qty)
             for c_move in list(shipment.customer_moves) + to_save:
-                if c_move.id is not None and c_move.state == 'cancelled':
-                    continue
-                c_move.cost_price = product_cost[c_move.product]
-                if c_move.id is None:
+                if c_move.id is None or c_move.state == 'cancelled':
                     continue
                 if product_qty[c_move.product] > 0:
                     exc_qty = UoM.compute_qty(
@@ -528,6 +516,41 @@
             )
 
     @classmethod
+    def set_cost(cls, shipments):
+        pool = Pool()
+        Move = pool.get('stock.move')
+        UoM = pool.get('product.uom')
+
+        to_save = []
+        for shipment in shipments:
+            product_cost = defaultdict(int)
+            s_product_qty = defaultdict(int)
+            for s_move in shipment.supplier_moves:
+                if s_move.state == 'cancelled':
+                    continue
+                internal_quantity = Decimal(str(s_move.internal_quantity))
+                product_cost[s_move.product] += (
+                    s_move.get_cost_price() * internal_quantity)
+
+                quantity = UoM.compute_qty(
+                    s_move.uom, s_move.quantity, s_move.product.default_uom,
+                    round=False)
+                s_product_qty[s_move.product] += quantity
+
+            for product, cost in product_cost.items():
+                qty = Decimal(str(s_product_qty[product]))
+                if qty:
+                    product_cost[product] = round_price(cost / qty)
+
+            for c_move in shipment.customer_moves:
+                cost_price = product_cost[c_move.product]
+                if cost_price != c_move.cost_price:
+                    c_move.cost_price = cost_price
+                    to_save.append(c_move)
+        if to_save:
+            Move.save(to_save)
+
+    @classmethod
     @ModelView.button
     @Workflow.transition('waiting')
     def wait(cls, shipments):
@@ -590,6 +613,7 @@
         pool = Pool()
         Move = pool.get('stock.move')
         Date = pool.get('ir.date')
+        cls.set_cost(shipments)
         Move.do([m for s in shipments for m in s.customer_moves])
         for company, shipments in groupby(shipments, key=lambda s: s.company):
             with Transaction().set_context(company=company.id):
diff -r 6a2afcc843a5 -r b28d22ff345e 
tests/scenario_sale_supply_drop_shipment.rst
--- a/tests/scenario_sale_supply_drop_shipment.rst      Wed Mar 09 00:57:17 
2022 +0100
+++ b/tests/scenario_sale_supply_drop_shipment.rst      Sat Mar 26 12:15:13 
2022 +0100
@@ -62,10 +62,23 @@
     >>> stock_force_group, = Group.find([
     ...     ('name', '=', 'Stock Force Assignment'),
     ...     ])
+    >>> product_admin_group, = Group.find([
+    ...     ('name', '=', "Product Administration"),
+    ...     ])
     >>> stock_user.groups.append(stock_group)
     >>> stock_user.groups.append(stock_force_group)
+    >>> stock_user.groups.append(product_admin_group)
     >>> stock_user.save()
 
+Create account user::
+
+    >>> account_user = User()
+    >>> account_user.name = "Account"
+    >>> account_user.login = 'account'
+    >>> account_group, = Group.find([('name', '=', "Account")])
+    >>> account_user.groups.append(account_group)
+    >>> account_user.save()
+
 Create fiscal year::
 
     >>> fiscalyear = set_fiscalyear_invoice_sequences(
@@ -190,7 +203,6 @@
     >>> move.unit_price
     Decimal('10.0000')
     >>> move.cost_price
-    Decimal('3.0000')
     >>> set_user(sale_user)
     >>> sale.reload()
     >>> sale.shipments
@@ -204,6 +216,9 @@
     >>> shipment.click('done')
     >>> shipment.state
     'done'
+    >>> move, = shipment.customer_moves
+    >>> move.cost_price
+    Decimal('3.0000')
     >>> set_user(sale_user)
     >>> sale.reload()
     >>> sale.shipments
@@ -251,8 +266,29 @@
     >>> sale.shipment_state
     'exception'
 
+Receive purchase invoice at different price::
+
+    >>> set_user(account_user)
+    >>> invoice, = purchase.invoices
+    >>> invoice_line, = invoice.lines
+    >>> invoice_line.unit_price = Decimal('4.0000')
+    >>> invoice.invoice_date = today
+    >>> invoice.click('post')
+
+    >>> set_user(stock_user)
+
+    >>> recompute = Wizard('product.recompute_cost_price', [product])
+    >>> recompute.execute('recompute')
+
+    >>> shipment, = [s for s in purchase.drop_shipments
+    ...     if s.state == 'done']
+    >>> move, = shipment.customer_moves
+    >>> move.cost_price
+    Decimal('4.0000')
+
 Cancelling the workflow on the purchase step::
 
+    >>> set_user(sale_user)
     >>> sale = Sale()
     >>> sale.party = customer
     >>> sale.payment_term = payment_term

Reply via email to