The test changed in this commit had a big issue: it did not properly
destroy the second form view that it created. Because of that, it was
still alive and could interact with other tests, such as the barcode
tests.
This is what happened:
1. var form was assigned to a form view
2. form was destroyed
3. in a try catch, form was reassigned to the result of createView, but
the evaluation of createView crashed (after creating a form view), so the
assignation was not done, and the var form was still pointing to the first
form view
4. form.destroy is called, which did nothing for the second form view.
There is no easy way to get the reference to the form view created by
createView without changing some other code, so we simply remove the
second part of the test, which was not particularly important.
The reference fields haven't been correctly implemented with the new views.
This commit restores their behaviour.
Note that an small improvement has been introduced in this commit.
Before saas-16, a `name_get` was done for each record (so 80 if the field
was displayed in a full list view), which is not optimal. The calls are now
batched by model (so one `name_get` by model appearing in the field values).
In the editable list, when pressing enter, the cursor goes to the next line and
the current row is saved.
This should not be the case for a textarea (i.e. in the FieldText widget). In
this particular case, we just want to continue editing the textarea.
When the value of a many2one field with widget selection was unset,
it's value wasn't actually set to false in the BasicModel. It was
set to a virtual id like 'virtual_294' instead. This caused a
traceback when this value was sent to the server, obviously.
This was for example the case of the Register Payment form view
(accessible from a customer invoice), when the user selected a
payment journal, and then unselected it (the virtual id was then
sent to the onchange RPC).
The MonetaryField can be rendered with or without a currency. The
currency can change during the lifecyle of the widget (e.g.
currency field in the view, whose value is set by an onchange).
More precisely, the widget can be instantiated without currency,
then a change on another field can trigger an onchange which sets
the currency, and our monetary widget is re-rendered with a
currency.
Before this rev., this wasn't supported in edit mode because the
$el's root node was different if there were a currency or not
(it was an input if there were no currency, and a div containing
an input and a span otherwise). This root node being rendered only
once (at the creation of the widget), the widget wasn't re-rendered
correctly if the currency was suddenly set or unset.
This fix makes the widget behave uniformly whether or not there is
a currency, so now it is always a div containing an input (and
optionally a span if there is a currency).
When x2many fields have to be evaluated in domains (e.g. ['id',
'in', some_x2many_field]), they must be evaluated as the list of
ids in the relation (whereas in contexts, they are evaluated as a
list of commands). Before this rev., they were always evaluated as
a list of commands.
Note that this didn't work neither before the new views.
Datapoints of the BasicModel store the data fetched from the
server and changes done (not saved yet) by the user. For x2many
fields, datapoints are of type 'list' and have a 'res_ids' key
containing the ids of all records in the relation. They also have a
'data' key containing datapoints of type 'record', representing the
records in the relation that have been fetched (with a limit set to
40 or 80 depending on the subview used). So basically, the length
of res_ids may be different than the length of data.
Before this rev., the changes in x2manys were saved under the key
'_changes' as a duplication of the content of 'data', i.e. same
list of datapoints of type 'record', modulo the changes done (it
contained the new records added and didn't contained the removed
records, whereas the 'data' remained unchanged). This didn't worked
at all when there were more records than the limit in the relation.
Indeed, when this happened, the commands to send to the server
couldn't be generated correctly.
This rev. modifies the way changes are stored in those datapoints.
Now, each change is stored as an operation. An operation could be
of type 'ADD', 'REMOVE', 'REMOVE_ALL' or 'UPDATE'. As before,
'data' remains unchanged and always keeps the datapoints of records
that have been fetched (the ones that are currently displayed in
the list or kanban subview). This way, commands can now be properly
computed.
This had a nice side-effect of fixing two other bugs linked to
x2manys.
First, when used in a context (e.g. {some_key: some_x2many_field}),
the value of some_x2many_field (i.e. a list of commands) was always
[] if the field was invisible="1" in the view, because it's
subrecords weren't fetched, and the generated commands were based
on them. Now, they are generated from the list of res_ids in the
relation, so it works fine.
Second, the x2many records are supposed to be sorted client-side,
after being read, and after each edition. This was working in the
first case, but not in the second. Now it's working even after
a record edition.
This rev. also correctly sets the 'parentID' and 'static' keys in
datapoints.
Finally, we also fixed the pager in x2manys as it was badly
displayed (small less tweaks).
When the show_address option is enabled, the display_name of a
partner becomes a newline concatenated version of his name and his
address, so the internal value of the widget is this conglomerate.
However, if one uses a keypress that does not change the value of
the input in edit mode, say ESC for example, the keyup handler of
this widget will compare the value in the input with its internal
value. If the internal value is still the conglomerate, which is
the case if the user hasn't changed the input content since
switching in edit mode, we need to trim it to compare it to the
actual input value, which is the "standard" display_name, ignoring
the magic show_address feature which is only used in readonly.
When creating a new record, the numeric field default value should be 0 in order
to avoid manually setting mandatory fields to 0.
This behaviour has been removed with the new views but one wants to restore it.
The `mockRead` function has also been adapted in this commit because the server
returns 0 for unset numeric fields.
Before this fix, multiple records were created if you clicked
on the quick create of a many2one in a modal.
The 'mutexify' event wasn't stopped and was thus handled by
the dialog's form controller and by the main form controller.
Field widgets must format and parse values according to the widget
type, not to the field type. For example, if a monetary field is
inserted in a form view with widget="float", the field's value must
be formatted with the float formatter, not the montetary one. This
wasn't the case before this rev.
A problem occured with a required Many2One inside an X2Many (e.g.
order_line field in sale.order form view). If the user clicked on
'Add an item' to add a row, then typed some text in the Many2One,
then clicked on 'Create ...' to quick create a new product, and
then directly clicked on 'Add an item' to add another row (before
the name_create returned), the first row was discarded.
This was because before the name_create returns, the Many2one still
has no value, and as it is required, the row was considered invalid
and then discarded.
This rev. ensures to wait for the name_create to return before
trying to save the row and to create a new one.
The problem occurred in form views with a many2one field, when the
user reproduced the following steps:
- type some text in the many2one
- click on 'Create ...' in the many2one dropdown to quick create
(name_create RPC) the record
- before the name_create returns, click on 'Save'.
When this happened, the record was saved before the many2one was
updated with its new value. So the record was incorrectly saved,
and the view was again dirty as soon as the name_create returned,
even if it was in 'readonly'. So after saving, a 'Changes will be
discarded' dialog opened when the user tried to leave the form.
We fix this issue by making the controller wait for the name_create
to return, and for the many2one to update its value, before saving.
This issue could also occur in list views.
The 'Add an item' button of one2many editable lists is deactivated
once clicked (since rev. f9a1241) to prevent concurrent record
creation. It is re-activated when the record has been created.
Unfortunately, it might happen that the record is actually not
created, so in this case the button is never re-activated.
This happens for example on the customer invoice form view, if
the user tries to add a line before selecting a partner.
This function should return a deferred, as indicated in the
docstring. However, in some cases, it doesn't. It then crashed
as the FieldX2Many tries to call done() on undefined.
This happens for instance on the customer invoice form view:
create a new record, select a partner, add a line, select a
product, directly click on the invoice date field (this will
trigger an onchange which will totally override the value of the
one2many). The FielX2Many calls setRowMode to switch the edited
row back to readonly, but this row doesn't exist anymore as the
one2many value has been overriden by the onchange, so setRowMode
wrongly returns nothing, and it crashes.
Previously, when an update operation was performed, the list of
ids in the x2many field was emptied beforehand.
It kinda worked in lots of cases where one would expect it to
fail because in thoses cases the widgets send link_to commands
along with the update one, thus re-adding the removed line.
Now the behavior is more consistent accross the board. We start
with the current list of ids and apply the requested changes.
When the user writes something in a Many2One input, after a short
delay, a name_search RPC is done with the value written in the
input, and results are displayed in a dropdown. Then, if the user
clicks on the input, the dropdown closes. If he clicks again, it
should do again a name_search RPC with the value of the input.
Before this rev., it was doing it with the empty string.
Clicking multiple times on 'Create' (in main list views) or 'Add an
item' (in o2m lists) might create invalid rows (i.e. rows with
required fields unset).
Creating a new record requires two sequential RPCs: a default_get
and an onchange (only if there is an onchange of one of the
record's field). When clicking twice to create a new record, that
sequence of RPCs is done twice in parallel, after unselecting the
current row (i.e. saving it if it is valid). However, in both
cases, there is no line to unselect yet (as both operations are
done basically simultaneously). When the first onchange returns,
a first new record is added to the list. When the second returns,
the second one is added and takes the edition, leaving the first
one, invalid, in the list.
This rev. ensures this doesn't happen by preventing concurrent
record creation.
... before saving a line.
Suppose that we have an editable list with one required field
(e.g. many2one) with an onchange. We add a record and select a value
for the many2one. Before the onchange returns, we click again on
'Add an item'. If we don't wait for the onchange to return before
unselecting the row (i.e. before saving it), a confirm dialog asking
if the changes can be discarded opens, because the line isn't valid
yet. This could easily be reproduced on the sale_order form view,
with some throttling.
This rev. ensures that we wait for the onchange to return before
saving the line, and thus before creating a new line.
In a slightly too naive way, we apparently tried to improve the
parse_float method while rewriting the new views. This was done by
using a regexp to match all occurences of a thousand separator.
The way the regexp was built was fine, until the thousand separator is a
'.', which, if used as a regexp, matches all characters. Also, sadly,
'.' is not so rare as a thousand separator...
Suppose that there is a many2many list (non editable) in a form
view. When one of the many2many subrecords is edited (from the
form view in a dialog), it is saved directly when the user clicks
on 'Save' in the dialog.
Before this rev., if the user clicked on 'Save' in the main form
view after editing a m2m subrecord, a 'write' RPC was done, whereas
there were nothing left to save (as the subrecord had already been
saved).
Suppose that we have a form view with a one2many list (non
editable) containing a record. In edit mode, the user clicks on
that record to edit it in a form view (in a dialog). In this form
view, there is an x2many field that isn't in the list. Before this
rev., it crashed when the user clicked on 'Save' in the dialog
after editing the value of the x2many.
This was because the basic model didn't use the correct fieldsInfo
in that case (it use the fieldsInfo of the list view instead of the
one of the form view, and the x2many field isn't in the list).
Also ensures that the record is correctly saved even if editing the
x2many is the only change done, as this wasn't the case before.
Before doing an onchange in a x2many, the basic model tries to evaluate
if the line is valid or not. In that computation, we used the
field.required property instead of using the required modifier. This
commit had to move the modifier handling from the BasicRenderer to the
BasicModel, in order to use it in the _isX2ManyValid method.
Before this commit, it was only possible to click on input to
change the value of the many2many_checkboxes widget.
For better consistency, we must allow to click on labels.
The motivation for this commit was to fix a wrong evaluation context for
x2m fields: it was using a list of res_id, which may contain virtual
ids.
For example, it could be seen in the form view for journal entries:
adding the first line (field line_ids) was fine, but clicking on 'Add an
item' a second time caused a crash. The reason was that the line_ids
was explicitely added to the context in the view, and the second time we
add a line, the web client sent a list looking like this [35,
'virtual_43'] to the server, in the nameget context.
The proper behaviour is to send a list of commands for those fields.
This means that we had to do some non trivial changes in the
getEvalContext method: we stop using the get method with raw: true. It
was nice to use the same method, but the eval context is actually
different that the values returned by the get method. That was the
motivation to introduce the 'noUnsetNumeric' flag, but now, we simply
create another method and let each method do its own work.
TLDR: eval contexts are hard...
It is not really easy to handle discard operations. We need to handle
the discard in editable lists, and in x2manys. This is not exactly the
same code, but most of this was done by moving the discard method from
the formcontroller to the basic controller.
With this commit, the user can now discard the changes for the current
record by pressing ESC. If the record is new, it will remove it, if
not, it will ask for confirmation before.
The issue that we fix in this commit is the following: open a form view
with a o2m with one required field, and the o2m is editable. When
adding a new line, it is invalid (since the field with required), but
when pressing ENTER, the editable list tries to navigate away, which
will discard the current line (in the unselectRow method), before
completing the move_to_next_line. So, at this point, the list editable
create a new (empty) record, and we are back to square one, but with a
visible flicker and no information on what happened.
To fix this issue, we simply check that the renderer can be saved when
moving to next line. If it is not (the line is invalid), then we simply
abort the move.
Notes:
* this behaviour worked fine in list view, because the canBeSaved check
is made by the saveRecord method in listController
* it looks like the protection should be put into unselectRow, but this is
not true (at least, in the current state): in some cases, calling
unselectRow with invalid state should result in discarding the current row
(in a new line in a o2m, when the record is not dirty, for example), and
the list editable renderer does not know if this is the case or not.
However, if the unselect action comes from a navigation event, then we want
to avoid discarding the current row.
When a many2one (in an editable list) with an onchange was edited, it
could happen that a modal window asking for confirmation of the creation
of a record was opened when it should not. Here is what actually could
happen:
* user focus a many2one
* user type something
* user press ENTER (keydown + keyup)
* user press ENTER again
* the onchange comes back
* modal window is open
Because of the onchange, the _reset method was called after the
_onInputKeyup method. So, the m2o_value was not equal to the
$input.val() and the 'floating' flag was set to true (after the first ENTER).
The second ENTER caused a unselectRow in the editable list, so the input
was focusouted, and the modal is set. This is basically caused by the
asynchronous behaviour of the onchange.
We simply fix the problem by setting the floating flag back to false in
the keydown method.
When a many2one (in an editable list) with an onchange was edited, it
could happen that a modal window asking for confirmation of the creation
of a record was opened when it should not. Here is what actually could
happen:
* user focus a many2one
* user type something
* user press ENTER (keydown + keyup)
* the onchange comes back
* user press ENTER again
* modal window is open
Because of the onchange, the _reset method was called after the
_onInputKeyup method. So, the m2o_value was not equal to the $input.val() and
the 'floating' flag was set to true, which means that the _onInputFocusout
method (called indirectly by the listeditablerenderer when unselecting the row)
was forced to open a confirm dialog.
We simply fix the problem by setting the floating flag back to false in the
reset method.
Before this commit, when the user write some text in a many2one, then
press ENTER, the key event will trigger a navigation move, which will
validate the field, and move the focus to the next line. The same issue
happens when pressing TAB.
With this commit, we simply stop the propagation, and preventdefault the
event that cause the selection.
Before this commit, when editing a many2one (with an onchange) by
clicking on the small form button on the right, then changing something,
then saving the modal dialog, no onchange was triggered, because,
technically, the value of the many2one was not changed, only the values
from the record pointed by the many2one were.
The same remarks applies to many2manys.
To see this issue in odoo, you can open a product (technically, a
product.template) from the 'Catalog' menu in sales, then edit it, then
click on the small form view icon next to Internal Category. This is a
many2one (categ_id) with an onchange. In the form view dialog, change
something, for example costing method, then save. The res_id of the
internal category was not changed, but an onchange should still be
triggered.
When a SelectCreateDialog was opened with a given domain, this
domain was completely lost if the user wrote a new domain (or
groupby) in the searchview, whereas they should be concatenated.
Suppose that there is a one2many list in a form view containing
a field F, displayed with no widget, and that F is also displayed
in the one2many form view (opened when clicking on a record of the
list), but that in this form view, F is displayed with a widget
requiring specialData (additional data to fetch alongside the
record), e.g. widget="domain".
Before this rev., it crashed when this happened because those
specialData weren't loaded when opening the related record.
Credit goes to @pga-odoo for reporting the bug and reproducing it
in a test.
DebounceFields debounce the function that notify field changes to
their environment (using _.debounce). It may happen that this
function is called once the field widget is destroyed (e.g.
type something in a char field and quickly click on Save, the
function is called directly by _commitChanges, but the debounced
call is still pending). It may be a problem if the subsequent
code does something like this.getSession().some_key, as if the
widget is destroyed, it has no parent that can provide it the
session, and thus it crashes.
Also rename attribute _debouncedStarted into _isDirty as it makes
much more sense.
Passing multiple ids wasn't correctly handled as the argument `record_id`
was wrapped in a list of list, which is not hashable.
The wrapping is now handled by the caller.
Before this commit, when creating a new record, we only looked at the
main fields to determine if a call to onchange was needed. This was not
enough, since sub fields (fields in a one2many for example) can have an
onchange.
This was an issue only for new records with onchanges in sub fields, but
no onchange on main fields. For example, the merge contact wizard.
To solve this issue, we had to propagate the current view type from the
field to the basic model, because otherwise, the main viewtype was used
to compute the onchangeSpec, and that is not always correct.
Before this rev., the FieldRadio didn't work on selection fields
with keys like "0", "1"... i.e. strings that can be converted to
numerical values. This was due to an automatic conversion of those
keys to their numerical value by JQuery, so when the user changed
the value, an unknow key was sent to the server (e.g. 1 instead of
"1"), causing a crash.
This rev. changes the logic a bit so that the keys are no more
written in the DOM, but their index in the selection are.
The server doesn't make a distinction between an unset selection
field, and a value 0 (integer). So when 0 is a valid key for the
field, the server returns false as value if the field is unset, or
if its value is 0, which is not really convenient.
As a consequence, when the value is 0, it appears as unset in the
interface. To circumvent this, we automatically converts false
value of selection fields to 0, if 0 is a key of the selection.
Many2many default value can be a list of ids, and in that case, we did
not properly load data.
It could be seen by selecting a few contacts (in list view), then
opening the 'Merge contact' wizard in the sidebar (action menu). Before
this commit, the wizard did show a list with some empty rows (for the
field partner_ids).
In the xml definition of views, a 'currency_field' can be specified
in the node options:
<field name="amount" widget='monetary'
options="{'currency_field': 'currency_id'}"/>
It indicates which field the monetary widget has to use to
determine the currency to display.
Before this rev., the widget didn't look for the 'currency_field'
in the options, but directly in the node attrs, so this simply
didn't work. This was for example the case on the crm.lead kanban
and form views.
This rev. removes some html injections issues:
- in char and text field widgets
- in phone field widget in mobile
- in list views (when no widget is instantiated)
Previously, the string attribute of the field node was ignored
in the title of the form dialog and it always displayed the
string value of the python field definition instead, which is
a default value instead.
Additionally, I took this opportunity to add tests for other
similar dialog titles which actually worked already with the
string attribute of the field node but were not tested yet.
Previously, when a checkbox was re-rendered, it was losing focus
since it was removed and re-created. Now, if the checkbox itself
is the origin point of the re-rendering, we give the focus to the
newly re-rendered checkbox, resulting in a seamless experience
just as if the checkbox was never removed and re-rendered.