{"record":{"id":"c2ff87d463d59c29","repo":"odoo/odoo","slug":"you-can-t-block-a-paid-invoice","errorCode":null,"errorMessage":"You can't block a paid invoice.","messagePattern":"You can't block a paid invoice\\.","errorType":"exception","errorClass":"UserError","httpStatus":null,"severity":"error","filePath":"addons/account/models/account_move.py","lineNumber":6372,"sourceCode":"        moves_to_reset_draft = self.filtered(lambda x: x.state == 'posted')\n        if moves_to_reset_draft:\n            moves_to_reset_draft.button_draft()\n\n        if any(move.state != 'draft' for move in self):\n            raise UserError(_(\"Only draft journal entries can be cancelled.\"))\n\n        self.line_ids.remove_move_reconcile()\n        self.payment_ids.state = \"canceled\"\n        self.write({'auto_post': 'no', 'state': 'cancel'})\n\n    def action_toggle_block_payment(self):\n        self.ensure_one()\n        if self.payment_state == 'blocked':\n            self.payment_state = 'not_paid'\n            self.env.add_to_compute(self._fields['payment_state'], self)\n        else:\n            if self.payment_state in ('paid', 'in_payment'):\n                raise UserError(_(\"You can't block a paid invoice.\"))\n            self.payment_state = 'blocked'\n\n    def action_activate_currency(self):\n        self.currency_id.filtered(lambda currency: not currency.active).write({'active': True})\n\n    def action_delete_duplicates(self):\n        for move in self:\n            move.duplicated_ref_ids.unlink()\n\n    def _get_mail_template(self):\n        \"\"\"\n        :return: the correct mail template based on the current move type\n        \"\"\"\n        template_xmlid = 'account.email_template_edi_invoice'\n        if all(move.move_type == 'out_refund' for move in self):\n            template_xmlid = 'account.email_template_edi_credit_note'\n        elif all(move.move_type == 'in_invoice' and move.journal_id.is_self_billing for move in self):\n            template_xmlid = 'account.email_template_edi_self_billing_invoice'","sourceCodeStart":6354,"sourceCodeEnd":6390,"githubUrl":"https://github.com/odoo/odoo/blob/1e661df964b1b264c9cef3ab28430d4785be3fda/addons/account/models/account_move.py#L6354-L6390","documentation":"Raised by account.move.action_toggle_block_payment() when a user tries to set the 'blocked' payment state on an invoice whose payment_state is already 'paid' or 'in_payment'. Blocking payments only makes sense for open invoices; once payments are (being) registered, the toggle refuses to mark it blocked.","triggerScenarios":"Clicking the 'Block Payment' toggle (or calling action_toggle_block_payment() over RPC) on an invoice with payment_state in ('paid', 'in_payment'). This state is reached when the invoice is fully reconciled with payments or a payment is in transit (in_payment via bank SUSPENSE).","commonSituations":"UI race: the invoice gets paid between page load and the user clicking the toggle; automated scripts flipping payment_state on old records; user trying to 'undo' a payment by blocking instead of unreconciling the payment.","solutions":["Refresh the record and re-check payment_state before toggling; only block when state is 'not_paid', 'partial' or similar open state.","If the intent is to undo a payment, unreconcile/cancel the payment (payment_ids) instead of blocking the invoice.","In custom wizards, guard: if move.payment_state in ('paid', 'in_payment'): skip or inform the user."],"exampleFix":"# before\ninvoice.action_toggle_block_payment()\n\n# after\nif invoice.payment_state not in ('paid', 'in_payment'):\n    invoice.action_toggle_block_payment()","handlingStrategy":"validation","validationCode":"if invoice.payment_state in ('paid', 'in_payment'):\n    raise UserError(_(\"Invoice %s is already paid; cannot block.\", invoice.name))\ninvoice.action_toggle_block_payment()","typeGuard":null,"tryCatchPattern":"from odoo.exceptions import UserError\ntry:\n    invoice.action_toggle_block_payment()\nexcept UserError as e:\n    if \"can't block a paid invoice\" in str(e):\n        invoice.invalidate_recordset()  # stale UI data; refresh and inform user\n        raise\n    raise","preventionTips":["Hide the block-payment toggle when payment_state is 'paid'/'in_payment'.","Refresh the form before toggling; payments may have registered since page load.","To undo a payment, unreconcile the payment instead of blocking."],"tags":["accounting","payment","state-machine","usererror"],"backgroundTag":null,"analyzedSha":"1e661df964b1b264c9cef3ab28430d4785be3fda","analyzedAt":"2026-08-15T05:22:16.142Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}