[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:
Arnold Moyaux
2018-11-14 12:23:33 +00:00
committed by Pierre Masereel
parent 116d45633b
commit b6ba6038ad
4 changed files with 221 additions and 35 deletions
+2 -11
View File
@@ -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):
+11
View File
@@ -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):
+65 -23
View File
@@ -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([
+143 -1
View File
@@ -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()