216 lines
8.5 KiB
Python
216 lines
8.5 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
|
|
||
|
from odoo import Command
|
||
|
from odoo.addons.stock.tests.common import TestStockCommon
|
||
|
from odoo.tests import Form
|
||
|
from odoo.exceptions import ValidationError
|
||
|
|
||
|
|
||
|
class TestLotSerial(TestStockCommon):
|
||
|
@classmethod
|
||
|
def setUpClass(cls):
|
||
|
super().setUpClass()
|
||
|
cls.locationA = cls.env['stock.location'].create({
|
||
|
'name': 'Location A',
|
||
|
'usage': 'internal',
|
||
|
})
|
||
|
cls.locationB = cls.env['stock.location'].create({
|
||
|
'name': 'Location B',
|
||
|
'usage': 'internal',
|
||
|
})
|
||
|
cls.locationC = cls.env['stock.location'].create({
|
||
|
'name': 'Location C',
|
||
|
'usage': 'internal',
|
||
|
})
|
||
|
cls.productA.tracking = 'lot'
|
||
|
cls.lot_p_a = cls.LotObj.create({
|
||
|
'name': 'lot_product_a',
|
||
|
'product_id': cls.productA.id,
|
||
|
})
|
||
|
cls.StockQuantObj.create({
|
||
|
'product_id': cls.productA.id,
|
||
|
'location_id': cls.locationA.id,
|
||
|
'quantity': 10.0,
|
||
|
'lot_id': cls.lot_p_a.id
|
||
|
})
|
||
|
|
||
|
cls.productB.tracking = 'serial'
|
||
|
cls.lot_p_b = cls.LotObj.create({
|
||
|
'name': 'lot_product_b',
|
||
|
'product_id': cls.productB.id,
|
||
|
})
|
||
|
cls.env['stock.quant']._update_available_quantity(
|
||
|
cls.productB,
|
||
|
cls.locationA,
|
||
|
1.0,
|
||
|
lot_id=cls.lot_p_b,
|
||
|
)
|
||
|
|
||
|
def test_single_location(self):
|
||
|
self.assertEqual(self.lot_p_a.location_id, self.locationA)
|
||
|
self.assertEqual(self.lot_p_b.location_id, self.locationA)
|
||
|
|
||
|
# testing changing the location from the lot form
|
||
|
lot_b_form = Form(self.lot_p_b)
|
||
|
lot_b_form.location_id = self.locationB
|
||
|
lot_b_form.save()
|
||
|
self.assertEqual(self.lot_p_b.quant_ids.filtered(lambda q: q.quantity > 0).location_id, self.locationB)
|
||
|
|
||
|
# testing changing the location from the quant
|
||
|
self.lot_p_b.quant_ids.move_quants(location_dest_id=self.locationC, message='test_quant_move')
|
||
|
self.assertEqual(self.lot_p_b.location_id, self.locationC)
|
||
|
|
||
|
# testing having the lot in multiple locations
|
||
|
self.StockQuantObj.create({
|
||
|
'product_id': self.productA.id,
|
||
|
'location_id': self.locationC.id,
|
||
|
'quantity': 10.0,
|
||
|
'lot_id': self.lot_p_a.id
|
||
|
})
|
||
|
self.assertEqual(self.lot_p_a.location_id.id, False)
|
||
|
|
||
|
# testing having the lot back in a single location
|
||
|
self.lot_p_a.quant_ids.filtered(lambda q: q.location_id == self.locationA).move_quants(location_dest_id=self.locationC)
|
||
|
self.StockQuantObj.invalidate_model()
|
||
|
self.StockQuantObj._unlink_zero_quants()
|
||
|
self.assertEqual(self.lot_p_a.location_id, self.locationC)
|
||
|
|
||
|
def test_import_lots(self):
|
||
|
vals = self.MoveObj.action_generate_lot_line_vals({
|
||
|
'default_tracking': 'lot',
|
||
|
'default_product_id': self.productA.id,
|
||
|
'default_location_dest_id': self.locationC.id,
|
||
|
}, "import", "", 0, "aze;2\nqsd;4\nwxc")
|
||
|
|
||
|
self.assertEqual(len(vals), 3)
|
||
|
self.assertEqual(vals[0]['lot_name'], 'aze')
|
||
|
self.assertEqual(vals[0]['quantity'], 2)
|
||
|
self.assertEqual(vals[1]['lot_name'], 'qsd')
|
||
|
self.assertEqual(vals[1]['quantity'], 4)
|
||
|
self.assertEqual(vals[2]['lot_name'], 'wxc')
|
||
|
self.assertEqual(vals[2]['quantity'], 1, "default lot qty")
|
||
|
|
||
|
def test_lot_uniqueness(self):
|
||
|
""" Checks that the same lot name cannot be inserted twice for the same company or 'no-company'.
|
||
|
"""
|
||
|
lot_1 = self.env['stock.lot'].create({
|
||
|
'name': 'unique',
|
||
|
'product_id': self.productB.id,
|
||
|
'company_id': False,
|
||
|
})
|
||
|
self.assertTrue(lot_1)
|
||
|
# Now try to insert the same one without company
|
||
|
with self.assertRaises(ValidationError):
|
||
|
self.env['stock.lot'].create({
|
||
|
'name': 'unique',
|
||
|
'product_id': self.productB.id,
|
||
|
'company_id': False,
|
||
|
})
|
||
|
# Same thing should happen when creating it from a company now
|
||
|
with self.assertRaises(ValidationError):
|
||
|
self.env['stock.lot'].create({
|
||
|
'name': 'unique',
|
||
|
'product_id': self.productB.id,
|
||
|
'company_id': self.env.company.id,
|
||
|
})
|
||
|
|
||
|
lot_2 = self.env['stock.lot'].create({
|
||
|
'name': 'also_unique',
|
||
|
'product_id': self.productB.id,
|
||
|
'company_id': self.env.company.id,
|
||
|
})
|
||
|
self.assertTrue(lot_2)
|
||
|
# Now try to insert the same one without company
|
||
|
with self.assertRaises(ValidationError):
|
||
|
self.env['stock.lot'].create({
|
||
|
'name': 'also_unique',
|
||
|
'product_id': self.productB.id,
|
||
|
'company_id': False,
|
||
|
})
|
||
|
# Same thing should happen when creating it from a company now
|
||
|
with self.assertRaises(ValidationError):
|
||
|
self.env['stock.lot'].create({
|
||
|
'name': 'also_unique',
|
||
|
'product_id': self.productB.id,
|
||
|
'company_id': self.env.company.id,
|
||
|
})
|
||
|
|
||
|
def test_bypass_reservation(self):
|
||
|
"""
|
||
|
Check that the reservation of is bypassed when a stock move is added after the picking is done
|
||
|
"""
|
||
|
customer = self.PartnerObj.create({'name': 'bob'})
|
||
|
delivery_picking = self.env['stock.picking'].create({
|
||
|
'partner_id': customer.id,
|
||
|
'picking_type_id': self.picking_type_out,
|
||
|
'move_ids': [Command.create({
|
||
|
'name': self.productC.name,
|
||
|
'product_id': self.productC.id,
|
||
|
'product_uom_qty': 5,
|
||
|
'quantity': 5,
|
||
|
'location_id': self.stock_location,
|
||
|
'location_dest_id': self.customer_location,
|
||
|
})]
|
||
|
})
|
||
|
stock = self.env['stock.location'].browse(self.stock_location)
|
||
|
additional_product = self.productA
|
||
|
lot = self.lot_p_a
|
||
|
lot.location_id = stock
|
||
|
quant = additional_product.stock_quant_ids.filtered(lambda q: q.location_id == stock)
|
||
|
self.assertRecordValues(quant, [{'quantity': 10.0, 'reserved_quantity': 0.0}])
|
||
|
delivery_picking.button_validate()
|
||
|
delivery_picking.is_locked = False
|
||
|
self.env['stock.move.line'].create({
|
||
|
'product_id': additional_product.id,
|
||
|
'product_uom_id': additional_product.uom_id.id,
|
||
|
'picking_id': delivery_picking.id,
|
||
|
'quantity': 3,
|
||
|
'lot_id': lot.id,
|
||
|
'quant_id': quant.id
|
||
|
})
|
||
|
self.assertRecordValues(delivery_picking.move_ids, [{'state': 'done', 'quantity': 5.0, 'picked': True}, {'state': 'done', 'quantity': 3.0, 'picked': True}])
|
||
|
self.assertRecordValues(quant, [{'quantity': 7.0, 'reserved_quantity': 0.0}])
|
||
|
|
||
|
def test_location_lot_id_update_quant_qty(self):
|
||
|
"""
|
||
|
Test that the location of a lot is updated when its linked quants change
|
||
|
"""
|
||
|
# check that the serial number linked to productB is in location A
|
||
|
self.assertEqual(self.lot_p_b.location_id, self.locationA)
|
||
|
# Make a delivery move
|
||
|
starting_quant = self.lot_p_b.quant_ids
|
||
|
self.assertEqual(starting_quant.quantity, 1)
|
||
|
move = self.env["stock.move"].create({
|
||
|
'name': 'test_move',
|
||
|
'location_id': self.locationA.id,
|
||
|
'location_dest_id': self.customer_location,
|
||
|
'product_id': self.productB.id,
|
||
|
'product_uom_qty': 1.0,
|
||
|
})
|
||
|
move._action_confirm()
|
||
|
self.assertEqual(move.state, 'confirmed')
|
||
|
move._action_assign()
|
||
|
move.picked = True
|
||
|
move._action_done()
|
||
|
self.assertEqual(move.state, 'done')
|
||
|
# check that the quantity of starting quant is moved to a new quant
|
||
|
self.assertEqual(starting_quant.quantity, 0)
|
||
|
# check that the sn is in customer location
|
||
|
self.assertEqual(self.lot_p_b.location_id.id, self.customer_location)
|
||
|
# create a return
|
||
|
move = self.env['stock.move'].create({
|
||
|
'name': 'test_move',
|
||
|
'location_id': self.customer_location,
|
||
|
'location_dest_id': self.locationA.id,
|
||
|
'product_id': self.productB.id,
|
||
|
'lot_ids': self.lot_p_b,
|
||
|
'product_uom_qty': 1.0,
|
||
|
})
|
||
|
move._action_confirm()
|
||
|
move.picked = True
|
||
|
move._action_done()
|
||
|
self.assertEqual(move.state, 'done')
|
||
|
self.assertEqual(starting_quant.quantity, 1)
|
||
|
self.assertEqual(self.lot_p_b.location_id, self.locationA)
|