[IMP] stock: archive warehouse
Archiving a warehouse didn't archive its related locations, routes and rules. So we now archive the related objects and add some constraints to avoid having an inconsistent state in stock with archived warehouse. TASK-ID: 30659 closes odoo/odoo#26555
This commit is contained in:
committed by
Pierre Masereel
parent
116d45633b
commit
b6ba6038ad
@@ -242,17 +242,8 @@ class StockWarehouse(models.Model):
|
||||
return routes
|
||||
|
||||
def _update_location_manufacture(self, new_manufacture_step):
|
||||
switch_warehouses = self.filtered(lambda wh: wh.manufacture_steps != new_manufacture_step)
|
||||
loc_warehouse = switch_warehouses.filtered(lambda wh: not wh._location_used(wh.pbm_loc_id))
|
||||
if loc_warehouse:
|
||||
loc_warehouse.mapped('pbm_loc_id').write({'active': False})
|
||||
loc_warehouse = switch_warehouses.filtered(lambda wh: not wh._location_used(wh.sam_loc_id))
|
||||
if loc_warehouse:
|
||||
loc_warehouse.mapped('sam_loc_id').write({'active': False})
|
||||
if new_manufacture_step != 'mrp_one_step':
|
||||
self.mapped('pbm_loc_id').write({'active': True})
|
||||
if new_manufacture_step == 'pbm_sam':
|
||||
self.mapped('sam_loc_id').write({'active': True})
|
||||
self.mapped('pbm_loc_id').write({'active': new_manufacture_step != 'mrp_one_step'})
|
||||
self.mapped('sam_loc_id').write({'active': new_manufacture_step == 'pbm_sam'})
|
||||
|
||||
@api.multi
|
||||
def _update_name_and_code(self, name=False, code=False):
|
||||
|
||||
@@ -96,6 +96,17 @@ class Location(models.Model):
|
||||
" location as there are products reserved in this location."
|
||||
" Please unreserve the products first."
|
||||
))
|
||||
if 'active' in values:
|
||||
if not self.env.context.get('do_not_check_quant'):
|
||||
children_location = self.env['stock.location'].with_context(active_test=False).search([('id', 'child_of', self.ids)])
|
||||
internal_children_locations = children_location.filtered(lambda l: l.usage == 'internal')
|
||||
children_quants = self.env['stock.quant'].search([('quantity', '!=', 0), ('reserved_quantity', '!=', 0), ('location_id', 'in', internal_children_locations.ids)])
|
||||
if children_quants and values['active'] == False:
|
||||
raise UserError(_('You still have some product in locations %s') %
|
||||
(','.join(children_quants.mapped('location_id.name'))))
|
||||
else:
|
||||
super(Location, children_location - self).with_context({'do_not_check_quant': True}).write(values)
|
||||
|
||||
return super(Location, self).write(values)
|
||||
|
||||
def name_get(self):
|
||||
|
||||
@@ -121,7 +121,7 @@ class Warehouse(models.Model):
|
||||
|
||||
def write(self, vals):
|
||||
Route = self.env['stock.location.route']
|
||||
warehouses = self.with_context(active_test=False) # TDE FIXME: check this
|
||||
warehouses = self.with_context(active_test=False)
|
||||
|
||||
if vals.get('code') or vals.get('name'):
|
||||
warehouses._update_name_and_code(vals.get('name'), vals.get('code'))
|
||||
@@ -146,7 +146,7 @@ class Warehouse(models.Model):
|
||||
|
||||
res = super(Warehouse, self).write(vals)
|
||||
|
||||
for warehouse in self:
|
||||
for warehouse in warehouses:
|
||||
# check if we need to delete and recreate route
|
||||
depends = [depend for depends in [value['depends'] for value in warehouse._get_routes_values().values()] for depend in depends]
|
||||
if any(depend in vals for depend in depends):
|
||||
@@ -166,12 +166,66 @@ class Warehouse(models.Model):
|
||||
any(depend in vals for depend in depends):
|
||||
warehouse._create_or_update_global_routes_rules()
|
||||
|
||||
if 'active' in vals:
|
||||
picking_type_ids = self.env['stock.picking.type'].with_context(active_test=False).search([('warehouse_id', '=', warehouse.id)])
|
||||
move_ids = self.env['stock.move'].search([
|
||||
('picking_type_id', 'in', picking_type_ids.ids),
|
||||
('state', 'not in', ('done', 'cancel')),
|
||||
])
|
||||
if move_ids:
|
||||
raise UserError(_('You still have ongoing operations for picking\
|
||||
types %s in warehouse %s') %
|
||||
(', '.join(move_ids.mapped('picking_type_id.name')), warehouse.name))
|
||||
else:
|
||||
picking_type_ids.write({'active': vals['active']})
|
||||
location_ids = self.env['stock.location'].with_context(active_test=False).search([('location_id', 'child_of', warehouse.view_location_id.id)])
|
||||
picking_type_using_locations = self.env['stock.picking.type'].search([
|
||||
('default_location_src_id', 'in', location_ids.ids),
|
||||
('default_location_dest_id', 'in', location_ids.ids),
|
||||
('id', 'not in', picking_type_ids.ids),
|
||||
])
|
||||
if picking_type_using_locations:
|
||||
raise UserError(_('%s use default source or destination locations\
|
||||
from warehouse %s that will be archived.') %
|
||||
(', '.join(picking_type_using_locations.mapped('name')), warehouse.name))
|
||||
warehouse.view_location_id.write({'active': vals['active']})
|
||||
|
||||
rule_ids = self.env['stock.rule'].with_context(active_test=False).search([('warehouse_id', '=', self.id)])
|
||||
# Only modify route that apply on this warehouse.
|
||||
route_ids = warehouse.route_ids.filtered(lambda r: len(r.warehouse_ids) == 1).write({'active': vals['active']})
|
||||
rule_ids.write({'active': vals['active']})
|
||||
|
||||
if warehouse.active:
|
||||
# Catch all warehouse fields that trigger a modfication on
|
||||
# routes, rules, picking types and locations (e.g the reception
|
||||
# steps). The purpose is to write on it in order to let the
|
||||
# write method set the correct field to active or archive.
|
||||
depends = set([])
|
||||
for rule_item in warehouse._get_global_route_rules_values().values():
|
||||
for depend in rule_item.get('depends', []):
|
||||
depends.add(depend)
|
||||
for rule_item in warehouse._get_routes_values().values():
|
||||
for depend in rule_item.get('depends', []):
|
||||
depends.add(depend)
|
||||
values = {'resupply_route_ids': [(4, route.id) for route in warehouse.resupply_route_ids]}
|
||||
for depend in depends:
|
||||
values.update({depend: warehouse[depend]})
|
||||
warehouse.write(values)
|
||||
|
||||
if vals.get('resupply_wh_ids') and not vals.get('resupply_route_ids'):
|
||||
for warehouse in warehouses:
|
||||
to_add = new_resupply_whs - old_resupply_whs[warehouse.id]
|
||||
to_remove = old_resupply_whs[warehouse.id] - new_resupply_whs
|
||||
if to_add:
|
||||
warehouse.create_resupply_routes(to_add)
|
||||
existing_route = Route.search([
|
||||
('supplied_wh_id', '=', warehouse.id),
|
||||
('supplier_wh_id', 'in', to_remove.ids),
|
||||
('active', '=', False)
|
||||
])
|
||||
if existing_route:
|
||||
existing_route.write({'active': True})
|
||||
else:
|
||||
warehouse.create_resupply_routes(to_add)
|
||||
if to_remove:
|
||||
Route.search([('supplied_wh_id', '=', warehouse.id), ('supplier_wh_id', 'in', to_remove.ids)]).write({'active': False})
|
||||
# TDE FIXME: shouldn't we remove stock rules also ? because this could make them global (not sure)
|
||||
@@ -366,7 +420,8 @@ class Warehouse(models.Model):
|
||||
'routing_key': self.reception_steps,
|
||||
'depends': ['reception_steps'],
|
||||
'route_update_values': {
|
||||
'name': self._format_routename(route_type=self.reception_steps)
|
||||
'name': self._format_routename(route_type=self.reception_steps),
|
||||
'active': self.active,
|
||||
},
|
||||
'route_create_values': {
|
||||
'product_categ_selectable': True,
|
||||
@@ -384,7 +439,8 @@ class Warehouse(models.Model):
|
||||
'routing_key': self.delivery_steps,
|
||||
'depends': ['delivery_steps'],
|
||||
'route_update_values': {
|
||||
'name': self._format_routename(route_type=self.delivery_steps)
|
||||
'name': self._format_routename(route_type=self.delivery_steps),
|
||||
'active': self.active,
|
||||
},
|
||||
'route_create_values': {
|
||||
'product_categ_selectable': True,
|
||||
@@ -665,26 +721,12 @@ class Warehouse(models.Model):
|
||||
warehouse.int_type_id.sequence_id.write(sequence_data['int_type_id'])
|
||||
|
||||
def _update_location_reception(self, new_reception_step):
|
||||
switch_warehouses = self.filtered(lambda wh: wh.reception_steps != new_reception_step and not wh._location_used(wh.wh_input_stock_loc_id))
|
||||
if switch_warehouses:
|
||||
(switch_warehouses.mapped('wh_input_stock_loc_id') + switch_warehouses.mapped('wh_qc_stock_loc_id')).write({'active': False})
|
||||
if new_reception_step == 'three_steps':
|
||||
self.mapped('wh_qc_stock_loc_id').write({'active': True})
|
||||
if new_reception_step != 'one_step':
|
||||
self.mapped('wh_input_stock_loc_id').write({'active': True})
|
||||
self.mapped('wh_qc_stock_loc_id').write({'active': new_reception_step == 'three_steps'})
|
||||
self.mapped('wh_input_stock_loc_id').write({'active': new_reception_step != 'one_step'})
|
||||
|
||||
def _update_location_delivery(self, new_delivery_step):
|
||||
switch_warehouses = self.filtered(lambda wh: wh.delivery_steps != new_delivery_step)
|
||||
loc_warehouse = switch_warehouses.filtered(lambda wh: not wh._location_used(wh.wh_output_stock_loc_id))
|
||||
if loc_warehouse:
|
||||
loc_warehouse.mapped('wh_output_stock_loc_id').write({'active': False})
|
||||
loc_warehouse = switch_warehouses.filtered(lambda wh: not wh._location_used(wh.wh_pack_stock_loc_id))
|
||||
if loc_warehouse:
|
||||
loc_warehouse.mapped('wh_pack_stock_loc_id').write({'active': False})
|
||||
if new_delivery_step == 'pick_pack_ship':
|
||||
self.mapped('wh_pack_stock_loc_id').write({'active': True})
|
||||
if new_delivery_step != 'ship_only':
|
||||
self.mapped('wh_output_stock_loc_id').write({'active': True})
|
||||
self.mapped('wh_pack_stock_loc_id').write({'active': new_delivery_step == 'pick_pack_ship'})
|
||||
self.mapped('wh_output_stock_loc_id').write({'active': new_delivery_step != 'ship_only'})
|
||||
|
||||
def _location_used(self, location):
|
||||
rules = self.env['stock.rule'].search_count([
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo.addons.stock.tests.common2 import TestStockCommon
|
||||
|
||||
from odoo.tests import Form
|
||||
|
||||
class TestWarehouse(TestStockCommon):
|
||||
|
||||
@@ -431,6 +431,148 @@ class TestWarehouse(TestStockCommon):
|
||||
# Ensure there still no quants in distribution warehouse
|
||||
self.assertEqual(sum(self.env['stock.quant']._gather(product, warehouse_distribution_wavre.lot_stock_id).mapped('quantity')), 0)
|
||||
|
||||
def test_toggle_active_warehouse_1(self):
|
||||
""" Basic test that create a warehouse with classic configuration.
|
||||
Archive it and check that locations, picking types, routes, rules are
|
||||
correclty active or archive.
|
||||
"""
|
||||
wh = Form(self.env['stock.warehouse'])
|
||||
wh.name = "The attic of Willy"
|
||||
wh.code = "WIL"
|
||||
warehouse = wh.save()
|
||||
|
||||
custom_location = Form(self.env['stock.location'])
|
||||
custom_location.name = "A Trunk"
|
||||
custom_location.location_id = warehouse.lot_stock_id
|
||||
custom_location = custom_location.save()
|
||||
|
||||
# Archive warehouse
|
||||
warehouse.toggle_active()
|
||||
# Global rule
|
||||
self.assertFalse(warehouse.mto_pull_id.active)
|
||||
|
||||
# Route
|
||||
self.assertFalse(warehouse.reception_route_id.active)
|
||||
self.assertFalse(warehouse.delivery_route_id.active)
|
||||
|
||||
# Location
|
||||
self.assertFalse(warehouse.lot_stock_id.active)
|
||||
self.assertFalse(warehouse.wh_input_stock_loc_id.active)
|
||||
self.assertFalse(warehouse.wh_qc_stock_loc_id.active)
|
||||
self.assertFalse(warehouse.wh_output_stock_loc_id.active)
|
||||
self.assertFalse(warehouse.wh_pack_stock_loc_id.active)
|
||||
self.assertFalse(custom_location.active)
|
||||
|
||||
# Picking Type
|
||||
self.assertFalse(warehouse.in_type_id.active)
|
||||
self.assertFalse(warehouse.out_type_id.active)
|
||||
self.assertFalse(warehouse.int_type_id.active)
|
||||
self.assertFalse(warehouse.pick_type_id.active)
|
||||
self.assertFalse(warehouse.pack_type_id.active)
|
||||
|
||||
# Active warehouse
|
||||
warehouse.toggle_active()
|
||||
# Global rule
|
||||
self.assertTrue(warehouse.mto_pull_id.active)
|
||||
|
||||
# Route
|
||||
self.assertTrue(warehouse.reception_route_id.active)
|
||||
self.assertTrue(warehouse.delivery_route_id.active)
|
||||
|
||||
# Location
|
||||
self.assertTrue(warehouse.lot_stock_id.active)
|
||||
self.assertFalse(warehouse.wh_input_stock_loc_id.active)
|
||||
self.assertFalse(warehouse.wh_qc_stock_loc_id.active)
|
||||
self.assertFalse(warehouse.wh_output_stock_loc_id.active)
|
||||
self.assertFalse(warehouse.wh_pack_stock_loc_id.active)
|
||||
self.assertTrue(custom_location.active)
|
||||
|
||||
# Picking Type
|
||||
self.assertTrue(warehouse.in_type_id.active)
|
||||
self.assertTrue(warehouse.out_type_id.active)
|
||||
self.assertTrue(warehouse.int_type_id.active)
|
||||
self.assertFalse(warehouse.pick_type_id.active)
|
||||
self.assertFalse(warehouse.pack_type_id.active)
|
||||
|
||||
def test_toggle_active_warehouse_2(self):
|
||||
wh = Form(self.env['stock.warehouse'])
|
||||
wh.name = "The attic of Willy"
|
||||
wh.code = "WIL"
|
||||
wh.reception_steps = "two_steps"
|
||||
wh.delivery_steps = "pick_pack_ship"
|
||||
warehouse = wh.save()
|
||||
|
||||
warehouse.resupply_wh_ids = [(6, 0, [self.warehouse_1.id])]
|
||||
|
||||
custom_location = Form(self.env['stock.location'])
|
||||
custom_location.name = "A Trunk"
|
||||
custom_location.location_id = warehouse.lot_stock_id
|
||||
custom_location = custom_location.save()
|
||||
|
||||
# Add a warehouse on the route.
|
||||
warehouse.reception_route_id.write({
|
||||
'warehouse_ids': [(4, self.warehouse_1.id)]
|
||||
})
|
||||
|
||||
route = Form(self.env['stock.location.route'])
|
||||
route.name = "Stair"
|
||||
route = route.save()
|
||||
|
||||
route.warehouse_ids = [(6, 0, [warehouse.id, self.warehouse_1.id])]
|
||||
|
||||
# Pre archive a location and a route
|
||||
warehouse.delivery_route_id.toggle_active()
|
||||
warehouse.wh_pack_stock_loc_id.toggle_active()
|
||||
|
||||
# Archive warehouse
|
||||
warehouse.toggle_active()
|
||||
# Global rule
|
||||
self.assertFalse(warehouse.mto_pull_id.active)
|
||||
|
||||
# Route
|
||||
self.assertTrue(warehouse.reception_route_id.active)
|
||||
self.assertFalse(warehouse.delivery_route_id.active)
|
||||
self.assertTrue(route.active)
|
||||
|
||||
# Location
|
||||
self.assertFalse(warehouse.lot_stock_id.active)
|
||||
self.assertFalse(warehouse.wh_input_stock_loc_id.active)
|
||||
self.assertFalse(warehouse.wh_qc_stock_loc_id.active)
|
||||
self.assertFalse(warehouse.wh_output_stock_loc_id.active)
|
||||
self.assertFalse(warehouse.wh_pack_stock_loc_id.active)
|
||||
self.assertFalse(custom_location.active)
|
||||
|
||||
# Picking Type
|
||||
self.assertFalse(warehouse.in_type_id.active)
|
||||
self.assertFalse(warehouse.out_type_id.active)
|
||||
self.assertFalse(warehouse.int_type_id.active)
|
||||
self.assertFalse(warehouse.pick_type_id.active)
|
||||
self.assertFalse(warehouse.pack_type_id.active)
|
||||
|
||||
# Active warehouse
|
||||
warehouse.toggle_active()
|
||||
# Global rule
|
||||
self.assertTrue(warehouse.mto_pull_id.active)
|
||||
|
||||
# Route
|
||||
self.assertTrue(warehouse.reception_route_id.active)
|
||||
self.assertTrue(warehouse.delivery_route_id.active)
|
||||
|
||||
# Location
|
||||
self.assertTrue(warehouse.lot_stock_id.active)
|
||||
self.assertTrue(warehouse.wh_input_stock_loc_id.active)
|
||||
self.assertFalse(warehouse.wh_qc_stock_loc_id.active)
|
||||
self.assertTrue(warehouse.wh_output_stock_loc_id.active)
|
||||
self.assertTrue(warehouse.wh_pack_stock_loc_id.active)
|
||||
self.assertTrue(custom_location.active)
|
||||
|
||||
# Picking Type
|
||||
self.assertTrue(warehouse.in_type_id.active)
|
||||
self.assertTrue(warehouse.out_type_id.active)
|
||||
self.assertTrue(warehouse.int_type_id.active)
|
||||
self.assertTrue(warehouse.pick_type_id.active)
|
||||
self.assertTrue(warehouse.pack_type_id.active)
|
||||
|
||||
class TestResupply(TestStockCommon):
|
||||
def setUp(self):
|
||||
super(TestResupply, self).setUp()
|
||||
|
||||
Reference in New Issue
Block a user