Billing Note Documentation¶
Overview¶
The Billing Note model (bill.note) groups multiple customer invoices for consolidated billing and payment processing. It provides a way to bundle invoices together, track combined totals, and facilitate batch payment collection.
Model Information¶
Model Name: bill.note
Display Name: Billing Note
Name Field: number
Key Fields: None (no unique constraint defined)
Features¶
- ✅ Audit logging enabled (
_audit_log) - ❌ Multi-company support (
company_id) - ❌ Full-text content search (
_content_search) - ✅ Automatic sequence numbering
- ✅ Computed totals from linked invoices
- ✅ One-click payment creation
Key Fields Reference¶
Header Fields¶
| Field | Type | Required | Description |
|---|---|---|---|
number |
Char | ✅ | Billing note number (auto-generated) |
date |
Date | ✅ | Billing note date |
customer_id |
Many2One | ✅ | Customer contact |
remarks |
Text | ❌ | Additional remarks/notes |
company_id |
Many2One | ❌ | Company |
Computed Fields¶
| Field | Type | Description |
|---|---|---|
amount_total |
Decimal | Sum of all invoice totals |
amount_due |
Decimal | Sum of all invoice due amounts |
Relationship Fields¶
| Field | Type | Description |
|---|---|---|
invoices |
One2Many | Customer invoices in this billing note |
payments |
One2Many | Payments referencing this billing note |
Default Values¶
_defaults = {
"number": _get_number, # Auto-generated sequence
"date": lambda *a: time.strftime("%Y-%m-%d"), # Today
"company_id": lambda *a: access.get_active_company(),
}
Default Ordering¶
Records are ordered by ID descending (newest first):
API Methods¶
1. Create Billing Note¶
Method: create(vals, context)
Creates a new billing note with auto-generated number.
Parameters:
vals = {
"customer_id": customer_id, # Required: Customer
"date": "2024-12-15", # Optional: Date (defaults to today)
"remarks": "December invoices", # Optional: Remarks
}
Returns: int - New record ID
Example:
bill_note_id = get_model("bill.note").create({
"customer_id": customer_id,
"remarks": "Q4 2024 Invoices",
})
2. Get Total¶
Method: get_total(ids, context)
Calculates totals from linked invoices.
Returns:
{
"amount_total": sum(invoice.amount_total for all invoices),
"amount_due": sum(invoice.amount_due for all invoices),
}
Example:
totals = get_model("bill.note").get_total([bill_note_id])
print(f"Total: {totals[bill_note_id]['amount_total']}")
print(f"Due: {totals[bill_note_id]['amount_due']}")
3. Copy to Payment¶
Method: copy_to_payment(ids, context)
Creates a payment record pre-populated with all invoices from the billing note.
Behavior:
1. Creates incoming payment (type = "in")
2. Adds payment lines for each invoice
3. Sets full invoice amounts as defaults
4. Links payment to billing note via related_id
Returns: Navigation to payment form with alert message
Example:
result = get_model("bill.note").copy_to_payment([bill_note_id])
# Returns: {
# "next": {"name": "payment", "mode": "form", "active_id": payment_id},
# "alert": "Payment XXX copied from billing note"
# }
Related Models¶
| Model | Relationship | Description |
|---|---|---|
account.invoice |
One2Many (invoices) | Invoices in billing note |
account.payment |
One2Many (payments) | Payments for this billing note |
contact |
Many2One (customer_id) | Customer |
company |
Many2One (company_id) | Company |
Common Use Cases¶
Use Case 1: Create Monthly Billing Note¶
# Find customer's unpaid invoices for the month
customer_id = 123
invoices = get_model("account.invoice").search_browse([
["contact_id", "=", customer_id],
["type", "=", "out"],
["state", "=", "waiting_payment"],
["date", ">=", "2024-12-01"],
["date", "<=", "2024-12-31"],
])
if invoices:
# Create billing note
bill_note_id = get_model("bill.note").create({
"customer_id": customer_id,
"remarks": "December 2024 Invoices",
})
# Link invoices
invoice_ids = [inv.id for inv in invoices]
get_model("account.invoice").write(invoice_ids, {
"bill_note_id": bill_note_id
})
# Get totals
note = get_model("bill.note").browse([bill_note_id])[0]
print(f"Billing Note: {note.number}")
print(f"Invoices: {len(note.invoices)}")
print(f"Total: {note.amount_total}")
Use Case 2: Process Payment from Billing Note¶
# Create payment for entire billing note
bill_note = get_model("bill.note").browse([bill_note_id])[0]
# Use copy_to_payment to create pre-filled payment
result = get_model("bill.note").copy_to_payment([bill_note_id])
# Payment is created with all invoices, ready to post
payment_id = result["next"]["active_id"]
get_model("account.payment").post([payment_id])
Use Case 3: Generate Billing Note Report¶
# Get billing note summary
bill_note = get_model("bill.note").browse([bill_note_id])[0]
print(f"Billing Note: {bill_note.number}")
print(f"Customer: {bill_note.customer_id.name}")
print(f"Date: {bill_note.date}")
print(f"Remarks: {bill_note.remarks}")
print("-" * 50)
print("Invoices:")
for inv in bill_note.invoices:
print(f" {inv.number}: {inv.amount_total:,.2f} (Due: {inv.amount_due:,.2f})")
print("-" * 50)
print(f"Total Amount: {bill_note.amount_total:,.2f}")
print(f"Total Due: {bill_note.amount_due:,.2f}")
Use Case 4: Track Payment Status¶
# Check payment status for billing note
bill_note = get_model("bill.note").browse([bill_note_id])[0]
total_paid = sum(pmt.amount for pmt in bill_note.payments if pmt.state == "posted")
remaining = bill_note.amount_due
print(f"Billing Note: {bill_note.number}")
print(f"Total: {bill_note.amount_total:,.2f}")
print(f"Paid: {total_paid:,.2f}")
print(f"Remaining: {remaining:,.2f}")
if remaining == 0:
print("Status: FULLY PAID")
elif total_paid > 0:
print("Status: PARTIALLY PAID")
else:
print("Status: UNPAID")
Billing Note Flow¶
1. Identify invoices to bundle
│
2. Create billing note
│
3. Link invoices to billing note
│
4. Send billing note to customer
│
5. Customer makes payment
│
├─ Option A: Use copy_to_payment
│ └─ Creates payment with all invoices
│
└─ Option B: Manual payment
└─ Reference billing note
│
6. Track payment status via amount_due
Best Practices¶
1. Group Invoices by Customer¶
# Good: All invoices same customer
bill_note = get_model("bill.note").create({
"customer_id": customer_id,
})
# Only link invoices for this customer
for inv in invoices:
if inv.contact_id.id == customer_id:
inv.write({"bill_note_id": bill_note_id})
2. Add Meaningful Remarks¶
# Good: Clear remarks
bill_note = get_model("bill.note").create({
"customer_id": customer_id,
"remarks": "Project ABC - Phase 2 Invoices (Dec 2024)",
})
3. Use Copy to Payment for Efficiency¶
# Good: Use built-in function
result = get_model("bill.note").copy_to_payment([bill_note_id])
# Payment is pre-filled with all invoices
Troubleshooting¶
"No invoices in billing note"¶
Cause: No invoices linked via bill_note_id
Solution: Link invoices: invoice.write({"bill_note_id": bill_note_id})
"Amount total is 0"¶
Cause: No invoices or all invoices have amount_total = 0 Solution: Verify invoices are posted and have amounts
"Sequence not generating"¶
Cause: Sequence type "bill_note" not configured Solution: Create sequence with type = "bill_note"
Testing Examples¶
Unit Test: Billing Note Totals¶
def test_billing_note_totals():
# Create billing note
bill_note_id = get_model("bill.note").create({
"customer_id": customer_id,
})
# Create and link invoices
for amount in [100, 200, 300]:
inv_id = get_model("account.invoice").create({
"type": "out",
"contact_id": customer_id,
"bill_note_id": bill_note_id,
"lines": [("create", {
"description": "Test",
"qty": 1,
"unit_price": amount,
})]
})
get_model("account.invoice").post([inv_id])
# Verify totals
note = get_model("bill.note").browse([bill_note_id])[0]
assert len(note.invoices) == 3
assert note.amount_total == 600.00
Security Considerations¶
Permission Model¶
- Billing note access linked to invoice permissions
- Payment creation requires payment permissions
Data Access¶
- Multi-company support via company_id
- Customer filtering via customer_id
Configuration Requirements¶
| Setting | Type | Description |
|---|---|---|
| Sequence (type="bill_note") | Sequence | Auto-numbering for billing notes |
Version History¶
Last Updated: December 2024 Model Version: bill_note.py Framework: Netforce
Additional Resources¶
- Invoice Documentation:
account.invoice - Payment Documentation:
account.payment - Contact Documentation:
contact
This documentation is generated for developer onboarding and reference purposes.