[16.0] Migrate account_invoice_en16931 and the einvoicing stack to 16.0 - #33
[16.0] Migrate account_invoice_en16931 and the einvoicing stack to 16.0#33njeudy wants to merge 74 commits into
Conversation
Fix crash when no last sync date on partner Fix criteria to create flow on customer invoices
Add support for invoice attachment generation in Factur-X as attachement to PDF. Simplify proto of method generate_en16931_xml() Change 'variant' by 'invoice_format' to unifiy vocabulary [FIX] account_invoice_en16931_py3o: add support for context key- 'regular_pdf_invoice' [IMP] l10n_fr_einvoicing: add checks on attachments for Chorus Pro Re-organize invoice form view: all fields are now in the dedicated tab- "e-Invoicing"
|
@njeudy First of all, thank you for this work! We deployed this branch ( Everything below is either a correction that removes work from your instructions, or a defect that pre-exists on 18.0 and that this port merely surfaced. None of it is caused by the backport. One correction to MIGRATION-16.0.md, which also explains a difference between our two runsThe document says the But that same commit did not only add the three helpers: it also added a constraint. # l10n_fr_siret/models/res_partner.py:42 (OCA l10n-france 16.0, added by 9e662b1a)
@api.constrains("siren", "nic", "vat")
def _check_siret(self):Among other things it now requires a French VAT number to end with the 9 digits of the SIREN. Before That is where our two runs diverge, and why installing this branch with demo data aborts the database for us and not for you: your instance consumes a reduced copy of The same pair is on the 18.0 branch, so it is not a backport artefact; it will surface there too as soon as an instance runs a Second correction, unrelatedThe VATEX codes are also already on Three things that block installing the stack on a multi-company databaseWe then installed it on a real multi-company database (one French company subject to the reform, alongside companies in two other countries (Italy and Spain). Three defects block that, none caused by the backport and all three present on 18.0 as well:
And one that is already reported by someone else: We have the 16.0 environment set up and will keep it, so if any of this is useful to test — a change on this branch, a fix on 18.0 you want checked against 16.0, or a case you cannot reproduce — we are happy to run it and report back. Thanks again for opening the branch up; it is what made it possible for us to evaluate the reform stack on 16.0 at all. |
Move France-specific code for BT-23 from account_invoice_en16931 to l10n_fr_account_invoice_en16931
…ction_confirm When sale_subscription is installed, action_confirm() splits self into recurring/non-recurring subsets and calls super().action_confirm() on each, including empty ones. self.ensure_one() at the top of _action_confirm() then raises on the empty subset, blocking confirmation of any non-subscription order. Moving ensure_one() inside the loop lets empty recordsets pass through as a no-op, matching how the rest of the method already handles batches. Fixes akretion#40
|
thanks for your tests, will check this and update this PR (and resolv conflicts too) :) |
…backport analysis Retarget the three in-scope modules to 16.0.1.0.0 and mark them not installable: the code is still the 18.0 code and would not install on 16.0. Document the gap analysis in MIGRATION-16.0.md: the module is built on the generic tax engine introduced in Odoo 18, which does not exist on 16.0, and two upstream dependencies must be backported first (VATEX codes on account_tax_unece, still an open OCA pull request, and the _get_siren/_get_siret/is_france_country additions to l10n_fr_siret, available on the 18.0 branch only). No migration code at this stage: specs are validated first.
Everything that does not touch the tax computation layer, so that what is left
is the rewrite of the tax engine itself.
* Translations: self.env._() only exists as of 17.0, and the 16.0 _() takes no
interpolation argument. Rewrite the 41 calls as _("...") % {...} (or % value),
and import _ from odoo. Done with an AST-based script, so multi-line calls and
the two chained .format(**format_vals) cases are preserved. The literal %% in
the VAT rate message still renders as %, as before.
* res_company._compute_no_vat_taxes(): _read_group(groupby=, aggregates=) and its
recordset results are 18.0 API. Rewritten on the 16.0 read_group(), which
returns dicts and exposes the count as <groupby>_count.
* Views: the 17.0+ attribute syntax (invisible="expr") is not supported on 16.0,
rewritten with attrs=. Applies to account_move.xml, the generate wizard and
l10n_fr_account_invoice_en16931.
* res_config_settings_view.xml: rewritten, not converted. The <block>/<setting>
tags do not exist on 16.0, where settings are built with div.o_settings_container
and div.o_setting_box. The anchor default_taxes_setting_container does exist,
so the view still hooks in the same place.
* _prepare_bt23(): on 16.0 the stock module adds ('product', 'Storable Product')
to product.type, while 18.0 only has consu/service/combo and carries storability
in is_storable. Testing == "consu" alone silently classified goods as a mixed
business process (BT-23 = M1/M2 instead of B1/B2). Match consu and product.
Not touched: account_move_line.py line 182 exceeds E501 upstream already; fixing
it here would only add noise to the diff with 18.0.
The tax computation layer is untouched, so the modules stay installable: False.
…ython 3.9
The target instance runs Odoo 16.0 on Python 3.9.2, where the PEP 604 syntax
raises TypeError: unsupported operand type(s) for |: 'type' and 'type'. Upstream
targets Python 3.10+ (see target-version = "py310" in .ruff.toml), which is
correct for 18.0 but not for us.
Use the (int, float) tuple form, which is valid on every version.
Found by running the code against the real image: it is valid syntax, so it
passes both review and a py_compile on a 3.12 host. Validate with:
docker run --rm -v "$PWD/code:/src:ro" python:3.9-slim sh -c \
'cp -r /src/<module> /tmp/ && cd /tmp && python3 -m compileall -q <module>'
decimal.precision is database-global, so this check forces every company of a multi-company database down to 2 decimals on discounts, and it does not protect the generated document: the discount percentage is never exported, and disc_prec has a single consumer, the float_compare deciding whether a line carries a discount. At 2 decimals a 0.001 discount compares equal to zero and BT-147 is silently omitted, so lowering the precision produces a less faithful document, not a more compliant one. Reported by @almumu in akretion#33. Same change proposed upstream on 18.0 for akretion#43; to be dropped from this branch if it is merged there.
…ng companies _post() runs the company-level configuration check for every sale document, with no condition, so in a multi-company database a single company subject to the reform makes every other company unable to post customer invoices until its own taxes are UNECE-coded -- including companies that will never emit an EN16931 document. The document itself is not at risk: the same checks run from _en16931_checks_upon_invoice_generation(), so an incomplete configuration still cannot produce an EN16931 document. Add en16931_issuer on res.company, unset by default, gate the _post() check on it, and keep the configuration verifiable on demand from the accounting settings. Reported by @almumu in akretion#33. Same change proposed upstream on 18.0 for akretion#42; to be dropped from this branch if it is merged there.
decimal.precision is database-global, so this check forces every company of a multi-company database down to 2 decimals on discounts, and it does not protect the generated document: the discount percentage is never exported, and disc_prec has a single consumer, the float_compare deciding whether a line carries a discount. At 2 decimals a 0.001 discount compares equal to zero and BT-147 is silently omitted, so lowering the precision produces a less faithful document, not a more compliant one. Reported by @almumu in akretion#33. Same change proposed upstream on 18.0 for akretion#43; to be dropped from this branch if it is merged there.
…ng companies _post() runs the company-level configuration check for every sale document, with no condition, so every company of the database has to be UNECE-coded before it can post a customer invoice -- including companies that will never emit an EN16931 document. Measured on a clean 19.0 database with the whole stack installed: the unconditional check raises "The following errors have been detected in company ... that block EN16931 e-invoicing" in **390 standard Odoo tests** (account 287, sale 45, account_edi_ubl_cii 18, account_payment 8), which all create their own test company and post invoices on it. With this commit, that count drops to zero. The document itself is not at risk: the same checks run from _en16931_checks_upon_invoice_generation(), so an incomplete configuration still cannot produce an EN16931 document. Add en16931_issuer on res.company, unset by default, gate the _post() check on it, and keep the configuration verifiable on demand from the accounting settings. Reported by @almumu in akretion#33. Same change proposed upstream on 18.0 for akretion#42; to be dropped from this branch if it is merged there.
|
Thanks for running it on a real 16.0 database — the branch is updated, and all four points are addressed. The two backports are gone. You're right on both, and I checked before removing them: The demo VAT number is fixed, and your diagnosis of the divergence was exactly right. FR23334175221 is the VAT number of SIREN 334175221, while the partner carries SIREN 892350455 — the consistent value is FR78892350455 (key = (12 + 3 × (siren mod 97)) mod 97 = 78). Worth noting for @alexis-via that the same pair is still on 18.0, so any 18.0 instance running a post-27/07 #41 is already on the 16.0 branch upstream, so the rebase brought it in. #42 and #43: both patched on this branch, and proposed upstream on 18.0 where they belong — #47 and #46. They are kept here so you can test them on 16.0 straight away, and will be dropped from this branch once merged upstream (or adjusted, if Alexis prefers a different shape). On #42 I went with your option 2 rather than option 1, plus an on-demand Check Configuration button so issuing companies keep an early warning. On that one, running the suite turned up an argument stronger than the multi-company case. On a clean 19.0 database with the whole stack installed, the unconditional #44: commented on the issue with what we measured on our own deployment, including a Saxon configuration that fixes the codedb lookups without The branch is also rebased on upstream We ran the cascade install ourselves this time, on a clean 16.0 database with demo data and plain OCA |
(cherry picked from commit e177222)
(cherry picked from commit 7ff630e)
(cherry picked from commit 449ba64)
(cherry picked from commit 6a3767a)
(cherry picked from commit e118dd1)
(cherry picked from commit 8855c91)
…rocess_type (cherry picked from commit 95d1fab)
…server is down Fixes bug akretion#44 the schematron validation is silently skipped when no Saxon server is reachable Update and improve fr translation (cherry picked from commit f8a72c6)
…ve (new method dedicated to the check on the ivoice lines) Inspired by bug akretion#38 (cherry picked from commit 1757cae)
Improve display_name of flows: add syntax (cherry picked from commit b7df7da)
…rectory status FIX l10n_fr_einvoicing_sale: activate checks only for VAT registered companies Mutualize code between l10n_fr_einvoicing_sale and l10n_fr_einvoicing: new method _fr_directory_should_sync_upon_confirmation() on res.partner Block back to draft when the invoice is not linked to a flow but would have to be sent to AP after re-confirmation (cherry picked from commit fcf1cad)
… line Fixes issue akretion#38 (cherry picked from commit 5fc122f)
…rmation for unittests (cherry picked from commit 7399781)
…ny directory lines This new feature require PR 1386 from OCA/edi Remove field company_partner_id from account.journal because it is a native field! (cherry picked from commit aba1448)
(cherry picked from commit 9f97c4e)
Two calls introduced by the cherry-picks of the upstream 18.0 commits. Before 17.0 the translation helper only returns the string and silently drops its arguments, so the message would have been rendered with its raw placeholders.
(cherry picked from commit d524e7b)
… 16.0
Five messages were written with the 18.0 signature _("… %(x)s", x=value). Before
17.0 the helper only returns the string and silently ignores what follows, so
those UserError messages reached the user with their raw placeholders instead of
the SIREN, the row number or the counters.
Surfaced while cherry-picking the upstream pre-commit reformatting, which moved
those calls around without changing them; the defect predates it and came from
the initial 16.0 port.
(cherry picked from commit 5b468fb)
WIP — migration of the whole
fr-einvoicingstack to 16.0, rebased onupstream/16.0.Ports the modules to 16.0: Python API gaps (
env._,read_group,button_draft,hard_lock_date,reversal_move_id…), views (<setting>→o_setting_box,attrs=,<tree>), and the tax layer rewritten on the 16.0 tax engine.Modules — all ten are
installable: Trueaccount_invoice_en16931— EN16931 Factur-X corel10n_fr_account_invoice_en16931— FR EN16931 layerl10n_fr_einvoicingl10n_fr_einvoicing_importl10n_fr_einvoicing_purchasel10n_fr_einvoicing_salel10n_fr_einvoicing_dashboard_bannerl10n_fr_einvoicing_directory_importl10n_fr_einvoicing_batch_payment(renamed froml10n_fr_einvoicing_payment_batch_oca:account_payment_batch_ocais 18.0-only, replaced byaccount_payment_order)account_invoice_en16931_py3oDependencies: plain OCA 16.0 is enough
Two prerequisites were missing from OCA 16.0 when this port started and were carried on Alusage forks. Both have since landed upstream, so no fork is needed: the VATEX codes are on
OCA/community-data-files16.0, andis_france_country/_get_siren()/_get_siret()/_get_nic()are onOCA/l10n-france16.0 since9e662b1a(27/07). Reported by @almumu, verified here.That same commit extended
_check_siretto thevatfield — a French VAT number must now end with the nine digits of the SIREN. The demo ofl10n_fr_einvoicingcarries an inconsistent pair (siren 892350455/vat FR23334175221, which belongs to SIREN 334175221), so loading it aborts the database. Fixed here asFR78892350455; the same pair is still on18.0.Rebase on
upstream/16.0The branch is rebased on the seven commits carried onto
16.0. Four of them had been backported here beforehand and were dropped in favour of upstream's own, along with the local fix for #36 — upstream's[FIX] don't store fr_einvoicing_requiredremoves the mass recompute at the root.Two of those commits are written against 18.0 APIs and needed re-porting: the twelve
self.env._()calls they add (before 17.0 the helper returns the string and silently drops its arguments), and BT-23, which now decides onproduct_id.type == "consu"— on 16.0 thestockmodule adds('product', 'Storable Product')toproduct.type, so every physical-goods invoice would go out as a mixed process (M1/M2) instead of B1/B2.Two fixes carried temporarily
#46 (#43, Discount precision) and #47 (#42, EN16931 config scope) are applied here so they can be tested on 16.0, and are proposed upstream on 18.0 where they belong. They will be dropped from this branch once merged there, or adjusted if a different shape is preferred.
Test run
Clean 16.0 community database, demo data, cascade install, plain OCA
l10n-france16.0 — so with_check_siretactually in place. All ten modules install,l10n_fr_einvoicingpasses its 17 tests, and the demo loads. Full run:31 failed, 9 errors of 1603 tests, all outside this stack (base,mail,sms,google_gmail,digest,base_vat,account_invoice_import).Context and rationale: #28