odoo/odoo · error · UserError

You can't block a paid invoice.

Error message

You can't block a paid invoice.

What it means

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.

Source

Thrown at addons/account/models/account_move.py:6372

        moves_to_reset_draft = self.filtered(lambda x: x.state == 'posted')
        if moves_to_reset_draft:
            moves_to_reset_draft.button_draft()

        if any(move.state != 'draft' for move in self):
            raise UserError(_("Only draft journal entries can be cancelled."))

        self.line_ids.remove_move_reconcile()
        self.payment_ids.state = "canceled"
        self.write({'auto_post': 'no', 'state': 'cancel'})

    def action_toggle_block_payment(self):
        self.ensure_one()
        if self.payment_state == 'blocked':
            self.payment_state = 'not_paid'
            self.env.add_to_compute(self._fields['payment_state'], self)
        else:
            if self.payment_state in ('paid', 'in_payment'):
                raise UserError(_("You can't block a paid invoice."))
            self.payment_state = 'blocked'

    def action_activate_currency(self):
        self.currency_id.filtered(lambda currency: not currency.active).write({'active': True})

    def action_delete_duplicates(self):
        for move in self:
            move.duplicated_ref_ids.unlink()

    def _get_mail_template(self):
        """
        :return: the correct mail template based on the current move type
        """
        template_xmlid = 'account.email_template_edi_invoice'
        if all(move.move_type == 'out_refund' for move in self):
            template_xmlid = 'account.email_template_edi_credit_note'
        elif all(move.move_type == 'in_invoice' and move.journal_id.is_self_billing for move in self):
            template_xmlid = 'account.email_template_edi_self_billing_invoice'

View on GitHub (pinned to 1e661df964)

Solutions

  1. Refresh the record and re-check payment_state before toggling; only block when state is 'not_paid', 'partial' or similar open state.
  2. If the intent is to undo a payment, unreconcile/cancel the payment (payment_ids) instead of blocking the invoice.
  3. In custom wizards, guard: if move.payment_state in ('paid', 'in_payment'): skip or inform the user.

Example fix

# before
invoice.action_toggle_block_payment()

# after
if invoice.payment_state not in ('paid', 'in_payment'):
    invoice.action_toggle_block_payment()
Defensive patterns

Strategy: validation

Validate before calling

if invoice.payment_state in ('paid', 'in_payment'):
    raise UserError(_("Invoice %s is already paid; cannot block.", invoice.name))
invoice.action_toggle_block_payment()

Try / catch

from odoo.exceptions import UserError
try:
    invoice.action_toggle_block_payment()
except UserError as e:
    if "can't block a paid invoice" in str(e):
        invoice.invalidate_recordset()  # stale UI data; refresh and inform user
        raise
    raise

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of odoo/odoo@1e661df964 (2026-08-15). Data as JSON: /api/errors/c2ff87d463d59c29. Report an issue: GitHub.