Skip to content

Payment Category Documentation

Overview

The Payment Category model (payment.category) provides a simple way to categorize and organize payment records. Categories help with reporting, filtering, and analysis by grouping similar types of payments together.


Model Information

Model Name: payment.category Display Name: Payment Category Key Fields: None (no unique constraint defined)

Features

  • ❌ Audit logging enabled (_audit_log)
  • ❌ Multi-company support (company_id)
  • ❌ Full-text content search (_content_search)
  • ✅ Active/Inactive flag for soft delete
  • ✅ Simple lookup model for categorization

Key Fields Reference

All Fields

Field Type Required Description
name Char Category name (searchable)
description Text Detailed description of the category
active Boolean Whether category is active (for filtering)

API Methods

1. Create Category

Method: create(vals, context)

Creates a new payment category.

Parameters:

vals = {
    "name": "Supplier Payments",         # Required: Category name
    "description": "Payments to suppliers and vendors",  # Optional
    "active": True,                       # Optional: Defaults to active
}

Returns: int - New record ID

Example:

categ_id = get_model("payment.category").create({
    "name": "Payroll",
    "description": "Employee salary and wage payments",
    "active": True,
})


2. Search Categories

Method: search(condition, context)

Search for payment categories.

Example:

# Find all active categories
active_categs = get_model("payment.category").search([["active", "=", True]])

# Find category by name
categ_ids = get_model("payment.category").search([["name", "=", "Payroll"]])

# Find categories with partial name match
categ_ids = get_model("payment.category").search([["name", "ilike", "%supplier%"]])


3. Browse Categories

Method: browse(ids, context)

Retrieve category records for reading.

Example:

categs = get_model("payment.category").browse(categ_ids)
for categ in categs:
    print(f"Category: {categ.name}")
    print(f"Description: {categ.description}")
    print(f"Active: {categ.active}")


4. Update Category

Method: write(ids, vals, context)

Update existing category records.

Example:

# Update description
get_model("payment.category").write([categ_id], {
    "description": "Updated description"
})

# Deactivate category (soft delete)
get_model("payment.category").write([categ_id], {
    "active": False
})


5. Delete Category

Method: delete(ids, context)

Delete payment categories.

Example:

# Prefer soft delete (set active=False) over hard delete
get_model("payment.category").write([categ_id], {"active": False})

# Or hard delete if necessary
get_model("payment.category").delete([categ_id])


Model Relationship Description
account.payment Many2One (payment_category_id) Payments linked to this category

Note: The relationship field payments is currently commented out in the model.


Common Use Cases

Use Case 1: Set Up Standard Payment Categories

# Create standard payment categories
categories = [
    {"name": "Supplier Payments", "description": "Payments to vendors and suppliers", "active": True},
    {"name": "Payroll", "description": "Employee salaries and wages", "active": True},
    {"name": "Tax Payments", "description": "Tax remittances to authorities", "active": True},
    {"name": "Utilities", "description": "Electricity, water, internet bills", "active": True},
    {"name": "Rent & Lease", "description": "Property rental and lease payments", "active": True},
    {"name": "Customer Refunds", "description": "Refunds issued to customers", "active": True},
    {"name": "Intercompany", "description": "Transfers between group companies", "active": True},
    {"name": "Miscellaneous", "description": "Other uncategorized payments", "active": True},
]

for cat in categories:
    existing = get_model("payment.category").search([["name", "=", cat["name"]]])
    if not existing:
        get_model("payment.category").create(cat)

Use Case 2: Get Active Categories for Dropdown

# Get all active categories for a selection field
categs = get_model("payment.category").search_browse([["active", "=", True]])

options = []
for categ in categs:
    options.append({
        "id": categ.id,
        "name": categ.name,
    })

Use Case 3: Categorize Payment Reports

# Get payments grouped by category
from collections import defaultdict

# Assuming payments have payment_category_id field
payments = get_model("account.payment").search_browse([
    ["date", ">=", "2024-01-01"],
    ["date", "<=", "2024-12-31"],
    ["state", "=", "posted"]
])

by_category = defaultdict(lambda: {"count": 0, "total": 0})

for pmt in payments:
    cat_name = pmt.payment_category_id.name if pmt.payment_category_id else "Uncategorized"
    by_category[cat_name]["count"] += 1
    by_category[cat_name]["total"] += pmt.amount

print("Payments by Category (2024):")
for cat, data in sorted(by_category.items()):
    print(f"  {cat}: {data['count']} payments, {data['total']:,.2f} total")

Use Case 4: Archive Unused Categories

# Find and deactivate categories with no payments
categs = get_model("payment.category").search_browse([["active", "=", True]])

for categ in categs:
    # Check if any payments use this category
    payments = get_model("account.payment").search([
        ["payment_category_id", "=", categ.id]
    ])

    if not payments:
        categ.write({"active": False})
        print(f"Deactivated unused category: {categ.name}")

Best Practices

1. Use Descriptive Names

# Good: Clear and specific
get_model("payment.category").create({
    "name": "International Wire Transfers",
    "description": "Overseas payments via SWIFT/wire transfer"
})

# Bad: Vague name
get_model("payment.category").create({
    "name": "Other"
})

2. Use Soft Delete

# Good: Preserve history by deactivating
get_model("payment.category").write([categ_id], {"active": False})

# Avoid: Hard delete loses referential integrity
get_model("payment.category").delete([categ_id])

3. Maintain Consistent Naming

  • Use Title Case for category names
  • Keep names concise (2-4 words)
  • Add detailed descriptions for clarity

Troubleshooting

"Category name is required"

Cause: Creating category without a name Solution: Always provide the name field

"Category not showing in dropdown"

Cause: Category is inactive Solution: Set active = True or include inactive in search

"Cannot delete category with linked payments"

Cause: Attempting to delete category referenced by payments Solution: Use soft delete (active=False) or reassign payments first


Testing Examples

Unit Test: Create and Query Category

def test_payment_category():
    # Create category
    categ_id = get_model("payment.category").create({
        "name": "Test Category",
        "description": "For testing",
        "active": True,
    })

    # Verify creation
    categ = get_model("payment.category").browse([categ_id])[0]
    assert categ.name == "Test Category"
    assert categ.active == True

    # Test soft delete
    categ.write({"active": False})
    categ = get_model("payment.category").browse([categ_id])[0]
    assert categ.active == False

    # Cleanup
    get_model("payment.category").delete([categ_id])

Security Considerations

Permission Model

  • Read access typically granted to all accounting users
  • Write/Delete access restricted to accounting managers

Data Access

  • Categories are shared across the system
  • Changes affect all users who reference the category

Version History

Last Updated: December 2024 Model Version: payment_category.py Framework: Netforce


Additional Resources

  • Payment Documentation: account.payment
  • Payment Method Documentation: payment.method

This documentation is generated for developer onboarding and reference purposes.