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
- Open the product form and go to the Accounting tab.
- 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_idin the creation values: the forced journal is applied. - Created in a single call with an explicit
journal_idin 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_idbecause the field is present and editable in the invoice form, which puts manual invoicing in this case. _compute_journal_iddeclares@api.depends('move_type', 'origin_payment_id', 'statement_line_id'). It does not depend oninvoice_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()callssuper()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. 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
Funders
The development of this module has been financially supported by:
Maintainer
This module is maintained by Elabore.