Skip to content

Payment Term Documentation

Overview

The Payment Term module (payment.term) defines payment conditions for invoices and purchase orders, specifying when payment is due. Payment terms are simple reference data used throughout the sales and purchasing processes to standardize credit terms with customers and suppliers.


Model Information

Model Name: payment.term Display Name: Payment Term Name Field: name Key Fields: None (allows duplicate names) Default Sort Order: sequence, name

Features

  • ✅ Simple reference data model
  • ✅ Sequence-based ordering
  • ✅ Days-based payment calculation
  • ✅ Used in invoices and purchase orders
  • ✅ Standard credit terms management

Understanding Payment Terms

What is a Payment Term?

A Payment Term defines when a customer must pay an invoice or when you must pay a supplier. Common examples: - Immediate - Payment on delivery (COD) - Net 30 - Payment due in 30 days - Net 60 - Payment due in 60 days - Net 15 - Payment due in 15 days - Due on Receipt - Payment due immediately

How Payment Terms Work

Invoice Date: 2025-01-01
Payment Term: Net 30 (30 days)
Due Date: 2025-01-31

Payment terms are typically: 1. Set as default on customer/supplier records 2. Applied automatically when creating invoices/bills 3. Used to calculate due dates 4. Referenced in aging reports


Key Fields Reference

Core Fields

Field Type Required Description
name Char Payment term name (e.g., "Net 30")
description Text Detailed description of terms
sequence Integer Display order (lower numbers first)
days Integer Number of days until payment due

Field Details

name: - Descriptive term name - Shown in dropdowns and reports - No uniqueness constraint (though unique names recommended) - Examples: "Net 30", "Net 60", "COD", "Due on Receipt"

description: - Optional detailed explanation - Can include special conditions - Not typically shown in UI dropdowns

sequence: - Controls display order in lists - Lower numbers appear first - Useful for prioritizing common terms

days: - Number of days from invoice date to due date - Used for automatic due date calculation - Can be 0 for immediate payment - Negative values possible for advance payment


API Methods

1. Create Payment Term

Method: create(vals, context)

Creates a new payment term.

Parameters:

vals = {
    "name": "Net 30",              # Required
    "description": "Payment due within 30 days",
    "sequence": 10,                # Optional
    "days": 30                     # Optional
}

Returns: int - New payment term ID

Example:

# Create standard terms
net30_id = get_model("payment.term").create({
    "name": "Net 30",
    "description": "Payment due within 30 days from invoice date",
    "sequence": 20,
    "days": 30
})

net60_id = get_model("payment.term").create({
    "name": "Net 60",
    "description": "Payment due within 60 days from invoice date",
    "sequence": 30,
    "days": 60
})

cod_id = get_model("payment.term").create({
    "name": "Cash on Delivery",
    "description": "Payment due immediately upon delivery",
    "sequence": 10,
    "days": 0
})


Search Functions

Find Term by Name

# Find Net 30 term
terms = get_model("payment.term").search_browse([
    ["name", "=", "Net 30"]
])

if terms:
    term = terms[0]
    print(f"{term.name}: {term.days} days")

Get All Terms (Ordered)

# Get all payment terms in display order
terms = get_model("payment.term").search_browse([])

print("Available Payment Terms:")
for term in terms:
    days_text = f"{term.days} days" if term.days else "Immediate"
    print(f"  {term.name}: {days_text}")

Search by Days

# Find all 30-day terms
terms_30 = get_model("payment.term").search_browse([
    ["days", "=", 30]
])

# Find terms over 30 days
long_terms = get_model("payment.term").search_browse([
    ["days", ">", 30]
])

Best Practices

1. Use Standard Names

# Good: Clear, standard names
"Net 30"
"Net 60"
"Cash on Delivery"
"Due on Receipt"
"Net 15"

# Less clear: Ambiguous names
"30 Days"
"Standard"
"Quick"

2. Set Logical Sequences

# Good: Ordered by payment urgency
{
    "name": "Cash on Delivery",
    "sequence": 10,
    "days": 0
}
{
    "name": "Net 15",
    "sequence": 20,
    "days": 15
}
{
    "name": "Net 30",
    "sequence": 30,
    "days": 30
}
{
    "name": "Net 60",
    "sequence": 40,
    "days": 60
}

3. Provide Clear Descriptions

# Good: Detailed description
{
    "name": "Net 30",
    "description": "Payment due within 30 days from invoice date. "
                   "Late payment subject to 1.5% monthly interest."
}

# Minimal: Just basics
{
    "name": "Net 30",
    "description": "30 day payment term"
}

Database Constraints

No explicit constraints defined in the model.

Recommendations: - Consider adding unique constraint on name - Validate days >= 0 in application logic if needed


Model Relationship Description
contact Referenced Customers/suppliers have default payment terms
account.invoice Referenced Invoices use payment terms
sale.order Referenced Sales orders may use payment terms
purchase.order Referenced Purchase orders may use payment terms

Common Use Cases

Use Case 1: Setup Standard Payment Terms

# Create standard set of payment terms

standard_terms = [
    {
        "name": "Cash on Delivery",
        "description": "Payment due immediately upon delivery",
        "sequence": 10,
        "days": 0
    },
    {
        "name": "Net 15",
        "description": "Payment due within 15 days from invoice date",
        "sequence": 20,
        "days": 15
    },
    {
        "name": "Net 30",
        "description": "Payment due within 30 days from invoice date",
        "sequence": 30,
        "days": 30
    },
    {
        "name": "Net 45",
        "description": "Payment due within 45 days from invoice date",
        "sequence": 40,
        "days": 45
    },
    {
        "name": "Net 60",
        "description": "Payment due within 60 days from invoice date",
        "sequence": 50,
        "days": 60
    },
    {
        "name": "Net 90",
        "description": "Payment due within 90 days from invoice date",
        "sequence": 60,
        "days": 90
    }
]

for term_data in standard_terms:
    # Check if exists
    existing = get_model("payment.term").search([
        ["name", "=", term_data["name"]]
    ])

    if not existing:
        term_id = get_model("payment.term").create(term_data)
        print(f"✓ Created: {term_data['name']}")
    else:
        print(f"⊙ Exists: {term_data['name']}")

Use Case 2: Calculate Due Date

# Calculate invoice due date from payment term
from datetime import datetime, timedelta

invoice_date = datetime.strptime("2025-01-15", "%Y-%m-%d")
payment_term_id = net30_id

# Get payment term
term = get_model("payment.term").browse([payment_term_id])[0]

# Calculate due date
if term.days:
    due_date = invoice_date + timedelta(days=term.days)
else:
    due_date = invoice_date  # Immediate payment

print(f"Invoice Date: {invoice_date.strftime('%Y-%m-%d')}")
print(f"Payment Term: {term.name}")
print(f"Due Date: {due_date.strftime('%Y-%m-%d')}")
# Output:
# Invoice Date: 2025-01-15
# Payment Term: Net 30
# Due Date: 2025-02-14

Use Case 3: Set Default Payment Term on Customer

# Set default payment term for customer

customer_id = 123
net30_id = get_model("payment.term").search([
    ["name", "=", "Net 30"]
])[0]

get_model("contact").write([customer_id], {
    "payment_term_id": net30_id
})

print(f"✓ Default payment term set to Net 30")

# Now all invoices for this customer will default to Net 30

Use Case 4: Payment Terms Report

# Generate report of all payment terms

terms = get_model("payment.term").search_browse([], order="sequence,name")

print("PAYMENT TERMS CONFIGURATION")
print("=" * 60)
print(f"{'Name':30} {'Days':>10} {'Sequence':>10}")
print("-" * 60)

for term in terms:
    days_display = str(term.days) if term.days else "Immediate"
    seq_display = str(term.sequence) if term.sequence else "-"
    print(f"{term.name:30} {days_display:>10} {seq_display:>10}")

print("=" * 60)
print(f"Total Terms: {len(terms)}")

Use Case 5: Aging Report by Payment Terms

# Analyze overdue invoices by payment term

from datetime import date

today = date.today()

terms = get_model("payment.term").search_browse([])

print("OVERDUE ANALYSIS BY PAYMENT TERM")
print("=" * 80)

for term in terms:
    # Find invoices with this term
    invoices = get_model("account.invoice").search_browse([
        ["payment_term_id", "=", term.id],
        ["state", "=", "waiting_payment"],
        ["due_date", "<", str(today)]
    ])

    if invoices:
        total_overdue = sum(inv.amount_due for inv in invoices)
        print(f"\n{term.name}:")
        print(f"  Overdue Invoices: {len(invoices)}")
        print(f"  Total Overdue: ${total_overdue:,.2f}")

        for inv in invoices:
            days_overdue = (today - datetime.strptime(inv.due_date, "%Y-%m-%d").date()).days
            print(f"    {inv.number}: ${inv.amount_due:,.2f} ({days_overdue} days overdue)")

Performance Tips

1. Index on Name Field

CREATE INDEX idx_payment_term_name ON payment_term(name);
CREATE INDEX idx_payment_term_sequence ON payment_term(sequence);

2. Cache Common Terms

# Cache frequently used terms
term_cache = {}

def get_payment_term(name):
    if name not in term_cache:
        terms = get_model("payment.term").search_browse([
            ["name", "=", name]
        ])
        term_cache[name] = terms[0] if terms else None
    return term_cache[name]

# Use cached lookup
net30 = get_payment_term("Net 30")

Troubleshooting

"Payment term not found"

Cause: Term hasn't been created yet Solution: Create the payment term:

term_id = get_model("payment.term").create({
    "name": "Net 30",
    "days": 30
})

"Wrong due date calculated"

Cause: Days value incorrect or not set Solution: Verify days field:

term = get_model("payment.term").browse([term_id])[0]
print(f"Days: {term.days}")

# Update if needed
get_model("payment.term").write([term_id], {"days": 30})

"Terms not showing in correct order"

Cause: Sequence values not set Solution: Set sequence values:

# Update sequences
terms = [
    ("Cash on Delivery", 10),
    ("Net 15", 20),
    ("Net 30", 30),
    ("Net 60", 40)
]

for name, seq in terms:
    term_ids = get_model("payment.term").search([["name", "=", name]])
    if term_ids:
        get_model("payment.term").write(term_ids, {"sequence": seq})


Testing Examples

Unit Test: Payment Term Creation

def test_payment_term():
    # Create test term
    term_id = get_model("payment.term").create({
        "name": "Test Net 30",
        "description": "Test payment term",
        "sequence": 100,
        "days": 30
    })

    # Verify
    term = get_model("payment.term").browse([term_id])[0]
    assert term.name == "Test Net 30"
    assert term.days == 30
    assert term.sequence == 100

    # Test due date calculation
    from datetime import datetime, timedelta
    invoice_date = datetime(2025, 1, 15)
    due_date = invoice_date + timedelta(days=term.days)

    assert due_date == datetime(2025, 2, 14)

    print("✓ Payment term test passed")

    # Cleanup
    get_model("payment.term").delete([term_id])

Security Considerations

Permission Model

  • Term creation typically restricted to accounting/admin users
  • Terms are global reference data (not company-specific)
  • Changes affect all invoices using the term

Data Integrity

  • Validate days value is reasonable (0-365 typical range)
  • Avoid deleting terms still referenced by invoices
  • Consider making terms inactive instead of deleting

Integration Points

Internal Modules

  • contact: Default payment terms for customers/suppliers
  • account.invoice: Invoice payment terms and due dates
  • sale.order: Sales order payment conditions
  • purchase.order: Purchase order payment conditions

External Systems

  • ERP systems (payment term mapping)
  • Aging reports (term-based analysis)
  • Collection systems (term tracking)

Version History

Last Updated: 2025-12-16 Model Version: payment_term.py Framework: Netforce


Additional Resources

  • Invoice Documentation: account.invoice
  • Contact Documentation: contact
  • Sales Order Documentation: sale.order
  • Purchase Order Documentation: purchase.order

This documentation is generated for developer onboarding and reference purposes.