2039 lines
95 KiB
Python
2039 lines
95 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from unittest.mock import patch
|
|
|
|
import odoo
|
|
from odoo.tests import Form, tagged
|
|
from odoo.addons.stock_barcode.tests.test_barcode_client_action import TestBarcodeClientAction
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestPickingBarcodeClientAction(TestBarcodeClientAction):
|
|
def test_internal_picking_from_scratch(self):
|
|
""" Opens an empty internal picking and creates following move through the form view:
|
|
- move 2 `self.product1` from shelf1 to shelf2
|
|
- move 1 `self.product2` from shelf1 to shelf3
|
|
- move 1 `self.product2` from shelf1 to shelf2
|
|
Then creates a fourth move by scanning product1 (from shelf1 to shelf3).
|
|
Counts the number of picking's write.
|
|
"""
|
|
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)]})
|
|
self.picking_type_internal.restrict_scan_dest_location = 'mandatory'
|
|
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,
|
|
'immediate_transfer': True,
|
|
})
|
|
url = self._get_client_action_url(internal_picking.id)
|
|
|
|
# Mock the calls to write and run the phantomjs script.
|
|
picking_write_orig = odoo.addons.stock.models.stock_picking.Picking.write
|
|
picking_button_validate_orig = odoo.addons.stock.models.stock_picking.Picking.button_validate
|
|
product1 = self.product1
|
|
stock_location = self.stock_location
|
|
shelf1 = self.shelf1
|
|
shelf3 = self.shelf3
|
|
assertEqual = self.assertEqual
|
|
self1 = self
|
|
self1.stop_count_write = False
|
|
|
|
def picking_write_mock(self, vals):
|
|
if self1.stop_count_write:
|
|
# Stops to count before `stock.picking` `button_validate` was called because
|
|
# that method and its overrides can call an unpredictable amount of write.
|
|
return picking_write_orig(self, vals)
|
|
self1.call_count += 1
|
|
if self1.call_count == 1: # Open the edit form view for a line added by scanning its product.
|
|
cmd = vals['move_line_ids'][0]
|
|
write_vals = cmd[2]
|
|
assertEqual(cmd[0], 0)
|
|
assertEqual(cmd[1], 0)
|
|
assertEqual(write_vals['product_id'], product1.id)
|
|
assertEqual(write_vals['picking_id'], internal_picking.id)
|
|
assertEqual(write_vals['location_id'], shelf1.id)
|
|
assertEqual(write_vals['location_dest_id'], stock_location.id)
|
|
assertEqual(write_vals['qty_done'], 1)
|
|
elif self1.call_count == 2: # Write before the validate.
|
|
cmd = vals['move_line_ids'][0]
|
|
write_vals = cmd[2]
|
|
assertEqual(cmd[0], 1)
|
|
assertEqual(write_vals['location_dest_id'], shelf3.id)
|
|
return picking_write_orig(self, vals)
|
|
|
|
def picking_button_validate_mock(self):
|
|
self1.stop_count_write = True # Stops to count write once validate is called.
|
|
return picking_button_validate_orig(self)
|
|
|
|
with patch('odoo.addons.stock.models.stock_picking.Picking.write', new=picking_write_mock):
|
|
with patch('odoo.addons.stock.models.stock_picking.Picking.button_validate', new=picking_button_validate_mock):
|
|
self.start_tour(url, 'test_internal_picking_from_scratch', login='admin', timeout=180)
|
|
|
|
self.assertEqual(self.call_count, 2)
|
|
self.assertEqual(len(internal_picking.move_line_ids), 4)
|
|
prod1_ml = internal_picking.move_line_ids.filtered(lambda ml: ml.product_id.id == self.product1.id)
|
|
prod2_ml = internal_picking.move_line_ids.filtered(lambda ml: ml.product_id.id == self.product2.id)
|
|
self.assertEqual(prod1_ml[0].qty_done, 2)
|
|
self.assertEqual(prod1_ml[0].location_id, self.shelf1)
|
|
self.assertEqual(prod1_ml[0].location_dest_id, self.shelf2)
|
|
self.assertEqual(prod1_ml[1].qty_done, 1)
|
|
self.assertEqual(prod1_ml[1].location_id, self.shelf1)
|
|
self.assertEqual(prod1_ml[1].location_dest_id, self.shelf3)
|
|
self.assertEqual(prod2_ml[0].qty_done, 1)
|
|
self.assertEqual(prod2_ml[0].location_id, self.shelf1)
|
|
self.assertEqual(prod2_ml[0].location_dest_id, self.shelf2)
|
|
self.assertEqual(prod2_ml[1].qty_done, 1)
|
|
self.assertEqual(prod2_ml[1].location_id, self.shelf1)
|
|
self.assertEqual(prod2_ml[1].location_dest_id, self.shelf3)
|
|
|
|
def test_internal_picking_reserved_1(self):
|
|
""" Open a reserved internal picking
|
|
- move 1 `self.product1` and 1 `self.product2` from shelf1 to shelf2
|
|
- move 1 `self.product1` from shelf3 to shelf4.
|
|
Before doing the reservation, move 1 `self.product1` from shelf3 to shelf2
|
|
"""
|
|
self.clean_access_rights()
|
|
self.picking_type_internal.restrict_scan_dest_location = 'mandatory'
|
|
self.picking_type_internal.restrict_scan_source_location = 'mandatory'
|
|
grp_multi_loc = self.env.ref('stock.group_stock_multi_locations')
|
|
self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]})
|
|
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,
|
|
})
|
|
odoo.addons.stock.models.stock_picking.Picking.write
|
|
url = self._get_client_action_url(internal_picking.id)
|
|
|
|
# prepare the picking
|
|
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.product2, self.shelf3, 1)
|
|
move1 = self.env['stock.move'].create({
|
|
'name': 'test_internal_picking_reserved_1_1',
|
|
'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': 1,
|
|
'picking_id': internal_picking.id,
|
|
})
|
|
move2 = self.env['stock.move'].create({
|
|
'name': 'test_internal_picking_reserved_1_2',
|
|
'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_picking.id,
|
|
})
|
|
internal_picking.action_confirm()
|
|
internal_picking.action_assign()
|
|
move1.move_line_ids.location_dest_id = self.shelf2.id
|
|
for ml in move2.move_line_ids:
|
|
if ml.location_id.id == self.shelf1.id:
|
|
ml.location_dest_id = self.shelf2.id
|
|
else:
|
|
ml.location_dest_id = self.shelf4.id
|
|
|
|
self.start_tour(url, 'test_internal_picking_reserved_1', login='admin', timeout=180)
|
|
|
|
def test_receipt_from_scratch_with_lots_1(self):
|
|
self.clean_access_rights()
|
|
grp_multi_loc = self.env.ref('stock.group_stock_multi_locations')
|
|
grp_lot = self.env.ref('stock.group_production_lot')
|
|
self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]})
|
|
self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]})
|
|
|
|
receipt_picking = self.env['stock.picking'].create({
|
|
'location_id': self.supplier_location.id,
|
|
'location_dest_id': self.stock_location.id,
|
|
'picking_type_id': self.picking_type_in.id,
|
|
'immediate_transfer': True,
|
|
})
|
|
url = self._get_client_action_url(receipt_picking.id)
|
|
self.start_tour(url, 'test_receipt_from_scratch_with_lots_1', login='admin', timeout=180)
|
|
self.assertEqual(receipt_picking.move_line_ids.mapped('lot_name'), ['lot1', 'lot2'])
|
|
|
|
def test_receipt_from_scratch_with_lots_2(self):
|
|
self.clean_access_rights()
|
|
grp_multi_loc = self.env.ref('stock.group_stock_multi_locations')
|
|
grp_lot = self.env.ref('stock.group_production_lot')
|
|
self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]})
|
|
self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]})
|
|
|
|
receipt_picking = self.env['stock.picking'].create({
|
|
'location_id': self.supplier_location.id,
|
|
'location_dest_id': self.stock_location.id,
|
|
'picking_type_id': self.picking_type_in.id,
|
|
'immediate_transfer': True,
|
|
})
|
|
url = self._get_client_action_url(receipt_picking.id)
|
|
self.start_tour(url, 'test_receipt_from_scratch_with_lots_2', login='admin', timeout=180)
|
|
self.assertEqual(receipt_picking.move_line_ids.mapped('lot_name'), ['lot1', 'lot2'])
|
|
self.assertEqual(receipt_picking.move_line_ids.mapped('qty_done'), [2, 2])
|
|
|
|
def test_receipt_from_scratch_with_lots_3(self):
|
|
""" Scans a non tracked product, then scans a tracked by lots product, then scans a
|
|
production lot twice and checks the tracked product quantity was rightly increased.
|
|
"""
|
|
self.clean_access_rights()
|
|
grp_multi_loc = self.env.ref('stock.group_stock_multi_locations')
|
|
grp_lot = self.env.ref('stock.group_production_lot')
|
|
self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]})
|
|
self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]})
|
|
|
|
receipt_picking = self.env['stock.picking'].create({
|
|
'location_id': self.supplier_location.id,
|
|
'location_dest_id': self.stock_location.id,
|
|
'picking_type_id': self.picking_type_in.id,
|
|
'immediate_transfer': True,
|
|
})
|
|
url = self._get_client_action_url(receipt_picking.id)
|
|
self.start_tour(url, 'test_receipt_from_scratch_with_lots_3', login='admin', timeout=180)
|
|
move_lines = receipt_picking.move_line_ids
|
|
self.assertEqual(move_lines[0].product_id.id, self.product1.id)
|
|
self.assertEqual(move_lines[0].qty_done, 1.0)
|
|
self.assertEqual(move_lines[1].product_id.id, self.productlot1.id)
|
|
self.assertEqual(move_lines[1].qty_done, 2.0)
|
|
self.assertEqual(move_lines[1].lot_name, 'lot1')
|
|
|
|
def test_receipt_from_scratch_with_lots_4(self):
|
|
""" With picking type options "use_create_lots" and "use existing lots" disabled,
|
|
scan a tracked product 3 times and checks the tracked product quantity was rightly
|
|
increased without the need to enter serial/lot number.
|
|
"""
|
|
self.clean_access_rights()
|
|
grp_multi_loc = self.env.ref('stock.group_stock_multi_locations')
|
|
grp_lot = self.env.ref('stock.group_production_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.picking_type_in.use_create_lots = False
|
|
self.picking_type_in.use_existing_lots = False
|
|
|
|
receipt_picking = self.env['stock.picking'].create({
|
|
'location_id': self.supplier_location.id,
|
|
'location_dest_id': self.stock_location.id,
|
|
'picking_type_id': self.picking_type_in.id,
|
|
'immediate_transfer': True,
|
|
})
|
|
url = self._get_client_action_url(receipt_picking.id)
|
|
self.start_tour(url, 'test_receipt_from_scratch_with_lots_4', login='admin', timeout=180)
|
|
move_lines = receipt_picking.move_line_ids
|
|
self.assertEqual(move_lines[0].product_id.id, self.productserial1.id)
|
|
self.assertEqual(move_lines[0].qty_done, 3.0)
|
|
|
|
def test_receipt_with_sn_1(self):
|
|
""" With picking type options "use_create_lots" and "use_existing_lots" enabled, scan a
|
|
tracked product and enter a serial number already registered (but not used) in the system.
|
|
"""
|
|
self.clean_access_rights()
|
|
grp_multi_loc = self.env.ref('stock.group_stock_multi_locations')
|
|
grp_lot = self.env.ref('stock.group_production_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.picking_type_in.use_create_lots = True
|
|
self.picking_type_in.use_existing_lots = True
|
|
snObj = self.env['stock.lot']
|
|
snObj.create({'name': 'sn1', 'product_id': self.productserial1.id, 'company_id': self.env.company.id})
|
|
|
|
receipt_picking = self.env['stock.picking'].create({
|
|
'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({
|
|
'name': 'test_receipt_1',
|
|
'location_id': self.supplier_location.id,
|
|
'location_dest_id': self.stock_location.id,
|
|
'product_id': self.productserial1.id,
|
|
'product_uom': self.productserial1.uom_id.id,
|
|
'product_uom_qty': 1,
|
|
'picking_id': receipt_picking.id,
|
|
'picking_type_id': self.picking_type_in.id,
|
|
})
|
|
|
|
url = self._get_client_action_url(receipt_picking.id)
|
|
self.start_tour(url, 'test_receipt_with_sn_1', login='admin', timeout=180)
|
|
move_lines = receipt_picking.move_line_ids
|
|
self.assertEqual(move_lines[0].product_id.id, self.productserial1.id)
|
|
self.assertEqual(move_lines[0].lot_id.name, 'sn1')
|
|
self.assertEqual(move_lines[0].qty_done, 1.0)
|
|
|
|
def test_receipt_reserved_1(self):
|
|
""" Open a receipt. Move four units of `self.product1` and four units of
|
|
unit of `self.product2` into shelf1.
|
|
"""
|
|
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)]})
|
|
receipt_picking = self.env['stock.picking'].create({
|
|
'location_id': self.supplier_location.id,
|
|
'location_dest_id': self.stock_location.id,
|
|
'picking_type_id': self.picking_type_in.id,
|
|
})
|
|
picking_write_orig = odoo.addons.stock.models.stock_picking.Picking.write
|
|
url = self._get_client_action_url(receipt_picking.id)
|
|
|
|
move1 = self.env['stock.move'].create({
|
|
'name': 'test_receipt_reserved_1_1',
|
|
'location_id': self.supplier_location.id,
|
|
'location_dest_id': self.stock_location.id,
|
|
'product_id': self.product1.id,
|
|
'product_uom': self.uom_unit.id,
|
|
'product_uom_qty': 4,
|
|
'picking_id': receipt_picking.id,
|
|
})
|
|
move2 = self.env['stock.move'].create({
|
|
'name': 'test_receipt_reserved_1_2',
|
|
'location_id': self.supplier_location.id,
|
|
'location_dest_id': self.stock_location.id,
|
|
'product_id': self.product2.id,
|
|
'product_uom': self.uom_unit.id,
|
|
'product_uom_qty': 4,
|
|
'picking_id': receipt_picking.id,
|
|
})
|
|
receipt_picking.action_confirm()
|
|
receipt_picking.action_assign()
|
|
|
|
# Mock the calls to write and run the phantomjs script.
|
|
assertEqual = self.assertEqual
|
|
ml1 = move1.move_line_ids
|
|
ml2 = move2.move_line_ids
|
|
shelf1 = self.shelf1
|
|
self1 = self
|
|
|
|
def picking_write_mock(self, vals):
|
|
self1.call_count += 1
|
|
if self1.call_count == 1:
|
|
assertEqual(len(vals['move_line_ids']), 2)
|
|
assertEqual(vals['move_line_ids'][0][:2], [1, ml2.id])
|
|
assertEqual(vals['move_line_ids'][1][:2], [1, ml1.id])
|
|
return picking_write_orig(self, vals)
|
|
|
|
with patch('odoo.addons.stock.models.stock_picking.Picking.write', new=picking_write_mock):
|
|
self.start_tour(url, 'test_receipt_reserved_1', login='admin', timeout=180)
|
|
self.assertEqual(self.call_count, 1)
|
|
self.assertEqual(receipt_picking.move_line_ids[0].location_dest_id.id, shelf1.id)
|
|
self.assertEqual(receipt_picking.move_line_ids[1].location_dest_id.id, shelf1.id)
|
|
|
|
def test_receipt_product_not_consecutively(self):
|
|
""" Check that there is no new line created when scanning the same product several times but not consecutively."""
|
|
self.clean_access_rights()
|
|
|
|
receipt_picking = self.env['stock.picking'].create({
|
|
'location_id': self.supplier_location.id,
|
|
'location_dest_id': self.stock_location.id,
|
|
'picking_type_id': self.picking_type_in.id,
|
|
})
|
|
|
|
url = self._get_client_action_url(receipt_picking.id)
|
|
self.start_tour(url, 'test_receipt_product_not_consecutively', login='admin', timeout=180)
|
|
|
|
self.assertEqual(len(receipt_picking.move_line_ids), 2)
|
|
self.assertEqual(receipt_picking.move_line_ids.product_id.mapped('id'), [self.product1.id, self.product2.id])
|
|
self.assertEqual(receipt_picking.move_line_ids.mapped('qty_done'), [2, 1])
|
|
|
|
def test_delivery_lot_with_package(self):
|
|
""" Have a delivery for a product tracked by SN, scan a non-reserved SN
|
|
and checks the new created line has the right SN's package & owner.
|
|
"""
|
|
self.clean_access_rights()
|
|
grp_lot = self.env.ref('stock.group_production_lot')
|
|
grp_owner = self.env.ref('stock.group_tracking_owner')
|
|
grp_pack = self.env.ref('stock.group_tracking_lot')
|
|
self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]})
|
|
self.env.user.write({'groups_id': [(4, grp_owner.id, 0)]})
|
|
self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]})
|
|
|
|
# Creates 4 serial numbers and adds 2 qty. for the reservation.
|
|
snObj = self.env['stock.lot']
|
|
sn1 = snObj.create({'name': 'sn1', 'product_id': self.productserial1.id, 'company_id': self.env.company.id})
|
|
sn2 = snObj.create({'name': 'sn2', 'product_id': self.productserial1.id, 'company_id': self.env.company.id})
|
|
sn3 = snObj.create({'name': 'sn3', 'product_id': self.productserial1.id, 'company_id': self.env.company.id})
|
|
sn4 = snObj.create({'name': 'sn4', 'product_id': self.productserial1.id, 'company_id': self.env.company.id})
|
|
package1 = self.env['stock.quant.package'].create({'name': 'pack_sn_1'})
|
|
package2 = self.env['stock.quant.package'].create({'name': 'pack_sn_2'})
|
|
partner = self.env['res.partner'].create({'name': 'Particulier'})
|
|
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.stock_location.id,
|
|
'package_id': package1.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.stock_location.id,
|
|
'package_id': package1.id,
|
|
}).action_apply_inventory()
|
|
|
|
# Creates and confirms the delivery.
|
|
delivery_picking = self.env['stock.picking'].create({
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'picking_type_id': self.picking_type_out.id,
|
|
})
|
|
self.env['stock.move'].create({
|
|
'name': self.productserial1.name,
|
|
'product_id': self.productserial1.id,
|
|
'product_uom_qty': 2,
|
|
'product_uom': self.productserial1.uom_id.id,
|
|
'picking_id': delivery_picking.id,
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
})
|
|
delivery_picking.action_confirm()
|
|
delivery_picking.action_assign()
|
|
# Add 2 more qty. after the reservation.
|
|
self.env['stock.quant'].with_context(inventory_mode=True).create({
|
|
'product_id': self.productserial1.id,
|
|
'inventory_quantity': 1,
|
|
'lot_id': sn3.id,
|
|
'location_id': self.stock_location.id,
|
|
'package_id': package2.id,
|
|
}).action_apply_inventory()
|
|
self.env['stock.quant'].with_context(inventory_mode=True).create({
|
|
'product_id': self.productserial1.id,
|
|
'inventory_quantity': 1,
|
|
'lot_id': sn4.id,
|
|
'location_id': self.stock_location.id,
|
|
'package_id': package2.id,
|
|
'owner_id': partner.id,
|
|
}).action_apply_inventory()
|
|
|
|
# Runs the tour.
|
|
url = self._get_client_action_url(delivery_picking.id)
|
|
self.start_tour(url, 'test_delivery_lot_with_package', login='admin', timeout=180)
|
|
|
|
# Checks move lines values after delivery was completed.
|
|
self.assertEqual(delivery_picking.state, "done")
|
|
move_line_1 = delivery_picking.move_line_ids[0]
|
|
move_line_2 = delivery_picking.move_line_ids[1]
|
|
self.assertEqual(move_line_1.lot_id, sn3)
|
|
self.assertEqual(move_line_1.package_id, package2)
|
|
self.assertEqual(move_line_1.owner_id.id, False)
|
|
self.assertEqual(move_line_2.lot_id, sn4)
|
|
self.assertEqual(move_line_2.package_id, package2)
|
|
self.assertEqual(move_line_2.owner_id, partner)
|
|
|
|
def test_delivery_reserved_1(self):
|
|
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)]})
|
|
delivery_picking = self.env['stock.picking'].create({
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'picking_type_id': self.picking_type_out.id,
|
|
'note': "A Test Note",
|
|
})
|
|
picking_write_orig = odoo.addons.stock.models.stock_picking.Picking.write
|
|
url = self._get_client_action_url(delivery_picking.id)
|
|
|
|
self.env['stock.move'].create({
|
|
'name': 'test_delivery_reserved_1_1',
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'product_id': self.product1.id,
|
|
'product_uom': self.uom_unit.id,
|
|
'product_uom_qty': 4,
|
|
'picking_id': delivery_picking.id,
|
|
})
|
|
self.env['stock.move'].create({
|
|
'name': 'test_delivery_reserved_1_2',
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'product_id': self.product2.id,
|
|
'product_uom': self.uom_unit.id,
|
|
'product_uom_qty': 4,
|
|
'picking_id': delivery_picking.id,
|
|
})
|
|
|
|
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 4)
|
|
self.env['stock.quant']._update_available_quantity(self.product2, self.stock_location, 4)
|
|
|
|
delivery_picking.action_confirm()
|
|
delivery_picking.action_assign()
|
|
|
|
self1 = self
|
|
|
|
# Mock the calls to write and run the phantomjs script.
|
|
def picking_write_mock(self, vals):
|
|
self1.call_count += 1
|
|
return picking_write_orig(self, vals)
|
|
with patch('odoo.addons.stock.models.stock_picking.Picking.write', new=picking_write_mock):
|
|
self.start_tour(url, 'test_delivery_reserved_1', login='admin', timeout=180)
|
|
self.assertEqual(self.call_count, 1)
|
|
|
|
def test_delivery_reserved_2(self):
|
|
self.clean_access_rights()
|
|
delivery_picking = self.env['stock.picking'].create({
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'picking_type_id': self.picking_type_out.id,
|
|
})
|
|
picking_write_orig = odoo.addons.stock.models.stock_picking.Picking.write
|
|
url = self._get_client_action_url(delivery_picking.id)
|
|
|
|
pg_1 = self.env['procurement.group'].create({'name': 'ProcurementGroup1'})
|
|
pg_2 = self.env['procurement.group'].create({'name': 'ProcurementGroup2'})
|
|
partner_1 = self.env['res.partner'].create({'name': 'Parter1'})
|
|
partner_2 = self.env['res.partner'].create({'name': 'Partner2'})
|
|
self.env['stock.move'].create({
|
|
'name': 'test_delivery_reserved_2_1',
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'product_id': self.product1.id,
|
|
'product_uom': self.uom_unit.id,
|
|
'product_uom_qty': 2,
|
|
'picking_id': delivery_picking.id,
|
|
'group_id': pg_1.id,
|
|
'restrict_partner_id': partner_1.id
|
|
})
|
|
self.env['stock.move'].create({
|
|
'name': 'test_delivery_reserved_2_2',
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'product_id': self.product1.id,
|
|
'product_uom': self.uom_unit.id,
|
|
'product_uom_qty': 2,
|
|
'picking_id': delivery_picking.id,
|
|
'group_id': pg_2.id,
|
|
'restrict_partner_id': partner_2.id
|
|
})
|
|
|
|
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 4)
|
|
self.env['stock.quant']._update_available_quantity(self.product2, self.stock_location, 4)
|
|
|
|
delivery_picking.action_confirm()
|
|
delivery_picking.action_assign()
|
|
self.assertEqual(len(delivery_picking.move_ids), 2)
|
|
|
|
self1 = self
|
|
|
|
def picking_write_mock(self, vals):
|
|
self1.call_count += 1
|
|
return picking_write_orig(self, vals)
|
|
|
|
with patch('odoo.addons.stock.models.stock_picking.Picking.write', new=picking_write_mock):
|
|
self.start_tour(url, 'test_delivery_reserved_2', login='admin', timeout=180)
|
|
self.assertEqual(self.call_count, 0)
|
|
|
|
def test_delivery_reserved_3(self):
|
|
self.clean_access_rights()
|
|
delivery_picking = self.env['stock.picking'].create({
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'picking_type_id': self.picking_type_out.id,
|
|
})
|
|
picking_write_orig = odoo.addons.stock.models.stock_picking.Picking.write
|
|
url = self._get_client_action_url(delivery_picking.id)
|
|
|
|
self.env['stock.move'].create({
|
|
'name': 'test_delivery_reserved_2_1',
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'product_id': self.product1.id,
|
|
'product_uom': self.uom_unit.id,
|
|
'product_uom_qty': 1,
|
|
'picking_id': delivery_picking.id,
|
|
})
|
|
|
|
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 2)
|
|
|
|
delivery_picking.action_confirm()
|
|
delivery_picking.action_assign()
|
|
|
|
self1 = self
|
|
|
|
def picking_write_mock(self, vals):
|
|
self1.call_count += 1
|
|
return picking_write_orig(self, vals)
|
|
|
|
with patch('odoo.addons.stock.models.stock_picking.Picking.write', new=picking_write_mock):
|
|
self.start_tour(url, 'test_delivery_reserved_3', login='admin', timeout=180)
|
|
self.assertEqual(self.call_count, 0)
|
|
|
|
def test_delivery_from_scratch_1(self):
|
|
""" Scan unreserved lots on a delivery order.
|
|
"""
|
|
self.clean_access_rights()
|
|
grp_lot = self.env.ref('stock.group_production_lot')
|
|
self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]})
|
|
|
|
# Adds lot1 and lot2 for productlot1
|
|
lotObj = self.env['stock.lot']
|
|
lotObj.create({'name': 'lot1', 'product_id': self.productlot1.id, 'company_id': self.env.company.id})
|
|
lotObj.create({'name': 'lot2', 'product_id': self.productlot1.id, 'company_id': self.env.company.id})
|
|
|
|
# Creates an empty picking.
|
|
delivery_picking = self.env['stock.picking'].create({
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'picking_type_id': self.picking_type_out.id,
|
|
'immediate_transfer': True,
|
|
})
|
|
url = self._get_client_action_url(delivery_picking.id)
|
|
|
|
self.start_tour(url, 'test_delivery_from_scratch_with_lots_1', login='admin', timeout=180)
|
|
|
|
lines = delivery_picking.move_line_ids
|
|
self.assertEqual(lines[0].lot_id.name, 'lot1')
|
|
self.assertEqual(lines[1].lot_id.name, 'lot2')
|
|
self.assertEqual(lines[0].qty_done, 2)
|
|
self.assertEqual(lines[1].qty_done, 2)
|
|
|
|
def test_delivery_from_scratch_with_common_lots_name(self):
|
|
"""
|
|
Suppose:
|
|
- two tracked-by-lot products
|
|
- these products share one lot name
|
|
- an extra product tracked by serial number
|
|
This test ensures that a user can scan the tracked products in a picking
|
|
that does not expect them and updates/creates the right line depending
|
|
of the scanned lot
|
|
"""
|
|
self.clean_access_rights()
|
|
group_lot = self.env.ref('stock.group_production_lot')
|
|
self.env.user.write({'groups_id': [(4, group_lot.id, 0)]})
|
|
|
|
(self.product1 + self.product2).tracking = 'lot'
|
|
|
|
lot01, lot02, sn = self.env['stock.lot'].create([{
|
|
'name': lot_name,
|
|
'product_id': product.id,
|
|
'company_id': self.env.company.id,
|
|
} for (lot_name, product) in [("LOT01", self.product1), ("LOT01", self.product2), ("SUPERSN", self.productserial1)]])
|
|
|
|
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 2, lot_id=lot01)
|
|
self.env['stock.quant']._update_available_quantity(self.product2, self.stock_location, 3, lot_id=lot02)
|
|
self.env['stock.quant']._update_available_quantity(self.productserial1, self.stock_location, 1, lot_id=sn)
|
|
|
|
picking_form = Form(self.env['stock.picking'].with_context(default_immediate_transfer=True))
|
|
picking_form.picking_type_id = self.picking_type_out
|
|
delivery = picking_form.save()
|
|
|
|
url = self._get_client_action_url(delivery.id)
|
|
self.start_tour(url, 'test_delivery_from_scratch_with_common_lots_name', login='admin', timeout=180)
|
|
|
|
self.assertRecordValues(delivery.move_line_ids, [
|
|
# pylint: disable=C0326
|
|
{'product_id': self.product1.id, 'lot_id': lot01.id, 'qty_done': 2},
|
|
{'product_id': self.product2.id, 'lot_id': lot02.id, 'qty_done': 3},
|
|
{'product_id': self.productserial1.id, 'lot_id': sn.id, 'qty_done': 1},
|
|
])
|
|
|
|
def test_delivery_reserved_lots_1(self):
|
|
self.clean_access_rights()
|
|
grp_lot = self.env.ref('stock.group_production_lot')
|
|
self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]})
|
|
|
|
delivery_picking = self.env['stock.picking'].create({
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'picking_type_id': self.picking_type_out.id,
|
|
})
|
|
url = self._get_client_action_url(delivery_picking.id)
|
|
|
|
self.env['stock.move'].create({
|
|
'name': 'test_delivery_reserved_lots_1',
|
|
'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': 3,
|
|
'picking_id': delivery_picking.id,
|
|
})
|
|
|
|
# Add lot1 et lot2 sur productlot1
|
|
lotObj = self.env['stock.lot']
|
|
lot1 = lotObj.create({'name': 'lot1', 'product_id': self.productlot1.id, 'company_id': self.env.company.id})
|
|
lot2 = lotObj.create({'name': 'lot2', 'product_id': self.productlot1.id, 'company_id': self.env.company.id})
|
|
|
|
self.env['stock.quant']._update_available_quantity(self.productlot1, self.stock_location, 1, lot_id=lot1)
|
|
self.env['stock.quant']._update_available_quantity(self.productlot1, self.stock_location, 2, lot_id=lot2)
|
|
|
|
delivery_picking.action_confirm()
|
|
delivery_picking.action_assign()
|
|
self.assertEqual(delivery_picking.move_ids.state, 'assigned')
|
|
self.assertEqual(len(delivery_picking.move_ids.move_line_ids), 2)
|
|
|
|
self.start_tour(url, 'test_delivery_reserved_lots_1', login='admin', timeout=180)
|
|
|
|
self.env.invalidate_all()
|
|
lines = delivery_picking.move_line_ids
|
|
self.assertEqual(lines[0].lot_id.name, 'lot1')
|
|
self.assertEqual(lines[1].lot_id.name, 'lot2')
|
|
self.assertEqual(lines[0].qty_done, 1)
|
|
self.assertEqual(lines[1].qty_done, 2)
|
|
|
|
def test_delivery_different_products_with_same_lot_name(self):
|
|
self.clean_access_rights()
|
|
grp_lot = self.env.ref('stock.group_production_lot')
|
|
self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]})
|
|
|
|
self.productlot2 = self.env['product.product'].create({
|
|
'name': 'productlot2',
|
|
'type': 'product',
|
|
'categ_id': self.env.ref('product.product_category_all').id,
|
|
'barcode': 'productlot2',
|
|
'tracking': 'lot',
|
|
})
|
|
|
|
delivery_picking = self.env['stock.picking'].create({
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'picking_type_id': self.picking_type_out.id,
|
|
})
|
|
url = self._get_client_action_url(delivery_picking.id)
|
|
|
|
self.env['stock.move'].create({
|
|
'name': 'test_delivery_different_products_with_same_lot_name_1',
|
|
'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': 2,
|
|
'picking_id': delivery_picking.id,
|
|
})
|
|
self.env['stock.move'].create({
|
|
'name': 'test_delivery_different_products_with_same_lot_name_2',
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'product_id': self.productlot2.id,
|
|
'product_uom': self.uom_unit.id,
|
|
'product_uom_qty': 2,
|
|
'picking_id': delivery_picking.id,
|
|
})
|
|
|
|
# Create 2 lots with the same name for productlot1 and productlot2
|
|
lot1 = self.env['stock.lot'].create({'name': 'lot1', 'product_id': self.productlot1.id, 'company_id': self.env.company.id})
|
|
lot2 = self.env['stock.lot'].create({'name': 'lot1', 'product_id': self.productlot2.id, 'company_id': self.env.company.id})
|
|
|
|
self.env['stock.quant']._update_available_quantity(self.productlot1, self.stock_location, 2, lot_id=lot1)
|
|
self.env['stock.quant']._update_available_quantity(self.productlot2, self.stock_location, 2, lot_id=lot2)
|
|
|
|
delivery_picking.action_confirm()
|
|
delivery_picking.action_assign()
|
|
|
|
self.assertEqual(len(delivery_picking.move_ids), 2)
|
|
|
|
self.start_tour(url, 'test_delivery_different_products_with_same_lot_name', login='admin', timeout=180)
|
|
|
|
self.env.invalidate_all()
|
|
lines = delivery_picking.move_line_ids
|
|
self.assertEqual(lines[0].lot_id.name, 'lot1')
|
|
self.assertEqual(lines[0].product_id.name, 'productlot1')
|
|
self.assertEqual(lines[0].qty_done, 2)
|
|
self.assertEqual(lines[1].lot_id.name, 'lot1')
|
|
self.assertEqual(lines[1].product_id.name, 'productlot2')
|
|
self.assertEqual(lines[1].qty_done, 2)
|
|
|
|
def test_delivery_from_scratch_sn_1(self):
|
|
""" Scan unreserved serial number on a delivery order.
|
|
"""
|
|
|
|
self.clean_access_rights()
|
|
grp_lot = self.env.ref('stock.group_production_lot')
|
|
self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]})
|
|
|
|
# Add 4 serial numbers productserial1
|
|
snObj = self.env['stock.lot']
|
|
sn1 = snObj.create({'name': 'sn1', 'product_id': self.productserial1.id, 'company_id': self.env.company.id})
|
|
sn2 = snObj.create({'name': 'sn2', 'product_id': self.productserial1.id, 'company_id': self.env.company.id})
|
|
sn3 = snObj.create({'name': 'sn3', 'product_id': self.productserial1.id, 'company_id': self.env.company.id})
|
|
sn4 = snObj.create({'name': 'sn4', 'product_id': self.productserial1.id, 'company_id': self.env.company.id})
|
|
|
|
self.env['stock.quant']._update_available_quantity(self.productserial1, self.stock_location, 1, lot_id=sn1)
|
|
self.env['stock.quant']._update_available_quantity(self.productserial1, self.stock_location, 1, lot_id=sn2)
|
|
self.env['stock.quant']._update_available_quantity(self.productserial1, self.stock_location, 1, lot_id=sn3)
|
|
self.env['stock.quant']._update_available_quantity(self.productserial1, self.stock_location, 1, lot_id=sn4)
|
|
|
|
# empty picking
|
|
delivery_picking = self.env['stock.picking'].create({
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'picking_type_id': self.picking_type_out.id,
|
|
})
|
|
|
|
self.env['stock.move'].create({
|
|
'name': 'test_delivery_reserved_lots_1',
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'product_id': self.productserial1.id,
|
|
'product_uom': self.uom_unit.id,
|
|
'product_uom_qty': 4,
|
|
'picking_id': delivery_picking.id,
|
|
})
|
|
|
|
delivery_picking.action_confirm()
|
|
delivery_picking.action_assign()
|
|
|
|
url = self._get_client_action_url(delivery_picking.id)
|
|
|
|
self.start_tour(url, 'test_delivery_reserved_with_sn_1', login='admin', timeout=180)
|
|
|
|
# TODO: the framework should call invalidate_cache every time a test cursor is asked or
|
|
# given back
|
|
self.env.invalidate_all()
|
|
lines = delivery_picking.move_line_ids
|
|
self.assertEqual(lines.mapped('lot_id.name'), ['sn1', 'sn2', 'sn3', 'sn4'])
|
|
self.assertEqual(lines.mapped('qty_done'), [1, 1, 1, 1])
|
|
|
|
def test_delivery_using_buttons(self):
|
|
""" Creates a delivery with 3 lines, then:
|
|
- Completes first line with "+1" button;
|
|
- Completes second line with "Add reserved quantities" button;
|
|
- Completes last line with "+1" button and scanning barcode.
|
|
Checks also written quantity on buttons is correctly updated and only
|
|
"+1" button is displayed on new line created by user.
|
|
"""
|
|
self.clean_access_rights()
|
|
|
|
# Creates a new product.
|
|
product3 = self.env['product.product'].create({
|
|
'name': 'product3',
|
|
'type': 'product',
|
|
'categ_id': self.env.ref('product.product_category_all').id,
|
|
'barcode': 'product3',
|
|
})
|
|
|
|
# Creates some quants.
|
|
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 2)
|
|
self.env['stock.quant']._update_available_quantity(self.product2, self.stock_location, 3)
|
|
self.env['stock.quant']._update_available_quantity(product3, self.stock_location, 4)
|
|
|
|
# Create the delivery transfer.
|
|
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 = 2
|
|
with delivery_form.move_ids_without_package.new() as move:
|
|
move.product_id = self.product2
|
|
move.product_uom_qty = 3
|
|
with delivery_form.move_ids_without_package.new() as move:
|
|
move.product_id = product3
|
|
move.product_uom_qty = 4
|
|
|
|
delivery_picking = delivery_form.save()
|
|
delivery_picking.action_confirm()
|
|
delivery_picking.action_assign()
|
|
|
|
url = self._get_client_action_url(delivery_picking.id)
|
|
self.start_tour(url, 'test_delivery_using_buttons', login='admin', timeout=180)
|
|
|
|
self.assertEqual(len(delivery_picking.move_line_ids), 4)
|
|
self.assertEqual(delivery_picking.move_line_ids.mapped('qty_done'), [2, 3, 4, 2])
|
|
|
|
def test_receipt_reserved_lots_multiloc_1(self):
|
|
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_lot = self.env.ref('stock.group_production_lot')
|
|
self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]})
|
|
|
|
receipts_picking = self.env['stock.picking'].create({
|
|
'location_id': self.supplier_location.id,
|
|
'location_dest_id': self.stock_location.id,
|
|
'picking_type_id': self.picking_type_in.id,
|
|
'user_id': False,
|
|
})
|
|
|
|
url = self._get_client_action_url(receipts_picking.id)
|
|
|
|
self.env['stock.move'].create({
|
|
'name': 'test_delivery_reserved_lots_1',
|
|
'location_id': self.supplier_location.id,
|
|
'location_dest_id': self.stock_location.id,
|
|
'product_id': self.productlot1.id,
|
|
'product_uom': self.uom_unit.id,
|
|
'product_uom_qty': 4,
|
|
'picking_id': receipts_picking.id,
|
|
})
|
|
|
|
# Creates lot1 and lot2 for productlot1.
|
|
lotObj = self.env['stock.lot']
|
|
lotObj.create({'name': 'lot1', 'product_id': self.productlot1.id, 'company_id': self.env.company.id})
|
|
lotObj.create({'name': 'lot2', 'product_id': self.productlot1.id, 'company_id': self.env.company.id})
|
|
|
|
receipts_picking.action_confirm()
|
|
receipts_picking.action_assign()
|
|
|
|
self.assertEqual(receipts_picking.user_id.id, False)
|
|
self.start_tour(url, 'test_receipt_reserved_lots_multiloc_1', login='admin', timeout=180)
|
|
self.assertEqual(receipts_picking.user_id.id, self.env.user.id)
|
|
self.env.invalidate_all()
|
|
lines = receipts_picking.move_line_ids
|
|
self.assertEqual(len(lines), 2)
|
|
self.assertEqual(lines.mapped('qty_done'), [2, 2])
|
|
self.assertEqual(lines.mapped('location_id.name'), ['Vendors'])
|
|
self.assertEqual(lines[0].location_dest_id.name, 'Section 1')
|
|
self.assertEqual(lines[0].lot_name, 'lot2')
|
|
self.assertEqual(lines[1].location_dest_id.name, 'Section 2')
|
|
self.assertEqual(lines[1].lot_name, 'lot1')
|
|
|
|
def test_pack_multiple_scan(self):
|
|
""" Make a reception of two products, put them in pack and validate.
|
|
Then make a delivery, scan the package two times (check the warning) and validate.
|
|
Finally, check that the package is in the customer location.
|
|
"""
|
|
self.clean_access_rights()
|
|
grp_pack = self.env.ref('stock.group_tracking_lot')
|
|
self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]})
|
|
|
|
action_id = self.env.ref('stock_barcode.stock_barcode_action_main_menu')
|
|
url = "/web#action=" + str(action_id.id)
|
|
|
|
# set sequence packages to 1000 to find it easily in the tour
|
|
sequence = self.env['ir.sequence'].search([(
|
|
'code', '=', 'stock.quant.package',
|
|
)], limit=1)
|
|
sequence.write({'number_next_actual': 1000})
|
|
|
|
self.start_tour(url, 'test_pack_multiple_scan', login='admin', timeout=180)
|
|
|
|
# Check the new package is well delivered
|
|
package = self.env['stock.quant.package'].search([
|
|
('name', '=', 'PACK0001000')
|
|
])
|
|
self.assertEqual(package.location_id, self.customer_location)
|
|
|
|
def test_pack_common_content_scan(self):
|
|
""" Simulate a picking where 2 packages have the same products
|
|
inside. It should display one barcode line for each package and
|
|
not a common barcode line for both packages.
|
|
"""
|
|
self.clean_access_rights()
|
|
grp_pack = self.env.ref('stock.group_tracking_lot')
|
|
self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]})
|
|
|
|
action_id = self.env.ref('stock_barcode.stock_barcode_action_main_menu')
|
|
url = "/web#action=" + str(action_id.id)
|
|
|
|
# Create a pack and 2 quants in this pack
|
|
pack1 = self.env['stock.quant.package'].create({
|
|
'name': 'PACK1',
|
|
})
|
|
pack2 = self.env['stock.quant.package'].create({
|
|
'name': 'PACK2',
|
|
})
|
|
|
|
self.env['stock.quant']._update_available_quantity(
|
|
product_id=self.product1,
|
|
location_id=self.stock_location,
|
|
quantity=5,
|
|
package_id=pack1,
|
|
)
|
|
self.env['stock.quant']._update_available_quantity(
|
|
product_id=self.product2,
|
|
location_id=self.stock_location,
|
|
quantity=1,
|
|
package_id=pack1,
|
|
)
|
|
|
|
self.env['stock.quant']._update_available_quantity(
|
|
product_id=self.product1,
|
|
location_id=self.stock_location,
|
|
quantity=5,
|
|
package_id=pack2,
|
|
)
|
|
self.env['stock.quant']._update_available_quantity(
|
|
product_id=self.product2,
|
|
location_id=self.stock_location,
|
|
quantity=1,
|
|
package_id=pack2,
|
|
)
|
|
|
|
self.start_tour(url, 'test_pack_common_content_scan', login='admin', timeout=180)
|
|
|
|
def test_pack_multiple_location(self):
|
|
""" Create a package in Shelf 1 and makes an internal transfer to move it to Shelf 2.
|
|
"""
|
|
self.clean_access_rights()
|
|
grp_pack = self.env.ref('stock.group_tracking_lot')
|
|
grp_multi_loc = self.env.ref('stock.group_stock_multi_locations')
|
|
self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]})
|
|
self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]})
|
|
self.picking_type_internal.active = True
|
|
self.picking_type_internal.show_entire_packs = True
|
|
self.picking_type_internal.restrict_scan_dest_location = 'mandatory'
|
|
self.picking_type_internal.restrict_scan_source_location = 'mandatory'
|
|
|
|
action_id = self.env.ref('stock_barcode.stock_barcode_action_main_menu')
|
|
url = "/web#action=" + str(action_id.id)
|
|
|
|
# Create a pack and 2 quants in this pack
|
|
pack1 = self.env['stock.quant.package'].create({
|
|
'name': 'PACK0000666',
|
|
})
|
|
|
|
self.env['stock.quant']._update_available_quantity(
|
|
product_id=self.product1,
|
|
location_id=self.shelf1,
|
|
quantity=5,
|
|
package_id=pack1,
|
|
)
|
|
self.env['stock.quant']._update_available_quantity(
|
|
product_id=self.product2,
|
|
location_id=self.shelf1,
|
|
quantity=5,
|
|
package_id=pack1,
|
|
)
|
|
|
|
self.start_tour(url, 'test_pack_multiple_location', login='admin', timeout=180)
|
|
|
|
# Check the new package is well transfered
|
|
self.assertEqual(pack1.location_id, self.shelf2)
|
|
|
|
def test_pack_multiple_location_02(self):
|
|
""" Creates an internal transfer and reserves a package. Then this test will scan the
|
|
location source, the package (already in the barcode view) and the location destination.
|
|
"""
|
|
self.clean_access_rights()
|
|
grp_pack = self.env.ref('stock.group_tracking_lot')
|
|
grp_multi_loc = self.env.ref('stock.group_stock_multi_locations')
|
|
self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]})
|
|
self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]})
|
|
|
|
# Creates a package with 1 quant in it.
|
|
pack1 = self.env['stock.quant.package'].create({
|
|
'name': 'PACK0002020',
|
|
})
|
|
self.env['stock.quant']._update_available_quantity(
|
|
product_id=self.product1,
|
|
location_id=self.shelf1,
|
|
quantity=5,
|
|
package_id=pack1,
|
|
)
|
|
|
|
# Creates an internal transfer for this package.
|
|
internal_picking = self.env['stock.picking'].create({
|
|
'location_id': self.shelf1.id,
|
|
'location_dest_id': self.shelf2.id,
|
|
'picking_type_id': self.picking_type_internal.id,
|
|
})
|
|
url = self._get_client_action_url(internal_picking.id)
|
|
|
|
self.env['stock.move'].create({
|
|
'name': 'test_delivery_reserved_2_1',
|
|
'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': 5,
|
|
'picking_id': internal_picking.id,
|
|
})
|
|
internal_picking.action_confirm()
|
|
internal_picking.action_assign()
|
|
|
|
self.start_tour(url, 'test_pack_multiple_location_02', login='admin', timeout=180)
|
|
|
|
# Checks the new package is well transfered.
|
|
self.assertEqual(pack1.location_id, self.shelf2)
|
|
|
|
def test_put_in_pack_from_multiple_pages(self):
|
|
""" In an internal picking 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.
|
|
"""
|
|
self.clean_access_rights()
|
|
# 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'
|
|
grp_pack = self.env.ref('stock.group_tracking_lot')
|
|
grp_multi_loc = self.env.ref('stock.group_stock_multi_locations')
|
|
self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]})
|
|
self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]})
|
|
|
|
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)
|
|
|
|
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,
|
|
})
|
|
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_picking.id,
|
|
})
|
|
|
|
url = self._get_client_action_url(internal_picking.id)
|
|
internal_picking.action_confirm()
|
|
internal_picking.action_assign()
|
|
|
|
self.start_tour(url, 'test_put_in_pack_from_multiple_pages', login='admin', timeout=180)
|
|
|
|
pack = self.env['stock.quant.package'].search([])[-1]
|
|
self.assertEqual(len(pack.quant_ids), 2)
|
|
self.assertEqual(sum(pack.quant_ids.mapped('quantity')), 4)
|
|
|
|
def test_reload_flow(self):
|
|
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)]})
|
|
|
|
action_id = self.env.ref('stock_barcode.stock_barcode_action_main_menu')
|
|
url = "/web#action=" + str(action_id.id)
|
|
|
|
self.start_tour(url, 'test_reload_flow', login='admin', timeout=180)
|
|
|
|
move_line1 = self.env['stock.move.line'].search_count([
|
|
('product_id', '=', self.product1.id),
|
|
('location_dest_id', '=', self.shelf1.id),
|
|
('location_id', '=', self.supplier_location.id),
|
|
('qty_done', '=', 2),
|
|
])
|
|
move_line2 = self.env['stock.move.line'].search_count([
|
|
('product_id', '=', self.product2.id),
|
|
('location_dest_id', '=', self.shelf1.id),
|
|
('location_id', '=', self.supplier_location.id),
|
|
('qty_done', '=', 1),
|
|
])
|
|
self.assertEqual(move_line1, 1)
|
|
self.assertEqual(move_line2, 1)
|
|
|
|
def test_duplicate_serial_number(self):
|
|
""" Simulate a receipt and a delivery with a product tracked by serial
|
|
number. It will try to break the ClientAction by using twice the same
|
|
serial number.
|
|
"""
|
|
self.clean_access_rights()
|
|
grp_lot = self.env.ref('stock.group_production_lot')
|
|
grp_multi_loc = self.env.ref('stock.group_stock_multi_locations')
|
|
self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]})
|
|
self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]})
|
|
|
|
action_id = self.env.ref('stock_barcode.stock_barcode_action_main_menu')
|
|
url = "/web#action=" + str(action_id.id)
|
|
|
|
self.start_tour(url, 'test_receipt_duplicate_serial_number', login='admin', timeout=180)
|
|
|
|
self.start_tour(url, 'test_delivery_duplicate_serial_number', login='admin', timeout=180)
|
|
|
|
def test_bypass_source_scan(self):
|
|
""" Scan a lot, package, product without source location scan. """
|
|
self.clean_access_rights()
|
|
grp_multi_loc = self.env.ref('stock.group_stock_multi_locations')
|
|
grp_pack = self.env.ref('stock.group_tracking_lot')
|
|
grp_lot = self.env.ref('stock.group_production_lot')
|
|
self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]})
|
|
self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]})
|
|
self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]})
|
|
# For the purpose of this test, disable the source scan (mandatory for a deliery otherwise).
|
|
self.picking_type_out.restrict_scan_source_location = 'no'
|
|
|
|
lot1 = self.env['stock.lot'].create({'name': 'lot1', 'product_id': self.productlot1.id, 'company_id': self.env.company.id})
|
|
lot2 = self.env['stock.lot'].create({'name': 'serial1', 'product_id': self.productserial1.id, 'company_id': self.env.company.id})
|
|
|
|
pack1 = self.env['stock.quant.package'].create({
|
|
'name': 'THEPACK',
|
|
})
|
|
|
|
self.env['stock.quant']._update_available_quantity(self.productlot1, self.shelf1, 2, lot_id=lot1)
|
|
self.env['stock.quant']._update_available_quantity(self.productserial1, self.shelf2, 1, lot_id=lot2)
|
|
self.env['stock.quant']._update_available_quantity(self.product1, self.shelf2, 4, package_id=pack1)
|
|
|
|
delivery_picking = self.env['stock.picking'].create({
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'picking_type_id': self.picking_type_out.id,
|
|
})
|
|
url = self._get_client_action_url(delivery_picking.id)
|
|
|
|
self.env['stock.move'].create({
|
|
'name': 'test_bypass_source_scan_1_1',
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'product_id': self.productserial1.id,
|
|
'product_uom': self.uom_unit.id,
|
|
'product_uom_qty': 1,
|
|
'picking_id': delivery_picking.id,
|
|
})
|
|
self.env['stock.move'].create({
|
|
'name': 'test_bypass_source_scan_1_2',
|
|
'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': 2,
|
|
'picking_id': delivery_picking.id,
|
|
})
|
|
self.env['stock.move'].create({
|
|
'name': 'test_bypass_source_scan_1_3',
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'product_id': self.product1.id,
|
|
'product_uom': self.uom_unit.id,
|
|
'product_uom_qty': 4,
|
|
'picking_id': delivery_picking.id,
|
|
})
|
|
delivery_picking.action_confirm()
|
|
delivery_picking.action_assign()
|
|
|
|
self.start_tour(url, 'test_bypass_source_scan', login='admin', timeout=180)
|
|
|
|
def test_put_in_pack_from_different_location(self):
|
|
""" Scans two different products from two different locations, then put them in pack and
|
|
scans a destination location. Checks the package is in the right location.
|
|
"""
|
|
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_internal.active = True
|
|
self.picking_type_internal.restrict_scan_source_location = 'no'
|
|
self.picking_type_internal.restrict_scan_dest_location = 'optional'
|
|
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_from_different_location',
|
|
'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,
|
|
})
|
|
self.env['stock.move'].create({
|
|
'name': 'test_put_in_pack_from_different_location2',
|
|
'location_id': self.shelf3.id,
|
|
'location_dest_id': self.shelf2.id,
|
|
'product_id': self.product2.id,
|
|
'product_uom': self.uom_unit.id,
|
|
'product_uom_qty': 1,
|
|
'picking_id': internal_picking.id,
|
|
})
|
|
|
|
url = self._get_client_action_url(internal_picking.id)
|
|
internal_picking.action_confirm()
|
|
internal_picking.action_assign()
|
|
|
|
self.start_tour(url, 'test_put_in_pack_from_different_location', login='admin', timeout=180)
|
|
pack = self.env['stock.quant.package'].search([])[-1]
|
|
self.assertEqual(len(pack.quant_ids), 2)
|
|
self.assertEqual(pack.location_id, self.shelf2)
|
|
|
|
def test_put_in_pack_before_dest(self):
|
|
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_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,
|
|
})
|
|
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_picking.id,
|
|
})
|
|
|
|
url = self._get_client_action_url(internal_picking.id)
|
|
internal_picking.action_confirm()
|
|
internal_picking.action_assign()
|
|
|
|
self.start_tour(url, 'test_put_in_pack_before_dest', login='admin', timeout=180)
|
|
pack = self.env['stock.quant.package'].search([])[-1]
|
|
self.assertEqual(len(pack.quant_ids), 2)
|
|
self.assertEqual(pack.location_id, self.shelf2)
|
|
|
|
def test_put_in_pack_scan_package(self):
|
|
""" Put in pack a product line, then scan the newly created package to
|
|
assign it to another 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.env['stock.quant']._update_available_quantity(self.product1, 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.shelf1, 1)
|
|
|
|
# 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
|
|
|
|
# Creates a delivery with three move lines: two 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 = 2
|
|
with delivery_form.move_ids_without_package.new() as move:
|
|
move.product_id = self.product2
|
|
move.product_uom_qty = 1
|
|
|
|
delivery = delivery_form.save()
|
|
delivery.action_confirm()
|
|
delivery.action_assign()
|
|
|
|
url = self._get_client_action_url(delivery.id)
|
|
self.start_tour(url, 'test_put_in_pack_scan_package', login='admin', timeout=180)
|
|
|
|
self.assertEqual(delivery.state, 'done')
|
|
self.assertEqual(len(delivery.move_line_ids), 3)
|
|
for move_line in delivery.move_line_ids:
|
|
self.assertEqual(move_line.result_package_id.name, 'PACK0000001')
|
|
|
|
def test_highlight_packs(self):
|
|
self.clean_access_rights()
|
|
grp_pack = self.env.ref('stock.group_tracking_lot')
|
|
self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]})
|
|
|
|
pack1 = self.env['stock.quant.package'].create({
|
|
'name': 'PACK001',
|
|
})
|
|
pack2 = self.env['stock.quant.package'].create({
|
|
'name': 'PACK002',
|
|
})
|
|
|
|
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 4, package_id=pack1)
|
|
self.env['stock.quant']._update_available_quantity(self.product2, self.stock_location, 4, package_id=pack1)
|
|
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 2, package_id=pack2)
|
|
self.env['stock.quant']._update_available_quantity(self.product2, self.stock_location, 2, package_id=pack2)
|
|
|
|
out_picking = self.env['stock.picking'].create({
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'picking_type_id': self.picking_type_out.id,
|
|
})
|
|
|
|
self.picking_type_out.show_entire_packs = True
|
|
|
|
self.env['stock.package_level'].create({
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'package_id': pack1.id,
|
|
'is_done': False,
|
|
'picking_id': out_picking.id,
|
|
'company_id': self.env.company.id,
|
|
})
|
|
|
|
url = self._get_client_action_url(out_picking.id)
|
|
out_picking.action_confirm()
|
|
out_picking.action_assign()
|
|
|
|
self.start_tour(url, 'test_highlight_packs', login='admin', timeout=180)
|
|
|
|
def test_picking_owner_scan_package(self):
|
|
grp_owner = self.env.ref('stock.group_tracking_owner')
|
|
grp_pack = self.env.ref('stock.group_tracking_lot')
|
|
self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]})
|
|
self.env.user.write({'groups_id': [(4, grp_owner.id, 0)]})
|
|
|
|
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 7, package_id=self.package, owner_id=self.owner)
|
|
action_id = self.env.ref('stock_barcode.stock_barcode_action_main_menu')
|
|
url = "/web#action=" + str(action_id.id)
|
|
|
|
self.start_tour(url, 'test_picking_owner_scan_package', login='admin', timeout=180)
|
|
|
|
move_line = self.env['stock.move.line'].search([('product_id', '=', self.product1.id)], limit=1)
|
|
self.assertTrue(move_line)
|
|
|
|
line_owner = move_line.owner_id
|
|
self.assertEqual(line_owner.id, self.owner.id)
|
|
|
|
def test_picking_type_mandatory_scan_settings(self):
|
|
''' Makes some operations with different scan's settings.'''
|
|
self.clean_access_rights()
|
|
|
|
# Enables packages and multi-locations.
|
|
grp_multi_loc = self.env.ref('stock.group_stock_multi_locations')
|
|
grp_lot = self.env.ref('stock.group_production_lot')
|
|
self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0), (4, grp_lot.id, 0)]})
|
|
# Creates a product without barcode to check it can always be processed regardless the config.
|
|
product_without_barcode = self.env['product.product'].create({
|
|
'name': 'Barcodeless Product',
|
|
'type': 'product',
|
|
'categ_id': self.env.ref('product.product_category_all').id,
|
|
})
|
|
# Adds products' quantities.
|
|
self.env['stock.quant']._update_available_quantity(self.product1, self.shelf1, 8)
|
|
self.env['stock.quant']._update_available_quantity(product_without_barcode, self.shelf1, 8)
|
|
|
|
# First config: products must be scanned, empty picking can't be immediatly validated,
|
|
# locations can't be scanned, no put in pack.
|
|
self.picking_type_internal.barcode_validation_after_dest_location = False
|
|
self.picking_type_internal.barcode_validation_all_product_packed = False
|
|
self.picking_type_internal.barcode_validation_full = False
|
|
self.picking_type_internal.restrict_scan_product = True
|
|
self.picking_type_internal.restrict_put_in_pack = 'optional'
|
|
self.picking_type_internal.restrict_scan_source_location = 'no'
|
|
self.picking_type_internal.restrict_scan_dest_location = 'no'
|
|
|
|
# Creates an internal transfer, from WH/Stock/Shelf 1 to WH/Stock.
|
|
picking_form = Form(self.env['stock.picking'])
|
|
picking_form.picking_type_id = self.picking_type_internal
|
|
with picking_form.move_ids_without_package.new() as move:
|
|
move.product_id = self.product1
|
|
move.product_uom_qty = 4
|
|
with picking_form.move_ids_without_package.new() as move:
|
|
move.product_id = product_without_barcode
|
|
move.product_uom_qty = 4
|
|
|
|
picking_internal_1 = picking_form.save()
|
|
picking_internal_1.action_confirm()
|
|
picking_internal_1.action_assign()
|
|
|
|
url = self._get_client_action_url(picking_internal_1.id)
|
|
self.start_tour(url, 'test_picking_type_mandatory_scan_settings_pick_int_1', login='admin', timeout=180)
|
|
self.assertEqual(picking_internal_1.state, 'done')
|
|
|
|
# Second picking: change the config (same than before but locations MUST be scanned).
|
|
self.picking_type_internal.restrict_scan_source_location = 'mandatory'
|
|
self.picking_type_internal.restrict_scan_dest_location = 'mandatory'
|
|
# Creates an internal transfer, from WH/Stock/Shelf 1 to WH/Stock.
|
|
picking_form = Form(self.env['stock.picking'])
|
|
picking_form.picking_type_id = self.picking_type_internal
|
|
with picking_form.move_ids_without_package.new() as move:
|
|
move.product_id = self.product1
|
|
move.product_uom_qty = 4
|
|
with picking_form.move_ids_without_package.new() as move:
|
|
move.product_id = product_without_barcode
|
|
move.product_uom_qty = 4
|
|
|
|
picking_internal_2 = picking_form.save()
|
|
picking_internal_2.action_confirm()
|
|
picking_internal_2.action_assign()
|
|
|
|
url = self._get_client_action_url(picking_internal_2.id)
|
|
self.start_tour(url, 'test_picking_type_mandatory_scan_settings_pick_int_2', login='admin', timeout=180)
|
|
self.assertEqual(picking_internal_2.state, 'done')
|
|
|
|
def test_picking_type_mandatory_scan_complete_flux(self):
|
|
""" From the receipt to the delivery, make a complete flux with each
|
|
picking types having their own barcode's settings:
|
|
- Starts by receive multiple products (some of them are tracked);
|
|
- Stores each product in a different location;
|
|
- Makes a picking operation;
|
|
- Then makes a packing operation and put all products in pack;
|
|
- And finally, does the delivery.
|
|
"""
|
|
def create_picking(picking_type):
|
|
picking_form = Form(self.env['stock.picking'])
|
|
picking_form.picking_type_id = picking_type
|
|
with picking_form.move_ids_without_package.new() as move:
|
|
move.product_id = self.product1
|
|
move.product_uom_qty = 2
|
|
with picking_form.move_ids_without_package.new() as move:
|
|
move.product_id = self.product2
|
|
move.product_uom_qty = 1
|
|
with picking_form.move_ids_without_package.new() as move:
|
|
move.product_id = product_without_barcode
|
|
move.product_uom_qty = 1
|
|
with picking_form.move_ids_without_package.new() as move:
|
|
move.product_id = self.productserial1
|
|
move.product_uom_qty = 3
|
|
with picking_form.move_ids_without_package.new() as move:
|
|
move.product_id = self.productlot1
|
|
move.product_uom_qty = 6
|
|
return picking_form.save()
|
|
|
|
self.clean_access_rights()
|
|
# Creates a product without barcode to check it can always be processed regardless the config.
|
|
product_without_barcode = self.env['product.product'].create({
|
|
'name': 'Barcodeless Product',
|
|
'type': 'product',
|
|
'categ_id': self.env.ref('product.product_category_all').id,
|
|
})
|
|
|
|
# Enables packages, multi-locations and multiple steps routes.
|
|
self.env.user.write({'groups_id': [(4, self.env.ref('stock.group_production_lot').id, 0)]})
|
|
self.env.user.write({'groups_id': [(4, self.env.ref('stock.group_tracking_lot').id, 0)]})
|
|
self.env.user.write({'groups_id': [(4, self.env.ref('stock.group_stock_multi_locations').id, 0)]})
|
|
self.env.user.write({'groups_id': [(4, self.env.ref('stock.group_adv_location').id, 0)]})
|
|
warehouse = self.env.ref('stock.warehouse0')
|
|
warehouse.reception_steps = 'two_steps'
|
|
warehouse.delivery_steps = 'pick_pack_ship'
|
|
|
|
# Creates two cluster packs.
|
|
self.env['stock.quant.package'].create({
|
|
'name': 'cluster-pack-01',
|
|
'package_use': 'reusable',
|
|
})
|
|
self.env['stock.quant.package'].create({
|
|
'name': 'cluster-pack-02',
|
|
'package_use': 'reusable',
|
|
})
|
|
# 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
|
|
|
|
# Configures the picking type's scan settings.
|
|
# Receipt: no put in pack, can not be directly validate.
|
|
self.picking_type_in.barcode_validation_full = False
|
|
self.picking_type_in.restrict_put_in_pack = 'no'
|
|
# Storage (internal transfer): no put in pack, scan dest. after each product.
|
|
self.picking_type_internal.barcode_validation_full = False
|
|
self.picking_type_internal.restrict_put_in_pack = 'no'
|
|
self.picking_type_internal.restrict_scan_dest_location = 'mandatory'
|
|
# Pick: source mandatory, lots reserved only.
|
|
warehouse.pick_type_id.barcode_validation_full = False
|
|
warehouse.pick_type_id.restrict_scan_source_location = 'mandatory'
|
|
warehouse.pick_type_id.restrict_put_in_pack = 'mandatory' # Will use cluster packs.
|
|
warehouse.pick_type_id.restrict_scan_tracking_number = 'mandatory'
|
|
warehouse.pick_type_id.restrict_scan_dest_location = 'no'
|
|
# Pack: pack after group, all products have to be packed to be validate.
|
|
warehouse.pack_type_id.restrict_put_in_pack = 'optional'
|
|
warehouse.pack_type_id.restrict_scan_tracking_number = 'mandatory'
|
|
warehouse.pack_type_id.barcode_validation_all_product_packed = True
|
|
warehouse.pick_type_id.restrict_scan_dest_location = 'no'
|
|
# Delivery: pack after group, all products have to be packed to be validate.
|
|
self.picking_type_out.restrict_put_in_pack = 'optional'
|
|
self.picking_type_out.restrict_scan_tracking_number = 'mandatory'
|
|
self.picking_type_out.barcode_validation_all_product_packed = True
|
|
self.picking_type_out.show_entire_packs = True
|
|
|
|
# Creates and assigns the receipt.
|
|
picking_receipt = create_picking(self.picking_type_in)
|
|
picking_receipt.action_confirm()
|
|
picking_receipt.action_assign()
|
|
# Get the storage operation (automatically created by the receipt).
|
|
picking_internal = picking_receipt.move_ids.move_dest_ids.picking_id
|
|
|
|
# Creates the pick, pack, ship.
|
|
picking_pick = create_picking(warehouse.pick_type_id)
|
|
picking_pack = create_picking(warehouse.pack_type_id)
|
|
picking_delivery = create_picking(self.picking_type_out)
|
|
picking_pack.location_dest_id = picking_delivery.location_id
|
|
|
|
# Process each picking one by one.
|
|
url = self._get_client_action_url(picking_receipt.id)
|
|
self.start_tour(url, 'test_picking_type_mandatory_scan_complete_flux_receipt', login='admin', timeout=180)
|
|
self.assertEqual(picking_receipt.state, 'done')
|
|
|
|
url = self._get_client_action_url(picking_internal.id)
|
|
self.start_tour(url, 'test_picking_type_mandatory_scan_complete_flux_internal', login='admin', timeout=180)
|
|
self.assertEqual(picking_internal.state, 'done')
|
|
|
|
picking_pick.action_confirm()
|
|
picking_pick.action_assign()
|
|
url = self._get_client_action_url(picking_pick.id)
|
|
self.start_tour(url, 'test_picking_type_mandatory_scan_complete_flux_pick', login='admin', timeout=180)
|
|
self.assertEqual(picking_pick.state, 'done')
|
|
|
|
picking_pack.action_confirm()
|
|
picking_pack.action_assign()
|
|
for move_line in picking_pack.move_line_ids: # TODO: shouldn't have to do that, reusable packages shouldn't be set in `result_package_id` for the next move.
|
|
move_line.result_package_id = False
|
|
url = self._get_client_action_url(picking_pack.id)
|
|
self.start_tour(url, 'test_picking_type_mandatory_scan_complete_flux_pack', login='admin', timeout=180)
|
|
self.assertEqual(picking_pack.state, 'done')
|
|
|
|
picking_delivery.action_confirm()
|
|
picking_delivery.action_assign()
|
|
url = self._get_client_action_url(picking_delivery.id)
|
|
self.start_tour(url, 'test_picking_type_mandatory_scan_complete_flux_delivery', login='admin', timeout=180)
|
|
self.assertEqual(picking_delivery.state, 'done')
|
|
|
|
def test_receipt_delete_button(self):
|
|
""" Scan products that not part of a receipt. Check that products not part of original receipt
|
|
can be deleted, but the products that are part of the original receipt cannot be deleted.
|
|
"""
|
|
self.clean_access_rights()
|
|
receipt_picking = self.env['stock.picking'].create({
|
|
'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({
|
|
'name': 'test_receipt_1',
|
|
'location_id': self.supplier_location.id,
|
|
'location_dest_id': self.stock_location.id,
|
|
'product_id': self.product1.id,
|
|
'product_uom': self.product1.uom_id.id,
|
|
'product_uom_qty': 1,
|
|
'picking_id': receipt_picking.id,
|
|
'picking_type_id': self.picking_type_in.id,
|
|
})
|
|
# extra product to test that deleting works
|
|
self.env['product.product'].create({
|
|
'name': 'product3',
|
|
'type': 'product',
|
|
'categ_id': self.env.ref('product.product_category_all').id,
|
|
'barcode': 'product3',
|
|
})
|
|
|
|
url = self._get_client_action_url(receipt_picking.id)
|
|
receipt_picking.action_confirm()
|
|
self.start_tour(url, 'test_receipt_delete_button', login='admin', timeout=180)
|
|
self.assertEqual(len(receipt_picking.move_line_ids), 2, "2 lines expected: product1 + product2")
|
|
|
|
def test_show_entire_package(self):
|
|
""" Enables 'Move Entire Packages' for delivery and then creates two deliveries:
|
|
- One where we use package level;
|
|
- One where we use move without package.
|
|
Then, checks it's the right type of line who is shown in the Barcode App."""
|
|
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
|
|
package1 = self.env['stock.quant.package'].create({'name': 'package001'})
|
|
package2 = self.env['stock.quant.package'].create({'name': 'package002'})
|
|
|
|
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 4, package_id=package1)
|
|
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 4, package_id=package2)
|
|
|
|
delivery_with_package_level = self.env['stock.picking'].create({
|
|
'name': "Delivery with Package Level",
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'picking_type_id': self.picking_type_out.id,
|
|
})
|
|
self.env['stock.package_level'].create({
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'package_id': package1.id,
|
|
'is_done': False,
|
|
'picking_id': delivery_with_package_level.id,
|
|
'company_id': self.env.company.id,
|
|
})
|
|
delivery_with_package_level.action_confirm()
|
|
delivery_with_package_level.action_assign()
|
|
|
|
delivery_with_move = self.env['stock.picking'].create({
|
|
'name': "Delivery with Stock Move",
|
|
'location_id': self.stock_location.id,
|
|
'location_dest_id': self.customer_location.id,
|
|
'picking_type_id': self.picking_type_out.id,
|
|
})
|
|
self.env['stock.move'].create({
|
|
'name': 'test_show_entire_package',
|
|
'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': delivery_with_move.id,
|
|
})
|
|
delivery_with_move.action_confirm()
|
|
delivery_with_move.action_assign()
|
|
|
|
action = self.env["ir.actions.actions"]._for_xml_id("stock_barcode.stock_barcode_action_main_menu")
|
|
url = '/web#action=%s' % action['id']
|
|
delivery_with_package_level.action_confirm()
|
|
delivery_with_package_level.action_assign()
|
|
|
|
self.assertFalse(delivery_with_package_level.package_level_ids.is_done)
|
|
self.start_tour(url, 'test_show_entire_package', login='admin', timeout=180)
|
|
self.assertTrue(delivery_with_package_level.package_level_ids.is_done)
|
|
|
|
def test_define_the_destination_package(self):
|
|
"""
|
|
Suppose a picking that moves a product from a package to another one
|
|
This test ensures that the user can scans the destination package
|
|
"""
|
|
self.clean_access_rights()
|
|
group_pack = self.env.ref('stock.group_tracking_lot')
|
|
self.env.user.write({'groups_id': [(4, group_pack.id, 0)]})
|
|
|
|
pack01, pack02 = self.env['stock.quant.package'].create([{
|
|
'name': name,
|
|
} for name in ('PACK01', 'PACK02')])
|
|
|
|
self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 2, package_id=pack01)
|
|
|
|
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
|
|
delivery = picking_form.save()
|
|
delivery.action_confirm()
|
|
|
|
url = self._get_client_action_url(delivery.id)
|
|
self.start_tour(url, 'test_define_the_destination_package', login='admin', timeout=180)
|
|
|
|
self.assertRecordValues(delivery.move_line_ids, [
|
|
{'product_id': self.product1.id, 'qty_done': 1, 'result_package_id': pack02.id, 'state': 'done'},
|
|
])
|
|
|
|
def test_avoid_useless_line_creation(self):
|
|
"""
|
|
Suppose
|
|
- the option "Create New Lots/Serial Numbers" disabled
|
|
- a tracked product P with an available lot L
|
|
On a delivery, a user scans L (it should add a line)
|
|
Then, the user scans a non-existing lot LX (it should not create any line)
|
|
"""
|
|
self.clean_access_rights()
|
|
group_lot = self.env.ref('stock.group_production_lot')
|
|
self.env.user.write({'groups_id': [(4, group_lot.id, 0)]})
|
|
|
|
lot01 = self.env['stock.lot'].create({
|
|
'name': "LOT01",
|
|
'product_id': self.productlot1.id,
|
|
'company_id': self.env.company.id,
|
|
})
|
|
|
|
self.env['stock.quant']._update_available_quantity(self.productlot1, self.stock_location, 1, lot_id=lot01)
|
|
|
|
picking_form = Form(self.env['stock.picking'].with_context(default_immediate_transfer=True))
|
|
picking_form.picking_type_id = self.picking_type_out
|
|
delivery = picking_form.save()
|
|
|
|
url = self._get_client_action_url(delivery.id)
|
|
self.start_tour(url, 'test_avoid_useless_line_creation', login='admin', timeout=180)
|
|
|
|
self.assertRecordValues(delivery.move_ids, [
|
|
{'product_id': self.productlot1.id, 'lot_ids': lot01.ids, 'quantity_done': 1},
|
|
])
|
|
|
|
def test_gs1_reserved_delivery(self):
|
|
""" Process a delivery by scanning multiple quantity multiple times.
|
|
"""
|
|
self.clean_access_rights()
|
|
self.env.company.nomenclature_id = self.env.ref('barcodes_gs1_nomenclature.default_gs1_nomenclature')
|
|
|
|
# Creates a product and adds some quantity.
|
|
product_gtin_8 = self.env['product.product'].create({
|
|
'name': 'PRO_GTIN_8',
|
|
'type': 'product',
|
|
'categ_id': self.env.ref('product.product_category_all').id,
|
|
'barcode': '11011019', # GTIN-8 format.
|
|
'uom_id': self.env.ref('uom.product_uom_unit').id,
|
|
})
|
|
self.env['stock.quant']._update_available_quantity(product_gtin_8, self.stock_location, 99)
|
|
|
|
# Creates and process the delivery.
|
|
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 = product_gtin_8
|
|
move.product_uom_qty = 10
|
|
|
|
delivery = picking_form.save()
|
|
delivery.action_confirm()
|
|
delivery.action_assign()
|
|
|
|
url = self._get_client_action_url(delivery.id)
|
|
self.start_tour(url, 'test_gs1_reserved_delivery', login='admin', timeout=180)
|
|
|
|
self.assertEqual(delivery.state, 'done')
|
|
self.assertEqual(len(delivery.move_ids), 1)
|
|
self.assertEqual(delivery.move_ids.product_qty, 14)
|
|
self.assertEqual(len(delivery.move_line_ids), 2)
|
|
self.assertEqual(delivery.move_line_ids[0].qty_done, 10)
|
|
self.assertEqual(delivery.move_line_ids[1].qty_done, 4)
|
|
|
|
def test_gs1_receipt_conflicting_barcodes(self):
|
|
""" Creates some receipts for two products but their barcodes mingle
|
|
together once they are adapted for GS1.
|
|
"""
|
|
self.clean_access_rights()
|
|
self.env.company.nomenclature_id = self.env.ref('barcodes_gs1_nomenclature.default_gs1_nomenclature')
|
|
|
|
product_gtin_8 = self.env['product.product'].create({
|
|
'name': 'PRO_GTIN_8',
|
|
'type': 'product',
|
|
'categ_id': self.env.ref('product.product_category_all').id,
|
|
'barcode': '11011019', # GTIN-8 format -> Will become 00000011011019.
|
|
'uom_id': self.env.ref('uom.product_uom_unit').id,
|
|
})
|
|
|
|
product_gtin_12 = self.env['product.product'].create({
|
|
'name': 'PRO_GTIN_12',
|
|
'type': 'product',
|
|
'categ_id': self.env.ref('product.product_category_all').id,
|
|
'barcode': '000011011019', # GTIN-12 format -> Will also become 00000011011019.
|
|
'uom_id': self.env.ref('uom.product_uom_unit').id,
|
|
})
|
|
|
|
# Test for product_gtin_8 only.
|
|
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 = product_gtin_8
|
|
move.product_uom_qty = 1
|
|
|
|
receipt_1 = picking_form.save()
|
|
receipt_1.action_confirm()
|
|
receipt_1.action_assign()
|
|
|
|
url = self._get_client_action_url(receipt_1.id)
|
|
self.start_tour(url, 'test_gs1_receipt_conflicting_barcodes_1', login='admin', timeout=180)
|
|
|
|
self.assertEqual(receipt_1.state, 'done')
|
|
self.assertEqual(len(receipt_1.move_line_ids), 1)
|
|
self.assertEqual(receipt_1.move_line_ids.product_id.id, product_gtin_8.id)
|
|
|
|
# Test for product_gtin_12 only.
|
|
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 = product_gtin_12
|
|
move.product_uom_qty = 1
|
|
|
|
receipt_2 = picking_form.save()
|
|
receipt_2.action_confirm()
|
|
receipt_2.action_assign()
|
|
|
|
url = self._get_client_action_url(receipt_2.id)
|
|
self.start_tour(url, 'test_gs1_receipt_conflicting_barcodes_2', login='admin', timeout=180)
|
|
|
|
self.assertEqual(receipt_2.state, 'done')
|
|
self.assertEqual(len(receipt_2.move_line_ids), 1)
|
|
self.assertEqual(receipt_2.move_line_ids.product_id.id, product_gtin_12.id)
|
|
|
|
# Test for both product_gtin_8 and product_gtin_12.
|
|
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 = product_gtin_8
|
|
move.product_uom_qty = 1
|
|
with picking_form.move_ids_without_package.new() as move:
|
|
move.product_id = product_gtin_12
|
|
move.product_uom_qty = 1
|
|
|
|
receipt_3 = picking_form.save()
|
|
receipt_3.action_confirm()
|
|
receipt_3.action_assign()
|
|
|
|
self.assertEqual(len(receipt_3.move_line_ids), 2)
|
|
url = self._get_client_action_url(receipt_3.id)
|
|
self.start_tour(url, 'test_gs1_receipt_conflicting_barcodes_3', login='admin', timeout=180)
|
|
|
|
self.assertEqual(receipt_3.state, 'done')
|
|
self.assertEqual(len(receipt_3.move_line_ids), 3)
|
|
self.assertEqual(receipt_3.move_line_ids[0].product_id.id, product_gtin_8.id)
|
|
self.assertEqual(receipt_3.move_line_ids[0].qty_done, 1)
|
|
self.assertEqual(receipt_3.move_line_ids[1].product_id.id, product_gtin_12.id)
|
|
self.assertEqual(receipt_3.move_line_ids[1].qty_done, 1)
|
|
self.assertEqual(receipt_3.move_line_ids[2].product_id.id, product_gtin_8.id)
|
|
self.assertEqual(receipt_3.move_line_ids[2].qty_done, 1)
|
|
|
|
def test_gs1_receipt_lot_serial(self):
|
|
""" Creates a receipt for a product tracked by lot, then process it in the Barcode App.
|
|
"""
|
|
self.clean_access_rights()
|
|
self.env.company.nomenclature_id = self.env.ref('barcodes_gs1_nomenclature.default_gs1_nomenclature')
|
|
|
|
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.product_tln_gtn8
|
|
move.product_uom_qty = 40
|
|
|
|
receipt = picking_form.save()
|
|
receipt.action_confirm()
|
|
receipt.action_assign()
|
|
|
|
url = self._get_client_action_url(receipt.id)
|
|
self.start_tour(url, 'test_gs1_receipt_lot_serial', login='admin', timeout=180)
|
|
|
|
self.assertEqual(receipt.state, 'done')
|
|
self.assertEqual(len(receipt.move_line_ids), 5)
|
|
self.assertEqual(
|
|
receipt.move_line_ids.lot_id.mapped('name'),
|
|
['b1-b001', 'b1-b002', 'b1-b003', 'b1-b004', 'b1-b005']
|
|
)
|
|
for move_line in receipt.move_line_ids:
|
|
self.assertEqual(move_line.qty_done, 8)
|
|
|
|
def test_gs1_receipt_quantity_with_uom(self):
|
|
""" Creates a new receipt and scans barcodes with different combinaisons
|
|
of product and quantity expressed with different UoM and checks the
|
|
quantity is taken only if the UoM is compatible with the product's one.
|
|
"""
|
|
self.clean_access_rights()
|
|
# Enables the UoM and the GS1 nomenclature.
|
|
grp_uom = self.env.ref('uom.group_uom')
|
|
group_user = self.env.ref('base.group_user')
|
|
group_user.write({'implied_ids': [(4, grp_uom.id)]})
|
|
self.env.user.write({'groups_id': [(4, grp_uom.id)]})
|
|
self.env.company.nomenclature_id = self.env.ref('barcodes_gs1_nomenclature.default_gs1_nomenclature')
|
|
# Configures three products using units, kg and g.
|
|
uom_unit = self.env.ref('product.product_category_all')
|
|
uom_g = self.env.ref('uom.product_uom_gram')
|
|
uom_kg = self.env.ref('uom.product_uom_kgm')
|
|
product_by_units = self.env['product.product'].create({
|
|
'name': 'Product by Units',
|
|
'type': 'product',
|
|
'categ_id': self.env.ref('product.product_category_all').id,
|
|
'barcode': '15264329',
|
|
'uom_id': uom_unit.id,
|
|
})
|
|
product_by_kg = self.env['product.product'].create({
|
|
'name': 'Product by g',
|
|
'type': 'product',
|
|
'categ_id': self.env.ref('product.product_category_all').id,
|
|
'barcode': '15264893',
|
|
'uom_id': uom_g.id,
|
|
'uom_po_id': uom_g.id,
|
|
})
|
|
product_by_g = self.env['product.product'].create({
|
|
'name': 'Product by kg',
|
|
'type': 'product',
|
|
'categ_id': self.env.ref('product.product_category_all').id,
|
|
'barcode': '15264879',
|
|
'uom_id': uom_kg.id,
|
|
'uom_po_id': uom_kg.id,
|
|
})
|
|
# Creates a new receipt.
|
|
picking_form = Form(self.env['stock.picking'].with_context(default_immediate_transfer=True))
|
|
picking_form.picking_type_id = self.picking_type_in
|
|
receipt = picking_form.save()
|
|
# Runs the tour.
|
|
url = self._get_client_action_url(receipt.id)
|
|
self.start_tour(url, 'test_gs1_receipt_quantity_with_uom', login='admin', timeout=180)
|
|
# Checks the moves' quantities and UoM.
|
|
self.assertTrue(len(receipt.move_ids), 3)
|
|
move1, move2, move3 = receipt.move_ids
|
|
self.assertTrue(move1.product_id.id, product_by_units.id)
|
|
self.assertTrue(move1.quantity_done, 4)
|
|
self.assertTrue(move1.product_uom.id, uom_unit.id)
|
|
self.assertTrue(move2.product_id.id, product_by_kg.id)
|
|
self.assertTrue(move2.quantity_done, 5)
|
|
self.assertTrue(move2.product_uom.id, uom_kg.id)
|
|
self.assertTrue(move3.product_id.id, product_by_g.id)
|
|
self.assertTrue(move3.quantity_done, 1250)
|
|
self.assertTrue(move3.product_uom.id, uom_g.id)
|
|
|
|
def test_gs1_package_receipt_and_delivery(self):
|
|
""" Receives some products and scans a GS1 barcode for a package, then
|
|
creates a delivery and scans the same package.
|
|
"""
|
|
self.clean_access_rights()
|
|
self.env.company.nomenclature_id = self.env.ref('barcodes_gs1_nomenclature.default_gs1_nomenclature')
|
|
grp_pack = self.env.ref('stock.group_tracking_lot')
|
|
self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]})
|
|
|
|
# Set package's sequence to 123 to generate always the same package's name in the tour.
|
|
sequence = self.env['ir.sequence'].search([('code', '=', 'stock.quant.package')], limit=1)
|
|
sequence.write({'number_next_actual': 123})
|
|
|
|
# Creates two products and two package's types.
|
|
product1 = self.env['product.product'].create({
|
|
'name': 'PRO_GTIN_8',
|
|
'type': 'product',
|
|
'categ_id': self.env.ref('product.product_category_all').id,
|
|
'barcode': '82655853', # GTIN-8
|
|
'uom_id': self.env.ref('uom.product_uom_unit').id
|
|
})
|
|
product2 = self.env['product.product'].create({
|
|
'name': 'PRO_GTIN_12',
|
|
'type': 'product',
|
|
'categ_id': self.env.ref('product.product_category_all').id,
|
|
'barcode': '584687955629', # GTIN-12
|
|
'uom_id': self.env.ref('uom.product_uom_unit').id,
|
|
})
|
|
wooden_chest_package_type = self.env['stock.package.type'].create({
|
|
'name': 'Wooden Chest',
|
|
'barcode': 'WOODC',
|
|
})
|
|
iron_chest_package_type = self.env['stock.package.type'].create({
|
|
'name': 'Iron Chest',
|
|
'barcode': 'IRONC',
|
|
})
|
|
|
|
action_id = self.env.ref('stock_barcode.stock_barcode_action_main_menu')
|
|
url = "/web#action=" + str(action_id.id)
|
|
|
|
self.start_tour(url, 'test_gs1_package_receipt', login='admin', timeout=180)
|
|
# Checks the package is in the stock location with the products.
|
|
package = self.env['stock.quant.package'].search([('name', '=', '546879213579461324')])
|
|
package2 = self.env['stock.quant.package'].search([('name', '=', '130406658041178543')])
|
|
package3 = self.env['stock.quant.package'].search([('name', '=', 'PACK0000123')])
|
|
self.assertEqual(len(package), 1)
|
|
self.assertEqual(len(package.quant_ids), 2)
|
|
self.assertEqual(package.package_type_id.id, wooden_chest_package_type.id)
|
|
self.assertEqual(package.quant_ids[0].product_id.id, product1.id)
|
|
self.assertEqual(package.quant_ids[1].product_id.id, product2.id)
|
|
self.assertEqual(package.location_id.id, self.stock_location.id)
|
|
self.assertEqual(package2.package_type_id.id, iron_chest_package_type.id)
|
|
self.assertEqual(package2.quant_ids.product_id.id, product1.id)
|
|
self.assertEqual(package3.package_type_id.id, iron_chest_package_type.id)
|
|
self.assertEqual(package3.quant_ids.product_id.id, product2.id)
|
|
|
|
self.start_tour(url, 'test_gs1_package_delivery', login='admin', timeout=180)
|
|
# Checks the package is in the customer's location.
|
|
self.assertEqual(package.location_id.id, self.customer_location.id)
|
|
|
|
def test_gs1_and_packaging(self):
|
|
"""
|
|
This test ensures that a user can scan a packaging when processing a receipt
|
|
"""
|
|
self.clean_access_rights()
|
|
|
|
group_packaging = self.env.ref('product.group_stock_packaging')
|
|
self.env.user.write({'groups_id': [(4, group_packaging.id)]})
|
|
self.env.company.nomenclature_id = self.env.ref('barcodes_gs1_nomenclature.default_gs1_nomenclature')
|
|
|
|
product = self.env['product.product'].create({
|
|
'name': 'Bottle',
|
|
'type': 'product',
|
|
'barcode': '1113',
|
|
'packaging_ids': [(0, 0, {
|
|
'name': '6-bottle pack',
|
|
'qty': 6,
|
|
'barcode': '2226',
|
|
})],
|
|
})
|
|
|
|
picking_form = Form(self.env['stock.picking'].with_context(default_immediate_transfer=True))
|
|
picking_form.picking_type_id = self.picking_type_in
|
|
receipt = picking_form.save()
|
|
|
|
url = self._get_client_action_url(receipt.id)
|
|
self.start_tour(url, 'test_gs1_receipt_packaging', login='admin', timeout=180)
|
|
|
|
move = receipt.move_ids
|
|
self.assertEqual(move.product_id, product)
|
|
self.assertEqual(move.quantity_done, 30)
|