Sales Profit Report Documentation¶
Overview¶
The Sales Profit Report module (report.sale.profit) provides summary-level profit and margin analysis for sales orders. It compares estimated costs (at order time) vs actual costs (from goods issues) to track profitability and margin performance over time.
Model Information¶
Model Name: report.sale.profit
Display Name: Sales Profit Report
Type: Transient Report Model (_transient = True)
Features¶
- Estimated vs actual cost comparison
- Profit and margin percentage calculations
- Date range filtering
- Order-by-order profit breakdown
- Summary totals and average margins
- Links to detailed profit drill-down
Key Fields Reference¶
Report Parameters¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
date_from |
Date | No | First day of month | Start date for order filtering |
date_to |
Date | No | Last day of month | End date for order filtering |
Output Fields¶
| Field | Description |
|---|---|
id |
Sales order ID (clickable for drill-down) |
date |
Order date |
number |
Order number |
customer |
Customer name |
amount_subtotal |
Order subtotal (before tax) |
est_cost_total |
Estimated total cost |
est_profit |
Estimated profit (subtotal - est cost) |
est_profit_percent |
Estimated profit % |
act_cost_total |
Actual cost from goods issues |
act_profit |
Actual profit (subtotal - actual cost) |
act_profit_percent |
Actual profit % |
Profit Calculation Logic¶
Estimated Profit (at Order Time)¶
est_cost_total = sum(line.cost_price * line.qty for line in order.lines)
est_profit = amount_subtotal - est_cost_total
est_profit_percent = (est_profit / amount_subtotal) * 100
Actual Profit (after Fulfillment)¶
act_cost_total = sum(actual costs from stock moves/goods issues)
act_profit = amount_subtotal - act_cost_total
act_profit_percent = (act_profit / amount_subtotal) * 100
Variance Analysis¶
cost_variance = act_cost_total - est_cost_total
profit_variance = act_profit - est_profit
margin_variance = act_profit_percent - est_profit_percent
API Methods¶
1. Generate Profit Report¶
Method: get_report_data(ids, context)
Generates profit analysis for sales orders in date range.
Parameters:
Returns: dict - Report data structure:
{
"company_name": "ABC Company",
"date_from": "2024-01-01",
"date_to": "2024-01-31",
"lines": [
{
"id": 123,
"date": "2024-01-15",
"number": "SO-2024-001",
"customer": "ABC Corp",
"amount_subtotal": 10000.00,
"est_cost_total": 7000.00,
"est_profit": 3000.00,
"est_profit_percent": 30.0,
"act_cost_total": 7200.00,
"act_profit": 2800.00,
"act_profit_percent": 28.0
}
],
"totals": {
"amount_subtotal": 100000.00,
"est_cost_total": 70000.00,
"est_profit": 30000.00,
"est_profit_percent": 30.0,
"act_cost_total": 72000.00,
"act_profit": 28000.00,
"act_profit_percent": 28.0
}
}
Example:
# Generate profit report for January 2024
report_id = get_model("report.sale.profit").create({
"date_from": "2024-01-01",
"date_to": "2024-01-31"
})
data = get_model("report.sale.profit").get_report_data([report_id])
print(f"Total Sales: ${data['totals']['amount_subtotal']:,.2f}")
print(f"Estimated Profit: ${data['totals']['est_profit']:,.2f} ({data['totals']['est_profit_percent']:.1f}%)")
print(f"Actual Profit: ${data['totals']['act_profit']:,.2f} ({data['totals']['act_profit_percent']:.1f}%)")
print(f"Variance: ${data['totals']['est_profit'] - data['totals']['act_profit']:,.2f}")
Related Models¶
| Model | Relationship | Description |
|---|---|---|
sale.order |
Data Source | Sales orders with profit calculations |
sale.order.line |
Data Source | Line-level cost and pricing |
report.sale.profit.details |
Drill-down | Detailed line-item profit view |
stock.move |
Reference | Actual cost data from goods issues |
contact |
Many2One | Customer information |
Common Use Cases¶
Use Case 1: Monthly Profit Performance¶
# Track profit trends month over month
from datetime import datetime
from dateutil.relativedelta import relativedelta
start_date = datetime(2024, 1, 1)
monthly_profits = []
for i in range(12):
month_start = (start_date + relativedelta(months=i)).strftime("%Y-%m-01")
month_end = (start_date + relativedelta(months=i, day=31)).strftime("%Y-%m-%d")
report_id = get_model("report.sale.profit").create({
"date_from": month_start,
"date_to": month_end
})
data = get_model("report.sale.profit").get_report_data([report_id])
monthly_profits.append({
"month": (start_date + relativedelta(months=i)).strftime("%B"),
"sales": data["totals"]["amount_subtotal"],
"profit": data["totals"]["act_profit"],
"margin": data["totals"]["act_profit_percent"]
})
print("Monthly Profit Trend:")
for month_data in monthly_profits:
print(f"{month_data['month']}: Sales ${month_data['sales']:,.2f}, "
f"Profit ${month_data['profit']:,.2f} ({month_data['margin']:.1f}%)")
Use Case 2: Cost Variance Analysis¶
# Identify orders with significant cost variances
report_id = get_model("report.sale.profit").create({
"date_from": "2024-01-01",
"date_to": "2024-03-31"
})
data = get_model("report.sale.profit").get_report_data([report_id])
# Find orders with >10% cost variance
variances = []
for line in data["lines"]:
if line.get("est_cost_total") and line.get("act_cost_total"):
variance_pct = ((line["act_cost_total"] - line["est_cost_total"]) / line["est_cost_total"]) * 100
if abs(variance_pct) > 10:
variances.append({
"order": line["number"],
"customer": line["customer"],
"est_cost": line["est_cost_total"],
"act_cost": line["act_cost_total"],
"variance": variance_pct
})
print("Orders with >10% Cost Variance:")
for v in sorted(variances, key=lambda x: abs(x["variance"]), reverse=True):
print(f"{v['order']} - {v['customer']}: "
f"Est ${v['est_cost']:,.2f} vs Act ${v['act_cost']:,.2f} ({v['variance']:+.1f}%)")
Use Case 3: Customer Profitability Ranking¶
# Rank customers by profitability
report_id = get_model("report.sale.profit").create({
"date_from": "2024-01-01",
"date_to": "2024-12-31"
})
data = get_model("report.sale.profit").get_report_data([report_id])
from collections import defaultdict
customer_profits = defaultdict(lambda: {"sales": 0, "profit": 0, "orders": 0})
for line in data["lines"]:
cust = line["customer"]
customer_profits[cust]["sales"] += line["amount_subtotal"]
customer_profits[cust]["profit"] += line.get("act_profit") or line.get("est_profit", 0)
customer_profits[cust]["orders"] += 1
# Calculate margins and rank
ranked_customers = []
for customer, metrics in customer_profits.items():
margin = (metrics["profit"] / metrics["sales"]) * 100 if metrics["sales"] else 0
ranked_customers.append({
"customer": customer,
"sales": metrics["sales"],
"profit": metrics["profit"],
"margin": margin,
"orders": metrics["orders"]
})
ranked_customers.sort(key=lambda x: x["profit"], reverse=True)
print("Top 10 Most Profitable Customers:")
for i, cust in enumerate(ranked_customers[:10], 1):
print(f"{i}. {cust['customer']}:")
print(f" Sales: ${cust['sales']:,.2f}")
print(f" Profit: ${cust['profit']:,.2f} ({cust['margin']:.1f}%)")
print(f" Orders: {cust['orders']}")
Use Case 4: Low Margin Alert¶
# Alert on orders with margins below threshold
report_id = get_model("report.sale.profit").create({
"date_from": "2024-01-01",
"date_to": "2024-01-31"
})
data = get_model("report.sale.profit").get_report_data([report_id])
margin_threshold = 15.0 # 15% minimum acceptable margin
low_margin_orders = []
for line in data["lines"]:
margin = line.get("act_profit_percent") or line.get("est_profit_percent", 0)
if margin < margin_threshold:
low_margin_orders.append({
"order_id": line["id"],
"order": line["number"],
"customer": line["customer"],
"sales": line["amount_subtotal"],
"margin": margin
})
if low_margin_orders:
print(f"WARNING: {len(low_margin_orders)} orders below {margin_threshold}% margin:")
for order in sorted(low_margin_orders, key=lambda x: x["margin"]):
print(f" {order['order']} - {order['customer']}: "
f"${order['sales']:,.2f} at {order['margin']:.1f}% margin")
# Optionally, open detail report for review
# detail_url = f"/report_sale_profit_details?sale_id={order['order_id']}"
Use Case 5: Profit Forecast vs Actual¶
# Compare forecast (estimated) vs actual profits for completed orders
report_id = get_model("report.sale.profit").create({
"date_from": "2024-01-01",
"date_to": "2024-03-31"
})
data = get_model("report.sale.profit").get_report_data([report_id])
# Only analyze orders with actual costs (completed)
completed_orders = [line for line in data["lines"] if line.get("act_cost_total")]
if completed_orders:
total_est_profit = sum(line["est_profit"] for line in completed_orders)
total_act_profit = sum(line["act_profit"] for line in completed_orders)
forecast_accuracy = (total_act_profit / total_est_profit) * 100 if total_est_profit else 0
print("Profit Forecast Accuracy:")
print(f" Estimated Profit: ${total_est_profit:,.2f}")
print(f" Actual Profit: ${total_act_profit:,.2f}")
print(f" Variance: ${total_act_profit - total_est_profit:,.2f}")
print(f" Accuracy: {forecast_accuracy:.1f}%")
# Identify most inaccurate forecasts
inaccurate = []
for line in completed_orders:
variance = abs(line["act_profit"] - line["est_profit"])
if variance > 1000: # Significant variance
inaccurate.append({
"order": line["number"],
"est": line["est_profit"],
"act": line["act_profit"],
"variance": variance
})
if inaccurate:
print("\nOrders with >$1000 profit variance:")
for order in sorted(inaccurate, key=lambda x: x["variance"], reverse=True)[:5]:
print(f" {order['order']}: Est ${order['est']:,.2f} vs Act ${order['act']:,.2f}")
Performance Tips¶
1. Date Range Optimization¶
# Good: Monthly/quarterly ranges
date_from = "2024-01-01"
date_to = "2024-03-31"
# Less optimal: Annual or unlimited ranges
date_from = "2020-01-01" # Too broad, slower query
2. Cost Calculation Dependencies¶
- Estimated costs come from
sale.order.line.cost_price(fast) - Actual costs require stock move lookups (slower for high-volume orders)
- Consider caching actual costs at order completion
3. Drill-Down to Details¶
- Use
report.sale.profit.detailsfor line-item analysis - Pass
sale_idfrom summary report to detail report - Avoid loading details for all orders at once
Troubleshooting¶
"Actual profit shows as None or zero"¶
Cause: Order not yet fulfilled, no goods issued Solution: - Actual costs only available after goods issue/delivery - Use estimated profit for open orders - Check order fulfillment status
"Profit percentages don't add up"¶
Cause: Percentage is calculated per order, not summed Solution: - Total profit % = (total_profit / total_sales) * 100 - Individual order %s should not be averaged - Use weighted average if needed
"Negative profit margins"¶
Cause: Cost exceeds selling price (loss-making order) Solution: - Review pricing strategy - Check for data entry errors in cost price - Investigate special discounts or pricing exceptions
Integration with Detail Report¶
# Generate summary, then drill into specific order
# 1. Summary report
summary_report_id = get_model("report.sale.profit").create({
"date_from": "2024-01-01",
"date_to": "2024-01-31"
})
summary_data = get_model("report.sale.profit").get_report_data([summary_report_id])
# 2. Find order needing investigation
problem_order = next((line for line in summary_data["lines"]
if line["act_profit_percent"] < 10), None)
if problem_order:
# 3. Generate detail report for that specific order
detail_report_id = get_model("report.sale.profit.details").create({
"sale_id": problem_order["id"]
})
detail_data = get_model("report.sale.profit.details").get_report_data([detail_report_id])
print(f"Detail analysis for {problem_order['number']}:")
for line in detail_data["lines"]:
line_profit = line["amount"] - line.get("cost_amount", 0)
line_margin = (line_profit / line["amount"]) * 100 if line["amount"] else 0
print(f" {line['description']}: Margin {line_margin:.1f}%")
Version History¶
Last Updated: 2026-01-05 Model Version: report_sale_profit.py (95 lines) Framework: Netforce
Additional Resources¶
- Detail Report Documentation:
report.sale.profit.details - Sales Order Documentation:
sale.order - Cost Calculation Guide: Stock and Costing module
This documentation is generated for developer onboarding and reference purposes.