first commit
This commit is contained in:
2
modules/stock_lot/tests/__init__.py
Normal file
2
modules/stock_lot/tests/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
BIN
modules/stock_lot/tests/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
modules/stock_lot/tests/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
modules/stock_lot/tests/__pycache__/test_module.cpython-311.pyc
Normal file
BIN
modules/stock_lot/tests/__pycache__/test_module.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
116
modules/stock_lot/tests/scenario_stock_lot_assign_try.rst
Normal file
116
modules/stock_lot/tests/scenario_stock_lot_assign_try.rst
Normal file
@@ -0,0 +1,116 @@
|
||||
=================================
|
||||
Stock Lot Assign Request Scenario
|
||||
=================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('stock_lot', create_company)
|
||||
|
||||
>>> Inventory = Model.get('stock.inventory')
|
||||
>>> Location = Model.get('stock.location')
|
||||
>>> Lot = Model.get('stock.lot')
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> ShipmentOut = Model.get('stock.shipment.out')
|
||||
|
||||
Create customer::
|
||||
|
||||
>>> customer = Party(name='Customer')
|
||||
>>> customer.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'Product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'goods'
|
||||
>>> template.list_price = Decimal('20')
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Create lot::
|
||||
|
||||
>>> lot = Lot()
|
||||
>>> lot.number = 'LOT'
|
||||
>>> lot.product = product
|
||||
>>> lot.save()
|
||||
|
||||
Get stock locations::
|
||||
|
||||
>>> warehouse_loc, = Location.find([('code', '=', 'WH')])
|
||||
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
|
||||
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
|
||||
>>> output_loc, = Location.find([('code', '=', 'OUT')])
|
||||
>>> storage_loc, = Location.find([('code', '=', 'STO')])
|
||||
>>> zone1 = Location(name='Zone1')
|
||||
>>> zone1.parent = storage_loc
|
||||
>>> zone1.type = 'storage'
|
||||
>>> zone1.save()
|
||||
>>> zone2, = zone1.duplicate(default={'name': 'Zone2'})
|
||||
|
||||
Add product quantities on zones storage locations::
|
||||
|
||||
>>> inventory = Inventory()
|
||||
>>> inventory.location = zone1
|
||||
>>> inventory_line = inventory.lines.new(product=product)
|
||||
>>> inventory_line.quantity = 100.0
|
||||
>>> inventory_line.expected_quantity = 0.0
|
||||
>>> inventory.click('confirm')
|
||||
>>> inventory.state
|
||||
'done'
|
||||
>>> inventory = Inventory()
|
||||
>>> inventory.location = zone2
|
||||
>>> inventory_line = inventory.lines.new()
|
||||
>>> inventory_line.product = product
|
||||
>>> inventory_line.lot = lot
|
||||
>>> inventory_line.quantity = 100.0
|
||||
>>> inventory_line.expected_quantity = 0.0
|
||||
>>> inventory.click('confirm')
|
||||
>>> inventory.state
|
||||
'done'
|
||||
|
||||
Create shipment out::
|
||||
|
||||
>>> shipment = ShipmentOut()
|
||||
>>> shipment.planned_date = today
|
||||
>>> shipment.customer = customer
|
||||
>>> shipment.warehouse = warehouse_loc
|
||||
>>> move = shipment.outgoing_moves.new()
|
||||
>>> move.product = product
|
||||
>>> move.unit = unit
|
||||
>>> move.quantity = 10
|
||||
>>> move.from_location = output_loc
|
||||
>>> move.to_location = customer_loc
|
||||
>>> move.company = shipment.company
|
||||
>>> move.unit_price = Decimal('1')
|
||||
>>> move.currency = shipment.company.currency
|
||||
>>> shipment.click('wait')
|
||||
|
||||
Request specific lot in stock assignation::
|
||||
|
||||
>>> move, = shipment.inventory_moves
|
||||
>>> move.lot = lot
|
||||
>>> move.save()
|
||||
|
||||
Assign the shipment::
|
||||
|
||||
>>> shipment.click('assign_try')
|
||||
>>> shipment.state
|
||||
'assigned'
|
||||
>>> move, = shipment.inventory_moves
|
||||
>>> move.from_location.name
|
||||
'Zone2'
|
||||
@@ -0,0 +1,74 @@
|
||||
==================================
|
||||
Stock Lot Inventory Count Scenario
|
||||
==================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('stock_lot', create_company)
|
||||
>>> config.skip_warning = True
|
||||
|
||||
>>> Inventory = Model.get('stock.inventory')
|
||||
>>> Location = Model.get('stock.location')
|
||||
>>> Lot = Model.get('stock.lot')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
|
||||
Get stock location::
|
||||
|
||||
>>> storage_loc, = Location.find([('code', '=', 'STO')])
|
||||
|
||||
Create product and lot::
|
||||
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'Product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'goods'
|
||||
>>> template.lot_required = ['storage']
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
>>> lot = Lot(number="0001", product=product)
|
||||
>>> lot.save()
|
||||
|
||||
Create an inventory::
|
||||
|
||||
>>> inventory = Inventory()
|
||||
>>> inventory.location = storage_loc
|
||||
>>> inventory.empty_quantity = 'keep'
|
||||
>>> inventory.save()
|
||||
|
||||
Count inventory::
|
||||
|
||||
>>> count = inventory.click('do_count')
|
||||
|
||||
>>> count.form.search = product
|
||||
>>> count.execute('quantity')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
RequiredValidationError: ...
|
||||
|
||||
>>> count.form.search = lot
|
||||
>>> count.execute('quantity')
|
||||
>>> assertEqual(count.form.product, product)
|
||||
>>> assertEqual(count.form.lot, lot)
|
||||
>>> count.form.quantity
|
||||
1.0
|
||||
>>> count.form.total_quantity
|
||||
1.0
|
||||
>>> count.execute('add')
|
||||
>>> count.execute('end')
|
||||
|
||||
Check inventory::
|
||||
|
||||
>>> line, = inventory.lines
|
||||
>>> assertEqual(line.product, product)
|
||||
>>> assertEqual(line.lot, lot)
|
||||
>>> line.quantity
|
||||
1.0
|
||||
107
modules/stock_lot/tests/scenario_stock_lot_move_add.rst
Normal file
107
modules/stock_lot/tests/scenario_stock_lot_move_add.rst
Normal file
@@ -0,0 +1,107 @@
|
||||
===========================
|
||||
Stock Lot Move Add Scenario
|
||||
===========================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.modules.currency.tests.tools import get_currency
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> modules = ['stock_lot']
|
||||
>>> if globals().get('stock_split', False):
|
||||
... modules.append('stock_split')
|
||||
>>> config = activate_modules(modules, create_company)
|
||||
|
||||
Create product::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'Product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'goods'
|
||||
>>> template.list_price = Decimal('20')
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Get stock locations::
|
||||
|
||||
>>> Location = Model.get('stock.location')
|
||||
>>> storage_loc, = Location.find([('code', '=', 'STO')])
|
||||
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
|
||||
|
||||
Create a move::
|
||||
|
||||
>>> Move = Model.get('stock.move')
|
||||
>>> move = Move()
|
||||
>>> move.from_location = storage_loc
|
||||
>>> move.to_location = customer_loc
|
||||
>>> move.product = product
|
||||
>>> move.quantity = 10
|
||||
>>> move.unit_price = Decimal('20')
|
||||
>>> move.currency = get_currency()
|
||||
>>> move.save()
|
||||
|
||||
Create a lot::
|
||||
|
||||
>>> Lot = Model.get('stock.lot')
|
||||
>>> lot2 = Lot(number='02', product=product)
|
||||
>>> lot2.save()
|
||||
|
||||
Add few lots::
|
||||
|
||||
>>> add_lots = move.click('add_lots_wizard')
|
||||
>>> assertEqual(add_lots.form.product, product)
|
||||
>>> add_lots.form.quantity
|
||||
10.0
|
||||
>>> assertEqual(add_lots.form.unit, unit)
|
||||
>>> add_lots.form.quantity_remaining
|
||||
10.0
|
||||
|
||||
>>> lot = add_lots.form.lots.new()
|
||||
>>> lot.quantity
|
||||
10.0
|
||||
>>> lot.number = '01'
|
||||
>>> lot.quantity = 2
|
||||
>>> lot = add_lots.form.lots.new()
|
||||
>>> lot.quantity
|
||||
8.0
|
||||
>>> lot.number = '02'
|
||||
>>> lot.quantity = 1
|
||||
|
||||
>>> add_lots.execute('add')
|
||||
|
||||
Check moves::
|
||||
|
||||
>>> move, = Move.find([('lot.number', '=', '01')])
|
||||
>>> move.quantity
|
||||
2.0
|
||||
>>> move, = Move.find([('lot.number', '=', '02')])
|
||||
>>> move.quantity
|
||||
1.0
|
||||
>>> assertEqual(move.lot, lot2)
|
||||
>>> move, = Move.find([('lot', '=', None)])
|
||||
>>> move.quantity
|
||||
7.0
|
||||
|
||||
Add lot to remaining::
|
||||
|
||||
>>> add_lots = move.click('add_lots_wizard')
|
||||
>>> lot = add_lots.form.lots.new()
|
||||
>>> lot.number = '03'
|
||||
>>> add_lots.execute('add')
|
||||
|
||||
>>> len(Move.find([]))
|
||||
3
|
||||
>>> move.lot.number
|
||||
'03'
|
||||
>>> move.quantity
|
||||
7.0
|
||||
65
modules/stock_lot/tests/scenario_stock_lot_number.rst
Normal file
65
modules/stock_lot/tests/scenario_stock_lot_number.rst
Normal file
@@ -0,0 +1,65 @@
|
||||
=========================
|
||||
Stock Lot Number Scenario
|
||||
=========================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('stock_lot')
|
||||
|
||||
Create lot sequence::
|
||||
|
||||
>>> Sequence = Model.get('ir.sequence')
|
||||
>>> SequenceType = Model.get('ir.sequence.type')
|
||||
>>> sequence_type, = SequenceType.find(
|
||||
... [('name', '=', "Stock Lot")], limit=1)
|
||||
>>> sequence = Sequence(name="Lot", sequence_type=sequence_type)
|
||||
>>> sequence.save()
|
||||
|
||||
Set default sequence::
|
||||
|
||||
>>> Configuration = Model.get('product.configuration')
|
||||
>>> configuration = Configuration(1)
|
||||
>>> configuration.default_lot_sequence = sequence
|
||||
>>> configuration.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
|
||||
>>> template = ProductTemplate()
|
||||
>>> assertEqual(template.lot_sequence, sequence)
|
||||
>>> template.name = 'Product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'goods'
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Create lot without number::
|
||||
|
||||
>>> Lot = Model.get('stock.lot')
|
||||
>>> lot = Lot(product=product)
|
||||
>>> lot.save()
|
||||
|
||||
>>> lot.number
|
||||
'1'
|
||||
|
||||
Copy set a new number::
|
||||
|
||||
>>> lot2, = lot.duplicate()
|
||||
>>> lot2.number
|
||||
'2'
|
||||
|
||||
Copy without sequence keep same number::
|
||||
|
||||
>>> template.lot_sequence = None
|
||||
>>> template.save()
|
||||
>>> lot3, = lot.duplicate()
|
||||
>>> lot3.number
|
||||
'1'
|
||||
91
modules/stock_lot/tests/scenario_stock_lot_shipment_in.rst
Normal file
91
modules/stock_lot/tests/scenario_stock_lot_shipment_in.rst
Normal file
@@ -0,0 +1,91 @@
|
||||
==============================
|
||||
Stock Lot Shipment In Scenario
|
||||
==============================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('stock_lot', create_company)
|
||||
|
||||
>>> Location = Model.get('stock.location')
|
||||
>>> Lot = Model.get('stock.lot')
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> ShipmentIn = Model.get('stock.shipment.in')
|
||||
|
||||
Create supplier::
|
||||
|
||||
>>> supplier = Party(name='Supplier')
|
||||
>>> supplier.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> unit, = ProductUom.find([('name', '=', "Unit")])
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = "Product"
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'goods'
|
||||
>>> template.list_price = Decimal('20')
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Get stock locations::
|
||||
|
||||
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
|
||||
>>> input_loc, = Location.find([('code', '=', 'IN')])
|
||||
>>> storage_loc, = Location.find([('code', '=', 'STO')])
|
||||
|
||||
Set 2 lots::
|
||||
|
||||
>>> lot1 = Lot(number="1", product=product)
|
||||
>>> lot1.save()
|
||||
>>> lot2 = Lot(number="2", product=product)
|
||||
>>> lot2.save()
|
||||
|
||||
Create a shipment::
|
||||
|
||||
>>> shipment = ShipmentIn()
|
||||
>>> shipment.supplier = supplier
|
||||
>>> move = shipment.incoming_moves.new()
|
||||
>>> move.product = product
|
||||
>>> move.quantity = 10
|
||||
>>> move.from_location = supplier_loc
|
||||
>>> move.to_location = input_loc
|
||||
>>> move.unit_price = Decimal('5')
|
||||
>>> move.currency = shipment.company.currency
|
||||
>>> shipment.save()
|
||||
|
||||
Receive the shipment with one lot::
|
||||
|
||||
>>> incoming_move, = shipment.incoming_moves
|
||||
>>> incoming_move.lot = lot1
|
||||
>>> shipment.click('receive')
|
||||
>>> shipment.state
|
||||
'received'
|
||||
|
||||
Change lot and try to finish::
|
||||
|
||||
>>> incoming_move, = shipment.inventory_moves
|
||||
>>> assertEqual(incoming_move.lot, lot1)
|
||||
>>> incoming_move.lot = lot2
|
||||
>>> shipment.click('do')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ShipmentCheckQuantityWarning: ...
|
||||
>>> incoming_move.reload()
|
||||
>>> incoming_move.lot = lot1
|
||||
>>> incoming_move.save()
|
||||
|
||||
Finish the shipment::
|
||||
|
||||
>>> shipment.click('do')
|
||||
>>> shipment.state
|
||||
'done'
|
||||
153
modules/stock_lot/tests/scenario_stock_lot_shipment_out.rst
Normal file
153
modules/stock_lot/tests/scenario_stock_lot_shipment_out.rst
Normal file
@@ -0,0 +1,153 @@
|
||||
===============================
|
||||
Stock Lot Shipment Out Scenario
|
||||
===============================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.modules.currency.tests.tools import get_currency
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('stock_lot', create_company)
|
||||
|
||||
Create customer::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> customer = Party(name='Customer')
|
||||
>>> customer.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'Product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'goods'
|
||||
>>> template.list_price = Decimal('20')
|
||||
>>> template.lot_required = ['storage']
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Get stock locations::
|
||||
|
||||
>>> Location = Model.get('stock.location')
|
||||
>>> warehouse_loc, = Location.find([('code', '=', 'WH')])
|
||||
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
|
||||
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
|
||||
>>> output_loc, = Location.find([('code', '=', 'OUT')])
|
||||
>>> storage_loc, = Location.find([('code', '=', 'STO')])
|
||||
|
||||
Create Shipment Out::
|
||||
|
||||
>>> ShipmentOut = Model.get('stock.shipment.out')
|
||||
>>> shipment_out = ShipmentOut()
|
||||
>>> shipment_out.planned_date = today
|
||||
>>> shipment_out.customer = customer
|
||||
>>> shipment_out.warehouse = warehouse_loc
|
||||
>>> move = shipment_out.outgoing_moves.new()
|
||||
>>> move.product = product
|
||||
>>> move.unit = unit
|
||||
>>> move.quantity = 10
|
||||
>>> move.from_location = output_loc
|
||||
>>> move.to_location = customer_loc
|
||||
>>> move.unit_price = Decimal('1')
|
||||
>>> move.currency = get_currency()
|
||||
>>> shipment_out.save()
|
||||
|
||||
Set the shipment state to waiting::
|
||||
|
||||
>>> shipment_out.click('wait')
|
||||
>>> len(shipment_out.outgoing_moves)
|
||||
1
|
||||
>>> len(shipment_out.inventory_moves)
|
||||
1
|
||||
|
||||
Split inventory move::
|
||||
|
||||
>>> move, = shipment_out.inventory_moves
|
||||
>>> move.quantity = 7
|
||||
>>> move.save()
|
||||
>>> with config.set_context(_stock_move_split=True):
|
||||
... _ = move.duplicate(default=dict(quantity=3))
|
||||
>>> shipment_out.reload()
|
||||
|
||||
Assign the shipment::
|
||||
|
||||
>>> shipment_out.click('assign_force')
|
||||
>>> shipment_out.state
|
||||
'assigned'
|
||||
>>> len(shipment_out.outgoing_moves)
|
||||
1
|
||||
|
||||
Check the inventory moves origin::
|
||||
|
||||
>>> outgoing_move, = shipment_out.outgoing_moves
|
||||
>>> for move in shipment_out.inventory_moves:
|
||||
... assertEqual(move.origin, outgoing_move)
|
||||
|
||||
Try to pick without lot::
|
||||
|
||||
>>> shipment_out.click('pick')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
RequiredValidationError: ...
|
||||
|
||||
Set 2 lots::
|
||||
|
||||
>>> Lot = Model.get('stock.lot')
|
||||
>>> for i, move in enumerate(shipment_out.inventory_moves, start=1):
|
||||
... lot = Lot(number='%05i' % i, product=move.product)
|
||||
... lot.save()
|
||||
... move.lot = lot
|
||||
>>> shipment_out.save()
|
||||
|
||||
Pick the shipment::
|
||||
|
||||
>>> shipment_out.click('pick')
|
||||
>>> shipment_out.state
|
||||
'picked'
|
||||
>>> len(shipment_out.outgoing_moves)
|
||||
3
|
||||
>>> sorted([m.quantity for m in shipment_out.outgoing_moves])
|
||||
[0.0, 3.0, 7.0]
|
||||
>>> lot_quantities = {}
|
||||
>>> for move in shipment_out.outgoing_moves:
|
||||
... number = move.lot.number if move.lot else ''
|
||||
... quantity = lot_quantities.setdefault(number, 0)
|
||||
... lot_quantities[number] += move.quantity
|
||||
>>> sorted(lot_quantities.items())
|
||||
[('', 0.0), ('00001', 7.0), ('00002', 3.0)]
|
||||
|
||||
Check the inventory moves have an outgoing move origin with the same lot::
|
||||
|
||||
>>> for move in shipment_out.inventory_moves:
|
||||
... assertEqual(move.lot, move.origin.lot)
|
||||
|
||||
Pack the shipment and return to pick::
|
||||
|
||||
>>> shipment_out.click('pack')
|
||||
>>> shipment_out.state
|
||||
'packed'
|
||||
>>> len(shipment_out.outgoing_moves)
|
||||
2
|
||||
>>> shipment_out.click('pick')
|
||||
>>> shipment_out.state
|
||||
'picked'
|
||||
>>> len(shipment_out.outgoing_moves)
|
||||
2
|
||||
|
||||
Check the inventory moves still have an outgoing move origin with the same lot::
|
||||
|
||||
>>> for move in shipment_out.inventory_moves:
|
||||
... assertEqual(move.lot, move.origin.lot)
|
||||
@@ -0,0 +1,133 @@
|
||||
====================================
|
||||
Stock Lot Shipment Internal Scenario
|
||||
====================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual, assertTrue
|
||||
|
||||
>>> today = dt.date.today()
|
||||
>>> yesterday = today - dt.timedelta(days=1)
|
||||
>>> tomorrow = today + dt.timedelta(days=1)
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('stock_lot', create_company)
|
||||
|
||||
>>> LeadTime = Model.get('stock.location.lead_time')
|
||||
>>> Location = Model.get('stock.location')
|
||||
>>> Lot = Model.get('stock.lot')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> Shipment = Model.get('stock.shipment.internal')
|
||||
|
||||
Create product::
|
||||
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = "Product"
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'goods'
|
||||
>>> template.list_price = Decimal('20')
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Get stock locations::
|
||||
|
||||
>>> warehouse1, = Location.find([('type', '=', 'warehouse')])
|
||||
>>> warehouse2, = warehouse1.duplicate()
|
||||
|
||||
Add lead time between warehouses::
|
||||
|
||||
>>> lead_time = LeadTime()
|
||||
>>> lead_time.warehouse_from = warehouse1
|
||||
>>> lead_time.warehouse_to = warehouse2
|
||||
>>> lead_time.lead_time = dt.timedelta(1)
|
||||
>>> lead_time.save()
|
||||
|
||||
Create Internal Shipment with lead time::
|
||||
|
||||
>>> shipment = Shipment()
|
||||
>>> shipment.planned_date = tomorrow
|
||||
>>> shipment.from_location = warehouse1.storage_location
|
||||
>>> shipment.to_location = warehouse2.storage_location
|
||||
>>> assertTrue(shipment.transit_location)
|
||||
>>> assertEqual(shipment.planned_start_date, today)
|
||||
>>> move = shipment.moves.new()
|
||||
>>> move.product = product
|
||||
>>> move.quantity = 3
|
||||
>>> move.from_location = shipment.from_location
|
||||
>>> move.to_location = shipment.to_location
|
||||
>>> shipment.save()
|
||||
|
||||
Set the shipment state to waiting::
|
||||
|
||||
|
||||
>>> shipment.click('wait')
|
||||
>>> shipment.state
|
||||
'waiting'
|
||||
>>> len(shipment.outgoing_moves)
|
||||
1
|
||||
>>> len(shipment.incoming_moves)
|
||||
1
|
||||
|
||||
Split outgoing moves::
|
||||
|
||||
>>> move, = shipment.outgoing_moves
|
||||
>>> move.quantity = 1
|
||||
>>> move.save()
|
||||
>>> with config.set_context(_stock_move_split=True):
|
||||
... _ = move.duplicate(default=dict(quantity=2))
|
||||
>>> shipment.reload()
|
||||
|
||||
Assign the shipment::
|
||||
|
||||
>>> shipment.click('assign_force')
|
||||
>>> shipment.state
|
||||
'assigned'
|
||||
|
||||
>>> len(shipment.incoming_moves)
|
||||
1
|
||||
|
||||
Check the inventory moves origin::
|
||||
|
||||
>>> incoming_move, = shipment.incoming_moves
|
||||
>>> for move in shipment.outgoing_moves:
|
||||
... assertEqual(move.origin, incoming_move)
|
||||
|
||||
Set 2 lots::
|
||||
|
||||
>>> for i, move in enumerate(shipment.outgoing_moves, start=1):
|
||||
... lot = Lot(number='%05i' % i, product=move.product)
|
||||
... lot.save()
|
||||
... move.lot = lot
|
||||
>>> shipment.save()
|
||||
|
||||
Ship the shipment::
|
||||
|
||||
>>> shipment.effective_start_date = yesterday
|
||||
>>> shipment.click('pack')
|
||||
>>> shipment.click('ship')
|
||||
>>> shipment.state
|
||||
'shipped'
|
||||
>>> len(shipment.incoming_moves)
|
||||
3
|
||||
>>> sorted([m.quantity for m in shipment.incoming_moves])
|
||||
[0.0, 1.0, 2.0]
|
||||
>>> lot_quantities = {}
|
||||
>>> for move in shipment.incoming_moves:
|
||||
... number = move.lot.number if move.lot else ''
|
||||
... quantity = lot_quantities.setdefault(number, 0)
|
||||
... lot_quantities[number] += move.quantity
|
||||
>>> sorted(lot_quantities.items())
|
||||
[('', 0.0), ('00001', 1.0), ('00002', 2.0)]
|
||||
|
||||
Check the outgoing moves have an incoming move origin with the same lot::
|
||||
|
||||
>>> for move in shipment.outgoing_moves:
|
||||
... assertEqual(move.lot, move.origin.lot)
|
||||
80
modules/stock_lot/tests/scenario_stock_lot_trace.rst
Normal file
80
modules/stock_lot/tests/scenario_stock_lot_trace.rst
Normal file
@@ -0,0 +1,80 @@
|
||||
========================
|
||||
Stock Lot Trace Scenario
|
||||
========================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.modules.currency.tests.tools import get_currency
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('stock_lot', create_company)
|
||||
|
||||
>>> Location = Model.get('stock.location')
|
||||
>>> Lot = Model.get('stock.lot')
|
||||
>>> LotTrace = Model.get('stock.lot.trace')
|
||||
>>> Move = Model.get('stock.move')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> UoM = Model.get('product.uom')
|
||||
|
||||
Create product::
|
||||
|
||||
>>> unit, = UoM.find([('name', '=', "Unit")])
|
||||
|
||||
>>> template = ProductTemplate(name="Product")
|
||||
>>> template.type = 'goods'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Create lot::
|
||||
|
||||
>>> lot = Lot(product=product, number="1")
|
||||
>>> lot.save()
|
||||
|
||||
Get locations::
|
||||
|
||||
>>> supplier_loc, = Location.find([('code', '=', 'SUP')])
|
||||
>>> storage_loc, = Location.find([('code', '=', 'STO')])
|
||||
>>> customer_loc, = Location.find([('code', '=', 'CUS')])
|
||||
|
||||
Make some moves::
|
||||
|
||||
>>> move_in = Move(product=product, lot=lot)
|
||||
>>> move_in.quantity = 10
|
||||
>>> move_in.from_location = supplier_loc
|
||||
>>> move_in.to_location = storage_loc
|
||||
>>> move_in.currency = get_currency()
|
||||
>>> move_in.unit_price = Decimal('0')
|
||||
>>> move_in.click('do')
|
||||
>>> move_in.state
|
||||
'done'
|
||||
|
||||
>>> move_out = Move(product=product, lot=lot)
|
||||
>>> move_out.quantity = 2
|
||||
>>> move_out.from_location = storage_loc
|
||||
>>> move_out.to_location = customer_loc
|
||||
>>> move_out.currency = get_currency()
|
||||
>>> move_out.unit_price = Decimal('0')
|
||||
>>> move_out.click('do')
|
||||
>>> move_out.state
|
||||
'done'
|
||||
|
||||
Check lot traces::
|
||||
|
||||
>>> trace = LotTrace(move_in.id)
|
||||
>>> trace.upward_traces == [LotTrace(move_out.id)]
|
||||
True
|
||||
>>> trace.downward_traces
|
||||
[]
|
||||
|
||||
>>> trace = LotTrace(move_out.id)
|
||||
>>> trace.upward_traces
|
||||
[]
|
||||
>>> trace.downward_traces == [LotTrace(move_in.id)]
|
||||
True
|
||||
313
modules/stock_lot/tests/test_module.py
Normal file
313
modules/stock_lot/tests/test_module.py
Normal file
@@ -0,0 +1,313 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
import datetime as dt
|
||||
from decimal import Decimal
|
||||
|
||||
from trytond.modules.company.tests import (
|
||||
CompanyTestMixin, create_company, set_company)
|
||||
from trytond.pool import Pool
|
||||
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
|
||||
class StockLotTestCase(CompanyTestMixin, ModuleTestCase):
|
||||
'Test Stock Lot module'
|
||||
module = 'stock_lot'
|
||||
extras = ['stock_split']
|
||||
|
||||
@with_transaction()
|
||||
def test_products_by_location(self):
|
||||
'Test products_by_location'
|
||||
pool = Pool()
|
||||
Uom = pool.get('product.uom')
|
||||
Template = pool.get('product.template')
|
||||
Product = pool.get('product.product')
|
||||
Location = pool.get('stock.location')
|
||||
Move = pool.get('stock.move')
|
||||
Lot = pool.get('stock.lot')
|
||||
|
||||
kg, = Uom.search([('name', '=', 'Kilogram')])
|
||||
g, = Uom.search([('name', '=', 'Gram')])
|
||||
template, = Template.create([{
|
||||
'name': 'Test products_by_location',
|
||||
'type': 'goods',
|
||||
'default_uom': kg.id,
|
||||
}])
|
||||
product, = Product.create([{
|
||||
'template': template.id,
|
||||
}])
|
||||
supplier, = Location.search([('code', '=', 'SUP')])
|
||||
customer, = Location.search([('code', '=', 'CUS')])
|
||||
storage, = Location.search([('code', '=', 'STO')])
|
||||
company = create_company()
|
||||
currency = company.currency
|
||||
with set_company(company):
|
||||
lot1, lot2 = Lot.create([{
|
||||
'number': '1',
|
||||
'product': product.id,
|
||||
}, {
|
||||
'number': '2',
|
||||
'product': product.id,
|
||||
}])
|
||||
|
||||
moves = Move.create([{
|
||||
'product': product.id,
|
||||
'lot': lot1.id,
|
||||
'unit': kg.id,
|
||||
'quantity': 5,
|
||||
'from_location': supplier.id,
|
||||
'to_location': storage.id,
|
||||
'company': company.id,
|
||||
'unit_price': Decimal('1'),
|
||||
'currency': currency.id,
|
||||
}, {
|
||||
'product': product.id,
|
||||
'lot': lot2.id,
|
||||
'unit': kg.id,
|
||||
'quantity': 10,
|
||||
'from_location': supplier.id,
|
||||
'to_location': storage.id,
|
||||
'company': company.id,
|
||||
'unit_price': Decimal('1'),
|
||||
'currency': currency.id,
|
||||
}, {
|
||||
'product': product.id,
|
||||
'lot': lot2.id,
|
||||
'unit': kg.id,
|
||||
'quantity': 2,
|
||||
'from_location': storage.id,
|
||||
'to_location': customer.id,
|
||||
'company': company.id,
|
||||
'unit_price': Decimal('1'),
|
||||
'currency': currency.id,
|
||||
}, {
|
||||
'product': product.id,
|
||||
'lot': None,
|
||||
'unit': kg.id,
|
||||
'quantity': 3,
|
||||
'from_location': supplier.id,
|
||||
'to_location': storage.id,
|
||||
'company': company.id,
|
||||
'unit_price': Decimal('1'),
|
||||
'currency': currency.id,
|
||||
}])
|
||||
Move.do(moves)
|
||||
|
||||
self.assertEqual(Product.products_by_location([storage.id],
|
||||
grouping_filter=([product.id],)), {
|
||||
(storage.id, product.id): 16,
|
||||
})
|
||||
self.assertEqual(Product.products_by_location([storage.id],
|
||||
grouping=('product', 'lot',),
|
||||
grouping_filter=([product.id],)), {
|
||||
(storage.id, product.id, lot1.id): 5,
|
||||
(storage.id, product.id, lot2.id): 8,
|
||||
(storage.id, product.id, None): 3,
|
||||
})
|
||||
with Transaction().set_context(locations=[storage.id]):
|
||||
lot1, lot2 = Lot.browse([lot1, lot2])
|
||||
self.assertEqual(lot1.quantity, 5)
|
||||
self.assertEqual(lot2.quantity, 8)
|
||||
|
||||
@with_transaction()
|
||||
def test_period(self):
|
||||
'Test period'
|
||||
pool = Pool()
|
||||
Uom = pool.get('product.uom')
|
||||
Template = pool.get('product.template')
|
||||
Product = pool.get('product.product')
|
||||
Location = pool.get('stock.location')
|
||||
Move = pool.get('stock.move')
|
||||
Lot = pool.get('stock.lot')
|
||||
Period = pool.get('stock.period')
|
||||
|
||||
unit, = Uom.search([('name', '=', 'Unit')])
|
||||
template, = Template.create([{
|
||||
'name': 'Test period',
|
||||
'type': 'goods',
|
||||
'default_uom': unit.id,
|
||||
}])
|
||||
product, = Product.create([{
|
||||
'template': template.id,
|
||||
}])
|
||||
supplier, = Location.search([('code', '=', 'SUP')])
|
||||
storage, = Location.search([('code', '=', 'STO')])
|
||||
company = create_company()
|
||||
currency = company.currency
|
||||
with set_company(company):
|
||||
lot1, lot2 = Lot.create([{
|
||||
'number': '1',
|
||||
'product': product.id,
|
||||
}, {
|
||||
'number': '2',
|
||||
'product': product.id,
|
||||
}])
|
||||
|
||||
today = dt.date.today()
|
||||
|
||||
moves = Move.create([{
|
||||
'product': product.id,
|
||||
'lot': lot1.id,
|
||||
'unit': unit.id,
|
||||
'quantity': 5,
|
||||
'from_location': supplier.id,
|
||||
'to_location': storage.id,
|
||||
'planned_date': today - dt.timedelta(days=1),
|
||||
'effective_date': today - dt.timedelta(days=1),
|
||||
'company': company.id,
|
||||
'unit_price': Decimal('1'),
|
||||
'currency': currency.id,
|
||||
}, {
|
||||
'product': product.id,
|
||||
'lot': lot2.id,
|
||||
'unit': unit.id,
|
||||
'quantity': 10,
|
||||
'from_location': supplier.id,
|
||||
'to_location': storage.id,
|
||||
'planned_date': today - dt.timedelta(days=1),
|
||||
'effective_date': today - dt.timedelta(days=1),
|
||||
'company': company.id,
|
||||
'unit_price': Decimal('1'),
|
||||
'currency': currency.id,
|
||||
}, {
|
||||
'product': product.id,
|
||||
'lot': None,
|
||||
'unit': unit.id,
|
||||
'quantity': 3,
|
||||
'from_location': supplier.id,
|
||||
'to_location': storage.id,
|
||||
'planned_date': today - dt.timedelta(days=1),
|
||||
'effective_date': today - dt.timedelta(days=1),
|
||||
'company': company.id,
|
||||
'unit_price': Decimal('1'),
|
||||
'currency': currency.id,
|
||||
}])
|
||||
Move.do(moves)
|
||||
|
||||
period, = Period.create([{
|
||||
'date': today - dt.timedelta(days=1),
|
||||
'company': company.id,
|
||||
}])
|
||||
Period.close([period])
|
||||
self.assertEqual(period.state, 'closed')
|
||||
|
||||
quantities = {
|
||||
supplier: -18,
|
||||
storage: 18,
|
||||
}
|
||||
for cache in period.caches:
|
||||
self.assertEqual(cache.product, product)
|
||||
self.assertEqual(cache.internal_quantity,
|
||||
quantities[cache.location])
|
||||
|
||||
quantities = {
|
||||
(supplier, lot1): -5,
|
||||
(storage, lot1): 5,
|
||||
(supplier, lot2): -10,
|
||||
(storage, lot2): 10,
|
||||
(supplier, None): -3,
|
||||
(storage, None): 3,
|
||||
}
|
||||
for lot_cache in period.lot_caches:
|
||||
self.assertEqual(lot_cache.product, product)
|
||||
self.assertEqual(lot_cache.internal_quantity,
|
||||
quantities[(lot_cache.location, lot_cache.lot)])
|
||||
|
||||
@with_transaction()
|
||||
def test_assign_try_with_lot(self):
|
||||
"Test Move assign_try with lot"
|
||||
pool = Pool()
|
||||
Template = pool.get('product.template')
|
||||
Product = pool.get('product.product')
|
||||
Uom = pool.get('product.uom')
|
||||
Location = pool.get('stock.location')
|
||||
Move = pool.get('stock.move')
|
||||
Lot = pool.get('stock.lot')
|
||||
|
||||
unit, = Uom.search([('name', '=', 'Meter')])
|
||||
template = Template(
|
||||
name="Product",
|
||||
type='goods',
|
||||
default_uom=unit,
|
||||
)
|
||||
template.save()
|
||||
product = Product(template=template.id)
|
||||
product.save()
|
||||
|
||||
supplier, = Location.search([('code', '=', 'SUP')])
|
||||
storage, = Location.search([('code', '=', 'STO')])
|
||||
customer, = Location.search([('code', '=', 'CUS')])
|
||||
|
||||
company = create_company()
|
||||
with set_company(company):
|
||||
lot1, lot2 = Lot.create([{
|
||||
'number': "1",
|
||||
'product': product.id,
|
||||
}, {
|
||||
'number': "2",
|
||||
'product': product.id,
|
||||
}])
|
||||
moves = Move.create([{
|
||||
'product': product.id,
|
||||
'lot': lot1.id,
|
||||
'unit': unit.id,
|
||||
'quantity': 2,
|
||||
'from_location': supplier.id,
|
||||
'to_location': storage.id,
|
||||
'company': company.id,
|
||||
'unit_price': Decimal(1),
|
||||
'currency': company.currency.id,
|
||||
}, {
|
||||
'product': product.id,
|
||||
'lot': lot2.id,
|
||||
'unit': unit.id,
|
||||
'quantity': 3,
|
||||
'from_location': supplier.id,
|
||||
'to_location': storage.id,
|
||||
'company': company.id,
|
||||
'unit_price': Decimal(1),
|
||||
'currency': company.currency.id,
|
||||
}, {
|
||||
'product': product.id,
|
||||
'lot': None,
|
||||
'unit': unit.id,
|
||||
'quantity': 3,
|
||||
'from_location': supplier.id,
|
||||
'to_location': storage.id,
|
||||
'company': company.id,
|
||||
'unit_price': Decimal(1),
|
||||
'currency': company.currency.id,
|
||||
}])
|
||||
Move.do(moves)
|
||||
|
||||
move, = Move.create([{
|
||||
'product': product.id,
|
||||
'unit': unit.id,
|
||||
'quantity': 10,
|
||||
'from_location': storage.id,
|
||||
'to_location': customer.id,
|
||||
'company': company.id,
|
||||
'unit_price': Decimal(1),
|
||||
'currency': company.currency.id,
|
||||
}])
|
||||
|
||||
self.assertFalse(
|
||||
Move.assign_try([move], grouping=('product', 'lot')))
|
||||
moves = Move.search([
|
||||
('product', '=', product.id),
|
||||
('from_location', '=', storage.id),
|
||||
('to_location', '=', customer.id),
|
||||
('company', '=', company.id),
|
||||
])
|
||||
self.assertEqual(len(moves), 4)
|
||||
self.assertEqual({
|
||||
(m.lot, m.quantity, m.state) for m in moves}, {
|
||||
(lot1, 2, 'assigned'),
|
||||
(lot2, 3, 'assigned'),
|
||||
(None, 3, 'assigned'),
|
||||
(None, 2, 'draft'),
|
||||
})
|
||||
|
||||
|
||||
del ModuleTestCase
|
||||
8
modules/stock_lot/tests/test_scenario.py
Normal file
8
modules/stock_lot/tests/test_scenario.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# 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.tests.test_tryton import load_doc_tests
|
||||
|
||||
|
||||
def load_tests(*args, **kwargs):
|
||||
return load_doc_tests(__name__, __file__, *args, **kwargs)
|
||||
Reference in New Issue
Block a user