Hello everyone,
I’m working on a customization in Odoo 16 where I added a new field on vendor bills:
bill_merge_id = fields.Many2one(
"account.move",
string="Bill Merge",
domain="[('partner_id', '=', partner_id),
('move_type', '=', 'in_invoice'),
('company_id', '=', company_id),
('state', '!=', 'cancel')]",
)
The idea is:
When the user selects a vendor bill in bill_merge_id, the invoice lines should be replaced with the invoice lines of the selected bill.
Here is my current code:
@api.onchange('bill_merge_id')
def _onchange_bill_merge_id(self):
_logger.info("Bill merge onchange triggered")
# Prevent recursion and duplication on saving/confirm
self = self.with_context(bill_merge_skip=True)
if not self.bill_merge_id:
self.invoice_line_ids = [(5, 0, 0)]
return
if self.env.context.get("bill_merge_skip"):
return
source_bill = self.bill_merge_id
line_commands = []
for line in source_bill.invoice_line_ids:
line_commands.append((0, 0, {
'name': line.name,
'product_id': line.product_id.id,
'quantity': line.quantity,
'price_unit': line.price_unit,
'account_id': line.account_id.id,
}))
self.invoice_line_ids = line_commands
The problem
When I select a bill, the invoice lines correctly update.
But when I click Save or Confirm, Odoo duplicates the invoice lines — meaning the same lines get inserted again.
So instead of:
Line A Line B
I get:
Line A Line B Line A Line B
What I’ve tried
Clearing the lines with (5, 0, 0)
Using new_ids = [(0,0,...)] instead of create()
Adding context flags like "skip_onchange": True
The duplication still happens because Odoo re-triggers onchange during create() and write().
My Question
What is the correct Odoo 16 approach to update invoice lines based on a selected bill without causing duplication during save/confirm?
Should I:
Use a button instead of @api.onchange?
Use api.depends instead of onchange?
Use context flags in override of create() and write()?
Something else entirely?
I only want the lines to appear once — exactly when the user selects a bill — and not be duplicated later.
Any guidance or best practices would be greatly appreciated.
Thanks!
Hello,
Yes, possible but not recommended:
def write(self, vals):
if 'invoice_line_ids' in vals and self.invoice_line_ids:
vals.pop('invoice_line_ids')
return super().write(vals)
Problem: This blocks ALL line edits after first save - users can't add/remove/modify lines anymore, breaking normal invoice workflow.
Better: Use the boolean flag approach - targets only merge duplication without side effects.