306 lines
12 KiB
Python
306 lines
12 KiB
Python
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
|
from odoo import Command
|
||
|
from odoo.tests import Form, HttpCase, tagged
|
||
|
|
||
|
|
||
|
@tagged('-at_install', 'post_install')
|
||
|
class TestStockPickingTour(HttpCase):
|
||
|
def setUp(self):
|
||
|
self.receipt = self.env['stock.picking'].create({
|
||
|
'picking_type_id': self.env.ref('stock.picking_type_in').id,
|
||
|
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||
|
'location_dest_id': self.env.ref('stock.stock_location_stock').id,
|
||
|
})
|
||
|
|
||
|
return super().setUp()
|
||
|
|
||
|
def _get_picking_url(self, picking_id):
|
||
|
return '/odoo/action-stock.action_picking_tree_incoming/%s' % (picking_id)
|
||
|
|
||
|
def test_detailed_op_no_save_1(self):
|
||
|
"""validate a receipt with some move without any save except the last one"""
|
||
|
product_lot = self.env['product.product'].create({
|
||
|
'name': 'Product Lot',
|
||
|
'is_storable': True,
|
||
|
'tracking': 'lot',
|
||
|
})
|
||
|
url = self._get_picking_url(self.receipt.id)
|
||
|
|
||
|
self.start_tour(url, 'test_detailed_op_no_save_1', login='admin', timeout=60)
|
||
|
self.assertEqual(self.receipt.state, 'done')
|
||
|
self.assertEqual(self.receipt.move_ids.product_uom_qty, 0)
|
||
|
self.assertEqual(self.receipt.move_ids.quantity, 4)
|
||
|
lot = self.env['stock.lot'].search([
|
||
|
('name', '=', 'lot1'),
|
||
|
('product_id', '=', product_lot.id),
|
||
|
])
|
||
|
self.assertEqual(len(lot), 1)
|
||
|
self.assertEqual(lot.product_qty, 4)
|
||
|
|
||
|
def test_generate_serial_1(self):
|
||
|
"""generate some serial numbers in the detailed operation modal"""
|
||
|
product_serial = self.env['product.product'].create({
|
||
|
'name': 'Product Serial',
|
||
|
'is_storable': True,
|
||
|
'tracking': 'serial',
|
||
|
})
|
||
|
url = self._get_picking_url(self.receipt.id)
|
||
|
|
||
|
self.start_tour(url, 'test_generate_serial_1', login='admin', timeout=60)
|
||
|
self.assertEqual(self.receipt.state, 'done')
|
||
|
self.assertEqual(self.receipt.move_ids.product_uom_qty, 0)
|
||
|
self.assertEqual(self.receipt.move_ids.quantity, 5)
|
||
|
self.assertEqual(len(self.receipt.move_ids.move_line_ids), 5)
|
||
|
|
||
|
serial = self.env['stock.lot'].search([
|
||
|
('name', 'ilike', 'serial_n_%'),
|
||
|
('product_id', '=', product_serial.id),
|
||
|
])
|
||
|
self.assertEqual(len(serial), 5)
|
||
|
|
||
|
def test_generate_serial_2(self):
|
||
|
""" Generate lot numbers in the detailed operation modal """
|
||
|
product_lot_1 = self.env['product.product'].create({
|
||
|
'name': 'Product Lot 1',
|
||
|
'is_storable': True,
|
||
|
'tracking': 'lot',
|
||
|
})
|
||
|
url = self._get_picking_url(self.receipt.id)
|
||
|
self.start_tour(url, 'test_generate_serial_2', login='admin', timeout=60)
|
||
|
self.assertEqual(self.receipt.state, 'done')
|
||
|
self.assertEqual(self.receipt.move_ids.product_uom_qty, 100)
|
||
|
self.assertEqual(self.receipt.move_ids.quantity, 100)
|
||
|
self.assertEqual(len(self.receipt.move_ids.move_line_ids), 11)
|
||
|
|
||
|
lots_batch_1 = self.env['stock.lot'].search([
|
||
|
('name', 'ilike', 'lot_n_1_%'),
|
||
|
('product_id', '=', product_lot_1.id)
|
||
|
], order='name asc')
|
||
|
lots_batch_2 = self.env['stock.lot'].search([
|
||
|
('name', 'ilike', 'lot_n_2_%'),
|
||
|
('product_id', '=', product_lot_1.id)
|
||
|
], order='name asc')
|
||
|
self.assertEqual(len(lots_batch_1), 7)
|
||
|
self.assertEqual(lots_batch_1[-1].product_qty, 5)
|
||
|
self.assertEqual(len(lots_batch_2), 4)
|
||
|
self.assertEqual(lots_batch_2[-1].product_qty, 11)
|
||
|
|
||
|
def test_inventory_adjustment_apply_all(self):
|
||
|
"""
|
||
|
Checks if the "Apply All" button works for all new entries, even if
|
||
|
it was pressed immediately after entering the last entry's quantity
|
||
|
(without clicking on anything else)
|
||
|
"""
|
||
|
self.env['product.product'].create([
|
||
|
{'name': 'Product 1', 'is_storable': True},
|
||
|
{'name': 'Product 2', 'is_storable': True},
|
||
|
])
|
||
|
|
||
|
menu = self.env.ref('stock.menu_action_inventory_tree')
|
||
|
url = '/odoo/stock.quant?menu_id=%s' % (menu['id'])
|
||
|
|
||
|
# We need a bigger window, so the "Apply All" button is immediately visible
|
||
|
self.browser_size = '1920,1080'
|
||
|
self.start_tour(url, 'test_inventory_adjustment_apply_all', login='admin', timeout=60)
|
||
|
|
||
|
def test_add_new_line(self):
|
||
|
product_one, _ = self.env["product.product"].create([
|
||
|
{
|
||
|
'name': 'Product one',
|
||
|
'is_storable': True,
|
||
|
'tracking': 'serial',
|
||
|
},
|
||
|
{
|
||
|
'name': 'Product two',
|
||
|
'is_storable': True,
|
||
|
'tracking': 'serial',
|
||
|
}
|
||
|
])
|
||
|
|
||
|
self.receipt.write({
|
||
|
"move_ids": [Command.create({
|
||
|
"name": "test",
|
||
|
"product_uom_qty": 1,
|
||
|
"product_id": product_one.id,
|
||
|
"location_id": self.receipt.location_id.id,
|
||
|
"location_dest_id": self.receipt.location_dest_id.id,
|
||
|
})]
|
||
|
})
|
||
|
|
||
|
self.receipt.action_confirm()
|
||
|
self.receipt.move_ids.move_line_ids.lot_name = "one"
|
||
|
|
||
|
url = self._get_picking_url(self.receipt.id)
|
||
|
self.start_tour(url, 'test_add_new_line', login='admin', timeout=60)
|
||
|
|
||
|
names = self.receipt.move_ids.move_line_ids.mapped('lot_name')
|
||
|
self.assertEqual(names, ["two", "one"])
|
||
|
|
||
|
def test_add_new_line_in_detailled_op(self):
|
||
|
"""
|
||
|
Check that the unsaved quantity/location changes of the detailed operations impact dynamically
|
||
|
the creation of new move lines (considering the real avaible quantity rather than DB data's).
|
||
|
"""
|
||
|
warehouse = self.env.ref("stock.warehouse0")
|
||
|
product_lot = self.env['product.product'].create({
|
||
|
'name': 'Product Lot',
|
||
|
'is_storable': True,
|
||
|
'tracking': 'lot',
|
||
|
})
|
||
|
lot_1, lot_2, lot_3 = self.env['stock.lot'].create([
|
||
|
{'name': 'LOT001', 'product_id': product_lot.id, 'company_id': warehouse.company_id.id},
|
||
|
{'name': 'LOT002', 'product_id': product_lot.id, 'company_id': warehouse.company_id.id},
|
||
|
{'name': 'LOT003', 'product_id': product_lot.id, 'company_id': warehouse.company_id.id},
|
||
|
])
|
||
|
self.env['stock.quant']._update_available_quantity(product_lot, warehouse.lot_stock_id, quantity=10, lot_id=lot_1)
|
||
|
self.env['stock.quant']._update_available_quantity(product_lot, warehouse.lot_stock_id, quantity=15, lot_id=lot_2)
|
||
|
self.env['stock.quant']._update_available_quantity(product_lot, warehouse.lot_stock_id, quantity=10, lot_id=lot_3)
|
||
|
partner = self.env['res.partner'].create({'name': 'Bob'})
|
||
|
delivery = self.picking_in = self.env['stock.picking'].create({
|
||
|
'picking_type_id': warehouse.out_type_id.id,
|
||
|
'partner_id': partner.id,
|
||
|
'location_id': warehouse.lot_stock_id.id,
|
||
|
'location_dest_id': self.ref('stock.stock_location_customers'),
|
||
|
'move_ids': [Command.create({
|
||
|
'name': product_lot.name,
|
||
|
'product_id': product_lot.id,
|
||
|
'location_id': warehouse.lot_stock_id.id,
|
||
|
'location_dest_id': self.ref('stock.stock_location_customers'),
|
||
|
'product_uom_qty': 20,
|
||
|
})]
|
||
|
})
|
||
|
delivery.action_confirm()
|
||
|
self.assertRecordValues(delivery.move_line_ids, [
|
||
|
{'lot_id': lot_1.id, 'quantity': 10.0},
|
||
|
{'lot_id': lot_2.id, 'quantity': 10.0},
|
||
|
])
|
||
|
url = self._get_picking_url(delivery.id)
|
||
|
self.start_tour(url, 'test_add_new_line_in_detailled_op', login='admin', timeout=100)
|
||
|
self.assertRecordValues(delivery.move_line_ids.sorted("quantity"), [
|
||
|
{'quantity': 2.0, 'lot_id': lot_1.id},
|
||
|
{'quantity': 3.0, 'lot_id': lot_1.id},
|
||
|
{'quantity': 15.0, 'lot_id': lot_2.id},
|
||
|
])
|
||
|
|
||
|
def test_edit_existing_line(self):
|
||
|
self.uom_unit = self.env.ref('uom.product_uom_unit')
|
||
|
product_one = self.env['product.product'].create({
|
||
|
'name': 'Product one',
|
||
|
'is_storable': True,
|
||
|
'tracking': 'serial',
|
||
|
})
|
||
|
|
||
|
self.receipt.write({
|
||
|
"move_ids": [Command.create({
|
||
|
"name": "test",
|
||
|
"product_id": product_one.id,
|
||
|
"location_id": self.receipt.location_id.id,
|
||
|
"location_dest_id": self.receipt.location_dest_id.id,
|
||
|
"product_uom_qty": 1,
|
||
|
})]
|
||
|
})
|
||
|
|
||
|
self.receipt.action_confirm()
|
||
|
self.receipt.move_ids.move_line_ids.lot_name = "one"
|
||
|
|
||
|
url = self._get_picking_url(self.receipt.id)
|
||
|
self.start_tour(url, 'test_edit_existing_line', login='admin', timeout=60)
|
||
|
|
||
|
names = self.receipt.move_ids.move_line_ids.mapped('lot_name')
|
||
|
self.assertEqual(names, ["one", "two"])
|
||
|
|
||
|
def test_edit_existing_lines_2(self):
|
||
|
self.uom_unit = self.env.ref('uom.product_uom_unit')
|
||
|
product_a, product_b = self.env["product.product"].create([
|
||
|
{
|
||
|
'name': 'Product a',
|
||
|
'is_storable': True,
|
||
|
'tracking': 'serial',
|
||
|
},
|
||
|
{
|
||
|
'name': 'Product b',
|
||
|
'is_storable': True,
|
||
|
'tracking': 'serial',
|
||
|
}
|
||
|
])
|
||
|
|
||
|
self.receipt.write({
|
||
|
"move_ids": [
|
||
|
Command.create({
|
||
|
"name": "Product a",
|
||
|
"product_id": product_a.id,
|
||
|
"location_id": self.receipt.location_id.id,
|
||
|
"location_dest_id": self.receipt.location_dest_id.id,
|
||
|
"product_uom_qty": 1,
|
||
|
}),
|
||
|
Command.create({
|
||
|
"name": "Product b",
|
||
|
"product_id": product_b.id,
|
||
|
"location_id": self.receipt.location_id.id,
|
||
|
"location_dest_id": self.receipt.location_dest_id.id,
|
||
|
"product_uom_qty": 1,
|
||
|
}),
|
||
|
]
|
||
|
})
|
||
|
|
||
|
self.receipt.action_confirm()
|
||
|
|
||
|
url = self._get_picking_url(self.receipt.id)
|
||
|
self.start_tour(url, 'test_edit_existing_lines_2', login='admin', timeout=60)
|
||
|
|
||
|
names = self.receipt.move_ids.move_line_ids.mapped('lot_name')
|
||
|
self.assertEqual(names, ["SNa001", "SNb001"])
|
||
|
|
||
|
def test_onchange_serial_lot_ids(self):
|
||
|
"""
|
||
|
Checks that onchange behaves correctly with respect to multiple unlinks
|
||
|
"""
|
||
|
product_serial = self.env['product.product'].create({
|
||
|
'name': 'PSerial',
|
||
|
'is_storable': True,
|
||
|
'tracking': 'serial',
|
||
|
'categ_id': self.env.ref('product.product_category_all').id,
|
||
|
})
|
||
|
|
||
|
lots = self.env['stock.lot'].create([{
|
||
|
'name': 'SN01',
|
||
|
'product_id': product_serial.id,
|
||
|
'company_id': self.env.company.id,
|
||
|
}, {
|
||
|
'name': 'SN02',
|
||
|
'product_id': product_serial.id,
|
||
|
'company_id': self.env.company.id,
|
||
|
}, {
|
||
|
'name': 'SN03',
|
||
|
'product_id': product_serial.id,
|
||
|
'company_id': self.env.company.id,
|
||
|
}])
|
||
|
|
||
|
stock_location = self.env.ref('stock.stock_location_stock')
|
||
|
for lot in lots:
|
||
|
self.env['stock.quant']._update_available_quantity(product_serial, stock_location, 1, lot_id=lot)
|
||
|
|
||
|
picking = self.env['stock.picking'].create({
|
||
|
'location_id': stock_location.id,
|
||
|
'location_dest_id': self.ref('stock.stock_location_customers'),
|
||
|
'picking_type_id': self.ref('stock.picking_type_out'),
|
||
|
'move_ids': [Command.create({
|
||
|
'name': product_serial.name,
|
||
|
'product_id': product_serial.id,
|
||
|
'product_uom_qty': 3,
|
||
|
'product_uom': product_serial.uom_id.id,
|
||
|
'location_id': stock_location.id,
|
||
|
'location_dest_id': self.ref('stock.stock_location_customers'),
|
||
|
})]
|
||
|
})
|
||
|
picking.action_confirm()
|
||
|
|
||
|
with Form(picking) as form:
|
||
|
with form.move_ids_without_package.edit(0) as move_form:
|
||
|
move_form.quantity = 3.0
|
||
|
move_form.lot_ids = lots
|
||
|
|
||
|
url = self._get_picking_url(picking.id)
|
||
|
self.start_tour(url, 'test_onchange_twice_lot_ids', login='admin')
|
||
|
self.assertRecordValues(picking.move_ids, [{"quantity": 1, "lot_ids": lots[2].ids}])
|