diff --git a/addons/account/__manifest__.py b/addons/account/__manifest__.py index a7af38a3f6f..9caffdabf8d 100644 --- a/addons/account/__manifest__.py +++ b/addons/account/__manifest__.py @@ -70,7 +70,6 @@ You could use this simplified accounting in case you work with an (external) acc 'views/account_onboarding_templates.xml', 'wizard/account_report_tax_view.xml', 'views/report_tax.xml', - 'views/res_users_views.xml', 'data/service_cron_reverse.xml', 'views/account_fiscal_year_view.xml', 'views/account_incoterms_view.xml', diff --git a/addons/account/models/__init__.py b/addons/account/models/__init__.py index b6459570b8e..0f474e3cb00 100644 --- a/addons/account/models/__init__.py +++ b/addons/account/models/__init__.py @@ -15,7 +15,6 @@ from . import company from . import res_config_settings from . import account_cash_rounding from . import reconciliation_widget -from . import res_users from . import account_incoterms from . import digest from . import account_invoice_import_wizard diff --git a/addons/account/models/res_users.py b/addons/account/models/res_users.py deleted file mode 100644 index 3f49ce9c0b0..00000000000 --- a/addons/account/models/res_users.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - has_group_show_line_subtotals_tax_excluded = fields.Boolean( - "Show line subtotals without taxes (B2B)", - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='account.group_show_line_subtotals_tax_excluded') - - has_group_show_line_subtotals_tax_included = fields.Boolean( - "Show line subtotals with taxes included (B2C)", - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='account.group_show_line_subtotals_tax_included') - - has_group_warning_account = fields.Boolean( - 'A warning can be set on a partner (Account)', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='account.group_warning_account') - - has_group_cash_rounding = fields.Boolean( - 'Allow the cash rounding management', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='account.group_cash_rounding') - - group_account_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_accounting_and_finance'), - string='Accounting', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_accounting_and_finance') diff --git a/addons/account/security/account_security.xml b/addons/account/security/account_security.xml index 3512781d919..d33d6cd6e56 100644 --- a/addons/account/security/account_security.xml +++ b/addons/account/security/account_security.xml @@ -32,6 +32,7 @@ Show Full Accounting Features + @@ -51,10 +52,12 @@ A warning can be set on a partner (Account) + Allow the cash rounding management + diff --git a/addons/account/views/res_users_views.xml b/addons/account/views/res_users_views.xml deleted file mode 100644 index 72b0fc08ff5..00000000000 --- a/addons/account/views/res_users_views.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - res.users.form.account.inherit - res.users - - 40 - - - - - - - - - diff --git a/addons/analytic/__manifest__.py b/addons/analytic/__manifest__.py index 47815af682e..e0c621fc4ec 100644 --- a/addons/analytic/__manifest__.py +++ b/addons/analytic/__manifest__.py @@ -18,7 +18,6 @@ that have no counterpart in the general financial accounts. 'security/analytic_security.xml', 'security/ir.model.access.csv', 'views/analytic_account_views.xml', - 'views/res_users_views.xml', ], 'demo': [ 'data/analytic_demo.xml', diff --git a/addons/analytic/models/__init__.py b/addons/analytic/models/__init__.py index 2e5e553c626..d1fd64b96f8 100644 --- a/addons/analytic/models/__init__.py +++ b/addons/analytic/models/__init__.py @@ -2,4 +2,3 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import analytic_account -from . import res_users diff --git a/addons/analytic/models/res_users.py b/addons/analytic/models/res_users.py deleted file mode 100644 index f1363c0d123..00000000000 --- a/addons/analytic/models/res_users.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - has_group_analytic_accounting = fields.Boolean( - 'Analytic Accounting', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='analytic.group_analytic_accounting') - - has_group_analytic_tags = fields.Boolean( - 'Analytic Accounting Tags', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='analytic.group_analytic_tags') diff --git a/addons/analytic/security/analytic_security.xml b/addons/analytic/security/analytic_security.xml index 30f813f4a19..5e66d317f5c 100644 --- a/addons/analytic/security/analytic_security.xml +++ b/addons/analytic/security/analytic_security.xml @@ -34,10 +34,12 @@ Analytic Accounting + Analytic Accounting Tags + diff --git a/addons/analytic/views/res_users_views.xml b/addons/analytic/views/res_users_views.xml deleted file mode 100644 index 0d4b070cdaf..00000000000 --- a/addons/analytic/views/res_users_views.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - res.users.form.analytic.inherit - res.users - - - - - - - diff --git a/addons/crm/__manifest__.py b/addons/crm/__manifest__.py index 8b4ba2ebc26..4223c2c1ad6 100644 --- a/addons/crm/__manifest__.py +++ b/addons/crm/__manifest__.py @@ -44,7 +44,6 @@ 'report/crm_activity_report_views.xml', 'report/crm_opportunity_report_views.xml', 'views/crm_team_views.xml', - 'views/res_users_views.xml', 'views/digest_views.xml', ], 'demo': [ diff --git a/addons/crm/models/res_users.py b/addons/crm/models/res_users.py index fbbe64501eb..2c94f1c05ab 100644 --- a/addons/crm/models/res_users.py +++ b/addons/crm/models/res_users.py @@ -5,11 +5,8 @@ from odoo import fields, models class Users(models.Model): + _inherit = 'res.users' target_sales_won = fields.Integer('Won in Opportunities Target') target_sales_done = fields.Integer('Activities Done Target') - - has_group_use_lead = fields.Boolean( - 'Show Lead Menu', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='crm.group_use_lead') diff --git a/addons/crm/security/crm_security.xml b/addons/crm/security/crm_security.xml index fd080b8e2a8..1cb7832a43a 100644 --- a/addons/crm/security/crm_security.xml +++ b/addons/crm/security/crm_security.xml @@ -5,6 +5,7 @@ Show Lead Menu + diff --git a/addons/crm/views/res_users_views.xml b/addons/crm/views/res_users_views.xml deleted file mode 100644 index 27c2bf286fa..00000000000 --- a/addons/crm/views/res_users_views.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - res.users.form.crm.inherit - res.users - - - - - - - diff --git a/addons/event/__manifest__.py b/addons/event/__manifest__.py index b3662b2f003..e9f22c0f53d 100644 --- a/addons/event/__manifest__.py +++ b/addons/event/__manifest__.py @@ -30,8 +30,6 @@ Key Features 'views/res_config_settings_views.xml', 'views/event_templates.xml', 'views/res_partner_views.xml', - 'views/res_users_views.xml', - ], 'demo': [ 'data/event_demo.xml', diff --git a/addons/event/models/__init__.py b/addons/event/models/__init__.py index 695ce7e13e4..eed59489b26 100644 --- a/addons/event/models/__init__.py +++ b/addons/event/models/__init__.py @@ -5,4 +5,3 @@ from . import event from . import event_mail from . import res_config_settings from . import res_partner -from . import res_users diff --git a/addons/event/models/res_partner.py b/addons/event/models/res_partner.py index 03934562824..009b182c1ae 100644 --- a/addons/event/models/res_partner.py +++ b/addons/event/models/res_partner.py @@ -3,11 +3,10 @@ from odoo import api, fields, models - class ResPartner(models.Model): _inherit = 'res.partner' - event_count = fields.Integer("Events Count", compute='_compute_event_count', help="Number of events the partner has participated.") + event_count = fields.Integer("Events", compute='_compute_event_count', help="Number of events the partner has participated.") def _compute_event_count(self): if not self.user_has_groups('event.group_event_user'): diff --git a/addons/event/models/res_users.py b/addons/event/models/res_users.py deleted file mode 100644 index 024677bcfe4..00000000000 --- a/addons/event/models/res_users.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - group_event_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_event_management'), - string='Events', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_event_management') diff --git a/addons/event/views/res_users_views.xml b/addons/event/views/res_users_views.xml deleted file mode 100644 index 20b722bf76a..00000000000 --- a/addons/event/views/res_users_views.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - res.users.form.event.inherit - res.users - - 105 - - - - - - - diff --git a/addons/fleet/__manifest__.py b/addons/fleet/__manifest__.py index f3ae778f3bd..b65db3bd169 100644 --- a/addons/fleet/__manifest__.py +++ b/addons/fleet/__manifest__.py @@ -36,7 +36,6 @@ Main Features 'views/fleet_vehicle_cost_views.xml', 'views/fleet_board_view.xml', 'views/mail_activity_views.xml', - 'views/res_users_views.xml', 'data/fleet_cars_data.xml', 'data/fleet_data.xml', 'data/mail_data.xml', diff --git a/addons/fleet/models/__init__.py b/addons/fleet/models/__init__.py index 0421e909283..781603220f3 100644 --- a/addons/fleet/models/__init__.py +++ b/addons/fleet/models/__init__.py @@ -4,4 +4,3 @@ from . import fleet_vehicle from . import fleet_vehicle_cost from . import fleet_vehicle_model -from . import res_users diff --git a/addons/fleet/models/res_users.py b/addons/fleet/models/res_users.py deleted file mode 100644 index 0f031278fb5..00000000000 --- a/addons/fleet/models/res_users.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - group_fleet_user = fields.Selection( - selection=lambda self: self._get_group_selection('fleet.module_fleet_category'), - string='Fleet', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='fleet.module_fleet_category') diff --git a/addons/fleet/views/res_users_views.xml b/addons/fleet/views/res_users_views.xml deleted file mode 100644 index 3cd5e04b63a..00000000000 --- a/addons/fleet/views/res_users_views.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - res.users.form.fleet.inherit - res.users - - 100 - - - - - - - diff --git a/addons/hr/__manifest__.py b/addons/hr/__manifest__.py index c91e611a875..721b4eacde8 100644 --- a/addons/hr/__manifest__.py +++ b/addons/hr/__manifest__.py @@ -27,7 +27,6 @@ 'views/hr_views.xml', 'views/hr_templates.xml', 'views/res_config_settings_views.xml', - 'views/res_users_views.xml', 'data/hr_data.xml', ], 'demo': [ diff --git a/addons/hr/models/res_users.py b/addons/hr/models/res_users.py index 2e5acfe8ee7..be6b5148eba 100644 --- a/addons/hr/models/res_users.py +++ b/addons/hr/models/res_users.py @@ -9,12 +9,6 @@ class User(models.Model): employee_ids = fields.One2many('hr.employee', 'user_id', string='Related employees') - group_hr_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_human_resources'), - string="Employees", compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_human_resources', - help='Officer: The user will be able to approve document created by employees.\nManager: The user will have access to the human resources configuration as well as statistic reports.') - @api.multi def write(self, vals): """ Synchronize user and its related employee """ diff --git a/addons/hr/views/res_users_views.xml b/addons/hr/views/res_users_views.xml deleted file mode 100644 index d68aac97bd2..00000000000 --- a/addons/hr/views/res_users_views.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - res.users.form.hr.inherit - res.users - - 50 - - - - - - - diff --git a/addons/hr_attendance/__manifest__.py b/addons/hr_attendance/__manifest__.py index f80001dca97..8de64bd8c60 100644 --- a/addons/hr_attendance/__manifest__.py +++ b/addons/hr_attendance/__manifest__.py @@ -26,7 +26,6 @@ actions(Check in/Check out) performed by them. 'views/hr_department_view.xml', 'views/hr_employee_view.xml', 'views/res_config_settings_views.xml', - 'views/res_users_views.xml', ], 'demo': [ 'data/hr_attendance_demo.xml' diff --git a/addons/hr_attendance/models/__init__.py b/addons/hr_attendance/models/__init__.py index 724d7ee23aa..fd62623b92b 100644 --- a/addons/hr_attendance/models/__init__.py +++ b/addons/hr_attendance/models/__init__.py @@ -3,4 +3,3 @@ from . import res_config_settings from . import hr_attendance from . import hr_employee -from . import res_users diff --git a/addons/hr_attendance/models/res_users.py b/addons/hr_attendance/models/res_users.py deleted file mode 100644 index 34b2450964d..00000000000 --- a/addons/hr_attendance/models/res_users.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - has_group_hr_attendance = fields.Boolean( - 'Manual Attendance', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='hr_attendance.group_hr_attendance', - help='The user will gain access to the human resources attendance menu, enabling him to manage his own attendance.') - - has_group_hr_attendance_use_pin = fields.Boolean( - 'Enable PIN use', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='hr_attendance.group_hr_attendance_use_pin', - help='The user will have to enter his PIN to check in and out manually at the company screen.') - - group_hr_attendance_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_hr_attendance'), - string='Attendance', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_hr_attendance') diff --git a/addons/hr_attendance/security/hr_attendance_security.xml b/addons/hr_attendance/security/hr_attendance_security.xml index 8c3bc9482fb..e5b99ce9928 100644 --- a/addons/hr_attendance/security/hr_attendance_security.xml +++ b/addons/hr_attendance/security/hr_attendance_security.xml @@ -34,6 +34,7 @@ Enable PIN use + The user will have to enter his PIN to check in and out manually at the company screen. diff --git a/addons/hr_attendance/views/res_users_views.xml b/addons/hr_attendance/views/res_users_views.xml deleted file mode 100644 index eb8115fbcf9..00000000000 --- a/addons/hr_attendance/views/res_users_views.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - res.users.form.hr_attendance.inherit - res.users - - 80 - - - - - - - - - diff --git a/addons/hr_contract/__manifest__.py b/addons/hr_contract/__manifest__.py index 3a239b204d3..e07f3975ff7 100644 --- a/addons/hr_contract/__manifest__.py +++ b/addons/hr_contract/__manifest__.py @@ -23,7 +23,6 @@ You can assign several contracts per employee. 'security/ir.model.access.csv', 'data/hr_contract_data.xml', 'views/hr_contract_views.xml', - 'views/res_users_views.xml', ], 'demo': [], 'installable': True, diff --git a/addons/hr_contract/models/__init__.py b/addons/hr_contract/models/__init__.py index 7e9d4a5f5eb..855ff940d55 100644 --- a/addons/hr_contract/models/__init__.py +++ b/addons/hr_contract/models/__init__.py @@ -2,4 +2,3 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import hr_contract -from . import res_users diff --git a/addons/hr_contract/models/res_users.py b/addons/hr_contract/models/res_users.py deleted file mode 100644 index de1a824cf0f..00000000000 --- a/addons/hr_contract/models/res_users.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - group_hr_contract_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_hr_contract'), - string='Contracts', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_hr_contract') diff --git a/addons/hr_contract/views/res_users_views.xml b/addons/hr_contract/views/res_users_views.xml deleted file mode 100644 index 61b0212145e..00000000000 --- a/addons/hr_contract/views/res_users_views.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - res.users.form.hr_contract.inherit - res.users - - 51 - - - - - - - diff --git a/addons/hr_expense/__manifest__.py b/addons/hr_expense/__manifest__.py index ccb4202fbaa..5a444c69b7f 100644 --- a/addons/hr_expense/__manifest__.py +++ b/addons/hr_expense/__manifest__.py @@ -43,7 +43,6 @@ This module also uses analytic accounting and is compatible with the invoice on 'views/assets.xml', 'views/res_config_settings_views.xml', 'views/account_journal_dashboard.xml', - 'views/res_users_views.xml', ], 'demo': ['data/hr_expense_demo.xml'], 'installable': True, diff --git a/addons/hr_expense/models/__init__.py b/addons/hr_expense/models/__init__.py index 5a1b4fcd975..af1e04d2dd2 100644 --- a/addons/hr_expense/models/__init__.py +++ b/addons/hr_expense/models/__init__.py @@ -7,4 +7,3 @@ from . import hr_expense from . import product_template from . import res_config_settings from . import account_journal_dashboard -from . import res_users diff --git a/addons/hr_expense/models/res_users.py b/addons/hr_expense/models/res_users.py deleted file mode 100644 index 84324e182f1..00000000000 --- a/addons/hr_expense/models/res_users.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - group_hr_expense_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_hr_expense'), - string='Expenses', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_hr_expense') diff --git a/addons/hr_expense/tests/common.py b/addons/hr_expense/tests/common.py index 0ff8e27fba0..0a6a6cb838d 100644 --- a/addons/hr_expense/tests/common.py +++ b/addons/hr_expense/tests/common.py @@ -15,7 +15,7 @@ class TestExpenseCommon(TestAccountNoChartCommon): # The user manager is only expense manager user_group_manager = cls.env.ref('hr_expense.group_hr_expense_manager') cls.user_manager.write({ - 'groups_id': [(6, 0, [user_group_manager.id])], + 'groups_id': [(6, 0, [user_group_manager.id, cls.env.ref('base.group_user').id])], }) # create employee diff --git a/addons/hr_expense/views/res_users_views.xml b/addons/hr_expense/views/res_users_views.xml deleted file mode 100644 index 4225955c135..00000000000 --- a/addons/hr_expense/views/res_users_views.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - res.users.form.hr_expense.inherit - res.users - - 65 - - - - - - - diff --git a/addons/hr_holidays/__manifest__.py b/addons/hr_holidays/__manifest__.py index 4be06d2e2ab..a0e95455d20 100644 --- a/addons/hr_holidays/__manifest__.py +++ b/addons/hr_holidays/__manifest__.py @@ -38,7 +38,6 @@ A synchronization with an internal agenda (Meetings of the CRM module) is also p 'views/hr_leave_type_views.xml', 'views/hr_leave_allocation_views.xml', 'views/mail_activity_views.xml', - 'views/res_users_views.xml', 'wizard/hr_holidays_summary_department_views.xml', 'wizard/hr_holidays_summary_employees_views.xml', diff --git a/addons/hr_holidays/models/__init__.py b/addons/hr_holidays/models/__init__.py index 0465a571c55..621aeccd08b 100644 --- a/addons/hr_holidays/models/__init__.py +++ b/addons/hr_holidays/models/__init__.py @@ -6,4 +6,3 @@ from . import hr from . import hr_leave from . import hr_leave_allocation from . import hr_leave_type -from . import res_users diff --git a/addons/hr_holidays/models/res_users.py b/addons/hr_holidays/models/res_users.py deleted file mode 100644 index d38d6c63d82..00000000000 --- a/addons/hr_holidays/models/res_users.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - group_hr_holidays_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_hr_holidays'), - string='Leaves', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_hr_holidays') diff --git a/addons/hr_holidays/views/res_users_views.xml b/addons/hr_holidays/views/res_users_views.xml deleted file mode 100644 index 354a681e7c4..00000000000 --- a/addons/hr_holidays/views/res_users_views.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - res.users.form.hr_holidays.inherit - res.users - - 55 - - - - - - - diff --git a/addons/hr_payroll/__manifest__.py b/addons/hr_payroll/__manifest__.py index 34d2f3d9a10..73bb35e4a66 100644 --- a/addons/hr_payroll/__manifest__.py +++ b/addons/hr_payroll/__manifest__.py @@ -28,7 +28,6 @@ 'views/report_contributionregister_templates.xml', 'views/report_payslip_templates.xml', 'views/report_payslipdetails_templates.xml', - 'views/res_users_views.xml', ], 'demo': ['data/hr_payroll_demo.xml'], } diff --git a/addons/hr_payroll/models/__init__.py b/addons/hr_payroll/models/__init__.py index 5c4f1a7008e..99a241743cf 100644 --- a/addons/hr_payroll/models/__init__.py +++ b/addons/hr_payroll/models/__init__.py @@ -6,4 +6,3 @@ from . import hr_employee from . import res_config_settings from . import hr_salary_rule from . import hr_payslip -from . import res_users diff --git a/addons/hr_payroll/models/res_users.py b/addons/hr_payroll/models/res_users.py deleted file mode 100644 index 3c2fe5c78d4..00000000000 --- a/addons/hr_payroll/models/res_users.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - group_hr_payroll_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_hr_payroll'), - string='Payroll', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_hr_payroll') diff --git a/addons/hr_payroll/views/res_users_views.xml b/addons/hr_payroll/views/res_users_views.xml deleted file mode 100644 index 5d5fd491325..00000000000 --- a/addons/hr_payroll/views/res_users_views.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - res.users.form.hr_payroll.inherit - res.users - - 95 - - - - - - - diff --git a/addons/hr_recruitment/__manifest__.py b/addons/hr_recruitment/__manifest__.py index 2a3a732c797..925a16656d6 100644 --- a/addons/hr_recruitment/__manifest__.py +++ b/addons/hr_recruitment/__manifest__.py @@ -29,7 +29,6 @@ 'views/hr_department_views.xml', 'views/hr_job_views.xml', 'views/mail_activity_views.xml', - 'views/res_users_views.xml', 'views/digest_views.xml', ], 'demo': [ diff --git a/addons/hr_recruitment/models/__init__.py b/addons/hr_recruitment/models/__init__.py index c7991cbbff2..6eca3380470 100644 --- a/addons/hr_recruitment/models/__init__.py +++ b/addons/hr_recruitment/models/__init__.py @@ -4,5 +4,4 @@ from . import hr_employee from . import hr_job from . import res_config_settings from . import calendar -from . import res_users from . import digest diff --git a/addons/hr_recruitment/models/res_users.py b/addons/hr_recruitment/models/res_users.py deleted file mode 100644 index a251cbcdcad..00000000000 --- a/addons/hr_recruitment/models/res_users.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - group_hr_recruitment_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_hr_recruitment'), - string='Recruitment', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_hr_recruitment') diff --git a/addons/hr_recruitment/views/res_users_views.xml b/addons/hr_recruitment/views/res_users_views.xml deleted file mode 100644 index ea5eb7cef39..00000000000 --- a/addons/hr_recruitment/views/res_users_views.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - res.users.form.hr_recruitment.inherit - res.users - - 60 - - - - - - - diff --git a/addons/hr_timesheet/__manifest__.py b/addons/hr_timesheet/__manifest__.py index 520b5470cdd..71f3721ab44 100644 --- a/addons/hr_timesheet/__manifest__.py +++ b/addons/hr_timesheet/__manifest__.py @@ -32,7 +32,6 @@ up a management by affair. 'report/hr_timesheet_report_view.xml', 'report/report_timesheet_templates.xml', 'views/hr_views.xml', - 'views/res_users_views.xml', 'data/hr_timesheet_data.xml', ], 'demo': [ diff --git a/addons/hr_timesheet/models/__init__.py b/addons/hr_timesheet/models/__init__.py index f6758089f5c..ce7d853abca 100644 --- a/addons/hr_timesheet/models/__init__.py +++ b/addons/hr_timesheet/models/__init__.py @@ -8,5 +8,4 @@ from . import ir_http from . import res_company from . import res_config_settings from . import project -from . import res_users from . import uom diff --git a/addons/hr_timesheet/models/res_users.py b/addons/hr_timesheet/models/res_users.py deleted file mode 100644 index f5eb9addcb3..00000000000 --- a/addons/hr_timesheet/models/res_users.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - group_hr_timesheet_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_hr_timesheet'), - string='Timesheets', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_hr_timesheet') diff --git a/addons/hr_timesheet/views/res_users_views.xml b/addons/hr_timesheet/views/res_users_views.xml deleted file mode 100644 index c44ab31980d..00000000000 --- a/addons/hr_timesheet/views/res_users_views.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - res.users.form.hr_timesheet.inherit - res.users - - 70 - - - - - - - diff --git a/addons/im_livechat/__manifest__.py b/addons/im_livechat/__manifest__.py index 8ba7243e2c0..63a3061506b 100644 --- a/addons/im_livechat/__manifest__.py +++ b/addons/im_livechat/__manifest__.py @@ -25,7 +25,6 @@ Help your customers with this chat, and analyse their feedback. "views/mail_channel_views.xml", "views/im_livechat_channel_views.xml", "views/im_livechat_channel_templates.xml", - "views/res_users_views.xml", "report/im_livechat_report_channel_views.xml", "report/im_livechat_report_operator_views.xml" ], diff --git a/addons/im_livechat/models/__init__.py b/addons/im_livechat/models/__init__.py index 380ebfbfc00..7ed0b5e53f9 100644 --- a/addons/im_livechat/models/__init__.py +++ b/addons/im_livechat/models/__init__.py @@ -3,4 +3,3 @@ from . import im_livechat_channel from . import ir_autovacuum from . import mail_channel from . import rating -from . import res_users diff --git a/addons/im_livechat/models/res_users.py b/addons/im_livechat/models/res_users.py deleted file mode 100644 index af9f066dbc7..00000000000 --- a/addons/im_livechat/models/res_users.py +++ /dev/null @@ -1,14 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - group_im_livechat_user = fields.Selection( - selection=lambda self: self._get_group_selection('im_livechat.module_category_im_livechat'), - string='Live Support', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='im_livechat.module_category_im_livechat', - help='User: The user will be able to join support channels.\nManager: The user will be able to delete support channels.') diff --git a/addons/im_livechat/views/res_users_views.xml b/addons/im_livechat/views/res_users_views.xml deleted file mode 100644 index 57209008b8e..00000000000 --- a/addons/im_livechat/views/res_users_views.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - res.users.form.im_livechat.inherit - res.users - - 125 - - - - - - - diff --git a/addons/im_livechat_mail_bot/models/__init__.py b/addons/im_livechat_mail_bot/models/__init__.py index dc2d4b2fef6..ce6a8455179 100644 --- a/addons/im_livechat_mail_bot/models/__init__.py +++ b/addons/im_livechat_mail_bot/models/__init__.py @@ -2,4 +2,3 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import mail_bot -from . import res_users diff --git a/addons/lunch/__manifest__.py b/addons/lunch/__manifest__.py index 828f2f29f45..ef5bc49a52f 100644 --- a/addons/lunch/__manifest__.py +++ b/addons/lunch/__manifest__.py @@ -29,7 +29,6 @@ If you want to save your employees' time and avoid them to always have coins in 'views/lunch_templates.xml', 'wizard/lucky_order_view.xml', 'views/lunch_views.xml', - 'views/res_users_views.xml', 'data/lunch_data.xml', ], 'demo': ['data/lunch_demo.xml'], diff --git a/addons/lunch/models/__init__.py b/addons/lunch/models/__init__.py index e16f6c755b9..9824b4ad3b0 100644 --- a/addons/lunch/models/__init__.py +++ b/addons/lunch/models/__init__.py @@ -1,4 +1,3 @@ # -*- coding: utf-8 -*- from . import lunch -from . import res_users diff --git a/addons/lunch/models/res_users.py b/addons/lunch/models/res_users.py deleted file mode 100644 index 9d3a39d8974..00000000000 --- a/addons/lunch/models/res_users.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - group_lunch_user = fields.Selection( - selection=lambda self: self._get_group_selection('lunch.module_lunch_category'), - string='Lunch', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='lunch.module_lunch_category') diff --git a/addons/lunch/views/res_users_views.xml b/addons/lunch/views/res_users_views.xml deleted file mode 100644 index c414ba40d57..00000000000 --- a/addons/lunch/views/res_users_views.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - res.users.form.lunch.inherit - res.users - - 90 - - - - - - - diff --git a/addons/maintenance/__manifest__.py b/addons/maintenance/__manifest__.py index b9263c33b84..72f729da096 100644 --- a/addons/maintenance/__manifest__.py +++ b/addons/maintenance/__manifest__.py @@ -18,7 +18,6 @@ 'views/maintenance_views.xml', 'views/maintenance_templates.xml', 'views/mail_activity_views.xml', - 'views/res_users_views.xml', 'data/maintenance_cron.xml', ], 'demo': ['data/maintenance_demo.xml'], diff --git a/addons/maintenance/models/__init__.py b/addons/maintenance/models/__init__.py index 155a108ce15..fb9363f4230 100644 --- a/addons/maintenance/models/__init__.py +++ b/addons/maintenance/models/__init__.py @@ -1,4 +1,3 @@ # -*- coding: utf-8 -*- from . import maintenance -from . import res_users diff --git a/addons/maintenance/models/res_users.py b/addons/maintenance/models/res_users.py deleted file mode 100644 index cac599eda08..00000000000 --- a/addons/maintenance/models/res_users.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - has_group_equipment_manager = fields.Boolean( - 'Equipment Manager', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='maintenance.group_equipment_manager', - help='The user will be able to manage equipments.') diff --git a/addons/maintenance/views/res_users_views.xml b/addons/maintenance/views/res_users_views.xml deleted file mode 100644 index f606d3ad869..00000000000 --- a/addons/maintenance/views/res_users_views.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - res.users.form.maintenance.inherit - res.users - - - - - - - diff --git a/addons/mass_mailing/__manifest__.py b/addons/mass_mailing/__manifest__.py index 5b7cf80ad2c..cbf5279f176 100644 --- a/addons/mass_mailing/__manifest__.py +++ b/addons/mass_mailing/__manifest__.py @@ -37,7 +37,6 @@ 'views/themes_templates.xml', 'views/snippets_themes.xml', 'views/snippets_themes_options.xml', - 'views/res_users_views.xml', 'views/mass_mailing_blacklist_views.xml', 'views/unsubscribe_templates.xml', ], diff --git a/addons/mass_mailing/models/__init__.py b/addons/mass_mailing/models/__init__.py index baa418a63a9..6afa7a98d09 100644 --- a/addons/mass_mailing/models/__init__.py +++ b/addons/mass_mailing/models/__init__.py @@ -8,4 +8,3 @@ from . import mail_thread from . import res_config_settings from . import mass_mailing_report from . import link_tracker -from . import res_users diff --git a/addons/mass_mailing/models/res_users.py b/addons/mass_mailing/models/res_users.py deleted file mode 100644 index ab640806fb3..00000000000 --- a/addons/mass_mailing/models/res_users.py +++ /dev/null @@ -1,17 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - group_mass_mailing_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_mass_mailing'), - string='Email Marketing', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_mass_mailing') - - has_group_mass_mailing_campaign = fields.Boolean( - 'Manage Mass Mailing Campaigns', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='mass_mailing.group_mass_mailing_campaign') diff --git a/addons/mass_mailing/security/mass_mailing_security.xml b/addons/mass_mailing/security/mass_mailing_security.xml index 64b28137d2b..7026ea17834 100644 --- a/addons/mass_mailing/security/mass_mailing_security.xml +++ b/addons/mass_mailing/security/mass_mailing_security.xml @@ -10,6 +10,7 @@ Manage Mass Mailing Campaigns + diff --git a/addons/mass_mailing/views/res_users_views.xml b/addons/mass_mailing/views/res_users_views.xml deleted file mode 100644 index b4bd171931d..00000000000 --- a/addons/mass_mailing/views/res_users_views.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - res.users.form.mass_mailing.inherit - res.users - - 110 - - - - - - - - - diff --git a/addons/mrp/__manifest__.py b/addons/mrp/__manifest__.py index b5bc531c3a6..5877e0847be 100644 --- a/addons/mrp/__manifest__.py +++ b/addons/mrp/__manifest__.py @@ -34,7 +34,6 @@ 'views/res_config_settings_views.xml', 'views/mrp_templates.xml', 'views/stock_scrap_views.xml', - 'views/res_users_views.xml', 'report/mrp_report_views_main.xml', 'report/mrp_report_bom_structure.xml', 'report/mrp_production_templates.xml', diff --git a/addons/mrp/models/__init__.py b/addons/mrp/models/__init__.py index c5bf273536b..e9fc93bef6a 100644 --- a/addons/mrp/models/__init__.py +++ b/addons/mrp/models/__init__.py @@ -18,4 +18,3 @@ from . import stock_production_lot from . import stock_rule from . import stock_scrap from . import stock_warehouse -from . import res_users diff --git a/addons/mrp/models/res_users.py b/addons/mrp/models/res_users.py deleted file mode 100644 index 28ab5647155..00000000000 --- a/addons/mrp/models/res_users.py +++ /dev/null @@ -1,17 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - has_group_mrp_routings = fields.Boolean( - 'Manage Work Order Operations', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='mrp.group_mrp_routings') - - group_mrp_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_manufacturing'), - string='Manufacturing', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_manufacturing') diff --git a/addons/mrp/security/mrp_security.xml b/addons/mrp/security/mrp_security.xml index a71565130a7..96fe8252d27 100644 --- a/addons/mrp/security/mrp_security.xml +++ b/addons/mrp/security/mrp_security.xml @@ -17,6 +17,7 @@ Manage Work Order Operations + diff --git a/addons/mrp/views/res_users_views.xml b/addons/mrp/views/res_users_views.xml deleted file mode 100644 index b3bec2a27e8..00000000000 --- a/addons/mrp/views/res_users_views.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - res.users.form.mrp.inherit - res.users - - 25 - - - - - - - - - diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py index 0e776d1cd2e..7f341a9d4ab 100644 --- a/addons/payment/models/payment_acquirer.py +++ b/addons/payment/models/payment_acquirer.py @@ -723,7 +723,7 @@ class PaymentTransaction(models.Model): @api.multi def _set_transaction_done(self): '''Move the transaction's payment to the done state(e.g. Paypal).''' - if any(trans.state not in ('draft', 'authorized') for trans in self): + if any(trans.state not in ('draft', 'authorized', 'pending') for trans in self): raise ValidationError(_('Only draft/authorized transaction can be posted.')) self.write({'state': 'done', 'date': datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT)}) diff --git a/addons/point_of_sale/__manifest__.py b/addons/point_of_sale/__manifest__.py index 1cb4f10517e..6d58943fc26 100644 --- a/addons/point_of_sale/__manifest__.py +++ b/addons/point_of_sale/__manifest__.py @@ -35,9 +35,9 @@ 'views/account_statement_view.xml', 'views/account_statement_report.xml', 'views/res_config_settings_views.xml', - 'views/res_users_views.xml', 'views/digest_views.xml', 'views/res_partner_view.xml', + 'views/res_users_views.xml', 'views/report_statement.xml', 'views/report_userlabel.xml', 'views/report_saledetails.xml', diff --git a/addons/point_of_sale/models/res_users.py b/addons/point_of_sale/models/res_users.py index 05adba44a5d..6fb09e8a44e 100644 --- a/addons/point_of_sale/models/res_users.py +++ b/addons/point_of_sale/models/res_users.py @@ -9,11 +9,6 @@ class ResUsers(models.Model): pos_security_pin = fields.Char(string='Security PIN', size=32, help='A Security PIN used to protect sensible functionality in the Point of Sale') - group_point_of_sale_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_point_of_sale'), - string='Point of Sale', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_point_of_sale') - @api.constrains('pos_security_pin') def _check_pin(self): if self.pos_security_pin and not self.pos_security_pin.isdigit(): diff --git a/addons/point_of_sale/views/res_users_views.xml b/addons/point_of_sale/views/res_users_views.xml index 61a504fae59..c4a70ebd317 100644 --- a/addons/point_of_sale/views/res_users_views.xml +++ b/addons/point_of_sale/views/res_users_views.xml @@ -3,12 +3,8 @@ res.users.form.view res.users - - 130 + - - - diff --git a/addons/product/__manifest__.py b/addons/product/__manifest__.py index 51280c2d3a9..303ea61747d 100644 --- a/addons/product/__manifest__.py +++ b/addons/product/__manifest__.py @@ -37,7 +37,6 @@ Print product labels with barcode. 'views/product_template_views.xml', 'views/product_pricelist_views.xml', 'views/res_partner_views.xml', - 'views/res_users_views.xml', 'report/product_reports.xml', 'report/product_pricelist_templates.xml', 'report/product_product_templates.xml', diff --git a/addons/product/models/__init__.py b/addons/product/models/__init__.py index d92be9dfb05..57917450de3 100644 --- a/addons/product/models/__init__.py +++ b/addons/product/models/__init__.py @@ -15,4 +15,3 @@ from . import product_attribute from . import product_pricelist from . import res_company from . import res_partner -from . import res_users diff --git a/addons/product/models/res_users.py b/addons/product/models/res_users.py deleted file mode 100644 index ee0f0395725..00000000000 --- a/addons/product/models/res_users.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - has_group_sale_pricelist = fields.Boolean( - 'Sales Pricelists', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='product.group_sale_pricelist') - - has_group_pricelist_item = fields.Boolean( - 'Manage Pricelist Items', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='product.group_pricelist_item') - - has_group_product_pricelist = fields.Boolean( - 'Pricelists On Product', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='product.group_product_pricelist') - - has_group_stock_packaging = fields.Boolean( - 'Manage Product Packaging', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='product.group_stock_packaging') - - has_group_product_variant = fields.Boolean( - 'Manage Product Variants', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='product.group_product_variant') diff --git a/addons/product/security/product_security.xml b/addons/product/security/product_security.xml index 08fec11f11c..422d7ac75ec 100644 --- a/addons/product/security/product_security.xml +++ b/addons/product/security/product_security.xml @@ -4,22 +4,27 @@ Sales Pricelists + Manage Pricelist Items + Pricelists On Product + Manage Product Packaging + Manage Product Variants + diff --git a/addons/product/views/res_users_views.xml b/addons/product/views/res_users_views.xml deleted file mode 100644 index a8c05f84437..00000000000 --- a/addons/product/views/res_users_views.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - res.users.form.product.inherit - res.users - - - - - - - diff --git a/addons/project/__manifest__.py b/addons/project/__manifest__.py index a2d56f1624f..530cb4fb8b8 100644 --- a/addons/project/__manifest__.py +++ b/addons/project/__manifest__.py @@ -32,7 +32,6 @@ 'views/project_assets.xml', 'views/project_portal_templates.xml', 'views/project_rating_templates.xml', - 'views/res_users_views.xml', 'data/digest_data.xml', 'data/project_mail_template_data.xml', 'data/project_data.xml', diff --git a/addons/project/models/__init__.py b/addons/project/models/__init__.py index 5a94820d26e..c796777486b 100644 --- a/addons/project/models/__init__.py +++ b/addons/project/models/__init__.py @@ -4,5 +4,4 @@ from . import project from . import res_config_settings from . import res_partner -from . import res_users from . import digest diff --git a/addons/project/models/res_users.py b/addons/project/models/res_users.py deleted file mode 100644 index f304dc2d826..00000000000 --- a/addons/project/models/res_users.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - has_group_subtask_project = fields.Boolean( - 'Use Subtasks', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='project.group_subtask_project') - - has_group_project_rating = fields.Boolean( - 'Use Rating on Project', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='project.group_project_rating') - - group_project_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_project_management'), - string='Project', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_project_management') diff --git a/addons/project/security/project_security.xml b/addons/project/security/project_security.xml index f064bd6ba86..ca80bbbd615 100644 --- a/addons/project/security/project_security.xml +++ b/addons/project/security/project_security.xml @@ -16,10 +16,12 @@ Use Subtasks + Use Rating on Project + diff --git a/addons/project/views/res_users_views.xml b/addons/project/views/res_users_views.xml deleted file mode 100644 index a31d5978ba6..00000000000 --- a/addons/project/views/res_users_views.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - res.users.form.project.inherit - res.users - - 5 - - - - - - - - - diff --git a/addons/purchase/__manifest__.py b/addons/purchase/__manifest__.py index d82795f39e2..78d6e761690 100644 --- a/addons/purchase/__manifest__.py +++ b/addons/purchase/__manifest__.py @@ -25,7 +25,6 @@ 'report/purchase_report_views.xml', 'data/mail_template_data.xml', 'views/portal_templates.xml', - 'views/res_users_views.xml', 'report/purchase_order_templates.xml', 'report/purchase_quotation_templates.xml', ], diff --git a/addons/purchase/models/__init__.py b/addons/purchase/models/__init__.py index 5aac1667e5d..a641ce05a71 100644 --- a/addons/purchase/models/__init__.py +++ b/addons/purchase/models/__init__.py @@ -7,4 +7,3 @@ from . import product from . import res_company from . import res_config_settings from . import res_partner -from . import res_users diff --git a/addons/purchase/models/res_users.py b/addons/purchase/models/res_users.py deleted file mode 100644 index 6366922e6fa..00000000000 --- a/addons/purchase/models/res_users.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - has_group_manage_vendor_price = fields.Boolean( - 'Manage Vendor Price', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='purchase.group_manage_vendor_price') - - has_group_warning_purchase = fields.Boolean( - 'A warning can be set on a product or a customer (Purchase)', - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='purchase.group_warning_purchase') - - group_purchase_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_purchase_management'), - string='Purchase', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_purchase_management') diff --git a/addons/purchase/security/purchase_security.xml b/addons/purchase/security/purchase_security.xml index 26fbbf5d8c9..85860353f21 100644 --- a/addons/purchase/security/purchase_security.xml +++ b/addons/purchase/security/purchase_security.xml @@ -17,10 +17,12 @@ Manage Vendor Price + A warning can be set on a product or a customer (Purchase) + diff --git a/addons/purchase/views/res_users_views.xml b/addons/purchase/views/res_users_views.xml deleted file mode 100644 index 54e0eb080f5..00000000000 --- a/addons/purchase/views/res_users_views.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - res.users.form.purchase.inherit - res.users - - 45 - - - - - - - - - diff --git a/addons/purchase_requisition/tests/test_purchase_requisition.py b/addons/purchase_requisition/tests/test_purchase_requisition.py index 165eff03d8a..b88a4af5cc9 100644 --- a/addons/purchase_requisition/tests/test_purchase_requisition.py +++ b/addons/purchase_requisition/tests/test_purchase_requisition.py @@ -23,11 +23,11 @@ class TestPurchaseRequisition(common.TransactionCase): # Create a user as 'Purchase Requisition Manager' self.res_users_purchase_requisition_manager = self.ResUser.create({'company_id': self.res_company_id, 'name': 'Purchase requisition Manager', 'login': 'prm', 'email': 'requisition_manager@yourcompany.com'}) # Added groups for Purchase Requisition Manager. - self.res_users_purchase_requisition_manager.groups_id = [(4, self.ref('purchase.group_purchase_manager'), False)] + self.res_users_purchase_requisition_manager.group_id = self.ref('purchase.group_purchase_manager') # Create a user as 'Purchase Requisition User' self.res_users_purchase_requisition_user = self.ResUser.create({'company_id': self.res_company_id, 'name': 'Purchase requisition User', 'login': 'pru', 'email': 'requisition_user@yourcompany.com'}) # Added groups for Purchase Requisition User. - self.res_users_purchase_requisition_user.groups_id = [(4, self.ref('purchase.group_purchase_user'), False)] + self.res_users_purchase_requisition_user.group_id = self.ref('purchase.group_purchase_user') # In order to test process of the purchase requisition ,create requisition self.requisition1 = self.env['purchase.requisition'].create({'line_ids': [(0, 0, {'product_id': self.product_09_id, 'product_qty': 10.0, 'product_uom_id': self.product_09_uom_id})]}) diff --git a/addons/sale/__manifest__.py b/addons/sale/__manifest__.py index 684f72b31c1..03ea9b45f96 100644 --- a/addons/sale/__manifest__.py +++ b/addons/sale/__manifest__.py @@ -30,7 +30,6 @@ This module contains all the common features of Sales Management and eCommerce. 'views/sale_portal_templates.xml', 'views/sale_onboarding_views.xml', 'views/res_config_settings_views.xml', - 'views/res_users_views.xml', 'views/payment_views.xml', ], 'demo': [ diff --git a/addons/sale/models/__init__.py b/addons/sale/models/__init__.py index 293a9217d67..1f6f027cba3 100644 --- a/addons/sale/models/__init__.py +++ b/addons/sale/models/__init__.py @@ -11,5 +11,4 @@ from . import res_partner from . import sale from . import res_config_settings from . import sales_team -from . import res_users from . import payment diff --git a/addons/sale/models/res_users.py b/addons/sale/models/res_users.py deleted file mode 100644 index 0e5c5364472..00000000000 --- a/addons/sale/models/res_users.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - has_group_warning_sale = fields.Boolean( - "A warning can be set on a product or a customer (Sale)", - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='sale.group_warning_sale') - - has_group_delivery_invoice_address = fields.Boolean( - "Addresses in Sales Orders", - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='sale.group_delivery_invoice_address') - - has_group_discount_per_so_line = fields.Boolean( - "Discount on lines", - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='sale.group_discount_per_so_line') - - has_group_proforma_sales = fields.Boolean( - "Pro-forma Invoices", - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='sale.group_proforma_sales') - - has_group_sale_order_dates = fields.Boolean( - "Sale Order Dates", - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='sale.group_sale_order_dates') diff --git a/addons/sale/security/sale_security.xml b/addons/sale/security/sale_security.xml index 6c5134c0b9f..454a93ffb37 100644 --- a/addons/sale/security/sale_security.xml +++ b/addons/sale/security/sale_security.xml @@ -3,22 +3,27 @@ Addresses in Sales Orders + Discount on lines + A warning can be set on a product or a customer (Sale) + Pro-forma Invoices + Manage delivery dates from sales orders. + This option introduces extra fields in the sales order to easily schedule product deliveries on your own: expected date, commitment date, effective date. diff --git a/addons/sale/tests/test_sale_common.py b/addons/sale/tests/test_sale_common.py index 17ad437a10e..4cac0a30702 100644 --- a/addons/sale/tests/test_sale_common.py +++ b/addons/sale/tests/test_sale_common.py @@ -17,7 +17,7 @@ class TestSale(AccountingTestCase): 'email': 'a.m@example.com', 'signature': '--\nAndreww', 'notification_type': 'email', - 'groups_id': [(6, 0, [group_manager.id])] + 'groups_id': [(6, 0, [group_manager.id, self.env.ref('base.group_user').id])] }) self.user = self.env['res.users'].create({ 'name': 'Mark User', diff --git a/addons/sale/tests/test_sale_order.py b/addons/sale/tests/test_sale_order.py index 5e59b6b7a83..78a1afec795 100644 --- a/addons/sale/tests/test_sale_order.py +++ b/addons/sale/tests/test_sale_order.py @@ -21,8 +21,9 @@ class TestSaleOrder(TestCommonSaleNoChart): cls.setUpUsers() group_salemanager = cls.env.ref('sales_team.group_sale_manager') group_salesman = cls.env.ref('sales_team.group_sale_salesman') - cls.user_manager.write({'groups_id': [(6, 0, [group_salemanager.id])]}) - cls.user_employee.write({'groups_id': [(6, 0, [group_salesman.id])]}) + group_employee = cls.env.ref('base.group_user') + cls.user_manager.write({'groups_id': [(6, 0, [group_salemanager.id, group_employee.id])]}) + cls.user_employee.write({'groups_id': [(6, 0, [group_salesman.id, group_employee.id])]}) # set up accounts and products and journals cls.setUpAdditionalAccounts() diff --git a/addons/sale/views/res_users_views.xml b/addons/sale/views/res_users_views.xml deleted file mode 100644 index ea8a9831de5..00000000000 --- a/addons/sale/views/res_users_views.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - res.users.form.sale.inherit - res.users - - - - - - - diff --git a/addons/sale_management/models/__init__.py b/addons/sale_management/models/__init__.py index 658b0890cf3..d4ea9bb0f8f 100644 --- a/addons/sale_management/models/__init__.py +++ b/addons/sale_management/models/__init__.py @@ -2,6 +2,5 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import digest from . import res_config_settings -from . import res_users from . import sale_order from . import sale_order_template diff --git a/addons/sale_management/models/res_users.py b/addons/sale_management/models/res_users.py deleted file mode 100644 index 1484e3c0cfa..00000000000 --- a/addons/sale_management/models/res_users.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - has_group_sale_order_template = fields.Boolean( - "Quotation Templates", - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='sale_management.group_sale_order_template') diff --git a/addons/sale_stock/__manifest__.py b/addons/sale_stock/__manifest__.py index 101882305bd..73b46286776 100644 --- a/addons/sale_stock/__manifest__.py +++ b/addons/sale_stock/__manifest__.py @@ -28,7 +28,6 @@ Preferences 'views/res_config_settings_views.xml', 'views/account_invoice_views.xml', 'views/sale_stock_portal_template.xml', - 'views/res_users_views.xml', 'views/stock_production_lot_views.xml', 'report/sale_order_report_templates.xml', 'report/stock_report_deliveryslip.xml', diff --git a/addons/sale_stock/models/__init__.py b/addons/sale_stock/models/__init__.py index 9aa9277bfe8..1f11274e77a 100644 --- a/addons/sale_stock/models/__init__.py +++ b/addons/sale_stock/models/__init__.py @@ -7,4 +7,3 @@ from . import sale_order from . import res_config_settings from . import stock from . import product_template -from . import res_users diff --git a/addons/sale_stock/models/res_users.py b/addons/sale_stock/models/res_users.py deleted file mode 100644 index 5a1cbd2e1e5..00000000000 --- a/addons/sale_stock/models/res_users.py +++ /dev/null @@ -1,18 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - has_group_route_so_lines = fields.Boolean( - 'Enable Route on Sales Order Line', - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='sale_stock.group_route_so_lines') - - has_group_display_incoterm = fields.Boolean( - 'Display incoterms on Sales Order and related invoices', - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='sale_stock.group_display_incoterm') diff --git a/addons/sale_stock/security/sale_stock_security.xml b/addons/sale_stock/security/sale_stock_security.xml index 72c18b01594..630a6512b2d 100644 --- a/addons/sale_stock/security/sale_stock_security.xml +++ b/addons/sale_stock/security/sale_stock_security.xml @@ -3,10 +3,12 @@ Enable Route on Sales Order Line - + + Display incoterms on Sales Order and related invoices - + + diff --git a/addons/sale_stock/views/res_users_views.xml b/addons/sale_stock/views/res_users_views.xml deleted file mode 100644 index 99d64a9f5a8..00000000000 --- a/addons/sale_stock/views/res_users_views.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - res.users.form.sale_stock.inherit - res.users - - - - - - - diff --git a/addons/sales_team/__manifest__.py b/addons/sales_team/__manifest__.py index 3ecf2d97481..471fc179c52 100644 --- a/addons/sales_team/__manifest__.py +++ b/addons/sales_team/__manifest__.py @@ -16,7 +16,6 @@ Using this application you can manage Sales Teams with CRM and/or Sales 'data/sales_team_data.xml', 'views/crm_team_views.xml', 'views/sales_team_dashboard.xml', - 'views/res_users_views.xml', ], 'demo': ['data/sales_team_demo.xml'], 'installable': True, diff --git a/addons/sales_team/models/res_users.py b/addons/sales_team/models/res_users.py index 6ce0c27f983..7a31376f887 100644 --- a/addons/sales_team/models/res_users.py +++ b/addons/sales_team/models/res_users.py @@ -11,12 +11,6 @@ class ResUsers(models.Model): 'crm.team', "User's Sales Team", help='Sales Team the user is member of. Used to compute the members of a Sales Team through the inverse one2many') - group_sales_team_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_sales_management'), - string="Sales", compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_sales_management', - help='User: Own Documents Only: the user will have access to his own data in the sales application.\nUser: All Documents: the user will have access to all records of everyone in the sales application.\nManager: the user will have an access to the sales configuration as well as statistic reports.') - @api.model def create(self, vals): # Assign the new user in the sales team if there's only one sales team of type `Sales` diff --git a/addons/sales_team/views/res_users_views.xml b/addons/sales_team/views/res_users_views.xml deleted file mode 100644 index 2bdb98fe0a7..00000000000 --- a/addons/sales_team/views/res_users_views.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - res.users.form.sales_team.inherit - res.users - - 1 - - - - - - - diff --git a/addons/stock/__manifest__.py b/addons/stock/__manifest__.py index 4b5bbb8f677..a9010b63ac2 100644 --- a/addons/stock/__manifest__.py +++ b/addons/stock/__manifest__.py @@ -64,7 +64,6 @@ 'views/report_stock_traceability.xml', 'views/stock_template.xml', 'views/stock_rule_views.xml', - 'views/res_users_views.xml', 'views/stock_package_level_views.xml', 'data/default_barcode_patterns.xml', diff --git a/addons/stock/models/__init__.py b/addons/stock/models/__init__.py index c34d37f6d19..6a103f06041 100644 --- a/addons/stock/models/__init__.py +++ b/addons/stock/models/__init__.py @@ -18,5 +18,4 @@ from . import stock_traceability from . import stock_warehouse from . import stock_scrap from . import product -from . import res_users from . import stock_package_level diff --git a/addons/stock/models/res_users.py b/addons/stock/models/res_users.py deleted file mode 100644 index 2a8cf3e743d..00000000000 --- a/addons/stock/models/res_users.py +++ /dev/null @@ -1,53 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - has_group_stock_multi_locations = fields.Boolean( - 'Manage Multiple Stock Locations', - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='stock.group_stock_multi_locations') - - has_group_stock_multi_warehouses = fields.Boolean( - 'Manage Multiple Warehouses', - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='stock.group_stock_multi_warehouses') - - has_group_production_lot = fields.Boolean( - 'Manage Lots / Serial Numbers', - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='stock.group_production_lot') - - has_group_lot_on_delivery_slip = fields.Boolean( - 'Display Lots & Serial Numbers on Delivery Slip', - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='stock.group_lot_on_delivery_slip') - - has_group_tracking_lot = fields.Boolean( - 'Manage Packages', - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='stock.group_tracking_lot') - - has_group_adv_location = fields.Boolean( - 'Manage Push and Pull inventory flows', - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='stock.group_adv_location') - - has_group_tracking_owner = fields.Boolean( - 'Manage Different Stock Owners', - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='stock.group_tracking_owner') - - has_group_warning_stock = fields.Boolean( - 'A warning can be set on a partner (Stock)', - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='stock.group_warning_stock') - - group_stock_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_warehouse_management'), - string='Inventory', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_warehouse_management') diff --git a/addons/stock/security/stock_security.xml b/addons/stock/security/stock_security.xml index f1ea0759215..c48b0627077 100644 --- a/addons/stock/security/stock_security.xml +++ b/addons/stock/security/stock_security.xml @@ -4,10 +4,12 @@ Manage Multiple Stock Locations + Manage Multiple Warehouses + @@ -24,6 +26,7 @@ Manage Lots / Serial Numbers + @@ -33,18 +36,22 @@ Manage Packages + Manage Push and Pull inventory flows + Manage Different Stock Owners + A warning can be set on a partner (Stock) + diff --git a/addons/stock/views/res_users_views.xml b/addons/stock/views/res_users_views.xml deleted file mode 100644 index beb0aaf5951..00000000000 --- a/addons/stock/views/res_users_views.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - res.users.form.stock.inherit - res.users - - 15 - - - - - - - - - diff --git a/addons/survey/__manifest__.py b/addons/survey/__manifest__.py index 697b784c7b4..6304e6957d7 100644 --- a/addons/survey/__manifest__.py +++ b/addons/survey/__manifest__.py @@ -1,4 +1,3 @@ - # -*- encoding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. { @@ -24,7 +23,6 @@ sent mails with personal token for the invitation of the survey. 'views/survey_views.xml', 'views/survey_templates.xml', 'views/survey_result.xml', - 'views/res_users_views.xml', 'data/mail_template_data.xml', 'wizard/survey_email_compose_message.xml', 'data/survey_stages.xml', diff --git a/addons/survey/models/__init__.py b/addons/survey/models/__init__.py index 1acf09ab8d1..26edfafe8ad 100644 --- a/addons/survey/models/__init__.py +++ b/addons/survey/models/__init__.py @@ -3,4 +3,3 @@ from . import survey from . import ir_autovacuum -from . import res_users diff --git a/addons/survey/models/res_users.py b/addons/survey/models/res_users.py deleted file mode 100644 index 939db447151..00000000000 --- a/addons/survey/models/res_users.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - group_survey_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_survey'), - string='Survey', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_survey') diff --git a/addons/survey/views/res_users_views.xml b/addons/survey/views/res_users_views.xml deleted file mode 100644 index dd60ccd8fae..00000000000 --- a/addons/survey/views/res_users_views.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - res.users.form.survey.inherit - res.users - - 115 - - - - - - - diff --git a/addons/uom/__manifest__.py b/addons/uom/__manifest__.py index 0ecdf757599..390682370b9 100644 --- a/addons/uom/__manifest__.py +++ b/addons/uom/__manifest__.py @@ -15,7 +15,6 @@ This is the base module for managing Units of measure. 'security/uom_security.xml', 'security/ir.model.access.csv', 'views/uom_uom_views.xml', - 'views/res_users_views.xml', ], 'installable': True, 'auto_install': False, diff --git a/addons/uom/models/__init__.py b/addons/uom/models/__init__.py index 4b4c458ea60..357b0410c00 100644 --- a/addons/uom/models/__init__.py +++ b/addons/uom/models/__init__.py @@ -2,4 +2,3 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import uom_uom -from . import res_users diff --git a/addons/uom/models/res_users.py b/addons/uom/models/res_users.py deleted file mode 100644 index 8281b78ef14..00000000000 --- a/addons/uom/models/res_users.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - has_group_uom = fields.Boolean( - 'Manage Multiple Units of Measure', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='uom.group_uom') diff --git a/addons/uom/security/uom_security.xml b/addons/uom/security/uom_security.xml index f7e0c0dd4d4..ebfe3e1178d 100644 --- a/addons/uom/security/uom_security.xml +++ b/addons/uom/security/uom_security.xml @@ -3,6 +3,7 @@ Manage Multiple Units of Measure + diff --git a/addons/uom/views/res_users_views.xml b/addons/uom/views/res_users_views.xml deleted file mode 100644 index 55240233a6a..00000000000 --- a/addons/uom/views/res_users_views.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - res.users.form.uom.inherit - res.users - - - - - - - diff --git a/addons/website/__manifest__.py b/addons/website/__manifest__.py index 463ec49af7c..fe7c479d23e 100644 --- a/addons/website/__manifest__.py +++ b/addons/website/__manifest__.py @@ -28,7 +28,6 @@ 'views/res_config_settings_views.xml', 'views/ir_actions_views.xml', 'views/ir_attachment_views.xml', - 'views/res_users_views.xml', 'views/res_partner_views.xml', 'wizard/base_language_install_views.xml', ], diff --git a/addons/website/models/__init__.py b/addons/website/models/__init__.py index 0ed659a8f3a..efeda5560b7 100644 --- a/addons/website/models/__init__.py +++ b/addons/website/models/__init__.py @@ -13,4 +13,3 @@ from . import res_company from . import res_partner from . import res_users from . import res_config_settings -from . import res_users diff --git a/addons/website/models/res_users.py b/addons/website/models/res_users.py index 3f9697eb797..6d701f55241 100644 --- a/addons/website/models/res_users.py +++ b/addons/website/models/res_users.py @@ -12,15 +12,6 @@ _logger = logging.getLogger(__name__) class ResUsers(models.Model): _inherit = 'res.users' - group_website_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_website'), - string='Website Editor', compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_website') - has_group_multi_website = fields.Boolean( - 'Multi-Websites', - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='website.group_multi_website') - _sql_constraints = [ # this is done in Python because a SQL constraint like UNIQUE # (login, website_id) allows ('abc', NULL) and diff --git a/addons/website/views/res_users_views.xml b/addons/website/views/res_users_views.xml deleted file mode 100644 index c17e79697c7..00000000000 --- a/addons/website/views/res_users_views.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - res.users.form.website.inherit - res.users - - 135 - - - - - - - diff --git a/addons/website_mass_mailing/__manifest__.py b/addons/website_mass_mailing/__manifest__.py index ac128e27a7f..3691d7a5da7 100644 --- a/addons/website_mass_mailing/__manifest__.py +++ b/addons/website_mass_mailing/__manifest__.py @@ -17,7 +17,6 @@ On a simple click, your visitors can subscribe to mailing lists managed in the E 'views/snippets_templates.xml', 'views/mass_mailing_view.xml', 'views/res_config_settings_views.xml', - 'views/res_users_views.xml', ], 'auto_install': True, } diff --git a/addons/website_mass_mailing/models/__init__.py b/addons/website_mass_mailing/models/__init__.py index d70758fc817..c9e39e466ca 100644 --- a/addons/website_mass_mailing/models/__init__.py +++ b/addons/website_mass_mailing/models/__init__.py @@ -3,4 +3,3 @@ from . import res_config_settings from . import mass_mailing_list -from . import res_users diff --git a/addons/website_mass_mailing/models/res_users.py b/addons/website_mass_mailing/models/res_users.py deleted file mode 100644 index 6024234dfb9..00000000000 --- a/addons/website_mass_mailing/models/res_users.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResUsers(models.Model): - _inherit = 'res.users' - - has_group_website_popup_on_exit = fields.Boolean( - 'Use subscription pop up on the website', - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='website_mass_mailing.group_website_popup_on_exit') diff --git a/addons/website_mass_mailing/security/mass_mailing_security.xml b/addons/website_mass_mailing/security/mass_mailing_security.xml index 95c9d4d56bb..1f1438fcab3 100644 --- a/addons/website_mass_mailing/security/mass_mailing_security.xml +++ b/addons/website_mass_mailing/security/mass_mailing_security.xml @@ -2,5 +2,6 @@ Use subscription pop up on the website + diff --git a/addons/website_mass_mailing/views/res_users_views.xml b/addons/website_mass_mailing/views/res_users_views.xml deleted file mode 100644 index dc0b20a84f0..00000000000 --- a/addons/website_mass_mailing/views/res_users_views.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - res.users.form.website_mass_mailing.inherit - res.users - - - - - - - diff --git a/addons/website_theme_install/views/views.xml b/addons/website_theme_install/views/views.xml index 95f9990be35..7cbe6d7102e 100644 --- a/addons/website_theme_install/views/views.xml +++ b/addons/website_theme_install/views/views.xml @@ -79,7 +79,7 @@ diff --git a/odoo/addons/base/data/ir_module_category_data.xml b/odoo/addons/base/data/ir_module_category_data.xml index 7d8b3803208..3346121dbbc 100644 --- a/odoo/addons/base/data/ir_module_category_data.xml +++ b/odoo/addons/base/data/ir_module_category_data.xml @@ -1,6 +1,12 @@ + + Technical Settings + 0 + + + Localization @@ -185,6 +191,16 @@ 100 + + Extra Rights + 101 + + + + Other Extra Rights + 102 + + @@ -197,6 +213,18 @@ + + + + + + + + + + + + @@ -205,5 +233,9 @@ + + + + diff --git a/odoo/addons/base/data/res_users_demo.xml b/odoo/addons/base/data/res_users_demo.xml index 38d6323ee28..7e0bfa7dfec 100644 --- a/odoo/addons/base/data/res_users_demo.xml +++ b/odoo/addons/base/data/res_users_demo.xml @@ -82,9 +82,6 @@ - - - diff --git a/odoo/addons/base/models/res_users.py b/odoo/addons/base/models/res_users.py index e966b2172b4..1f88db8d519 100644 --- a/odoo/addons/base/models/res_users.py +++ b/odoo/addons/base/models/res_users.py @@ -42,23 +42,48 @@ DEFAULT_CRYPT_CONTEXT = passlib.context.CryptContext( concat = chain.from_iterable # -# Functions for manipulating boolean and selection fields +# Functions for manipulating boolean and selection pseudo-fields # +def name_boolean_group(id): + return 'in_group_' + str(id) +def name_selection_groups(ids): + return 'sel_groups_' + '_'.join(str(it) for it in ids) def is_boolean_group(name): - return name.startswith('has_group_') - + return name.startswith('in_group_') def is_selection_groups(name): - return name.startswith('group_') + return name.startswith('sel_groups_') +def is_reified_group(name): + return is_boolean_group(name) or is_selection_groups(name) + +def get_boolean_group(name): + return int(name[9:]) + +def get_selection_groups(name): + return [int(v) for v in name[11:].split('_')] + +def parse_m2m(commands): + "return a list of ids corresponding to a many2many value" + ids = [] + for command in commands: + if isinstance(command, (tuple, list)): + if command[0] in (1, 4): + ids.append(command[1]) + elif command[0] == 5: + ids = [] + elif command[0] == 6: + ids = list(command[2]) + else: + ids.append(command) + return ids #---------------------------------------------------------- # Basic res.groups and res.users #---------------------------------------------------------- - class Groups(models.Model): _name = "res.groups" _description = "Access Groups" @@ -225,36 +250,6 @@ class Users(models.Model): name = fields.Char(related='partner_id.name', inherited=True) email = fields.Char(related='partner_id.email', inherited=True) - group_base_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_user_type'), - string="User type", compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_user_type') - - group_administration_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_administration'), - string="Administration", compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_administration') - - has_group_multi_company = fields.Boolean( - 'Multi Companies', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='base.group_multi_company') - - has_group_multi_currency = fields.Boolean( - 'Multi Currencies', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='base.group_multi_currency') - - has_group_no_one = fields.Boolean( - 'Technical Features', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='base.group_no_one') - - has_group_partner_manager = fields.Boolean( - 'Contact Creation', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='base.group_partner_manager') - - has_group_private_addresses = fields.Boolean( - 'Access to Private Addresses', compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='base.group_private_addresses') - _sql_constraints = [ ('login_key', 'UNIQUE (login)', 'You can not have two users with the same login !') ] @@ -738,111 +733,6 @@ class Users(models.Model): """ return DEFAULT_CRYPT_CONTEXT - def _add_missing_default_values(self, vals): - # Remove the default values of 'group_' and 'has_group' fields at the user creation if - # 'groups_id' is provided in vals and if the fields value is not explicitely given. - res = super(Users, self)._add_missing_default_values(vals) - return { - key: value for (key, value) in res.items() - if 'groups_id' not in vals or key in vals or (not is_boolean_group(key) or not is_selection_groups(key)) - } - - def _get_group_selection(self, category_xml_id): - """Returns the ordered selection for the given ir.module.category xmlid""" - category = self.env.ref(category_xml_id, raise_if_not_found=False) - selections = [] - if category: - groups = self.env['res.groups'].search([('category_id', '=', category.id)]) - order = {group: len(group.trans_implied_ids & groups) for group in groups} - groups = groups.sorted(key=order.get) - selections = [(group.id, group.name) for group in groups] - return selections - - @api.depends('groups_id') - def _compute_groups_id(self): - """Set the value for the group field (Selection of Boolean) - according to the user's groups, defined by the field 'groups_id'. - - These methods should be used on fields defined like one the - following cases: - 1. A boolean field with the compute/inverse method and an attribute - group_xml_id. - Example: - has_group_discount_per_so_line = fields.Boolean( - "Discount on lines", - compute='_compute_groups_id', inverse='_inverse_groups_id', - group_xml_id='sale.group_discount_per_so_line') - 2. A selection field with the compute/inverse method, the selection - got from '_get_group_selection' and an attribute - 'category_xml_id' - Example: - group_sales_team_user = fields.Selection( - selection=lambda self: self._get_group_selection('base.module_category_sales_management'), - string="Sales", compute='_compute_groups_id', inverse='_inverse_groups_id', - category_xml_id='base.module_category_sales_management') - The fields will be set according to the groups defined in the 'groups_id' - field, and the 'groups_id' field will be modified according to the - modifications made on theses fields when saving the users's form. - """ - computed_group_fields = [field for field in self._field_computed.keys() if field.compute == '_compute_groups_id'] - all_groups = self.env['res.groups'].search([]) - category_data = {} - # Retrieve groups order for selection fields - for field in computed_group_fields: - if getattr(field, 'category_xml_id', None): - category = self.env.ref(getattr(field, 'category_xml_id')) - category_groups = all_groups.filtered(lambda group: group.category_id == category) - order = {group: len(group.trans_implied_ids & category_groups) for group in category_groups} - category_data[field] = category_groups.sorted(key=order.get, reverse=True) - # Set group fields values according to groups_id - for user in self: - for field in computed_group_fields: - # Checkbox group - if getattr(field, 'group_xml_id', None): - group = self.env.ref(getattr(field, 'group_xml_id')) - user[field.name] = group in user.groups_id - # Selection group - elif getattr(field, 'category_xml_id', None): - # Take the highest level group the user belongs to - user_groups = [group for group in category_data[field] if group in user.groups_id] - user[field.name] = user_groups[0].id if user_groups else False - else: - _logger.warning(_("No 'group_xml_id' or 'category_xml_id' is set on the computed field %s linked to the method _compute_groups_id") % (field.name)) - - def _inverse_groups_id(self): - """Update 'groups_id' according the group fields values in cache.""" - computed_group_fields = [field for field in self._field_computed.keys() if field.compute == '_compute_groups_id'] - all_groups = self.env['res.groups'].search([]) - for user in self: - # we need to read all values in cache before any prefetch - field_values = {field: user[field.name] for field in computed_group_fields if field.name in user._cache} - groups_id_vals = [] - for field, value in field_values.items(): - # Checkbox group - if getattr(field, 'group_xml_id', None): - selected_group = self.env.ref(getattr(field, 'group_xml_id')) - if value: - groups_id_vals.append((4, selected_group.id)) - else: - groups_id_vals.append((3, selected_group.id)) - for group in selected_group.trans_implied_ids: - groups_id_vals.append((4, group.id)) - # Selection group - elif getattr(field, 'category_xml_id', None): - category = self.env.ref(getattr(field, 'category_xml_id')) - category_groups = [group for group in all_groups if group.category_id == category] - selected_group = [group for group in all_groups if group.id == value] - selected_group = selected_group and selected_group[0] or self.env['res.groups'] - for group in category_groups: - if group in (selected_group.trans_implied_ids | selected_group): - groups_id_vals.append((4, group.id)) - else: - groups_id_vals.append((3, group.id)) - else: - _logger.warning(_("No 'group_xml_id' or 'category_xml_id' is set on the computed field %s linked to the method _compute_groups_id") % (field.name)) - if groups_id_vals: - user.write({'groups_id': groups_id_vals}) - @contextlib.contextmanager def _assert_can_auth(self): """ Checks that the current environment even allows the current auth @@ -939,7 +829,6 @@ class Users(models.Model): def _register_hook(self): if hasattr(self, 'check_credentials'): _logger.warn("The check_credentials method of res.users has been renamed _check_credentials. One of your installed modules defines one, but it will not be called anymore.") - # # Implied groups # @@ -977,7 +866,6 @@ class GroupsImplied(models.Model): @api.multi def write(self, values): res = super(GroupsImplied, self).write(values) - if values.get('users') or values.get('implied_ids'): # add all implied groups (to all users of each group) for group in self: @@ -1004,11 +892,309 @@ class UsersImplied(models.Model): if values.get('groups_id'): # add implied groups for all users for user in self.with_context({}): - gs = set(concat(g.trans_implied_ids for g in user.groups_id)) - vals = {'groups_id': [(4, g.id) for g in gs]} + if not user.has_group('base.group_user'): + vals = {'groups_id': [(5, 0, 0)] + values['groups_id']} + else: + gs = set(concat(g.trans_implied_ids for g in user.groups_id)) + vals = {'groups_id': [(4, g.id) for g in gs]} super(UsersImplied, user).write(vals) return res +# +# Virtual checkbox and selection for res.user form view +# +# Extension of res.groups and res.users for the special groups view in the users +# form. This extension presents groups with selection and boolean widgets: +# - Groups are shown by application, with boolean and/or selection fields. +# Selection fields typically defines a role "Name" for the given application. +# - Uncategorized groups are presented as boolean fields and grouped in a +# section "Others". +# +# The user form view is modified by an inherited view (base.user_groups_view); +# the inherited view replaces the field 'groups_id' by a set of reified group +# fields (boolean or selection fields). The arch of that view is regenerated +# each time groups are changed. +# +# Naming conventions for reified groups fields: +# - boolean field 'in_group_ID' is True iff +# ID is in 'groups_id' +# - selection field 'sel_groups_ID1_..._IDk' is ID iff +# ID is in 'groups_id' and ID is maximal in the set {ID1, ..., IDk} +# + +class GroupsView(models.Model): + _inherit = 'res.groups' + + @api.model + def create(self, values): + user = super(GroupsView, self).create(values) + self._update_user_groups_view() + # actions.get_bindings() depends on action records + self.env['ir.actions.actions'].clear_caches() + return user + + @api.multi + def write(self, values): + res = super(GroupsView, self).write(values) + self._update_user_groups_view() + # actions.get_bindings() depends on action records + self.env['ir.actions.actions'].clear_caches() + return res + + @api.multi + def unlink(self): + res = super(GroupsView, self).unlink() + self._update_user_groups_view() + # actions.get_bindings() depends on action records + self.env['ir.actions.actions'].clear_caches() + return res + + @api.model + def _update_user_groups_view(self): + """ Modify the view with xmlid ``base.user_groups_view``, which inherits + the user form view, and introduces the reified group fields. + """ + if self._context.get('install_mode'): + # use installation/admin language for translatable names in the view + user_context = self.env['res.users'].context_get() + self = self.with_context(**user_context) + + # We have to try-catch this, because at first init the view does not + # exist but we are already creating some basic groups. + view = self.env.ref('base.user_groups_view', raise_if_not_found=False) + if view and view.exists() and view._name == 'ir.ui.view': + group_no_one = view.env.ref('base.group_no_one') + group_employee = view.env.ref('base.group_user') + xml1, xml2, xml3 = [], [], [] + xml1.append(E.separator(string=_('User Type'), colspan="2", groups='base.group_no_one')) + xml2.append(E.separator(string=_('Application Accesses'), colspan="2")) + + user_type_field_name = '' + for app, kind, gs in self.get_groups_by_application(): + attrs = {} + # hide groups in categories 'Hidden' and 'Extra' (except for group_no_one) + if app.xml_id in ('base.module_category_hidden', 'base.module_category_extra', 'base.module_category_usability'): + attrs['groups'] = 'base.group_no_one' + + # User type (employee, portal or public) is a separated group. This is the only 'selection' + # group of res.groups without implied groups (with each other). + if app.xml_id == 'base.module_category_user_type': + # application name with a selection field + field_name = name_selection_groups(gs.ids) + user_type_field_name = field_name + attrs['widget'] = 'radio' + attrs['groups'] = 'base.group_no_one' + xml1.append(E.field(name=field_name, **attrs)) + xml1.append(E.newline()) + + elif kind == 'selection': + # application name with a selection field + field_name = name_selection_groups(gs.ids) + xml2.append(E.field(name=field_name, **attrs)) + xml2.append(E.newline()) + else: + # application separator with boolean fields + app_name = app.name or _('Other') + xml3.append(E.separator(string=app_name, colspan="4", **attrs)) + for g in gs: + field_name = name_boolean_group(g.id) + if g == group_no_one: + # make the group_no_one invisible in the form view + xml3.append(E.field(name=field_name, invisible="1", **attrs)) + else: + xml3.append(E.field(name=field_name, **attrs)) + + xml3.append({'class': "o_label_nowrap"}) + user_type_attrs = {'invisible': [(user_type_field_name, '!=', group_employee.id)]} + + xml = E.field( + E.group(*(xml1), col="2"), + E.group(*(xml2), col="2", attrs=str(user_type_attrs)), + E.group(*(xml3), col="4", attrs=str(user_type_attrs)), name="groups_id", position="replace") + xml.addprevious(etree.Comment("GENERATED AUTOMATICALLY BY GROUPS")) + xml_content = etree.tostring(xml, pretty_print=True, encoding="unicode") + + new_context = dict(view._context) + new_context.pop('install_mode_data', None) # don't set arch_fs for this computed view + new_context['lang'] = None + view.with_context(new_context).write({'arch': xml_content}) + + def get_application_groups(self, domain): + """ Return the non-share groups that satisfy ``domain``. """ + return self.search(domain + [('share', '=', False)]) + + @api.model + def get_groups_by_application(self): + """ Return all groups classified by application (module category), as a list:: + + [(app, kind, groups), ...], + + where ``app`` and ``groups`` are recordsets, and ``kind`` is either + ``'boolean'`` or ``'selection'``. Applications are given in sequence + order. If ``kind`` is ``'selection'``, ``groups`` are given in + reverse implication order. + """ + def linearize(app, gs): + # 'User Type' is an exception + if app.xml_id == 'base.module_category_user_type': + return (app, 'selection', gs) + # determine sequence order: a group appears after its implied groups + order = {g: len(g.trans_implied_ids & gs) for g in gs} + # check whether order is total, i.e., sequence orders are distinct + if len(set(order.values())) == len(gs): + return (app, 'selection', gs.sorted(key=order.get)) + else: + return (app, 'boolean', gs) + + # classify all groups by application + by_app, others = defaultdict(self.browse), self.browse() + for g in self.get_application_groups([]): + if g.category_id: + by_app[g.category_id] += g + else: + others += g + # build the result + res = [] + for app, gs in sorted(by_app.items(), key=lambda it: it[0].sequence or 0): + res.append(linearize(app, gs)) + if others: + res.append((self.env['ir.module.category'], 'boolean', others)) + return res + + +class UsersView(models.Model): + _inherit = 'res.users' + + @api.model + def create(self, values): + values = self._remove_reified_groups(values) + user = super(UsersView, self).create(values) + group_multi_company = self.env.ref('base.group_multi_company', False) + if group_multi_company and 'company_ids' in values: + if len(user.company_ids) <= 1 and user.id in group_multi_company.users.ids: + group_multi_company.write({'users': [(3, user.id)]}) + elif len(user.company_ids) > 1 and user.id not in group_multi_company.users.ids: + group_multi_company.write({'users': [(4, user.id)]}) + return user + + @api.multi + def write(self, values): + values = self._remove_reified_groups(values) + res = super(UsersView, self).write(values) + group_multi_company = self.env.ref('base.group_multi_company', False) + if group_multi_company and 'company_ids' in values: + for user in self: + if len(user.company_ids) <= 1 and user.id in group_multi_company.users.ids: + group_multi_company.write({'users': [(3, user.id)]}) + elif len(user.company_ids) > 1 and user.id not in group_multi_company.users.ids: + group_multi_company.write({'users': [(4, user.id)]}) + return res + + def _remove_reified_groups(self, values): + """ return `values` without reified group fields """ + add, rem = [], [] + values1 = {} + + for key, val in values.items(): + if is_boolean_group(key): + (add if val else rem).append(get_boolean_group(key)) + elif is_selection_groups(key): + rem += get_selection_groups(key) + if val: + add.append(val) + else: + values1[key] = val + + if 'groups_id' not in values and (add or rem): + # remove group ids in `rem` and add group ids in `add` + values1['groups_id'] = list(itertools.chain( + pycompat.izip(repeat(3), rem), + pycompat.izip(repeat(4), add) + )) + + return values1 + + @api.model + def default_get(self, fields): + group_fields, fields = partition(is_reified_group, fields) + fields1 = (fields + ['groups_id']) if group_fields else fields + values = super(UsersView, self).default_get(fields1) + self._add_reified_groups(group_fields, values) + return values + + @api.multi + def read(self, fields=None, load='_classic_read'): + # determine whether reified groups fields are required, and which ones + fields1 = fields or list(self.fields_get()) + group_fields, other_fields = partition(is_reified_group, fields1) + + # read regular fields (other_fields); add 'groups_id' if necessary + drop_groups_id = False + if group_fields and fields: + if 'groups_id' not in other_fields: + other_fields.append('groups_id') + drop_groups_id = True + else: + other_fields = fields + + res = super(UsersView, self).read(other_fields, load=load) + + # post-process result to add reified group fields + if group_fields: + for values in res: + self._add_reified_groups(group_fields, values) + if drop_groups_id: + values.pop('groups_id', None) + return res + + def _add_reified_groups(self, fields, values): + """ add the given reified group fields into `values` """ + gids = set(parse_m2m(values.get('groups_id') or [])) + for f in fields: + if is_boolean_group(f): + values[f] = get_boolean_group(f) in gids + elif is_selection_groups(f): + selected = [gid for gid in get_selection_groups(f) if gid in gids] + values[f] = selected and selected[-1] or False + + @api.model + def fields_get(self, allfields=None, attributes=None): + res = super(UsersView, self).fields_get(allfields, attributes=attributes) + # add reified groups fields + for app, kind, gs in self.env['res.groups'].sudo().get_groups_by_application(): + if kind == 'selection': + # 'User Type' should not be 'False'. A user is either 'employee', 'portal' or 'public' (required). + selection_vals = [(False, '')] + if app.xml_id == 'base.module_category_user_type': + selection_vals = [] + field_name = name_selection_groups(gs.ids) + if allfields and field_name not in allfields: + continue + # selection group field + tips = ['%s: %s' % (g.name, g.comment) for g in gs if g.comment] + res[field_name] = { + 'type': 'selection', + 'string': app.name or _('Other'), + 'selection': selection_vals + [(g.id, g.name) for g in gs], + 'help': '\n'.join(tips), + 'exportable': False, + 'selectable': False, + } + else: + # boolean group fields + for g in gs: + field_name = name_boolean_group(g.id) + if allfields and field_name not in allfields: + continue + res[field_name] = { + 'type': 'boolean', + 'string': g.name, + 'help': g.comment, + 'exportable': False, + 'selectable': False, + } + return res + #---------------------------------------------------------- # change password wizard #---------------------------------------------------------- diff --git a/odoo/addons/base/tests/test_non_regression.py b/odoo/addons/base/tests/test_non_regression.py index 1f75578079a..12faee0e1f0 100644 --- a/odoo/addons/base/tests/test_non_regression.py +++ b/odoo/addons/base/tests/test_non_regression.py @@ -8,8 +8,11 @@ from openerp.tests.common import TransactionCase class TestNR(TransactionCase): def test_issue26036(self): + # Coming from https://github.com/odoo/odoo/pull/26036 U = self.env["res.users"] G = self.env["res.groups"] + group_user = self.env.ref('base.group_user') + group_no_one = self.env.ref('base.group_no_one') group_A = G.create({"name": "A"}) group_AA = G.create({"name": "AA", "implied_ids": [(6, 0, [group_A.id])]}) @@ -17,10 +20,13 @@ class TestNR(TransactionCase): group_BB = G.create({"name": "BB", "implied_ids": [(6, 0, [group_B.id])]}) group_C = G.create({"name": "C"}) - user_a = U.create({"name": "a", "login": "a", "groups_id": [(6, 0, [group_AA.id])]}) + user_a = U.create({"name": "a", "login": "a", "groups_id": [(6, 0, [group_AA.id, group_user.id])]}) user_b = U.create({"name": "b", "login": "b", "groups_id": [(6, 0, [group_BB.id])]}) + self.assertEqual(user_a.groups_id, (group_AA + group_A + group_user + group_no_one)) + (user_a + user_b).write({"groups_id": [(4, group_C.id)]}) - self.assertEqual(user_a.groups_id, (group_AA + group_A + group_C)) - self.assertEqual(user_b.groups_id, (group_BB + group_B + group_C)) + self.assertEqual(user_a.groups_id, (group_AA + group_A + group_C + group_user + group_no_one)) + # As user_b is not an internal user, all its groups are removed + self.assertEqual(user_b.groups_id, group_C) diff --git a/odoo/addons/base/views/res_users_views.xml b/odoo/addons/base/views/res_users_views.xml index 9844be381e1..88aca904336 100644 --- a/odoo/addons/base/views/res_users_views.xml +++ b/odoo/addons/base/views/res_users_views.xml @@ -141,6 +141,11 @@ + @@ -185,40 +190,12 @@ - - - - - - - - - + @@ -235,27 +212,12 @@ - - - - - - - res.users.view.form.administration - res.users - - 1000 - - - - - - + res.users.tree res.users @@ -315,6 +277,16 @@ + + res.users.groups + res.users + + + + + + + Users