[ADD] contract_invoice_auto_validate_send - #1501
Conversation
df7f509 to
2f90a10
Compare
bosd
left a comment
There was a problem hiding this comment.
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.
2f90a10 to
dddf377
Compare
| 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): |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
dddf377 to
bf98e64
Compare
bf98e64 to
a01128f
Compare
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: