Odoo 15:如何通过Python XML-RPC API为发票关联指定未结付款(模拟界面添加按钮功能)
Got it, let's walk through exactly how to link that existing customer payment (your unapplied credit, or crédits en circulation) to a newly created invoice using Odoo 15's XML-RPC API. I’ve worked through this exact workflow before, so here’s the step-by-step breakdown that mirrors what the UI’s "Ajouter" button does under the hood.
First, a quick note on the Odoo logic: When you click "Ajouter" in the invoice UI to attach an unapplied payment, Odoo is actually reconciling the payment's accounting lines with the invoice's receivable lines. So our API approach needs to replicate that reconciliation process, not just add the payment to a field on the invoice.
Step 1: Set Up Your XML-RPC Connection
Assuming you already have this, but here’s a quick reminder for context:
import xmlrpc.client # Replace these with your Odoo instance details ODOO_URL = "https://your-odoo-domain.com" ODOO_DB = "your-database-name" ODOO_USER = "your-username" ODOO_PASS = "your-password" # Authenticate and get the model proxy common = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/common") uid = common.authenticate(ODOO_DB, ODOO_USER, ODOO_PASS, {}) models = xmlrpc.client.ServerProxy(f"{ODOO_URL}/xmlrpc/2/object")
Step 2: Validate Your Payment and Invoice
Before doing anything, make sure:
- The payment is a posted, unreconciled inbound customer payment (matches your "unapplied credit" status)
- The payment and invoice belong to the same customer
Add these checks to avoid errors:
# Replace with your existing payment ID and newly created invoice ID PAYMENT_ID = 123 INVOICE_ID = 456 # Fetch payment details to validate payment = models.execute_kw(ODOO_DB, uid, ODOO_PASS, 'account.payment', 'read', [[PAYMENT_ID], ['payment_type', 'state', 'reconciled', 'partner_id', 'move_id']])[0] # Validate payment status if payment['payment_type'] != 'inbound' or payment['state'] != 'posted' or payment['reconciled']: raise ValueError("Payment must be a posted, unreconciled inbound customer payment.") # Validate customer match between payment and invoice invoice_partner = models.execute_kw(ODOO_DB, uid, ODOO_PASS, 'account.move', 'read', [[INVOICE_ID], ['partner_id']])[0]['partner_id'][0] if payment['partner_id'][0] != invoice_partner: raise ValueError("Payment and invoice must belong to the same customer.")
Step 3: Fetch the Reconciliation Lines
We need to get the specific accounting lines from both the payment and invoice that need to be reconciled:
- Invoice's receivable line: The line representing the customer's outstanding balance on the invoice
- Payment's unapplied lines: The lines representing the unapplied credit from the payment
# Get the invoice's unreconciled receivable line invoice_receivable_line = models.execute_kw(ODOO_DB, uid, ODOO_PASS, 'account.move.line', 'search_read', [[ ['move_id', '=', INVOICE_ID], ['account_id.internal_type', '=', 'receivable'], ['reconciled', '=', False] ]], ['id'])[0] invoice_line_id = invoice_receivable_line['id'] # Get the payment's unreconciled accounting lines payment_lines = models.execute_kw(ODOO_DB, uid, ODOO_PASS, 'account.move.line', 'search_read', [[ ['move_id', '=', payment['move_id'][0]], ['reconciled', '=', False] ]], ['id']) payment_line_ids = [line['id'] for line in payment_lines]
Step 4: Reconcile the Lines
This is the key step that mimics the UI's "Ajouter" button. We’ll call Odoo’s built-in reconciliation method to link the payment lines to the invoice line:
# Reconcile the payment lines with the invoice's receivable line models.execute_kw(ODOO_DB, uid, ODOO_PASS, 'account.move.line', 'reconcile', [payment_line_ids + [invoice_line_id]])
Notes for Edge Cases
- Partial Reconciliation: If the payment amount is larger than the invoice, you can pass additional parameters to the
reconcilemethod to handle the leftover credit (likewriteoff_amountorwriteoff_account_id). - Multi-Currency: If dealing with multi-currency, ensure the payment and invoice currencies match, or add currency handling logic to the reconciliation call.
内容的提问来源于stack exchange,提问作者VictorYvergniaux

