From 172ab5efdfc4a654700dcf9a36d96e29383d6f5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 11 Oct 2020 16:12:36 +0200 Subject: [PATCH] Keep cost of unused input products We set the average cost price of the input products as the unit price of the output of the same product. This way the cost price computation for this product does not change. issue9637 review316051002 --- CHANGELOG | 1 + production.py | 38 +++++++++++++++++++++++--- tests/scenario_production_set_cost.rst | 25 ++++++++++------- 3 files changed, 50 insertions(+), 14 deletions(-) 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