Skip to content

Commit

Permalink
[MIG] stock_putaway_hook: Migration to 17.0
Browse files Browse the repository at this point in the history
  • Loading branch information
alexandregaldeano committed Jan 23, 2025
1 parent b935047 commit 95a9784
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 56 deletions.
2 changes: 1 addition & 1 deletion stock_putaway_hook/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{
"name": "Stock Putaway Hooks",
"summary": "Add hooks allowing modules to add more putaway strategies",
"version": "16.0.1.0.1",
"version": "17.0.1.0.0",
"category": "Hidden",
"website": "https://github.com/OCA/stock-logistics-workflow",
"author": "Camptocamp, Odoo Community Association (OCA)",
Expand Down
2 changes: 1 addition & 1 deletion stock_putaway_hook/i18n/it.po
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Project-Id-Version: Odoo Server 17.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-07-01 09:47+0000\n"
"Last-Translator: mymage <[email protected]>\n"
Expand Down
2 changes: 1 addition & 1 deletion stock_putaway_hook/i18n/stock_putaway_hook.pot
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Project-Id-Version: Odoo Server 17.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
Expand Down
4 changes: 2 additions & 2 deletions stock_putaway_hook/models/stock_location.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ def _alternative_putaway_strategy(self):
value = strategy_values[strategy]
# Looking for a putaway from the strategy
putaway_rules = current_location.putaway_rule_ids.filtered(
lambda x: x[strategy] in value
if isinstance(value, (models.BaseModel, list, tuple))
lambda x, strategy=strategy, value=value: x[strategy] in value
if isinstance(value, models.BaseModel | list | tuple)
else x[strategy] == value
)
putaway_location = first(putaway_rules).location_out_id
Expand Down
74 changes: 24 additions & 50 deletions stock_putaway_hook/models/stock_putaway_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,22 @@

from lxml import etree

from odoo import models
from odoo.osv.expression import AND, OR
from odoo import api, models
from odoo.tools.safe_eval import safe_eval

from odoo.addons.base.models.ir_ui_view import (
transfer_modifiers_to_node,
transfer_node_to_modifiers,
)


class StockPutawayRule(models.Model):
_inherit = "stock.putaway.rule"

def fields_view_get(
self, view_id=None, view_type="form", toolbar=False, submenu=False
):
result = super().fields_view_get(
view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu
)
if result["name"] == "stock.putaway.rule.tree":
result["arch"] = self._fields_view_get_adapt_attrs(result["arch"])
return result

def _fields_view_get_add_exclusive_selection_attrs(self, doc):
@api.model
def _get_view(self, view_id=None, view_type="form", **options):
arch, view = super()._get_view(view_id=view_id, view_type=view_type, **options)
if view.type == "tree":
self._get_view_set_adapt_attrs(arch)
view.arch = etree.tostring(arch, encoding="unicode")
return arch, view

def _get_view_add_exclusive_selection_attrs(self, arch):
"""Make the readonly and required attrs dynamic for putaway rules
By default, product_id and category_id fields have static domains
Expand Down Expand Up @@ -56,56 +48,38 @@ def _fields_view_get_add_exclusive_selection_attrs(self, doc):
Look in module stock_putaway_by_route (where this is tested as well).
"""
exclusive_fields = set()
nodes = doc.xpath("//field[@options]")
nodes = arch.xpath("//field[@options]")
for field in nodes:
options = safe_eval(field.attrib.get("options", "{}"))
if options.get("exclusive_selection"):
exclusive_fields.add(field)

for field in exclusive_fields:
readonly_domain = OR(
[
[(other.attrib["name"], "!=", False)]
for other in exclusive_fields
if other != field
]
readonly_domain = " or ".join(
[other.attrib["name"] for other in exclusive_fields if other != field]
)
required_domain = AND(
required_domain = " and ".join(
[
[(other.attrib["name"], "=", False)]
f"not {other.attrib['name']}"
for other in exclusive_fields
if other != field
]
)
field.set("readonly", str(readonly_domain))
field.set("required", str(required_domain))

if field.attrib.get("attrs"):
attrs = safe_eval(field.attrib["attrs"])
else:
attrs = {}
attrs["readonly"] = readonly_domain
attrs["required"] = required_domain

field.set("attrs", str(attrs))
modifiers = {}
transfer_node_to_modifiers(field, modifiers, context=self.env.context)
transfer_modifiers_to_node(modifiers, field)

def _add_exclusive_selection(self, doc, field_name):
nodes = doc.xpath(f"//field[@name='{field_name}']")
def _get_view_add_exclusive_selection(self, arch, field_name):
nodes = arch.xpath(f"//field[@name='{field_name}']")
for field in nodes:
options = safe_eval(field.attrib.get("options", "{}"))
options["exclusive_selection"] = True
field.set("options", str(options))

def _fields_view_get_adapt_attrs(self, view_arch):
doc = etree.XML(view_arch)
def _get_view_set_adapt_attrs(self, arch):
# Add the "exclusive_selection" option on product_id and category_id
# fields from core, so they are treated by
# _fields_view_get_add_exclusive_selection_attrs
self._add_exclusive_selection(doc, "product_id")
self._add_exclusive_selection(doc, "category_id")

self._fields_view_get_add_exclusive_selection_attrs(doc)
# _view_get_add_exclusive_selection_attrs
self._get_view_add_exclusive_selection(arch, "product_id")
self._get_view_add_exclusive_selection(arch, "category_id")

new_view = etree.tostring(doc, encoding="unicode")
return new_view
self._get_view_add_exclusive_selection_attrs(arch)
2 changes: 1 addition & 1 deletion stock_putaway_hook/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def setUpClass(cls):
}
)

cls.env["stock.putaway.rule"].create(
cls.stock_putaway_rule = cls.env["stock.putaway.rule"].create(
{
"foo": True, # The field that is passed through context
"location_in_id": cls.location_internal_parent_3.id,
Expand Down
55 changes: 55 additions & 0 deletions stock_putaway_hook/tests/test_putaway_hook.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Copyright 2022 ACSONE SA/NV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from unittest import mock

from odoo.tests.common import TransactionCase

from .common import PutawayHookCommon
Expand Down Expand Up @@ -43,3 +45,56 @@ def test_putaway_hook_alternative(self):
self.location_internal_shelf_3,
strategy,
)


class TestPutawayHookViews(PutawayHookCommon, TransactionCase):
@mock.patch(
"odoo.addons.stock_putaway_hook.models.stock_putaway_rule."
"StockPutawayRule._get_view_set_adapt_attrs"
)
def test_form_view(self, mock_get_view_set_adapt_attrs: mock.Mock):
self.stock_putaway_rule._get_view(view_type="form")
mock_get_view_set_adapt_attrs.assert_not_called()

def test_tree_view(self):
arch, _view = self.stock_putaway_rule._get_view(view_type="tree")

product_id_field = arch.xpath("//field[@name='product_id']")[0]
self.assertEqual(product_id_field.attrib["readonly"], "category_id")
self.assertEqual(product_id_field.attrib["required"], "not category_id")

category_id_field = arch.xpath("//field[@name='category_id']")[0]
self.assertEqual(category_id_field.attrib["readonly"], "product_id")
self.assertEqual(category_id_field.attrib["required"], "not product_id")

@mock.patch(
"odoo.addons.stock_putaway_hook.models.stock_putaway_rule."
"StockPutawayRule._get_view_set_adapt_attrs"
)
def test_tree_view_with_mocked_get_view_set_adapt_attrs(
self, _mock_get_view_set_adapt_attrs: mock.Mock
):
"""
This is equivalent to calling the _get_view method without this module
"""
arch, _view = self.stock_putaway_rule._get_view(view_type="tree")

product_id_field = arch.xpath("//field[@name='product_id']")[0]
self.assertEqual(
product_id_field.attrib["readonly"],
"context.get('single_product', False) or category_id",
)
self.assertEqual(
product_id_field.attrib["required"],
"not category_id and not package_type_ids",
)

category_id_field = arch.xpath("//field[@name='category_id']")[0]
self.assertEqual(
category_id_field.attrib["readonly"],
"context.get('fixed_category', False) or product_id",
)
self.assertEqual(
category_id_field.attrib["required"],
"not product_id and not package_type_ids",
)
1 change: 1 addition & 0 deletions test-requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
odoo_test_helper

0 comments on commit 95a9784

Please sign in to comment.