Commit Graph
198 Commits
Author SHA1 Message Date
Aaron Bohy ffcd0bd156 [REF] web: remove expand parameters from web_read_group
The expand, expand_limit and expand_groupby allowed to ask
read_group to populate groups with search_read results directly.
It was only used in the list view, when `expand="1"` was set on
its root node in the arch. Since [1], the list view no longer uses
these arguments, as we uniformized the logic between list and
kanban (where groups are always opened by default).

[1] https://github.com/odoo/odoo/pull/114024

Part of task~3179751

closes odoo/odoo#129881

Signed-off-by: Mathieu Duckerts-Antoine (dam) <dam@odoo.com>
2023-07-27 16:14:21 +02:00
Raphael Collet 7bfbd75d57 [FIX] core: web_read() on new records
This completes 96a98f4f4c in the case
where a many2one field has a new record as value, even when that
many2one field is not a delegate field.

Part-of: odoo/odoo#114024
2023-07-24 20:17:49 +02:00
william-andre 0eff29409d [IMP] web: manage hierarchy in company selector
In order to manage different branches, the usability of the company
selector is being improved
* display in a hierarchic manner
* when selecting a company, select all the available children with it

task-3371677

Part-of: odoo/odoo#125642
2023-07-20 11:49:05 +02:00
Raphael Collet 96a98f4f4c [FIX] web: web_read() on new records with _inherits
This commit partly reverts 59afbf6686 and
provides an alternative solution.  In this solution, records.web_read()
returns a list of dicts where the key 'id' corresponds to each record.id
in records, which enables to reliably relate each record to its values.

This also fixes the implementation of web_read() on new records where
the fields contain a reference field with some context.

closes odoo/odoo#128878

Signed-off-by: Raphael Collet <rco@odoo.com>
2023-07-18 19:28:13 +02:00
Raphael Collet 59afbf6686 [FIX] web: web_read() on new records with _inherits
Part-of: odoo/odoo#127718
2023-07-10 18:16:19 +02:00
Raphael Collet b3e8560c9c [FIX] tests: make first call to onchange2() return complete x2many value
The use-case is a first call to onchange2() where:
 - a one2many field has a default value with a new line
 - some onchange method discards that line

The diff should not return a "delete" command for the discarded line,
since the client does not know about it.  Instead, for that first call,
it should behave like if the initial value of the field was empty.

Part-of: odoo/odoo#127718
2023-07-10 18:16:18 +02:00
Raphael Collet 92e5c7b160 [FIX] web: make onchange2() use new() with initial values
This enables an override of new() to do what is needed to make onchanges
on account.payment work as expected.

Part-of: odoo/odoo#127400
2023-07-05 18:46:50 +02:00
Florian Charlier 77f9ff50db [REF] *: use onboarding module
* = account{_payment}, base, onboarding, payment{_stripe},
sale{_management}, web, website_sale

Use the dedicated onboarding module introduced in 16.0 instead of
the res.company model to store onboarding progress.

It allows
 * onboarding steps to be reused across panels
 * to support steps that should be completed per-database or per-company
 * to clean the res.company model from many fields and methods,
 * to remove many views, controllers, actions

Module-specific notes:
* account: We also clean the remaining two steps that are not
part of an accounting panel but make the most sense to be kept here.
* account_payment: Following 8e4e8eb8, the payment provider step is
added to the invoicing onboarding panel. We apply this change here too.
Also impacts the website_sale_dashboard panel (see related ENT PR).
(The "sale tax" one is currently used for to the website sale dashboard).
* payment: Note that the step was already not part of an onboarding
panel within this module.
* website_sale: We clean
  * a field not used (The website_sale dashboard onboarding panel used
  the payment_provider_onboarding_state field).
  * a method that was only called from website_sale_dashboard, so it is
  moved there. See related ENT PR.

Includes a few tests.

Moving views/templates/styling, as well as cleaning residual onboarding-related fields and methods in base, including populate.

This also includes restoring the "onboarding_complete" overlay panel
animating it to disappear after a few seconds so that it doesn't hide
text and block buttons to re-open steps.

Task-3025136

Part-of: odoo/odoo#104223
2023-06-30 23:37:50 +02:00
Pierre Masereel a746294b46 [FIX] model: correctly count groups
When you are sorting agregates of groups and you have more groups than
the limit, you get a traceback.

It is because you give the orderby to the private _read_group that is
not managed the same way when you pass it to the private _web_read_group
so it doesn't correctly build the query.

As we are calling _read_group to count the groups, we don't need to
order it. So we don't.

closes odoo/odoo#126862

X-original-commit: 6fdf86823088e0e413bef72dc0f20f926aa8f10a
Signed-off-by: Rémy Voet (ryv) <ryv@odoo.com>
Signed-off-by: Pierre Masereel (pim) <pim@odoo.com>
2023-06-30 01:33:00 +02:00
Julien Carion (juca) 2a37a3d06f [REF] web, base, mail: move res.users.settings from mail to base
This commit moves and adapt the res.users.settings model from mail to base and
and allows to access and modify its content with web's user service.
This allows to access user settings without needing the mail module.

closes odoo/odoo#116005

Related: odoo/enterprise#38360
Signed-off-by: Michaël Mattiello (mcm) <mcm@odoo.com>
2023-06-22 15:40:43 +02:00
Raphael Collet d30b5c0392 [FIX] web: onchange2() format of values in LINK commands
The LINK command must include the corresponding record data in the
web_read() format instead of the diff() format.  The real difference
shows up in x2many fields: web_read() returns them as lists of dicts (or
list of ids), while diff() returns them as lists of commands.

Part-of: odoo/odoo#124612
2023-06-13 12:49:22 +02:00
Raphael Collet 67a402edb5 [FIX] web: onchange2() copy of sub-x2many fields in cache
onchange2() reads the origin record from database, and copies its field
values in cache on the corresponding new records.  The copied x2many
fields must use NewId instead of their real id.

Part-of: odoo/odoo#124612
2023-06-13 12:49:21 +02:00
Raphael Collet 243a2e8d03 [FIX] web: make onchange2() apply x2many commands on existing record values
When doing an onchange on some existing record, the x2many commands must
be applied on the current x2many value of the record.  When doing an
onchange on a new record, the x2many commands must be applied on empty
x2many values.

Part-of: odoo/odoo#124612
2023-06-13 12:49:20 +02:00
Raphael Collet a980d58e48 [FIX] web: onchange2() did not return default value for x2many field
Make the parameter 'force' in method RecordSnapshot.diff() effective in
the case of x2many fields.

Part-of: odoo/odoo#124612
2023-06-13 12:49:19 +02:00
Maximilien (malb) 96f22bd8f0 [FIX] web: company details
In Odoo editor when a html field is empty, we add en empty <br> inside the <p>.
This change broke all if statement checking that company details is empty.

In this commit, we add a function is_empty_company_details that return True if
the company details field contains only a <br> and False otherwise. With this
method, we can check that company details is empty and displaying other thing
that just an empty line break.

closes odoo/odoo#124440

Task-id: 3168705
X-original-commit: 2aaca9afb6c54be3ef87ea38646af90a2599aa6b
Signed-off-by: John Laterre (jol) <jol@odoo.com>
Signed-off-by: Maximilien La Barre (malb) <malb@odoo.com>
2023-06-09 13:50:08 +02:00
Pierre Masereel b5854af2b6 [FIX] base,web: get correct mimetype for menu item icon
Since changes made in https://github.com/odoo/enterprise/pull/41117 we
cannot provide icons for menus in an other format than png. As it was
allowed before to use SVG, we don't want to restrict to only one format.

Before, it was trying to gess the mimetype based on the image content
which is not also the best case.

So as the icon is a binary field store=True, we can just take the
mimetype from the attachment.

closes odoo/odoo#122580

X-original-commit: 044e6b680bc988708a9bbcc3c93dabf787c4a190
Related: odoo/enterprise#41529
Signed-off-by: Masereel Pierre <pim@odoo.com>
2023-05-26 10:29:10 +02:00
Xavier Bol (xbo) 1c2ce8c213 [IMP] web: display the current user first in the result of name_search
Before this commit, when the user wants to self assign to a task for
instance, he has to write his name to be able to select himself.

This commit displays the current user first in the result of
`name_search` (the method called by relational widgets in JS).
If the current user does not satisfy the search condition then
he will not be in the result of the `name_search`.

task-3291745

closes odoo/odoo#121146

Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
2023-05-15 09:37:56 +02:00
Raphael Collet b4702cc208 [FIX] web: RecordSnapshot uses variable with incorrect value
Part-of: odoo/odoo#120457
2023-05-05 18:08:09 +02:00
Vincent Schippefilt 7d276aa941 [IMP] web: add reference fields to web_read
add support for fields of type `reference` and `many2one_reference` to `web_read` and `unity_web_search_read`

for both you can add a field_spec requesting fields of the "co-model":

request:
```python
{
    #reference
    'field_reference':
        {
            'fields': {'write_date': {}},
        }

    #many2one_reference
    'm2o_reference_id':
        {
            'fields': {'display_name': {}, 'write_date': {}},
        },
    'm2o_reference_model': {}
}
```
response:
```python
{
    'id': ...,
    #reference
    'field_reference': {
        'id': {'id': 3, 'model': 'comodel_name'},
        'write_date': '2004-11-23 11:30'
    }

    #many2one_reference
    'm2o_reference_id': {
        'id': 3,
        'display_name': "special first day",
        'write_date': '2004-11-23 11:30'
    },
    'm2o_reference_model': 'comodel_name',
}
```

closes odoo/odoo#119995

Task-id: 3284222
Signed-off-by: Raphael Collet <rco@odoo.com>
2023-05-04 00:46:53 +02:00
f5e6494da3 [IMP] web: introduce onchange2
The purpose of onchange2() is to adress two shortcomings of onchange():
 - reduce the payload of the RPC call by minimizing the diff
 - use the "unity" format for returning the data

Because of the dependency of onchange2() on web_read(), the new method
has been introduced in module web.

closes odoo/odoo#119510

Signed-off-by: Raphael Collet <rco@odoo.com>
Co-authored-by: Julien Castiaux <juc@odoo.com>
Co-authored-by: VincentSchippefilt <VincentSchippefilt@users.noreply.github.com>
2023-04-28 14:44:00 +02:00
Rémy Voet (ryv) 70fd18ef67 [FIX] web: fix bad groupby as str instead of list
A mistake introduced in  234db70d86, the
`groupby` of  `_read_group` should be list/tuple of  `str`, not a `str`.

closes odoo/odoo#119400

Signed-off-by: Raphael Collet <rco@odoo.com>
2023-04-22 06:53:49 +02:00
Vincent Schippefilt 7d2baaa0c7 [IMP] web: project unity_read
Introduce an optimised way of reading a graph of data from the webclient.

Before this commit:
When reading data from the webclient it could at most read multiple ids of the same model in one RPC.
This mean that when reading x2many or specific information on many2one, that could only be done after the initial read (when the client knows the ids of the comodels) and model by model.

After this commit:
Introduce methods web_read and web_search_read_unity. Both method receive a specificiation for the fields instead of a list of fields. The specification can request fields from the model, as well as follow relations and request fields for each relations, recursively.

Example of web_read specification for account_move
```python
{'name': {}},
{'date': {}},
{'journal_id': {'fields': {'display_name':{}}}
},
...
{'invoice_line_ids' :
    {
        'fields': {
            'journal_id' : {'fields': {'display_name:{}}},
            'move_name' : {},
            ...
            'tax_ids' : {
                fields: {
                    'display_name':{},
                    ...
                }
            }
        }
    }
}
```

Result for this example with 2 invoice lines
```python
{
    'id': 1234,
    'name' : 'invoice name ABC',
    'journal_id: {
        'id': 999,
        'display_name': 'Customer Invoices'
    },
    ...
    'invoice_line_ids': [
        {
            'id': 666,
            'journal_id': {
                'id': 999,
                'display_name': 'Customer Invoices'
            },
            'move_name': 'a move name',
            'tax_ids': [
                {
                    'id': 333,
                    'display_name: "15% tax",
                    ...
                }
            ]
        },
        {
            'id': 667,
            'journal_id': {
                'id': 999,
                'display_name': 'Customer Invoices'
            },
            'move_name': 'another move name',
            'tax_ids': [
                {
                    'id': 334,
                    'display_name: "21% customer tax",
                    ...
                }
            ]
        }
    ]

}
```

closes odoo/odoo#119034

Signed-off-by: Vincent Schippefilt (vsc) <vsc@odoo.com>
2023-04-21 08:02:17 +02:00
Rémy Voet (ryv) 6d930c7b9d [REF] mail: replace override of read_progress_bar _read_group_groupby
closes odoo/odoo#110737

Related: odoo/documentation#4064
Related: odoo/enterprise#38639
Signed-off-by: Vincent Schippefilt (vsc) <vsc@odoo.com>
2023-04-19 21:58:28 +02:00
Rémy Voet (ryv) 234db70d86 [IMP] *: Use the new API of _read_group for backend use
Part-of: odoo/odoo#110737
2023-04-19 21:58:27 +02:00
Raphael Collet 6ef3772847 [IMP] *: optimize code with search_fetch() and fetch()
closes odoo/odoo#112126

Related: odoo/enterprise#36782
Signed-off-by: Raphael Collet <rco@odoo.com>
2023-03-05 15:12:57 +01:00
Yolann Sabaux 3a177c448d [FIX] models: use localized first day of the week when grouping by week
Steps to reproduce:
- Make sure language preference is 'en_US'
- In accounting, in the dashboard click on bills
- Filter 'due_date' by week

Issue: The start day is Monday and should be, for 'en_US', Sunday as it
is the case in the dashboard view in accounting (see appendix).

Cause: The query uses the `date_trunc('week', date)` which in Postgres
retrieves the first day of the week as Monday (ISO week).

Solution: Create an offset in the query depending on the first day of
the locale variable.

Note: the `web/tests/test_read_progress_bar.py` has been modified: since
the default language is 'en_US' there will be an offset of one day.  To
make it less confusing, I used only two anglo-saxons countries so the
day offset is not the variable tested.  (for this matter, pleaser refer
to `test_read_group/tests/test_read_group_process_groupby.py`)

Appendix:
Language (english-US)

		VIEW (per week)			|		DASHBOARD
	___________________________________________________________________
	W23		->	06/05		|	05/29	->	06/04
	W24	06/06	->	06/12		|	06/05 	->	06/11
	W25	06/13	->			|	06/12	->	06/18

		(Monday - Sunday)			(Sunday - Saturday)

Language (french-BE)

		VIEW (per week)			|		DASHBOARD
	___________________________________________________________________
	W22		->	06/05		|	05/30	->	06/05
	W23	06/06	->	06/12		|	06/06 	->	06/12
	W24	06/13	->			|	06/13	->	06/19

		(Monday - Sunday)			(Monday - Sunday)

opw-2747066

closes odoo/odoo#93053

Related: odoo/enterprise#29539
Signed-off-by: Raphael Collet <rco@odoo.com>
2023-02-24 10:05:58 +01:00
Laurent Smet d724f69a1d [FIX] web: Fix useless search_count with count_limit
When count_limit is set and lower than the current number of fetched records, making an extra search_count is useless.

closes odoo/odoo#111895

X-original-commit: 6f90d6924e24e700694111732ee85465f802b22f
Signed-off-by: Rémy Voet <ryv@odoo.com>
2023-02-03 17:41:15 +01:00
Julien Castiaux bf4c5015ce [IMP] core: make some cookies expire on logout
Some cookies were left around even when the user logged out

The next user should log into the default company instead of the company
of the last user.

task-3077421

closes odoo/odoo#108998

Related: odoo/enterprise#35685
Signed-off-by: Julien Castiaux <juc@odoo.com>
2023-01-13 16:27:35 +01:00
Julien Castiaux 5502313853 [FIX] web, *: multi-db /web/session/authenticate
*: base_setup, hr_timesheet, mail, partner_autocomplete, web_tour

Start odoo without -d and with a --dbfilter that allows multiple
databases. Via JSON-RPC access the /web/session/authenticate route
providing a non-filtered database and valid credentials. Traceback,
`request.env` is None.

Since httpocalypse the initialization of the ORM (cursor, registry,
environment) is greedy. It means that the connection to the database is
established very early during the request routing or skip altogether in
case no dbname was known at that time. This contrast with prepocalypse
where the various ORM thingies were lazily setup the first time they
were accessed.

This changement has an important implication regarding authentication.

In prepocalypse, thanks to the lazy approache, a cursor/registry/env
would be setup on the database you just login upon using the
`request.env` for the first time. This was very nice in this regard but
had other problems.

Since httpocalypse such operation is no more possible. Devs must
initialize and use their own cursor/registry/env in case they
authenticate on another database than the one `request.cr` is (maybe)
connected to.

The `/web/session/authenticate` controller is an example of such case.
It crates its own cr/registry/environment after authentication. The
problem the controller uses `ir.http.session_info` and that not all
overrides were updated to use `self.env` (=the env created in the web
controller) instead of `request.env` (=the missing env of the request).

closes odoo/odoo#108063

X-original-commit: 7b9bd9d37731fae724dc5d91da656dab70aa9ad4
Related: odoo/enterprise#35012
Signed-off-by: Julien Castiaux <juc@odoo.com>
2022-12-15 15:45:07 +01:00
Vincent Schippefilt 25c6c15a06 [IMP] base,*: remove __last_update from all models
The main goal of this commit is to reduce the size of the registry by
removing the (almost) useless __last_update field.

Statistics # of fields with all modules installed:
before 30184 fields, 1299x last_update (4.30%)

Before this commit, the computed field __last_update was added on every model.
The idea behind this field was to have a computed field that had either
the write_date or the create_date if the write_date was empty. However,
the write_date is always written, even on creation, making it useless
to have the computed field __last_update

After this update, we completely remove from BaseModel:
* __last_update
* CONCURRENCY_CHECK_FIELD that was always defined as "__last_update"
* _compute_concurrency_field that was the compute function for __last_update

closes odoo/odoo#105739

Task-id: 3062140 (part of 3062137 improve registry load time)
Related: odoo/upgrade#4038
Related: odoo/enterprise#33939
Signed-off-by: Raphael Collet <rco@odoo.com>
2022-12-07 18:29:01 +01:00
Florian Vranckx 40a78e1302 [REF] web, base: change alias of Markup
Simple refactor in order to make changes to these line trigger the CI/Security

closes odoo/odoo#107032

X-original-commit: 3c215581aa1f8f74f2d9daf1e15f30ee750ec3df
Signed-off-by: Martin Trigaux (mat) <mat@odoo.com>
2022-12-01 18:31:15 +01:00
fdardenne 4edbe54456 [REM] web: qweb views
QWeb views allowed to render QWeb templates rendered by the server in
a view.

Today, QWeb views are only used in website to see the hierarchy of
their views. The use case of website has now more sense in a client
action rather than an extension of a QWeb view.

This commit removes this view because it is not used anymore and will
probably not find any useful use case in the future.

closes odoo/odoo#103477

Related: odoo/upgrade#4016
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
2022-11-15 18:25:12 +01:00
Sergey Shebanin bc0cd0bb56 [FIX] web: translations hash don't consider all loaded modules
Only server wide modules are being taken into account when calculating translations hash.
So user probably can't get translations of new installed modules unless it is forced by hard page reload (Ctrl+Shift+R).

Problem exists since https://github.com/odoo/odoo/commit/80d74e7ee0eab83dc5100e0776df09d04b882fec and the cause in that `mods = odoo.conf.server_wide_modules or []` string was unpaired with the following `if` statement during refactoring.

This commit restores computation of hash based on all loaded modules.

closes odoo/odoo#105466

X-original-commit: 859cd0463aec4533df30e4839367832fef0685fd
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2022-11-09 17:02:54 +01:00
qsm-odoo d80b8cbc28 [IMP] web, website: reserve some space for the navbar logo
Setting width *and* height attributes allows to reserve some space to
avoid layout shift during page loading. Of course, CSS rules set the
height the user chose, while the width is set to 'auto'. But while the
image is loading, it is best to already reserve some width to reduce
layout shift (like making the menu move or even re-render itself into a
"+" menu).

The chosen values for the space reservation are the ones of the default
logo and theme, but it does not really matter as long as they are
coherent. While the image is being loaded, the chosen user height is
still applied and the 'auto' width rule induces a width that respects
the aspect ratio set by the width and height attributes. That could be a
problem if the real logo has a larger height than width, in which case
the layout shift would be increased because of the arbitrary values set
as width and height, but in most cases, this should reduce it.

This also allows to gain some page speed scoring.

Examples:

=# Logo 200 x 100, height set to 50

Before this commit
- while loading: width = 0, height = 50
- once loaded: width = 200 / 100 * 50 = 100, height = 50
-> layout shift of 100 - 0 = 100

After this commit
- while loading: width = 95 / 40 * 50 = 118.75, height = 50
- once loaded: width = 200 / 100 * 50 = 100, height = 50
-> layout shift of 100 - 118.75 = -18.75

=> Shift of 18.75px to the left, way better than shift of 100px to the
   right

=# Logo 100 x 100, height set to 50

Before this commit
- while loading: width = 0, height = 50
- once loaded: width = 100 / 100 * 50 = 50, height = 50
-> layout shift of 50 - 0 = 50

After this commit
- while loading: width = 95 / 40 * 50 = 118.75, height = 50
- once loaded: width = 100 / 100 * 50 = 50, height = 50
-> layout shift of 50 - 118.75 = -68.75

=> Shift of 68.75px to the left, kinda the same as a shift of 50px to
   the right.

=# Logo 100 x 200, height set to 50

Before this commit
- while loading: width = 0, height = 50
- once loaded: width = 100 / 200 * 50 = 25, height = 50
-> layout shift of 25 - 0 = 25

After this commit
- while loading: width = 95 / 40 * 50 = 118.75, height = 50
- once loaded: width = 100 / 200 * 50 = 25, height = 50
-> layout shift of 25 - 118.75 = -93.75

=> Shift of 93.75px to the left, worse than shift of 25px to the right
   but the case of having a 'portrait' logo is considered less common
   than having a 'landscape' logo. Ideally, we should choose the
   arbitrary values related to the most common aspect ratio.

closes odoo/odoo#101149

X-original-commit: 73786c5c45202914e02325c19a2afaa47d31c341
Signed-off-by: Romain Derie (rde) <rde@odoo.com>
2022-09-26 16:55:20 +02:00
John Laterre (jol) 78789cfc28 [FIX] account,base,web: fix "Send & Print" on invoices
The purpose of the task is twofold:

1. Remove empty lines in the company address.

Until now, the address format was fixed, which could
lead to empty lines if one or more field(s) were missing.
We are now removing empty fields to avoid that.

2. Make sure the external report layout is configured
before generating the PDF.

This will ensure that the company data will appear
in the file. If no layout is defined,
it would not be shown.

task-2834517

closes odoo/odoo#100936

X-original-commit: f36bb6acdacaaba26afdd8f62c48fd2c8784d1e1
Signed-off-by: Olivier Colson (oco) <oco@odoo.com>
Signed-off-by: John Laterre (jol) <jol@odoo.com>
2022-09-23 07:21:43 +02:00
ef00294e71 [IMP] core: store translated fields as JSONB columns
Translated fields no longer use the model ir.translation.  Instead they store
all their values as JSON, and store them into JSONB columns in the model's
table.  The field's column value is either NULL or a JSON dict mapping language
codes to text (the field's value in the corresponding language), and must
contain an entry for key 'en_US' (as it is used as a fallback for all other
languages).  Empty text is allowed in translation values, but not NULL.

Here are examples for a field with translate=True:

    NULL
    {"en_US": "Foo"}
    {"en_US": "Foo", "fr_FR": "Bar", "nl_NL": "Baz"}
    {"en_US": "Foo", "fr_FR": "", "nl_NL": "Baz"}

Like before, writing False to the field makes it NULL, i.e., False in all
languages.  However, writing "" to the field makes its value empty in the
current language, but does not discard the values in the other languages.

Here are examples for a field with translate=xml_translate:

    NULL
    {"en_US": "<div>Foo<p>Bar</p></div>", "fr_FR": "<div>Fou<p>Barre</p></div>"}

Change for callable(translate) fields: one can now write any value in any
language on such a field.  The new value will be adapted in all languages, based
on the mapping of terms between languages in the old values.  Basically the
structure of the value must remain the same in all languages, like before.

Reading a translated field is now both simpler and faster than the former
implementation.  We fetch the value of the field in the current language by
coalescing its value with the 'en_US' value of the field:

    SELECT id, COALESCE(name->>'fr_FR', name->>'en_US') AS name ...

The raw cache of the field contains either None or a dict which is conceptually
a subset of the JSON value in database (except for missing languages).  For the
sake of simplicity, most cache operations deal with the dict and return the text
value in the current language.

Trigram indexes have been adapted to the new storing strategy, and should enable
to search in any language.  Before this change, only the source value of the
field ('en_US') could be indexed.

Computed stored translated fields are not supported by the framework, because of
the complexity of the computation itself: the field would need to be computed in
all active languages.  We chose to not provide any hook to compute a field in
all languages at once, and the framework always invokes a compute method once to
recompute it.

Code translations are no longer stored into the database.  They become static,
and are extracted from the PO files when needed.  The worker simply uses a cache
with extracted code translations for performance.  This is reasonable, since
fr_FR code translations for all modules takes around 2MB of memory, and the
cache can be shared among all registries in the worker.  Changing code
translations requires to update the corresponding PO file and reloading the
worker(s).

Performance summary:
 (+) reading 'model' translated fields is faster
 (+) reading 'model_terms' translated fields is much faster (no need to inject
     translations into the source value)
 (+) searching translated fields with operator 'ilike' is much faster when the
     field is indexed with 'trigram'
 (+) updating translated fields requires less ORM flushing
 (-) importing translations from PO files is 2x slower

Some extra fixes:
 - make field 'name' of ir.actions.actions translated; because of the PG
   inheritance, this is necessary to make the column definition consistent in
   all models that inherit from ir.actions.actions.
 - add some backend API for the web/website client for editing translations
 - move methods get_field_string() to model ir.model.fields
 - move _load_module_terms to model ir.module.module
 - adapt tests in test_impex, test_new_api
 - because env.lang is injected into SQL queries, its returned value is
   now guaranteed to correspond to a valid active language or None
 - remove wizard to insert missing translations (no longer makes sense)

task-id: 2081307

Co-authored-by: Fabien Pinckaers <fp@openerp.com>
Co-authored-by: Raphael Collet <rco@odoo.com>
2022-09-15 22:37:50 +02:00
Gorash 5410b7c238 [IMP] base/web: XML templates are added into the asset bundles.
XML files are now declared in python module manifests. During the qweb
't-call-asset' directive, assetbundle will fetch the declared xml files,
apply the inheritance (t-inherit) and create a javascript service (for
eg: 'web.assets_backend.bundle.xml') which is added at the end of the
*.js mimifier file.

When the debug mode is activated, comments are added in the template
indicating which file the template comes from as well as the
inheritances applied to it.

****

JavaScript:

assets.js (module @web/core/assets) takes care of loading libraries,
javascripts and styles.
`loadJS(url)` (loads the javascript and returns a resolved promise when
the templates are also loaded via the '*.bundle.xml' service)
`loadCSS(url)` (loads the style a resolved promise when the file is
loaded)
`loadXML(xml, app=assets.defaultApp)` (load template into
application/owl, used by the `*.bundle.xml` services)
`getBundle(bundleName)` (get the bundle descriptor)
`loadBundle(desc)` (load the files and bundle from a descriptor)

templates (XML element content all owl templates)

A new `ready(serviceName)` method on boot.js lets you know when a
service is loaded are the require.

The xmlDependencies attribute no longer exists.

Python:

The xmls taken into account by assetbundle.py, applying `t-inherit`
inheritances and adding an `name_of_the_bundle.bundle.xml` service in
the generated JavaScript file.

****

Every manifest changes is into the next commit, except 'web_tour' in
this current commit as example.

Part-of: odoo/odoo#95500
2022-09-14 20:25:01 +02:00
std-odoo f26cae6db5 [IMP] web: allow non administrators to use relational properties
Purpose
=======

Allow non administrators to use relational properties.

Standard internal users can not read ir.model. Because of that we
created a component that simulate the behavior of a many2one, but
that call custom public method that check which model the user can
access.

Task-2852259

Part-of: odoo/odoo#95184
2022-08-29 23:46:07 +02:00
std-odoo a740989ba5 [MOV] web: move the domain selector component to web
Purpose
=======

For security reason access on ir.model is not granted to internal suers. Due
to this constraint a component exists in spreadsheet to be able to select
the models for which we have a read access on the records of this model.

This is required for the properties fields feature, hence moving its code
to web.

Some renaming is performed to make it generic. This generates some changes
in other addons, notably some class renaming.el.

Task-2852259

Part-of: odoo/odoo#95184
2022-08-29 23:46:07 +02:00
Xavier Morel e75f2989b4 [FIX] core, web: Pillow 9.1 deprecations
Pillow 9.1 deprecates most if not all toplevel Image constants:
https://pillow.readthedocs.io/en/stable/releasenotes/9.1.0.html#constants

These constants have been moved to thematic enum classes (e.g. all the
resampling constants in `PIL.Image.Resampling`).

This triggers warnings in Odoo, and the removal delay is quite short
(slated for Pillow 10, release planned mid 2023). Pillow 9.1 is also
already in Debian Bookworm (current testing).

Fix by shimming at the import level: if the enums are available import
them into the local namespace, otherwise alias `PIL.Image` itself as
to the enum.

closes odoo/odoo#96799

X-original-commit: 7be04d31bad078681ef0a2919234c11840a2e8e2
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2022-07-28 02:40:23 +02:00
6acfad2651 [IMP] web: speed up pager total counter for large DB
Doing a `search_count` on millions of records could be very slow. On
large databases, the pager counter "1-80 / 8000000" could take several
seconds just to get the total number of records.

This commit limits the counter in list and kanban views to 10k records,
and shows "1-80 / 10000+" if the limit is reached. If you click on
10000+, it updates with the real count (or if you go up to 10000 with
pager or input value).

The search_count on res.partner of odoo.com takes 4s. With this patch,
it will take ~20ms.

Task 2761165

closes odoo/odoo#95642

Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
Co-authored-by: Aaron Bohy <aab@odoo.com>
Co-authored-by: Damien Bouvy <dbo@odoo.com>
2022-07-19 11:50:24 +02:00
Gorash 0452d0701f [IMP] website: remove cache from website.page
The cache placed on the pages is no longer useful thanks to the use of
the new directive t-cache.

closes odoo/odoo#88276

Related: odoo/enterprise#27582
Related: odoo/documentation#2056
Related: odoo/upgrade#3451
Signed-off-by: Vincent Schippefilt (vsc) <vsc@odoo.com>
2022-06-03 16:40:40 +02:00
Julien Castiaux da8def8e41 [IMP] core, web: Delegate delivery of static files
Rationnals
----------

Web servers can serve some resources (e.g. static files) right away
without any interaction with the web application. The network model of
most web servers makes them capable of handling thousands of
simultaneous requests when it comes to intensive IO operations such as
streaming data from a file. The network model of Odoo is different: it
is capable of a lot of processing power but can only serve a handful of
requests at a time, i.e. Odoo (with some help from postgres) is
optimized for CPU operations, not IO.

Some users don't configure their web server, they use a basic
configuration that relay all requests to Odoo. The result is that many
Odoo HTTP Workers can be busy streaming static files instead of
processing other requests. This can lead to a worker starvation, i.e.
all workers are busy streaming files and cannot process new requests.

X-Sendfile
----------

In this work, we add the support for the [X-Sendfile] header family,
they are multiples http headers that can be used by the web application
to communicate with the web server in order to delegate the delivery of
files stored on the file system. Odoo still receives the request but it
does no more stream the file content from within its HTTP worker,
instead it skips the response body altogether and sets the `X-Sendfile`
special header with the path of the file on the filesystem. The web
server intercepts that special header, open the file and stream it.

Using those headers, we can use the best of both the web application and
the web server. The web application is still responsible to locate the
resource and verify the access rights, the web server is still
responsible of streaming the content.

Using X-Sendfile is opt-in via the `--x-sendfile` CLI flag. We set both
`X-Sendfile` (apache) and `X-Accel-Redirect` (nginx). If you are using
apache, make sure `mod_xsendfile` is enabled. If you are using NGINX
you have to add the following location block:

    location /web/filestore {  # custom path, hardcoded within Odoo
        # Prevent access from the outside world, i.e. makes this
        # route only accessible via X-Accel. MANDATORY!!!
        internal;

        # Give access to the filestore using this server's
        # permissions. Odoo is in charge of verifying the access
        # rights.
        alias /path/to/odoo/data-dir/filestore;
    }

The Odoo [deployment documentation] has been updated accordingly.

[X-Sendfile]: https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/
[deployment documentation]: https://www.odoo.com/documentation/master/administration/install/deploy.html#serving-static-files-and-attachments

Changes to the API
------------------

To benefit most from X-Sendfile, all APIs related to streaming content
over HTTP has to be adapted. They are: (1) `request._serve_static`,
(2) `ir.http._serve_fallback`, (3) `/web/content` and (4) `/web/image`.

Each used it own way to deliver content: (1) `_serve_static` was using
`send_file` (flask's send_file that as been vendored with odoo 10
years ago and not maintenained since then), (2) _serve_fallback was
handcrafting a `werkzeug.wrappers.Response`, (3) /web/content-image were
using the "binary server" `ir.http.binary_content` API.

I has been decided to remove all 3 APIs and to merge the code inside of
the new `http.Stream` object and the `ir.binary` helper model.

A Stream wraps what is going to be sent to the browser, it can be a path
to a file on the locale filesystem, a blob of raw data or an URL to an
external resource. The Stream also holds various metadata that are
mainly used for caching. The preferred way to create a Stream is via one
of its three factories so that all the metadata are set. The factories
are: `from_path`, `from_attachment` and `from_binary_field`. A stream
instance exposes a single method `get_response()` used to create the
corresponding HTTP response object out of the stream.

Inside of `ir.http` were a few methods that were not related to the http
routing and formed what was called the "binary server". All those
methods have been removed and the feature have been refactored inside of
the new `ir.binary` model. The removed methods are:

- `_xmlid_to_obj`
- `_get_record_and_check`
- `_binary_ir_attachment_redirect_content`
- `_binary_record_content`
- `_binary_set_headers`
- `binary_content`
- `_response_by_status`
- `_get_content_common`
- `_content_image`
- `_content_image_get_response`
- `_placeholder_image_get_response`

The new `ir.binary` abstract model exposes the following utilities:

**`_find_record`**

Find an attachment or a record with a binary-field out of an xmlid or
out of a pair record-model/record-id. Check the access rights and the
access token.

**`_get_stream_from`**

Create a Stream from an attachment or a record with a binary-field.

**`_get_image_stream_from`**

Same as `_get_stream_from` but adapted for images. It sets a sensible
ETag on the stream and has image resizing support.

**`_placeholder`**

Get the image placeholder blob.

Testing
-------

It is possible to test the web server configuration using the
`test_http` module. Install the module then run the unittest using the
`webserver` test-tag. By default it attempts to connect to a web-server
running on `http://localhost:80`, you can change this URL by setting the
`WEB_SERVER_URL` environment variable.

    odoo-bin -i test_http --stop-after-init
    WEB_SERVER_URL='http://localhost:80' odoo-bin --test-tags webserver --stop-after-init

closes odoo/odoo#88134

Task: 2801675
Related: odoo/documentation#2083
Related: odoo/enterprise#26191
Signed-off-by: Julien Castiaux <juc@odoo.com>
2022-06-01 02:53:59 +02:00
Denis Ledoux b03c227e88 [REF] models: refactor fields_view_get, load_views
Refactor the `load_views` API so it no longer sends multiple times the same
fields description.

e.g.
When `load_views` is called to get the kanban, tree and form views,
the list of fields of the model was sent 4 times:
- Once for each view, with only the fields used in the view,
  in `['fields_views']['kanban']['fields']` for instance
- Once globally, with all the fields of the model, in `['fields']`

The goal of this revision is to change that so it sends the list of all fields
only once.

In addition, if a view contains x2many fields,
the fields description of the comodel is also sent.
It was sent in the `views` key of the view fields dict.
e.g.
When calling `load_views` of `res.partner` to get the kanban,
tree and form views,
the `res.partner` fields description was actually sent 6 times:
- Once for each view
- Once globally
- Once for each view of the many2many field `child_ids` of the form view, in
  - `['fields_views']['form']['fields']['child_ids']['views']['kanban']['fields']`
  - `['fields_views']['form']['fields']['child_ids']['views']['form']['fields']`

The change suggested in this revision is to:
- Remove the fields description for each view in `['fields_views']`.
  As it no longer contains the fields,
  the key becomes `['views']` instead of `['fields_views']`.
- Replace the dict key `['fields']` by `['models']`,
  which is a dict with as key the model name and as values
  the model fields description. It contains the fields description
  for all models implied in the view:
  the model of the main view and the model of all one2many and many2many fields.

With this change, the fields description will only be sent once by model
implied in the view.

In addition, the web client was getting the information about the fields
sometimes in the global fields description list (e.g. `['fields']`),
sometimes in the fields description list of the view type
(e.g. `['fields_views']['form']['fields']`),
making it a pain to try to make changes / performance gain
in these field description dictionaries, because you never knew in which dict
the web client was getting its info.
Now, as there is only one place to get the fields description from,
it's clearer and cleaner.

- one2many and many2many fields views are passed directly in the main view
  architecture rather than being put in the `views` key
  of the field description.
  This is actually easier to treat by the web client,
  and this will allow in a future work to cache an entire view in one block
  of text rather than having to combine multiple cached blocks of text
  to return one view.
- one2many and many2many fields which do not have directly embedded views
  have their views directly injected in the architecture,
  so the web client doesn't have to do RPC calls to `load_views`
  for each one2many and many2many fields not having embedded views.
  For instance, this allow to reduce the number of RPC calls to `load_views`
  from 8 to 1 when loading the form of `product.product`.
  Currently, this behavior is limited to 1 level deep but we consider making it
  go all the way down in future works. We did not do it for the moment because
  in certain cases it rises the processing time and the size (bytes) too much.
  e.g. the sale.order view can be 5 levels deep,
  meaning you can reach 4 dialogs on top the main view.
  ```
  sale.order form > order_line > sale.order.line form > invoice_lines >
  account.move.line form > asset_ids > account.asset form >
  depreciation_move_ids > account.move form.
  ```
  This will also benefit in future works to cache an entire view in one block
  of text rather to having to combine multiple cached block of text
  to get one view.
- `fields_view_get` becomes `get_view`.
  As it no longer returns the fields description,
  keeping the `fields` in the name `fields_view_get` no longer makes sense.
  Hence removing `fields` from the method name, it becomes `view_get`.
  As it gets renamed anyway, we take the opportunity to rename it `get_view`,
  which is more in line with the general getter/setter guidelines
  in the model object world.
- `_fields_view_get` becomes `_get_view`. For the same reasons than above.
- `load_views` becomes `get_views`.
  This is not mandatory, there is no technical reason to rename `load_views` as
  it practically sends the same info as before,
  the view architectures and their fields description. Just in another way.
  We just take the opportunity of this pull request to suggest a cleaner API:
  `_get_view`, `get_view` and `get_views`.
- Arguments `toolbar=False, submenu=False` fo the methods
  `_fields_view_get` and `fields_view_get` are converted to a kwargs `**options`
  in `_get_view` and `get_view`.
  The rationale is that submenu was already no longer used (deprecated)
  and the mobile options is introduced.
  The mobile options is necessary to tell the server to send the mobile views
  for x2many fields (kanban instead of tree).
  Instead of adding a new argument each time we add a new option to
  `fields_view_get`, it seems wiser to have a kwargs `**options` to avoid
  to re-write all overrides each time a new option is introduced.
- `_fields_view_get` returned a dict containing the arch in text and some of the
  view information. Now, `get_view` returns a tuple with the view architecture
  as an `etree` node, and the view as a browse record. The rationale is that all
  overrides of `_fields_view_get` were about modifying the arch only
  (e.g. changing the address format/re-organizing the address related field
  nodes of the partner according to the company country).
  To do so, all these overrides were doing `etree.fromstring` to parse the arch
  which was sent in text to convert it to an `etree`,
  then operations were done on the `etree`,
  and then `etree.tostring` was called to convert back the arch to string.
  With this change of signature to send the arch as an `etree`,
  all these back and forth `etree.fromstring` -> `etree.tostring` are avoided,
  allowing some performance gain and less code in the end.
- A cleanup of the keys returned in the dict of `fields_view_get`
  has been performed in `get_view`:
  - `fields` is removed, as explained above,
  - `view_id` is renamed `id`,
  - `name` is removed, it was unused by the web client,
  - `type` is removed, it was unused by the web client,
  - `field_parent` is removed, it was unused by the web client,
  - `base_model` is removed, it was unused by the web client.
- `filters` is moved from the global dict returned by `load_views`
  (now `get_views`) to the dict returned by `fields_view_get` (now `get_view`)
  as it applies only to the `search` view type.
- Retro-compatible methods for the 3 methods
  `fields_view_get`, `_fields_view_get` and `load_views` are provided,
  with deprecation warnings in them.

- The web client could cache the model fields description
  (as it already caches the views),
  so it doesn't need to fetch them again if it asks for another view of a model
  for which he already has the fields description.
  If we do so, `get_views` could return only the list of models used by
  the views, without the fields description as of now,
  and the web client would then call `fields_get` independently only for
  the models for which it doesn't have yet the fields description.
  This would avoid the server to return the fields description
  and to call `fields_get`, which is costly, for each `get_views`,
  therefore gaining performances.
- Inject the views of the one2many and many2many fields all the way down,
  unlimited depth level, as explained above.
- Cache with `ormcache` the architecture of back-end views.
  This is already done for qweb views, it's not done for back-end views.
  Therefore the postprocessing of the views is performed for each `get_views`,
  which is costly, while the view architecture doesn't change for users
  belonging to the same groups, according to the groups implied by the view.

This pull request is co-authored by
Aaron Bohy (aab) for the web client part and
Denis Ledoux (dle) for the server part.

Part-of: odoo/odoo#87522
2022-04-29 09:57:44 +02:00
Jeremy Kersten 66f886e480 [FIX] website_event_*exhibitor,(q)web: don't crash sponsor if no name
Field name on sponsor are not required. So when we display the image of a
sponsor without field-name it will use the rec_name 'name' that is not set
and so False.
It will crash during rendering of the widget image that use it.

Now we force to use a field required 'partner_name' to avoid the crash.
By default, when you confirm the sale_order from the backend, we copy
the partner name in field name.

Because we use a class avatar to have a max-width of 96, we don't need
to use a image_512, image_128 is enough in most of case.
The max-width option on widget image has no effect on rendering.

In other case where the rec_name is Falsy and we don't provide other name to
the widget, we now use a default 'name' value to avoid a crash:

closes odoo/odoo#88296

Attributeerror: 'bool' object has no attribute 'replace'
X-original-commit: b054dfc715afb225eb032a722ec58e6888b1b905
Signed-off-by: Jérémy Kersten <jke@odoo.com>
2022-04-08 17:13:35 +02:00
Julien Castiaux 1dd3865208 [IMP] *: odoo.addons.web.controllers.main splitted
The odoo.addons.web.controllers.main python module have been splitted
over multiple files on the basis 1 controller = 1 file. In this work we
adapt all modules to use the new imports.

A non-exhaustive list of where stuff have been moved:

* main.Home		--> home.Home
* main.Session		--> session.Session
* main.WebClient	--> webclient.WebClient
* main.clean_action	--> action.clean_action
* main.ensure_db	--> home.ensure_db

The complete list is accessible in odoo.addons.web.controllers.main.

closes odoo/odoo#87571

Related: odoo/enterprise#25746
Signed-off-by: Raphael Collet <rco@odoo.com>
2022-03-31 02:10:53 +02:00
Gorash 880954ebfc [IMP] *: remove _render from ir.ui.view and simplify report
There were inconsistencies in the calls to `_render`.
* the view context could contain information that misled developers.
Indeed, the context and value of the view are not supposed to be found
in the rendering. Thus by calling `ir.qweb` with the name of the
template, we ensure that there is no unwanted information and in
addition the cache key is that of the name of the template which saves
a query.
* the context used for rendering was modified by a method on
`ir.ui.view`, except this is not information used by this model. There
is now a `_prepare_environment` method residing on `ir.qweb`. This
method allows to modify the value dictionary as well as the context in
which the rendering will be done. This preparation of the data as well
as my security check is done only once per rendering. This also saves
some queries
* Freeze options for rendering were inconsistent. It could be that
options on which rendering depends were not part of the cache key. Thus,
depending on the user who generated the generation of the rendering
function, there was or was not information in the template. For example
for automatic branding. This is no longer possible, because it is the
context that is used. The options serving as a cache key are only
recorded for information (for the profiling system for example). A
simplification of the `ir.qweb.field` models could be made.

The report rendering and call `ir.qweb` instead of `ir.ui.view`.

Part-of: odoo/odoo#85110
2022-03-29 10:56:15 +02:00
Julien Castiaux 8639f9b257 [FIX] website: restore debug mode in website pages
Install website, create a custom web page, we'll call it page_1. Ensure
you are not in debug mode (go to /web/health?debug=0 to disable it).
Open the web page enabling the debug-mode /page_1?debug=1, the page
opens but the debug mode is disabled.

Because web pages are served using another routing mechanism than
controlers we have to ensure we load the debug query-string in those
mechanisms too.

closes odoo/odoo#85340

Signed-off-by: Romain Derie (rde) <rde@odoo.com>
2022-02-28 13:53:09 +00:00
Thibault Delavallée 2a3d8dd1c2 [MOV] various: move ir.qweb.fields code into right files
When working on qweb fields it is difficult to find some custom override as
they are never in the right file. Finding them is always a bit of random pick.

Task-2607416

Part-of: odoo/odoo#74171
2022-02-28 11:07:39 +00:00
Julien Castiaux f04b90b6e8 [REF] core: HTTPocalypse (12) web ir.http & login
This commit is the 12th commit of a comprehensive refactor of our HTTP
framework. See odoo/odoo#78857 for complete historic, discussions and
rationnals.

The web module is twofold, on one side there are many controllers: /,
/web, /web/login, /web/database/selector, /web/dataset/call_kw, etc, on
the other side there is `session_info`: the method responsible to create
the web client's environ.

This module is kinda an exception as it is (with base) a server wide
module. In the case of the HTTP framework, it means that the controllers
of web are always accessible, i.e. going to / or /web/login will never
return a 404 Not Found even if the user is not connected to a database.

This is both a blessing and a curse. It is a blessing because the
controllers are always accessible it means that a new users can freely
access those routes. It is a curse because *any* user can access them,
even user who don't have a session yet thus who are not connected to a
database yet. From a developer standpoint, we have to put extra care to
correct serve users with and without a database. An example is the
/web/login route, the login/password pair is stored in a database,
without database it is impossible to validate a user login but users can
still access this route without db.

To solve this problem, there is the `ensure_db` function. This function
attempts to find a database using various sources (?db= query-string,
session db, mono db) and to save it on the user session. In case no db
is found, the user is redirected to the database selector. In a way,
this function grants a database to the user in a seamingly experience.
In a way, this function brings a welcome differentiation between
`auth='none'` with a database and `auth='none'` without a database. Such
differentiation only matters for the server wide modules as "regular"
module controllers are only accessible via the ir.http routing map, i.e.
it is not possible to declare a nodb controller outside of server wide
modules.

An important changement is the `session.authenticate` method, before it
was possible to call the method when the cursor was not yet initialized,
authenticate would open a cursor against the given database, setup a
registry and an environment and ultimately save everything on the
current request. Because the cursor is now greedily created, it is no
more possible to update the request environment when authenticating on
another database.

PR: odoo#78857
Task: 2571224
2022-02-24 13:30:50 +00:00