First In First Out (FIFO)
fifo
Last In First Out (LIFO)
lifo
Closest Location
closest
Correct inconsistencies for reservation
code
MoveLines = env['stock.move.line'].sudo()
Quants = env['stock.quant'].sudo()
tracked_fields = ['product_id', 'location_id', 'lot_id', 'package_id', 'owner_id']
impacted_quants, impacted_move_lines = set(), set()
all_errors = {}
# Compute rounding precision for each UOM
uom_roundings = {
u['id']: u['rounding'] for u in env['uom.uom'].sudo().with_context(active_test=False).search_read([], ['rounding'])
}
products_rounding = {
p['id']: uom_roundings[p['uom_id'][0]]
for p in env['product.product'].sudo().with_context(active_test=False).search_read([], ['uom_id'])
}
# Move that bypass reservations
ignore_moves = env['stock.move'].sudo().search([('state', 'not in', ['draft', 'cancel', 'done'])])
ignore_moves = ignore_moves.filtered(lambda m: m._should_bypass_reservation()).ids
# Get move_lines with incorrect reservation (negative or invalid on state)
incorrect_lines_grouped = MoveLines.read_group(
[
'|',
('reserved_qty', '<', 0),
'&',
('reserved_qty', '!=', 0),
'|',
('state', 'in', ['done', 'draft', 'cancel']),
('move_id', '=', False),
],
tracked_fields + ['ids:array_agg(id)', 'reserved_qty:sum'],
tracked_fields,
lazy=False,
)
for lines_grp in incorrect_lines_grouped:
rd = products_rounding[lines_grp['product_id'][0]]
if float_compare(0, lines_grp['reserved_qty'], precision_rounding=rd) != 0:
impacted_move_lines.update(lines_grp['ids'])
# Get key to match between quants and sml
def get_key(res):
return res['product_id'], res['location_id'], res['lot_id'], res['package_id'], res['owner_id']
# Create a python dictionary containing all quants with reserved quantities in the following format:
# (product_id, location_id, lot_id, package_id, owner_id): (id, reserved_quantity, quantity, rounding)
all_quants = {
get_key(q): (q['id'], q['reserved_quantity'], q['quantity'], products_rounding[q['product_id'][0]])
for q in Quants.search_read([('reserved_quantity', '!=', 0)], tracked_fields + ['reserved_quantity', 'quantity'])
}
# Get all move_lines with reserved quantities
all_grouped_move_lines = MoveLines.read_group(
[
('move_id', 'not in', ignore_moves),
('state', 'not in', ['done', 'draft', 'cancel']),
('reserved_qty', '>', 0),
('move_id', '!=', False),
],
tracked_fields + ['ids:array_agg(id)', 'reserved_qty:sum'],
tracked_fields,
lazy=False,
)
def check_quant(quant_key, quant_val=None, lines=None):
if quant_val is None and lines is None:
return
if quant_val is None:
quant_val = (None, 0, 0, products_rounding[quant_key[0][0]])
if lines is None:
lines = {'ids': [], 'reserved_qty': 0}
quant_id, quant_res, quant_qty, rounding = quant_val
err = False
# Quant reserved must be inferior or equal to the Quant quantity (Before Odoo 17)
err |= float_compare(quant_qty, quant_res, precision_rounding=rounding) == -1
# Quant reserved must be higher or equal to 0
err |= float_compare(0, quant_res, precision_rounding=rounding) == 1
# Quant reserved must be equal to Move reserved
err |= float_compare(lines['reserved_qty'], quant_res, precision_rounding=rounding) != 0
if err:
impacted_move_lines.update(lines['ids'])
if quant_id:
impacted_quants.add(quant_id)
# Check errors on Move Lines and Quants
for lines_grp in all_grouped_move_lines:
sq_key = get_key(lines_grp)
check_quant(sq_key, quant_val=all_quants.pop(sq_key, None), lines=lines_grp)
# Quants with reservation without move lines reserved on it
for sq_key, sq_val in all_quants.items():
check_quant(sq_key, quant_val=sq_val, lines=None)
if impacted_quants:
Quants.browse(impacted_quants).write({'reserved_quantity': 0})
if impacted_move_lines:
lines = MoveLines.browse(impacted_move_lines)
lines.with_context(bypass_reservation_update=True).write({'reserved_uom_qty': 0})
lines.move_id._recompute_state()
if impacted_quants or impacted_move_lines:
report = "Reserved quantity set to 0 for the following: \n- stock.quant: {}\n- stock.move.line: {}".format(
impacted_quants, impacted_move_lines
)
log(report, level="debug")
action = {'type': 'ir.actions.client', 'tag': 'reload'}
Physical Locations
view
Partners
view
1
Virtual Locations
view
1
Vendors
supplier
Customers
customer
Inter-company transit
transit
Replenish on Order (MTO)
False
5
property_stock_supplier
property_stock_customer
WH