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:
@@ -1,3 +1,4 @@
|
||||
* Recompute cost price of drop shipment
|
||||
* Add support for Python 3.10
|
||||
* Remove support for Python 3.6
|
||||
|
||||
|
||||
+2
-1
@@ -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
@@ -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)
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user