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¶
Search by Period¶
# Find targets for specific period
condition = [
["date_from", ">=", "2026-01-01"],
["date_to", "<=", "2026-12-31"]
]
Search by Product Category¶
Search by Tracking Category¶
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.
Related Models¶
| 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.