Files
account-tools/account_invoice_default_journal/README.md
2026-10-20 21:02:05 +02:00

126 lines
5.4 KiB
Markdown

# 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.