{"record":{"id":"2c1fe7307ac2dbde","repo":"odoo/odoo","slug":"you-cannot-modify-the-taxes-related-to-a-posted-jo","errorCode":null,"errorMessage":"You cannot modify the taxes related to a posted journal item, you should reset the journal entry to draft to do so.","messagePattern":"You cannot modify the taxes related to a posted journal item, you should reset the journal entry to draft to do so\\.","errorType":"exception","errorClass":"UserError","httpStatus":null,"severity":"error","filePath":"addons/account/models/account_move_line.py","lineNumber":1844,"sourceCode":"                \"You cannot edit the following fields: %(fields)s.\\n\"\n                \"The following entries are already hashed:\\n%(entries)s\",\n                fields=[f['string'] for f in self.fields_get(violated_fields).values()],\n                entries='\\n'.join(hashed_moves.mapped('name')),\n            ))\n\n        line_to_write = self\n        vals = self._sanitize_vals(vals)\n        matching2lines = None  # lazy cache\n        lines_to_unreconcile = self.env['account.move.line']\n        st_lines_to_unreconcile = self.env['account.bank.statement.line']\n        tax_lock_check_ids = []\n        for line in self:\n            if not any(self.env['account.move']._field_will_change(line, vals, field_name) for field_name in vals):\n                line_to_write -= line\n                continue\n\n            if line.parent_state == 'posted' and any(self.env['account.move']._field_will_change(line, vals, field_name) for field_name in ('tax_ids', 'tax_line_id')):\n                raise UserError(_('You cannot modify the taxes related to a posted journal item, you should reset the journal entry to draft to do so.'))\n\n            # Check the lock date.\n            if line.parent_state == 'posted' and any(self.env['account.move']._field_will_change(line, vals, field_name) for field_name in protected_fields['fiscal']):\n                line.move_id._check_fiscal_lock_dates()\n\n            # Check the tax lock date.\n            if line.parent_state == 'posted' and any(self.env['account.move']._field_will_change(line, vals, field_name) for field_name in protected_fields['tax']):\n                tax_lock_check_ids.append(line.id)\n\n            # Break the reconciliation.\n            if (\n                line.matching_number\n                and (changing_fields := {\n                    field_name\n                    for field_name in protected_fields['reconciliation']\n                    if self.env['account.move']._field_will_change(line, vals, field_name)\n                })\n            ):","sourceCodeStart":1826,"sourceCodeEnd":1862,"githubUrl":"https://github.com/odoo/odoo/blob/1e661df964b1b264c9cef3ab28430d4785be3fda/addons/account/models/account_move_line.py#L1826-L1862","documentation":"account.move.line.write() refuses to change tax_ids or tax_line_id on a line whose parent move is in state 'posted'. Tax fields on posted entries are immutable because they affect the tax report and legal figures; you must first reset the entry to draft.","triggerScenarios":"write({'tax_ids': [...]}) or write({'tax_line_id': id}) on an account.move.line with line.parent_state == 'posted', where _field_will_change() confirms the value actually differs. Common from UI edits, API imports, or code applying a manual tax after posting.","commonSituations":"Fixing a wrong tax on a posted invoice from a custom script; syncing tax changes from an external system into posted Odoo entries; onchange-side effects trying to recompute tax lines while the move stays posted.","solutions":["Call move.button_draft() first, apply the tax change, then re-post with move.action_post().","If the move is locked by a lock date, move the lock date or post a correcting credit note instead.","For code paths that must not unpost, filter out posted lines and report them instead of writing (see validation snippet)."],"exampleFix":"// before\ninvoice.line_ids.write({'tax_ids': [(6, 0, [tax.id])]})  # UserError if posted\n\n// after\nif invoice.state == 'posted':\n    invoice.button_draft()\ninvoice.line_ids.write({'tax_ids': [(6, 0, [tax.id])]})\ninvoice.action_post()","handlingStrategy":"validation","validationCode":"tax_vals = {k: vals[k] for k in ('tax_ids', 'tax_line_id') if k in vals}\nif tax_vals and line.parent_state == 'posted':\n    line.move_id.button_draft()\nline.write(vals)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Wrap any tax-editing automation in draft -> edit -> post.","Import pipelines should filter out tax fields when the target move is posted.","Unit-test write paths against posted moves."],"tags":["accounting","odoo","taxes","posted-state","workflow"],"backgroundTag":null,"analyzedSha":"1e661df964b1b264c9cef3ab28430d4785be3fda","analyzedAt":"2026-08-15T05:22:16.142Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}