diff --git a/CHANGELOG b/CHANGELOG index 53c3289af6..d71e28c569 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Define one lost and found location per warehouse * Show warehouse in user status if not unique * Add cost price revision * Add wizard to modify product cost price diff --git a/doc/index.rst b/doc/index.rst index b0dd2f95b1..f7a89f4db2 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -18,13 +18,14 @@ virtually stored. The following location types are defined: * Warehouse - Warehouses are meta-locations which define input, storage, picking and output - locations. These locations are all of type Storage. Input and Output are - locations where incoming an outgoing product are temporally stored awaiting - transportation. The storage location is often the biggest location where - products are stored for middle or long periods of time. The picking location - is optionally where the products are picked by the customer shipment - otherwise the storage location is used. + Warehouses are meta-locations which define input, storage, picking, output + and lost and found locations. These locations are all of type Storage. Input + and Output are locations where incoming an outgoing product are temporally + stored awaiting transportation. The storage location is often the biggest + location where products are stored for middle or long periods of time. The + picking location is optionally where the products are picked by the customer + shipment otherwise the storage location is used. The lost and found location + is the location used by inventories when correcting stock levels. * Customer @@ -261,9 +262,8 @@ of moves. It can be in one of these states: Inventory ********* -Inventories allow to control and update stock levels. They are mainly -composed of two locations (a Storage location and a Lost And Found -location), and a list of inventory lines. +Inventories allow to control and update stock levels. They are mainly composed +of the inventoried storage location and a list of inventory lines. Inventory lines consist of a product and it's default unit of measure, an expected quantity and the real quantity (the real products on the shelves). diff --git a/inventory.py b/inventory.py index 4ddaf8ba41..1713a24930 100644 --- a/inventory.py +++ b/inventory.py @@ -37,11 +37,6 @@ class Inventory(Workflow, ModelSQL, ModelView): }, depends=['state'], help="The date of the stock count.") - lost_found = fields.Many2One( - 'stock.location', 'Lost and Found', required=True, - domain=[('type', '=', 'lost_found')], states=_states, depends=_depends, - help="Used for the balancing entries needed when the stock is " - "corrected.") lines = fields.One2Many( 'stock.inventory.line', 'inventory', 'Lines', states={ @@ -106,6 +101,9 @@ class Inventory(Workflow, ModelSQL, ModelView): # Add index on create_date table.index_action('create_date', action='add') + # Migration from 5.4: remove lost_found + table.not_null_action('lost_found', 'remove') + @staticmethod def default_state(): return 'draft' @@ -119,13 +117,6 @@ class Inventory(Workflow, ModelSQL, ModelView): def default_company(): return Transaction().context.get('company') - @classmethod - def default_lost_found(cls): - Location = Pool().get('stock.location') - locations = Location.search(cls.lost_found.domain) - if len(locations) == 1: - return locations[0].id - @classmethod def view_attributes(cls): return [ @@ -446,7 +437,12 @@ class InventoryLine(ModelSQL, ModelView): if delta_qty == 0.0: return from_location = self.inventory.location - to_location = self.inventory.lost_found + to_location = self.inventory.location.lost_found_used + if not to_location: + raise InventoryValidationError( + gettext('stock.msg_inventory_location_missing_lost_found', + inventory=self.inventory.rec_name, + location=self.inventory.location.rec_name)) if delta_qty < 0: (from_location, to_location, delta_qty) = \ (to_location, from_location, -delta_qty) diff --git a/location.py b/location.py index 6768952d42..aee68eb76d 100644 --- a/location.py +++ b/location.py @@ -112,6 +112,18 @@ class Location(DeactivableMixin, tree(), ModelSQL, ModelView): depends=['type', 'storage_location'], help="Where stock is picked from.\n" "Leave empty to use the storage location.") + lost_found_location = fields.Many2One( + 'stock.location', "Lost and Found", + states={ + 'invisible': Eval('type') != 'warehouse', + 'readonly': ~Eval('active'), + }, + domain=[ + ('type', '=', 'lost_found'), + ], + depends=['type', 'active'], + help="Used, by inventories, when correcting stock levels " + "in the warehouse.") quantity = fields.Function( fields.Float('Quantity', help="The amount of stock in the location."), @@ -304,6 +316,11 @@ class Location(DeactivableMixin, tree(), ModelSQL, ModelView): cls._default_warehouse_cache.set(None, warehouse) return warehouse + @property + def lost_found_used(self): + if self.warehouse: + return self.warehouse.lost_found_location + @classmethod def search_rec_name(cls, name, clause): if clause[1].startswith('!') or clause[1].startswith('not '): diff --git a/location.xml b/location.xml index 9e8a91563d..e60ad7c2f9 100644 --- a/location.xml +++ b/location.xml @@ -194,6 +194,10 @@ this repository contains the full copyright notices and license terms. --> STO storage + + Lost and Found + lost_found + Warehouse WH @@ -201,6 +205,7 @@ this repository contains the full copyright notices and license terms. --> + Supplier @@ -212,10 +217,6 @@ this repository contains the full copyright notices and license terms. --> CUS customer - - Lost and Found - lost_found - Transit storage diff --git a/message.xml b/message.xml index 423ba029b2..ff6d8fa822 100644 --- a/message.xml +++ b/message.xml @@ -41,6 +41,9 @@ because the type does not support moves and location has existing moves. To confirm the inventory "%(inventory)s" you must select an option for empty quantity. + + To confirm the inventory "%(inventory)s" you must set a lost and found on a parent of location "%(location)s". + Inventory line "%(line)s" is not unique on inventory "%(inventory)s". diff --git a/tests/scenario_stock_inventory.rst b/tests/scenario_stock_inventory.rst index 4a0773ff0f..0c809a0f24 100644 --- a/tests/scenario_stock_inventory.rst +++ b/tests/scenario_stock_inventory.rst @@ -28,6 +28,7 @@ Get stock locations:: >>> supplier_loc, = Location.find([('code', '=', 'SUP')]) >>> storage_loc, = Location.find([('code', '=', 'STO')]) >>> customer_loc, = Location.find([('code', '=', 'CUS')]) + >>> lost_found_loc, = Location.find([('type', '=', 'lost_found')]) Create products:: @@ -170,7 +171,7 @@ Confirm the inventory:: >>> move, = line_p1.moves >>> move.quantity 1.0 - >>> move.from_location == inventory.lost_found + >>> move.from_location == lost_found_loc True >>> move.to_location == inventory.location True @@ -230,7 +231,7 @@ Add quantity of consumable product:: >>> move, = line.moves >>> move.quantity 5.0 - >>> move.from_location == inventory.lost_found + >>> move.from_location == lost_found_loc True >>> move.to_location == inventory.location True diff --git a/tests/scenario_stock_inventory_empty_quantity.rst b/tests/scenario_stock_inventory_empty_quantity.rst index efd941e450..ab1f8e40b6 100644 --- a/tests/scenario_stock_inventory_empty_quantity.rst +++ b/tests/scenario_stock_inventory_empty_quantity.rst @@ -28,6 +28,7 @@ Get stock locations:: >>> supplier_loc, = Location.find([('code', '=', 'SUP')]) >>> storage_loc, = Location.find([('code', '=', 'STO')]) >>> customer_loc, = Location.find([('code', '=', 'CUS')]) + >>> lost_found_loc, = Location.find([('type', '=', 'lost_found')]) Create product:: @@ -83,5 +84,5 @@ Creating an inventory with empty quantities creates and empty move:: 1.0 >>> move.from_location == inventory.location True - >>> move.to_location == inventory.lost_found + >>> move.to_location == lost_found_loc True diff --git a/view/inventory_form.xml b/view/inventory_form.xml index 3dae6e5f2f..b4d7daa110 100644 --- a/view/inventory_form.xml +++ b/view/inventory_form.xml @@ -6,15 +6,17 @@ this repository contains the full copyright notices and license terms. -->