From ee5cb279626635a764ed2cc962ff406f05ec50fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 24 Jan 2023 10:53:23 +0100 Subject: [PATCH] Create lot for output product when needed When the output product has a sequence and a lot is required, we create a new lot to set on the output move. Closes #11923 --- modules/production/CHANGELOG | 1 + modules/production/__init__.py | 1 + modules/production/production.py | 26 ++++++- modules/production/setup.py | 5 +- .../tests/scenario_production_lot_number.rst | 70 +++++++++++++++++++ modules/stock_lot/product.py | 11 +++ modules/stock_lot/stock.py | 6 ++ 7 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 modules/production/tests/scenario_production_lot_number.rst diff --git a/modules/production/CHANGELOG b/modules/production/CHANGELOG index da70da7262..4ba30ff3d7 100644 --- a/modules/production/CHANGELOG +++ b/modules/production/CHANGELOG @@ -1,3 +1,4 @@ +* Create lot for output product when needed * Assign all possible productions when trying Version 6.6.0 - 2022-10-31 diff --git a/modules/production/__init__.py b/modules/production/__init__.py index 2c1c637119..0b6768155d 100644 --- a/modules/production/__init__.py +++ b/modules/production/__init__.py @@ -28,6 +28,7 @@ def register(): module='production', type_='model') Pool.register( stock.LotTrace, + production.Production_Lot, module='production', type_='model', depends=['stock_lot']) Pool.register( bom.OpenBOMTree, diff --git a/modules/production/production.py b/modules/production/production.py index baa749c385..2e2e27e5b9 100644 --- a/modules/production/production.py +++ b/modules/production/production.py @@ -15,7 +15,7 @@ from trytond.model import ( from trytond.modules.company.model import employee_field, set_employee from trytond.modules.product import price_digits, round_price from trytond.modules.stock.shipment import ShipmentAssignMixin -from trytond.pool import Pool +from trytond.pool import Pool, PoolMeta from trytond.pyson import Bool, Eval, If from trytond.transaction import Transaction @@ -830,3 +830,27 @@ class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView): if self.warehouse: return (self.warehouse.production_output_location or self.warehouse.storage_location) + + +class Production_Lot(metaclass=PoolMeta): + __name__ = 'production' + + @classmethod + @ModelView.button + @Workflow.transition('done') + def done(cls, productions): + pool = Pool() + Lot = pool.get('stock.lot') + Move = pool.get('stock.move') + lots, moves = [], [] + for production in productions: + for move in production.outputs: + if not move.lot and move.product.lot_is_required( + move.from_location, move.to_location): + move.add_lot() + if move.lot: + lots.append(move.lot) + moves.append(move) + Lot.save(lots) + Move.save(moves) + super().done(productions) diff --git a/modules/production/setup.py b/modules/production/setup.py index 92bb72c777..6c865335f9 100755 --- a/modules/production/setup.py +++ b/modules/production/setup.py @@ -47,7 +47,10 @@ for dep in info.get('depends', []): requires.append(get_require_version('trytond_%s' % dep)) requires.append(get_require_version('trytond')) -tests_require = [get_require_version('proteus')] +tests_require = [ + get_require_version('proteus'), + get_require_version('trytond_stock_lot'), + ] setup(name=name, version=version, diff --git a/modules/production/tests/scenario_production_lot_number.rst b/modules/production/tests/scenario_production_lot_number.rst new file mode 100644 index 0000000000..a6cd950410 --- /dev/null +++ b/modules/production/tests/scenario_production_lot_number.rst @@ -0,0 +1,70 @@ +==================================== +Stock Lot Number Production Scenario +==================================== + +Imports:: + + >>> from decimal import Decimal + + >>> from proteus import Model + >>> from trytond.tests.tools import activate_modules + >>> from trytond.modules.company.tests.tools import ( + ... create_company, get_company) + +Activate modules:: + + >>> config = activate_modules(['stock_lot', 'production']) + + >>> ProductTemplate = Model.get('product.template') + >>> Production = Model.get('production') + >>> Production = Model.get('production') + >>> Sequence = Model.get('ir.sequence') + >>> SequenceType = Model.get('ir.sequence.type') + >>> UoM = Model.get('product.uom') + +Create company:: + + >>> _ = create_company() + >>> company = get_company() + +Create lot sequence:: + + >>> sequence_type, = SequenceType.find( + ... [('name', '=', "Stock Lot")], limit=1) + >>> sequence = Sequence(name="Lot", sequence_type=sequence_type) + >>> sequence.save() + +Create product:: + + >>> unit, = UoM.find([('name', '=', 'Unit')]) + + >>> template = ProductTemplate() + >>> template.name = "Product" + >>> template.default_uom = unit + >>> template.type = 'goods' + >>> template.list_price = Decimal('10.0000') + >>> template.lot_required = ['storage'] + >>> template.lot_sequence = sequence + >>> template.save() + >>> product, = template.products + +Make a production:: + + >>> production = Production() + >>> output = production.outputs.new() + >>> output.from_location = production.location + >>> output.to_location = production.warehouse.storage_location + >>> output.product = product + >>> output.quantity = 1 + >>> output.unit_price = Decimal(0) + >>> output.currency = production.company.currency + >>> production.click('wait') + >>> production.click('assign_force') + >>> production.click('run') + >>> production.click('done') + >>> production.state + 'done' + + >>> output, = production.outputs + >>> bool(output.lot) + True diff --git a/modules/stock_lot/product.py b/modules/stock_lot/product.py index 4ececa619b..fa36fc7ed7 100644 --- a/modules/stock_lot/product.py +++ b/modules/stock_lot/product.py @@ -112,3 +112,14 @@ class Product(metaclass=PoolMeta): def lot_is_required(self, from_, to): 'Is product lot required for move with "from_" and "to" location ?' return any(l.type in (self.lot_required or []) for l in [from_, to]) + + def create_lot(self): + pool = Pool() + Lot = pool.get('stock.lot') + if self.lot_sequence: + lot = Lot(product=self) + try: + lot.on_change_product() + except AttributeError: + pass + return lot diff --git a/modules/stock_lot/stock.py b/modules/stock_lot/stock.py index d05c8a5fbc..89d2b4aac3 100644 --- a/modules/stock_lot/stock.py +++ b/modules/stock_lot/stock.py @@ -433,6 +433,12 @@ class Move(metaclass=PoolMeta): def add_lots_wizard(cls, moves): pass + def add_lot(self): + if not self.lot and self.product: + lot = self.product.create_lot() + if lot: + self.lot = lot + def check_lot(self): "Check if lot is required" if (self.state == 'done'