Skip to content

[ADD] contract_invoice_auto_validate_send - #1501

Open
tobiaszehntner wants to merge 1 commit into
OCA:18.0from
acsone:18.0-contract-invoice-auto-validate-send
Open

[ADD] contract_invoice_auto_validate_send#1501
tobiaszehntner wants to merge 1 commit into
OCA:18.0from
acsone:18.0-contract-invoice-auto-validate-send

Conversation

@tobiaszehntner

Copy link
Copy Markdown
Contributor

This module extends Contract Invoice Auto Validate. When a contract
invoice is automatically validated, it is also sent to the customer
using their preferred method (email or Peppol), as configured on the
partner.

AI:

  • Claude Opus 4.8: Conception and writing of code

@tobiaszehntner
tobiaszehntner marked this pull request as draft August 20, 2026 10:10
@OCA-git-bot OCA-git-bot added series:18.0 mod:contract_invoice_auto_validate_send Module contract_invoice_auto_validate_send labels Aug 20, 2026
@tobiaszehntner
tobiaszehntner force-pushed the 18.0-contract-invoice-auto-validate-send branch from df7f509 to 2f90a10 Compare August 21, 2026 10:48
@tobiaszehntner
tobiaszehntner marked this pull request as ready for review August 21, 2026 10:48

@bosd bosd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small and focused, and the core API use is right: the sending_data payload is exactly what core's own account.move.send.batch.wizard writes, and is_sale_document(include_receipts=True) correctly keeps vendor bills from purchase contracts out. Two things I would like to see addressed before this goes in.

The send cron is never checked for being active

models/contract_contract.py writes sending_data and then calls _trigger(). When the cron is inactive, ir.cron._trigger_list drops the trigger without a word:

if not self.sudo().active:
    at_list = [at for at in at_list if at > now]   # at_list == [now] -> becomes []
if not at_list:
    return self.env['ir.cron.trigger']

So on any database where account.ir_cron_account_move_send has been deactivated - routine in production - every auto-validated contract invoice gets sending_data written, is_being_sent stays True forever, and nothing is ever sent. No error, no log, no trace. Silent no-op is the worst outcome for an unattended path, and it is why core blocks on this: account_move_send_batch_wizard.action_send_and_print checks account_move_send_cron.sudo().active and raises a RedirectWarning/UserError before writing sending_data.

Raising is not right here, since it would break the invoicing cron for everyone. But the check should still happen:

cron = self.env.ref("account.ir_cron_account_move_send", raise_if_not_found=False)
if not cron or not cron.sudo().active:
    _logger.warning(
        "Contract invoices are not sent: the 'Send invoices automatically' "
        "cron is not active."
    )
    return moves
to_send.sending_data = {...}
cron._trigger()

Invoicing keeps working, the invoices stay in a clean state, and the misconfiguration becomes visible. Worth a test with the cron deactivated.

Recording the cron user as the sending author

author_user_id / author_partner_id are taken from self.env.user. On the path this module actually runs on - the contract invoicing cron - that is OdooBot, and _cron_account_move_send groups by sending_data['author_partner_id'] to bus-notify the outcome. So every success and every failure notification is addressed to OdooBot, i.e. to nobody.

Attributing the send to OdooBot is not good practice: it puts a system account on record as the author of customer-facing correspondence, and it routes the only real-time feedback channel into a void. _hook_if_errors does post errors to the move chatter, so failures are recoverable after the fact, but nobody is told at the time.

The author should be a real, configurable partner - a company-level "invoice sender" setting alongside auto_send_contract_invoice, falling back to the company partner, would fit the existing configuration nicely.

Checked and cleared

Access rights are fine. ir.cron is group_system-only in ir.model.access.csv, but _trigger_list never reads the un-sudoed record (ensure_one(), then self.sudo().active and sudo().create), so a billing user manually creating invoices on a contract will not hit an AccessError.

@tobiaszehntner
tobiaszehntner force-pushed the 18.0-contract-invoice-auto-validate-send branch from 2f90a10 to dddf377 Compare September 7, 2026 10:52
send_cron = self.env.ref(
"account.ir_cron_account_move_send", raise_if_not_found=False
)
if not (send_cron and send_cron.sudo().active):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO a cron is not a business object (but a technical object who call scheduled business code).
And the user is not supposed to enable/disable the cron depending on the behavior he wants.

Instead, you can create a configuration (on company maybe) to know if you have to send or not.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a setting (auto_send_contract_invoice) and the user is not encouraged to change any cron. This is purely technical.

move.sending_data (json) is a technical field that is being treated by a cron. Here we just make sure to not set the field if the cron isn't running to treat it.

Comment thread contract_invoice_auto_validate_send/models/contract_contract.py
@tobiaszehntner
tobiaszehntner force-pushed the 18.0-contract-invoice-auto-validate-send branch from dddf377 to bf98e64 Compare September 7, 2026 12:32
@tobiaszehntner
tobiaszehntner force-pushed the 18.0-contract-invoice-auto-validate-send branch from bf98e64 to a01128f Compare September 7, 2026 15:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mod:contract_invoice_auto_validate_send Module contract_invoice_auto_validate_send series:18.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants