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