1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/stock_barcode_picking_batch/tests/test_barcode_batch.py
2024-12-10 09:04:09 +07:00

844 lines
40 KiB
Python

# -*- encoding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import Command
from odoo.tests import tagged
from odoo.tests.common import Form
from odoo.addons.stock_barcode.tests.test_barcode_client_action import TestBarcodeClientAction
@tagged('post_install', '-at_install')
class TestBarcodeBatchClientAction(TestBarcodeClientAction):
def setUp(self):
super().setUp()
self.clean_access_rights()
grp_lot = self.env.ref('stock.group_production_lot')
grp_multi_loc = self.env.ref('stock.group_stock_multi_locations')
grp_pack = self.env.ref('stock.group_tracking_lot')
self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]})
self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]})
self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]})
# Create some products
self.product3 = self.env['product.product'].create({
'name': 'product3',
'type': 'product',
'categ_id': self.env.ref('product.product_category_all').id,
'barcode': 'product3',
})
self.product4 = self.env['product.product'].create({
'name': 'product4',
'type': 'product',
'categ_id': self.env.ref('product.product_category_all').id,
'barcode': 'product4',
})
self.product5 = self.env['product.product'].create({
'name': 'product5',
'type': 'product',
'categ_id': self.env.ref('product.product_category_all').id,
'barcode': 'product5',
})
# Create locations dedicated to package
self.shelf5 = self.env['stock.location'].create({
'name': 'Section 5',
'location_id': self.stock_location.id,
'barcode': 'shelf5',
})
# Create some packages
self.package1 = self.env['stock.quant.package'].create({'name': 'p5pack01'})
self.package2 = self.env['stock.quant.package'].create({'name': 'p5pack02'})
# Create some quants (for deliveries)
Quant = self.env['stock.quant']
quants = Quant.with_context(inventory_mode=True).create({
'product_id': self.product1.id,
'location_id': self.shelf1.id,
'inventory_quantity': 2
})
quants |= Quant.with_context(inventory_mode=True).create({
'product_id': self.product2.id,
'location_id': self.shelf2.id,
'inventory_quantity': 1
})
quants |= Quant.with_context(inventory_mode=True).create({
'product_id': self.product2.id,
'location_id': self.shelf3.id,
'inventory_quantity': 1
})
quants |= Quant.with_context(inventory_mode=True).create({
'product_id': self.product3.id,
'location_id': self.shelf3.id,
'inventory_quantity': 2
})
quants |= Quant.with_context(inventory_mode=True).create({
'product_id': self.product4.id,
'location_id': self.shelf1.id,
'inventory_quantity': 1
})
quants |= Quant.with_context(inventory_mode=True).create({
'product_id': self.product4.id,
'location_id': self.shelf4.id,
'inventory_quantity': 1
})
quants |= Quant.with_context(inventory_mode=True).create({
'product_id': self.product5.id,
'location_id': self.shelf5.id,
'package_id': self.package1.id,
'inventory_quantity': 4,
})
quants |= Quant.with_context(inventory_mode=True).create({
'product_id': self.product5.id,
'location_id': self.shelf5.id,
'inventory_quantity': 4,
})
quants.action_apply_inventory()
# Create a first receipt for 2 products.
picking_form = Form(self.env['stock.picking'])
picking_form.picking_type_id = self.picking_type_in
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.product1
move.product_uom_qty = 1
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.productserial1
move.product_uom_qty = 2
self.picking_receipt_1 = picking_form.save()
self.picking_receipt_1.action_confirm()
# Create a second receipt for 2 products.
picking_form = Form(self.env['stock.picking'])
picking_form.picking_type_id = self.picking_type_in
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.product1
move.product_uom_qty = 3
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.productlot1
move.product_uom_qty = 8
self.picking_receipt_2 = picking_form.save()
self.picking_receipt_2.action_confirm()
# Changes name of pickings to be able to track them on the tour
self.picking_receipt_1.name = 'picking_receipt_1'
self.picking_receipt_2.name = 'picking_receipt_2'
# Create a first delivery for 2 products.
picking_form = Form(self.env['stock.picking'])
picking_form.picking_type_id = self.picking_type_out
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.product1
move.product_uom_qty = 1
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.product2
move.product_uom_qty = 2
self.picking_delivery_1 = picking_form.save()
self.picking_delivery_1.action_confirm()
self.picking_delivery_1.action_assign()
# Create a second delivery for 3 products.
picking_form = Form(self.env['stock.picking'])
picking_form.picking_type_id = self.picking_type_out
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.product1
move.product_uom_qty = 1
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.product3
move.product_uom_qty = 2
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.product4
move.product_uom_qty = 2
self.picking_delivery_2 = picking_form.save()
self.picking_delivery_2.action_confirm()
self.picking_delivery_2.action_assign()
# Create a delivery dedicated to package testing.
picking_form = Form(self.env['stock.picking'])
picking_form.picking_type_id = self.picking_type_out
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.product5
move.product_uom_qty = 8
self.picking_delivery_package = picking_form.save()
self.picking_delivery_package.action_confirm()
self.picking_delivery_package.action_assign()
# Create another quant with package after reservation to test scan
# unexpected package in the Barcode App.
Quant.with_context(inventory_mode=True).create({
'product_id': self.product5.id,
'location_id': self.shelf5.id,
'package_id': self.package2.id,
'inventory_quantity': 4,
}).action_apply_inventory()
# Changes name of pickings to be able to track them on the tour
self.picking_delivery_1.name = 'picking_delivery_1'
self.picking_delivery_2.name = 'picking_delivery_2'
self.picking_delivery_package.name = 'picking_delivery_package'
def _get_batch_client_action_url(self, batch_id):
action = self.env["ir.actions.actions"]._for_xml_id("stock_barcode_picking_batch.stock_barcode_picking_batch_client_action")
return '/web#action=%s&active_id=%s' % (action['id'], batch_id)
def test_barcode_batch_receipt_1(self):
""" Create a batch picking with 3 receipts, then open the batch in
barcode app and scan each product, SN or LN one by one.
"""
# Creates an additional receipt for the product tracked by lots.
picking_form = Form(self.env['stock.picking'])
picking_form.picking_type_id = self.picking_type_in
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.productlot1
move.product_uom_qty = 4
picking_receipt_3 = picking_form.save()
picking_receipt_3.action_confirm()
picking_receipt_3.name = 'picking_receipt_3'
picking_receipt_3.user_id = False
batch_form = Form(self.env['stock.picking.batch'])
batch_form.picking_ids.add(self.picking_receipt_1)
batch_form.picking_ids.add(self.picking_receipt_2)
batch_form.picking_ids.add(picking_receipt_3)
batch_receipt = batch_form.save()
self.assertEqual(
batch_receipt.picking_type_id.id,
self.picking_receipt_1.picking_type_id.id,
"Batch picking must take the picking type of its sub-pickings"
)
batch_receipt.action_confirm()
self.assertEqual(len(batch_receipt.move_ids), 5)
self.assertEqual(len(batch_receipt.move_line_ids), 6)
self.assertEqual(batch_receipt.user_id.id, False)
self.assertEqual(picking_receipt_3.user_id.id, False)
url = self._get_batch_client_action_url(batch_receipt.id)
self.start_tour(url, 'test_barcode_batch_receipt_1', login='admin', timeout=180)
# Checks user was assign on the batch and its pickings.
self.assertEqual(batch_receipt.user_id.id, self.env.user.id)
self.assertEqual(picking_receipt_3.user_id.id, self.env.user.id)
def test_barcode_batch_delivery_1(self):
""" Create a batch picking with 2 deliveries (split into 3 locations),
then open the batch in barcode app and scan each product.
Change the location when all products of the page has been scanned.
"""
batch_form = Form(self.env['stock.picking.batch'])
# Adds two quantities for product tracked by SN.
sn1 = self.env['stock.lot'].create({'name': 'sn1', 'product_id': self.productserial1.id, 'company_id': self.env.company.id})
sn2 = self.env['stock.lot'].create({'name': 'sn2', 'product_id': self.productserial1.id, 'company_id': self.env.company.id})
self.env['stock.quant'].with_context(inventory_mode=True).create({
'product_id': self.productserial1.id,
'inventory_quantity': 1,
'lot_id': sn1.id,
'location_id': self.shelf1.id,
}).action_apply_inventory()
self.env['stock.quant'].with_context(inventory_mode=True).create({
'product_id': self.productserial1.id,
'inventory_quantity': 1,
'lot_id': sn2.id,
'location_id': self.shelf1.id,
}).action_apply_inventory()
# Creates a delivery for a product tracked by SN, the purpose is to
# reserve sn1 and scan sn2 instead.
picking_form = Form(self.env['stock.picking'])
picking_form.picking_type_id = self.picking_type_out
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.productserial1
move.product_uom_qty = 1
picking_delivery_sn = picking_form.save()
picking_delivery_sn.name = 'picking_delivery_sn'
picking_delivery_sn.action_confirm()
picking_delivery_sn.action_assign()
batch_form.picking_ids.add(self.picking_delivery_1)
batch_form.picking_ids.add(self.picking_delivery_2)
batch_form.picking_ids.add(self.picking_delivery_package)
batch_form.picking_ids.add(picking_delivery_sn)
batch_delivery = batch_form.save()
self.assertEqual(
batch_delivery.picking_type_id.id,
self.picking_delivery_1.picking_type_id.id,
"Batch picking must take the picking type of its sub-pickings"
)
batch_delivery.action_confirm()
self.assertEqual(len(batch_delivery.move_ids), 7)
self.assertEqual(len(batch_delivery.move_line_ids), 10)
packaged_move_lines = batch_delivery.move_line_ids.filtered(lambda ml: ml.product_id.id == self.product5.id)
self.assertEqual(len(packaged_move_lines), 2)
self.assertEqual(packaged_move_lines.package_id.id, self.package1.id)
url = self._get_batch_client_action_url(batch_delivery.id)
self.start_tour(url, 'test_barcode_batch_delivery_1', login='admin', timeout=180)
def test_barcode_batch_delivery_2_move_entire_package(self):
""" Creates a batch picking with 2 deliveries while the delivery picking type use the "move
entire package" setting and check lines are correctly displayed as package line when moving
the entire package, or as usual barcode line in other cases.
"""
self.clean_access_rights()
grp_pack = self.env.ref('stock.group_tracking_lot')
self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]})
self.picking_type_out.show_entire_packs = True
# Creates two packages and adds some quantities on hand.
pack1 = self.env['stock.quant.package'].create({'name': 'pack1'})
pack2 = self.env['stock.quant.package'].create({'name': 'pack2'})
self.env['stock.quant'].with_context(inventory_mode=True).create({
'product_id': self.product1.id,
'inventory_quantity': 10,
'package_id': pack1.id,
'location_id': self.stock_location.id,
}).action_apply_inventory()
self.env['stock.quant'].with_context(inventory_mode=True).create({
'product_id': self.product2.id,
'inventory_quantity': 10,
'package_id': pack2.id,
'location_id': self.stock_location.id,
}).action_apply_inventory()
self.env['stock.quant'].with_context(inventory_mode=True).create({
'product_id': self.product3.id,
'inventory_quantity': 10,
'package_id': pack1.id,
'location_id': self.stock_location.id,
}).action_apply_inventory()
# Creates two deliveries.
picking_form = Form(self.env['stock.picking'])
picking_form.picking_type_id = self.picking_type_out
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.product1
move.product_uom_qty = 10
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.product2
move.product_uom_qty = 5
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.product3
move.product_uom_qty = 10
delivery_1 = picking_form.save()
delivery_1.action_confirm()
delivery_1.action_assign()
picking_form = Form(self.env['stock.picking'])
picking_form.picking_type_id = self.picking_type_out
with picking_form.move_ids_without_package.new() as move:
move.product_id = self.product2
move.product_uom_qty = 5
delivery_2 = picking_form.save()
delivery_2.action_confirm()
delivery_2.action_assign()
# Changes name of pickings to be able to track them on the tour.
delivery_1.name = 'delivery_1'
delivery_2.name = 'delivery_2'
# Creates and confirms the batch.
batch_form = Form(self.env['stock.picking.batch'])
batch_form.picking_ids.add(delivery_1)
batch_form.picking_ids.add(delivery_2)
batch_delivery = batch_form.save()
batch_delivery.action_confirm()
url = self._get_batch_client_action_url(batch_delivery.id)
self.start_tour(url, 'test_barcode_batch_delivery_2_move_entire_package', login='admin', timeout=180)
def test_barcode_batch_scan_lots(self):
""" Checks while scanning lots for a tracked product, the currently selected line must be
completed before changing the line, even if the scanned lot is planned for another picking
or another picking has an empty line for this product.
"""
self.env.ref('base.group_user').implied_ids += self.env.ref('stock.group_production_lot')
self.clean_access_rights()
grp_pack = self.env.ref('stock.group_tracking_lot')
self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]})
common_vals = {'product_id': self.productlot1.id, 'company_id': self.env.company.id}
lot1 = self.env['stock.lot'].create({**common_vals, 'name': 'lot1'})
lot2 = self.env['stock.lot'].create({**common_vals, 'name': 'lot2'})
self.env['stock.quant']._update_available_quantity(self.productlot1, self.stock_location, 3, lot_id=lot1)
self.env['stock.quant']._update_available_quantity(self.productlot1, self.stock_location, 3, lot_id=lot2)
# Create two receipts and batch them (test for unreserved lots.)
receipt_form = Form(self.env['stock.picking'])
receipt_form.picking_type_id = self.picking_type_in
with receipt_form.move_ids_without_package.new() as move:
move.product_id = self.productlot1
move.product_uom_qty = 4
receipt_1 = receipt_form.save()
receipt_1.action_confirm()
receipt_1.name = "receipt_1"
receipt_2 = receipt_1.copy()
receipt_2.action_confirm
receipt_2.name = "receipt_2"
batch_form = Form(self.env['stock.picking.batch'])
batch_form.picking_ids.add(receipt_1)
batch_form.picking_ids.add(receipt_2)
batch_receipt = batch_form.save()
batch_receipt.action_confirm()
batch_receipt.name = "test_barcode_batch_scan_lots - receipt"
# Create two deliveries and batch them (test for reserved lots.)
delivery_form = Form(self.env['stock.picking'])
delivery_form.picking_type_id = self.picking_type_out
with delivery_form.move_ids_without_package.new() as move:
move.product_id = self.productlot1
move.product_uom_qty = 3
delivery_1 = delivery_form.save()
delivery_1.action_confirm()
delivery_1.name = "delivery_1"
delivery_2 = delivery_1.copy()
delivery_2.action_confirm()
delivery_2.name = "delivery_2"
batch_form = Form(self.env['stock.picking.batch'])
batch_form.picking_ids.add(delivery_1)
batch_form.picking_ids.add(delivery_2)
batch_delivery = batch_form.save()
batch_delivery.action_confirm()
batch_delivery.name = "test_barcode_batch_scan_lots - delivery"
# Process both of them (first the receipt then the delivery.)
action = self.env.ref('stock_barcode.stock_barcode_action_main_menu')
self.start_tour(f'/web#action={action.id}', 'test_barcode_batch_scan_lots', login='admin', timeout=180)
# Checks pickings move lines values.
lot1, lot2, lot3 = (batch_receipt | batch_delivery).move_line_ids.lot_id.sorted('name')
self.assertRecordValues(receipt_1.move_line_ids, [
{'lot_id': lot1.id, 'lot_name': 'lot1', 'quantity': 3, 'picked': True},
{'lot_id': lot2.id, 'lot_name': 'lot2', 'quantity': 1, 'picked': True},
])
self.assertRecordValues(receipt_2.move_line_ids, [
{'lot_id': lot2.id, 'lot_name': 'lot2', 'quantity': 2, 'picked': True},
{'lot_id': lot3.id, 'lot_name': 'lot3', 'quantity': 2, 'picked': True},
])
self.assertRecordValues(delivery_1.move_line_ids, [
{'lot_id': lot2.id, 'quantity': 2, 'picked': True},
{'lot_id': lot3.id, 'quantity': 1, 'picked': True},
])
self.assertRecordValues(delivery_2.move_line_ids, [
{'lot_id': lot2.id, 'quantity': 1, 'picked': True},
{'lot_id': lot1.id, 'quantity': 1, 'picked': True},
{'lot_id': lot3.id, 'quantity': 1, 'picked': True},
])
def test_barcode_batch_scan_other_reserved_lost(self):
""" Checks that scanning a lot won't erase the reserved one if the
scanned one is reserved too."""
self.env.ref('base.group_user').implied_ids += self.env.ref('stock.group_production_lot')
self.clean_access_rights()
grp_pack = self.env.ref('stock.group_tracking_lot')
self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]})
# Create some lots and add quantity in stock for them.
common_vals = {'product_id': self.productlot1.id, 'company_id': self.env.company.id}
lots = self.env['stock.lot'].create([{**common_vals, 'name': f'lot{i}'} for i in range(1, 4)])
for lot in lots:
self.env['stock.quant']._update_available_quantity(self.productlot1, self.stock_location, 3, lot_id=lot)
# Create a delivery and batch it alone (a batch with a single picking is enough for this test's purpose.)
delivery = self.env['stock.picking'].create({
'name': "delivery with lots",
'location_id': self.stock_location.id,
'location_dest_id': self.customer_location.id,
'picking_type_id': self.picking_type_out.id,
'move_ids': [Command.create({
'name': 'productlot1 delivery move',
'location_id': self.stock_location.id,
'location_dest_id': self.customer_location.id,
'product_id': self.productlot1.id,
'product_uom': self.uom_unit.id,
'product_uom_qty': 9,
})],
})
batch = self.env['stock.picking.batch'].create({
'name': "delivery batch",
'picking_ids': [Command.link(delivery.id)],
})
batch.action_confirm()
action = self.env.ref('stock_barcode.stock_barcode_action_main_menu')
self.start_tour(f'/web#action={action.id}', 'test_barcode_batch_scan_other_reserved_lost', login='admin', timeout=180)
def test_put_in_pack_from_multiple_pages(self):
""" A batch picking of 2 internal pickings where prod1 and prod2 are reserved in shelf1 and shelf2,
processing all these products and then hitting put in pack should move them all in the new pack.
This is a copy of the stock_barcode `test_put_in_pack_from_multiple_pages` test with exception that
there are 2 internal pickings containing the 2 products. We expect the same UI and behavior with the
batch's `put_in_pack` button as we do with a single internal transfer so we re-use the same exact tour.
Note that batch `put_in_pack` logic is not the same as it is for pickings.
"""
self.env['stock.quant']._update_available_quantity(self.product1, self.shelf1, 1)
self.env['stock.quant']._update_available_quantity(self.product2, self.shelf1, 1)
self.env['stock.quant']._update_available_quantity(self.product1, self.shelf2, 1)
self.env['stock.quant']._update_available_quantity(self.product2, self.shelf2, 1)
# Adapts the setting to scan only the source location.
self.picking_type_internal.restrict_scan_dest_location = 'no'
self.picking_type_internal.restrict_scan_source_location = 'mandatory'
internal_picking = self.env['stock.picking'].create({
'location_id': self.stock_location.id,
'location_dest_id': self.stock_location.id,
'picking_type_id': self.picking_type_internal.id,
})
self.env['stock.move'].create({
'name': 'test_put_in_pack_from_multiple_pages',
'location_id': self.stock_location.id,
'location_dest_id': self.stock_location.id,
'product_id': self.product1.id,
'product_uom': self.uom_unit.id,
'product_uom_qty': 2,
'picking_id': internal_picking.id,
})
internal_picking2 = self.env['stock.picking'].create({
'location_id': self.stock_location.id,
'location_dest_id': self.stock_location.id,
'picking_type_id': self.picking_type_internal.id,
})
self.env['stock.move'].create({
'name': 'test_put_in_pack_from_multiple_pages',
'location_id': self.stock_location.id,
'location_dest_id': self.stock_location.id,
'product_id': self.product2.id,
'product_uom': self.uom_unit.id,
'product_uom_qty': 2,
'picking_id': internal_picking2.id,
})
internal_picking.action_confirm()
internal_picking.action_assign()
internal_picking2.action_confirm()
internal_picking2.action_assign()
batch_form = Form(self.env['stock.picking.batch'])
batch_form.picking_ids.add(internal_picking)
batch_form.picking_ids.add(internal_picking2)
batch_internal = batch_form.save()
batch_internal.action_confirm()
self.assertEqual(len(batch_internal.move_ids), 2)
url = self._get_batch_client_action_url(batch_internal.id)
self.start_tour(url, 'test_put_in_pack_from_multiple_pages', login='admin', timeout=180)
pack = self.env['stock.quant.package'].search([('location_id', '=', self.stock_location.id)], limit=1)
self.assertEqual(len(pack.quant_ids), 2)
self.assertEqual(sum(pack.quant_ids.mapped('quantity')), 4)
def test_put_in_pack_before_dest(self):
""" A batch picking of 2 internal pickings where prod1 and prod2 are reserved in shelf1 and shelf3,
and have different move destinations. Processing the products and then put in pack should open a choose
destination wizard which will help make sure the package ends up where its expected.
This is a copy of the stock_barcode `test_put_in_pack_before_dest` test with exception that
there are 2 internal pickings containing the 2 products. We expect the same UI and behavior with the
batch's `put_in_pack` button as we do with a single internal transfer so we re-use the same exact tour.
For some reason the order of the move lines in the destination wizard is different, so we swap the expected
destination in this test (since it doesn't matter).
"""
self.picking_type_internal.active = True
self.picking_type_internal.restrict_scan_dest_location = 'mandatory'
self.picking_type_internal.restrict_scan_source_location = 'mandatory'
self.env['stock.quant']._update_available_quantity(self.product1, self.shelf1, 1)
self.env['stock.quant']._update_available_quantity(self.product2, self.shelf3, 1)
internal_picking = self.env['stock.picking'].create({
'location_id': self.stock_location.id,
'location_dest_id': self.stock_location.id,
'picking_type_id': self.picking_type_internal.id,
})
self.env['stock.move'].create({
'name': 'test_put_in_pack_before_dest',
'location_id': self.shelf1.id,
'location_dest_id': self.shelf2.id,
'product_id': self.product1.id,
'product_uom': self.uom_unit.id,
'product_uom_qty': 1,
'picking_id': internal_picking.id,
})
internal_picking2 = self.env['stock.picking'].create({
'location_id': self.stock_location.id,
'location_dest_id': self.stock_location.id,
'picking_type_id': self.picking_type_internal.id,
})
self.env['stock.move'].create({
'name': 'test_put_in_pack_before_dest',
'location_id': self.shelf3.id,
'location_dest_id': self.shelf4.id,
'product_id': self.product2.id,
'product_uom': self.uom_unit.id,
'product_uom_qty': 1,
'picking_id': internal_picking2.id,
})
internal_picking.action_confirm()
internal_picking.action_assign()
internal_picking2.action_confirm()
internal_picking2.action_assign()
batch_form = Form(self.env['stock.picking.batch'])
batch_form.picking_ids.add(internal_picking)
batch_form.picking_ids.add(internal_picking2)
batch_internal = batch_form.save()
batch_internal.action_confirm()
self.assertEqual(len(batch_internal.move_ids), 2)
url = self._get_batch_client_action_url(batch_internal.id)
self.start_tour(url, 'test_put_in_pack_before_dest', login='admin', timeout=180)
pack = self.env['stock.quant.package'].search([('location_id', '=', self.shelf2.id)], limit=1)
self.assertEqual(len(pack.quant_ids), 2)
self.assertEqual(pack.location_id, self.shelf2)
def test_put_in_pack_scan_suggested_package(self):
""" Create two deliveries with a line from two different locations each.
Then, group them in a batch and process the batch in barcode.
Put first picking line in a package and the second one in another package,
then change the location page and scan the suggested packaged for each picking lines.
"""
self.clean_access_rights()
grp_multi_loc = self.env.ref('stock.group_stock_multi_locations')
self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]})
grp_pack = self.env.ref('stock.group_tracking_lot')
self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]})
self.picking_type_out.barcode_validation_all_product_packed = True
self.env['stock.quant']._update_available_quantity(self.product1, self.shelf1, 2)
self.env['stock.quant']._update_available_quantity(self.product2, self.shelf2, 2)
self.env['stock.quant']._update_available_quantity(self.product3, self.shelf1, 1)
# Creates a first delivery with 2 move lines: one from Section 1 and one from Section 2.
delivery_form = Form(self.env['stock.picking'])
delivery_form.picking_type_id = self.picking_type_out
with delivery_form.move_ids_without_package.new() as move:
move.product_id = self.product1
move.product_uom_qty = 1
with delivery_form.move_ids_without_package.new() as move:
move.product_id = self.product2
move.product_uom_qty = 1
with delivery_form.move_ids_without_package.new() as move:
move.product_id = self.product3
move.product_uom_qty = 1
delivery_1 = delivery_form.save()
delivery_1.action_confirm()
delivery_1.action_assign()
# Creates a second delivery (same idea than the first one).
delivery_form = Form(self.env['stock.picking'])
delivery_form.picking_type_id = self.picking_type_out
with delivery_form.move_ids_without_package.new() as move:
move.product_id = self.product1
move.product_uom_qty = 1
with delivery_form.move_ids_without_package.new() as move:
move.product_id = self.product2
move.product_uom_qty = 1
delivery_2 = delivery_form.save()
delivery_2.action_confirm()
delivery_2.action_assign()
# Changes name of pickings to be able to track them on the tour
delivery_1.name = 'test_delivery_1'
delivery_2.name = 'test_delivery_2'
batch_form = Form(self.env['stock.picking.batch'])
batch_form.picking_ids.add(delivery_1)
batch_form.picking_ids.add(delivery_2)
batch_delivery = batch_form.save()
batch_delivery.action_confirm()
self.assertEqual(len(batch_delivery.move_ids), 5)
self.assertEqual(len(batch_delivery.move_line_ids), 5)
# Resets package sequence to be sure we'll have the attended packages name.
seq = self.env['ir.sequence'].search([('code', '=', 'stock.quant.package')])
seq.number_next_actual = 1
url = self._get_batch_client_action_url(batch_delivery.id)
self.start_tour(url, 'test_put_in_pack_scan_suggested_package', login='admin', timeout=180)
self.assertEqual(batch_delivery.state, 'done')
self.assertEqual(len(batch_delivery.move_line_ids), 5)
for move_line in delivery_1.move_line_ids:
self.assertEqual(move_line.result_package_id.name, 'PACK0000001')
for move_line in delivery_2.move_line_ids:
self.assertEqual(move_line.result_package_id.name, 'PACK0000002')
def test_batch_create(self):
""" Create a batch picking via barcode app from scratch """
action_id = self.env.ref('stock_barcode.stock_barcode_action_main_menu')
url = "/web#action=" + str(action_id.id)
self.start_tour(url, 'test_batch_create', login='admin', timeout=180)
self.assertEqual(self.picking_delivery_1.batch_id, self.picking_delivery_2.batch_id)
batch_delivery = self.picking_delivery_1.batch_id
self.assertEqual(len(batch_delivery.move_ids), 5)
self.assertEqual(len(batch_delivery.move_line_ids), 7)
def test_pack_and_same_product_several_sml(self):
"""
A batch with two transfers, source and destination are the same. The
first picking contains 3 x P1 and 25 x P2, the second one 7 x P1 and
30 x P2. The 10 P1 are in a package PK1. The situation is more
complicated for the second product: there are 100 P2 in a package PK2.
When processing the batch, if the user scans the package PK1, it should
update all move lines related to P1. Then, when scanning PK2, it should
also update the move lines related to P2 and a new line should be
created for the surplus (45 x P2).
"""
self.clean_access_rights()
grp_pack = self.env.ref('stock.group_tracking_lot')
self.env.user.write({'groups_id': [(4, grp_pack.id)]})
package02 = self.package.copy({'name': 'P00002'})
self.env['stock.quant']._update_available_quantity(self.product1, self.shelf1, 10, package_id=self.package)
self.env['stock.quant']._update_available_quantity(self.product2, self.shelf1, 100, package_id=package02)
# Two pickings,
# one with 3 x P1 and 25 x P2
# one with 7 x P1 and 30 x P2
pickings = self.env['stock.picking'].create([{
'location_id': self.shelf1.id,
'location_dest_id': self.shelf2.id,
'picking_type_id': self.picking_type_internal.id,
'move_ids': [(0, 0, {
'name': 'test_put_in_pack_from_multiple_pages',
'location_id': self.shelf1.id,
'location_dest_id': self.shelf2.id,
'product_id': product.id,
'product_uom': self.uom_unit.id,
'product_uom_qty': qty,
}) for product, qty in picking_lines]
} for picking_lines in [
[(self.product1, 3), (self.product2, 25)],
[(self.product1, 7), (self.product2, 30)],
]])
pickings.action_confirm()
pickings.action_assign()
batch_form = Form(self.env['stock.picking.batch'])
batch_form.picking_ids.add(pickings[0])
batch_form.picking_ids.add(pickings[1])
batch = batch_form.save()
batch.action_confirm()
url = self._get_batch_client_action_url(batch.id)
self.start_tour(url, 'test_pack_and_same_product_several_sml', login='admin', timeout=180)
self.assertRecordValues(pickings.move_ids, [
{'picking_id': pickings[0].id, 'product_id': self.product1.id, 'state': 'done', 'quantity': 3, 'picked': True},
{'picking_id': pickings[0].id, 'product_id': self.product2.id, 'state': 'done', 'quantity': 70, 'picked': True},
{'picking_id': pickings[1].id, 'product_id': self.product1.id, 'state': 'done', 'quantity': 7, 'picked': True},
{'picking_id': pickings[1].id, 'product_id': self.product2.id, 'state': 'done', 'quantity': 30, 'picked': True},
])
def test_delete_from_batch(self):
action_id = self.env.ref('stock_barcode.stock_barcode_action_main_menu')
url = "/web#action=" + str(action_id.id)
self.start_tour(url, 'test_delete_from_batch', login='admin', timeout=180)
def test_split_line_on_exit_for_batch(self):
""" Ensures that exit an unfinished batch will split the uncompleted move lines to have one
move line with all picked quantity and one move line with the remaining quantity."""
self.clean_access_rights()
# Creates a new batch.
batch_receipts = self.env['stock.picking.batch'].create({
'name': 'batch_split_line_on_exit',
'picking_type_id': self.picking_type_in.id,
})
# Creates two receipts (one for 4x product1, one for 4x product2) and add them to the batch.
# Creates a receipt for 4x product1 and a second receipt for 4x product2.
receipt1 = self.env['stock.picking'].create({
'batch_id': batch_receipts.id,
'name': "receipt1",
'location_id': self.supplier_location.id,
'location_dest_id': self.stock_location.id,
'picking_type_id': self.picking_type_in.id,
})
self.env['stock.move'].create({
'location_dest_id': receipt1.location_dest_id.id,
'location_id': receipt1.location_id.id,
'name': "product1 x4",
'picking_id': receipt1.id,
'product_id': self.product1.id,
'product_uom_qty': 4,
})
receipt2 = self.env['stock.picking'].create({
'batch_id': batch_receipts.id,
'name': "receipt2",
'location_id': self.supplier_location.id,
'location_dest_id': self.stock_location.id,
'picking_type_id': self.picking_type_in.id,
})
self.env['stock.move'].create({
'location_dest_id': receipt2.location_dest_id.id,
'location_id': receipt2.location_id.id,
'name': "product2 x4",
'picking_id': receipt2.id,
'product_id': self.product2.id,
'product_uom_qty': 4,
})
batch_receipts.action_confirm()
action_id = self.env.ref('stock_barcode.stock_barcode_action_main_menu')
url = f"/web#action={action_id.id}"
self.start_tour(url, 'test_split_line_on_exit_for_batch', login='admin')
# Checks the receipts moves values.
self.assertRecordValues(receipt1.move_ids, [
{'product_id': self.product1.id, 'quantity': 2, 'picked': True},
{'product_id': self.product1.id, 'quantity': 2, 'picked': False},
])
self.assertRecordValues(receipt2.move_ids, [
{'product_id': self.product2.id, 'quantity': 1, 'picked': True},
{'product_id': self.product2.id, 'quantity': 3, 'picked': False},
])
def test_scan_can_change_destination_location(self):
""" When we have multiple pickings in a batch for the same product, we should be able to change
the destination location of each of them by scanning only once the destination.
"""
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, quantity=2)
self.picking_type_internal.write({
'restrict_scan_source_location': 'mandatory',
'restrict_scan_product': True,
'restrict_scan_dest_location': 'mandatory',
})
internal_picking_1 = self.env['stock.picking'].create({
'location_id': self.stock_location.id,
'location_dest_id': self.stock_location.id,
'picking_type_id': self.picking_type_internal.id,
})
self.env['stock.move'].create({
'location_dest_id': internal_picking_1.location_dest_id.id,
'location_id': internal_picking_1.location_id.id,
'name': 'product1 x1',
'picking_id': internal_picking_1.id,
'product_id': self.product1.id,
'product_uom_qty': 1,
})
internal_picking_2 = internal_picking_1.copy()
internal_picking_1.name = 'test_int_picking_1'
internal_picking_2.name = 'test_int_picking_2'
batch_form = Form(self.env['stock.picking.batch'])
batch_form.picking_type_id = self.picking_type_internal
batch_form.picking_ids.add(internal_picking_1)
batch_form.picking_ids.add(internal_picking_2)
batch = batch_form.save()
batch.action_confirm()
url = self._get_batch_client_action_url(batch.id)
self.start_tour(url, 'test_scan_can_change_destination_location', login='admin')
# validate that SML destination locations changed
move_lines = internal_picking_1.move_line_ids + internal_picking_2.move_line_ids
self.assertEqual(move_lines.mapped('location_dest_id'), self.shelf3)