Account Expense Line Documentation¶
Overview¶
The Account Expense Line model (account.expense.line) represents individual line items within an account expense record. Each line captures details about a specific item or service purchased, including quantity, unit price, account allocation, and tax rate.
Model Information¶
Model Name: account.expense.line
Display Name: Expense Line
Key Fields: None (no unique constraint defined)
Features¶
- ❌ Audit logging enabled (
_audit_log) - ❌ Multi-company support (
company_id) - ❌ Full-text content search (
_content_search) - ✅ Computed amount field
- ✅ Cascade delete with parent expense
Key Fields Reference¶
All Fields¶
| Field | Type | Required | Description |
|---|---|---|---|
expense_id |
Many2One | ✅ | Parent expense record |
description |
Char | ✅ | Item description |
qty |
Decimal | ✅ | Quantity purchased |
unit_price |
Decimal | ✅ | Price per unit |
amount |
Decimal | ❌ | Computed: qty × unit_price |
account_id |
Many2One | ❌ | GL account for posting |
tax_id |
Many2One | ❌ | Applicable tax rate |
Computed Fields¶
get_amount(ids, context)¶
Calculates the line amount as quantity multiplied by unit price.
Formula:
Example:
API Methods¶
1. Create Line¶
Method: create(vals, context)
Creates a new expense line. Usually created via parent expense.
Parameters:
vals = {
"expense_id": expense_id, # Required: Parent expense
"description": "Office chair", # Required: Item description
"qty": 2, # Required: Quantity
"unit_price": 150.00, # Required: Unit price
"account_id": furniture_acc_id, # Optional: GL account
"tax_id": gst_rate_id, # Optional: Tax rate
}
Returns: int - New record ID
Example (Standalone):
line_id = get_model("account.expense.line").create({
"expense_id": expense_id,
"description": "Wireless Mouse",
"qty": 1,
"unit_price": 45.00,
"account_id": equipment_account_id,
})
Example (Via Parent):
# More common: Create lines when creating expense
expense_id = get_model("account.expense").create({
"ref": "EXP-001",
"contact_id": vendor_id,
"date": "2024-12-15",
"tax_type": "tax_in",
"lines": [
("create", {
"description": "Wireless Mouse",
"qty": 1,
"unit_price": 45.00,
}),
("create", {
"description": "USB Hub",
"qty": 1,
"unit_price": 25.00,
})
]
})
2. Read Line¶
Method: browse(ids, context)
Retrieve line records for reading.
Example:
lines = get_model("account.expense.line").browse(line_ids)
for line in lines:
print(f"{line.description}: {line.qty} x {line.unit_price} = {line.amount}")
3. Update Line¶
Method: write(ids, vals, context)
Update existing line records.
Example:
4. Delete Line¶
Method: delete(ids, context)
Delete line records. Lines are also automatically deleted when parent expense is deleted (cascade).
Example:
5. Get Amount¶
Method: get_amount(ids, context)
Returns computed amount for each line.
Returns: Dict mapping line IDs to amounts
Example:
amounts = get_model("account.expense.line").get_amount(line_ids)
for line_id, amount in amounts.items():
print(f"Line {line_id}: {amount}")
Related Models¶
| Model | Relationship | Description |
|---|---|---|
account.expense |
Many2One (expense_id) | Parent expense record |
account.account |
Many2One (account_id) | GL account for posting |
account.tax.rate |
Many2One (tax_id) | Tax rate for line |
Common Use Cases¶
Use Case 1: Add Line to Existing Expense¶
# Add a new line to an existing expense
line_id = get_model("account.expense.line").create({
"expense_id": expense_id,
"description": "External Hard Drive",
"qty": 1,
"unit_price": 120.00,
"account_id": equipment_account_id,
"tax_id": gst_rate_id,
})
Use Case 2: Get All Lines for an Expense¶
# Get all lines for a specific expense
expense = get_model("account.expense").browse([expense_id])[0]
total = 0
for line in expense.lines:
print(f"{line.description}: {line.amount}")
total += line.amount
print(f"Total: {total}")
Use Case 3: Update Multiple Lines¶
# Update account for all lines in an expense
expense = get_model("account.expense").browse([expense_id])[0]
line_ids = [line.id for line in expense.lines]
get_model("account.expense.line").write(line_ids, {
"account_id": new_account_id
})
Use Case 4: Calculate Line Totals with Tax¶
# Calculate line totals including tax (for tax-exclusive expenses)
expense = get_model("account.expense").browse([expense_id])[0]
for line in expense.lines:
base_amount = line.amount
if line.tax_id and expense.tax_type == "tax_ex":
tax = get_model("account.tax.rate").compute_tax(
line.tax_id.id,
base_amount,
tax_type="tax_ex"
)
total = base_amount + tax
else:
total = base_amount
print(f"{line.description}: {base_amount} + tax = {total}")
Best Practices¶
1. Always Specify Account¶
# Good: Specify account for proper GL posting
line = ("create", {
"description": "Office supplies",
"qty": 1,
"unit_price": 50.00,
"account_id": supplies_account_id, # Proper categorization
})
# Bad: Missing account
line = ("create", {
"description": "Office supplies",
"qty": 1,
"unit_price": 50.00,
# No account - may cause posting issues
})
2. Use Descriptive Line Items¶
# Good: Specific description
line = ("create", {
"description": "HP LaserJet Toner Cartridge (Black)",
"qty": 2,
"unit_price": 85.00,
})
# Bad: Vague description
line = ("create", {
"description": "Supplies",
"qty": 2,
"unit_price": 85.00,
})
3. Match Tax Rates to Account¶
# Good: Tax rate matches account's default tax
acc = get_model("account.account").browse([account_id])[0]
line = ("create", {
"description": "Item",
"qty": 1,
"unit_price": 100.00,
"account_id": account_id,
"tax_id": acc.tax_id.id if acc.tax_id else None,
})
Troubleshooting¶
"Required field missing: description"¶
Cause: Creating line without description Solution: Always provide a description for each line item
"Required field missing: qty"¶
Cause: Creating line without quantity Solution: Provide qty field, even if it's 1
"Required field missing: unit_price"¶
Cause: Creating line without unit price Solution: Always specify unit_price
"Expense line has no parent"¶
Cause: expense_id not set or invalid Solution: Ensure valid expense_id is provided when creating standalone lines
"Amount shows as 0"¶
Cause: qty or unit_price is 0 or None Solution: Verify both qty and unit_price have valid positive values
Testing Examples¶
Unit Test: Line Amount Calculation¶
def test_expense_line_amount():
# Create expense
expense_id = get_model("account.expense").create({
"ref": "TEST-001",
"contact_id": vendor_id,
"date": "2024-12-15",
"tax_type": "no_tax",
"lines": [
("create", {
"description": "Test Item",
"qty": 3,
"unit_price": 25.00,
})
]
})
# Get line
expense = get_model("account.expense").browse([expense_id])[0]
line = expense.lines[0]
# Verify amount calculation
assert line.qty == 3
assert line.unit_price == 25.00
assert line.amount == 75.00 # 3 x 25 = 75
# Cleanup
get_model("account.expense").delete([expense_id])
Unit Test: Multiple Lines¶
def test_multiple_expense_lines():
# Create expense with multiple lines
expense_id = get_model("account.expense").create({
"ref": "TEST-002",
"contact_id": vendor_id,
"date": "2024-12-15",
"tax_type": "no_tax",
"lines": [
("create", {"description": "Item A", "qty": 2, "unit_price": 10.00}),
("create", {"description": "Item B", "qty": 1, "unit_price": 50.00}),
("create", {"description": "Item C", "qty": 5, "unit_price": 5.00}),
]
})
expense = get_model("account.expense").browse([expense_id])[0]
# Verify line count
assert len(expense.lines) == 3
# Verify total
total = sum(line.amount for line in expense.lines)
assert total == 95.00 # (2*10) + (1*50) + (5*5) = 20 + 50 + 25 = 95
# Cleanup
get_model("account.expense").delete([expense_id])
Security Considerations¶
Permission Model¶
- Lines inherit permissions from parent expense
- Users who can edit expense can edit its lines
Data Access¶
- Lines are accessed through parent expense
- Cascade delete ensures orphan lines don't exist
Database Behavior¶
Cascade Delete¶
When a parent expense is deleted, all its lines are automatically deleted:
This ensures data integrity - no orphan lines remain.
Version History¶
Last Updated: December 2024 Model Version: account_expense_line.py Framework: Netforce
Additional Resources¶
- Account Expense Documentation:
account.expense - Account Documentation:
account.account - Tax Rate Documentation:
account.tax.rate
This documentation is generated for developer onboarding and reference purposes.