Handle remaining qty when synchronizing supplier and customer moves
issue6833 review39781002
This commit is contained in:
1 parent
d6ba357f76
commit
1b1e5558f9
3 files changed
+202
-15
No files matched your search
@@ -445,37 +445,90 @@ class ShipmentDrop(Workflow, ModelSQL, ModelView):
|
||||
for shipment in shipments:
|
||||
product_qty = defaultdict(lambda: 0)
|
||||
product_cost = defaultdict(lambda: 0)
|
||||
|
||||
for c_move in shipment.customer_moves:
|
||||
if c_move.state == 'cancel':
|
||||
continue
|
||||
product_qty[c_move.product] += UoM.compute_qty(
|
||||
c_move.uom, c_move.quantity, c_move.product.default_uom,
|
||||
round=False)
|
||||
|
||||
for s_move in shipment.supplier_moves:
|
||||
if s_move.state == 'cancel':
|
||||
continue
|
||||
product_qty[s_move.product] += UoM.compute_qty(s_move.uom,
|
||||
s_move.quantity, s_move.product.default_uom, round=False)
|
||||
if s_move.cost_price:
|
||||
internal_quantity = Decimal(str(s_move.internal_quantity))
|
||||
product_cost[s_move.product] += (
|
||||
s_move.unit_price * internal_quantity)
|
||||
|
||||
quantity = UoM.compute_qty(
|
||||
s_move.uom, s_move.quantity, s_move.product.default_uom,
|
||||
round=False)
|
||||
if product_qty[s_move.product]:
|
||||
if quantity <= product_qty[s_move.product]:
|
||||
product_qty[s_move.product] -= quantity
|
||||
continue
|
||||
else:
|
||||
out_quantity = (
|
||||
quantity - product_qty[s_move.product])
|
||||
out_quantity = UoM.compute_qty(
|
||||
s_move.product.default_uom, out_quantity,
|
||||
s_move.uom)
|
||||
product_qty[s_move.product] = 0
|
||||
else:
|
||||
out_quantity = s_move.quantity
|
||||
|
||||
if not out_quantity:
|
||||
continue
|
||||
unit_price = UoM.compute_price(
|
||||
s_move.product.default_uom, s_move.product.list_price,
|
||||
s_move.uom)
|
||||
new_customer_move = shipment._get_customer_move(s_move)
|
||||
new_customer_move.quantity = out_quantity
|
||||
new_customer_move.unit_price = unit_price
|
||||
to_save.append(new_customer_move)
|
||||
|
||||
for product, cost in product_cost.iteritems():
|
||||
qty = Decimal(str(product_qty[product]))
|
||||
if qty:
|
||||
product_cost[product] = (cost / qty).quantize(cost_exp)
|
||||
for c_move in shipment.customer_moves:
|
||||
if c_move.state == 'cancel':
|
||||
for c_move in list(shipment.customer_moves) + to_save:
|
||||
if c_move.id is not None and c_move.state == 'cancel':
|
||||
continue
|
||||
if product_qty[c_move.product] <= 0:
|
||||
c_move.shipment = None
|
||||
else:
|
||||
move_qty = UoM.compute_qty(c_move.uom, c_move.quantity,
|
||||
c_move.product.default_uom, round=False)
|
||||
qty = min(product_qty[c_move.product], move_qty)
|
||||
c_move.quantity = UoM.compute_qty(
|
||||
c_move.cost_price = product_cost[c_move.product]
|
||||
if c_move.id is None:
|
||||
continue
|
||||
if product_qty[c_move.product] > 0:
|
||||
exc_qty = UoM.compute_qty(
|
||||
c_move.product.default_uom,
|
||||
qty, c_move.uom)
|
||||
product_qty[c_move.product] -= qty
|
||||
c_move.cost_price = product_cost[c_move.product]
|
||||
to_save.append(c_move)
|
||||
product_qty[c_move.product], c_move.uom)
|
||||
removed_qty = UoM.compute_qty(
|
||||
c_move.uom, min(exc_qty, c_move.quantity),
|
||||
c_move.product.default_uom, round=False)
|
||||
c_move.quantity = max(
|
||||
0, c_move.uom.round(c_move.quantity - exc_qty))
|
||||
product_qty[c_move.product] -= removed_qty
|
||||
to_save.append(c_move)
|
||||
|
||||
if to_save:
|
||||
Move.save(to_save)
|
||||
|
||||
def _get_customer_move(self, move):
|
||||
pool = Pool()
|
||||
Move = pool.get('stock.move')
|
||||
return Move(
|
||||
from_location=move.to_location,
|
||||
to_location=self.customer.customer_location,
|
||||
product=move.product,
|
||||
uom=move.uom,
|
||||
quantity=move.quantity,
|
||||
shipment=self,
|
||||
planned_date=self.planned_date,
|
||||
company=move.company,
|
||||
currency=move.company.currency,
|
||||
unit_price=move.unit_price,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('waiting')
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
==================================
|
||||
Sale Supply Drop Shipment Scenario
|
||||
==================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime
|
||||
>>> from dateutil.relativedelta import relativedelta
|
||||
>>> from decimal import Decimal
|
||||
>>> from proteus import Model, Wizard
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
>>> from trytond.modules.company.tests.tools import create_company, \
|
||||
... get_company
|
||||
>>> from trytond.modules.account.tests.tools import create_fiscalyear, \
|
||||
... create_chart, get_accounts
|
||||
>>> from trytond.modules.account_invoice.tests.tools import \
|
||||
... set_fiscalyear_invoice_sequences, create_payment_term
|
||||
>>> today = datetime.date.today()
|
||||
|
||||
Install sale_supply_drop_shipment, sale, purchase::
|
||||
|
||||
>>> config = activate_modules([
|
||||
... 'sale_supply_drop_shipment',
|
||||
... 'sale',
|
||||
... 'purchase',
|
||||
... ])
|
||||
|
||||
Create company::
|
||||
|
||||
>>> _ = create_company()
|
||||
>>> company = get_company()
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(
|
||||
... create_fiscalyear(company))
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Create chart of accounts::
|
||||
|
||||
>>> _ = create_chart(company)
|
||||
>>> accounts = get_accounts(company)
|
||||
>>> revenue = accounts['revenue']
|
||||
>>> expense = accounts['expense']
|
||||
|
||||
Create parties::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> supplier = Party(name='Supplier')
|
||||
>>> supplier.save()
|
||||
>>> customer = Party(name='Customer')
|
||||
>>> customer.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> ProductSupplier = Model.get('purchase.product_supplier')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'goods'
|
||||
>>> template.purchasable = True
|
||||
>>> template.salable = True
|
||||
>>> template.list_price = Decimal('10')
|
||||
>>> template.account_expense = expense
|
||||
>>> template.account_revenue = revenue
|
||||
>>> template.supply_on_sale = True
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
>>> product_supplier = ProductSupplier()
|
||||
>>> product_supplier.product = template
|
||||
>>> product_supplier.party = supplier
|
||||
>>> product_supplier.drop_shipment = True
|
||||
>>> product_supplier.lead_time = datetime.timedelta(0)
|
||||
>>> product_supplier.save()
|
||||
|
||||
Create payment term::
|
||||
|
||||
>>> payment_term = create_payment_term()
|
||||
>>> payment_term.save()
|
||||
|
||||
Sale 250 products::
|
||||
|
||||
>>> Sale = Model.get('sale.sale')
|
||||
>>> sale = Sale()
|
||||
>>> sale.party = customer
|
||||
>>> sale.payment_term = payment_term
|
||||
>>> sale_line = sale.lines.new()
|
||||
>>> sale_line.product = product
|
||||
>>> sale_line.quantity = 250
|
||||
>>> sale.click('quote')
|
||||
>>> sale.click('confirm')
|
||||
>>> sale.click('process')
|
||||
|
||||
Create Purchase from Request::
|
||||
|
||||
>>> Purchase = Model.get('purchase.purchase')
|
||||
>>> PurchaseRequest = Model.get('purchase.request')
|
||||
>>> purchase_request, = PurchaseRequest.find()
|
||||
>>> purchase_request.quantity
|
||||
250.0
|
||||
>>> create_purchase = Wizard('purchase.request.create_purchase',
|
||||
... [purchase_request])
|
||||
>>> purchase, = Purchase.find()
|
||||
>>> purchase.payment_term = payment_term
|
||||
>>> purchase.click('quote')
|
||||
>>> purchase.click('confirm')
|
||||
>>> purchase.click('process')
|
||||
>>> purchase.state
|
||||
u'processing'
|
||||
|
||||
>>> sale.reload()
|
||||
>>> shipment, = sale.drop_shipments
|
||||
|
||||
The supplier sends more than expected::
|
||||
|
||||
>>> move, = shipment.supplier_moves
|
||||
>>> move.quantity = 300
|
||||
>>> shipment.click('ship')
|
||||
|
||||
Another move has been created to synchronize supplier and customer quantities::
|
||||
|
||||
>>> len(shipment.customer_moves)
|
||||
2
|
||||
>>> sum(m.quantity for m in shipment.customer_moves)
|
||||
300.0
|
||||
@@ -22,4 +22,9 @@ def suite():
|
||||
tearDown=doctest_teardown, encoding='utf-8',
|
||||
checker=doctest_checker,
|
||||
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
|
||||
suite.addTests(doctest.DocFileSuite(
|
||||
'scenario_sale_supply_drop_shipment_remaining_qty.rst',
|
||||
tearDown=doctest_teardown, encoding='utf-8',
|
||||
checker=doctest_checker,
|
||||
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
|
||||
return suite
|
||||
Reference in new issue
Block a user