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
This commit is contained in:
Cédric Krier
2022-03-26 12:15:13 +01:00
parent bf18eef1f1
commit 89367c74db
5 changed files with 106 additions and 15 deletions
+1
View File
@@ -1,3 +1,4 @@
* Recompute cost price of drop shipment
* Add support for Python 3.10
* Remove support for Python 3.6
+2 -1
View File
@@ -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 @@ def register():
purchase.Purchase,
purchase.Line,
purchase.ProductSupplier,
product.Product,
module='sale_supply_drop_shipment', type_='model')
Pool.register(
purchase.RequestCreatePurchase,
+29
View File
@@ -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)
+37 -13
View File
@@ -443,7 +443,6 @@ class ShipmentDrop(Workflow, ModelSQL, ModelView):
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 @@ class ShipmentDrop(Workflow, ModelSQL, ModelView):
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 @@ class ShipmentDrop(Workflow, ModelSQL, ModelView):
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(
@@ -527,6 +515,41 @@ class ShipmentDrop(Workflow, ModelSQL, ModelView):
company=move.company,
)
@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')
@@ -590,6 +613,7 @@ class ShipmentDrop(Workflow, ModelSQL, ModelView):
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):
+37 -1
View File
@@ -62,10 +62,23 @@ Create stock user::
>>> 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 @@ Receiving only 100 products::
>>> move.unit_price
Decimal('10.0000')
>>> move.cost_price
Decimal('3.0000')
>>> set_user(sale_user)
>>> sale.reload()
>>> sale.shipments
@@ -204,6 +216,9 @@ Receiving only 100 products::
>>> 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 @@ As a consequence the sale order is now in exception::
>>> 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