mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
[MIG] stock_inventory_preparation_filter: Migration to 16.0
This commit is contained in:
@@ -5,8 +5,8 @@
|
||||
|
||||
{
|
||||
"name": "Extended Inventory Preparation Filters",
|
||||
"version": "14.0.1.0.1",
|
||||
"depends": ["stock"],
|
||||
"version": "16.0.1.0.0",
|
||||
"depends": ["stock", "stock_inventory"],
|
||||
"author": "AvanzOSC," "Tecnativa," "Odoo Community Association (OCA)",
|
||||
"category": "Inventory, Logistic, Storage",
|
||||
"website": "https://github.com/OCA/stock-logistics-warehouse",
|
||||
|
||||
@@ -3,63 +3,29 @@
|
||||
# Copyright 2020 Sergio Teruel <sergio.teruel@tecnativa.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo import fields, models
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
|
||||
|
||||
class StockInventory(models.Model):
|
||||
_inherit = "stock.inventory"
|
||||
|
||||
@api.model
|
||||
def _selection_filter(self):
|
||||
"""Get the list of filter allowed according to the options checked
|
||||
in 'Settings / Warehouse'."""
|
||||
res_filter = [
|
||||
("products", _("All products")),
|
||||
("categories", _("Selected Categories")),
|
||||
("domain", _("Filtered Products")),
|
||||
]
|
||||
if self.user_has_groups("stock.group_production_lot"):
|
||||
res_filter.insert(-1, ("lots", _("Selected Lots")))
|
||||
return res_filter
|
||||
product_selection = fields.Selection(
|
||||
selection_add=[("domain", "Filtered Products")],
|
||||
ondelete={"domain": "set default"},
|
||||
)
|
||||
|
||||
filter = fields.Selection(
|
||||
string="Inventory of",
|
||||
selection="_selection_filter",
|
||||
required=True,
|
||||
default="products",
|
||||
help="If you do an entire inventory, you can choose 'All Products' and "
|
||||
"it will prefill the inventory with the current stock. If you "
|
||||
"only do some products (e.g. Cycle Counting) you can choose "
|
||||
"'Manual Selection of Products' and the system won't propose "
|
||||
"anything. You can also let the system propose for a single "
|
||||
"product / lot /... ",
|
||||
)
|
||||
categ_ids = fields.Many2many(
|
||||
comodel_name="product.category",
|
||||
relation="rel_inventories_categories",
|
||||
column1="inventory_id",
|
||||
column2="category_id",
|
||||
string="Categories",
|
||||
)
|
||||
lot_ids = fields.Many2many(
|
||||
comodel_name="stock.production.lot",
|
||||
relation="rel_inventories_lots",
|
||||
column1="inventory_id",
|
||||
column2="lot_id",
|
||||
string="Lots",
|
||||
)
|
||||
product_domain = fields.Char("Domain", default=[("name", "ilike", "")])
|
||||
|
||||
def _action_start(self):
|
||||
def action_state_to_in_progress(self):
|
||||
for inventory in self:
|
||||
if inventory.state != "draft":
|
||||
continue
|
||||
if inventory.filter:
|
||||
if inventory.product_selection:
|
||||
products = inventory._prepare_inventory_filter()
|
||||
if products:
|
||||
inventory.product_ids = [(6, 0, products.ids)]
|
||||
return super()._action_start()
|
||||
return super().action_state_to_in_progress()
|
||||
|
||||
def _prepare_inventory_filter(self):
|
||||
# This method is designed to be inherited by other modules
|
||||
@@ -67,17 +33,14 @@ class StockInventory(models.Model):
|
||||
self.ensure_one()
|
||||
Product = self.env["product.product"]
|
||||
products = Product
|
||||
if self.filter == "categories":
|
||||
products = Product.search([("categ_id", "in", self.categ_ids.ids)])
|
||||
elif self.filter == "lots":
|
||||
products = self.lot_ids.product_id
|
||||
elif self.filter == "domain":
|
||||
if self.product_selection == "domain":
|
||||
domain = safe_eval(self.product_domain)
|
||||
products = Product.search(domain)
|
||||
return products
|
||||
|
||||
def _get_inventory_lines_values(self):
|
||||
vals = super()._get_inventory_lines_values()
|
||||
if self.filter == "lots":
|
||||
vals = list(filter(lambda x: x["prod_lot_id"] in self.lot_ids.ids, vals))
|
||||
return vals
|
||||
def _get_quants(self, locations):
|
||||
if self.product_selection == "domain":
|
||||
domain = safe_eval(self.product_domain)
|
||||
products = self.env["product.product"].search(domain)
|
||||
return self.env["stock.quant"].search([("product_id", "in", products.ids)])
|
||||
return super()._get_quants(locations)
|
||||
|
||||
@@ -49,7 +49,7 @@ class TestStockInventoryPreparationFilterCategories(common.TransactionCase):
|
||||
"categ_id": self.category2.id,
|
||||
}
|
||||
)
|
||||
self.lot = self.env["stock.production.lot"].create(
|
||||
self.lot = self.env["stock.lot"].create(
|
||||
{
|
||||
"name": "Lot test",
|
||||
"product_id": self.product_lot.id,
|
||||
@@ -99,33 +99,49 @@ class TestStockInventoryPreparationFilterCategories(common.TransactionCase):
|
||||
def test_inventory_filter(self):
|
||||
# Filter all products
|
||||
inventory = self.inventory_model.create(
|
||||
{"name": "Inventory test", "filter": "products"}
|
||||
{
|
||||
"name": "Inventory test",
|
||||
"product_selection": "all",
|
||||
"location_ids": self.env.ref("stock.stock_location_stock"),
|
||||
}
|
||||
)
|
||||
inventory.action_state_to_in_progress()
|
||||
self.assertTrue(
|
||||
self.test_products <= inventory.stock_quant_ids.mapped("product_id")
|
||||
)
|
||||
inventory.action_start()
|
||||
self.assertTrue(self.test_products <= inventory.line_ids.mapped("product_id"))
|
||||
# Filter by categories
|
||||
inventory.action_cancel_draft()
|
||||
inventory.action_state_to_draft()
|
||||
inventory.update(
|
||||
{"filter": "categories", "categ_ids": [(6, 0, [self.category.id])]}
|
||||
{
|
||||
"product_selection": "category",
|
||||
"category_id": self.category.id,
|
||||
}
|
||||
)
|
||||
inventory.action_start()
|
||||
self.assertEqual(len(inventory.line_ids), 3)
|
||||
inventory.action_state_to_in_progress()
|
||||
self.assertEqual(len(inventory.stock_quant_ids), 3)
|
||||
# Filter by lots
|
||||
inventory.action_cancel_draft()
|
||||
inventory.update({"filter": "lots", "lot_ids": [(6, 0, self.lot.ids)]})
|
||||
inventory.action_start()
|
||||
self.assertEqual(len(inventory.line_ids), 1)
|
||||
inventory.action_state_to_draft()
|
||||
inventory.update(
|
||||
{
|
||||
"product_selection": "lot",
|
||||
"lot_ids": self.lot.ids,
|
||||
"product_ids": self.product_lot,
|
||||
}
|
||||
)
|
||||
inventory.action_state_to_in_progress()
|
||||
self.assertEqual(len(inventory.stock_quant_ids), 1)
|
||||
|
||||
def test_inventory_domain_filter(self):
|
||||
inventory = self.inventory_model.create(
|
||||
{
|
||||
"name": "Domain inventory",
|
||||
"filter": "domain",
|
||||
"product_selection": "domain",
|
||||
"product_domain": [("id", "=", self.product1.id)],
|
||||
"location_ids": self.env.ref("stock.stock_location_stock"),
|
||||
}
|
||||
)
|
||||
inventory.action_start()
|
||||
self.assertEqual(len(inventory.line_ids), 1)
|
||||
line1 = inventory.line_ids[0]
|
||||
inventory.action_state_to_in_progress()
|
||||
self.assertEqual(len(inventory.stock_quant_ids), 1)
|
||||
line1 = inventory.stock_quant_ids[0]
|
||||
self.assertEqual(line1.product_id, self.product1)
|
||||
self.assertEqual(line1.theoretical_qty, 2.0)
|
||||
self.assertEqual(line1.quantity, 2.0)
|
||||
|
||||
@@ -1,47 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record model="ir.ui.view" id="view_inventory_form">
|
||||
<record model="ir.ui.view" id="view_inventory_group_form">
|
||||
<field name="model">stock.inventory</field>
|
||||
<field name="inherit_id" ref="stock.view_inventory_form" />
|
||||
<field name="inherit_id" ref="stock_inventory.view_inventory_group_form" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="product_ids" position="attributes">
|
||||
<attribute
|
||||
name="attrs"
|
||||
>{'invisible': [('filter', '!=', 'products')]}</attribute>
|
||||
>{'invisible': [('product_selection', '!=', 'all')]}</attribute>
|
||||
</field>
|
||||
<xpath expr="//field[@name='location_ids']/../.." position="before">
|
||||
<xpath expr="//field[@name='owner_id']/../.." position="after">
|
||||
<group name="preparation_filter">
|
||||
<group name="preparation_filter_left">
|
||||
<field
|
||||
name="filter"
|
||||
string="Inventory of"
|
||||
widget='radio'
|
||||
attrs="{'readonly': [('state', '!=', 'draft')]}"
|
||||
/>
|
||||
</group>
|
||||
<group name="preparation_filter_right">
|
||||
<field
|
||||
name="product_domain"
|
||||
nolabel="1"
|
||||
widget="domain"
|
||||
attrs="{'invisible': [('filter', '!=', 'domain')]}"
|
||||
attrs="{'invisible': [('product_selection', '!=', 'domain')]}"
|
||||
options="{'model': 'product.product'}"
|
||||
/>
|
||||
</group>
|
||||
</group>
|
||||
</xpath>
|
||||
<field name="product_ids" position="after">
|
||||
<field
|
||||
name="categ_ids"
|
||||
widget="many2many_tags"
|
||||
attrs="{'invisible':[('filter','!=','categories')], 'required': [('filter','=','categories')]}"
|
||||
/>
|
||||
<field
|
||||
name="lot_ids"
|
||||
widget="many2many_tags"
|
||||
attrs="{'invisible':[('filter','!=','lots')], 'required': [('filter','=','lots')]}"
|
||||
/>
|
||||
</field>
|
||||
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user