Skip to content

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

_order = "id desc"

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"
# }


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.