Repository navigation
[20.0][MIG] base_user_role: Migration to 20.0 - #6
Merged
Merged
Conversation
…608) * [ADD] New module 'base_user_role' * [FIX] base_user_role - Review * [FIX] base_user_role - Review s/is_active/is_enabled/ * [FIX] base_user_role - Review s/is_active/is_enabled/ * [IMP] base_user_role - Translations updated (template + FR) * [FIX] base_user_role - Lint
* FIX: Update users' groups when a role is deleted * IMP: add some test cases to check user's groups when its last role line is removed * FIX: ensure that the user's groups are updated when its last role line is removed
Currently translated at 100,0% (37 of 37 strings) Translation: server-backend-11.0/server-backend-11.0-base_user_role Translate-URL: https://translation.odoo-community.org/projects/server-backend-11-0/server-backend-11-0-base_user_role/da/
Updated by Update PO files to match POT (msgmerge) hook in Weblate.
the groups that have changed with respecto the role.
…notebook users line (#1170)
…users.role.line" This reverts commit b537941d5a2d1fcb3fbacc602124c1b3f6e1d495.
Currently translated at 100.0% (82 of 82 strings) Translation: server-backend-19.0/server-backend-19.0-base_user_role Translate-URL: https://translation.odoo-community.org/projects/server-backend-19-0/server-backend-19-0-base_user_role/sv/
Searching by user in the res.users.role list/search view raised 'Cannot convert res.users.role.role_user_ids to SQL because it is not stored' because role_user_ids is a non-stored computed One2many. Add a search method redirecting the query to the stored line_ids.user_id relation.
Currently translated at 63.4% (52 of 82 strings) Translation: server-backend-19.0/server-backend-19.0-base_user_role Translate-URL: https://translation.odoo-community.org/projects/server-backend-19-0/server-backend-19-0-base_user_role/de/
Currently translated at 100.0% (82 of 82 strings) Translation: server-backend-19.0/server-backend-19.0-base_user_role Translate-URL: https://translation.odoo-community.org/projects/server-backend-19-0/server-backend-19-0-base_user_role/de/
Currently translated at 100.0% (82 of 82 strings) Translation: server-backend-19.0/server-backend-19.0-base_user_role Translate-URL: https://translation.odoo-community.org/projects/server-backend-19-0/server-backend-19-0-base_user_role/da/
Currently translated at 91.4% (75 of 82 strings) Translation: server-backend-19.0/server-backend-19.0-base_user_role Translate-URL: https://translation.odoo-community.org/projects/server-backend-19-0/server-backend-19-0-base_user_role/sl/
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: server-backend-19.0/server-backend-19.0-base_user_role Translate-URL: https://translation.odoo-community.org/projects/server-backend-19-0/server-backend-19-0-base_user_role/
The self-writable Notification field on users, which allows choosing to be notified by email or via push, actually sets or unsets a group on the user. This would conflict with role-based management of groups: when setting push notification, the field would be set and push notification received but the group would immediately be reset by base_user_role (and some UI element would not be shown) and, in some case like module activation, the notification type would be reset to mail, the field being recomputed from the user's groups. This change excludes the group in question form being unset or set by roles. Exclusions can be added by overriding a method.
This commit adds a new feature to the module: New button to open wizard so it's possible to select multiple users at once, keeping the ability to choose the dates.
…_type
Deleting all roles from a user with notification type 'inbox' may trigger
```
ERROR: new row for relation "res_users" violates check constraint "res_users_notification_type"
```
because notification_type `inbox` is not allowed when the user is no longer has
the share flag (which is set for users without group base.group_user).
The issue is only triggered due to intermittent flushes, such as the one caused
by website's `_check_one_user_type` code constraint on res.users.
Fixes
```
File "/home/odoo/src/oca-server-backend/base_user_role/tests/test_user_role.py", line 312, in test_notification_type_reset
user.write(
File "/home/odoo/odoo/18.0/odoo/addons/auth_totp_mail/models/res_users.py", line 11, in write
res = super().write(vals)
^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/18.0/odoo/addons/auth_signup/models/res_users.py", line 362, in write
return super().write(vals)
^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/18.0/odoo/addons/mail/models/discuss/res_users.py", line 17, in write
res = super().write(vals)
^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/18.0/odoo/addons/mail/models/res_users.py", line 108, in write
write_res = super(Users, self).write(vals)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/18.0/odoo/addons/resource/models/res_users.py", line 17, in write
rslt = super().write(vals)
^^^^^^^^^^^^^^^^^^^
File "/home/odoo/src/oca-server-backend/base_user_role/models/user.py", line 60, in write
res = super().write(vals)
^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/18.0/odoo/odoo/addons/base/models/res_users.py", line 1920, in write
res = super(UsersView, self).write(values)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/18.0/odoo/odoo/addons/base/models/res_users.py", line 1609, in write
return super(UsersImplied, self).write(values)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/18.0/odoo/odoo/addons/base/models/res_users.py", line 759, in write
res = super(Users, self).write(values)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/18.0/odoo/odoo/models.py", line 4812, in write
field.write(self, value)
File "/home/odoo/odoo/18.0/odoo/odoo/fields.py", line 4552, in write
self.write_batch([(records, value)])
File "/home/odoo/odoo/18.0/odoo/odoo/fields.py", line 4573, in write_batch
self.write_real(records_commands_list, create)
File "/home/odoo/odoo/18.0/odoo/odoo/fields.py", line 4769, in write_real
flush()
File "/home/odoo/odoo/18.0/odoo/odoo/fields.py", line 4720, in flush
comodel.browse(to_delete).unlink()
File "/home/odoo/src/oca-server-backend/base_user_role/models/role.py", line 180, in unlink
users.set_groups_from_roles(force=True)
File "/home/odoo/src/oca-server-backend/base_user_role/models/user.py", line 121, in set_groups_from_roles
super(ResUsers, user).write(vals)
File "/home/odoo/odoo/18.0/odoo/odoo/addons/base/models/res_users.py", line 1920, in write
res = super(UsersView, self).write(values)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/18.0/odoo/odoo/addons/base/models/res_users.py", line 1611, in write
res = super(UsersImplied, self.with_context(no_add_implied_groups=True)).write(values)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/18.0/odoo/odoo/addons/base/models/res_users.py", line 759, in write
res = super(Users, self).write(values)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/18.0/odoo/odoo/models.py", line 4834, in write
real_recs._validate_fields(vals, inverse_fields)
File "/home/odoo/odoo/18.0/odoo/odoo/models.py", line 1631, in _validate_fields
check(self)
File "/home/odoo/odoo/18.0/odoo/addons/website/models/res_users.py", line 105, in _check_one_user_type
internal_users = self.env.ref('base.group_user').users & self
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/18.0/odoo/odoo/fields.py", line 3102, in __get__
return super().__get__(records, owner)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/18.0/odoo/odoo/fields.py", line 1274, in __get__
recs._fetch_field(self)
File "/home/odoo/odoo/18.0/odoo/odoo/models.py", line 4119, in _fetch_field
self.fetch(fnames)
File "/home/odoo/odoo/18.0/odoo/odoo/models.py", line 4157, in fetch
fetched = self._fetch_query(query, fields_to_fetch)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/18.0/odoo/odoo/models.py", line 4268, in _fetch_query
field.read(fetched)
File "/home/odoo/odoo/18.0/odoo/odoo/fields.py", line 5063, in read
for id1, id2 in records.env.execute_query(query.select(sql_id1, sql_id2)):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/odoo/odoo/18.0/odoo/odoo/api.py", line 992, in execute_query
self.flush_query(query)
File "/home/odoo/odoo/18.0/odoo/odoo/api.py", line 984, in flush_query
self[model_name].flush_model(field_names)
File "/home/odoo/odoo/18.0/odoo/odoo/models.py", line 6773, in flush_model
self._flush(fnames)
File "/home/odoo/odoo/18.0/odoo/odoo/models.py", line 6851, in _flush
model.browse(some_ids)._write_multi(vals_list)
File "/home/odoo/odoo/18.0/odoo/odoo/models.py", line 4937, in _write_multi
self.env.execute_query(SQL(
File "/home/odoo/odoo/18.0/odoo/odoo/api.py", line 993, in execute_query
self.cr.execute(query)
File "/home/odoo/odoo/18.0/odoo/odoo/sql_db.py", line 364, in execute
res = self._obj.execute(query, params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
psycopg2.errors.CheckViolation: new row for relation "res_users" violates check constraint "res_users_notification_type"
DETAIL: Failing row contains (81, 1, 151, t, 2026-02-10 15:41:40.105622, user_test_roles, null, null, 1, 84, <p data-o-mail-quote="1">--<br data-o-mail-quote="1">USER TEST (..., t, 2026-02-10 15:41:40.105622, null, f, inbox, null, null, null, null).
```
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: server-backend-19.0/server-backend-19.0-base_user_role Translate-URL: https://translation.odoo-community.org/projects/server-backend-19-0/server-backend-19-0-base_user_role/
Per the `create` and `write` methods on the `res.users` model [^1], `base.group_multi_company` group membership is a derivative of company membership. [^1]: https://github.com/odoo/odoo/blob/2c2641a0d725e3548328e756fd8b9301308a3f6c/odoo/addons/base/models/res_users.py#L1902-L1930
Currently translated at 100.0% (86 of 86 strings) Translation: server-backend-19.0/server-backend-19.0-base_user_role Translate-URL: https://translation.odoo-community.org/projects/server-backend-19-0/server-backend-19-0-base_user_role/it/
Assisted-by: OpenAI Codex
In Odoo 20 every group without a privilege becomes a "regular" group (`res.groups._apply_group_regular()`), so a role group turned all its users into regular users even when the role only implied light groups, and `base.group_user_regular` leaked into the role's `implied_ids`. Role groups are containers, like `base.default_user_group`: declare them in `_get_light_group_xmlids()` so the light/regular tier of a user comes from the groups implied by its roles. Return the group ids instead of creating `__custom__` XML IDs with `_ensure_xml_id()`: `SetDefinitions` resolves ids as well, nothing is written from a compute path and the arbitrary `limit=1000` is not needed (the domain on `role_id` also keeps pylint-odoo `no-search-all` quiet). Restore the original assertions in `test_create_role_from_user` and `test_group_groups_into_role` (no need to discard `base.group_user_regular` anymore) and add tests for the light/regular behaviour and for the read-only `role` radio on the user form. Assisted-by: Claude Fable 5.1
FontAwesome is gone from the web client in 20.0: use the Material
Symbols names of the Odoo icon font (`lock`, as the Access Rights
button of the group form, `group` and `settings`).
Keep the plain `_inherits = {...}` syntax used across the Odoo code
base; ruff RUF012 (enabled by the repo pre-commit) is silenced inline.
Assisted-by: Claude Fable 5.1
Available types: 'task#', 't#' (case insensitive) Examples:
|
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 20.0 #6 +/- ##
=======================================
Coverage ? 89.39%
=======================================
Files ? 12
Lines ? 509
Branches ? 39
=======================================
Hits ? 455
Misses ? 44
Partials ? 10 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The default user template implies regular groups as soon as sales or purchase are installed, so the test created its light user explicitly with the internal group only. Assisted-by: Claude Fable 5.1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Mirror of the upstream migration OCA#488 plus fixes:
res.groups._get_light_group_xmlids()): a role group is a container likebase.default_user_group, so it must not turn its users into regular users by itself; the light/regular tier now comes from the groups implied by the roles. The override returns the role group ids (SetDefinitions.get_id()resolves ids too) instead of creating__custom__XML IDs with_ensure_xml_id()from a compute path, and drops the arbitrarylimit=1000. The_apply_group_regular()calls on role create/unlink from the upstream PR are kept.test_create_role_from_userandtest_group_groups_into_role(no need to discardbase.group_user_regularanymore), addedtest_role_group_is_light(light-only role →light_user, role implying a regular group →regular_user, group back to regular after the role is deleted) andtest_user_form_role_readonly(nativeroleradio read-only when the user has role lines, as requested in the upstream review).fa-book,fa-users,fa-gears) replaced with the 20.0 icon-font names (lock,group,settings)._inherits = {"res.groups": "group_id"} # noqa: RUF012instead of theClassVarannotation objected to in the upstream review (ruff RUF012 is enabled by the repo pre-commit).The upstream commits are untouched; the fixes are the two last commits.
Upstream CI is red only because of the codecov upload step (SSL handshake failure); both test jobs there report
0 failed, 0 error(s).Tested on Odoo 20.0 (
odoo/odoo20.0, database with demo data):-i base_user_role --test-enable --test-tags /base_user_role: 28 tests, 0 failed, 0 errors, no deprecation warnings.-u base --test-tags /base:TestGroupsOdoo,/base:TestUsers2,/base:TestUsers,/base:TestResUsersFormwith the module installed (light/regular group invariants of core): 52 tests, 0 failed, 0 errors.Assisted-by: Claude Fable 5.1