Skip to content

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:

ids = [report_id]  # Report configuration ID
context = {}       # Optional context

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


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.details for line-item analysis
  • Pass sale_id from 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.