-
- Downloads
[REF] accounting v16. Yeeeeaah
TLDR: * invoices are implemented using computed methods instead of onchange * the synchronization only happens when switching tabs in the Form view to improve perfs. _______________________________________________________________________ The whole engine of the synchronization of Invoices to the Journal Entries has been refactored * by using computed fields instead of onchange functions * by synchronizing only from invoice to journal entry in `create` and `write` * by saving when switching tabs on the Invoice form, to synchronize before showing the values This comes with numerous advantages: * no need to call the onchange methods manually * no need to use the Form emulator to build invoices (i.e. EDI, OCR, intercompany, ...) * the performance for invoices with many lines improves drastically, going from 2 minutes to 4 seconds to create an invoice with 500 lines * the model is more declarative, we can now see how the values are computed instead of having the values being copied from various places. * remove the hack in `onchange` that disabled the recursivity of it, which was unexpected and needed to be managed manually in all the onchange methods This means that: * Some fields need to be exclusively computed on journal entries values or invoice values, more specifically the Tax Summary widget. It is now - computed from entry lines, when opening the view - computed from invoice lines when changing those, because the tax lines will need to be recomputed anyways, erasing previously set values - set with an inverse function when saving; after the sync has been done * Some possible operations previously possible have been dropped. (i.e. look at the removed test test_in_invoice_line_onchange_accounting_fields_1) This is because such a behavior was undefined (how is changing the balance going to affect the unit price? How is the amount currency going to affect it?) _______________________________________________________________________ Implementation Details ---------------------- The "dynamic lines", meaning the payment terms and the tax lines are now only created in the `create` and `write` functions. In order to reduce code duplication, it has been implemented using context managers used in both `account.move` and `account.move.line` These context managers help comparing the values before/after, acting like a local `onchange`, but getting benefit from the dirty flags from the `compute` dependences. This is relying on computed fields on the move (`needed_terms`) and on the lines (`compute_all_tax`) which contain the values needed for the related move. Depending on the needed values and the existing values (`term_key` and `tax_key`, respectively) the context manager will determine what needs to be created/updated/deleted. Some related changes are to produce a `dict` instead of a `str` for the `tax_totals` (previously `tax_totals_json`) fields, by simplicity to reduce the complexity of IO, and simplicity of debugging, because the logic of the field needed to change (cannot be computed at the same time anymore since it needed the lines to be synced) By simplicity, and also because it makes more sense, some boolean fields have been merged into `display_type`: * `is_rounding_line` * `exclude_from_invoice_tab` * `is_anglo_saxon_line` The `price_unit`, `quantity` and other "invoice fields" are now not set anymore on lines that are not product lines since it didn't make any sense to have it. Performances ------------ You have to keep in mind that a simple `create` didn't compute a lot of fields, for instance not taxes were set, no payment terms,... Now it does. ```python import random from timeit import timeit from odoo import Command domain = [('company_id', 'in', (False, self.env.company.id))] products = self.env['product.product'].search(domain).ids partners = self.env['res.partner'].search(domain).ids taxes = self.env['account.tax'].search(domain).ids def create(nmove, nline): self.env['account.move'].create([ { 'move_type': 'out_invoice', 'partner_id': random.choice(partners), 'invoice_line_ids': [ Command.create({ 'name': f'line{i}', 'product_id': random.choice(products), 'tax_ids': [Command.set([random.choice(taxes)])], }) for i in range(nline) ] } for j in range(nmove) ]) # After | Before print(timeit("create(1, 1)", globals=globals(), number=1)) # 0.11 | 0.09 print(timeit("create(100, 1)", globals=globals(), number=1)) # 2.76 | 2.50 print(timeit("create(500, 1)", globals=globals(), number=1)) # 14.56 | 12.34 print(timeit("create(1, 100)", globals=globals(), number=1)) # 1.03 | 5.52 print(timeit("create(1, 500)", globals=globals(), number=1)) # 3.99 | 125.02 print(timeit("create(50, 50)", globals=globals(), number=1)) # 19.44 | 79.55 ``` Another metric that can be used is running the test suite with `--test-tags=/account` (only `account` installed) * before: 404s, 267127 queries (366 tests) * after: 318s, 232125 queries (362 tests) Why this commit title? ---------------------- Someone told me that this was the perfect way of naming your commits. c04065ab task-2711317 closes odoo/odoo#96134 Related: odoo/upgrade#3715 Related: odoo/enterprise#29758 Signed-off-by:Laurent Smet <las@odoo.com>
Showing
- addons/account/__init__.py 1 addition, 0 deletionsaddons/account/__init__.py
- addons/account/__manifest__.py 1 addition, 0 deletionsaddons/account/__manifest__.py
- addons/account/demo/account_demo.py 3 additions, 18 deletionsaddons/account/demo/account_demo.py
- addons/account/models/__init__.py 1 addition, 0 deletionsaddons/account/models/__init__.py
- addons/account/models/account_bank_statement.py 15 additions, 3 deletionsaddons/account/models/account_bank_statement.py
- addons/account/models/account_journal.py 14 additions, 13 deletionsaddons/account/models/account_journal.py
- addons/account/models/account_journal_dashboard.py 1 addition, 1 deletionaddons/account/models/account_journal_dashboard.py
- addons/account/models/account_move.py 2590 additions, 5144 deletionsaddons/account/models/account_move.py
- addons/account/models/account_move_line.py 2590 additions, 0 deletionsaddons/account/models/account_move_line.py
- addons/account/models/account_partial_reconcile.py 3 additions, 16 deletionsaddons/account/models/account_partial_reconcile.py
- addons/account/models/account_payment.py 22 additions, 29 deletionsaddons/account/models/account_payment.py
- addons/account/models/account_payment_term.py 29 additions, 16 deletionsaddons/account/models/account_payment_term.py
- addons/account/models/account_tax.py 52 additions, 21 deletionsaddons/account/models/account_tax.py
- addons/account/models/company.py 0 additions, 1 deletionaddons/account/models/company.py
- addons/account/populate/account_move.py 64 additions, 73 deletionsaddons/account/populate/account_move.py
- addons/account/report/account_invoice_report.py 2 additions, 2 deletionsaddons/account/report/account_invoice_report.py
- addons/account/static/src/js/account_move_form.js 53 additions, 0 deletionsaddons/account/static/src/js/account_move_form.js
- addons/account/static/src/js/account_payment_field.js 2 additions, 2 deletionsaddons/account/static/src/js/account_payment_field.js
- addons/account/static/src/js/legacy_tax_totals.js 53 additions, 7 deletionsaddons/account/static/src/js/legacy_tax_totals.js
- addons/account/static/src/js/tax_totals.js 51 additions, 23 deletionsaddons/account/static/src/js/tax_totals.js
Loading