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

5.4 KiB

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