[IMP] base: uniform "groups" in back-end view

This revision is to make uniform the behavior of the `groups` attribute
on the Python model fields
and on the node in the view architecture.
In both cases, remove the node from the view completely.

Before this revision,

in a back-end view:
 - In the Python model, if a field has the `groups` attribute set
   and the user is not part of
   the groups, the field is removed, completely, from the view.
 - In the view architecture, if a node has the `groups` attribute set
   and the user is not part of
   the groups, the node is made invisible (not completely removed, just
   made invisible).

in a front-end view:
 - if a node has a "groups" or "t-groups" set and the user
   is not part of the groups, the node is removed from the view.

So it's 2/3 cases removing nodes restricted to a group.
and 1/3 case making invisible nodes restricted to a group.
It's simpler to have a uniform behavior for the 3 cases,
simpler to understandard for developers.

In addition, this will help for the goal to cache back-end views.
It makes possible to convert views using the `groups_id` field
by moving the content of these views directly
in the view to which they add content which is suppose to be completely
removed when the user has not the according group.
By getting rid of the `groups_id` many2many field on `ir.ui.view`,
it makes possible to cache the view architecture without
requiring to use the groups in the cache key.
Currently, if we want to cache the view architecture,
it would be required to use the intersection of the user
groups with the `groups_id` groups of the view,
making it costly to compute the cache key,
therefore altering the performance point to cache the view
architectures.

closes odoo/odoo#95729

Related: odoo/enterprise#29592
Signed-off-by: Denis Ledoux (dle) <dle@odoo.com>
This commit is contained in:
Denis Ledoux
2022-08-19 19:10:39 +02:00
parent 33f54c22c6
commit 0501bbd62e
105 changed files with 875 additions and 69 deletions
+5 -1
View File
@@ -2175,7 +2175,11 @@ class AccountMove(models.Model):
for key in list(field_onchange):
if key == to_del or key.startswith(f"{to_del}."):
del field_onchange[key]
del values[to_del]
# test_01_account_tour
# File "/data/build/odoo/addons/account/models/account_move.py", line 2127, in onchange
# del values[to_del]
# KeyError: 'line_ids'
values.pop(to_del, None)
return super().onchange(values, field_name, field_onchange)
# -------------------------------------------------------------------------
@@ -89,6 +89,7 @@
<field name="model">account.account</field>
<field name="arch" type="xml">
<tree editable="top" create="1" delete="1" multi_edit="1" string="Chart of accounts">
<field name="company_id" invisible="1"/>
<field name="code"/>
<field name="name"/>
<field name="account_type" widget="account_type_selection"/>
@@ -27,6 +27,9 @@
<field name="priority">1</field>
<field name="arch" type="xml">
<form string="Account Journal">
<field name="company_id" invisible="1"/>
<field name="default_account_id" invisible="1"/>
<field name="bank_statements_source" invisible="1"/>
<sheet>
<div name="button_box" class="oe_button_box">
<button class="oe_stat_button" type="action"
@@ -241,6 +244,7 @@
<field name="priority">1</field>
<field name="arch" type="xml">
<tree editable="bottom">
<field name="company_id" invisible="1"/>
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="excluded_journal_ids" widget="many2many_tags" options="{'no_create': True}"/>
@@ -257,6 +261,7 @@
<form string="Journal Groups">
<sheet>
<group>
<field name="company_id" invisible="1"/>
<field name="name" placeholder="e.g. GAAP, IFRS, ..."/>
<field name="excluded_journal_ids" widget="many2many_tags" options="{'no_create': True}"/>
<field name="sequence" groups="base.group_no_one"/>
@@ -165,6 +165,7 @@
<field name="arch" type="xml">
<tree string="Journal Items" create="false" edit="false" expand="context.get('expand', False)" multi_edit="1" sample="1">
<field name="date" readonly="1"/>
<field name="company_id" invisible="1"/>
<field name="company_id" groups="base.group_multi_company" optional="hide"/>
<field name="journal_id" options='{"no_open":True}' optional="hide"/>
<field name="move_name" string="Journal Entry" widget="open_move_widget"/>
@@ -706,12 +707,15 @@
<!-- Invisible fields -->
<field name="id" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="journal_id" invisible="1"/>
<field name="show_name_warning" invisible="1"/>
<field name="posted_before" invisible="1"/>
<field name="move_type" invisible="1"/>
<field name="payment_state" invisible="1" force_save="1"/>
<field name="invoice_filter_type_domain" invisible="1"/>
<field name="suitable_journal_ids" invisible="1"/>
<field name="currency_id" invisible="1"/>
<field name="company_currency_id" invisible="1"/>
<field name="commercial_partner_id" invisible="1"/>
<field name="bank_partner_id" invisible="1"/>
@@ -897,6 +901,9 @@
<field name="company_id" invisible="1"/>
<field name="company_currency_id" invisible="1"/>
<field name="display_type" force_save="1" invisible="1"/>
<!-- /l10n_in_edi.test_edi_json -->
<!-- required for @api.onchange('product_id') -->
<field name="product_uom_id" invisible="1"/>
</tree>
<kanban class="o_kanban_mobile">
<!-- Displayed fields -->
+3 -2
View File
@@ -127,6 +127,7 @@
<field name="model">account.tax</field>
<field name="arch" type="xml">
<form string="Account Tax">
<field name="company_id" invisible="1"/>
<sheet>
<group>
<group>
@@ -149,9 +150,9 @@
<div attrs="{'invisible': [('amount_type', '=', 'group')]}">
<field name="country_code" invisible="1"/>
<label for="invoice_repartition_line_ids"/>
<field name="invoice_repartition_line_ids" context="{'default_company_id': company_id}"/>
<field name="invoice_repartition_line_ids"/>
<label for="refund_repartition_line_ids"/>
<field name="refund_repartition_line_ids" context="{'default_company_id': company_id}"/>
<field name="refund_repartition_line_ids"/>
</div>
<field name="children_tax_ids" attrs="{'invisible':['|', ('amount_type','!=','group'), ('type_tax_use','=','none')]}" domain="[('type_tax_use','in',('none',type_tax_use)), ('amount_type','!=','group')]">
<tree string="Children Taxes">
+1
View File
@@ -40,6 +40,7 @@
<group>
<group>
<field name="active" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="states_count" invisible="1"/>
<field name="company_country_id" invisible="1"/>
<field name="foreign_vat_header_mode" invisible="1"/>
@@ -65,6 +65,7 @@
<field name="model">account.analytic.group</field>
<field name="arch" type="xml">
<form string="Analytic Account Groups">
<field name="company_id" invisible="1"/>
<sheet>
<group>
<field name="name"/>
@@ -175,6 +176,7 @@
<field name="model">account.analytic.account</field>
<field name="arch" type="xml">
<form string="Analytic Account">
<field name="company_id" invisible="1"/>
<sheet string="Analytic Account">
<div class="oe_button_box" name="button_box">
<button class="oe_stat_button" type="action" name="%(account_analytic_line_action)d" icon="fa-usd">
@@ -312,6 +314,7 @@
<field name="priority">1</field>
<field name="arch" type="xml">
<form string="Analytic Entry">
<field name="company_id" invisible="1"/>
<sheet>
<group>
<group name="analytic_entry" string="Analytic Entry">
+6 -2
View File
@@ -34,7 +34,9 @@ class TestPartner(TestCrmCommon):
self.assertEqual(child.user_id, self.env.user)
# test form tool
partner_form = Form(self.env['res.partner'], 'base.view_partner_form')
# <field name="team_id" groups="base.group_no_one"/>
with self.debug_mode():
partner_form = Form(self.env['res.partner'], 'base.view_partner_form')
partner_form.parent_id = contact_company
partner_form.company_type = 'person'
partner_form.name = 'Hermes Conrad'
@@ -45,7 +47,9 @@ class TestPartner(TestCrmCommon):
self.assertEqual(partner_form.user_id, self.env.user)
# test form tool
partner_form = Form(self.env['res.partner'], 'base.view_partner_form')
# <field name="team_id" groups="base.group_no_one"/>
with self.debug_mode():
partner_form = Form(self.env['res.partner'], 'base.view_partner_form')
# `parent_id` is invisible when `is_company` is True (`company_type == 'company'`)
# and parent_id is not set
# So, set a temporary `parent_id` before setting the contact as company
+1
View File
@@ -24,6 +24,7 @@
</header>
<sheet>
<field name="active" invisible="1"/>
<field name="company_id" invisible="1"/>
<div class="oe_button_box" name="button_box">
<button name="action_schedule_meeting" type="object"
class="oe_stat_button" icon="fa-calendar"
+1
View File
@@ -137,6 +137,7 @@
<field name="priority">12</field>
<field name="arch" type="xml">
<xpath expr="//sheet" position="before">
<field name="use_leads" invisible="1"/>
<header>
<button name="action_assign_leads" type="object"
string="Assign Leads"
+3
View File
@@ -44,6 +44,9 @@
<field string="Opportunities" name="opportunity_count" widget="statinfo"/>
</button>
</div>
<field name="parent_id" position="before">
<field name="team_id" invisible="1"/>
</field>
<field name="parent_id" position="attributes">
<attribute name="context">{'default_is_company': True, 'show_vat': True, 'default_user_id': user_id, 'default_team_id': team_id}</attribute>
</field>
@@ -12,6 +12,7 @@
</header>
<div class="alert alert-danger text-center my-0" role="alert" attrs="{'invisible': [('error_type', '=', False)]}">
<field name="error_type" invisible="1"/>
<field name="lead_type" invisible="1"/>
<span attrs="{'invisible': [('error_type', '!=', 'credits')]}">
<span>You do not have enough credits to submit this request.
<button name="action_buy_credits" type="object" class="oe_inline p-0 border-0 align-top text-primary">Buy credits.</button>
+1
View File
@@ -83,6 +83,7 @@
<group>
<group name="provider_details">
<field name="active" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="prod_environment" invisible="1"/>
<field name="debug_logging" invisible="1"/>
<label for="delivery_type"/>
+1
View File
@@ -27,6 +27,7 @@
</button>
</div>
<field name="active" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="legend_blocked" invisible="1"/>
<field name="legend_normal" invisible="1"/>
<field name="legend_done" invisible="1"/>
@@ -42,6 +42,7 @@
<h1><field name="name" placeholder="e.g. B2B Fairs"/></h1>
</div>
<field name="active" invisible="1"/>
<field name="company_id" invisible="1"/>
<group name="lead_creation_configuration">
<group name="lead_creation_basis" invisible="1">
<field name="lead_creation_basis" widget="radio"/>
@@ -5,6 +5,7 @@
<field name="model">fleet.vehicle.log.contract</field>
<field name="arch" type="xml">
<form string="Contract logs">
<field name="company_id" invisible="1"/>
<header>
<button name="action_open" states="futur" type="object" string="Start Contract" class="oe_highlight" groups="fleet.fleet_group_manager"/>
<button name="action_close" states="futur" type="object" string="Cancel" groups="fleet.fleet_group_manager"/>
@@ -16,6 +16,7 @@
<field name="state_id" widget="statusbar" options="{'clickable': '1'}"/>
</header>
<sheet>
<field name="company_id" invisible="1"/>
<field name="currency_id" invisible="1"/>
<field name="country_code" invisible="1"/>
<div class="oe_button_box" name="button_box">
+1
View File
@@ -6,6 +6,7 @@
<field name="model">hr.department</field>
<field name="arch" type="xml">
<form string="department">
<field name="company_id" invisible="1"/>
<sheet>
<div class="oe_button_box" name="button_box">
<button class="oe_stat_button" type="action" name="%(hr.act_employee_from_department)d" icon="fa-users">
+2
View File
@@ -46,9 +46,11 @@
<field name="arch" type="xml">
<form string="Employee" js_class="hr_employee_form" class='o_employee_form'>
<field name="active" invisible="1"/>
<field name="user_id" invisible="1"/>
<field name="user_partner_id" invisible="1"/>
<field name="hr_presence_state" invisible="1"/>
<field name="image_128" invisible="1" />
<field name="company_id" invisible="1"/>
<header>
<button name="%(plan_wizard_action)d" string="Launch Plan" type="action" groups="hr.group_hr_manager"/>
</header>
+1
View File
@@ -8,6 +8,7 @@
<field name="arch" type="xml">
<form string="Job">
<field name="active" invisible="1"/>
<field name="company_id" invisible="1"/>
<sheet>
<div class="oe_button_box" name="button_box"/>
<div class="oe_title">
+2
View File
@@ -33,6 +33,7 @@
<field name="model">hr.plan</field>
<field name="arch" type="xml">
<form string="Planning">
<field name="company_id" invisible="1"/>
<sheet>
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
<div class="oe_title">
@@ -82,6 +83,7 @@
<field name="model">hr.plan.activity.type</field>
<field name="arch" type="xml">
<form string="Activity">
<field name="company_id" invisible="1"/>
<sheet>
<group>
<field name="activity_type_id"/>
@@ -19,6 +19,7 @@
<field name="model">hr.work.location</field>
<field name="arch" type="xml">
<form string="Work Location">
<field name="company_id" invisible="1"/>
<sheet>
<group>
<group>
@@ -10,6 +10,7 @@
<data>
<div name="button_box" position="inside">
<field name="contract_warning" invisible="1"/>
<field name="employee_type" invisible="1"/>
<button name="action_open_contract_history"
class="oe_stat_button"
icon="fa-book"
@@ -153,6 +154,8 @@
<group name="top_info">
<group name="top_info_left">
<field name="active" invisible="1"/>
<!-- employee_id = fields.Many2one('hr.employee', string='Employee', tracking=True, domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]") -->
<field name="company_id" invisible="1"/>
<field name="employee_id"/>
<field name="date_start" string="Contract Start Date"/>
<field name="date_end" string="Contract End Date"/>
@@ -132,6 +132,8 @@
<field name="same_currency" invisible="1"/>
<field name="is_editable" invisible="1"/>
<field name="is_ref_editable" invisible="1"/>
<field name="currency_id" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="company_currency_id" invisible="1"/>
<field name="amount_tax_company" invisible="1"/>
<field name="unit_amount" invisible="1"/>
@@ -688,6 +690,7 @@
<form string="Expense Reports" class="o_expense_sheet">
<field name="can_reset" invisible="1"/>
<field name="can_approve" invisible="1"/>
<field name="company_id" invisible="1"/>
<header>
<button name="action_submit_sheet" states="draft" string="Submit to Manager" type="object" class="oe_highlight o_expense_sheet_submit" data-hotkey="l"/>
<button name="approve_expense_sheets"
@@ -130,7 +130,7 @@
</div>
</group>
<group name="alloc_right_col">
<field name="employee_id" invisible="1" groups="hr_holidays.group_hr_holidays_user"/>
<field name="employee_id" invisible="1"/>
<field name="department_id" invisible="1"/>
</group>
</group>
@@ -168,6 +168,8 @@
<field name="holiday_type" string="Mode" groups="hr_holidays.group_hr_holidays_user" context="{'employee_id':employee_id}" />
</xpath>
<xpath expr="//field[@name='employee_id']" position="replace">
<!-- :TestLeaveRequests.test_allocation_request -->
<field name="employee_id" invisible="1"/>
<field name="multi_employee" invisible="1" force_save="1"/>
<!-- Employee id is only visible if the allocation is created specifically for that user in `_action_validate_create_childs` -->
<field name="employee_id" groups="hr_holidays.group_hr_holidays_user"
@@ -29,6 +29,7 @@
<field name="model">hr.leave.stress.day</field>
<field name="arch" type="xml">
<tree editable="bottom">
<field name="company_id" invisible="1"/>
<field name="name"/>
<field name="company_id" groups="base.group_multi_company" optional="hide"/>
<field name="start_date"/>
@@ -463,7 +463,6 @@
<field name="holiday_status_id" options="{'no_open': True}" context="{'request_type':'leave', 'from_manager_leave_form': True ,'employee_id': employee_id}"/>
</h1>
</div>
<field name="employee_id" position="replace"/>
<label id="label_dates" position="before">
<field name="multi_employee" invisible="1" force_save="1"/>
<field name="employee_id" groups="hr_holidays.group_hr_holidays_user" attrs="{
+1
View File
@@ -226,6 +226,7 @@
<field name="inherit_id" ref="hr.hr_employee_public_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='coach_id']" position="after">
<field name="company_id" invisible="1"/>
<field name="leave_manager_id"/>
</xpath>
<xpath expr="//div[@name='button_box']" position="inside">
@@ -39,6 +39,7 @@
<attribute name="invisible">1</attribute>
</xpath>
<xpath expr="//field[@name='date_to']" position="after">
<field name="company_id" invisible="1"/>
<xpath expr="//field[@name='calendar_id']" position="move"/>
</xpath>
</field>
@@ -73,6 +73,7 @@
<field name="model">hr.applicant</field>
<field name="arch" type="xml">
<form string="Jobs - Recruitment Form" class="o_applicant_form">
<field name="company_id" invisible="1"/>
<field name="emp_id" invisible="1"/>
<field name="meeting_ids" invisible="1"/>
<field name="refuse_reason_id" invisible="1"/>
@@ -149,6 +149,7 @@
<field name="amount" invisible="1"/>
<field name="unit_amount" widget="timesheet_uom" decoration-danger="unit_amount &gt; 24"/>
<field name="currency_id" invisible="1"/>
<field name="company_id" invisible="1"/>
</group>
</group>
</sheet>
@@ -243,6 +243,7 @@
</xpath>
<xpath expr="//field[@name='depend_on_ids']/tree//field[@name='company_id']" position="after">
<field name="allow_subtasks" invisible="1" />
<field name="progress" invisible="1"/>
<field name="planned_hours" widget="timesheet_uom_no_toggle" sum="Initially Planned Hours" optional="hide"/>
<field name="effective_hours" widget="timesheet_uom" sum="Effective Hours" optional="hide"/>
<field name="subtask_effective_hours" widget="timesheet_uom" attrs="{'invisible' : [('allow_subtasks', '=', False)]}" optional="hide"/>
@@ -272,6 +273,7 @@
<field name="arch" type="xml">
<field name="company_id" position="after">
<field name="allow_subtasks" invisible="1"/>
<field name="progress" invisible="1"/>
<field name="planned_hours" widget="timesheet_uom_no_toggle" sum="Initially Planned Hours" optional="hide"/>
<field name="effective_hours" widget="timesheet_uom" sum="Effective Hours" optional="show"/>
<field name="subtask_effective_hours" widget="timesheet_uom" attrs="{'invisible' : [('allow_subtasks', '=', False)]}" optional="hide"/>
@@ -29,6 +29,7 @@
</button>
</div>
<field name="active" invisible="1"/>
<field name="applies_on" invisible="1"/>
<div class="oe_title">
<label for="name" string="Program Name"/>
<h1>
@@ -67,6 +67,7 @@
<field name="model">lunch.product</field>
<field name="arch" type="xml">
<form string="Products Form">
<field name="company_id" invisible="1"/>
<field name="currency_id" invisible="1"/>
<sheet>
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
+1 -1
View File
@@ -29,6 +29,7 @@
<field name="state" widget="statusbar"/>
</header>
<sheet>
<field name="active" invisible="1"/>
<widget name="web_ribbon" title="Archived" bg_color="bg-danger"
attrs="{'invisible': [('active', '=', True)]}"/>
<group>
@@ -74,7 +75,6 @@
<field name="priority"/>
<field name="attach"/>
<field name="original"/>
<field name="active" invisible="1"/>
</group>
</page>
</notebook>
+2
View File
@@ -11,6 +11,7 @@
<field name="is_mail_blacklist" attrs="{'readonly': [('state','!=', 'manual')]}" groups="base.group_no_one"/>
</field>
<xpath expr="//field[@name='field_id']//field[@name='copied']" position="after">
<field name="state" invisible="1"/>
<field name="tracking" attrs="{'readonly': [('state','!=', 'manual')]}"/>
</xpath>
</field>
@@ -33,6 +34,7 @@
<field name="inherit_id" ref="base.view_model_fields_form"/>
<field name="arch" type="xml">
<field name="copied" position="after">
<field name="state" invisible="1"/>
<field name="tracking" attrs="{'readonly': [('state','!=', 'manual')]}"/>
</field>
</field>
@@ -17,6 +17,7 @@
<field name="category"/>
<field name="default_user_id" options="{'no_create': True}" domain="[('share', '=', False)]"/>
<field name="res_model" groups="base.group_no_one"/>
<field name="res_model" invisible="1"/>
<field name="res_model_change" invisible="1"/>
<field name="initial_res_model" invisible="1"/>
<field name="summary" placeholder="e.g. &quot;Discuss proposal&quot;"/>
+2
View File
@@ -13,6 +13,8 @@
<field name="state" widget="statusbar" statusbar_visible="outgoing,sent,received,exception,cancel"/>
</header>
<sheet>
<field name="model" invisible="1"/>
<field name="res_id" invisible="1"/>
<div class="oe_button_box" name="button_box">
<button name="action_open_document" string="Open Document"
type="object" class="oe_link" icon="fa-file-text-o"
@@ -55,6 +55,8 @@
<field name="model">maintenance.request</field>
<field name="arch" type="xml">
<form string="Maintenance Request">
<field name="company_id" invisible="1"/>
<field name="category_id" invisible="1"/>
<header>
<button string="Cancel" name="archive_equipment_request" type="object" attrs="{'invisible': [('archive', '=', True)]}"/>
<button string="Reopen Request" name="reset_equipment_request" type="object" attrs="{'invisible': [('archive', '=', False)]}"/>
@@ -336,6 +338,7 @@
<field name="arch" type="xml">
<form string="Equipments">
<sheet>
<field name="company_id" invisible="1"/>
<div class="oe_button_box" name="button_box">
<button name="%(hr_equipment_request_action_from_equipment)d"
type="action"
@@ -457,6 +457,8 @@
<attribute name="class" remove="o-aside"/>
</xpath>
<xpath expr="//notebook/page[@name='mail_body']" position="after">
<!-- test_01_mass_mailing_editor_tour -->
<field name="body_html" invisible="1"/>
<page string="Mail Debug" name="mail_debug" groups="base.group_no_one">
<div class="position-relative">
<div class="mt-n2">
@@ -17,7 +17,6 @@
attrs="{'invisible': ['|', ('mailing_mail_count', '=', 0), ('is_mailing_campaign_activated', '=', False)]}"
groups="mass_mailing.group_mass_mailing_user">
<field name="mailing_mail_count" widget="statinfo" string="Mailings"/>
<field name="ab_testing_mailings_count" invisible="1"/>
</button>
</xpath>
<xpath expr="//notebook" position="inside">
@@ -46,6 +45,7 @@
</page>
</xpath>
<xpath expr="//notebook" position="after">
<field name="ab_testing_mailings_count" invisible="1"/>
<group name="ab_test_group" groups="mass_mailing.group_mass_mailing_campaign" attrs="{'invisible': [('ab_testing_mailings_count', '=', 0)]}">
<group string="A/B Test">
<field name="ab_testing_completed" invisible="1"/>
@@ -8,6 +8,7 @@
<field name="arch" type="xml">
<xpath expr="//field[@name='stage_id']" position="before">
<field name="event_registrations_open" invisible="1"/>
<field name="seats_expected" invisible="1"/>
<button name="action_invite_contacts" type="object" string="Invite"
class="btn btn-primary"
groups="mass_mailing.group_mass_mailing_user"
@@ -7,6 +7,7 @@
<field name="inherit_id" ref="event.view_event_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='stage_id']" position="before">
<field name="track_count" invisible="1"/>
<button name="action_mass_mailing_track_speakers" string="Contact Track Speakers" type="object" attrs="{'invisible': [('track_count', '=', 0)]}" groups="mass_mailing.group_mass_mailing_user"/>
</xpath>
</field>
@@ -6,6 +6,7 @@
<field name="inherit_id" ref="website_slides.view_slide_channel_form"/>
<field name="arch" type="xml">
<button name="action_channel_invite" position="after">
<field name="members_count" invisible="1"/>
<button name="action_mass_mailing_attendees" string="Contact Attendees" type="object"
class="oe_highlight" attrs="{'invisible': [('members_count', '=', 0)]}"
groups="mass_mailing.group_mass_mailing_user"/>
@@ -17,9 +17,11 @@
attrs="{'invisible': ['|', ('mailing_sms_count', '=', 0), ('is_mailing_campaign_activated', '=', False)]}"
icon="fa-mobile" groups="mass_mailing.group_mass_mailing_user">
<field name="mailing_sms_count" widget="statinfo" string="SMS"/>
<field name="ab_testing_mailings_sms_count" invisible="1"/>
</button>
</xpath>
<xpath expr="//sheet" position="inside">
<field name="ab_testing_mailings_sms_count" invisible="1"/>
</xpath>
<xpath expr="//notebook" position="inside">
<page string="SMS" name="sms"
attrs="{'invisible': ['|', ('mailing_sms_count', '=', 0), ('is_mailing_campaign_activated', '=', False)]}"
+4
View File
@@ -1822,6 +1822,10 @@ class TestMrpOrder(TestMrpCommon):
})
self.env['stock.quant']._update_available_quantity(storable_component, self.env.ref('stock.stock_location_stock'), 100)
# Despite the purpose of this test is to use multi uom
# tests the production choose the right uoms on all models without
# having the uom fields in the interface views
self.env.user.groups_id -= self.env.ref('uom.group_uom')
for component in [consumable_component, storable_component]:
bom = self.env['mrp.bom'].create({
'product_tmpl_id': product.product_tmpl_id.id,
+5 -3
View File
@@ -51,8 +51,10 @@
<group>
<group>
<field name="active" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="product_tmpl_id" context="{'default_detailed_type': 'product'}"/>
<field name="product_uom_category_id" invisible="1"/>
<field name="allow_operation_dependencies" invisible="1"/>
<field name="product_id" groups="product.group_product_variant" context="{'default_detailed_type': 'product'}"/>
<label for="product_qty" string="Quantity"/>
<div class="o_row">
@@ -79,7 +81,7 @@
</group>
<notebook>
<page string="Components" name="components">
<field name="bom_line_ids" widget="one2many" context="{'default_parent_product_tmpl_id': product_tmpl_id, 'default_product_id': False, 'default_company_id': company_id, 'default_bom_id': id}">
<field name="bom_line_ids" widget="one2many" context="{'default_parent_product_tmpl_id': product_tmpl_id, 'default_product_id': False, 'default_bom_id': id}">
<tree string="Components" editable="bottom">
<field name="company_id" invisible="1"/>
<field name="sequence" widget="handle"/>
@@ -108,13 +110,13 @@
<field name="operation_ids"
attrs="{'invisible': [('type','not in',('normal','phantom'))]}"
groups="mrp.group_mrp_routings"
context="{'bom_id_invisible': True, 'default_company_id': company_id, 'default_bom_id': id, 'tree_view_ref': 'mrp.mrp_routing_workcenter_bom_tree_view'}" widget="mrp_one2many_with_copy"/>
context="{'bom_id_invisible': True, 'default_bom_id': id, 'tree_view_ref': 'mrp.mrp_routing_workcenter_bom_tree_view'}" widget="mrp_one2many_with_copy"/>
</page>
<page string="By-products"
name="by_products"
attrs="{'invisible': [('type','!=','normal')]}"
groups="mrp.group_mrp_byproducts">
<field name="byproduct_ids" context="{'form_view_ref' : 'mrp.mrp_bom_byproduct_form_view', 'default_company_id': company_id, 'default_bom_id': id}">
<field name="byproduct_ids" context="{'form_view_ref' : 'mrp.mrp_bom_byproduct_form_view', 'default_bom_id': id}">
<tree string="By-products" editable="top">
<field name="company_id" invisible="1"/>
<field name="product_uom_category_id" invisible="1"/>
+27
View File
@@ -138,6 +138,7 @@
<field name="consumption" invisible="1"/>
<field name="is_planned" invisible="1"/>
<field name="show_allocation" invisible="1"/>
<field name="workorder_ids" invisible="1"/>
<div class="oe_button_box" name="button_box">
<button name="action_view_reception_report" string="Allocation" type="object"
class="oe_stat_button" icon="fa-list"
@@ -194,6 +195,7 @@
<field name="product_id" context="{'default_detailed_type': 'product'}" attrs="{'readonly': [('state', '!=', 'draft')]}" default_focus="1"/>
<field name="product_tmpl_id" invisible="1"/>
<field name="forecasted_issue" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="product_description_variants" attrs="{'invisible': [('product_description_variants', 'in', (False, ''))], 'readonly': [('state', '!=', 'draft')]}"/>
<label for="bom_id" name="bom_label"/>
<div class='o_row d-flex' name="bom_div">
@@ -213,6 +215,7 @@
</button>
<label for="product_uom_id" string="" class="oe_inline"/>
<field name="product_uom_category_id" invisible="1"/>
<field name="product_uom_id" groups="!uom.group_uom" invisible="1"/>
<field name="product_uom_id" options="{'no_open': True, 'no_create': True}" groups="uom.group_uom" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
<span class='fw-bold'>To Produce</span>
<button type="object" name="action_product_forecast_report" title="Forecast Report" icon="fa-area-chart" attrs="{'invisible': [('forecasted_issue', '=', True)]}"/>
@@ -267,6 +270,26 @@
<!-- Useless as the editable in tree declaration -> For Form Test-->
<field name="product_uom_category_id"/>
<field name="allowed_operation_ids"/>
<!--
Required for test_00_mrp_byproduct
when changing the mrp.production product_qty
`_onchange_producing` is called,
calling `_set_qty_producing`,
which changes the `quantity_done` of byproducts
If byproducts are not in the view (`groups="mrp.group_mrp_byproducts"`)
and `quantity_done` is not within the finished move views,
(byproduct moves are a subset of finished moves)
the `quantity_done` of byproducts is not updated correctly with the onchange
Another solution is to add `self.env.user.groups_id += self.env.ref('mrp.group_mrp_byproducts')`
to the test `test_00_mrp_byproduct`, which could makes sense as it's a test testing the byproducts features,
for which you should have the byproducts group to have access to,
but it seemed better to keep the feature working even if you do not see the byproducts features with your user.
That being said, the best would be to have the byproducts feature working without relying on anything in the view,
e.g. so the byproducts feature works with xmlrpc calls.
-->
<field name="quantity_done"/>
<!-- Required for test_fifo_byproduct -->
<field name="cost_share"/>
</tree>
</field>
</group>
@@ -280,6 +303,8 @@
<tree default_order="is_done, manual_consumption desc, sequence" editable="bottom">
<field name="product_id" force_save="1" required="1" context="{'default_detailed_type': 'product'}" attrs="{'readonly': ['|', '|', ('move_lines_count', '&gt;', 0), ('state', '=', 'cancel'), '&amp;', ('state', '!=', 'draft'), ('additional', '=', False) ]}"/>
<field name="location_id" string="From" readonly="1" groups="stock.group_stock_multi_locations" optional="show"/>
<!-- test_immediate_validate_uom_2, test_product_produce_different_uom -->
<field name="product_uom" invisible="1"/>
<field name="propagate_cancel" invisible="1"/>
<field name="price_unit" invisible="1"/>
<field name="company_id" invisible="1"/>
@@ -385,8 +410,10 @@
<group>
<field name="picking_type_id" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
<field name="location_src_id" groups="stock.group_stock_multi_locations" options="{'no_create': True}" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
<field name="location_src_id" groups="!stock.group_stock_multi_locations" invisible="1"/>
<field name="warehouse_id" invisible="1"/>
<field name="location_dest_id" groups="stock.group_stock_multi_locations" options="{'no_create': True}" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
<field name="location_dest_id" groups="!stock.group_stock_multi_locations" invisible="1"/>
</group>
<group>
<field name="origin"/>
+1
View File
@@ -82,6 +82,7 @@
<group>
<group name="description">
<field name="active" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="name"/>
<field name="bom_id" invisible="context.get('bom_id_invisible', False)" domain="[]" readonly="context.get('default_bom_id', False)"/>
<field name="workcenter_id" context="{'default_company_id': company_id}"/>
+2
View File
@@ -88,6 +88,7 @@
<field name="model">mrp.unbuild</field>
<field name="arch" type="xml">
<form string="Unbuild Orders">
<field name="company_id" invisible="1"/>
<header>
<button name="action_validate" string="Unbuild" type="object" states="draft" class="oe_highlight" data-hotkey="v"/>
<field name="state" widget="statusbar" statusbar_visible="draft,done"/>
@@ -140,6 +141,7 @@
<sheet>
<group>
<group>
<field name="company_id" invisible="1"/>
<field name="state" invisible="1"/>
<field name="product_id" invisible="1"/>
<field name="bom_id" invisible="1"/>
+2 -2
View File
@@ -342,12 +342,12 @@
<group>
<group>
<field name="active" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="name" string="Work Center Name" required="True"/>
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
<field
name="alternative_workcenter_ids"
widget="many2many_tags"
context="{'default_company_id': company_id}"
/>
</group>
<group>
@@ -388,7 +388,7 @@
<field name="note" nolabel="1" placeholder="Description of the work center..."/>
</page>
<page string="Specific Capacities" name="capacity">
<field name="capacity_ids" context="{'default_workcenter_id': id, 'default_company_id': company_id}">
<field name="capacity_ids" context="{'default_workcenter_id': id}">
<tree editable="bottom">
<field name="product_id"/>
<field name="product_uom_id" groups="uom.group_uom"/>
+1
View File
@@ -230,6 +230,7 @@
<page string="Blocked By" name="dependencies" attrs="{'invisible': [('allow_workorder_dependencies', '=', False)]}">
<field name="blocked_by_workorder_ids" nolabel="1">
<tree editable="bottom">
<field name="company_id" invisible="1"/>
<field name="name" string="Operation"/>
<field name="company_id" optional="hide" groups="base.group_multi_company"/>
<field name="workcenter_id"/>
@@ -10,6 +10,7 @@
<attribute name="groups">stock.group_stock_manager</attribute>
</field>
<field name="picking_ids" position="after">
<field name="target_model" invisible="1"/>
<field name="mrp_production_ids"
widget="many2many_tags" options="{'no_create_edit': True}"
attrs="{'invisible': [('target_model', '!=', 'manufacturing')]}"
@@ -88,7 +88,7 @@
<field name="arch" type="xml">
<form create="0" delete="0">
<header>
<field name="state" widget="statusbar" groups="base.group_user"/>
<field name="state" widget="statusbar"/>
</header>
<sheet>
<field name="product_uom_category_id" invisible="1"/>
@@ -99,6 +99,7 @@
<field name="picking_id" invisible="1"/>
<field name="location_dest_id" invisible="1"/>
<field name="has_tracking" invisible="1"/>
<field name="product_uom" invisible="1"/>
<field name="product_uom_qty" invisible="1"/>
<group>
<field name="order_finished_lot_id"/>
@@ -6,6 +6,7 @@
<field name="model">payment.acquirer</field>
<field name="arch" type="xml">
<form string="Payment Acquirer">
<field name="company_id" invisible="1"/>
<field name="is_published" invisible="1"/>
<field name="main_currency_id" invisible="1"/>
<field name="support_fees" invisible="1"/>
@@ -22,6 +23,7 @@
<field name="show_auth_msg" invisible="1"/>
<field name="show_done_msg" invisible="1"/>
<field name="show_cancel_msg" invisible="1"/>
<field name="provider" invisible="1"/>
<sheet>
<!-- === Stat Buttons === -->
<div class="oe_button_box" name="button_box">
@@ -42,6 +42,7 @@
</h1>
<group>
<field name="id" invisible='1'/>
<field name="company_id" invisible="1"/>
<group name="group_product">
<field name="product_id" required='True' attrs="{'readonly': [('id', '!=', False)]}"/>
</group>
@@ -57,6 +57,7 @@
<!-- Scope = coming from a product/product template -->
<field name="pricelist_id" string="Pricelist" options="{'no_create_edit':1, 'no_open': 1}"/>
<field name="name" string="Applied On"/>
<field name="company_id" invisible="1"/>
<field name="categ_id" invisible="1"/>
<field name="product_tmpl_id"
invisible="context.get('active_model')!='product.category'"
@@ -93,6 +94,7 @@
<form string="Pricelist Rule">
<sheet>
<field name="name" invisible="1"/>
<field name="company_id" invisible="1"/>
<group name="pricelist_rule_computation" groups="product.group_sale_pricelist" string="Price Computation">
<group name="pricelist_rule_method">
<field name="compute_price" string="Computation" widget="radio"/>
+2 -1
View File
@@ -16,6 +16,7 @@
<field name='is_product_variant' invisible='1'/>
<field name='attribute_line_ids' invisible='1'/>
<field name="type" invisible="1"/>
<field name="company_id" invisible="1"/>
<div class="oe_button_box" name="button_box">
<button class="oe_stat_button"
name="open_pricelist_rules"
@@ -53,7 +54,7 @@
margin-right: 0px;
}
</style>
<div name="options" groups="base.group_user">
<div name="options">
<span class="d-inline-block">
<field name="sale_ok"/>
<label for="sale_ok"/>
+3 -1
View File
@@ -133,7 +133,9 @@ const ProjectFormController = FormController.extend({
async _saveRecord(recordID, options) {
const task = this.model.get(recordID || this.handle);
const result = await this._super(...arguments);
let tasksX2ManyFields = result.filter(fieldName => ['child_ids', 'depend_on_ids'].includes(fieldName));
// --test-tags /industry_fsm.test_ui
// <page name="task_dependencies" groups="project.group_project_task_dependencies"></page>
let tasksX2ManyFields = Object.keys(task.data).filter(fieldName => ['child_ids', 'depend_on_ids'].includes(fieldName));
if (tasksX2ManyFields.length) {
const taskResIds = [];
for (const taskX2ManyFieldName of tasksX2ManyFields) {
+7
View File
@@ -434,6 +434,8 @@
<field name="model">project.project</field>
<field name="arch" type="xml">
<form string="Project" class="o_form_project_project" js_class="form_description_expander">
<field name="company_id" invisible="1"/>
<field name="analytic_account_id" invisible="1"/>
<header>
<button name="%(project.project_share_wizard_action)d" string="Share Readonly" type="action" class="oe_highlight" groups="project.group_project_manager"
attrs="{'invisible': [('privacy_visibility', '!=', 'portal')]}" context="{'default_access_mode': 'read'}" data-hotkey="r"/>
@@ -444,6 +446,7 @@
<sheet string="Project">
<div class="oe_button_box" name="button_box" groups="base.group_user">
<field name="currency_id" invisible="1"/>
<field name="allow_milestones" invisible="1"/>
<button class="oe_stat_button" type="object" name="action_view_analytic_account_entries" icon="fa-usd" attrs="{'invisible': [('analytic_account_id', '=', False)]}" groups="analytic.group_analytic_accounting">
<div class="o_form_field o_stat_info">
<span class="o_stat_value">
@@ -1080,6 +1083,8 @@
<field name="rating_last_value" invisible="1"/>
<field name="rating_count" invisible="1"/>
<field name="allow_milestones" invisible="1" />
<field name="parent_id" invisible="1"/>
<field name="company_id" invisible="1"/>
<header>
<button name="action_assign_to_me" string="Assign to Me" type="object" attrs="{'invisible': &quot;['|', ('user_ids', 'in', uid), ('user_ids', '=', [])]&quot;}" data-hotkey="q"/>
<button name="action_assign_to_me" string="Assign to Me" type="object" class="oe_highlight" attrs="{'invisible' : &quot;['|', ('user_ids', 'in', uid), ('user_ids', '!=', [])]&quot;}" data-hotkey="q"/>
@@ -1189,6 +1194,7 @@
<page name="sub_tasks_page" string="Sub-tasks" attrs="{'invisible': [('allow_subtasks', '=', False)]}">
<field name="child_ids" context="{'default_project_id': project_id if not parent_id or not display_project_id else display_project_id, 'default_user_ids': user_ids, 'default_parent_id': id, 'default_partner_id': partner_id, 'default_milestone_id': allow_milestones and milestone_id}" widget="subtasks_one2many">
<tree editable="bottom">
<field name="company_id" invisible="1"/>
<field name="legend_normal" invisible="1"/>
<field name="legend_done" invisible="1"/>
<field name="legend_blocked" invisible="1"/>
@@ -1226,6 +1232,7 @@
<page name="task_dependencies" string="Blocked By" attrs="{'invisible': [('allow_task_dependencies', '=', False)]}" groups="project.group_project_task_dependencies">
<field name="depend_on_ids" nolabel="1" context="{'default_project_id' : project_id}">
<tree editable="bottom">
<field name="company_id" invisible="1"/>
<field name="allow_milestones" invisible="1"/>
<field name="parent_id" invisible="1" />
<field name="display_project_id" invisible="1" />
@@ -10,6 +10,7 @@
<group name="timesheet" groups="base.group_no_one" style="width:50%">
<h2>Timesheets</h2>
<field name="timesheet_project_id" context="{'active_test': False}"/>
<field name="company_id" invisible="1"/>
<field name="timesheet_task_id" context="{'active_test': False, 'default_project_id': timesheet_project_id}" attrs="
{'invisible': [('timesheet_project_id', '=', False)], 'required': [('timesheet_project_id', '!=', False)]}"/>
<field name="timesheet_generate" invisible="1"/>
+3
View File
@@ -12,6 +12,9 @@
<xpath expr="//tree" position="attributes">
<attribute name="editable">bottom</attribute>
</xpath>
<xpath expr="//tree" position="inside">
<field name="company_id" invisible="1"/>
</xpath>
<xpath expr="//field[@name='company_id']" position="attributes">
<attribute name="readonly">0</attribute>
<attribute name="optional">hide</attribute>
+2
View File
@@ -176,6 +176,7 @@
<field name="partner_ref"/>
<field name="currency_id" groups="base.group_multi_currency" force_save="1"/>
<field name="id" invisible="1"/>
<field name="company_id" invisible="1"/>
</group>
<group>
<field name="date_order" attrs="{'invisible': [('state','in',('purchase','done'))]}"/>
@@ -221,6 +222,7 @@
<field name="currency_id" invisible="1"/>
<field name="state" invisible="1"/>
<field name="product_type" invisible="1"/>
<field name="product_uom" invisible="1" groups="!uom.group_uom"/>
<field name="product_uom_category_id" invisible="1"/>
<field name="invoice_lines" invisible="1"/>
<field name="sequence" widget="handle"/>
@@ -104,6 +104,7 @@
<field name="model">purchase.requisition</field>
<field name="arch" type="xml">
<form string="Purchase Agreements">
<field name="company_id" invisible="1"/>
<header>
<button name="%(action_purchase_requisition_to_so)d" type="action"
string="New Quotation"
+1
View File
@@ -75,6 +75,7 @@
<field name="description"/>
<field name="invoice_state" invisible="1"/>
<field name="tracking" invisible="1" attrs="{'readonly': 1}"/>
<field name="company_id" invisible="1"/>
<field name="product_id"/>
<field name="lot_id" context="{'default_product_id': product_id, 'default_company_id': company_id}" groups="stock.group_production_lot" attrs="{'required':[('tracking', 'in', ['serial', 'lot'])], 'invisible': [('tracking', 'not in', ['serial', 'lot'])], 'readonly': ['|', ('state', '=', 'done'), ('tracking', 'not in', ['serial', 'lot'])]}"/>
<field name="product_uom_category_id" invisible="1"/>
+3
View File
@@ -33,6 +33,7 @@
<form string="Resource">
<sheet>
<field name="active" invisible="1" />
<field name="company_id" invisible="1"/>
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
<group>
<group name="user_details">
@@ -57,6 +58,7 @@
<field name="model">resource.resource</field>
<field name="arch" type="xml">
<tree string="Resources" multi_edit="1" default_order="name">
<field name="company_id" invisible="1"/>
<field name="name" />
<field name="user_id" />
<field name="company_id" groups="base.group_multi_company" optional="show"/>
@@ -125,6 +127,7 @@
<field name="model">resource.calendar.leaves</field>
<field name="arch" type="xml">
<form string="Leave Detail">
<field name="company_id" invisible="1"/>
<sheet>
<group>
<group name="leave_details">
+8 -1
View File
@@ -272,7 +272,9 @@
confirm="This will update all unit prices based on the currently set pricelist."
attrs="{'invisible': ['|', ('show_update_pricelist', '=', False), ('state', 'in', ['sale', 'done','cancel'])]}"/>
</div>
<field name="company_id" invisible="1"/>
<field name="currency_id" invisible="1"/>
<field name="pricelist_id" invisible="1"/>
<field name="tax_country_id" invisible="1"/>
<field name="payment_term_id" options="{'no_open':True,'no_create': True}"/>
</group>
@@ -318,6 +320,7 @@
<field
context="{'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':product_uom, 'uom_qty_change':True, 'company_id': parent.company_id}"
name="product_uom_qty"/>
<field name="product_uom" invisible="1" groups="!uom.group_uom"/>
<field
name="product_uom"
force_save="1"
@@ -465,6 +468,7 @@
/>
<field name="qty_to_invoice" invisible="1"/>
<field name="product_uom_readonly" invisible="1"/>
<field name="product_uom" invisible="1" groups="!uom.group_uom"/>
<field
name="product_uom"
force_save="1"
@@ -511,7 +515,7 @@
<field name="name"/>
<field name="product_id"/>
<field name="product_uom_qty"/>
<field name="product_uom" groups="uom.group_uom"/>
<field name="product_uom"/>
<field name="price_subtotal"/>
<field name="price_total"/>
<field name="price_tax" invisible="1"/>
@@ -601,8 +605,11 @@
confirm="This will update all taxes based on the currently selected fiscal position."
attrs="{'invisible': ['|', ('show_update_fpos', '=', False), ('state', 'in', ['sale', 'done','cancel'])]}"/>
</div>
<field name="partner_invoice_id" invisible="1"/>
<field name="analytic_account_id" context="{'default_partner_id':partner_invoice_id, 'default_name':name}" attrs="{'readonly': [('invoice_count','!=',0),('state','=','sale')]}" groups="analytic.group_analytic_accounting" force_save="1"/>
<field name="invoice_status" states="sale,done" groups="base.group_no_one"/>
<!-- test_event_configurator -->
<field name="invoice_status" invisible="1" groups="!base.group_no_one"/>
</group>
</group>
<group>
+6
View File
@@ -12,6 +12,12 @@
<xpath expr="//field[@name='child_ids']//field[@name='type']" position="attributes">
<attribute name="groups">account.group_delivery_invoice_address</attribute>
</xpath>
<xpath expr="//group/group/span/field[@name='type']" position="after">
<field name="type" invisible="1"/>
</xpath>
<xpath expr="//field[@name='child_ids']//field[@name='type']" position="after">
<field name="type" invisible="1"/>
</xpath>
</field>
</record>
@@ -32,6 +32,7 @@
<group>
<group>
<field name="active" invisible="1"/>
<field name="company_id" invisible="1"/>
<label for="number_of_days" string="Quotation Validity"/>
<div id="number_of_days">
<field name="number_of_days" class="oe_inline"/> days
@@ -36,6 +36,7 @@
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='order_line']/tree//field[@name='price_unit']" position="after">
<field name="price_subtotal" invisible="1"/>
<field name="purchase_price" optional="hide"/>
<field name="margin" optional="hide"/>
<field name="margin_percent"
@@ -8,11 +8,11 @@
<field name="priority">10</field>
<field name="arch" type="xml">
<xpath expr="//button[@name='action_view_invoice']" position="before">
<field name="project_ids" invisible="1"/>
<field name="is_product_milestone" invisible="1"/>
<button type="object" name="action_view_project_ids" class="oe_stat_button" icon="fa-puzzle-piece" attrs="{'invisible': ['|', ('state', 'in', ['draft', 'sent']), ('project_ids', '=', [])]}" groups="project.group_project_user">
<field name="project_ids" invisible="1"/>
<field name="project_count" widget="statinfo" string="Projects"/>
</button>
<field name="is_product_milestone" invisible="1"/>
<button class="oe_stat_button" name="action_view_milestone" type="object" icon="fa-check-square-o" attrs="{'invisible': ['|', ('is_product_milestone', '=', False), ('project_ids', '=', [])]}" groups="project.group_project_milestone">
<field name="milestone_count" widget="statinfo" string="Milestones"/>
</button>
@@ -175,6 +175,7 @@
optional="hide"/>
</xpath>
<xpath expr="//field[@name='remaining_hours']" position="after">
<field name="sale_order_id" invisible="1"/>
<field name="remaining_hours_available" invisible="1"/>
<span id="remaining_hours_so_label" attrs="{'invisible': ['|', '|', '|', '|', ('allow_billable', '=', False), ('sale_order_id', '=', False), ('partner_id', '=', False), ('sale_line_id', '=', False), ('remaining_hours_available', '=', False)]}">
<label class="fw-bold" for="remaining_hours_so" string="Remaining Hours on SO"
@@ -95,6 +95,7 @@
<field name="arch" type="xml">
<form string="Sales Men">
<field name="active" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="is_membership_multi" invisible="1"/>
<field name="member_warning" invisible="1"/>
<field name="user_in_teams_ids" invisible="1"/>
@@ -5,6 +5,8 @@
<field name="model">stock.putaway.rule</field>
<field name="arch" type="xml">
<tree string="Putaway Rules" editable="bottom" sample='1'>
<field name="company_id" invisible="1"/>
<field name="package_type_ids" invisible="1"/>
<field name="sequence" widget="handle"
invisible="context.get('invisible_handle', False)"/>
<field name="location_in_id" string="When product arrives in"
+2
View File
@@ -252,6 +252,7 @@
attrs="{'invisible': [('type', 'not in', ['consu', 'product'])]}"/>
</header>
<div name="button_box" position="inside">
<field name="tracking" invisible="1"/>
<field name="show_on_hand_qty_status_button" invisible="1"/>
<field name="show_forecasted_qty_status_button" invisible="1"/>
<button class="oe_stat_button"
@@ -358,6 +359,7 @@
attrs="{'invisible': [('type', 'not in', ['consu', 'product'])]}"/>
</header>
<div name="button_box" position="inside">
<field name="tracking" invisible="1"/>
<field name="show_on_hand_qty_status_button" invisible="1"/>
<field name="show_forecasted_qty_status_button" invisible="1"/>
<button type="object"
@@ -15,6 +15,7 @@
<field name="model">stock.location</field>
<field name="arch" type="xml">
<form string="Stock Location" create="false">
<field name="company_id" invisible="1"/>
<sheet>
<div class="oe_button_box" name="button_box">
<button string="Putaway Rules"
@@ -180,6 +181,7 @@
<field eval="7" name="priority" />
<field name="arch" type="xml">
<form string="Route">
<field name="company_id" invisible="1"/>
<sheet>
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
<div class="oe_title">
+1
View File
@@ -7,6 +7,7 @@
<field name="arch" type="xml">
<form string="Lots/Serial Numbers">
<sheet>
<field name="company_id" invisible="1"/>
<field name="display_complete" invisible="1"/>
<div class="oe_button_box" name="button_box"
attrs="{'invisible': [('display_complete', '=', False)]}">
@@ -62,6 +62,9 @@
<sheet>
<field name="company_id" invisible="1"/>
<field name="picking_id" invisible="1"/>
<field name="location_id" invisible="1"/>
<field name="location_dest_id" invisible="1"/>
<field name="package_id" invisible="1"/>
<field name="product_uom_category_id" invisible="1"/>
<group>
<group>
+8
View File
@@ -220,6 +220,9 @@
<field name="product_uom_category_id" invisible="1"/>
<field name="product_id" invisible="1"/>
<field name="package_level_id" invisible="1"/>
<field name="location_id" invisible="1"/>
<field name="location_dest_id" invisible="1"/>
<field name="package_id" invisible="1"/>
<field name="location_id" options="{'no_create': True}" attrs="{'readonly': ['&amp;', ('package_level_id', '!=', False), ('parent.picking_type_entire_packs', '=', True)]}" invisible="not context.get('show_source_location')" domain="[('id', 'child_of', parent.location_id), '|', ('company_id', '=', False), ('company_id', '=', company_id), ('usage', '!=', 'view')]" groups="stock.group_stock_multi_locations"/>
<field name="location_dest_id" width="0.75" attrs="{'readonly': ['&amp;', ('package_level_id', '!=', False), ('parent.picking_type_entire_packs', '=', True)]}" invisible="not context.get('show_destination_location')" domain="[('id', 'child_of', parent.location_dest_id), '|', ('company_id', '=', False), ('company_id', '=', company_id), ('usage', '!=', 'view')]" groups="stock.group_stock_multi_locations"/>
<field name="lot_id" groups="stock.group_production_lot"
@@ -263,6 +266,10 @@
<field name="move_id" invisible="1"/>
<field name="picking_id" invisible="1"/>
<field name="product_uom_category_id" invisible="1"/>
<field name="package_id" invisible="1"/>
<field name="result_package_id" invisible="1"/>
<field name="location_id" invisible="1"/>
<field name="location_dest_id" invisible="1"/>
<field name="location_id" options="{'no_create': True}" attrs="{'column_invisible': [('parent.picking_type_code', '=', 'incoming')]}" groups="stock.group_stock_multi_locations" domain="[('id', 'child_of', parent.location_id), '|', ('company_id', '=', False), ('company_id', '=', company_id), ('usage', '!=', 'view')]"/>
<field name="location_dest_id" options="{'no_create': True}" attrs="{'column_invisible': [('parent.picking_type_code', '=', 'outgoing')]}" groups="stock.group_stock_multi_locations" domain="[('id', 'child_of', parent.location_dest_id), '|', ('company_id', '=', False), ('company_id', '=', company_id), ('usage', '!=', 'view')]"/>
<field name="package_id" groups="stock.group_tracking_lot"/>
@@ -287,6 +294,7 @@
<field eval="1" name="priority"/>
<field name="arch" type="xml">
<form string="Stock Moves" create="0" edit="0">
<field name="company_id" invisible="1"/>
<header>
<field name="state" widget="statusbar" statusbar_visible="draft,confirmed,assigned,done" />
</header>
@@ -40,6 +40,7 @@
<field name="arch" type="xml">
<tree string="Reordering Rules" editable="bottom" js_class="stock_orderpoint_list" sample="1" multi_edit="1">
<field name="active" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="product_category_id" invisible="1"/>
<field name="product_tmpl_id" invisible="1"/>
<field name="product_id" attrs="{'readonly': [('product_id', '!=', False)]}" invisible="context.get('default_product_id')" force_save="1"/>
@@ -140,6 +141,7 @@
<group>
<group>
<field name="active" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="route_id" invisible="1"/>
<field name="product_id"/>
<label for="product_min_qty"/>
@@ -9,6 +9,7 @@
<field name="state" widget="statusbar" statusbar_visible="draft,confirmed,assigned,done" />
</header>
<group>
<field name="company_id" invisible="1"/>
<field name="picking_id" invisible="1"/>
<field name="show_lots_m2o" invisible="1"/>
<field name="show_lots_text" invisible="1"/>
@@ -39,6 +39,7 @@
<group>
<group>
<field name="active" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="hide_reservation_method" invisible="1"/>
<field name="name"/>
<field name="sequence_id" groups="base.group_no_one"/>
@@ -244,6 +245,7 @@
<field name="use_create_lots" invisible="1"/>
<field name="show_set_qty_button" invisible="1"/>
<field name="show_clear_qty_button" invisible="1"/>
<field name="company_id" invisible="1"/>
<header>
<button name="action_confirm" attrs="{'invisible': [('show_mark_as_todo', '=', False)]}" string="Mark as Todo" type="object" class="oe_highlight" groups="base.group_user" data-hotkey="x"/>
@@ -317,6 +319,8 @@
</div>
<field name="partner_id" nolabel="1"/>
<field name="picking_type_id" attrs="{'invisible': [('hide_picking_type', '=', True)], 'readonly': [('state', '!=', 'draft')]}"/>
<field name="location_id" groups="!stock.group_stock_multi_locations" invisible="1"/>
<field name="location_dest_id" groups="!stock.group_stock_multi_locations" invisible="1"/>
<field name="location_id" options="{'no_create': True}" groups="stock.group_stock_multi_locations" attrs="{'invisible': [('picking_type_code', '=', 'incoming')]}"/>
<field name="location_dest_id" options="{'no_create': True}" groups="stock.group_stock_multi_locations" attrs="{'invisible': [('picking_type_code', '=', 'outgoing')]}"/>
<field name="backorder_id" attrs="{'invisible': [('backorder_id','=',False)]}"/>
+3
View File
@@ -55,6 +55,7 @@
<group>
<group>
<field name="tracking" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="product_id" readonly="0" options="{'no_create': True}"/>
<field name="location_id" readonly="0" options="{'no_create': True}"/>
<field name="lot_id" groups="stock.group_production_lot"
@@ -93,6 +94,7 @@
sample="1">
<field name="id" invisible="1"/>
<field name="tracking" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="product_id" attrs="{'readonly': [('id', '!=', False)]}"
readonly="context.get('single_product', False)" force_save="1"
options="{'no_create': True}"/>
@@ -337,6 +339,7 @@
<field name="sn_duplicated" invisible="1"/>
<field name="tracking" invisible="1"/>
<field name="inventory_quantity_set" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="location_id" domain="[('usage', 'in', ['internal', 'transit'])]" attrs="{'readonly': [('id', '!=', False)]}" invisible="context.get('hide_location', False)" options="{'no_create': True}"/>
<field name="storage_category_id" groups="stock.group_stock_storage_categories" invisible="context.get('hide_location', False)" options="{'no_create': True}" optional="hidden"/>
<field name="cyclic_inventory_frequency" invisible="context.get('hide_location', False)" options="{'no_create': True}" optional="hidden"/>
+4
View File
@@ -69,6 +69,7 @@
<group>
<group>
<field name="active" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="picking_type_code_domain" invisible="1"/>
<field name="action"/>
<field name="picking_type_id"/>
@@ -123,6 +124,9 @@
<xpath expr="//group[@name='apply_on']" position="attributes">
<attribute name="groups">base.group_multi_company,base.group_no_one</attribute>
</xpath>
<xpath expr="//form" position="inside">
<field name="route_company_id" invisible="1"/>
</xpath>
</field>
</record>
+1
View File
@@ -55,6 +55,7 @@
</div>
</group>
<group>
<field name="company_id" invisible="1"/>
<field name="lot_id" context="{'default_product_id': product_id, 'default_company_id': company_id}" attrs="{'invisible': ['|',('product_id', '=', False),('tracking', '=', 'none')], 'required': [('tracking', '!=', 'none')]}" groups="stock.group_production_lot"/>
<field name="tracking" invisible="1"/>
<field name="package_id" groups="stock.group_tracking_lot"/>
@@ -5,6 +5,7 @@
<field name="model">stock.storage.category</field>
<field name="arch" type="xml">
<form string="Storage Category">
<field name="company_id" invisible="1"/>
<sheet>
<div class="oe_button_box" name="button_box">
<button name="%(action_storage_category_locations)d" string="Locations" type="action" class="oe_stat_button" icon="fa-arrows-v"/>
@@ -86,6 +87,7 @@
<field name="quantity"/>
<field name="product_uom_id" options="{'no_create': True, 'no_open': True}"/>
<field name="company_id" invisible="1"/>
<field name="package_type_id" invisible="1"/>
</tree>
</field>
</record>
@@ -20,6 +20,7 @@
<group>
<group>
<field name="active" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="code" placeholder="e.g. CW"/>
</group>
<group>
@@ -24,6 +24,7 @@
<tree editable="bottom" create="0" delete="0">
<field name="id" invisible="1"/>
<field name="tracking" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="product_id" attrs="{'readonly': [('id', '!=', False)]}" invisible="context.get('single_product', False)" readonly="context.get('single_product', False)" force_save="1" options="{'no_create': True}"/>
<field name="location_id" attrs="{'readonly': [('id', '!=', False)]}" invisible="context.get('hide_location', False)" options="{'no_create': True}"/>
<field name="lot_id" groups="stock.group_production_lot" attrs="{
+8 -3
View File
@@ -111,14 +111,19 @@ class ReturnPicking(models.TransientModel):
return vals
def _prepare_picking_default_values(self):
return {
vals = {
'move_ids': [],
'picking_type_id': self.picking_id.picking_type_id.return_picking_type_id.id or self.picking_id.picking_type_id.id,
'state': 'draft',
'origin': _("Return of %s") % self.picking_id.name,
'location_id': self.picking_id.location_dest_id.id,
'location_dest_id': self.location_id.id
}
# TestPickShip.test_mto_moves_return, TestPickShip.test_mto_moves_return_extra,
# TestPickShip.test_pick_pack_ship_return, TestPickShip.test_pick_ship_return, TestPickShip.test_return_lot
if self.picking_id.location_dest_id:
vals['location_id'] = self.picking_id.location_dest_id.id
if self.location_id:
vals['location_dest_id'] = self.location_id.id
return vals
def _create_returns(self):
# TODO sle: the unreserve of the next moves could be less brutal
@@ -33,6 +33,7 @@
<field name="arch" type="xml">
<group name="account_property" position="inside">
<group name="account_stock_property" string="Account Stock Properties" groups="account.group_account_readonly" attrs="{'invisible':[('property_valuation', '=', 'manual_periodic')]}">
<field name="property_valuation" invisible="1"/>
<field name="property_stock_valuation_account_id" options="{'no_create': True}" attrs="{'required':[('property_valuation', '=', 'real_time')]}"/>
<field name="property_stock_journal" attrs="{'required':[('property_valuation', '=', 'real_time')]}" />
<field name="property_stock_account_input_categ_id" attrs="{'required':[ ('property_valuation', '=', 'real_time')]}" />
@@ -7,6 +7,7 @@
<field name="model">stock.landed.cost</field>
<field name="arch" type="xml">
<form string="Landed Costs">
<field name="company_id" invisible="1"/>
<field name="stock_valuation_layer_ids" invisible="1"/>
<header>
<button name="button_validate" string="Validate" states="draft" class="oe_highlight" type="object"/>
@@ -60,6 +60,7 @@
<tree editable="top" decoration-muted="state == 'cancel'" string="Move Lines" default_order="location_id">
<field name="tracking" invisible="1"/>
<field name="state" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="product_id" context="{'default_detailed_type': 'product'}" required="1" attrs="{'readonly': [('id', '!=', False)]}"/>
<field name="picking_id" required="1" attrs="{'readonly': [('id', '!=', False)]}"
options="{'no_create_edit': True}" domain="[('id', 'in', parent.picking_ids)]"/>
@@ -82,6 +83,7 @@
<field name="model">stock.picking.batch</field>
<field name="arch" type="xml">
<form string="Stock Batch Transfer">
<field name="company_id" invisible="1"/>
<field name="show_check_availability" invisible="1"/>
<field name="show_validate" invisible="1"/>
<field name="show_allocation" invisible="1"/>
@@ -52,6 +52,7 @@
<field string="Attempts" name="attempts_count" widget="statinfo"/>
</button>
</div>
<field name="test_entry" invisible="1"/>
<widget name="web_ribbon" title="Test Entry" bg_color="bg-info"
attrs="{'invisible': [('test_entry', '!=', True)]}"/>
<widget name="web_ribbon" title="Failed" bg_color="bg-danger"
@@ -69,6 +69,7 @@
<field name="seniority_id" attrs="{'invisible': [('contact_filter_type','!=', 'seniority')]}" options="{'no_create_edit': True, 'no_quick_create': True}"/>
</group>
</group>
<field name="lead_type" invisible="1"/>
<separator attrs="{'invisible': [('lead_type', '!=', 'lead')]}" string="Lead Data" />
<separator attrs="{'invisible': [('lead_type', '!=', 'opportunity')]}" string="Opportunity Data" />
<group>
@@ -37,6 +37,7 @@
<field name="inherit_id" ref="event.view_event_tree"/>
<field name="arch" type="xml">
<field name="company_id" position="after">
<field name="company_id" invisible="1"/>
<field name="website_id" groups="website.group_multi_website" domain="['|', ('company_id', '=', company_id), ('company_id', '=', False)]" optional="show"/>
</field>
</field>
@@ -283,6 +283,7 @@
<field name="arch" type="xml">
<xpath expr="//group[@name='pricelist_availability']" position="after">
<group name="pricelist_website" string="Website">
<field name="company_id" invisible="1"/>
<field name="website_id" options="{'no_create': True}"/>
<field name="selectable"/>
<field name="code"/>
@@ -5,6 +5,7 @@
<field name="model">coupon.share</field>
<field name="arch" type="xml">
<form string="Share Loyalty Card" edit="1" create="0">
<field name="website_id" invisible="1"/>
<field name="program_website_id" invisible="1"/>
<group attrs="{'invisible': [('website_id', '=', False)]}">
<div colspan="2">
@@ -21,6 +21,7 @@
<div class="row mt16"
id="website_warehouse_setting"
groups="stock.group_stock_multi_warehouses">
<field name="website_company_id" invisible="1"/>
<label for="website_warehouse_id" string="Warehouse" class="col-lg-3 o_light_label" />
<field name="website_warehouse_id"/>
</div>
+146 -40
View File
@@ -974,16 +974,11 @@ actual arch.
def _apply_groups(self, node, name_manager, node_info):
""" Apply group restrictions: elements with a 'groups' attribute should
be made invisible to people who are not members.
be removed from the view to people who are not members.
"""
if node.get('groups'):
can_see = self.user_has_groups(groups=node.get('groups'))
if not can_see:
node.set('invisible', '1')
node_info['modifiers']['invisible'] = True
if 'attrs' in node.attrib:
del node.attrib['attrs'] # avoid making field visible later
del node.attrib['groups']
if not self.user_has_groups(groups=node.get('groups')):
node.getparent().remove(node)
def _get_view_refs(self, node):
""" Extract the `[view_type]_view_ref` keys and values from the node context attribute,
@@ -1145,10 +1140,10 @@ actual arch.
def _postprocess_tag_calendar(self, node, name_manager, node_info):
for additional_field in ('date_start', 'date_delay', 'date_stop', 'color', 'all_day'):
if node.get(additional_field):
name_manager.has_field(node.get(additional_field).split('.', 1)[0])
name_manager.has_field(node, node.get(additional_field).split('.', 1)[0])
for f in node:
if f.tag == 'filter':
name_manager.has_field(f.get('name'))
name_manager.has_field(node, f.get('name'))
def _postprocess_tag_field(self, node, name_manager, node_info):
if node.get('name'):
@@ -1203,7 +1198,7 @@ actual arch.
node.set('can_create', 'true' if can_create else 'false')
node.set('can_write', 'true' if can_write else 'false')
name_manager.has_field(node.get('name'), attrs)
name_manager.has_field(node, node.get('name'), attrs)
field_info = name_manager.field_info.get(node.get('name'))
if field_info:
@@ -1225,7 +1220,7 @@ actual arch.
self.with_context(
base_model_name=name_manager.model._name,
)._postprocess_view(node, field.comodel_name, editable=False, parent_name_manager=name_manager)
name_manager.has_field(name)
name_manager.has_field(node, name)
def _postprocess_tag_label(self, node, name_manager, node_info):
if node.get('for'):
@@ -1355,10 +1350,10 @@ actual arch.
def _validate_tag_calendar(self, node, name_manager, node_info):
for additional_field in ('date_start', 'date_delay', 'date_stop', 'color', 'all_day'):
if node.get(additional_field):
name_manager.has_field(node.get(additional_field).split('.', 1)[0])
name_manager.has_field(node, node.get(additional_field).split('.', 1)[0])
for f in node:
if f.tag == 'filter':
name_manager.has_field(f.get('name'))
name_manager.has_field(node, f.get('name'))
def _validate_tag_search(self, node, name_manager, node_info):
if node_info['validate'] and not node.iterdescendants(tag="field"):
@@ -1397,7 +1392,7 @@ actual arch.
fnames, vnames = self._get_domain_identifiers(node, domain, desc)
self._check_field_paths(node, fnames, field.comodel_name, f"{desc} ({domain})")
if vnames:
name_manager.must_have_fields(vnames, f"{desc} ({domain})")
name_manager.must_have_fields(node, vnames, f"{desc} ({domain})")
elif validate and node.get('domain'):
msg = _(
@@ -1413,8 +1408,9 @@ actual arch.
sub_manager = self._validate_view(
child, field.comodel_name, editable=node_info['editable'], full=validate,
)
for fname, use in sub_manager.mandatory_parent_fields.items():
name_manager.must_have_field(fname, use)
for fname, groups_uses in sub_manager.mandatory_parent_fields.items():
for groups, use in groups_uses.items():
name_manager.must_have_field(node, fname, use, groups=groups)
elif validate and name not in name_manager.field_info:
msg = _(
@@ -1423,7 +1419,7 @@ actual arch.
)
self._raise_view_error(msg, node)
name_manager.has_field(name, {'id': node.get('id'), 'select': node.get('select')})
name_manager.has_field(node, name, {'id': node.get('id'), 'select': node.get('select')})
if validate:
for attribute in ('invisible', 'readonly', 'required'):
@@ -1447,7 +1443,7 @@ actual arch.
fnames, vnames = self._get_domain_identifiers(node, domain, desc)
self._check_field_paths(node, fnames, name_manager.model._name, f"{desc} ({domain})")
if vnames:
name_manager.must_have_fields(vnames, f"{desc} ({domain})")
name_manager.must_have_fields(node, vnames, f"{desc} ({domain})")
def _validate_tag_button(self, node, name_manager, node_info):
if not node_info['validate']:
@@ -1535,7 +1531,7 @@ actual arch.
fnames, vnames = self._get_domain_identifiers(node, domain, desc)
self._check_field_paths(node, fnames, field.comodel_name, f"{desc} ({domain})")
if vnames:
name_manager.must_have_fields(vnames, f"{desc} ({domain})")
name_manager.must_have_fields(node, vnames, f"{desc} ({domain})")
# move all children nodes into a new node <groupby>
groupby_node = E.groupby(*node)
@@ -1543,9 +1539,10 @@ actual arch.
sub_manager = self._validate_view(
groupby_node, field.comodel_name, editable=False, full=node_info['validate'],
)
name_manager.has_field(name)
for fname, use in sub_manager.mandatory_parent_fields.items():
name_manager.must_have_field(fname, use)
name_manager.has_field(node, name)
for fname, groups_uses in sub_manager.mandatory_parent_fields.items():
for groups, use in groups_uses.items():
name_manager.must_have_field(node, fname, use, groups=groups)
elif node_info['validate']:
msg = _(
@@ -1639,11 +1636,11 @@ actual arch.
# domains in attrs are used for readonly, invisible, ...
# and thus are only executed client side
fnames, vnames = self._get_domain_identifiers(node, val_ast, attr, expr)
name_manager.must_have_fields(fnames | vnames, f"attrs ({expr})")
name_manager.must_have_fields(node, fnames | vnames, f"attrs ({expr})")
else:
vnames = get_variable_names(val_ast)
if vnames:
name_manager.must_have_fields(vnames, f"attrs ({expr})")
name_manager.must_have_fields(node, vnames, f"attrs ({expr})")
elif attr == 'context':
for key, val_ast in get_dict_asts(expr).items():
@@ -1665,7 +1662,7 @@ actual arch.
else:
vnames = get_variable_names(val_ast)
if vnames:
name_manager.must_have_fields(vnames, f"context ({expr})")
name_manager.must_have_fields(node, vnames, f"context ({expr})")
elif attr == 'groups':
for group in expr.replace('!', '').split(','):
@@ -1688,7 +1685,7 @@ actual arch.
elif attr.startswith('decoration-'):
vnames = get_variable_names(expr)
if vnames:
name_manager.must_have_fields(vnames, f"{attr}={expr}")
name_manager.must_have_fields(node, vnames, f"{attr}={expr}")
elif attr == 'data-bs-toggle' and expr == 'tab':
if node.get('role') != 'tab':
@@ -2194,11 +2191,11 @@ class NameManager:
def __init__(self, model, parent=None):
self.model = model
self.available_fields = collections.defaultdict(dict) # {field_name: field_info}
self.available_fields = collections.defaultdict(dict) # {field_name: {'groups': groups, 'info': field_info}}
self.available_actions = set()
self.available_names = set()
self.mandatory_fields = dict() # {field_name: use}
self.mandatory_parent_fields = dict() # {field_name: use}
self.mandatory_fields = collections.defaultdict(dict) # {field_name: {'groups': 'use}}
self.mandatory_parent_fields = collections.defaultdict(dict) # {field_name: {'groups': use}}
self.mandatory_names = dict() # {name: use}
self.parent = parent
self.children = []
@@ -2215,26 +2212,36 @@ class NameManager:
info['states'] = {}
return field_info
def has_field(self, name, info=frozendict()):
self.available_fields[name].update(info)
def has_field(self, node, name, info=frozendict()):
groups = self._get_node_groups(node)
self.available_fields[name].setdefault('info', {}).update(info)
self.available_fields[name].setdefault('groups', []).append(groups)
self.available_names.add(info.get('id') or name)
def has_action(self, name):
self.available_actions.add(name)
def must_have_field(self, name, use):
if name.startswith('parent.'):
self.mandatory_parent_fields[name[7:]] = use
def must_have_field(self, node, name, use, groups=None):
node_groups = self._get_node_groups(node)
if groups:
groups = groups + node_groups
else:
self.mandatory_fields[name] = use
groups = node_groups
if name.startswith('parent.'):
self.mandatory_parent_fields[name[7:]][groups] = use
else:
self.mandatory_fields[name][groups] = use
def must_have_fields(self, names, use):
def must_have_fields(self, node, names, use, groups=None):
for name in names:
self.must_have_field(name, use)
self.must_have_field(node, name, use, groups=groups)
def must_have_name(self, name, use):
self.mandatory_names[name] = use
def _get_node_groups(self, node):
return tuple(tuple(n.get('groups').split(',')) for n in node.xpath('ancestor-or-self::*[@groups]'))
def check(self, view):
# context for translations below
context = view.env.context # pylint: disable=unused-variable
@@ -2252,7 +2259,8 @@ class NameManager:
message = _("Field `%(name)s` does not exist", name=name)
view._raise_view_error(message)
for name, use in self.mandatory_fields.items():
for name, groups_uses in self.mandatory_fields.items():
use = next(iter(groups_uses.values()))
if name == 'id': # always available
continue
if "." in name:
@@ -2261,7 +2269,7 @@ class NameManager:
definition=name, use=use,
)
view._raise_view_error(msg)
info = self.available_fields.get(name)
info = self.available_fields[name].get('info')
if info is None:
msg = _(
"Field %(name)r used in %(use)s must be present in view but is missing.",
@@ -2275,6 +2283,104 @@ class NameManager:
)
view._raise_view_error(msg)
def combinate(groups):
# [['A'], ['B', 'C'], ['D', 'E']]
# returns
# [['A', 'B', 'D'], ['A', 'C', 'D'], ['A', 'B', 'E'], ['A', 'C', 'E']]
# [['!A', '!B'], ['C', 'D']]
# returns
# [['!A', '!B', 'C'], ['!A', '!B', 'D']]
# [['A', 'B'], ['!C', '!D']]
# returns
# [['A', '!C', '!D'], ['B', '!C', '!D']]
# [['!A', '!B', 'C', 'D'], ['E', 'F']]
# returns
# [['!A', '!B', 'C', 'E'], ['!A', '!B', 'D', 'E'], ['!A', '!B', 'C', 'F'], ['!A', '!B', 'D', 'F']]
if not groups:
return []
positive = [(group,) for group in groups[0] if not group or not group.startswith('!')]
negative = tuple(group for group in groups[0] if group and group.startswith('!'))
combinations = [negative + p for p in positive] if positive else [negative]
if len(groups) > 1:
combinations = [group + tuple(c) for c in combinate(groups[1:]) for group in combinations]
return [set(combination) for combination in set(tuple(combination) for combination in combinations)]
for mandatory_for_groups, use in groups_uses.items():
mandatory_combinations = combinate(mandatory_for_groups or [(None,)])
available_combinations = [
combination
for available_for_groups in self.available_fields[name]['groups']
for combination in combinate(available_for_groups or [(None,)])
]
if (
# If (None,) is in available_combinations,
# it means the field is in the view without any group restriction,
# and is therefore available for anyone / any group
{None} not in available_combinations
# If there are two mutually exclusive combinations,
# it means the field is available for anyone / any group
and not any(
{group[1:] if group.startswith('!') else '!' + group for group in combination}
in available_combinations
for combination in available_combinations
)
# For all mandatory combinations, find an available combination
# which is included in the mandatory combination
# e.g.
# mandatory combination: A B C
# available combination: A B
# The above is valid, the field will be available for users having both A and B groups
# and the field is mandatory only for users having A B and C groups.
and not all(
any(
available_combination.issubset(mandatory_combination)
for available_combination in available_combinations
) for mandatory_combination in mandatory_combinations
)
# Same than above but take into account implied groups (e.g. group_system includes group_erp_manager and group_user)
# for positive groups
and not all(
any(
available_combination.issubset(combination)
for available_combination in available_combinations
for combination in
(
mandatory_combination - {group} | {trans_group}
for group in mandatory_combination if group and not group.startswith('!')
for trans_group in view.env.ref(group).trans_implied_ids.get_external_id().values()
)
)
for mandatory_combination in mandatory_combinations
)
# Same than above but take into account implied groups (e.g. group_system includes group_erp_manager and group_user)
# for negative groups
and not all(
any(
combination.issubset(mandatory_combination)
for available_combination in available_combinations
for combination in
(
available_combination - {group} | {'!' + trans_group}
for group in available_combination if group and group.startswith('!')
for trans_group in view.env.ref(group[1:]).trans_implied_ids.get_external_id().values()
)
) for mandatory_combination in mandatory_combinations
)
):
msg = _(
"Field %(name)r used in %(use)s is restricted to the group(s) %(groups)s.",
name=name, use=use, groups=','.join(
group
for availability in self.available_fields[name]['groups']
for combination in availability
for group in combination
)
)
view._raise_view_error(msg)
def update_available_fields(self):
for name, info in self.available_fields.items():
info.update(self.field_info.get(name, ()))
+7 -1
View File
@@ -1308,7 +1308,7 @@ class GroupsView(models.Model):
else:
group_no_one = view.env.ref('base.group_no_one')
group_employee = view.env.ref('base.group_user')
xml1, xml2, xml3 = [], [], []
xml0, xml1, xml2, xml3 = [], [], [], []
xml_by_category = {}
xml1.append(E.separator(string='User Type', colspan="2", groups='base.group_no_one'))
@@ -1327,6 +1327,11 @@ class GroupsView(models.Model):
if app.xml_id == 'base.module_category_user_type':
# application name with a selection field
field_name = name_selection_groups(gs.ids)
# test_reified_groups, put the user category type in invisible
# as it's used in domain of attrs of other fields,
# and the normal user category type field node is wrapped in a `groups="base.no_one"`,
# and is therefore removed when not in debug mode.
xml0.append(E.field(name=field_name, invisible="1", on_change="1"))
user_type_field_name = field_name
user_type_readonly = str({'readonly': [(user_type_field_name, '!=', group_employee.id)]})
attrs['widget'] = 'radio'
@@ -1369,6 +1374,7 @@ class GroupsView(models.Model):
xml2.append(E.group(*(xml_by_category[xml_cat]), col="2", string=master_category_name))
xml = E.field(
*(xml0),
E.group(*(xml1), col="2", groups="base.group_no_one"),
E.group(*(xml2), col="2", attrs=str(user_type_attrs)),
E.group(*(xml3), col="4", attrs=str(user_type_attrs), groups="base.group_no_one"), name="groups_id", position="replace")

Some files were not shown because too many files have changed in this diff Show More