Here is a situation where we had a problem:
- a form view with a one2many field, which has no inline views
- the (non inline) list view has a field A, and is not editable
- the (non inline) sub form view has fields A and B, with an onchange on
B which modifies A
In that case, the user could do this:
- go to edit mode
- click on 'add' a new one2many line
- change the value of B in the form view, this changes the value of A
- click on save to close the modal form view
- click on the new o2m record to reopen the modal form view
- rechange B
=> the onchange does not work
The explanation is that when we reopen the modal, we update the known fields
information, but we had to perform a fieldviewget to fetch the list
view, so we have a full knowledge of the fields. However, the code did
not update the fields info (because it uses _.default), which means that
the onchange information contained in the form view is lost.
Note: the test system had to be adapted to more closely simulate what
actually happens. In particular, the onchange flag is no longer added
by the mock server, since it should be done by the data manager, like
'real' code. This change broke the basic model tests, which had to be
modified accordingly, by setting manually the onchange flag.
Here is a scenario that could cause an issue:
- open a form view with a many2one
- click on edit button
- click on small external button for many2one. this opens a modal form
view
- click on save in the modal (without changing anything)
- click on save in the main record
- exit form view. this opens a discard dialog
There were two issues here:
- saving the modal dialog automatically marks the many2one field as changed.
This was necessary to force reloading the data, because editing a sub value
in the modal form view could have changed the display_name of the manyone
field. However, this is not necessary when no change was done.
- when saving a record, the _isDirty flag was reset to false only if an
actual rpc was done. However, it may happen that the flag is set to
dirty (for example, when modifying a value inside a many2one), but the
main record has no changed fields.
In this commit, we also remove the on_save handler in the
formviewdialog. This is a small refactoring in a stable version, but no code
currently use it, and I believe that it will make the code much easier
to maintain (the previous code was really awkward), so I think that the
tradeoff is acceptable.
After entering a value in a many2one, if one clicks somewhere else, a popup
is opened to suggest the user to create (or not) a new record with the entered
value.
Before this rev, closing this popup resulted in an unclear situation where the
input was still set with the entered value but the new record hadn't been created.
This commit fixes this by clearing the input value if the record is not created.
See task#36055
Before this commit, many2one fields were not (fully) usable in a list view: if
the user wrote some text, then when a dropdown with various choices
opened, pressed the 'down' key (to select a choice), the down keypress
caused a navigation move to the next line, which cancelled the
selection.
In this commit, we make the assumption that if the many2one dropdown is
opened, then navigation moves are not what the user wants.
When an onchange fails (for example, because the onchange python code
raised a ValidationError, it is convenient to revert the form view to a
valid state, which is the previous state.
To do that, we keep in the _applyChange function the initial state, and
restore it in the case the onchange fails.
A recent fix in web changed the field FieldImage to make sure it also
loads a __last_update field. This is fine, except that a test in
enterprise (web_clearbit_tests) was using a field image, and failed
because it could not find the __last_update field in the demo data.
With this commit, we make sure that the mock server is always aware of
the __last_update field.
A traceback was raised when opening a new record for a model with a
one2many displayed inside a one2many (e.g. simply displaying the
number of records in the relation), and with an onchange setting a
default value to the inner one2many (for example, linking it to
existing records).
As the inner o2m has no subviews, it has no fieldsInfo, and the
code assumed that fieldsInfo was always set.
This was for example reproducible from v11 as follows:
- create a product (with MTO/Buy and call for tender (need
purchase agreement module))
- create an SO with this product and confirm it
- go to purchase order form view and add a move_dest_ids field in
the tree definition of field order_line
- go to menu purchase agreement and choose the PA generated by
your SO
- add a vendor, confirm the PA and click on 'New Quotation'.
The ORM doesn't automatically link subrecords when it receives an
update command for a one2many. However, it may be necessary when
the default_get returns existing records for a one2many field. It
only worked before this rev. for the hr_expense model because of an
hack from 2016 (to reproduce: go to Expenses, check some expenses
from the list view, in Actions, click on 'Submit to Manager', the
new hr_expense_sheet contains the checked expenses (one2many), and
the webclient only sent an update command by linked subrecord).
This rev. fixes the problem properly: when the default_get returns
existing subrecords for a one2many field, a link_to command (4) is
generated alongside the update (1) command.
As a way to optimize loading, images are not necessarily fetched in db.
They have, in their url a "unique" parameter, which is the last_update date on **the record** and controls on the python-side whether it should get the image from a cache or from the db.
Before this commit, this __last_update field wasn't present in the view, so it wasn't fetched, and writes on a model's image worked but did not refresh.
The image displayed was the old one.
After this commit, when the image field widget is present, we force the loading of the __last_update field of the record.
Upon update, the image displayed is the new one.
OPW 777552
closes#20457
It could happen in some cases that a rpc coming from a view was not made
with the context of the view (and from the action). For example,
- enable "Lots & Serial Numbers" in the inventory settings
- create a stockable product tracked by lot
- go to the inventory dashboard, on the "receipts" card click on the
three dots and then on "planned transfer"
this link forces the context key "planned_transfer" in the action
https://github.com/odoo/odoo/blob/11.0/addons/stock/views/stock_picking_views.xml#L725
- now add a stock move to move some quantity of the product you just
created
- see that no button appears on the one2many list
- save
- see that a button appears on the one2many list
This is an issue because the read on the o2m at the end is made with the
wrong context. The reason is that when we fetch x2m, we create a new
datapoint of type 'list' which did not reuse the context of its parent.
issue: When reorder each line trigger an onchange on the line (if they are an
onchange linked to the field) and one onchange per line on the parent.
Adding an option doNotNotifyChange to avoid the trigger on the parent.
With the new views, we mainly focused on making them work. But we also
need to be able to handle unusual cases, such as some RPC failing. An
example of such a problem was the following:
- a form view with a one2many with an onchange
- edit mode, change a field in the one2many, click elsewhere
- the onchange triggers, calls the server
- it may happen that the server fails with a ValidationError
- the onchange deferred fails, the line in the one2many is restored to
the value known by the list renderer, which is the previous value (but
the new value was applied to the model)
- we now have a 'corrupted' state: clicking on save will send the
invalid value, even though the UI displays the previous one.
We solve this problem in this commit by making sure that the onchange
deferred returned by the _performOnchange always succeed (or stays
pending). If the rpc fails, the deferred will succeed with an empty
dictionary. This means that the list renderer will be updated with the
value from the model.
Note that it may not be intuitive that a failure in the onchange is not
seen as an error from the perspective of the _performOnChange, but I
think that it still makes sense:
- the validation/network errors are not really a concern of the UI. The
UI just display the values entered by the user.
- if an onchange fails (for example, because of a coding error), we do
not really want to prevent the user from working.
Also, when the onchange is performed, the initial change was already
applied to the local data. Rollbacking them would be a more difficult
task.
Note that we also improve the mock server in this commit, to allow
mocking failures.
We had an interesting bug with the field many2many_checkbox: in some
cases, it could cause a crash, because it could not find an element in the
localdata.
To reproduce this, start with a non empty field many2many checkbox, then
uncheck them twice.
The reason is that the datapoint of type 'list' that represents the
current value is actually modified, and the replace_with operation
assumed that it could find the localID by looking into its data. I have
the feeling that a more complete fix is to make sure that the datapoint
is not modified (in its 'data' key), but is modified in its '_changes'
key. However, this would probably be a bigger change.
This use case was not correctly managed as the function evaluating if the
value has changed compares datetime and date.
This triggered an issue if the widget `date` was set on the field `date_order`
on a purchase order for example ; it was not possible to create a record as
the datapoint was set `dirty`.
This rev. ensures that a `field_changed` is not triggered if the day is the same
on a date widget.
This rev. also adds support of `datetime` fields on date widget, which appears
to be a valid use case.
Fixes https://github.com/odoo/odoo/issues/20311
Before this commit, the image was directly built without using the corresponding
template, which set some properties on the image (class, width, etc.).
When we generate commands for many2many, it could happen that the id was
in the list of changed fields (when we are in a new records, with a non
empty initial onchange). When this happens, the server is not happy.
To reproduce this, open the uninstall wizard of a module in the 'App'
application. This creates a new record with a non-empty many2many
field. Then click on 'Show technical modules', this will trigger an
onchange which should not send the id field.
When creating a new record with a non empty default value for a one2many
field, we did send the command 0 (for each one2many line) instead of the
command 1 (update). This was a pretty big problem, because then the
server would try to create a line with possibly incomplete data.
For example, it was not possible to create expense sheets: go to expense
menu, select one expense in the list view, click on the action menu
'submit to manager', this opens a form view with a one2many with the
selected expense, then save. Before this commit, this would lead to a
server error.
This commit ensures that tab navigation works properly when editing a
form view that contains input fields with phone widgets. Previously,
pressing TAB would skip those fields.
Before this commit, the widget many2many_tags only loaded the first 40
records. This was difficult to see, because there were not so many views
with a lot of tags.
Before this commit, there was a bug when creating a new record which
contains a one2many with an onchange. The commands sent to the onchange
did not contain the full information.
Here is how you could reproduce the issue: install expense, go to the
expense list view, select the 2 lines, then, in the 'action' menu in the
control panel, choose 'Expense: submit to manager'. In the new expense
sheet, you could see that there were two lines in the one2many
expense_line_ids. However, these lines are empty. The reason is that
the commands sent in the onchange were 2 commands 0, with {} as data.
So, in short, the BasicModel did generate the proper commands, but with
empty data, in the specific case when we have a non empty one2many in a
default_get, then an onchange.
To solve this issue, I had to fix 2 things: the internal data struture
was wrongly updated, and the commands should be generated with all the
data, and not only the changes.
The parsing of the integers does not escape the thousands separator.
issue: Integers are currently not properly parsed. For example, create
a database in Spanish language, install product_expiry and in settings
activate Lots & Serial Numbers. Go to Products and create a new product.
Choose to track by lots and specify the use_time, life_time, etc.
When saving, all integers are set to 0
We clear the input field when clicking 'Create and Edit' to ensure that
the field is empty if the user chooses to cancel the record creation. If
a record is indeed created, then the field value will still be updated.
Problem occured in the MRP Production wizard (with a consumed
product tracked by serial number). The opened record is a new
record, and its one2many has 2 default rows that are prefilled.
If the user edited one of the quantities in those rows, and set
an invalid value instead (e.g. a letter), the row was removed
without notice when it was focused out. This was because the
model wasn't aware that a change has been done on the record,
and as the record was flagged as new, it was simply abandonned.
The one2many default get code used to meddle with basic_model's res_ids
manually, which ended up desynchronizing the data and res_ids fields
of the list representation of the one2many values.
This commit solves the issue by using the proper mechanisms of the
basic_model, that is creating a proper datapoint for those ids.
Steps to reproduce the original issue:
- Install sale_mamangement, mrp and purchase
- Create a product
- In the Inventory tab, in Routes, uncheck Buy
- Check and then uncheck back any other route than Buy
- Get a traceback because we are looking for a record with a given
res_id in the list data but since they are desynchronized, data
is empty at that point even though res_ids is not.
The o_row class mechanism is supposed to be used to put another
element next to a field, like a button for example. However,
in the case of phone and email field, the o_text_overflow hack
gets in the way.
The problem that o_text_overflow is trying to solve is when you
have a long email, the table used to display the form view fields
tends to use very long cells (td) so it can display the whole email
address. This behavior completely breaks the form view, even though
the email address is clearly set to wrap in css. The o_text_overflow
class is a hack that forces the table to think that the email is
small, then defaulting to the width 50% css rules. If the email is
too long, it is correctly wrapped inside the cell, without breaking
the form view layout.
However, when we need to add a button next to those fields, this
hack gets in the way of the o_row class css rules, completely
wrecking it. We looked for a fix with qsm-odoo for hours but were
unable to find one that did not require a complete rewrite of the
form view css rules. In the end, we decided to remove the hack from
the phone field and keep it on the email field, as we think it is
less common to have a very long phone number than to have a very
long email.
The FieldDomain allows to open a list in a dialog to display the
records matching the current domain. In this dialog, clicking on
a record should do nothing (this was the behavior before the new
views).
Before this rev., it actually tried to open the record and it
produced a crash.
The `upgrade_boolean` widget should display an `Enterprise` label next to it.
There is however an issue when a `label` is specified for this field. In this case,
the `Enterprise` label should be placed next to the field label (and not the field
itself).
The easiest solution would have been to instantiate the field widget with its label,
but this can't work because the field widget is instantiated before its label.
We thus re-render the widget after its label creation.
The server postproccess the view to add attributes on many2one fields, i.e.
`can_create` and `can_write`, according to access rights.
These attributes are parsed by the client as string but should also be parsed by
the many2one when evaluating them.
The mock server has also been adapted to set these attributes.
When selecting a partner in the reconciliation widget, using the SelectCreateDialog
(Search More), the partner wasn't correctly displayed.
This was due to a difference between the change format of the many2one ; when using
the dialog, only the `id` is sent (while the `id` and `display_name` are sent when
using the many2one dropdown).
These two behaviours are now standardized and both now send `id` and `display_name`.
It has the advantage of sparing some `name_get`s called by the model because the
`display_name` was missing.
This change implies some refactoring where the SelectCreateDialog was used.
The server response to an `onchange` on a one2many is a list of
commands. In case of one2many update (command 1), the server
returns all fields (and not only the modified ones).
When saving the record, this is the client responsability to
specify which fields really changed.
Before this commit, all fields were sent at `write` and not only
the changed ones. This was causing issues when the user couldn't
write on the field ; an error was triggered even though the user
didn't change anything.
To fix this, we only tag as changed (in the datapoint `_changes`
attribute) the fields that have a different value after the onchange.
In an embedded editable list view with the widget `handle`, resequencing a
record and try to edit it afterwards won't work. The selected record isn't the
correct one, as if the resequencing had no impact.
To make this work, the list datapoint needs to be ordered by the field with the
widget `handle` ; if it isn't, resequencing the records won't change the x2many
data order but the list view expects the same order.
Before this commit, we did not really handle gracefully editing a
one2many with many records. The main issue is that if the user was not
on the last page, creating a new record added it to the last page, which
is confusing and actually a bad idea for records with required fields.
This fix requires a way to track the current changes (because we do not
display the current state of the form view: the new record is displayed
in the first page, but is actually added at the end of the dataset).
Note that this probably does not solve all issues with pagination. This
is a quite delicate problem. We will add tests and fixes as needed.
The problem occurred on form views with an x2many field with a
default value containing CREATE commands, i.e. when opening such
a form view in create mode, the x2many field is pre-filled with
some (non-existing yet) subrecords.
If the user then edited those subrecords, and then clicked on
'Save' to create the main record, an 'UPDATE' command was sent
alongside the 'CREATE' one for the x2many, and the id of the
'UPDATE' command was a virtual id (as the subrecord didn't exist
yet), and it crashed.
This could happen in MRP:
- Enable lots in Iventory
- Choose a product which is a component of another (through mrp bom)
- Set this product as tracked by lot
- Set this product inventory quantity to 0
- Create a new manufacturing order for the product for which it is a
component
- Click on "Check Availability"
- click on Produce: the many2many already contains a subrecord
- edit the lot field of that subrecord
- click on 'Record Production'
Before this rev., when the value of a field with widget='domain'
was false, the search_count RPC made to display the number of
records matching the domain crashed, and the widget displayed that
the domain was invalid. This rev. adds a fallback on [] when the
domain is false.
In grouped list view, when a pager is clicked inside a group, a
'load' event is triggered to reload the data with the new offset.
The event is handled by the list's controller.
However, when the list is in the modal, the event must be stop
propagated by the controller of the modal, so that it doesn't go
up to the controller of the view displayed behind the modal. It
happens for example by reproducing the following steps:
- go to Lunch
- add a line
- search more products
- group by vendor in the modal
- unfold a group
- click on the pager (the group must contain more than 80 records)
Before this rev., it crashed because the controller of the form
view tried to handle the event.
Before this commit, a list containing a handle widget would only display
the handles associated with non-zero integer values. This is due to the
fact that unset fields are hidden in Odoo, while a field is by default
considered 'defined' if its value is truthy.
We override the isSet method for the handle widget to always return
true. This tells the handle widget that the associated integer value is
always defined, thereby ensuring that the handle will always be shown.
The field 'display_name' is automatically added to the list of
fields to fetch when fetching a record. Before this rev., its
value was also sent alongside the fields in the view for onchange
RPCs. It shouldn't: only the fields in the view should be sent.
It produced a weird bug in the res.partner form view: open a
contact (company), add a sub-contact, save -> the display_name of
the contact was then '' (and 'Contacts' was displayed in the
breadcrumbs instead of the display_name of the opened contact).
This is because 'display_name' was sent to the onchange, which
returned '' as value (because of another bug python side). So as
it's value changed, when saving, the new display_name was sent to
the server (even if this field was actually readonly).
Before this rev., we also always fetched 'display_name' when
fetching list of records (read or search_read). This wasn't a bug
per se, but it might be a computed field, and it isn't necessary,
so we stop fetching it in that case if it isn't in the view (as
it was done in saas-15, before the new views).
Before this commit, it could happen that a x2many field was considered
invalid, which prevent triggering onchanges, even though each of its
required fields are properly set. Here is such a scenario:
We have a one2many field something_ids in a form view, with an inline
tree view, which contains a field X. The field X is required on the
model, but marked required="0" in the inline view.
In that case, we incorrectly evaluated the required modifiers: since the
attrs did not contain the required attribute, we fell back on the
field_get value, which is true in this case. But this is not correct,
the fieldviewget already computed the actual modifiers and took into
account the fact that it was overridden. We need to only look at the
modifiers.
The reference widget is also meant to be used on a char field, not only on a
reference field.
The rev. https://github.com/odoo/odoo/commit/a4e30a1b43d0b9acd76508d9e6c466cd1dbdffab removed
the use of `field_utils.format.many2one` and replaced it by `_formatValue` to
support this use case.
The replacement wasn't complete as some occurences was remaining (in reset),
which led to traceback when the widget was reset (impossible to create an
External Identifier anymore).
Also, the reference `_formatValue` wasn't correct as it was displaying the
record `display_name` and not the reference field value ; this only makes sense
for the External Identifier as its display name is the reference field value but
it doesn't make any sense for other reference field.
Last but not least, this commit implements the `onchange` and `default_get` for
the reference field.
With the new views, we stopped using the browser timezone to
display the dates in Odoo, and we used the timezone defined on
the User profile instead. When loading the webclient, the
timezone offset was put into the session and used to display all
dates. This wasn't a good idea.
The given offset was computed for the current time, meaning that
it may be incorrect for specific dates (e.g. with the daylight
saving, the UTC offset of today is not the same as 6 months ago).
Moreover (but less likely), as the offset was stored in the
session, it wasn't recalculated afterwards. So if the offset
actually changed during the session (e.g. from or to daylight
saving time), the displayed dates were incorrect until the user
reloaded the page.
With this rev., we don't retrieve the offset from the server
anymore and we use the browser timezone again (like before the new
views). However, we keep the computation of the offset (on the fly)
in the session, so that it can be mocked in the test environment.
Before this rev., all date fields values were off by one day as
soon as the user timezone was negative.
With the new views, all dates manipulated by the JS are moment
instances in UTC, and when being displayed, they are correctly
formatted in the user locale by applying the timezone offset.
However, this should obviously not be done for date fields as
when no time is specified when creating a moment instance, it
defaults to 00:00:00, so applying a negative offset always leads
to the day before.
Remark:
It happens that datetime fields are displayed as a date (e.g.
purchase order line form view). In that case, the offset is
applied on the displayed date.
Before this commit, it was not possible to copy the contents of a
multiline text field in read-only mode in Firefox without losing all
line breaks in the process. This is due to a bug affecting div tags
(see https://bugzilla.mozilla.org/show_bug.cgi?id=1390115) in Firefox.
This commit ensures that the text in a text field is surrounded by a
span (as opposed to div) tag.
Note that spans were used in saas-15, so we are in fact simply undoing
the change to divs introduced by the new views.