Goal:
* Simplified modifiers to only have one way to define modifiers;
* Remove states attributes on python field;
* Use python expression in view `required`, `readonly`, `invisible`;
* More accurate validation of xml views.
This commit change the syntax to python expression. The next commit
will update/convert all xml views.
Before this commit:
* the `required`, `readonly` and `invisible` attributes can only have
values of `True`, `False`, 1, 0 or a python expression to use the
context;
* the `attrs` attribute define a dict. The key of this dict was
`required`, `readonly` and `invisible` and the values are the domain or
a string representing a domain to be evaluate as python expression.
This python expressions was evaluate by the javascript with view fields
and other contextual values as: context, uid, parent, active_id,
active_ids, active_model, allowed_company_ids, current_company_id.
* the `states` attribute in the view was a comma separated list of the
state. This list was combined with the `invisible` attribute;
* the `invisible` attribute on python field is used as default value;
* the `states` attribute on python field was dictionnary with state as
key and list of tuple. This structure was combined with `readonly` view
attribute.
* After combining, the resulting domains of the different attributes
`required`, `readonly` and `invisible` are evaluated with the values of
the fields. The `invisible` attributes is splitted into two use:
`invisible` and `column_invisible`.
After this commit:
* The attributes `required`, `readonly`, `invisible` and
`column_invisible` define python expression. This python expressions
are evaluate by the javascript with view fields and other contextual
values as: context, uid, parent, active_id, active_ids, active_model,
allowed_company_ids, current_company_id.
The domains can contains contextual value and will be evaluate by the
javascript.
```xml
<field name="field_a" readonly="not context.get('show_a')" attrs="{'readonly': [('field_b', '!=', False), ('field_c', '=', parent.c)]}"/>
<field name="field_b" states="draft"/>
```
will be replaced by
```xml
<field name="field_a" readonly="not context.get('show_a') or field_b and field_c == parent.c"/>
<field name="field_b" invisible="state != 'draft'"/>
```
Some inherited views will be modified differently in order to maintain
the previous behavior:
```xml
<field name="field_a" readonly="not context.get('show_a')" attrs="{'invisible': [('field_b', '!=', False)]}">
```
```xml
<field name="field_a" position="attributes">
<attribute name="attrs">{'readonly': [('field_c', '=', False)], 'invisible': [('field_d', '!=', '3')]}<attribute>
</field>
```
will be replaced by
```xml
<field name="field_a" readonly="not context.get('show_a')" invisible="field_b">
```
```xml
<field name="field_a" position="attributes">
<attribute name="readonly" add="(not field_c)" separator=" or "/>
<attribute name="invisible">field_d != 3<attribute>
</field>
```
Validation:
A stricter control is made on the level of the attributes (modifiers)
and the fields necessary for these. The use of the previous attributes
'attr' and 'states' triggers an error (these no longer exist after the
application of the migration script)
task-2495504
Part-of: odoo/odoo#104741
THese are rarely intended for all users but often intended only for
employees.
account:
account.incoterms: only used within internal business models
account.journal.group: same as account.journal, add sudo in computed field
account_edi: need access to accounting objects
base_address_extended:
res.city: only employees should access address data
board: only employees uses this (old) module
crm:
crm.stage: internal users business object
hr_recruitment: employees can read
im_livechat: apply same as for the steps
l10n_ar: used on partner, not only invoices
l10n_ec: accessed only through account.move
l10n_latam: accessed on res.partner
mail:
publisher.warrenty.contract: no data, only static models
mail.channel: group_user has already his own rule
mail.group: group_user has already his own rule
mail.message.subtype: group_user has already his own rule
mail.message.all: remove, already has a portal and employee rule
partner_autocomplete: no interaction with public
project:
project.tags: only needed for project sharing
sale_management:
sale.order.option: same as sale.order
utm: employee already has write access
web_editor: test models that have nothing to do here
web_tour: only employees uses tours
website_sale:
product.ribbon: add sudo for access
base:
ir.default: only employees uses set (could probably be converted to group_system)
ir.ui.view.custom: same as ir.ui.view, add sudo when needed
report.*: portal users don't configure reports
res.users.log: create in sudo, no access needed (adapt test to use another model)
res.lang: still needed for public
closesodoo/odoo#118701
Related: odoo/enterprise#41285
Signed-off-by: Martin Trigaux (mat) <mat@odoo.com>
Steps to reproduce:
- install sale_management module;
- go to settings of sale_management and check "Automatic invoice" parameter;
- in debug mode, we can see the template selected (do not change it);
- save the settings;
(This is just one example)
Issue:
The parameter is not added to the "ir_config_parameter" table.
Cause:
The improvement which consists in comparing the settings which one wishes to record with those which already exist in order to set the parameters which are different creates a problem.
Indeed, the current settings are retrieved directly from the model with their default value if there is one.
These are the values that will be used for the comparison.
Because of this, when we want to save a parameter which is set to its default value (on first save), the logic will not notice a difference.
Therefore, the parameter will not be saved in the "ir_config_parameter" table.
Consequence:
In the code, when we wanted to look up the value of a parameter, if it is not saved, we use its default value instead of saving it with its default value.
Example:
```
field_example = fields.Integer(string='Example', default=1000, config_parameter='model.field_example')
value_field_example = params.get_param('model.field_example', default=1000)
```
The default value is repeated.
Solution:
To know whether or not we add a new parameter in the "ir_config_parameter" table, we must look at what already exists in the table and not in the settings defined in the model.
opw-3057306
closesodoo/odoo#106188
X-original-commit: d881dfaaa163b409a94494e35afa2cbda7cf2aa2
Related: odoo/enterprise#34258
Signed-off-by: Victor Feyens (vfe) <vfe@odoo.com>
Issue is specifically in the case of an onchange removing a record in
an o2m in an o2m (so a sub-o2m) if loading an *existing* record in the
SSF: since the server pretty much only returns a REMOVE_ALL followed
by the records to keep or create, conserving the removal information
requires diffing the value currently stored in the form and the result
fo the onchange.
Diff which was properly done for top-level o2ms, but not for the ones
below that (apparently forgot this bit when improving support for
nested o2ms earlier this year).
X-original-commit: 177d009541cca589c6ebb85fc051b893ec001536
The SSF had pretty much punted on nested o2m as that's... not usually
a concern because few people are insane enough to put o2ms in their
o2ms in their views. And also because even if somebody did, sometimes
nothing would break because it turns out to be pretty difficult to
actually get into *using* those things.
MRP managed to do it though, and repro-ing required copying over parts
of mrp.production and then understanding why it didn't fail:
* obviously needs an o2m (f1) which contains an o2m (f2), in the
view (an invisible tree inside a visible tree)
* needs an onchange which somehow updates the sub-o2m
* needs to actually trigger an onchange on the root form for f1,
meaning f1 must be a dependency of a compute field or something (here
I just marked every damn field as on_change as the optimisation of
"don't call onchange when there's no need to" doesn't matter)
Also needs to be working on an existing record with existing
lines *and sublines* as the issue occurs with records to update.
The issue here is that `_onchange_values` would clean up f1 e.g. send
nothing for unmodified entries, and only send modified fields
otherwise, but it would only do so for the toplevel, meaning the
sub-level would not go through this step, and could send UPDATE
commands with an `id` field (set to the original value but
still). This would then proceed to blow up while loading the record,
as id fields are not writeable.
The fix is to perform `_onchange_values` recursively. Do that using a
separate helper in order to avoid blowing up on override and whatnot,
or faffling about with weird branching to get the "default" values in
case they're not provided, the root function can get all the relevant
bits and call the helper with them, then the helper does that setup
internally and calls itself directly.
An other issue I stumbled upon when investigating is a similar problem
on *save*, due to an implementation detail of the SSF: UPDATE commands
are fetched lazily.
`_values_to_save` took care of "hydrating" all update commands (and
validating and filtering them) of modified o2m fields, but as it would
not do so recursively a modified f2 would not get properly hydrated
and filtered, and could try to write `None` onto existing records.
closesodoo/odoo#51350
X-original-commit: 3dfb4cfad849936748cc6a5b8f712e100461a86f
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
In the SSF, o2m updates get initialized with no values (1, id,
{}). The record data is only fetched when the "record" gets updated
explicitly from which updates will hopefully get properly tracked &
saved.
However if the "record" was first initialized through an onchange
values which are updated by the onchange (diverging from the db) those
would not get tracked and thus wouldn't get saved when the record is
saved.
* use more specific placeholder (None) for "o2m records to update but
we don't have values yet"
* once we have values, always store them as an update-tracking dict
* if we don't have values yet for an o2m and an onchange is trying to
write to it, initialize with values from database first (might
eventually be a good idea to initialize upfront though there's the
question of what happens for default values and recursive views)
* mark anything coming back from the onchange and differing from local
values as changed (so they get sent out on save)
* properly reify parent values for onchange instead of sending them
as-is
* the evaluation context for contexts (and domains) needs properly
formatted values so use `_values_to_save` to get them, however it
cares about neither required-ing nor filtering out e.g. unmodified
fields, therefore add an awful toggle to handle this
Task 2150302
Probably todo in the future:
* better UI for change-tracking dict, should have "snapshot"
support (to freeze / discard previous changes)
* cleanup save, it's unclear that it properly resets the form
closesodoo/odoo#43235
X-original-commit: 6c99fe3ffec3aeb8d210e3b943ccef81d62d44ce
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
A required boolean accepts two values, True and False, however the SSF
and the web-client assume False to be equal to NULL and treat them
interchangeably.
In the SSF, we verify that a required field is filled by checking that
its value is different from False, however False is a valid value for a
boolean, this means that setting a required boolean to False would never
work in the SSF.
This commit overcomes this issue by simply skipping the check for fields
of type boolean.
closesodoo/odoo#34729
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
The SSF would correctly filter out readonly fields when saving a
toplevel form, however it could not remove readonly values when saving
o2m pseudo-records to the parent form (as these would be expected to
remain available for reading upon the next edition and whatnot), so
these values would get sent in 0/1 commands.
Filter out these fields during the parent / toplevel save call.
Complexity notes:
* evaluating readonly modifiers requires the entire record, so
unchanged fields still have to be written back to the parent form
and be filtered out when *it* is set up for save, an alternative
would be to store the `changed` and `readonly` flags alongside the
record dict, and have the post-process only override the
pre-computed readonly flag using force_save
* had to fix a test to match the new behaviour, the post-edition
states turns out to be in line with the client's behaviour (or how
it looks anyway)
Fixes#32019
The extra setup probably affects any o2m whose edition view itself
contains an o2m, but most likely to blow up entirely on models with
some sort of tree structure (parent/child relationship): the SSF
eagerly loads and setups the o2m's view, and the o2m's o2m's, ... ad
infinitam.
A better / cleaner fix would be to set up the subview on-demand (and
possibly cache it), but the rest of the o2m stuff is unlikely to work
correctly recursively so just don't recurse the o2m view setup at all
for now.
fixes#31458
The SSF would properly mark its own records as deleted, but it would
not properly handle deletion requests coming from an onchange, and it
would not necessarily convert DELETE_ALL commands (5) into the proper
sequence of individual (2)s matching existing records.
closesodoo/odoo#31431
In the normal course of saving-action, readonly fields are stripped
out when sending data to the server. The SSF would reuse the same code
when "saving" O2M lines to the parent record however that is not
correct and would lead to misbehaving views as readonly (non-stored)
fields used as "transients" (storing data within the extent of a
record's edition session) would not behave properly.
Fix by overriding the O2M Form's save so that readonly fields are
kept and stored in the parent record.
Closes#23620
Useful to better test business flows & replace yaml test files:
instantiating records & calling onchanges by hand is very
error-prone (it's easy to miss onchanges, or to manually set fields
which can't be set through the view we're interested in, ...)
odoo.tests.common.Form implements the basic *creation*
flow (fields_get, default_get, onchange) up to saving the record,
including proper handling of the required and readonly modifiers &
their domains.
* edition of existing records, including filtering out unmodified
fields when saving
* possibly buttons
* ensuring the API is convenient