mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
Migrate stock_reserve_rule to 13.0
This commit is contained in:
committed by
Sébastien Alix
parent
fdab54b432
commit
dfc97bb24d
@@ -1,29 +1,26 @@
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
|
||||
{
|
||||
'name': 'Stock Reservation Rules',
|
||||
'summary': 'Configure reservation rules by location',
|
||||
'version': '12.0.1.0.0',
|
||||
'author': "Camptocamp, Odoo Community Association (OCA)",
|
||||
'website': "https://github.com/OCA/stock-logistics-warehouse",
|
||||
'category': 'Stock Management',
|
||||
'depends': [
|
||||
'stock',
|
||||
'product_packaging_type', # OCA/product-attribute
|
||||
"name": "Stock Reservation Rules",
|
||||
"summary": "Configure reservation rules by location",
|
||||
"version": "13.0.1.0.0",
|
||||
"author": "Camptocamp, Odoo Community Association (OCA)",
|
||||
"website": "https://github.com/OCA/stock-logistics-warehouse",
|
||||
"category": "Stock Management",
|
||||
"depends": ["stock", "product_packaging_type"], # OCA/product-attribute
|
||||
"demo": [
|
||||
"demo/product_demo.xml",
|
||||
"demo/stock_location_demo.xml",
|
||||
"demo/stock_reserve_rule_demo.xml",
|
||||
"demo/stock_inventory_demo.xml",
|
||||
"demo/stock_picking_demo.xml",
|
||||
],
|
||||
'demo': [
|
||||
'demo/product_demo.xml',
|
||||
'demo/stock_location_demo.xml',
|
||||
'demo/stock_reserve_rule_demo.xml',
|
||||
'demo/stock_inventory_demo.xml',
|
||||
'demo/stock_picking_demo.xml',
|
||||
"data": [
|
||||
"views/stock_reserve_rule_views.xml",
|
||||
"security/ir.model.access.csv",
|
||||
"security/stock_reserve_rule_security.xml",
|
||||
],
|
||||
'data': [
|
||||
'views/stock_reserve_rule_views.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'security/stock_reserve_rule_security.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'development_status': 'Alpha',
|
||||
'license': 'AGPL-3',
|
||||
"installable": True,
|
||||
"development_status": "Alpha",
|
||||
"license": "AGPL-3",
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
<field name="tracking">none</field>
|
||||
<field name="uom_id" ref="uom.product_uom_unit"/>
|
||||
<field name="uom_po_id" ref="uom.product_uom_unit"/>
|
||||
<field name="property_stock_inventory" ref="stock.location_inventory"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -27,8 +27,11 @@
|
||||
<field name="location_id" ref="stock_location_zone_c_bin_1_demo"/>
|
||||
</record>
|
||||
|
||||
<function model="stock.inventory" name="action_validate">
|
||||
<function model="stock.inventory" name="_action_start">
|
||||
<function eval="[[('state','=','draft'),('id', '=', ref('stock_reserve_rule.stock_inventory_1_demo'))]]" model="stock.inventory" name="search"/>
|
||||
</function>
|
||||
<function model="stock.inventory" name="action_validate">
|
||||
<function eval="[[('state','=','confirm'),('id', '=', ref('stock_reserve_rule.stock_inventory_1_demo'))]]" model="stock.inventory" name="search"/>
|
||||
</function>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -84,8 +84,7 @@ class StockMove(models.Model):
|
||||
break
|
||||
|
||||
need_zero = (
|
||||
float_compare(still_need, 0, precision_rounding=rounding)
|
||||
!= 1
|
||||
float_compare(still_need, 0, precision_rounding=rounding) != 1
|
||||
)
|
||||
if need_zero:
|
||||
# useless to eval the other rules when still_need <= 0
|
||||
|
||||
@@ -27,4 +27,4 @@ class StockQuant(models.Model):
|
||||
seen[location] = seen[location] | quant
|
||||
else:
|
||||
seen[location] = quant
|
||||
return [(location, quants) for location, quants in seen.items()]
|
||||
return [(loc, quants) for loc, quants in seen.items()]
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Copyright 2019 Camptocamp SA
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from odoo import fields, models
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
from odoo.osv import expression
|
||||
from odoo.tools.float_utils import float_compare
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
|
||||
|
||||
def _default_sequence(record):
|
||||
@@ -33,8 +33,7 @@ class StockReserveRule(models.Model):
|
||||
sequence = fields.Integer(default=lambda s: _default_sequence(s))
|
||||
active = fields.Boolean(default=True)
|
||||
company_id = fields.Many2one(
|
||||
comodel_name="res.company",
|
||||
default=lambda self: self.env.user.company_id.id,
|
||||
comodel_name="res.company", default=lambda self: self.env.user.company_id.id
|
||||
)
|
||||
|
||||
location_id = fields.Many2one(comodel_name="stock.location", required=True)
|
||||
@@ -143,9 +142,7 @@ class StockReserveRuleRemoval(models.Model):
|
||||
|
||||
def _eval_quant_domain(self, quants, domain):
|
||||
quant_domain = [("id", "in", quants.ids)]
|
||||
return self.env["stock.quant"].search(
|
||||
expression.AND([quant_domain, domain])
|
||||
)
|
||||
return self.env["stock.quant"].search(expression.AND([quant_domain, domain]))
|
||||
|
||||
def _filter_quants(self, move, quants):
|
||||
domain = safe_eval(self.quant_domain) or []
|
||||
@@ -206,7 +203,6 @@ class StockReserveRuleRemoval(models.Model):
|
||||
(
|
||||
sum(quants.mapped("quantity"))
|
||||
- sum(quants.mapped("reserved_quantity")),
|
||||
quants,
|
||||
location,
|
||||
)
|
||||
for location, quants in quants_per_bin
|
||||
@@ -217,7 +213,7 @@ class StockReserveRuleRemoval(models.Model):
|
||||
# Propose the largest quants first, so we have as less operations
|
||||
# as possible. We take goods only if we empty the bin.
|
||||
rounding = fields.first(quants).product_id.uom_id.rounding
|
||||
for location_quantity, quants, location in bins:
|
||||
for location_quantity, location in bins:
|
||||
if location_quantity <= 0:
|
||||
continue
|
||||
|
||||
@@ -253,9 +249,7 @@ class StockReserveRuleRemoval(models.Model):
|
||||
rounding = product.uom_id.rounding
|
||||
|
||||
def is_greater_eq(value, other):
|
||||
return (
|
||||
float_compare(value, other, precision_rounding=rounding) >= 0
|
||||
)
|
||||
return float_compare(value, other, precision_rounding=rounding) >= 0
|
||||
|
||||
for pack_quantity in packaging_quantities:
|
||||
# Get quants quantity on each loop because they may change.
|
||||
@@ -266,7 +260,6 @@ class StockReserveRuleRemoval(models.Model):
|
||||
(
|
||||
sum(quants.mapped("quantity"))
|
||||
- sum(quants.mapped("reserved_quantity")),
|
||||
quants,
|
||||
location,
|
||||
)
|
||||
for location, quants in quants_per_bin
|
||||
@@ -274,12 +267,10 @@ class StockReserveRuleRemoval(models.Model):
|
||||
reverse=True,
|
||||
)
|
||||
|
||||
for location_quantity, quants, location in bins:
|
||||
for location_quantity, location in bins:
|
||||
if location_quantity <= 0:
|
||||
continue
|
||||
enough_for_packaging = is_greater_eq(
|
||||
location_quantity, pack_quantity
|
||||
)
|
||||
enough_for_packaging = is_greater_eq(location_quantity, pack_quantity)
|
||||
asked_more_than_packaging = is_greater_eq(need, pack_quantity)
|
||||
if enough_for_packaging and asked_more_than_packaging:
|
||||
# compute how much packaging we can get
|
||||
|
||||
@@ -101,9 +101,7 @@ class TestReserveRule(common.SavepointCase):
|
||||
return picking
|
||||
|
||||
def _update_qty_in_location(self, location, product, quantity):
|
||||
self.env["stock.quant"]._update_available_quantity(
|
||||
product, location, quantity
|
||||
)
|
||||
self.env["stock.quant"]._update_available_quantity(product, location, quantity)
|
||||
|
||||
def _create_rule(self, rule_values, removal_values):
|
||||
rule_config = {
|
||||
@@ -113,6 +111,8 @@ class TestReserveRule(common.SavepointCase):
|
||||
}
|
||||
rule_config.update(rule_values)
|
||||
self.env["stock.reserve.rule"].create(rule_config)
|
||||
# workaround for https://github.com/odoo/odoo/pull/41900
|
||||
self.env["stock.reserve.rule"].invalidate_cache()
|
||||
|
||||
def _setup_packagings(self, product, packagings):
|
||||
"""Create packagings on a product
|
||||
@@ -219,7 +219,7 @@ class TestReserveRule(common.SavepointCase):
|
||||
],
|
||||
)
|
||||
self.assertEqual(move.state, "partially_available")
|
||||
self.assertEqual(move.reserved_availability, 300.)
|
||||
self.assertEqual(move.reserved_availability, 300.0)
|
||||
|
||||
def test_rule_fallback(self):
|
||||
reserve = self.env["stock.location"].create(
|
||||
@@ -254,7 +254,7 @@ class TestReserveRule(common.SavepointCase):
|
||||
],
|
||||
)
|
||||
self.assertEqual(move.state, "assigned")
|
||||
self.assertEqual(move.reserved_availability, 400.)
|
||||
self.assertEqual(move.reserved_availability, 400.0)
|
||||
|
||||
def test_rule_domain(self):
|
||||
self._update_qty_in_location(self.loc_zone1_bin1, self.product1, 100)
|
||||
@@ -355,9 +355,9 @@ class TestReserveRule(common.SavepointCase):
|
||||
self.assertRecordValues(
|
||||
ml,
|
||||
[
|
||||
{"location_id": self.loc_zone1_bin2.id, "product_qty": 150.},
|
||||
{"location_id": self.loc_zone2_bin1.id, "product_qty": 50.},
|
||||
{"location_id": self.loc_zone3_bin1.id, "product_qty": 50.},
|
||||
{"location_id": self.loc_zone1_bin2.id, "product_qty": 150.0},
|
||||
{"location_id": self.loc_zone2_bin1.id, "product_qty": 50.0},
|
||||
{"location_id": self.loc_zone3_bin1.id, "product_qty": 50.0},
|
||||
],
|
||||
)
|
||||
self.assertEqual(move.state, "assigned")
|
||||
@@ -389,8 +389,8 @@ class TestReserveRule(common.SavepointCase):
|
||||
self.assertRecordValues(
|
||||
ml,
|
||||
[
|
||||
{"location_id": self.loc_zone1_bin1.id, "product_qty": 50.},
|
||||
{"location_id": self.loc_zone2_bin1.id, "product_qty": 30.},
|
||||
{"location_id": self.loc_zone1_bin1.id, "product_qty": 50.0},
|
||||
{"location_id": self.loc_zone2_bin1.id, "product_qty": 30.0},
|
||||
],
|
||||
)
|
||||
self.assertEqual(move.state, "assigned")
|
||||
@@ -424,8 +424,8 @@ class TestReserveRule(common.SavepointCase):
|
||||
self.assertRecordValues(
|
||||
ml,
|
||||
[
|
||||
{"location_id": self.loc_zone1_bin2.id, "product_qty": 60.},
|
||||
{"location_id": self.loc_zone2_bin1.id, "product_qty": 20.},
|
||||
{"location_id": self.loc_zone1_bin2.id, "product_qty": 60.0},
|
||||
{"location_id": self.loc_zone2_bin1.id, "product_qty": 20.0},
|
||||
],
|
||||
)
|
||||
self.assertEqual(move.state, "assigned")
|
||||
@@ -433,10 +433,7 @@ class TestReserveRule(common.SavepointCase):
|
||||
def test_rule_packaging(self):
|
||||
self._setup_packagings(
|
||||
self.product1,
|
||||
[
|
||||
("Pallet", 500, self.pallet),
|
||||
("Retail Box", 50, self.retail_box),
|
||||
],
|
||||
[("Pallet", 500, self.pallet), ("Retail Box", 50, self.retail_box)],
|
||||
)
|
||||
self._update_qty_in_location(self.loc_zone1_bin1, self.product1, 40)
|
||||
self._update_qty_in_location(self.loc_zone1_bin2, self.product1, 510)
|
||||
@@ -470,9 +467,9 @@ class TestReserveRule(common.SavepointCase):
|
||||
self.assertRecordValues(
|
||||
ml,
|
||||
[
|
||||
{"location_id": self.loc_zone1_bin2.id, "product_qty": 500.},
|
||||
{"location_id": self.loc_zone2_bin1.id, "product_qty": 50.},
|
||||
{"location_id": self.loc_zone3_bin1.id, "product_qty": 40.},
|
||||
{"location_id": self.loc_zone1_bin2.id, "product_qty": 500.0},
|
||||
{"location_id": self.loc_zone2_bin1.id, "product_qty": 50.0},
|
||||
{"location_id": self.loc_zone3_bin1.id, "product_qty": 40.0},
|
||||
],
|
||||
)
|
||||
self.assertEqual(move.state, "assigned")
|
||||
@@ -553,9 +550,9 @@ class TestReserveRule(common.SavepointCase):
|
||||
self.assertRecordValues(
|
||||
ml,
|
||||
[
|
||||
{"location_id": self.loc_zone1_bin2.id, "product_qty": 500.},
|
||||
{"location_id": self.loc_zone2_bin2.id, "product_qty": 50.},
|
||||
{"location_id": self.loc_zone3_bin1.id, "product_qty": 10.},
|
||||
{"location_id": self.loc_zone1_bin2.id, "product_qty": 500.0},
|
||||
{"location_id": self.loc_zone2_bin2.id, "product_qty": 50.0},
|
||||
{"location_id": self.loc_zone3_bin1.id, "product_qty": 10.0},
|
||||
],
|
||||
)
|
||||
self.assertEqual(move.state, "assigned")
|
||||
|
||||
@@ -7,18 +7,21 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Stock Reservation Rule">
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="toggle_active" type="object" class="oe_stat_button" icon="fa-archive">
|
||||
<field name="active" widget="boolean_button" options='{"terminology": "archive"}'/>
|
||||
</button>
|
||||
</div>
|
||||
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1><field name="name"/></h1>
|
||||
<group string="Rule Applicability" name="configuration">
|
||||
<group>
|
||||
<field name="active" invisible="1"/>
|
||||
<field name="location_id"/>
|
||||
<field name="fallback_location_id"/>
|
||||
<field name="rule_domain" widget="domain" options="{'model': 'stock.move'}" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="rule_domain" widget="domain" options="{'model': 'stock.move', 'in_dialog': true}" />
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Removal Rules" name="rule">
|
||||
<field name="rule_removal_ids" nolabel="1">
|
||||
<tree string="Removal Rules">
|
||||
@@ -36,7 +39,7 @@
|
||||
widget="many2many_tags"
|
||||
attrs="{'invisible': [('removal_strategy', '!=', 'packaging')]}"
|
||||
/>
|
||||
<field name="quant_domain" widget="domain" options="{'model': 'stock.quant'}" />
|
||||
<field name="quant_domain" widget="domain" options="{'model': 'stock.quant', 'in_dialog': true}" />
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
@@ -74,7 +77,6 @@
|
||||
<field name="name">Stock Reservation Rules</field>
|
||||
<field name="res_model">stock.reserve.rule</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_id" ref="view_stock_reserve_rule_tree"/>
|
||||
<field name="search_view_id" ref="view_stock_reserve_rule_search"/>
|
||||
<field name="context"></field>
|
||||
|
||||
Reference in New Issue
Block a user