Skip to content

Sales Target Documentation

Overview

The Sales Target module (sale.target) provides sales goal-setting and performance tracking capabilities for salespeople and teams. It enables management to set realistic, measurable targets based on territory, product category, and time period, then track actual achievement through sales opportunities. This module is essential for performance management, commission calculations, and sales team motivation.


Model Information

Model Name: sale.target Display Name: Sales Target Key Fields: None (allows multiple targets per combination)

Features

  • ❌ Audit logging enabled (_audit_log)
  • ❌ Multi-company support (company_id)
  • ❌ Full-text content search (_content_search)
  • ✅ Performance tracking through sale.opportunity integration
  • ✅ Both amount and quantity targets
  • ✅ Expected vs actual achievement calculation

Target Setting Framework

Sales targets follow SMART goal principles:

Principle Implementation
Specific Define by salesperson, product category, and tracking category
Measurable Track both amount (revenue) and quantity (units) targets
Achievable Based on historical performance and market conditions
Relevant Aligned with business objectives and sales strategy
Time-bound Set clear date ranges (daily, monthly, quarterly, annually)

Target Types

Type Description Use Case
Amount Target Revenue/sales amount goal Overall sales performance, commission tiers
Quantity Target Unit/volume goal Product-focused targets, volume incentives
Combined Both amount and quantity Comprehensive performance management
User-Specific Assigned to individual salesperson Individual accountability
Category-Specific By product category Product line focus
Tracking-Specific By account tracking category Territory or segment focus

Key Fields Reference

Target Definition Fields

Field Type Required Description
user_id Many2One Salesperson/user assigned this target (base.user)
prod_categ_id Many2One Product category for target segmentation (product.categ)
track_id Many2One Accounting tracking category for territory/segment
date_from Date Start date of target period
date_to Date End date of target period

Amount Target Fields

Field Type Description
amount_target Decimal Target revenue/sales amount for the period
amount_actual Decimal Actual achieved revenue from won opportunities (computed)
amount_expected Decimal Expected revenue from open opportunities (computed)

Quantity Target Fields

Field Type Description
qty_target Decimal Target quantity/volume for the period
qty_actual Decimal Actual achieved quantity from won opportunities (computed)
qty_expected Decimal Expected quantity from open opportunities (computed)

Relationship Fields

Field Type Description
comments One2Many User comments and notes (message model)

API Methods

1. Create Record

Method: create(vals, context)

Creates a new sales target for a specific period and optional filters.

Parameters:

vals = {
    "user_id": 5,                      # Optional: Specific salesperson
    "prod_categ_id": 10,               # Optional: Product category
    "track_id": 3,                     # Optional: Tracking category
    "date_from": "2026-01-01",         # Required: Period start
    "date_to": "2026-03-31",           # Required: Period end
    "amount_target": 500000,           # Optional: Revenue target
    "qty_target": 10000                # Optional: Quantity target
}

context = {
    "company_id": 1                    # Current company context
}

Returns: int - New target ID

Example:

# Create quarterly target for salesperson
target_id = get_model("sale.target").create({
    "user_id": 5,
    "date_from": "2026-01-01",
    "date_to": "2026-03-31",
    "amount_target": 500000,
    "qty_target": 10000
})


2. Get Achievement Amounts

Method: get_amount(ids, context)

Computed field function that calculates actual and expected achievement from sales opportunities.

Behavior: - Queries sale.opportunity for won and open opportunities - Filters by date_close within target period - Optionally filters by user_id if specified - Optionally filters by product category if specified - For won opportunities: sums full amount and quantity - For open opportunities: sums probability-weighted amounts (amount * probability / 100) - Returns multi-field computed values

Returns: dict - Achievement metrics:

{
    target_id: {
        "amount_actual": Decimal,      # Total from won opportunities
        "amount_expected": Decimal,    # Probability-weighted from open opportunities
        "qty_actual": Decimal,         # Total quantity from won
        "qty_expected": Decimal        # Probability-weighted quantity from open
    }
}

Example:

# Automatically calculated when browsing target
target = get_model("sale.target").browse(target_id)
print(f"Target: ${target.amount_target}")
print(f"Actual: ${target.amount_actual}")
print(f"Expected: ${target.amount_expected}")
achievement_pct = (target.amount_actual / target.amount_target * 100) if target.amount_target else 0
print(f"Achievement: {achievement_pct:.1f}%")


3. Generate Daily Targets

Method: gen_targets(context)

Utility method to automatically generate daily targets for the current month.

Behavior: - Calculates first day and last day of current month - Iterates through each day in the month - Creates target record for each day if it doesn't already exist - Uses default date_from and date_to values

Example:

# Generate daily targets for current month
get_model("sale.target").gen_targets()
# Creates target records for each day of the month


Search Functions

Search by Salesperson

# Find all targets for a specific salesperson
condition = [["user_id", "=", 5]]

Search by Period

# Find targets for specific period
condition = [
    ["date_from", ">=", "2026-01-01"],
    ["date_to", "<=", "2026-12-31"]
]

Search by Product Category

# Find targets for specific product category
condition = [["prod_categ_id", "=", 10]]

Search by Tracking Category

# Find targets by territory/segment
condition = [["track_id", "=", 3]]

Search Active Targets

# Find targets for current period
from datetime import date
today = date.today().strftime("%Y-%m-%d")
condition = [
    ["date_from", "<=", today],
    ["date_to", ">=", today]
]

Computed Fields Functions

get_amount(ids, context)

Calculates actual achievement from won opportunities and expected achievement from open opportunities (probability-weighted). Returns both amount and quantity metrics for comprehensive performance tracking.


Best Practices

1. SMART Goal Setting

Follow SMART principles when creating targets:

# Bad: Vague, unrealistic target
target = {
    "date_from": "2026-01-01",
    "date_to": "2026-12-31",
    "amount_target": 10000000  # $10M for entire year, no breakdown
}

# Good: Specific, measurable, time-bound
q1_target = {
    "user_id": 5,                    # Specific person
    "prod_categ_id": 10,             # Specific product line
    "date_from": "2026-01-01",       # Clear timeframe
    "date_to": "2026-03-31",
    "amount_target": 500000,         # Realistic quarterly target
    "qty_target": 10000              # Volume component
}

Why: SMART goals are more achievable and provide better performance measurement.


2. Hierarchical Target Structure

Create targets at multiple levels for comprehensive tracking:

# Level 1: Overall company target
company_target = get_model("sale.target").create({
    "date_from": "2026-01-01",
    "date_to": "2026-03-31",
    "amount_target": 2000000  # Total company Q1 target
})

# Level 2: Product category targets
electronics_target = get_model("sale.target").create({
    "prod_categ_id": 10,             # Electronics
    "date_from": "2026-01-01",
    "date_to": "2026-03-31",
    "amount_target": 800000
})

furniture_target = get_model("sale.target").create({
    "prod_categ_id": 15,             # Furniture
    "date_from": "2026-01-01",
    "date_to": "2026-03-31",
    "amount_target": 600000
})

# Level 3: Individual salesperson targets
sales_rep_1 = get_model("sale.target").create({
    "user_id": 5,
    "prod_categ_id": 10,
    "date_from": "2026-01-01",
    "date_to": "2026-03-31",
    "amount_target": 400000
})

Why: Hierarchical targets enable drill-down analysis and ensure alignment across organization.


3. Regular Performance Reviews

Monitor target achievement throughout the period:

# Weekly performance review
targets = get_model("sale.target").search_browse([
    ["date_from", ">=", "2026-01-01"],
    ["date_to", "<=", "2026-03-31"]
])

for target in targets:
    # Calculate achievement
    if target.amount_target:
        actual_pct = (target.amount_actual / target.amount_target * 100)
        pipeline_pct = (target.amount_expected / target.amount_target * 100)
        total_pct = ((target.amount_actual + target.amount_expected) / target.amount_target * 100)

        user_name = target.user_id.name if target.user_id else "All Users"
        category = target.prod_categ_id.name if target.prod_categ_id else "All Products"

        print(f"\n{user_name} - {category}")
        print(f"  Target: ${target.amount_target:,.0f}")
        print(f"  Actual: ${target.amount_actual:,.0f} ({actual_pct:.1f}%)")
        print(f"  Pipeline: ${target.amount_expected:,.0f} ({pipeline_pct:.1f}%)")
        print(f"  Total Expected: {total_pct:.1f}%")

        # Alert if significantly off track
        if total_pct < 70:
            print(f"  ⚠️ WARNING: Below target!")

Why: Regular reviews enable early intervention and course correction.


4. Historical Baseline

Use previous period performance to set realistic targets:

# Calculate historical average for Q4
historical_targets = get_model("sale.target").search_browse([
    ["user_id", "=", 5],
    ["date_from", ">=", "2023-10-01"],
    ["date_to", "<=", "2025-12-31"]  # Last 2+ years of Q4
], order="date_from")

# Calculate average achievement
total_achievement = sum(t.amount_actual for t in historical_targets)
avg_achievement = total_achievement / len(historical_targets) if historical_targets else 0

# Set new target with 10% growth
new_target = get_model("sale.target").create({
    "user_id": 5,
    "date_from": "2026-10-01",
    "date_to": "2026-12-31",
    "amount_target": avg_achievement * 1.10  # 10% growth
})

print(f"Historical Q4 average: ${avg_achievement:,.0f}")
print(f"New Q4 target: ${new_target.amount_target:,.0f}")

Why: Data-driven target setting improves accuracy and fairness.


5. Commission Integration

Link targets to commission structures:

# Define tiered commission structure
def calculate_commission(target):
    if not target.amount_target or target.amount_target == 0:
        return 0

    achievement_pct = (target.amount_actual / target.amount_target) * 100
    actual_amount = target.amount_actual

    # Tiered commission rates
    if achievement_pct < 80:
        rate = 0.03  # 3% base
    elif achievement_pct < 100:
        rate = 0.05  # 5% for 80-100%
    elif achievement_pct < 120:
        rate = 0.07  # 7% for 100-120%
    else:
        rate = 0.10  # 10% for >120%

    commission = actual_amount * rate
    return commission

# Calculate commissions for all salespeople
targets = get_model("sale.target").search_browse([
    ["date_from", ">=", "2026-01-01"],
    ["date_to", "<=", "2026-03-31"],
    ["user_id", "!=", None]
])

for target in targets:
    commission = calculate_commission(target)
    print(f"{target.user_id.name}: ${commission:,.2f}")

Why: Clear target-commission linkage motivates sales team.


Database Constraints

Default Values

_defaults = {
    "date_from": lambda *a: date.today().strftime("%Y-%m-01"),  # First of month
    "date_to": lambda *a: (date.today() + relativedelta(day=31)).strftime("%Y-%m-%d")  # Last of month
}

New targets default to the current month period.


Model Relationship Description
base.user Many2One Salesperson assigned to target
product.categ Many2One Product category for segmented targets
account.track.categ Many2One Tracking category for territory/segment
sale.opportunity Computed Source of actual and expected achievement data
message One2Many Comments and notes

Common Use Cases

Use Case 1: Set Quarterly Sales Targets for Team

# Define Q1 2026 targets for sales team

# Sales team roster
sales_team = [
    {"user_id": 5, "name": "John Smith", "target": 500000},
    {"user_id": 8, "name": "Jane Doe", "target": 450000},
    {"user_id": 12, "name": "Bob Wilson", "target": 400000}
]

# Create targets
for rep in sales_team:
    target_id = get_model("sale.target").create({
        "user_id": rep["user_id"],
        "date_from": "2026-01-01",
        "date_to": "2026-03-31",
        "amount_target": rep["target"]
    })
    print(f"Created Q1 target for {rep['name']}: ${rep['target']:,}")

# Total team target
total_target = sum(rep["target"] for rep in sales_team)
print(f"\nTotal team target: ${total_target:,}")

Use Case 2: Performance Dashboard

# Build comprehensive performance dashboard

# Get all active targets
targets = get_model("sale.target").search_browse([
    ["date_from", ">=", "2026-01-01"],
    ["date_to", "<=", "2026-03-31"]
], order="user_id,prod_categ_id")

print("SALES PERFORMANCE DASHBOARD - Q1 2026")
print("=" * 80)

# Group by salesperson
from collections import defaultdict
by_user = defaultdict(list)

for target in targets:
    user_key = target.user_id.name if target.user_id else "Company-Wide"
    by_user[user_key].append(target)

# Display by user
for user_name, user_targets in sorted(by_user.items()):
    print(f"\n{user_name}")
    print("-" * 80)

    total_target = sum(t.amount_target or 0 for t in user_targets)
    total_actual = sum(t.amount_actual or 0 for t in user_targets)
    total_expected = sum(t.amount_expected or 0 for t in user_targets)

    for target in user_targets:
        category = target.prod_categ_id.name if target.prod_categ_id else "All Categories"

        if target.amount_target:
            achievement = (target.amount_actual / target.amount_target) * 100
            print(f"  {category:30} Target: ${target.amount_target:>12,.0f}  "
                  f"Actual: ${target.amount_actual:>12,.0f}  "
                  f"({achievement:>5.1f}%)")

    if total_target:
        overall_achievement = (total_actual / total_target) * 100
        pipeline_achievement = ((total_actual + total_expected) / total_target) * 100

        print(f"\n  {'TOTAL':30} Target: ${total_target:>12,.0f}  "
              f"Actual: ${total_actual:>12,.0f}  "
              f"({overall_achievement:>5.1f}%)")
        print(f"  {'PIPELINE':30}                           "
              f"${total_expected:>12,.0f}  "
              f"({pipeline_achievement:>5.1f}%)")

Use Case 3: Territory-Based Targets

# Set targets by sales territory using tracking categories

# Define territories
territories = [
    {"track_id": 1, "name": "North Region", "target": 1000000},
    {"track_id": 2, "name": "South Region", "target": 800000},
    {"track_id": 3, "name": "East Region", "target": 900000},
    {"track_id": 4, "name": "West Region", "target": 700000}
]

# Create territory targets
for territory in territories:
    target_id = get_model("sale.target").create({
        "track_id": territory["track_id"],
        "date_from": "2026-01-01",
        "date_to": "2026-12-31",
        "amount_target": territory["target"]
    })
    print(f"Created annual target for {territory['name']}: ${territory['target']:,}")

# Later: Review territory performance
territory_targets = get_model("sale.target").search_browse([
    ["track_id", "!=", None],
    ["date_from", "=", "2026-01-01"]
])

print("\nTERRITORY PERFORMANCE")
for target in territory_targets:
    achievement_pct = (target.amount_actual / target.amount_target * 100) if target.amount_target else 0
    print(f"{target.track_id.name}: {achievement_pct:.1f}% of target")

Use Case 4: Product Line Targets

# Set targets by product category

# Define product line targets for Q1
product_targets = [
    {"categ_id": 10, "name": "Electronics", "amount": 800000, "qty": 5000},
    {"categ_id": 15, "name": "Furniture", "amount": 600000, "qty": 2000},
    {"categ_id": 20, "name": "Office Supplies", "amount": 400000, "qty": 10000}
]

# Create category targets
for product in product_targets:
    target_id = get_model("sale.target").create({
        "prod_categ_id": product["categ_id"],
        "date_from": "2026-01-01",
        "date_to": "2026-03-31",
        "amount_target": product["amount"],
        "qty_target": product["qty"]
    })
    print(f"Created Q1 target for {product['name']}:")
    print(f"  Amount: ${product['amount']:,}")
    print(f"  Quantity: {product['qty']:,} units")

# Review product line performance
category_targets = get_model("sale.target").search_browse([
    ["prod_categ_id", "!=", None],
    ["date_from", "=", "2026-01-01"],
    ["date_to", "=", "2026-03-31"]
])

print("\nPRODUCT LINE PERFORMANCE")
for target in category_targets:
    if target.amount_target:
        amount_achievement = (target.amount_actual / target.amount_target * 100)
        print(f"\n{target.prod_categ_id.name}:")
        print(f"  Amount: {amount_achievement:.1f}% of ${target.amount_target:,} target")

    if target.qty_target:
        qty_achievement = (target.qty_actual / target.qty_target * 100)
        print(f"  Quantity: {qty_achievement:.1f}% of {target.qty_target:,} unit target")

Use Case 5: Commission Calculation

# Calculate sales commissions based on target achievement

def calculate_tiered_commission(target):
    """Calculate commission with tiered rates based on achievement"""
    if not target.amount_target or target.amount_target == 0:
        return {
            "amount": 0,
            "rate": 0,
            "tier": "None",
            "achievement_pct": 0
        }

    achievement_pct = (target.amount_actual / target.amount_target) * 100
    actual_amount = target.amount_actual

    # Define commission tiers
    if achievement_pct >= 120:
        rate = 0.10
        tier = "Platinum (≥120%)"
    elif achievement_pct >= 100:
        rate = 0.07
        tier = "Gold (100-119%)"
    elif achievement_pct >= 80:
        rate = 0.05
        tier = "Silver (80-99%)"
    else:
        rate = 0.03
        tier = "Bronze (<80%)"

    commission = actual_amount * rate

    return {
        "amount": commission,
        "rate": rate,
        "tier": tier,
        "achievement_pct": achievement_pct
    }

# Get Q1 targets for all salespeople
targets = get_model("sale.target").search_browse([
    ["date_from", "=", "2026-01-01"],
    ["date_to", "=", "2026-03-31"],
    ["user_id", "!=", None]
], order="user_id")

print("SALES COMMISSION REPORT - Q1 2026")
print("=" * 100)
print(f"{'Salesperson':<20} {'Target':>15} {'Actual':>15} {'Achievement':>12} "
      f"{'Tier':<20} {'Commission':>15}")
print("-" * 100)

total_commission = 0
for target in targets:
    comm_data = calculate_tiered_commission(target)

    print(f"{target.user_id.name:<20} "
          f"${target.amount_target:>14,.0f} "
          f"${target.amount_actual:>14,.0f} "
          f"{comm_data['achievement_pct']:>11.1f}% "
          f"{comm_data['tier']:<20} "
          f"${comm_data['amount']:>14,.2f}")

    total_commission += comm_data["amount"]

print("-" * 100)
print(f"{'TOTAL COMMISSIONS':>77} ${total_commission:>14,.2f}")

Performance Tips

1. Efficient Target Queries

# Bad: Multiple separate queries
for user_id in user_ids:
    targets = get_model("sale.target").search_browse([
        ["user_id", "=", user_id],
        ["date_from", ">=", "2026-01-01"]
    ])

# Good: Single query with all user IDs
targets = get_model("sale.target").search_browse([
    ["user_id", "in", user_ids],
    ["date_from", ">=", "2026-01-01"]
], order="user_id")

2. Minimize Computed Field Access

# Bad: Multiple accesses trigger multiple queries
for target in targets:
    if target.amount_actual > 0:        # Query 1
        pct = target.amount_actual / target.amount_target  # Query 2

# Good: Access once, cache result
for target in targets:
    actual = target.amount_actual       # Single query
    if actual > 0:
        pct = actual / target.amount_target

3. Use Date Indexing

The model is ordered by date_from,user_id,prod_categ_id for efficient date-range queries:

# Efficient: Uses indexed date field
current_targets = get_model("sale.target").search_browse([
    ["date_from", ">=", "2026-01-01"],
    ["date_to", "<=", "2026-12-31"]
])

Troubleshooting

"amount_actual showing zero despite won opportunities"

Cause: Opportunities may have date_close outside target period, or wrong user/category Solution: Verify opportunity dates and assignments:

# Check opportunities for target period
opps = get_model("sale.opportunity").search_browse([
    ["state", "=", "won"],
    ["date_close", ">=", target.date_from],
    ["date_close", "<=", target.date_to]
])

if target.user_id:
    opps = [o for o in opps if o.user_id.id == target.user_id.id]

for opp in opps:
    print(f"Opportunity {opp.number}: ${opp.amount}, Date: {opp.date_close}")

"amount_expected not reflecting pipeline"

Cause: Open opportunities missing probability values Solution: Ensure opportunities have probability set:

# Find open opportunities without probability
opps = get_model("sale.opportunity").search_browse([
    ["state", "=", "open"],
    ["probability", "=", None]
])
print(f"Found {len(opps)} opportunities missing probability")

"Targets not aligning with actual sales"

Cause: Using sale.order instead of sale.opportunity for achievement Solution: Remember that targets track opportunities (pipeline), not orders:

Target achievement comes from sale.opportunity, not sale.order
- Won opportunities = actual achievement
- Open opportunities = expected achievement (probability-weighted)


Testing Examples

Unit Test: Achievement Calculation

def test_target_achievement():
    # Create target
    target_id = get_model("sale.target").create({
        "user_id": 5,
        "date_from": "2026-01-01",
        "date_to": "2026-03-31",
        "amount_target": 100000,
        "qty_target": 1000
    })

    # Verify target
    target = get_model("sale.target").browse(target_id)
    assert target.amount_target == 100000
    assert target.qty_target == 1000

    # Check computed fields exist
    assert hasattr(target, "amount_actual")
    assert hasattr(target, "amount_expected")
    assert hasattr(target, "qty_actual")
    assert hasattr(target, "qty_expected")

    # Calculate achievement percentage
    if target.amount_target:
        achievement = (target.amount_actual / target.amount_target) * 100
        assert 0 <= achievement <= 200  # Reasonable range

Security Considerations

Permission Model

  • sale.target.create - Create new targets (typically managers only)
  • sale.target.write - Edit existing targets (managers only)
  • sale.target.delete - Delete targets (restricted)
  • sale.target.read - View targets (salespeople can view own targets)

Data Access

  • Consider implementing record rules to limit visibility
  • Salespeople should see only their own targets
  • Managers should see team targets
  • Executives should see all targets

Configuration Settings

Required Settings

Setting Location Description
Users base.user Active salespeople with proper permissions
Opportunities sale.opportunity Must track date_close and probability

Optional Settings

Setting Default Description
date_from First of current month Default target start date
date_to Last of current month Default target end date

Integration Points

Internal Modules

  • Sales Opportunities: Primary source of achievement data (won and open opportunities)
  • User Management: Links to salespeople and user accounts
  • Product Categories: Enables product-line focused targets
  • Accounting Tracking: Supports territory/segment-based targets
  • Commission Calculations: Can drive performance-based compensation

Version History

Last Updated: 2026-01-05 Model Version: sale_target.py (98 lines) Framework: Netforce


Additional Resources

  • Sales Forecast Documentation: sale.forecast
  • Sales Opportunity Documentation: sale.opportunity
  • User Management Documentation: base.user
  • Product Category Documentation: product.categ
  • Commission Documentation: Related seller.commission or similar modules

Support & Feedback

For issues or questions about this module: 1. Check sale.opportunity configuration and data 2. Verify date ranges align with opportunity close dates 3. Ensure probability values are set on opportunities 4. Review user and category assignments 5. Test achievement calculations in development environment


This documentation is generated for developer onboarding and reference purposes.