From 4234395ed3a31f46d2f8a464ca0cb63452960f53 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Fri, 22 May 2026 08:43:46 +0000 Subject: [PATCH 01/21] [IMP] base_custom_filter: reflect OCA changes --- base_custom_filter/README.rst | 9 ++- base_custom_filter/models/base.py | 5 +- base_custom_filter/models/ir_filters.py | 45 +++++++++++- base_custom_filter/readme/CONFIGURE.md | 17 +++-- .../static/description/index.html | 19 ++++-- base_custom_filter/tests/test_filters.py | 68 +++++++++++++------ base_custom_filter/views/ir_filters_views.xml | 10 ++- 7 files changed, 136 insertions(+), 37 deletions(-) diff --git a/base_custom_filter/README.rst b/base_custom_filter/README.rst index e509365..7c97df7 100644 --- a/base_custom_filter/README.rst +++ b/base_custom_filter/README.rst @@ -56,9 +56,16 @@ Configuration Filter: - - Domain (``domain``) + - Domain (``domain``) - OR - + - Date Field (``date_field``) - creates a date filter with period + options - User Groups (``groups``) + **Note:** For filter type, you must specify either Domain or Date + Field, but not both. Date filters automatically provide period + options like Today, This Week, This Month, This Quarter, and This + Year. + Group By: - Group By Field (field to be assigned to ``group_by`` context) diff --git a/base_custom_filter/models/base.py b/base_custom_filter/models/base.py index 93347c8..c91ec01 100644 --- a/base_custom_filter/models/base.py +++ b/base_custom_filter/models/base.py @@ -58,7 +58,10 @@ def _build_filter_element(self, item, is_groupby=False): if is_groupby: attrs["context"] = str({"group_by": item.groupby_field.sudo().name}) else: - attrs["domain"] = item.domain + if item.date_field: + attrs["date"] = item.date_field.sudo().name + else: + attrs["domain"] = item.domain return etree.Element("filter", attrs) @api.model diff --git a/base_custom_filter/models/ir_filters.py b/base_custom_filter/models/ir_filters.py index 44700f6..d7dce30 100644 --- a/base_custom_filter/models/ir_filters.py +++ b/base_custom_filter/models/ir_filters.py @@ -2,7 +2,8 @@ # Copyright 2021 ForgeFlow S.L. (https://www.forgeflow.com) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import api, fields, models +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError from odoo.osv import expression @@ -38,9 +39,51 @@ def _selection_type(self): help="""Enter a filter domain expression if necessary. Example: [('default_code', 'ilike', self)]""" ) + date_field = fields.Many2one( + comodel_name="ir.model.fields", + ondelete="cascade", + domain=( + "[('model', '=', model_id), " + "('ttype', 'in', ['date', 'datetime']), " + "('store', '=', True)]" + ), + compute="_compute_date_field", + store=True, + readonly=False, + inverse="_inverse_date_field", + help="If set, creates a date filter with period options " + "(Today, This Week, This Month, etc.) instead of a domain filter. " + "Only applicable for filter type.", + ) group_ids = fields.Many2many("res.groups", string="User Groups") group_id = fields.Many2one(comodel_name="ir.filters.group", string="Filter Group") + @api.constrains("type", "domain", "date_field") + def _check_filter_type_fields(self): + for record in self: + if record.type != "filter": + continue + has_domain = record.domain and record.domain != "[]" + if has_domain and record.date_field: + raise ValidationError( + _( + "Filter '%(name)s': You cannot set both Domain and " + "Date Field. Please choose only one.", + name=record.name, + ) + ) + + @api.depends("type", "domain") + def _compute_date_field(self): + for record in self: + if record.type != "filter" or (record.domain and record.domain != "[]"): + record.date_field = False + + def _inverse_date_field(self): + for record in self: + if record.date_field: + record.domain = "[]" + @api.model def get_filters( self, diff --git a/base_custom_filter/readme/CONFIGURE.md b/base_custom_filter/readme/CONFIGURE.md index 89d2922..fd025cc 100644 --- a/base_custom_filter/readme/CONFIGURE.md +++ b/base_custom_filter/readme/CONFIGURE.md @@ -12,8 +12,13 @@ Filter: - > - Domain (`domain`) + > - Domain (`domain`) - OR - + > - Date Field (`date_field`) - creates a date filter with period options > - User Groups (`groups`) + > + > **Note:** For filter type, you must specify either Domain or Date Field, + > but not both. Date filters automatically provide period options like + > Today, This Week, This Month, This Quarter, and This Year. Group By: @@ -29,8 +34,8 @@ 3. Filter groups (accessible via *Settings \> Custom Filters \> Custom Filter Groups*) support the following optional settings: - > - Insert XPath: XPath expression for the insertion point - > (e.g. `//filter[@name='my_filter']`) - > - Insert Position: Insert before or after the target element - > - Separator Position: Place separator before, after, or none - > (filter type only) + - Insert XPath: XPath expression for the insertion point + (e.g. `//filter[@name='my_filter']`) + - Insert Position: Insert before or after the target element + - Separator Position: Place separator before, after, or none + (filter type only) diff --git a/base_custom_filter/static/description/index.html b/base_custom_filter/static/description/index.html index 769fb5d..b50cb7e 100644 --- a/base_custom_filter/static/description/index.html +++ b/base_custom_filter/static/description/index.html @@ -8,10 +8,11 @@ /* :Author: David Goodger (goodger@python.org) -:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $ +:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $ :Copyright: This stylesheet has been placed in the public domain. Default cascading style sheet for the HTML output of Docutils. +Despite the name, some widely supported CSS2 features are used. See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to customize this style sheet. @@ -274,7 +275,7 @@ margin-left: 2em ; margin-right: 2em } -pre.code .ln { color: grey; } /* line numbers */ +pre.code .ln { color: gray; } /* line numbers */ pre.code, code { background-color: #eeeeee } pre.code .comment, code .comment { color: #5C6576 } pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } @@ -300,7 +301,7 @@ span.pre { white-space: pre } -span.problematic { +span.problematic, pre.problematic { color: red } span.section-subtitle { @@ -407,9 +408,15 @@

Configuration

Filter:

+

Note: For filter type, you must specify either Domain or Date +Field, but not both. Date filters automatically provide period +options like Today, This Week, This Month, This Quarter, and This +Year.

Group By:

@@ -485,7 +492,9 @@

Contributors

Maintainers

This module is maintained by the OCA.

-Odoo Community Association + +Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

diff --git a/base_custom_filter/tests/test_filters.py b/base_custom_filter/tests/test_filters.py index a3234cc..2199594 100644 --- a/base_custom_filter/tests/test_filters.py +++ b/base_custom_filter/tests/test_filters.py @@ -113,6 +113,31 @@ def test_get_view_content_groupby(self): "The string is not in the returned view", ) + def test_get_filters_excludes_non_favorites(self): + """Test that get_filters excludes non-favorite filter types.""" + # Create a non-favorite filter + self.filters_obj.create( + { + "name": "Non-Favorite Filter", + "type": "filter", + "model_id": "ir.filters.group", + "domain": '[["id","=",1]]', + } + ) + # Create a favorite filter + self.filters_obj.create( + { + "name": "Favorite Filter", + "type": "favorite", + "model_id": "ir.filters.group", + "domain": '[["id","=",2]]', + } + ) + results = self.filters_obj.get_filters("ir.filters.group") + result_names = [r["name"] for r in results] + self.assertNotIn("Non-Favorite Filter", result_names) + self.assertIn("Favorite Filter", result_names) + def test_insert_xpath_validation(self): group = self.filters_group_obj.create( { @@ -158,27 +183,26 @@ def test_filter_with_custom_xpath(self): between_content = view_content[filter_pos:target_pos] self.assertNotIn("", between_content) - def test_get_filters_excludes_non_favorites(self): - """Test that get_filters excludes non-favorite filter types.""" - # Create a non-favorite filter - self.filters_obj.create( - { - "name": "Non-Favorite Filter", - "type": "filter", - "model_id": "ir.filters.group", - "domain": '[["id","=",1]]', - } + def test_get_view_content_date_filter(self): + with Form(self.filters_obj) as date_filter: + date_filter.name = "Test Date Filter" + date_filter.type = "filter" + date_filter.model_id = "ir.filters.group" + date_filter.date_field = self.env.ref( + "base_custom_filter.field_ir_filters_group__create_date" + ) + filter_record = self.filters_obj.search([("name", "=", "Test Date Filter")]) + self.assertEqual(filter_record.name, "Test Date Filter") + view_dict = self.filters_group_obj.get_view(view_type="search") + view_content = view_dict.get("arch", b"").decode("utf-8") + filter_name = "ir_custom_filter_" + str(filter_record.id) + date_string = ( + f'' ) - # Create a favorite filter - self.filters_obj.create( - { - "name": "Favorite Filter", - "type": "favorite", - "model_id": "ir.filters.group", - "domain": '[["id","=",2]]', - } + self.assertIn( + date_string, + view_content, + "The date filter is not in the returned view", ) - results = self.filters_obj.get_filters("ir.filters.group") - result_names = [r["name"] for r in results] - self.assertNotIn("Non-Favorite Filter", result_names) - self.assertIn("Favorite Filter", result_names) diff --git a/base_custom_filter/views/ir_filters_views.xml b/base_custom_filter/views/ir_filters_views.xml index 32d4d43..c18cdf0 100644 --- a/base_custom_filter/views/ir_filters_views.xml +++ b/base_custom_filter/views/ir_filters_views.xml @@ -29,9 +29,15 @@ /> - type in ['search', 'groupby'] + type in ['search', 'groupby'] or date_field + + @@ -81,6 +88,7 @@ + From 2b1d6556bbfed21043f0a65c526be40107212dce Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Fri, 22 May 2026 08:43:46 +0000 Subject: [PATCH 02/21] [IMP] base_tier_validation: reflect OCA changes --- base_tier_validation/README.rst | 2 +- base_tier_validation/__manifest__.py | 2 +- base_tier_validation/models/tier_review.py | 1 + .../models/tier_validation.py | 7 +++ .../static/description/index.html | 2 +- .../tier_review_widget/tier_review_widget.xml | 2 +- .../tests/test_tier_validation.py | 54 ++++++++++++++++++- 7 files changed, 65 insertions(+), 5 deletions(-) diff --git a/base_tier_validation/README.rst b/base_tier_validation/README.rst index b54f257..768af4a 100644 --- a/base_tier_validation/README.rst +++ b/base_tier_validation/README.rst @@ -11,7 +11,7 @@ Base Tier Validation !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:dc242d6c50cc357fbd98be3778c5e3013971e9ea0622db72e500fd72d0de7c80 + !! source digest: sha256:1823d16061ca83ece8322b77d320bb735e7462305d7ae60fd3e15819f3c6f276 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Mature-brightgreen.png diff --git a/base_tier_validation/__manifest__.py b/base_tier_validation/__manifest__.py index c43098f..a046a2d 100644 --- a/base_tier_validation/__manifest__.py +++ b/base_tier_validation/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Base Tier Validation", "summary": "Implement a validation process based on tiers.", - "version": "18.0.3.3.1", + "version": "18.0.3.4.0", "development_status": "Mature", "maintainers": ["LoisRForgeFlow"], "category": "Tools", diff --git a/base_tier_validation/models/tier_review.py b/base_tier_validation/models/tier_review.py index ef64f2b..34a6a01 100644 --- a/base_tier_validation/models/tier_review.py +++ b/base_tier_validation/models/tier_review.py @@ -23,6 +23,7 @@ class TierReview(models.Model): ("pending", "Pending"), ("rejected", "Rejected"), ("approved", "Approved"), + ("cancel", "Cancel"), ], default="waiting", ) diff --git a/base_tier_validation/models/tier_validation.py b/base_tier_validation/models/tier_validation.py index 6e85ec4..97ff5be 100644 --- a/base_tier_validation/models/tier_validation.py +++ b/base_tier_validation/models/tier_validation.py @@ -690,6 +690,13 @@ def _rejected_tier(self, tiers=False): ) # We need to notify all pending users if there is approve sequence if tier_reviews and any(review.approve_sequence for review in tier_reviews): + # If there are waiting reviews that should be approved sequentially, + # they must be marked as canceled + waiting_reviews = self.review_ids.filtered( + lambda r: r.status == "waiting" and r.approve_sequence + ) + if waiting_reviews: + waiting_reviews.write({"status": "cancel"}) reviews_to_notify = self.review_ids.filtered( lambda r: r.status == "pending" and r.definition_id.notify_on_rejected ) diff --git a/base_tier_validation/static/description/index.html b/base_tier_validation/static/description/index.html index 5459706..94b4a40 100644 --- a/base_tier_validation/static/description/index.html +++ b/base_tier_validation/static/description/index.html @@ -372,7 +372,7 @@

Base Tier Validation

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:dc242d6c50cc357fbd98be3778c5e3013971e9ea0622db72e500fd72d0de7c80 +!! source digest: sha256:1823d16061ca83ece8322b77d320bb735e7462305d7ae60fd3e15819f3c6f276 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Mature License: AGPL-3 OCA/server-ux Translate me on Weblate Try me on Runboat

Validating some operations is a common need across different areas in a diff --git a/base_tier_validation/static/src/components/tier_review_widget/tier_review_widget.xml b/base_tier_validation/static/src/components/tier_review_widget/tier_review_widget.xml index 99582e5..8798e59 100644 --- a/base_tier_validation/static/src/components/tier_review_widget/tier_review_widget.xml +++ b/base_tier_validation/static/src/components/tier_review_widget/tier_review_widget.xml @@ -50,7 +50,7 @@ t-key="review.id" > diff --git a/base_tier_validation/tests/test_tier_validation.py b/base_tier_validation/tests/test_tier_validation.py index 94cbc12..0f5033f 100644 --- a/base_tier_validation/tests/test_tier_validation.py +++ b/base_tier_validation/tests/test_tier_validation.py @@ -194,7 +194,7 @@ def test_11_add_comment_rejection(self): )._notify_rejected_review_body() self.assertEqual(comment, "A review was rejected by John. (Test Comment)") - def test_12_approve_sequence(self): + def test_12_approve_sequence_validate(self): # Create new test record test_record = self.test_model.create({"test_field": 2.5}) # Create tier definitions @@ -239,6 +239,58 @@ def test_12_approve_sequence(self): record1.validate_tier() self.assertTrue(any(r.status == "approved" for r in record1.review_ids)) + def test_12_approve_sequence_reject(self): + # Create new test record + test_record = self.test_model.create({"test_field": 2.5}) + # Create tier definitions + tier_def_1 = self.tier_def_obj.create( + { + "model_id": self.tester_model.id, + "review_type": "individual", + "reviewer_id": self.test_user_1.id, + "definition_domain": "[('test_field', '>', 1.0)]", + "approve_sequence": True, + "sequence": 30, + } + ) + tier_def_2 = self.tier_def_obj.create( + { + "model_id": self.tester_model.id, + "review_type": "individual", + "reviewer_id": self.test_user_2.id, + "definition_domain": "[('test_field', '>', 1.0)]", + "approve_sequence": True, + "sequence": 20, + } + ) + tier_def_3 = self.tier_def_obj.create( + { + "model_id": self.tester_model.id, + "review_type": "individual", + "reviewer_id": self.test_user_3_multi_company.id, + "definition_domain": "[('test_field', '>', 1.0)]", + "approve_sequence": True, + "sequence": 10, + } + ) + # Request validation + self.assertFalse(self.test_record.review_ids) + reviews = test_record.with_user(self.test_user_2.id).request_validation() + self.assertTrue(reviews) + record = test_record.with_user(self.test_user_1.id) + self.assertTrue(record.can_review) + # User 1 validates the record, extra reviews should be cancel. + self.assertFalse(any(r.status == "approved" for r in record.review_ids)) + record.reject_tier() + self.assertTrue(any(r.status == "rejected" for r in record.review_ids)) + self.assertTrue(any(r.status == "cancel" for r in record.review_ids)) + review_1 = record.review_ids.filtered(lambda x: x.definition_id == tier_def_1) + self.assertEqual(review_1.status, "rejected") + review_2 = record.review_ids.filtered(lambda x: x.definition_id == tier_def_2) + self.assertEqual(review_2.status, "cancel") + review_3 = record.review_ids.filtered(lambda x: x.definition_id == tier_def_3) + self.assertEqual(review_3.status, "cancel") + def test_12_approve_sequence_same_user(self): """Similar to test_12_approve_sequence, but all same users, the approve_sequence still apply correctly""" From 77c9c86316d0254c21047597947bf5adb0bc7890 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Fri, 22 May 2026 08:43:46 +0000 Subject: [PATCH 03/21] [IMP] base_user_role: reflect OCA changes --- base_user_role/README.rst | 2 +- base_user_role/__manifest__.py | 2 +- base_user_role/models/role.py | 6 +- base_user_role/models/user.py | 12 ++- base_user_role/static/description/index.html | 2 +- base_user_role/tests/test_user_role.py | 84 +++++++++++++++----- 6 files changed, 83 insertions(+), 25 deletions(-) diff --git a/base_user_role/README.rst b/base_user_role/README.rst index 6ae4a1b..ec8a516 100644 --- a/base_user_role/README.rst +++ b/base_user_role/README.rst @@ -11,7 +11,7 @@ User roles !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:b3aa00f609d45dcd82a69b2a3e13b327096ab1d25883b543e3e45538e854d40c + !! source digest: sha256:99ab17708d54615c87b2764f44327b9a8908fbf188fdcfbd80b301b453c8207a !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png diff --git a/base_user_role/__manifest__.py b/base_user_role/__manifest__.py index 1a06e06..2c70a95 100644 --- a/base_user_role/__manifest__.py +++ b/base_user_role/__manifest__.py @@ -4,7 +4,7 @@ { "name": "User roles", - "version": "18.0.1.0.5", + "version": "18.0.1.0.7", "category": "Tools", "author": "ABF OSIELL, Odoo Community Association (OCA)", "license": "LGPL-3", diff --git a/base_user_role/models/role.py b/base_user_role/models/role.py index 4cf1666..2a24197 100644 --- a/base_user_role/models/role.py +++ b/base_user_role/models/role.py @@ -21,7 +21,10 @@ class ResUsersRole(models.Model): string="Associated group", ) line_ids = fields.One2many( - comodel_name="res.users.role.line", inverse_name="role_id", string="Role lines" + comodel_name="res.users.role.line", + inverse_name="role_id", + string="Role lines", + domain=[("user_id.active", "=", True)], ) user_ids = fields.One2many( comodel_name="res.users", string="Users list", compute="_compute_user_ids" @@ -134,7 +137,6 @@ class ResUsersRoleLine(models.Model): _name = "res.users.role.line" _description = "Users associated to a role" - active = fields.Boolean(related="user_id.active") role_id = fields.Many2one( comodel_name="res.users.role", required=True, string="Role", ondelete="cascade" ) diff --git a/base_user_role/models/user.py b/base_user_role/models/user.py index d3264e5..90a759f 100644 --- a/base_user_role/models/user.py +++ b/base_user_role/models/user.py @@ -102,6 +102,16 @@ def set_groups_from_roles(self, force=False): to_remove = [(3, gr) for gr in groups_to_remove] groups = to_remove + to_add if groups: - vals = {"groups_id": groups} + # Prevent tiggering res_users_notification_type for share users + vals = {} + if ( + self.env.ref("base.group_user").id in groups_to_remove + and "notification_type" in user._fields + and user.notification_type == "inbox" + ): + vals["notification_type"] = "email" + pass + + vals["groups_id"] = groups super(ResUsers, user).write(vals) return True diff --git a/base_user_role/static/description/index.html b/base_user_role/static/description/index.html index 00038a4..4c59134 100644 --- a/base_user_role/static/description/index.html +++ b/base_user_role/static/description/index.html @@ -372,7 +372,7 @@

User roles

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:b3aa00f609d45dcd82a69b2a3e13b327096ab1d25883b543e3e45538e854d40c +!! source digest: sha256:99ab17708d54615c87b2764f44327b9a8908fbf188fdcfbd80b301b453c8207a !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Production/Stable License: LGPL-3 OCA/server-backend Translate me on Weblate Try me on Runboat

This module was written to extend the standard functionality regarding diff --git a/base_user_role/tests/test_user_role.py b/base_user_role/tests/test_user_role.py index 1033186..4b46c29 100644 --- a/base_user_role/tests/test_user_role.py +++ b/base_user_role/tests/test_user_role.py @@ -4,17 +4,16 @@ from odoo import fields from odoo.exceptions import AccessError +from odoo.fields import Command from odoo.tests import tagged -from odoo.tests.common import TransactionCase +from odoo.addons.base.tests.common import BaseCommon -class TestUserRole(TransactionCase): + +class TestUserRoleCommon(BaseCommon): @classmethod def setUpClass(cls): super().setUpClass() - cls.env = cls.env( - context=dict(cls.env.context, tracking_disable=True, no_reset_password=True) - ) cls.user_model = cls.env["res.users"] cls.role_model = cls.env["res.users.role"] cls.wiz_model = cls.env["wizard.groups.into.role"] @@ -59,7 +58,7 @@ def setUpClass(cls): # Setup for multi-company testing cls.multicompany_user_1 = cls.user_model.create( { - "name": "User 2", + "name": "multicompany_user_1", "company_id": cls.company1.id, "company_ids": [(6, 0, [cls.company1.id, cls.company2.id])], "groups_id": [(6, 0, cls.env.ref("base.group_erp_manager").ids)], @@ -68,7 +67,7 @@ def setUpClass(cls): ) cls.multicompany_user_2 = cls.user_model.create( { - "name": "User 2", + "name": "multicompany_user_2", "company_id": cls.company2.id, "company_ids": [(6, 0, [cls.company2.id])], "groups_id": [(6, 0, cls.env.ref("base.group_user").ids)], @@ -83,6 +82,8 @@ def setUpClass(cls): } ) + +class TestUserRole(TestUserRoleCommon): def test_role_1(self): self.user_id.write({"role_line_ids": [(0, 0, {"role_id": self.role1_id.id})]}) user_group_ids = sorted({group.id for group in self.user_id.groups_id}) @@ -232,18 +233,6 @@ def test_role_multicompany(self): ): role.read() - @tagged("-at_install", "post_install") - def test_notification_type_not_reset(self): - """Test that roles don't reset notification settings.""" - if self.env["ir.module.module"]._get("mail").state != "installed": - self.skipTest("Mail module is not installed.") - notification_group = self.env.ref("mail.group_mail_notification_type_inbox") - self.assertNotIn(notification_group, self.user_id.groups_id) - self.user_id.notification_type = "inbox" - self.assertIn(notification_group, self.user_id.groups_id) - self.user_id.write({"role_line_ids": [(0, 0, {"role_id": self.role1_id.id})]}) - self.assertIn(notification_group, self.user_id.groups_id) - def test_create_role_from_user(self): # Use a wizard instance to create a new role based on the user. # We use assign_to_user = False, as otherwise this module forcibly @@ -287,3 +276,60 @@ def test_group_groups_into_role(self): self.assertEqual(new_role.name, "Test Role") # Check that the role has the correct groups (even if the order is not equal) self.assertEqual(set(new_role.implied_ids.ids), set(user_group_ids)) + + +@tagged("post_install", "-at_install") +class TestUserRoleMail(TestUserRoleCommon): + def test_notification_type_not_reset(self): + """Test that roles don't reset notification settings.""" + if self.env["ir.module.module"]._get("mail").state != "installed": + self.skipTest("Mail module is not installed.") + notification_group = self.env.ref("mail.group_mail_notification_type_inbox") + self.assertNotIn(notification_group, self.user_id.groups_id) + self.user_id.notification_type = "inbox" + self.assertIn(notification_group, self.user_id.groups_id) + self.user_id.write( + {"role_line_ids": [Command.create({"role_id": self.role1_id.id})]} + ) + self.assertIn(notification_group, self.user_id.groups_id) + + def test_notification_type_reset(self): + """When user is demoted to share user, update notification settings. + + The issue only occurs when the writing user is not the superuser, and + if an intermittent flush is triggered by for instance + `_check_one_user_type` in website's res.users override. This triggers + constraint res_users_notification_type as Odoo has not yet recomputed + this at that point. + """ + # Notification settings depend on mail being installed + if self.env["ir.module.module"]._get("mail").state != "installed": + self.skipTest("Mail module is not installed.") + + # Set up a non-superuser user + admin_user = self.env["res.users"].create( + { + "name": "Some admin", + "login": "admin@example.com", + }, + ) + admin_user.groups_id += self.env.ref("base.group_system") + + self.user_id.write( + { + "role_line_ids": [Command.create({"role_id": self.role1_id.id})], + "notification_type": "inbox", + }, + ) + + # As non-superuser, delete all roles from the user + user = self.user_id.with_user(admin_user) + user.write( + { + "role_line_ids": [ + Command.delete(rl.id) for rl in self.user_id.role_line_ids + ], + }, + ) + # Database constraint has not been triggered + self.assertEqual(user.notification_type, "email") From 8b124dd8cc5275f846faf4ea9328cc1efc3de468 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Fri, 22 May 2026 08:43:47 +0000 Subject: [PATCH 04/21] [IMP] hr_attendance_flex_rest_time: reflect OCA changes --- .../static/description/index.html | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/hr_attendance_flex_rest_time/static/description/index.html b/hr_attendance_flex_rest_time/static/description/index.html index eed948d..371d6c7 100644 --- a/hr_attendance_flex_rest_time/static/description/index.html +++ b/hr_attendance_flex_rest_time/static/description/index.html @@ -8,10 +8,11 @@ /* :Author: David Goodger (goodger@python.org) -:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $ +:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $ :Copyright: This stylesheet has been placed in the public domain. Default cascading style sheet for the HTML output of Docutils. +Despite the name, some widely supported CSS2 features are used. See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to customize this style sheet. @@ -274,7 +275,7 @@ margin-left: 2em ; margin-right: 2em } -pre.code .ln { color: grey; } /* line numbers */ +pre.code .ln { color: gray; } /* line numbers */ pre.code, code { background-color: #eeeeee } pre.code .comment, code .comment { color: #5C6576 } pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } @@ -300,7 +301,7 @@ span.pre { white-space: pre } -span.problematic { +span.problematic, pre.problematic { color: red } span.section-subtitle { @@ -471,7 +472,9 @@

Contributors

Maintainers

This module is maintained by the OCA.

-Odoo Community Association + +Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

From e6b609bd01c3805557e8f5b60e821b04b93fdf3e Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Fri, 22 May 2026 08:43:47 +0000 Subject: [PATCH 05/21] [IMP] hr_attendance_manage_own: reflect OCA changes --- hr_attendance_manage_own/README.rst | 35 +++++++++++++------ hr_attendance_manage_own/readme/CONFIGURE.md | 2 +- .../readme/CONTRIBUTORS.md | 2 ++ .../static/description/index.html | 19 ++++++++-- 4 files changed, 43 insertions(+), 15 deletions(-) diff --git a/hr_attendance_manage_own/README.rst b/hr_attendance_manage_own/README.rst index 5012f19..d35d0e6 100644 --- a/hr_attendance_manage_own/README.rst +++ b/hr_attendance_manage_own/README.rst @@ -1,6 +1,6 @@ -============================ -HR Attendance Own Management -============================ +================================ +HR Attendance Manage Own Records +================================ .. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -41,11 +41,11 @@ This module introduces a "User: Manage own attendances" group that grants create and edit access to the user's own attendance records, with the following restrictions: -- Users cannot change the overtime status or extra hours of their - records. -- When the company's extra hours validation is set to "By Manager", - records that have been approved or refused cannot be modified or - deleted. +- Users cannot change the overtime status or extra hours of their + records. +- When the company's extra hours validation is set to "By Manager", + records that have been approved or refused cannot be modified or + deleted. **Table of contents** @@ -62,6 +62,17 @@ To configure this module: 3. Add users to this group to allow them to create and edit their own attendance records +Optionally, you can configure fields that bypass the processed-record +write restriction. This is useful when other modules (e.g. +``hr_timesheet_sheet``) need to write certain fields on attendance +records that have already been approved or refused. + +1. Go to *Settings > Technical > Parameters > System Parameters* +2. Create a parameter with key + ``hr_attendance_manage_own.write_bypass_fields`` +3. Set the value to a comma-separated list of field names (e.g. + ``sheet_id``) + Bug Tracker =========== @@ -83,10 +94,12 @@ Authors Contributors ------------ -- Quartile +- Quartile - - Tatsuki Kanda - - Aung Ko Ko Lin + - Tatsuki Kanda + - Aung Ko Ko Lin + - Yoshi Tashiro + - Sanami Koshita Maintainers ----------- diff --git a/hr_attendance_manage_own/readme/CONFIGURE.md b/hr_attendance_manage_own/readme/CONFIGURE.md index 60faba0..bf25d1a 100644 --- a/hr_attendance_manage_own/readme/CONFIGURE.md +++ b/hr_attendance_manage_own/readme/CONFIGURE.md @@ -11,4 +11,4 @@ refused. 1. Go to *Settings \> Technical \> Parameters \> System Parameters* 2. Create a parameter with key `hr_attendance_manage_own.write_bypass_fields` -3. Set the value to a comma-separated list of field names (e.g. `sheet_id`) +3. Set the value to a comma-separated list of field names (e.g. `sheet_id`) \ No newline at end of file diff --git a/hr_attendance_manage_own/readme/CONTRIBUTORS.md b/hr_attendance_manage_own/readme/CONTRIBUTORS.md index c1911de..c7ae06a 100644 --- a/hr_attendance_manage_own/readme/CONTRIBUTORS.md +++ b/hr_attendance_manage_own/readme/CONTRIBUTORS.md @@ -1,3 +1,5 @@ - Quartile \<\> - Tatsuki Kanda - Aung Ko Ko Lin + - Yoshi Tashiro + - Sanami Koshita \ No newline at end of file diff --git a/hr_attendance_manage_own/static/description/index.html b/hr_attendance_manage_own/static/description/index.html index 8ea350f..f37c519 100644 --- a/hr_attendance_manage_own/static/description/index.html +++ b/hr_attendance_manage_own/static/description/index.html @@ -3,7 +3,7 @@ -HR Attendance Own Management +HR Attendance Manage Own Records -
-

HR Attendance Own Management

+
+

HR Attendance Manage Own Records

Beta License: AGPL-3 OCA/timesheet Translate me on Weblate Try me on Runboat

This module extends the functionality of hr_timesheet_sheet and help From 7c3cb122fade470a8b82b63d1843a7b7d12765c5 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Fri, 22 May 2026 08:43:47 +0000 Subject: [PATCH 07/21] [IMP] hr_timesheet_sheet: reflect OCA changes --- hr_timesheet_sheet/README.rst | 2 +- hr_timesheet_sheet/__manifest__.py | 2 +- hr_timesheet_sheet/models/hr_timesheet_sheet.py | 2 +- hr_timesheet_sheet/static/description/index.html | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hr_timesheet_sheet/README.rst b/hr_timesheet_sheet/README.rst index 208b5af..2e0a989 100644 --- a/hr_timesheet_sheet/README.rst +++ b/hr_timesheet_sheet/README.rst @@ -11,7 +11,7 @@ HR Timesheet Sheet !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:148718bdb5717a22eca899c19a8abc9e2ae3b9945ee803f8a84768477eb6bc0f + !! source digest: sha256:93b65554582c0e5fb4eb757777a1f6cc35a751d26aa72ea64c691513915ab3bd !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/hr_timesheet_sheet/__manifest__.py b/hr_timesheet_sheet/__manifest__.py index cb5199e..4e1a757 100644 --- a/hr_timesheet_sheet/__manifest__.py +++ b/hr_timesheet_sheet/__manifest__.py @@ -6,7 +6,7 @@ { "name": "HR Timesheet Sheet", - "version": "18.0.1.3.1", + "version": "18.0.1.3.2", "category": "Human Resources", "sequence": 80, "summary": "Timesheet Sheets, Activities", diff --git a/hr_timesheet_sheet/models/hr_timesheet_sheet.py b/hr_timesheet_sheet/models/hr_timesheet_sheet.py index aa748a2..729753e 100644 --- a/hr_timesheet_sheet/models/hr_timesheet_sheet.py +++ b/hr_timesheet_sheet/models/hr_timesheet_sheet.py @@ -454,7 +454,7 @@ def _compute_available_task_ids(self): rec.available_task_ids = project_task_obj.search( [ ("project_id", "=", rec.add_line_project_id.id), - ("company_id", "=", rec.company_id.id), + ("company_id", "in", [rec.company_id.id, False]), ("id", "not in", rec.timesheet_ids.mapped("task_id").ids), ] ).ids diff --git a/hr_timesheet_sheet/static/description/index.html b/hr_timesheet_sheet/static/description/index.html index 3d6fb9a..49009b4 100644 --- a/hr_timesheet_sheet/static/description/index.html +++ b/hr_timesheet_sheet/static/description/index.html @@ -372,7 +372,7 @@

HR Timesheet Sheet

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:148718bdb5717a22eca899c19a8abc9e2ae3b9945ee803f8a84768477eb6bc0f +!! source digest: sha256:93b65554582c0e5fb4eb757777a1f6cc35a751d26aa72ea64c691513915ab3bd !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/timesheet Translate me on Weblate Try me on Runboat

This module supplies a new screen enabling you to manage your work From 407b5cc73a9312cfd14f3b53af19eb3c226a9853 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Fri, 22 May 2026 08:43:47 +0000 Subject: [PATCH 08/21] [IMP] l10n_jp_country_state: reflect OCA changes --- l10n_jp_country_state/README.rst | 11 ++- l10n_jp_country_state/__manifest__.py | 5 +- .../data/res.country.state.csv | 48 +++++++++ l10n_jp_country_state/i18n_extra/ja.po | 99 +++++++++---------- l10n_jp_country_state/readme/DESCRIPTION.md | 8 +- .../static/description/index.html | 11 ++- 6 files changed, 123 insertions(+), 59 deletions(-) create mode 100644 l10n_jp_country_state/data/res.country.state.csv diff --git a/l10n_jp_country_state/README.rst b/l10n_jp_country_state/README.rst index 782254a..3ba94e6 100644 --- a/l10n_jp_country_state/README.rst +++ b/l10n_jp_country_state/README.rst @@ -11,7 +11,7 @@ Japan Country States !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:b2117b837ac8bdbe84219bddad8c240ef6346fd60db9ee433b30c864a8e1380f + !! source digest: sha256:58dad6de76321726c50c6e037797585b0718dd19ac54915f6634338cc60222f4 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png @@ -32,8 +32,13 @@ Japan Country States |badge1| |badge2| |badge3| |badge4| |badge5| -This module only adds translations to country state records for Japan, -for the sake of convenience. +As of Odoo 17, the Japan prefecture (``res.country.state``) records are +shipped with their native Japanese names (e.g. ``東京都``, ``北海道``) +as the source value. This module overrides those records to use English +names (e.g. ``Tokyo``, ``Hokkaido``) as the source and provides Japanese +translations, so users with ``ja_JP`` selected continue to see the +native prefecture names while other users see the romanized English +names. **Table of contents** diff --git a/l10n_jp_country_state/__manifest__.py b/l10n_jp_country_state/__manifest__.py index db1b43b..598a39e 100644 --- a/l10n_jp_country_state/__manifest__.py +++ b/l10n_jp_country_state/__manifest__.py @@ -2,11 +2,14 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). { "name": "Japan Country States", - "version": "18.0.1.0.0", + "version": "18.0.1.1.0", "depends": ["base_country_state_translatable"], "author": "Quartile, Odoo Community Association (OCA)", "license": "AGPL-3", "website": "https://github.com/OCA/l10n-japan", "category": "Localization", + "data": [ + "data/res.country.state.csv", + ], "installable": True, } diff --git a/l10n_jp_country_state/data/res.country.state.csv b/l10n_jp_country_state/data/res.country.state.csv new file mode 100644 index 0000000..b506141 --- /dev/null +++ b/l10n_jp_country_state/data/res.country.state.csv @@ -0,0 +1,48 @@ +id,name +base.state_jp_jp-01,"Hokkaido" +base.state_jp_jp-02,"Aomori" +base.state_jp_jp-03,"Iwate" +base.state_jp_jp-04,"Miyagi" +base.state_jp_jp-05,"Akita" +base.state_jp_jp-06,"Yamagata" +base.state_jp_jp-07,"Fukushima" +base.state_jp_jp-08,"Ibaraki" +base.state_jp_jp-09,"Tochigi" +base.state_jp_jp-10,"Gunma" +base.state_jp_jp-11,"Saitama" +base.state_jp_jp-12,"Chiba" +base.state_jp_jp-13,"Tokyo" +base.state_jp_jp-14,"Kanagawa" +base.state_jp_jp-15,"Niigata" +base.state_jp_jp-16,"Toyama" +base.state_jp_jp-17,"Ishikawa" +base.state_jp_jp-18,"Fukui" +base.state_jp_jp-19,"Yamanashi" +base.state_jp_jp-20,"Nagano" +base.state_jp_jp-21,"Gifu" +base.state_jp_jp-22,"Shizuoka" +base.state_jp_jp-23,"Aichi" +base.state_jp_jp-24,"Mie" +base.state_jp_jp-25,"Shiga" +base.state_jp_jp-26,"Kyoto" +base.state_jp_jp-27,"Osaka" +base.state_jp_jp-28,"Hyogo" +base.state_jp_jp-29,"Nara" +base.state_jp_jp-30,"Wakayama" +base.state_jp_jp-31,"Tottori" +base.state_jp_jp-32,"Shimane" +base.state_jp_jp-33,"Okayama" +base.state_jp_jp-34,"Hiroshima" +base.state_jp_jp-35,"Yamaguchi" +base.state_jp_jp-36,"Tokushima" +base.state_jp_jp-37,"Kagawa" +base.state_jp_jp-38,"Ehime" +base.state_jp_jp-39,"Kochi" +base.state_jp_jp-40,"Fukuoka" +base.state_jp_jp-41,"Saga" +base.state_jp_jp-42,"Nagasaki" +base.state_jp_jp-43,"Kumamoto" +base.state_jp_jp-44,"Oita" +base.state_jp_jp-45,"Miyazaki" +base.state_jp_jp-46,"Kagoshima" +base.state_jp_jp-47,"Okinawa" diff --git a/l10n_jp_country_state/i18n_extra/ja.po b/l10n_jp_country_state/i18n_extra/ja.po index f8bfa5c..a043e48 100644 --- a/l10n_jp_country_state/i18n_extra/ja.po +++ b/l10n_jp_country_state/i18n_extra/ja.po @@ -1,12 +1,11 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: +# * l10n_jp_country_state # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 18.0+e\n" +"Project-Id-Version: Odoo Server 17.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-06-06 06:15+0000\n" -"PO-Revision-Date: 2025-06-06 06:15+0000\n" "Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -14,237 +13,237 @@ msgstr "" "Content-Transfer-Encoding: \n" "Plural-Forms: \n" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-01 msgid "Hokkaido" msgstr "北海道" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-02 msgid "Aomori" msgstr "青森県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-03 msgid "Iwate" msgstr "岩手県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-04 msgid "Miyagi" msgstr "宮城県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-05 msgid "Akita" msgstr "秋田県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-06 msgid "Yamagata" msgstr "山形県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-07 msgid "Fukushima" msgstr "福島県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-08 msgid "Ibaraki" msgstr "茨城県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-09 msgid "Tochigi" msgstr "栃木県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-10 msgid "Gunma" msgstr "群馬県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-11 msgid "Saitama" msgstr "埼玉県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-12 msgid "Chiba" msgstr "千葉県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-13 msgid "Tokyo" msgstr "東京都" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-14 msgid "Kanagawa" msgstr "神奈川県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-15 msgid "Niigata" msgstr "新潟県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-16 msgid "Toyama" msgstr "富山県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-17 msgid "Ishikawa" msgstr "石川県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-18 msgid "Fukui" msgstr "福井県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-19 msgid "Yamanashi" msgstr "山梨県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-20 msgid "Nagano" msgstr "長野県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-21 msgid "Gifu" msgstr "岐阜県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-22 msgid "Shizuoka" msgstr "静岡県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-23 msgid "Aichi" msgstr "愛知県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-24 msgid "Mie" msgstr "三重県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-25 msgid "Shiga" msgstr "滋賀県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-26 msgid "Kyoto" msgstr "京都府" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-27 msgid "Osaka" msgstr "大阪府" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-28 msgid "Hyogo" msgstr "兵庫県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-29 msgid "Nara" msgstr "奈良県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-30 msgid "Wakayama" msgstr "和歌山県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-31 msgid "Tottori" msgstr "鳥取県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-32 msgid "Shimane" msgstr "島根県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-33 msgid "Okayama" msgstr "岡山県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-34 msgid "Hiroshima" msgstr "広島県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-35 msgid "Yamaguchi" msgstr "山口県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-36 msgid "Tokushima" msgstr "徳島県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-37 msgid "Kagawa" msgstr "香川県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-38 msgid "Ehime" msgstr "愛媛県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-39 msgid "Kochi" msgstr "高知県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-40 msgid "Fukuoka" msgstr "福岡県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-41 msgid "Saga" msgstr "佐賀県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-42 msgid "Nagasaki" msgstr "長崎県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-43 msgid "Kumamoto" msgstr "熊本県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-44 msgid "Oita" msgstr "大分県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-45 msgid "Miyazaki" msgstr "宮崎県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-46 msgid "Kagoshima" msgstr "鹿児島県" -#. module: base +#. module: l10n_jp_country_state #: model:res.country.state,name:base.state_jp_jp-47 msgid "Okinawa" msgstr "沖縄県" diff --git a/l10n_jp_country_state/readme/DESCRIPTION.md b/l10n_jp_country_state/readme/DESCRIPTION.md index 24b8c64..cd5d653 100644 --- a/l10n_jp_country_state/readme/DESCRIPTION.md +++ b/l10n_jp_country_state/readme/DESCRIPTION.md @@ -1,2 +1,6 @@ -This module only adds translations to country state records for Japan, -for the sake of convenience. +As of Odoo 17, the Japan prefecture (`res.country.state`) records are +shipped with their native Japanese names (e.g. `東京都`, `北海道`) as the +source value. This module overrides those records to use English names +(e.g. `Tokyo`, `Hokkaido`) as the source and provides Japanese +translations, so users with `ja_JP` selected continue to see the native +prefecture names while other users see the romanized English names. diff --git a/l10n_jp_country_state/static/description/index.html b/l10n_jp_country_state/static/description/index.html index 41a220f..c9d0da7 100644 --- a/l10n_jp_country_state/static/description/index.html +++ b/l10n_jp_country_state/static/description/index.html @@ -372,11 +372,16 @@

Japan Country States

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:b2117b837ac8bdbe84219bddad8c240ef6346fd60db9ee433b30c864a8e1380f +!! source digest: sha256:58dad6de76321726c50c6e037797585b0718dd19ac54915f6634338cc60222f4 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/l10n-japan Translate me on Weblate Try me on Runboat

-

This module only adds translations to country state records for Japan, -for the sake of convenience.

+

As of Odoo 17, the Japan prefecture (res.country.state) records are +shipped with their native Japanese names (e.g. 東京都, 北海道) +as the source value. This module overrides those records to use English +names (e.g. Tokyo, Hokkaido) as the source and provides Japanese +translations, so users with ja_JP selected continue to see the +native prefecture names while other users see the romanized English +names.

Table of contents

    From 47e98de0ce05bc0bef5dedc00889f043a99b91b6 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Fri, 22 May 2026 08:43:47 +0000 Subject: [PATCH 09/21] [IMP] mrp_stock_move_actual_date: reflect OCA changes --- mrp_stock_move_actual_date/README.rst | 19 +++++++----- .../readme/DESCRIPTION.md | 5 +-- .../static/description/index.html | 31 ++++++++++++------- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/mrp_stock_move_actual_date/README.rst b/mrp_stock_move_actual_date/README.rst index 67176ca..7b7ed2f 100644 --- a/mrp_stock_move_actual_date/README.rst +++ b/mrp_stock_move_actual_date/README.rst @@ -1,3 +1,7 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + ========================== MRP Stock Move Actual Date ========================== @@ -7,13 +11,13 @@ MRP Stock Move Actual Date !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:fbff73f2ce169a24ce6474eafe8a24eac98dd7fac68599d9156f35dbe373227f + !! source digest: sha256:1dcb9b715cd08a745eb3f857886a8dd7dd04eb2254043e96b41245003852e5a7 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github @@ -28,8 +32,9 @@ MRP Stock Move Actual Date |badge1| |badge2| |badge3| |badge4| |badge5| -This module adopts the functionality of stock_move_actual_date to -manufacturing orders, unbuild orders, and scraps. +This module extends the Actual Date functionality provided by +`stock_move_actual_date `__ +to manufacturing orders, unbuild orders, and scraps. **Table of contents** @@ -57,10 +62,10 @@ Authors Contributors ------------ -- `Quartile `__: +- `Quartile `__: - - Aung Ko Ko Lin - - Yoshi Tashiro + - Aung Ko Ko Lin + - Yoshi Tashiro Maintainers ----------- diff --git a/mrp_stock_move_actual_date/readme/DESCRIPTION.md b/mrp_stock_move_actual_date/readme/DESCRIPTION.md index 678b155..d2b29fe 100644 --- a/mrp_stock_move_actual_date/readme/DESCRIPTION.md +++ b/mrp_stock_move_actual_date/readme/DESCRIPTION.md @@ -1,2 +1,3 @@ -This module adopts the functionality of stock_move_actual_date to -manufacturing orders, unbuild orders, and scraps. +This module extends the Actual Date functionality provided by +[stock_move_actual_date](https://github.com/OCA/stock-logistics-workflow/tree/18.0/stock_move_actual_date#readme) +to manufacturing orders, unbuild orders, and scraps. diff --git a/mrp_stock_move_actual_date/static/description/index.html b/mrp_stock_move_actual_date/static/description/index.html index 769105c..c73684e 100644 --- a/mrp_stock_move_actual_date/static/description/index.html +++ b/mrp_stock_move_actual_date/static/description/index.html @@ -3,7 +3,7 @@ -MRP Stock Move Actual Date +README.rst -
    -

    MRP Stock Move Actual Date

    +
    + + +Odoo Community Association + +
    +

    MRP Stock Move Actual Date

    -

    Beta License: AGPL-3 OCA/manufacture Translate me on Weblate Try me on Runboat

    -

    This module adopts the functionality of stock_move_actual_date to -manufacturing orders, unbuild orders, and scraps.

    +

    Beta License: AGPL-3 OCA/manufacture Translate me on Weblate Try me on Runboat

    +

    This module extends the Actual Date functionality provided by +stock_move_actual_date +to manufacturing orders, unbuild orders, and scraps.

    Table of contents

      @@ -385,7 +391,7 @@

      MRP Stock Move Actual Date

    -

    Bug Tracker

    +

    Bug Tracker

    Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -393,15 +399,15 @@

    Bug Tracker

    Do not contact contributors directly about support or help with technical issues.

    -

    Credits

    +

    Credits

    -

    Authors

    +

    Authors

    • Quartile
    -

    Maintainers

    +

    Maintainers

    This module is maintained by the OCA.

    Odoo Community Association @@ -429,5 +435,6 @@

    Maintainers

    +
    From f2fcc14adc380cb8314cc36e2b980812fb1a5df4 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Fri, 22 May 2026 08:43:47 +0000 Subject: [PATCH 10/21] [IMP] product_tag_view: reflect OCA changes --- product_tag_view/README.rst | 12 +++++--- product_tag_view/static/description/icon.png | Bin 0 -> 10254 bytes .../static/description/index.html | 26 +++++++++++------- 3 files changed, 24 insertions(+), 14 deletions(-) create mode 100644 product_tag_view/static/description/icon.png diff --git a/product_tag_view/README.rst b/product_tag_view/README.rst index c5f680d..f4e4087 100644 --- a/product_tag_view/README.rst +++ b/product_tag_view/README.rst @@ -1,3 +1,7 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + ================ Product Tag View ================ @@ -7,13 +11,13 @@ Product Tag View !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:31f99642e6eaa5c747aea823c87ee70812806c662fb663b82d6995920a03f524 + !! source digest: sha256:b961914735e55f705855d3fae8ea980650e31862f0d618a02792a3551ab67fbf !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fproduct--attribute-lightgray.png?logo=github @@ -60,9 +64,9 @@ Authors Contributors ------------ -- `Quartile `__: +- `Quartile `__: - - Aung Ko Ko Lin + - Aung Ko Ko Lin Maintainers ----------- diff --git a/product_tag_view/static/description/icon.png b/product_tag_view/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1dcc49c24f364e9adf0afbc6fc0bac6dbecdeb11 GIT binary patch literal 10254 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 -Product Tag View +README.rst -
    -

    Product Tag View

    +
    + + +Odoo Community Association + +
    +

    Product Tag View

    -

    Beta License: AGPL-3 OCA/product-attribute Translate me on Weblate Try me on Runboat

    +

    Beta License: AGPL-3 OCA/product-attribute Translate me on Weblate Try me on Runboat

    This module adds the Tags (product_tag_ids) field to the General Information tab on the product template and product variant forms. In standard Odoo, tags are shown under the Sales tab; since that tab is @@ -388,7 +393,7 @@

    Product Tag View

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -396,15 +401,15 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Quartile
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -428,5 +433,6 @@

Maintainers

+
From f759e92658cec2980409b3b4ebb5119eec2f1739 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Fri, 22 May 2026 08:43:47 +0000 Subject: [PATCH 11/21] [IMP] product_tier_validation: reflect OCA changes --- product_tier_validation/README.rst | 8 +++-- .../static/description/index.html | 30 +++++++++++-------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/product_tier_validation/README.rst b/product_tier_validation/README.rst index 4f483ba..d9d4e11 100644 --- a/product_tier_validation/README.rst +++ b/product_tier_validation/README.rst @@ -1,3 +1,7 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + ======================= Product Tier Validation ======================= @@ -7,13 +11,13 @@ Product Tier Validation !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:885900e1a54767980b2445d0f2899260bf3c1ee6949ebb71d637d9a99ae3c621 + !! source digest: sha256:bbedc7f58c2fd0be654b348e29bd33fb90f048595e38c5b8a83f03b6adb40163 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fproduct--attribute-lightgray.png?logo=github diff --git a/product_tier_validation/static/description/index.html b/product_tier_validation/static/description/index.html index f8598cb..acd37eb 100644 --- a/product_tier_validation/static/description/index.html +++ b/product_tier_validation/static/description/index.html @@ -3,7 +3,7 @@ -Product Tier Validation +README.rst -
-

Product Tier Validation

+
+ + +Odoo Community Association + +
+

Product Tier Validation

-

Beta License: AGPL-3 OCA/product-attribute Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/product-attribute Translate me on Weblate Try me on Runboat

Adds an approval workflow to Products. The default rule requires new Product to be approved before they can be used.

For this, the new Product record is kept as “Archived” until it is @@ -389,12 +394,12 @@

Product Tier Validation

-

Installation

+

Installation

This module depends on base_tier_validation. You can find it at OCA/server-ux

-

Usage

+

Usage

A regular user creates a new Product and sends it for approval:

  1. Create a Product triggering at least one “Tier Definition”. Product @@ -416,7 +421,7 @@

    Usage

    could be a future improvement.

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -424,15 +429,15 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Open Source Integrators
-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -457,5 +462,6 @@

Maintainers

+
From ec5d962f26b9a712c3bee9fe50c60de2f9c42fe8 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Fri, 22 May 2026 08:43:47 +0000 Subject: [PATCH 12/21] [IMP] project_task_default_stage: reflect OCA changes --- project_task_default_stage/README.rst | 8 ++++-- project_task_default_stage/__manifest__.py | 2 +- .../data/project_data.xml | 8 ++++++ .../migrations/18.0.1.0.1/post-migration.py | 19 +++++++++++++ .../static/description/index.html | 28 +++++++++++-------- 5 files changed, 51 insertions(+), 14 deletions(-) create mode 100644 project_task_default_stage/migrations/18.0.1.0.1/post-migration.py diff --git a/project_task_default_stage/README.rst b/project_task_default_stage/README.rst index 05ac59b..342726f 100644 --- a/project_task_default_stage/README.rst +++ b/project_task_default_stage/README.rst @@ -1,3 +1,7 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + ========================== Project Task Default Stage ========================== @@ -7,13 +11,13 @@ Project Task Default Stage !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:5aff01d38fa5ec9f8c3f15740d4a504fbda95d4e2cb95458c98b20ccf582073a + !! source digest: sha256:3fefbf82ca334046fdb8853dd05417ecdec4f67c3dd58ef29b4b9073b40587e9 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fproject-lightgray.png?logo=github diff --git a/project_task_default_stage/__manifest__.py b/project_task_default_stage/__manifest__.py index 06c840a..f310d3c 100644 --- a/project_task_default_stage/__manifest__.py +++ b/project_task_default_stage/__manifest__.py @@ -5,7 +5,7 @@ { "name": "Project Task Default Stage", "summary": "Recovery default task stages for projects from v8", - "version": "18.0.1.0.0", + "version": "18.0.1.0.1", "category": "Project", "author": "Tecnativa, Odoo Community Association (OCA)", "website": "https://github.com/OCA/project", diff --git a/project_task_default_stage/data/project_data.xml b/project_task_default_stage/data/project_data.xml index 52b5d95..c086cf6 100644 --- a/project_task_default_stage/data/project_data.xml +++ b/project_task_default_stage/data/project_data.xml @@ -5,43 +5,51 @@ 10 Analysis + 20 Specification + 30 Design + 40 Development + 50 Testing + 60 Merge + 70 Done + 80 Cancelled + diff --git a/project_task_default_stage/migrations/18.0.1.0.1/post-migration.py b/project_task_default_stage/migrations/18.0.1.0.1/post-migration.py new file mode 100644 index 0000000..926702e --- /dev/null +++ b/project_task_default_stage/migrations/18.0.1.0.1/post-migration.py @@ -0,0 +1,19 @@ +from openupgradelib import openupgrade + + +@openupgrade.migrate() +def migrate(env, version): + xml_ids = [ + "project_task_default_stage.project_tt_analysis", + "project_task_default_stage.project_tt_specification", + "project_task_default_stage.project_tt_design", + "project_task_default_stage.project_tt_development", + "project_task_default_stage.project_tt_testing", + "project_task_default_stage.project_tt_merge", + "project_task_default_stage.project_tt_deployment", + "project_task_default_stage.project_tt_cancel", + ] + for xml_id in xml_ids: + task_type = env.ref(xml_id, raise_if_not_found=False) + if task_type: + task_type.user_id = False diff --git a/project_task_default_stage/static/description/index.html b/project_task_default_stage/static/description/index.html index d528d12..e937870 100644 --- a/project_task_default_stage/static/description/index.html +++ b/project_task_default_stage/static/description/index.html @@ -3,7 +3,7 @@ -Project Task Default Stage +README.rst -
-

Project Task Default Stage

+
+ + +Odoo Community Association + +
+

Project Task Default Stage

-

Beta License: AGPL-3 OCA/project Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/project Translate me on Weblate Try me on Runboat

This module restores the case_default fields to Project Tasks and project stage tab, removed in Odoo 9.0 and later.

Table of contents

@@ -386,13 +391,13 @@

Project Task Default Stage

-

Configuration

+

Configuration

After installing this module you can assign stage tasks in project stage tab and you can check the default stages for new projects as previously done in Odoo 8.0.

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -400,15 +405,15 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Tecnativa
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -434,5 +439,6 @@

Maintainers

+
From f095929d3a8e3584f400f13b11e2b498ee5b2b66 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Fri, 22 May 2026 08:43:47 +0000 Subject: [PATCH 13/21] [IMP] purchase_order_secondary_unit: reflect OCA changes --- purchase_order_secondary_unit/README.rst | 55 ++++------- purchase_order_secondary_unit/__manifest__.py | 4 +- .../readme/CONFIGURE.md | 14 --- .../reports/purchase_order_templates.xml | 52 +--------- .../reports/purchase_quotation_templates.xml | 47 +-------- .../purchase_order_secondary_unit_groups.xml | 9 -- .../static/description/index.html | 71 ++++++-------- .../views/purchase_order_portal_templates.xml | 95 ------------------- 8 files changed, 57 insertions(+), 290 deletions(-) delete mode 100644 purchase_order_secondary_unit/security/purchase_order_secondary_unit_groups.xml delete mode 100644 purchase_order_secondary_unit/views/purchase_order_portal_templates.xml diff --git a/purchase_order_secondary_unit/README.rst b/purchase_order_secondary_unit/README.rst index f0dc232..e321456 100644 --- a/purchase_order_secondary_unit/README.rst +++ b/purchase_order_secondary_unit/README.rst @@ -1,3 +1,7 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + ============================= Purchase Order Secondary Unit ============================= @@ -7,13 +11,13 @@ Purchase Order Secondary Unit !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:2b71909b287e9e5c437aea172c4c165ed1bee7b3e981d1c44d99c8bfaae5740d + !! source digest: sha256:931f6f1aa647d4627f4c32d5a657bb5fcbfef63f595c2c84f0790ff27a44ee7d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpurchase--workflow-lightgray.png?logo=github @@ -50,23 +54,6 @@ For configuration of displaying secondary unit information in purchase reports and the Purchase Order portal, see the guidelines provided in product_secondary_unit. -Settings Visibility -------------------- - -When installing this module, all internal users are automatically added -to the ``product_secondary_unit.group_purchase_secondary_unit`` security -group. This makes the Purchase-related "Hide Secondary Qty Column" and -"Secondary Unit Price Display" settings visible in **Settings > Units of -Measure**. - -If you installed this module before these report presentation settings -were introduced in ``product_secondary_unit``, users may not see these -configuration options. To fix this: - -1. Go to **Settings > Users & Companies > Groups** -2. Search for "Purchase Secondary Unit" -3. Add the relevant users to that group - Usage ===== @@ -81,13 +68,13 @@ To use this module you need to: **Vendor Pricelist Integration** -- When adding a vendor to a product's pricelist (via *Purchase tab > - Vendors*), the secondary unit of measure is automatically defaulted - from the product variant's purchase secondary UOM, or from the - product template if not set on the variant. -- When a new vendor pricelist record is created from purchase order - confirmation, the secondary UOM from the purchase order line is - automatically stored in the vendor pricelist entry. +- When adding a vendor to a product's pricelist (via *Purchase tab > + Vendors*), the secondary unit of measure is automatically defaulted + from the product variant's purchase secondary UOM, or from the product + template if not set on the variant. +- When a new vendor pricelist record is created from purchase order + confirmation, the secondary UOM from the purchase order line is + automatically stored in the vendor pricelist entry. Known issues / Roadmap ====================== @@ -118,17 +105,17 @@ Authors Contributors ------------ -- `Tecnativa `__: +- `Tecnativa `__: - - Sergio Teruel - - Ernesto Tejeda + - Sergio Teruel + - Ernesto Tejeda -- Nikul Chaudhary -- Pimolnat Suntian -- Miguel Ángel Gómez -- `Quartile `__: +- Nikul Chaudhary +- Pimolnat Suntian +- Miguel Ángel Gómez +- `Quartile `__: - - Yoshi Tashiro + - Yoshi Tashiro Maintainers ----------- diff --git a/purchase_order_secondary_unit/__manifest__.py b/purchase_order_secondary_unit/__manifest__.py index 7368f53..04badf6 100644 --- a/purchase_order_secondary_unit/__manifest__.py +++ b/purchase_order_secondary_unit/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Purchase Order Secondary Unit", "summary": "Purchase product in a secondary unit", - "version": "18.0.1.2.1", + "version": "18.0.1.2.2", "development_status": "Beta", "category": "Purchase", "website": "https://github.com/OCA/purchase-workflow", @@ -14,10 +14,8 @@ "auto_install": True, "depends": ["purchase", "product_secondary_unit"], "data": [ - "security/purchase_order_secondary_unit_groups.xml", "views/product_views.xml", "views/product_supplierinfo_views.xml", - "views/purchase_order_portal_templates.xml", "views/purchase_order_views.xml", "reports/purchase_order_templates.xml", "reports/purchase_quotation_templates.xml", diff --git a/purchase_order_secondary_unit/readme/CONFIGURE.md b/purchase_order_secondary_unit/readme/CONFIGURE.md index d50c70c..2ba195b 100644 --- a/purchase_order_secondary_unit/readme/CONFIGURE.md +++ b/purchase_order_secondary_unit/readme/CONFIGURE.md @@ -1,16 +1,2 @@ For configuration of displaying secondary unit information in purchase reports and the Purchase Order portal, see the guidelines provided in product_secondary_unit. - -## Settings Visibility - -When installing this module, all internal users are automatically added to the -`product_secondary_unit.group_purchase_secondary_unit` security group. This makes -the Purchase-related "Hide Secondary Qty Column" and "Secondary Unit Price Display" -settings visible in **Settings > Units of Measure**. - -If you installed this module before these report presentation settings were introduced -in `product_secondary_unit`, users may not see these configuration options. To fix this: - -1. Go to **Settings > Users & Companies > Groups** -2. Search for "Purchase Secondary Unit" -3. Add the relevant users to that group diff --git a/purchase_order_secondary_unit/reports/purchase_order_templates.xml b/purchase_order_secondary_unit/reports/purchase_order_templates.xml index 86425de..b79d2fc 100644 --- a/purchase_order_secondary_unit/reports/purchase_order_templates.xml +++ b/purchase_order_secondary_unit/reports/purchase_order_templates.xml @@ -6,64 +6,16 @@ > - + Second Qty - + - - - line.get_secondary_uom_display_mode() != 'secondary' - - - line.get_secondary_uom_display_mode() != 'secondary' - - - - - - - - - - - -
- - ( - ) - -
-
- - - line.get_secondary_uom_display_mode() != 'secondary' - - - - diff --git a/purchase_order_secondary_unit/reports/purchase_quotation_templates.xml b/purchase_order_secondary_unit/reports/purchase_quotation_templates.xml index 41661d3..ef64f13 100644 --- a/purchase_order_secondary_unit/reports/purchase_quotation_templates.xml +++ b/purchase_order_secondary_unit/reports/purchase_quotation_templates.xml @@ -4,57 +4,18 @@ id="report_purchasequotation_document" inherit_id="purchase.report_purchasequotation_document" > - + - + Second Qty - + - + - - - order_line.get_secondary_uom_display_mode() != 'secondary' - - - order_line.get_secondary_uom_display_mode() != 'secondary' - - - - - - - - - - - -
- - ( - ) - -
-
diff --git a/purchase_order_secondary_unit/security/purchase_order_secondary_unit_groups.xml b/purchase_order_secondary_unit/security/purchase_order_secondary_unit_groups.xml deleted file mode 100644 index 3467b8c..0000000 --- a/purchase_order_secondary_unit/security/purchase_order_secondary_unit_groups.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/purchase_order_secondary_unit/static/description/index.html b/purchase_order_secondary_unit/static/description/index.html index 27a815c..21fc85d 100644 --- a/purchase_order_secondary_unit/static/description/index.html +++ b/purchase_order_secondary_unit/static/description/index.html @@ -3,7 +3,7 @@ -Purchase Order Secondary Unit +README.rst -
-

Purchase Order Secondary Unit

+
+ + +Odoo Community Association + +
+

Purchase Order Secondary Unit

-

Beta License: AGPL-3 OCA/purchase-workflow Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/purchase-workflow Translate me on Weblate Try me on Runboat

This module extends the functionality of purchase orders to allow buy products in secondary unit of distinct category.

Users can enter quantities and prices in secondary units on purchase @@ -380,45 +385,26 @@

Purchase Order Secondary Unit

Table of contents

-

Configuration

+

Configuration

For configuration of displaying secondary unit information in purchase reports and the Purchase Order portal, see the guidelines provided in product_secondary_unit.

-
-

Settings Visibility

-

When installing this module, all internal users are automatically added -to the product_secondary_unit.group_purchase_secondary_unit security -group. This makes the Purchase-related “Hide Secondary Qty Column” and -“Secondary Unit Price Display” settings visible in Settings > Units of -Measure.

-

If you installed this module before these report presentation settings -were introduced in product_secondary_unit, users may not see these -configuration options. To fix this:

-
    -
  1. Go to Settings > Users & Companies > Groups
  2. -
  3. Search for “Purchase Secondary Unit”
  4. -
  5. Add the relevant users to that group
  6. -
-
-

Usage

+

Usage

To use this module you need to:

  1. Go to a Product > General Information tab.
  2. @@ -432,22 +418,22 @@

    Usage

    • When adding a vendor to a product’s pricelist (via Purchase tab > Vendors), the secondary unit of measure is automatically defaulted -from the product variant’s purchase secondary UOM, or from the -product template if not set on the variant.
    • +from the product variant’s purchase secondary UOM, or from the product +template if not set on the variant.
    • When a new vendor pricelist record is created from purchase order confirmation, the secondary UOM from the purchase order line is automatically stored in the vendor pricelist entry.
-

Known issues / Roadmap

+

Known issues / Roadmap

Updating existing vendor pricelist records from purchase order confirmation does not currently support secondary UOM or secondary UOM pricing. This is not included in the current scope and may be considered in future improvements.

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -455,15 +441,15 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Tecnativa
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -493,5 +479,6 @@

Maintainers

+
diff --git a/purchase_order_secondary_unit/views/purchase_order_portal_templates.xml b/purchase_order_secondary_unit/views/purchase_order_portal_templates.xml deleted file mode 100644 index 3f01e80..0000000 --- a/purchase_order_secondary_unit/views/purchase_order_portal_templates.xml +++ /dev/null @@ -1,95 +0,0 @@ - - - - - From 865869cffb9d222a9cba22b9c72c3161f9023c90 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Fri, 22 May 2026 08:43:47 +0000 Subject: [PATCH 14/21] [IMP] purchase_stock_secondary_unit: reflect OCA changes --- purchase_stock_secondary_unit/README.rst | 8 ++++-- .../static/description/index.html | 28 +++++++++++-------- .../test_purchase_stock_secondary_unit.py | 4 --- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/purchase_stock_secondary_unit/README.rst b/purchase_stock_secondary_unit/README.rst index 4ef7deb..d582c64 100644 --- a/purchase_stock_secondary_unit/README.rst +++ b/purchase_stock_secondary_unit/README.rst @@ -1,3 +1,7 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + ============================= Purchase Stock Secondary Unit ============================= @@ -7,13 +11,13 @@ Purchase Stock Secondary Unit !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:2eccf2612961c4c83d2ebf9692d3cc88d112d4f13f57f65111a5aea6180cec62 + !! source digest: sha256:274a3f09d32b7a025612a58f61b0446cfd9b5d71e992acbe0ae21a6dab41fa3c !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status :alt: Beta -.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpurchase--workflow-lightgray.png?logo=github diff --git a/purchase_stock_secondary_unit/static/description/index.html b/purchase_stock_secondary_unit/static/description/index.html index 1678a9d..dc089ba 100644 --- a/purchase_stock_secondary_unit/static/description/index.html +++ b/purchase_stock_secondary_unit/static/description/index.html @@ -3,7 +3,7 @@ -Purchase Stock Secondary Unit +README.rst -
-

Purchase Stock Secondary Unit

+
+ + +Odoo Community Association + +
+

Purchase Stock Secondary Unit

-

Beta License: AGPL-3 OCA/purchase-workflow Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/purchase-workflow Translate me on Weblate Try me on Runboat

This module extends the functionality of stock secondary unit module and purchase_order_secondary_unit to allow copy secondary purchase data to stock move when a purchase order is confirmed.

@@ -387,7 +392,7 @@

Purchase Stock Secondary Unit

-

Usage

+

Usage

To use this module you need to:

  1. Go to a Product > General Information tab.
  2. @@ -401,7 +406,7 @@

    Usage

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -409,15 +414,15 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • Jarsa
-

Contributors

+

Contributors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -444,5 +449,6 @@

Maintainers

+
diff --git a/purchase_stock_secondary_unit/tests/test_purchase_stock_secondary_unit.py b/purchase_stock_secondary_unit/tests/test_purchase_stock_secondary_unit.py index 6910de1..ac0b49c 100644 --- a/purchase_stock_secondary_unit/tests/test_purchase_stock_secondary_unit.py +++ b/purchase_stock_secondary_unit/tests/test_purchase_stock_secondary_unit.py @@ -1,14 +1,10 @@ # Copyright 2020 Jarsa Sistemas # Copyright 2021 Tecnativa - Sergio Teruel # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). -import logging - from odoo.tests import Form from odoo.addons.base.tests.common import BaseCommon -_logger = logging.getLogger(__name__) - class TestPurchaseStockSecondaryUnit(BaseCommon): @classmethod From 9bd947e870b892ceb31e9f46faca8f960c4c2d36 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Fri, 22 May 2026 08:43:47 +0000 Subject: [PATCH 15/21] [IMP] report_qweb_field_option: reflect OCA changes --- report_qweb_field_option/README.rst | 5 +- report_qweb_field_option/__manifest__.py | 2 +- .../models/qweb_field_options.py | 50 ++++++++++++++++++- report_qweb_field_option/readme/CONFIGURE.md | 3 ++ .../static/description/index.html | 5 +- .../tests/test_report_qweb_field_options.py | 36 +++++++++++++ .../views/qweb_field_options_views.xml | 7 +++ 7 files changed, 104 insertions(+), 4 deletions(-) diff --git a/report_qweb_field_option/README.rst b/report_qweb_field_option/README.rst index ee4a44c..0905565 100644 --- a/report_qweb_field_option/README.rst +++ b/report_qweb_field_option/README.rst @@ -11,7 +11,7 @@ Report Qweb Field Option !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:6a839e0b8361541500cea7946ac9d7bfbcbe37ab2a102576061a2940c4343c5c + !! source digest: sha256:2e6250e04e346bc7f61b36e4fb244e8f744f480b1e42e0ba753a71342ebd648f !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png @@ -52,6 +52,9 @@ For each record: - Set **Model** and **Field** (required) - Set **UoM** and **UoM Field**, or **Currency** and **Currency Field** only for fields of float type (optional) +- Set **Domain** to specify a domain for more specific filtering (e.g., + ``[('secondary_uom_id', '=', 1)]`` to apply only when a specific + secondary UoM is used) (optional) - Set **Company** (optional) - Set **Options** as a string representation of a dictionary. E.g., ``{"widget": "date"}``, ``{"widget": "monetary"}``, or diff --git a/report_qweb_field_option/__manifest__.py b/report_qweb_field_option/__manifest__.py index 10d68fd..e88ab83 100644 --- a/report_qweb_field_option/__manifest__.py +++ b/report_qweb_field_option/__manifest__.py @@ -2,7 +2,7 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). { "name": "Report Qweb Field Option", - "version": "18.0.1.0.1", + "version": "18.0.1.1.0", "category": "Technical Settings", "license": "AGPL-3", "author": "Quartile, Odoo Community Association (OCA)", diff --git a/report_qweb_field_option/models/qweb_field_options.py b/report_qweb_field_option/models/qweb_field_options.py index 91c4b35..2dc6251 100644 --- a/report_qweb_field_option/models/qweb_field_options.py +++ b/report_qweb_field_option/models/qweb_field_options.py @@ -4,8 +4,10 @@ import ast import logging -from odoo import _, api, fields, models +from odoo import _, api, fields, models, tools from odoo.exceptions import ValidationError +from odoo.osv.expression import normalize_domain +from odoo.tools.safe_eval import safe_eval _logger = logging.getLogger(__name__) @@ -52,6 +54,14 @@ class QwebFieldOptions(models.Model): ) digits = fields.Integer() company_id = fields.Many2one("res.company", string="Company") + domain = fields.Char( + help="Optional domain for additional filtering conditions.\n" + "This is evaluated in addition to UoM/Currency conditions.\n" + "Examples:\n" + "[('secondary_uom_id', '=', 1)]\n" + "[('secondary_uom_id.name', '=', 'Box')]\n" + "[('state', 'in', ['sale', 'done'])]", + ) @api.constrains("field_options") def _check_field_options_format(self): @@ -74,6 +84,28 @@ def _check_field_options_format(self): _("Options must be a dictionary, but got %s") % type(field_options) ) + def _get_eval_context(self): + return { + "time": tools.safe_eval.time, + "datetime": tools.safe_eval.datetime, + "dateutil": tools.safe_eval.dateutil, + "timezone": tools.safe_eval.pytz.timezone, + "context_today": lambda: fields.Date.context_today(self), + } + + @api.constrains("domain") + def _check_domain_format(self): + for rec in self: + if not rec.domain: + continue + try: + normalize_domain(safe_eval(rec.domain, rec._get_eval_context())) + except Exception as e: + raise ValidationError( + _("Invalid domain format: %(domain)s.\n" "Error: %(error)s") + % {"domain": rec.domain, "error": e} + ) from e + def _get_score(self, record): self.ensure_one() score = 1 @@ -91,6 +123,22 @@ def _get_score(self, record): score += 1 else: return -1 + if self.domain: + try: + domain = normalize_domain( + safe_eval(self.domain, self._get_eval_context()) + ) + if not record.filtered_domain(domain): + return -1 + score += 1 + except Exception as e: + _logger.warning( + "Failed to evaluate domain %s for record %s: %s", + self.domain, + record, + e, + ) + return -1 return score def _update_field_options(self, record, field_options): diff --git a/report_qweb_field_option/readme/CONFIGURE.md b/report_qweb_field_option/readme/CONFIGURE.md index e8532ad..35be096 100644 --- a/report_qweb_field_option/readme/CONFIGURE.md +++ b/report_qweb_field_option/readme/CONFIGURE.md @@ -6,6 +6,9 @@ For each record: - Set **Model** and **Field** (required) - Set **UoM** and **UoM Field**, or **Currency** and **Currency Field** only for fields of float type (optional) +- Set **Domain** to specify a domain for more specific filtering + (e.g., `[('secondary_uom_id', '=', 1)]` to apply only when + a specific secondary UoM is used) (optional) - Set **Company** (optional) - Set **Options** as a string representation of a dictionary. E.g., `{"widget": "date"}`, `{"widget": "monetary"}`, or diff --git a/report_qweb_field_option/static/description/index.html b/report_qweb_field_option/static/description/index.html index 65f598b..0595bfb 100644 --- a/report_qweb_field_option/static/description/index.html +++ b/report_qweb_field_option/static/description/index.html @@ -372,7 +372,7 @@

Report Qweb Field Option

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:6a839e0b8361541500cea7946ac9d7bfbcbe37ab2a102576061a2940c4343c5c +!! source digest: sha256:2e6250e04e346bc7f61b36e4fb244e8f744f480b1e42e0ba753a71342ebd648f !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/reporting-engine Translate me on Weblate Try me on Runboat

This module allows administrators to define the decimal precision of @@ -402,6 +402,9 @@

Configuration

  • Set Model and Field (required)
  • Set UoM and UoM Field, or Currency and Currency Field only for fields of float type (optional)
  • +
  • Set Domain to specify a domain for more specific filtering (e.g., +[('secondary_uom_id', '=', 1)] to apply only when a specific +secondary UoM is used) (optional)
  • Set Company (optional)
  • Set Options as a string representation of a dictionary. E.g., {"widget": "date"}, {"widget": "monetary"}, or diff --git a/report_qweb_field_option/tests/test_report_qweb_field_options.py b/report_qweb_field_option/tests/test_report_qweb_field_options.py index e55e439..c5670ab 100644 --- a/report_qweb_field_option/tests/test_report_qweb_field_options.py +++ b/report_qweb_field_option/tests/test_report_qweb_field_options.py @@ -153,3 +153,39 @@ def test_qweb_field_option_with_uom(self): self.test_record, "quantity", False, False, {}, values ) self.assertEqual(content, "1.0") + + def test_domain_validation(self): + """Test that invalid domain raises validation error""" + with self.assertRaises(ValidationError): + self.env["qweb.field.options"].create( + { + "res_model_id": self.test_model.id, + "field_id": self.value_field.id, + "domain": "invalid domain", + "digits": 2, + } + ) + + def test_qweb_field_option_with_domain(self): + values = {"report_type": "pdf"} + jpy_currency = self.env.ref("base.JPY") + jpy_currency.active = True + self.qweb_options_rec.digits = 2 + self.env["qweb.field.options"].create( + { + "res_model_id": self.test_model.id, + "field_id": self.value_field.id, + "domain": f"[('currency_id', '=', {jpy_currency.id})]", + "digits": 0, + } + ) + _, content, _ = self.IrQweb._get_field( + self.test_record, "value", False, False, {}, values + ) + self.assertEqual(content, "1.00") + # Test with JPY: domain matches, uses JPY-specific option (0 digits) + self.test_record.currency_id = jpy_currency.id + _, content, _ = self.IrQweb._get_field( + self.test_record, "value", False, False, {}, values + ) + self.assertEqual(content, "1") diff --git a/report_qweb_field_option/views/qweb_field_options_views.xml b/report_qweb_field_option/views/qweb_field_options_views.xml index 679477d..776a4c1 100644 --- a/report_qweb_field_option/views/qweb_field_options_views.xml +++ b/report_qweb_field_option/views/qweb_field_options_views.xml @@ -27,6 +27,13 @@ readonly="currency_id == False" required="currency_id != False" /> + + Date: Fri, 22 May 2026 08:43:47 +0000 Subject: [PATCH 16/21] [IMP] stock_secondary_unit: reflect OCA changes --- stock_secondary_unit/models/stock_move.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/stock_secondary_unit/models/stock_move.py b/stock_secondary_unit/models/stock_move.py index 599039e..7493ce4 100644 --- a/stock_secondary_unit/models/stock_move.py +++ b/stock_secondary_unit/models/stock_move.py @@ -84,12 +84,8 @@ def default_get(self, fields_list): res = super().default_get(fields_list) move_id = self.env.context.get("default_move_id") or res.get("move_id") if move_id and not res.get("secondary_uom_id"): - # default_move_id may resolve to the MO id instead of the stock.move id - # when saving the detailed operations dialog from an MO component line - # (https://github.com/odoo/odoo/issues/261740); a wrong default here - # should be harmless as it only pre-fills the line - move = self.env["stock.move"].browse(move_id).exists() - if move and move.secondary_uom_id: + move = self.env["stock.move"].browse(move_id) + if move.secondary_uom_id: res["secondary_uom_id"] = move.secondary_uom_id.id return res From 44bb8ea41f1a009907bf54edecd2121019957b8b Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Fri, 22 May 2026 08:43:47 +0000 Subject: [PATCH 17/21] [IMP] template_content_swapper: reflect OCA changes --- .../models/template_content_mapping.py | 2 +- .../views/template_content_mapping_views.xml | 32 ++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/template_content_swapper/models/template_content_mapping.py b/template_content_swapper/models/template_content_mapping.py index 64d5481..6825bbb 100644 --- a/template_content_swapper/models/template_content_mapping.py +++ b/template_content_swapper/models/template_content_mapping.py @@ -113,6 +113,6 @@ def open_template_mapping(self): "type": "ir.actions.act_window", "name": "Template Content Mappings", "res_model": "template.content.mapping", - "view_mode": "list", + "view_mode": "list,form", "context": {"multi_lang": multi_lang}, } diff --git a/template_content_swapper/views/template_content_mapping_views.xml b/template_content_swapper/views/template_content_mapping_views.xml index 8602b74..5324761 100644 --- a/template_content_swapper/views/template_content_mapping_views.xml +++ b/template_content_swapper/views/template_content_mapping_views.xml @@ -4,7 +4,7 @@ template.content.mapping.list template.content.mapping - + @@ -21,6 +21,36 @@ + + template.content.mapping.form + template.content.mapping + +
    + + + + + + + + + + + + + + + + + + + +
    +
    +
    template.content.mapping.search template.content.mapping From 18cac188fba87317c6108abcfd963786ff1298b7 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Fri, 22 May 2026 08:43:47 +0000 Subject: [PATCH 18/21] [IMP] web_form_banner: reflect OCA changes --- web_form_banner/README.rst | 2 +- web_form_banner/__manifest__.py | 2 +- web_form_banner/static/description/index.html | 2 +- web_form_banner/views/web_form_banner_rule_views.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/web_form_banner/README.rst b/web_form_banner/README.rst index 0475d62..94b6c0c 100644 --- a/web_form_banner/README.rst +++ b/web_form_banner/README.rst @@ -11,7 +11,7 @@ Web Form Banner !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:e717dbf8e121cc9ae1a93ad095f7d6a3d3a0219dbbb41e7292c39b58cb2eb653 + !! source digest: sha256:d5dd848e3f2205d7a480f4606bad02c27855e9a060c2593c209f79dbdfa14a0d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/web_form_banner/__manifest__.py b/web_form_banner/__manifest__.py index b3e4647..1b0c230 100644 --- a/web_form_banner/__manifest__.py +++ b/web_form_banner/__manifest__.py @@ -2,7 +2,7 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). { "name": "Web Form Banner", - "version": "18.0.1.0.1", + "version": "18.0.1.1.0", "category": "Web", "author": "Quartile, Odoo Community Association (OCA)", "website": "https://github.com/OCA/web", diff --git a/web_form_banner/static/description/index.html b/web_form_banner/static/description/index.html index 5cca122..ac269e1 100644 --- a/web_form_banner/static/description/index.html +++ b/web_form_banner/static/description/index.html @@ -372,7 +372,7 @@

    Web Form Banner

    !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:e717dbf8e121cc9ae1a93ad095f7d6a3d3a0219dbbb41e7292c39b58cb2eb653 +!! source digest: sha256:d5dd848e3f2205d7a480f4606bad02c27855e9a060c2593c209f79dbdfa14a0d !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

    Beta License: AGPL-3 OCA/web Translate me on Weblate Try me on Runboat

    The module adds configurable banners for backend form views. Define diff --git a/web_form_banner/views/web_form_banner_rule_views.xml b/web_form_banner/views/web_form_banner_rule_views.xml index 7db6561..0697efc 100644 --- a/web_form_banner/views/web_form_banner_rule_views.xml +++ b/web_form_banner/views/web_form_banner_rule_views.xml @@ -113,7 +113,7 @@ (one2many/reference, etc).

  • current_id: Integer id of the record being edited, or record_id: Integer id of the record being edited, or False if the form is creating a new record.
  • From a1fd881f7917f08bc1fcb5c9d319daa29b8e6a36 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Fri, 22 May 2026 08:43:47 +0000 Subject: [PATCH 19/21] [IMP] web_m2x_dialog_no_create: reflect OCA changes --- web_m2x_dialog_no_create/__manifest__.py | 3 + .../models/res_company.py | 3 + .../static/description/index.html | 11 +-- .../static/tests/select_create_dialog.test.js | 72 +++++++++++++++++++ 4 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 web_m2x_dialog_no_create/static/tests/select_create_dialog.test.js diff --git a/web_m2x_dialog_no_create/__manifest__.py b/web_m2x_dialog_no_create/__manifest__.py index c9d2aa8..7d4eceb 100644 --- a/web_m2x_dialog_no_create/__manifest__.py +++ b/web_m2x_dialog_no_create/__manifest__.py @@ -17,6 +17,9 @@ "web_m2x_dialog_no_create/static/src/select_create_dialog.esm.js", "web_m2x_dialog_no_create/static/src/select_create_dialog.xml", ], + "web.assets_unit_tests": [ + "web_m2x_dialog_no_create/static/tests/**/*", + ], }, "installable": True, } diff --git a/web_m2x_dialog_no_create/models/res_company.py b/web_m2x_dialog_no_create/models/res_company.py index 388101e..c888770 100644 --- a/web_m2x_dialog_no_create/models/res_company.py +++ b/web_m2x_dialog_no_create/models/res_company.py @@ -9,5 +9,8 @@ class ResCompany(models.Model): m2x_dialog_create_model_ids = fields.Many2many( "ir.model", + relation="res_company_m2x_dialog_create_model_rel", + column1="company_id", + column2="model_id", string="Allow Create from Search Dialog", ) diff --git a/web_m2x_dialog_no_create/static/description/index.html b/web_m2x_dialog_no_create/static/description/index.html index f0ec382..5721738 100644 --- a/web_m2x_dialog_no_create/static/description/index.html +++ b/web_m2x_dialog_no_create/static/description/index.html @@ -8,10 +8,11 @@ /* :Author: David Goodger (goodger@python.org) -:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $ +:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $ :Copyright: This stylesheet has been placed in the public domain. Default cascading style sheet for the HTML output of Docutils. +Despite the name, some widely supported CSS2 features are used. See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to customize this style sheet. @@ -274,7 +275,7 @@ margin-left: 2em ; margin-right: 2em } -pre.code .ln { color: grey; } /* line numbers */ +pre.code .ln { color: gray; } /* line numbers */ pre.code, code { background-color: #eeeeee } pre.code .comment, code .comment { color: #5C6576 } pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } @@ -300,7 +301,7 @@ span.pre { white-space: pre } -span.problematic { +span.problematic, pre.problematic { color: red } span.section-subtitle { @@ -426,7 +427,9 @@

    Contributors

    Maintainers

    This module is maintained by the OCA.

    -Odoo Community Association + +Odoo Community Association +

    OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

    diff --git a/web_m2x_dialog_no_create/static/tests/select_create_dialog.test.js b/web_m2x_dialog_no_create/static/tests/select_create_dialog.test.js new file mode 100644 index 0000000..1b82975 --- /dev/null +++ b/web_m2x_dialog_no_create/static/tests/select_create_dialog.test.js @@ -0,0 +1,72 @@ +// Copyright 2026 Quartile (https://www.quartile.co) +// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import {expect, test} from "@odoo/hoot"; +import { + contains, + defineModels, + fields, + getService, + models, + mountWithCleanup, + onRpc, + patchWithCleanup, +} from "@web/../tests/web_test_helpers"; +import {SelectCreateDialog} from "@web/views/view_dialogs/select_create_dialog"; +import {WebClient} from "@web/webclient/webclient"; +import {animationFrame} from "@odoo/hoot-mock"; +import {session} from "@web/session"; + +class Partner extends models.Model { + name = fields.Char(); + _records = [ + {id: 1, name: "first"}, + {id: 2, name: "second"}, + ]; + _views = { + list: /* xml */ ``, + search: /* xml */ ``, + form: /* xml */ `
    `, + }; +} + +defineModels([Partner]); + +const openDialog = async (props = {}) => { + await mountWithCleanup(WebClient); + getService("dialog").add(SelectCreateDialog, { + resModel: "partner", + ...props, + }); + await animationFrame(); + expect(".o_dialog").toHaveCount(1); +}; + +test("New button is hidden when model is not in the allow-list", async () => { + onRpc("has_group", () => true); + patchWithCleanup(session, { + web_m2x_dialog_no_create: {allowed_models: []}, + }); + await openDialog(); + expect(".o_dialog footer button.o_create_button").toHaveCount(0); +}); + +test("New button is shown when model is in the allow-list", async () => { + onRpc("has_group", () => true); + patchWithCleanup(session, { + web_m2x_dialog_no_create: {allowed_models: ["partner"]}, + }); + await openDialog(); + expect(".o_dialog footer button.o_create_button").toHaveCount(1); + await contains(".o_dialog footer button.o_create_button").click(); + expect(".o_dialog .o_form_view").toHaveCount(1); +}); + +test("noCreate prop still hides New button when model is allow-listed", async () => { + onRpc("has_group", () => true); + patchWithCleanup(session, { + web_m2x_dialog_no_create: {allowed_models: ["partner"]}, + }); + await openDialog({noCreate: true}); + expect(".o_dialog footer button.o_create_button").toHaveCount(0); +}); From daeffedc56cddd2e7699e87552ad1c187f50292a Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Fri, 22 May 2026 08:43:47 +0000 Subject: [PATCH 20/21] [IMP] web_m2x_options: reflect OCA changes --- web_m2x_options/README.rst | 2 +- web_m2x_options/__manifest__.py | 2 +- web_m2x_options/static/description/index.html | 2 +- .../static/src/components/form.esm.js | 63 ++++++++++++------- 4 files changed, 44 insertions(+), 25 deletions(-) diff --git a/web_m2x_options/README.rst b/web_m2x_options/README.rst index 25df0b2..84c42c5 100644 --- a/web_m2x_options/README.rst +++ b/web_m2x_options/README.rst @@ -11,7 +11,7 @@ web_m2x_options !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:881a75e6602b8037ae91fff6ff77e99caff9cba4b1ccda3dff174f64f4e42824 + !! source digest: sha256:72edae262a5750aa942302a30dfa960c912c99d637f4777222243f3e021f3924 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/web_m2x_options/__manifest__.py b/web_m2x_options/__manifest__.py index 47ddd26..e075c44 100644 --- a/web_m2x_options/__manifest__.py +++ b/web_m2x_options/__manifest__.py @@ -6,7 +6,7 @@ { "name": "web_m2x_options", - "version": "18.0.1.0.1", + "version": "18.0.1.0.3", "category": "Web", "author": "initOS GmbH," "ACSONE SA/NV, " diff --git a/web_m2x_options/static/description/index.html b/web_m2x_options/static/description/index.html index 3330f38..181a3ea 100644 --- a/web_m2x_options/static/description/index.html +++ b/web_m2x_options/static/description/index.html @@ -372,7 +372,7 @@

    web_m2x_options

    !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:881a75e6602b8037ae91fff6ff77e99caff9cba4b1ccda3dff174f64f4e42824 +!! source digest: sha256:72edae262a5750aa942302a30dfa960c912c99d637f4777222243f3e021f3924 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

    Beta License: AGPL-3 OCA/web Translate me on Weblate Try me on Runboat

    This modules modifies “many2one” and “many2manytags” form widgets so as diff --git a/web_m2x_options/static/src/components/form.esm.js b/web_m2x_options/static/src/components/form.esm.js index 4811046..32d3875 100644 --- a/web_m2x_options/static/src/components/form.esm.js +++ b/web_m2x_options/static/src/components/form.esm.js @@ -27,41 +27,57 @@ function evaluateSystemParameterDefaultTrue(option) { return isOptionSet ? evaluateBooleanExpr(isOptionSet) : true; } +function evaluateHasCreatePermission(attrs) { + return attrs.can_create ? evaluateBooleanExpr(attrs.can_create) : true; +} + +function evaluateFieldBooleanOption(option) { + if (typeof option === "boolean") { + return option; + } + if (typeof option === "string") { + return evaluateBooleanExpr(option); + } + return true; +} + patch(many2OneField, { m2o_options_props_create(props, attrs, options) { const canQuickCreate = evaluateSystemParameterDefaultTrue("create"); + const hasCreatePermission = evaluateHasCreatePermission(attrs); if (options.no_quick_create) { props.canQuickCreate = false; } else if ("no_quick_create" in options) { - props.canQuickCreate = attrs.can_create - ? evaluateBooleanExpr(attrs.can_create) - : true; + props.canQuickCreate = hasCreatePermission; + } else if ("create" in options) { + // Field option set, but must respect can_create security attribute + props.canQuickCreate = + hasCreatePermission && evaluateFieldBooleanOption(options.create); } else if (!canQuickCreate && props.canQuickCreate) { props.canQuickCreate = false; } else if (canQuickCreate && !props.canQuickCreate) { - props.canQuickCreate = attrs.can_create - ? evaluateBooleanExpr(attrs.can_create) - : true; + props.canQuickCreate = hasCreatePermission; } return props; }, m2o_options_props_create_edit(props, attrs, options) { const canCreateEdit = evaluateSystemParameterDefaultTrue("create_edit"); + const hasCreatePermission = evaluateHasCreatePermission(attrs); if (options.no_create_edit) { props.canCreateEdit = false; } else if ("no_create_edit" in options) { // Same condition set in web/views/fields/many2one/many2one_field - props.canCreateEdit = attrs.can_create - ? evaluateBooleanExpr(attrs.can_create) - : true; + props.canCreateEdit = hasCreatePermission; + } else if ("create_edit" in options) { + // Field option set, but must respect can_create security attribute + props.canCreateEdit = + hasCreatePermission && evaluateFieldBooleanOption(options.create_edit); } else if (!canCreateEdit && props.canCreateEdit) { props.canCreateEdit = false; } else if (canCreateEdit && !props.canCreateEdit) { // Same condition set in web/views/fields/many2one/many2one_field - props.canCreateEdit = attrs.can_create - ? evaluateBooleanExpr(attrs.can_create) - : true; + props.canCreateEdit = hasCreatePermission; } return props; }, @@ -152,14 +168,17 @@ patch(Many2OneField.prototype, { patch(many2ManyTagsField, { m2m_options_props_create(props, attrs, options) { const canQuickCreate = evaluateSystemParameterDefaultTrue("create"); + const hasCreatePermission = evaluateHasCreatePermission(attrs); // Create option already available for m2m fields if (!options.no_quick_create) { - if (!canQuickCreate && props.canQuickCreate) { + if ("create" in options) { + // Field option set, but must respect can_create security attribute + props.canQuickCreate = + hasCreatePermission && evaluateFieldBooleanOption(options.create); + } else if (!canQuickCreate && props.canQuickCreate) { props.canQuickCreate = false; } else if (canQuickCreate && !props.canQuickCreate) { - props.canQuickCreate = attrs.can_create - ? evaluateBooleanExpr(attrs.can_create) - : true; + props.canQuickCreate = hasCreatePermission; } } return props; @@ -167,20 +186,20 @@ patch(many2ManyTagsField, { m2m_options_props_create_edit(props, attrs, options) { const canCreateEdit = evaluateSystemParameterDefaultTrue("create_edit"); + const hasCreatePermission = evaluateHasCreatePermission(attrs); if (options.no_create_edit) { props.canCreateEdit = false; } else if ("no_create_edit" in options) { // Same condition set in web/views/fields/many2one/many2one_field - props.canCreateEdit = attrs.can_create - ? evaluateBooleanExpr(attrs.can_create) - : true; + props.canCreateEdit = hasCreatePermission; + } else if ("create_edit" in options) { + props.canCreateEdit = + hasCreatePermission && evaluateFieldBooleanOption(options.create_edit); } else if (!canCreateEdit && props.canCreateEdit) { props.canCreateEdit = false; } else if (canCreateEdit && !props.canCreateEdit) { // Same condition set in web/views/fields/many2one/many2one_field - props.canCreateEdit = attrs.can_create - ? evaluateBooleanExpr(attrs.can_create) - : true; + props.canCreateEdit = hasCreatePermission; } return props; }, From 254bee3e23014c952dc4e39c995fc2d380d6a272 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Fri, 22 May 2026 08:43:47 +0000 Subject: [PATCH 21/21] [IMP] web_widget_x2many_2d_matrix: reflect OCA changes --- web_widget_x2many_2d_matrix/README.rst | 2 +- web_widget_x2many_2d_matrix/__manifest__.py | 2 +- .../static/description/index.html | 2 +- .../x2many_2d_matrix_field.scss | 14 ++ .../x2many_2d_matrix_renderer.esm.js | 15 +- .../tests/web_widget_x2many_2d_matrix.test.js | 157 ++++++++++++------ .../tests/test_web_widget_x2many_2d_matrix.py | 2 +- 7 files changed, 129 insertions(+), 65 deletions(-) diff --git a/web_widget_x2many_2d_matrix/README.rst b/web_widget_x2many_2d_matrix/README.rst index c4b3dfa..cc0ce0b 100644 --- a/web_widget_x2many_2d_matrix/README.rst +++ b/web_widget_x2many_2d_matrix/README.rst @@ -11,7 +11,7 @@ !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:e422b074c4b533bda09b6422c878ae711365bf51d0a784706df06696acc6a3d3 + !! source digest: sha256:d0bca13b8ccc233974d08abaa46f3830104199003c12034d144fdfd6abd654d4 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png diff --git a/web_widget_x2many_2d_matrix/__manifest__.py b/web_widget_x2many_2d_matrix/__manifest__.py index 4b65fe9..0dbfa38 100644 --- a/web_widget_x2many_2d_matrix/__manifest__.py +++ b/web_widget_x2many_2d_matrix/__manifest__.py @@ -5,7 +5,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { "name": "2D matrix for x2many fields", - "version": "18.0.2.0.2", + "version": "18.0.2.1.0", "maintainers": ["JasminSForgeFlow", "DavidJForgeFlow", "hbrunn"], "development_status": "Production/Stable", "author": ( diff --git a/web_widget_x2many_2d_matrix/static/description/index.html b/web_widget_x2many_2d_matrix/static/description/index.html index 320db48..157a93c 100644 --- a/web_widget_x2many_2d_matrix/static/description/index.html +++ b/web_widget_x2many_2d_matrix/static/description/index.html @@ -372,7 +372,7 @@

    2D matrix for x2many fields

    !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:e422b074c4b533bda09b6422c878ae711365bf51d0a784706df06696acc6a3d3 +!! source digest: sha256:d0bca13b8ccc233974d08abaa46f3830104199003c12034d144fdfd6abd654d4 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

    Production/Stable License: AGPL-3 OCA/web Translate me on Weblate Try me on Runboat

    This module allows to show an x2many field with 3-tuples ($x_value, diff --git a/web_widget_x2many_2d_matrix/static/src/components/x2many_2d_matrix_field/x2many_2d_matrix_field.scss b/web_widget_x2many_2d_matrix/static/src/components/x2many_2d_matrix_field/x2many_2d_matrix_field.scss index 1fe9e97..c4168f1 100644 --- a/web_widget_x2many_2d_matrix/static/src/components/x2many_2d_matrix_field/x2many_2d_matrix_field.scss +++ b/web_widget_x2many_2d_matrix/static/src/components/x2many_2d_matrix_field/x2many_2d_matrix_field.scss @@ -6,6 +6,11 @@ $x2many_2d_matrix_max_height: 450px; overflow-y: auto; } + /* Prevent having double border for monetary fields (for instance if you use web_theme_classic) */ + span.o_input:has(span.o_monetary_ghost_value) + input.o_input { + border-color: transparent; + } + .o_input { padding: 1px 0px; } @@ -16,6 +21,7 @@ $x2many_2d_matrix_max_height: 450px; position: sticky; top: 0; z-index: 1; + background-color: $o-gray-100; &.total { right: 0; @@ -33,6 +39,10 @@ $x2many_2d_matrix_max_height: 450px; text-align: left; } + .o_monetary_ghost_value { + width: 100%; + } + &:first-child { position: sticky; left: 0; @@ -40,7 +50,9 @@ $x2many_2d_matrix_max_height: 450px; border-right-width: 1px; border-right-color: $gray-300; border-right-style: solid; + background-color: $o-view-background-color; } + &.row-total { padding: 0.5rem 0.75rem; font-weight: bold; @@ -49,6 +61,7 @@ $x2many_2d_matrix_max_height: 450px; border-left-width: 1px; border-left-color: $gray-300; border-left-style: solid; + background-color: $o-view-background-color; } } } @@ -58,6 +71,7 @@ $x2many_2d_matrix_max_height: 450px; text-align: right; position: sticky; bottom: 0; + background-color: $o-gray-100; &.col-total { padding: 0.75rem; diff --git a/web_widget_x2many_2d_matrix/static/src/components/x2many_2d_matrix_renderer/x2many_2d_matrix_renderer.esm.js b/web_widget_x2many_2d_matrix/static/src/components/x2many_2d_matrix_renderer/x2many_2d_matrix_renderer.esm.js index 20016e5..2ee6ece 100644 --- a/web_widget_x2many_2d_matrix/static/src/components/x2many_2d_matrix_renderer/x2many_2d_matrix_renderer.esm.js +++ b/web_widget_x2many_2d_matrix/static/src/components/x2many_2d_matrix_renderer/x2many_2d_matrix_renderer.esm.js @@ -1,19 +1,16 @@ -import {Component, onWillUpdateProps} from "@odoo/owl"; import {evaluateBooleanExpr, evaluateExpr} from "@web/core/py_js/py"; +import {Component, onWillRender} from "@odoo/owl"; import {Domain} from "@web/core/domain"; import {Record} from "@web/model/relational_model/record"; import {getFieldContext} from "@web/model/relational_model/utils"; export class X2Many2DMatrixRenderer extends Component { setup() { - this.columns = this._getColumns(); - this.rows = this._getRows(); - this.matrix = this._getMatrix(); - - onWillUpdateProps((newProps) => { - this.columns = this._getColumns(newProps.list.records); - this.rows = this._getRows(newProps.list.records); - this.matrix = this._getMatrix(newProps.list.records); + onWillRender(() => { + const records = this.list.records; + this.columns = this._getColumns(records); + this.rows = this._getRows(records); + this.matrix = this._getMatrix(records); }); } diff --git a/web_widget_x2many_2d_matrix/static/tests/web_widget_x2many_2d_matrix.test.js b/web_widget_x2many_2d_matrix/static/tests/web_widget_x2many_2d_matrix.test.js index 942132b..c3f8575 100644 --- a/web_widget_x2many_2d_matrix/static/tests/web_widget_x2many_2d_matrix.test.js +++ b/web_widget_x2many_2d_matrix/static/tests/web_widget_x2many_2d_matrix.test.js @@ -1,5 +1,12 @@ -import {defineModels, fields, models, mountView} from "@web/../tests/web_test_helpers"; +import { + contains, + defineModels, + fields, + models, + mountView, +} from "@web/../tests/web_test_helpers"; import {describe, expect, test} from "@odoo/hoot"; +import {queryAll} from "@odoo/hoot-dom"; describe.current.tags("desktop"); @@ -63,12 +70,13 @@ class Line extends models.Model { } defineModels([Line, Main]); -test("matrix displaying float fields are rendered correctly", async () => { - await mountView({ - type: "form", - resModel: "main", - resId: 1, - arch: ` +describe("X2Many2DMatrixRenderer", () => { + test("matrix displaying float fields are rendered correctly", async () => { + await mountView({ + type: "form", + resModel: "main", + resId: 1, + arch: `

    @@ -78,17 +86,17 @@ test("matrix displaying float fields are rendered correctly", async () => {
    `, + }); + expect(".o_field_widget input").toHaveCount(4); + expect(".col-total").toHaveText("168.00"); }); - expect(".o_field_widget input").toHaveCount(4); - expect(".col-total").toHaveText("168.00"); -}); -test("matrix displaying float fields can be configured", async () => { - await mountView({ - type: "form", - resModel: "main", - resId: 1, - arch: ` + test("matrix displaying float fields can be configured", async () => { + await mountView({ + type: "form", + resModel: "main", + resId: 1, + arch: `
    @@ -98,16 +106,16 @@ test("matrix displaying float fields can be configured", async () => {
    `, + }); + expect(".o_field_widget input").toHaveValue("42.000"); }); - expect(".o_field_widget input").toHaveValue("42.000"); -}); -test("matrix displaying char fields are rendered correctly", async () => { - await mountView({ - type: "form", - resModel: "main", - resId: 1, - arch: ` + test("matrix displaying char fields are rendered correctly", async () => { + await mountView({ + type: "form", + resModel: "main", + resId: 1, + arch: `
    @@ -117,17 +125,17 @@ test("matrix displaying char fields are rendered correctly", async () => {
    `, + }); + expect(".o_field_widget input").toHaveCount(4); + expect(".col-total").toHaveCount(0); }); - expect(".o_field_widget input").toHaveCount(4); - expect(".col-total").toHaveCount(0); -}); -test("matrix displaying many2one fields are rendered correctly", async () => { - await mountView({ - type: "form", - resModel: "main", - resId: 1, - arch: ` + test("matrix displaying many2one fields are rendered correctly", async () => { + await mountView({ + type: "form", + resModel: "main", + resId: 1, + arch: `
    @@ -137,16 +145,16 @@ test("matrix displaying many2one fields are rendered correctly", async () => {
    `, + }); + expect(".o_field_many2one_selection").toHaveCount(4); + expect(".o_form_uri").toHaveCount(0); }); - expect(".o_field_many2one_selection").toHaveCount(4); - expect(".o_form_uri").toHaveCount(0); -}); -test("matrix displaying many2one fields can be configured", async () => { - await mountView({ - type: "form", - resModel: "main", - resId: 1, - arch: ` + test("matrix displaying many2one fields can be configured", async () => { + await mountView({ + type: "form", + resModel: "main", + resId: 1, + arch: `
    @@ -156,16 +164,16 @@ test("matrix displaying many2one fields can be configured", async () => {
    `, + }); + expect(".o_field_many2one_selection").toHaveCount(2); + expect(".o_form_uri").toHaveCount(2); }); - expect(".o_field_many2one_selection").toHaveCount(2); - expect(".o_form_uri").toHaveCount(2); -}); -test("matrix axis can be clickable", async () => { - await mountView({ - type: "form", - resModel: "main", - resId: 1, - arch: ` + test("matrix axis can be clickable", async () => { + await mountView({ + type: "form", + resModel: "main", + resId: 1, + arch: `
    @@ -174,6 +182,51 @@ test("matrix axis can be clickable", async () => {
    `, + }); + expect(".o_form_uri").toHaveCount(2); + }); + + test("matrix totals are updated correctly after several edits", async () => { + await mountView({ + type: "form", + resModel: "main", + resId: 1, + arch: ` +
    + + + + + + + +
    `, + }); + + expect(".col-total").toHaveText("168.00"); + + const inputs = queryAll(".o_field_widget input"); + // Initial state: [42, 42, 42, 42] -> Total 168 + + await contains(inputs[0]).edit("100"); + // State: [100, 42, 42, 42] -> Total 226 + expect(".col-total").toHaveText("226.00"); + + await contains(inputs[1]).edit("200"); + // State: [100, 200, 42, 42] -> Total 384 + expect(".col-total").toHaveText("384.00"); + + await contains(inputs[2]).edit("50"); + // State: [100, 200, 50, 42] -> Total 392 + expect(".col-total").toHaveText("392.00"); + + await contains(inputs[3]).edit("0"); + // State: [100, 200, 50, 0] -> Total 350 + expect(".col-total").toHaveText("350.00"); + + // Edit the first one again + await contains(inputs[0]).edit("10"); + // State: [10, 200, 50, 0] -> Total 260 + expect(".col-total").toHaveText("260.00"); }); - expect(".o_form_uri").toHaveCount(2); }); diff --git a/web_widget_x2many_2d_matrix/tests/test_web_widget_x2many_2d_matrix.py b/web_widget_x2many_2d_matrix/tests/test_web_widget_x2many_2d_matrix.py index 590738a..d5ae574 100644 --- a/web_widget_x2many_2d_matrix/tests/test_web_widget_x2many_2d_matrix.py +++ b/web_widget_x2many_2d_matrix/tests/test_web_widget_x2many_2d_matrix.py @@ -5,7 +5,7 @@ class TestWebWidgetX2Many2DMatrix(HttpCase): def test_js(self): self.browser_js( - "/web/tests?headless&loglevel=2&preset=desktop&timeout=15000&id=b42fa5f8", + "/web/tests?headless&loglevel=2&preset=desktop&timeout=15000&filter=X2Many2DMatrixRenderer", "", "", login="admin",