Daily updates from Odoo
Monday, August 26, 2019
6 changes
Enhancements to existing features
This change lets Odoo apply business rules automatically when records are created or updated, instead of relying mainly on screen-based onchange behavior. It should make imports, automated processes, and module extensions more consistent and easier to maintain across apps like Accounting, CRM, Sales, Inventory, Events, HR, and Calendar.
Original PR description
Support for compute, with readonly=False as an alternative to onchange & conditional defaults. ### Rationale Most onchange includes business logic (ex: changing a partner on an invoice sets the…
Support for compute, with readonly=False as an alternative to onchange & conditional defaults.
### Rationale
Most onchange includes business logic (ex: changing a partner on an invoice sets the
fiscal position, and the payment terms). But the business logic should not be exclusive to
the user interface. Thus, most of these onchanges should actually be compute fields with
store=True and readonly=False.
Compute fields are usually orthogonal between each others, thus simpler to implement and
inherits. Example: on an invoice, it's easy to implement _compute_fiscal_position (it's a
related partner_id.fiscal_position_id), but very complex to implement _onchange_partner_id
(it has impact on lots of different concepts depending on installed modules: fiscal positions,
payment terms, address, ...). By splitting this big onchange into smaller compute, it should
simplify the code and inheritability.
USE CASE 1:
If onchanges are replaced by compute fields, the code to create an invoice, or automated tests
it is simplified as you can let the business logic do its job:
self.env['account.invoice'].create({
'partner_id': 1
'line_ids': [{
'product_id': 1
})
)}
-> the compute fields will compute the right fiscal position, taxes, customer address, etc. It's
not the role of the sale order to implement this logic anymore.
This simplifies creation of records in the code, but also import. (you import only some data, and you can let the business logic do the rest: import customers of your invoice, and the system will setup the journal, currency, fiscal position, taxes, etx)
USE CASE 2:
Let's say the module l10n_mx want to add a TAXCODE field on an invoice,
there is two way to do it:
@api.onchange['partner_id')
def _change_partner_id(self):
super()
self.taxcode = self.partner_id.taxcode
or, the new approach:
@api.depends('partner_id')
def _compute_taxcode(self):
self.taxcode = self.partner_id.taxcode
The onchange approach creates a big issue: you will have to manage this logic by creating
bridge modules for every module that creates an invoice (e.g. l10n_mx_sale and
l10n_mx_subsription).
With the new approach, the TAXCODE is delegated to the invoice, and handled automatically
at creation, unless provided explicitly. Every module who create an invoice will have the new
business logic applied automatically: no need to create bridge modules.
Having some data that are computed downstream and others upstream, and some backend and others for the interface only is, in my opinion, a big design flaw in Odoo's framework. It is much easier if everything is computed in the same direction, with only one dependency tree, and one concept. (imagine computed fields are a like an excel cell, whose formula depends on others cell; imagine the mess if Excel would have introduced "onchange")
### Technicals
The main change is to evaluate computed fields only when we need it, instead of "at every write & create". Thus, computed fields are evaluated at the end of the transaction (commit), when you read a field that is marked as "to recompute", or when you search on a field that have record marked as "to recompute".
As opposed to the current version, the value in cache is usually not invalidated. Instead, we use the existing "todo" concept of the framework to mark fields to be recomputed. Except that we don't process the "todo" at each write() / create() anymore.
That allows multiple speed improvements:
- do not recompute fields, when we write the same value than already in the cache (when validating an invoice, a lot of computed fields are currently evaluated several times during the time of building the invoice. -> the company_id is set by default, but then computed as related to the journal_id --> triggers most computed fields)
- in case of multiple write/create, the computed fields are evaluated only once: if you add lines one by one on an invoice, the total/taxes of the invoice will still be computed only once, at the end of the transaction.
So, the following code **will evaluate invoice's computed fields only once** (e.g., the subtotal & taxes):
invoice = Invoice.create(...)
invoice.write(...)
for line in ...:
InvoiceLine.create({... 'invoice_id': invoice.id)
But the triggers to evaluate compute fields' dependencies are still called once every create/write. Thus, it's still a good optimization to create all the lines at once to avoid the triggers. (but I think the triggers cost can be reduced a lot, see bellow)
### Compatibilities
This version of the ORM is mostly compatible with the current version in master (all the tests of the base module passed without modifications). But there are subtles impacts that requires to fix some business modules. The main ones are:
- if you perform direct SQL queries, you might want to do self.recompute() or self.recompute_fields(...) to evaluate and write the computed values before the SQL queries. (In master, you had to do it in computed fields, but you were safe after a write().
- there are some bugs to fix in business modules that were not noticeable because function fields were updated, at the end of write() or create(). (the main ones are missing dependencies in @depends() who were not a big deal before and can create bugs now)
- recursive fields feature has been removed. Instead, explicitly call a self.add_todo() on children to mark them as to "recompute". --> it's actually very easy to put them back, but as they are not used a lot, I think it's better to remove this feature from the framework and explicitly manage recursivity in the business code.
### Side Effect
Performances seem much better with this refactoring. The following code is **2.32x faster** with demo data (0.0086s vs 0.0037s):
for partner in self.env['res.partner'].search([]):
if partner.state_id:
cn = partner.state_id.country_id.name
Writing and creating complex records (with related fields, computed fields) seems faster too, but I still have to do the benchmark on real business modules. The method testme() in this branch, that create a complex object with one2many, multiple related and computed fields is:
- 1.82x faster: 0.0351s vs 0.0192s
- 1.66x less SQL queries: 50 vs 30
Stack trace is shorter when crashing in computed fields.
This branch will also allow to remove computed fields' triggers using inverse fields instead or hard SQL queries. I expect this to speed up most write() / create() by reducing SQL queries by an extra ~35%, which should translates into an extra speed improvement.
### Misc Notes
- record.field = 3 is now 100% equivalent to record.write({'field': 3})
- record.write(...) doet not write, but update in-memory
- _inherits are not anymore handled by write(...), but by related fields inverse's method
- prefetch should be improved (what about passing the record set instead of an iterable on ids)
- recompute computes in-memory, but does not write anymore
- stored fields have only one value in cache (cache does not depend on the context for those fields)
- @depends are evaluated in memory through inverse fields when possible, instead of hard SQL queries
- towrite holds value that have changed but not written to the DB anymore --> use towrite_flush() before direct SQL queries
- ACLs checks are made in write(). --> _write(...) which become a low level function that do not check ACL
- removed "global" from ir.rule and check if groups are set instead (global is a python protected keyword)
- boolean: is false at pg level instead of python level
### Status
- the compute alternative to onchange works perfectly
- base module installs and pass all tests
- but there are still bugs to fix, and business modules to review
It's a quick POC written in a few evenings; it still requires a huge cleanup.
Once these are done, ideas for future improvements: https://pad.odoo.com/p/r.896af7a5c2a2fc86575f8c5b4d306419
### About onchange
Onchange will remain supported in v13, but should be limited to pure UI changes, without business logic. (so only a few; pretty much everything should be implemented with computed fields instead)
--
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prThis change explores replacing many screen-only automatic updates with reusable business rules that also work when records are created by imports, integrations, or automated processes. It should make invoices, sales, CRM, HR leave, mail activities, and products behave more consistently while reducing duplicated logic across modules.
Original PR description
POC moved to branch: master-nochange-fp Support for compute, with readonly=False as an alternative to onchange & conditional defaults Most onchange includes business logic (ex: changing a partner on…
POC moved to branch: master-nochange-fp
Support for compute, with readonly=False as an alternative to onchange & conditional defaults
Most onchange includes business logic (ex: changing a partner on an invoice sets the
fiscal position, and the payment terms). But the business logic should not be exclusive to
the user interface. Thus, most of these onchanges should actually be compute fields with
store=True and readonly=False.
Compute fields are usually orthogonal between each others, thus simpler to implement and
inherits. Example: on an invoice, it's easy to implement _compute_fiscal_position (it's a
related partner_id.fiscal_position_id), but very complex to implement _onchange_partner_id
(it has impact on lots of different concepts depending on installed modules: fiscal positions,
payment terms, address, ...). By splitting this big onchange into smaller compute, it should
simplify the code and inheritability.
USE CASE 1:
If onchanges are replaced by compute fields, the code to create an invoice, or automated tests
it is simplified as you can let the business logic do its job:
self.env['account.invoice'].create({
'partner_id': 1
'line_ids': [{
'product_id': 1
})
)}
-> the compute fields will compute the right fiscal position, taxes, customer address, etc. It's
not the role of the sale order to implement this logic anymore.
USE CASE 2:
Let's say the module l10n_mx want to add a TAXCODE field on an invoice,
there is two way to do it:
@api.onchange['partner_id')
def _change_partner_id(self):
super()
self.taxcode = self.partner_id.taxcode
or, the new approach:
@api.depends('partner_id')
def _compute_taxcode(self):
self.taxcode = self.partner_id.taxcode
The onchange approach creates a big issue: you will have to manage this logic by creating
bridge modules for every module that creates an invoice (e.g. l10n_mx_sale and
l10n_mx_subsription).
With the new approach, the TAXCODE is delegated to the invoice, and handled automatically
at creation, unless provided explicitly. Every module who create an invoice will have the new
business logic applied automatically: no need to create bridge modules.
--
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prThe website editor experience has been redesigned so editing options are shown in a clearer side panel instead of older dropdown and overlay controls. This makes page and email design tools easier to use and also cleans up editor asset organization to avoid duplicate styling resources.
Original PR description
* website, mass_mailing, point_of_sale, website_form
Adds a guided workflow to recognize expenses or revenue in the period they belong to, while keeping taxes reported in the original invoice period. This helps accounting teams handle bills or invoices received after the relevant period without manual journal entry workarounds.
Original PR description
Task 2006617 ======= Purpose ======= Accruals are revenues and expenses that are incurred during an accounting period for which no invoices or payments were received or made. => We suggest a wizard…
Task 2006617
=======
Purpose
=======
Accruals are revenues and expenses that are incurred during an accounting period for which no invoices or payments were received or made.
=> We suggest a wizard allowing recognizing the Expense/Revenue in a different period keeping tax return in the invoice period.
Example:
A vendor bill received in February which was for the December consumption.
You should recognize the expense in December but the tax return is still due to February.
1) Regular Vendor Bill in february
Account D C
600 ACHAT 10000
411 TVA 2100
440 FOUR 12100
2) Action opening a wizard that allows specifying when we have to move the expense.
- Targeted date, accrued account (expense, revenue, taxes, ...)
- Action from account.move.line ?
3) Wizard result
a) The expense account is replaced by the accrued expense account in the original vendor bill journal entry (february)
Compte D C
444 FAE 10000
411 TVA 2100
440 FOUR 12100
b) A new accrual journal entry is created for the original expense account in december
Compte D C
600 ACHAT 10000
444 FAE 10000
Description of the issue/feature this PR addresses:
Current behavior before PR:
Desired behavior after PR is merged:
--
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-prThis pull request adds shared support for Latin American customer identification and invoice document numbering, then uses it to modernize Argentina, Chile, and Peru localization features. Businesses in these countries get more accurate partner records, invoice sequencing, tax handling, and local geographic/accounting data needed for compliance and future electronic invoicing.
Original PR description
Description of the issue/feature this PR addresses: Current behavior before PR: Desired behavior after PR is merged: -- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr
Adds official VAT purchase and sales reporting for Argentina, including detailed invoice tax breakdowns and flexible pivot analysis. Adds a Chilean F29 tax return proposal tool to help businesses review tax information already reported to the government.