# account_invoice_default_journal Forces the journal of an invoice from a journal configured on its products. Two company-dependent journal fields are added on the product form, under the **Accounting** tab: - `forced_journal_id` ("Forced sale journal", restricted to sale journals), used for customer invoices and credit notes (`out_invoice`, `out_refund`). - `forced_purchase_journal_id` ("Forced purchase journal", restricted to purchase journals), used for vendor bills and refunds (`in_invoice`, `in_refund`). When Odoo looks for the default journal of an invoice (`account.move._search_default_journal()`), the journal forced by the invoice lines' products takes precedence over the company default. A move belongs to a single journal, so a `UserError` is raised if the lines force two different journals — split the document into two invoices in that case. Both fields are company-dependent: each company configures its own forced journal on the same product. ## Installation `account` is the only dependency, and it is part of Odoo core. Use the standard Odoo module installation procedure to install `account_invoice_default_journal`. ### Required access group Both fields are added inside the **Accounting** tab of the product form, which comes from `account.product_template_form_view`. In Odoo 18.0 that tab is restricted to the technical group `account.group_account_readonly`, and neither `account.group_account_invoice` ("Invoicing") nor `account.group_account_manager` ("Administrator") implies it — only `account.group_account_user` ("Show Full Accounting Features") does. Odoo 16.0 also allowed `account.group_account_invoice` on that tab, so the restriction is tighter than it used to be. On an Invoicing-only installation, meaning without the `account_accountant` module, there is no standard way to obtain that group: it has to be granted explicitly. Otherwise both fields stay invisible even though the module is correctly installed. Upgrading an Odoo 16.0 database: the values of both fields were stored in `ir.property`, a model removed in Odoo 17.0. The module ships `migrations/18.0.1.0.0/post-migration.py`, which converts them to the new `jsonb` storage automatically during the upgrade. ## Configuration No configuration is needed. Set the journal on each product that needs one; a product with no forced journal does not affect journal selection. ## Usage 1. Open the product form and go to the **Accounting** tab. 2. Set **Forced sale journal** and/or **Forced purchase journal**. The forced journal is then applied when Odoo computes the journal of a new invoice, which happens only if the invoice is created together with its lines **and** without an explicit journal in the creation values. That is the case for invoices generated from another document, such as a sale order. It is **not** the case when editing an invoice in the user interface: see the limitation below. Read it before relying on this module for manual invoicing. ## Known issues / Roadmap ### The forced journal is a default, not a constraint The module plugs into `account.move._search_default_journal()`, which supplies a **default** value. A default is overridable by design, so the forced journal is only honored on one of the three ways an invoice can get its journal: - Created in a single call, with its lines, and no `journal_id` in the creation values: the forced journal **is** applied. - Created in a single call with an explicit `journal_id` in the creation values: the forced journal is **not** applied. - Created first, then lines added afterwards: the forced journal is **not** applied. Two independent Odoo behaviors cause the two failing cases: - An explicit value in the creation values short-circuits the computation entirely, so the default is never requested. The web client sends `journal_id` because the field is present and editable in the invoice form, which puts manual invoicing in this case. - `_compute_journal_id` declares `@api.depends('move_type', 'origin_payment_id', 'statement_line_id')`. It does not depend on `invoice_line_ids`, so adding a product never invalidates the journal. It also skips any move whose journal already has a valid type, which is true of the company default sale journal. Both behaviors are identical in Odoo 16.0, so this is a limitation of the module's design rather than a regression introduced by the 18.0 migration. Lifting it means moving from a default to an actual constraint, by overriding `create` and `write`, or by refusing to save a mismatched journal. Both change behavior for existing users and neither is implemented. ### Other - `_search_default_journal()` calls `super()` first, so a company with no journal of the expected type still raises before the forced journal is considered. ## Bug Tracker Bugs are tracked on [our issues website](https://github.com/elabore-coop/account-tools/issues). In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed feedback. ## Credits ### Contributors - Stéphan SAINLEGER - [Github](https://github.com/stephansainleger) - Nicolas JEUDY - [Github](https://github.com/njeudy) ### Funders The development of this module has been financially supported by: - [Lokavaluto](https://lokavaluto.fr) - [Mycéliandre](https://myceliandre.fr) - [Elabore](https://elabore.coop) ### Maintainer This module is maintained by Elabore.