diff --git a/CHANGELOG b/CHANGELOG index 67d93b272f..a611353c99 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Keep cost of unused input products * Use the shipment assign wizard for production * Rename production state from cancel to cancelled diff --git a/production.py b/production.py index 76dcc0e951..3beebf3a0f 100644 --- a/production.py +++ b/production.py @@ -1,5 +1,6 @@ # 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 collections import defaultdict from decimal import Decimal from itertools import chain @@ -529,7 +530,36 @@ class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView): for production in productions: sum_ = Decimal(0) prices = {} + cost = production.cost + + input_quantities = defaultdict(Decimal) + input_costs = defaultdict(Decimal) + for input_ in production.inputs: + if input_.cost_price is not None: + cost_price = input_.cost_price + else: + cost_price = input_.product.cost_price + input_quantities[input_.product] += ( + Decimal(str(input_.internal_quantity))) + input_costs[input_.product] += ( + Decimal(str(input_.internal_quantity)) * cost_price) + outputs = [] for output in production.outputs: + product = output.product + if input_quantities.get(output.product): + cost_price = ( + input_costs[product] / input_quantities[product]) + unit_price = round_price(Uom.compute_price( + product.default_uom, cost_price, output.uom)) + if output.unit_price != unit_price: + output.unit_price = unit_price + moves.append(output) + cost -= min( + unit_price * Decimal(str(output.quantity)), cost) + else: + outputs.append(output) + + for output in outputs: product = output.product with Transaction().set_context(production._list_price_context): list_price = product.list_price_used @@ -541,7 +571,7 @@ class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView): if not sum_ and production.product: prices.clear() - for output in production.outputs: + for output in outputs: if output.product == production.product: quantity = Uom.compute_qty( output.uom, output.quantity, @@ -550,13 +580,13 @@ class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView): prices[output] = quantity sum_ += quantity - for output in production.outputs: + for output in outputs: if sum_: ratio = prices.get(output, 0) / sum_ else: - ratio = Decimal(1) / len(production.outputs) + ratio = Decimal(1) / len(outputs) quantity = Decimal(str(output.quantity)) - unit_price = round_price(production.cost * ratio / quantity) + unit_price = round_price(cost * ratio / quantity) if output.unit_price != unit_price: output.unit_price = unit_price moves.append(output) diff --git a/tests/scenario_production_set_cost.rst b/tests/scenario_production_set_cost.rst index 1fd245f91e..cc61dcaf4b 100644 --- a/tests/scenario_production_set_cost.rst +++ b/tests/scenario_production_set_cost.rst @@ -51,13 +51,13 @@ Create Bill of Material:: >>> bom = BOM(name='product') >>> input = bom.inputs.new() >>> input.product = component - >>> input.quantity = 2 + >>> input.quantity = 3 >>> output = bom.outputs.new() >>> output.product = product >>> output.quantity = 1 >>> bom.save() -Make a production:: +Make a production with 2 unused component:: >>> Production = Model.get('production') >>> production = Production() @@ -67,15 +67,20 @@ Make a production:: >>> production.click('wait') >>> production.click('assign_force') >>> production.click('run') + >>> output = production.outputs.new() + >>> output.product = component + >>> output.quantity = 2 + >>> output.unit_price = Decimal(0) + >>> output.from_location = production.location + >>> output.to_location = production.warehouse.storage_location >>> production.click('done') Check output price:: >>> production.cost - Decimal('20.0000') - >>> output, = production.outputs - >>> output.unit_price - Decimal('10.0000') + Decimal('30.0000') + >>> sorted([o.unit_price for o in production.outputs]) + [Decimal('5.0000'), Decimal('10.0000')] Change cost of input:: @@ -97,9 +102,9 @@ Launch cron task:: >>> cron_set_cost.companies.append(Company(company.id)) >>> cron_set_cost.click('run_once') - >>> output.reload() - >>> output.unit_price - Decimal('12.0000') - >>> input.reload() + >>> production.reload() + >>> sorted([o.unit_price for o in production.outputs]) + [Decimal('6.0000'), Decimal('12.0000')] + >>> input, = production.inputs >>> bool(input.production_cost_price_updated) False