Skip to content

Opportunity Competitor Tracking Documentation

Overview

The Opportunity Competitor module (opport.compet) enables tracking of competitors encountered on sales opportunities. This simple but powerful model helps sales teams analyze competitive threats, document competitor strengths and weaknesses, and improve win/loss strategies through competitive intelligence gathering.


Model Information

Model Name: opport.compet Display Name: Opportunity Competitor Key Fields: None (simple relationship model)

Features

  • No audit logging
  • No multi-company support (inherits from opportunity)
  • Simple relationship tracking
  • Supports comments for collaboration

Understanding Competitor Tracking

Why Track Competitors?

Competitor tracking on opportunities provides critical business intelligence:

Strategic Benefits: - Win/Loss Analysis: Identify which competitors you win/lose against most often - Positioning: Understand competitor strengths to differentiate your offering - Pricing Strategy: Learn competitor pricing patterns and adjust accordingly - Product Development: Identify feature gaps based on competitive weaknesses - Sales Enablement: Equip sales reps with battle cards and competitive talking points

When to Track Competitors

Add competitors to opportunities when: - Customer mentions competitor during discovery - RFP includes known competitive vendors - Market intelligence suggests competitive threat - Customer is evaluating multiple vendors - Incumbent vendor relationship exists


Key Fields Reference

Core Fields

Field Type Required Description
opport_id Many2One No Link to sales opportunity
compet_id Many2One No Competitor company record
strengths Text No Competitor's strengths on this deal
weaknesses Text No Competitor's weaknesses on this deal

Relationship Fields

Field Type Description
comments One2Many Internal team comments about this competitor

API Methods

1. Create Competitor Record

Method: create(vals, context)

Links a competitor to an opportunity with analysis.

Parameters:

vals = {
    "opport_id": 123,                    # Opportunity ID
    "compet_id": 45,                     # Competitor company ID
    "strengths": "Text analysis",        # What makes them strong
    "weaknesses": "Text analysis"        # Their vulnerabilities
}

Returns: int - New competitor tracking record ID

Example:

# Track competitor on opportunity
compet_rec_id = get_model("opport.compet").create({
    "opport_id": opport_id,
    "compet_id": 8,  # Competitor: "ACME Corp"
    "strengths": """
        - Incumbent vendor with 3-year relationship
        - Lower upfront cost ($80K vs our $100K)
        - Well-known brand in the market
        - Local support team
    """,
    "weaknesses": """
        - Limited cloud capabilities
        - Poor customer support ratings (2.5/5)
        - No mobile app
        - Older technology stack
        - Higher total cost of ownership long-term
    """
})


2. Track Multiple Competitors

Method: Create via opportunity record

Most commonly, competitors are added when creating or updating opportunities.

Example:

# Create opportunity with multiple competitors
opport_id = get_model("sale.opportunity").create({
    "name": "Enterprise Software Deal",
    "contact_id": 456,
    "amount": 250000,
    "competitors": [
        ("create", {
            "compet_id": 5,
            "strengths": "Market leader, established brand",
            "weaknesses": "Premium pricing, slow innovation"
        }),
        ("create", {
            "compet_id": 12,
            "strengths": "Aggressive pricing, fast deployment",
            "weaknesses": "Limited features, poor support"
        })
    ]
})

# Add competitor to existing opportunity
get_model("sale.opportunity").write([opport_id], {
    "competitors": [
        ("create", {
            "compet_id": 7,
            "strengths": "Strong local presence",
            "weaknesses": "No international support"
        })
    ]
})


3. Update Competitor Analysis

Method: write(ids, vals, context)

Update strengths/weaknesses as intelligence evolves.

Example:

# Update analysis after learning more
get_model("opport.compet").write([compet_rec_id], {
    "strengths": """
        - Incumbent vendor (updated: 5-year relationship, not 3)
        - Lower upfront cost
        - Recently added mobile app (new info)
    """,
    "weaknesses": """
        - Limited cloud capabilities
        - Poor support ratings
        - Higher TCO due to licensing model
        - Mobile app is basic, missing key features
    """
})


4. Add Team Comments

Method: Create via message model

Collaborate with team on competitive strategy.

Example:

# Sales rep adds competitive note
get_model("message").create({
    "related_id": f"opport.compet,{compet_rec_id}",
    "body": "Customer mentioned ACME quoted $75K but with limited scope. "
            "We should emphasize our comprehensive feature set justifies the price difference.",
    "type": "comment"
})


Search Functions

Find Opportunities Against Specific Competitor

# All opportunities competing against "ACME Corp"
acme_opports = get_model("opport.compet").search([
    ["compet_id.name", "=", "ACME Corp"]
])

# Get the opportunities
opport_ids = [rec.opport_id.id for rec in get_model("opport.compet").browse(acme_opports)]

Find Open Opportunities with Competitors

# Opportunities with competitive threats
comp_recs = get_model("opport.compet").search_browse([
    ["opport_id.state", "=", "open"]
])

for comp in comp_recs:
    print(f"Opportunity: {comp.opport_id.name}")
    print(f"Competitor: {comp.compet_id.name}")
    print(f"Amount at risk: ${comp.opport_id.amount}")

Model Relationship Description
sale.opportunity Many2One Parent opportunity record
competitor Many2One Competitor company master data
message One2Many Team comments and notes

Common Use Cases

Use Case 1: Competitive Deal Analysis

# Document competitor on high-value deal
opport_id = get_model("sale.opportunity").create({
    "name": "City Government ERP Implementation",
    "contact_id": 789,
    "amount": 500000,
    "probability": 60,
    "stage_id": 3,
    "competitors": [
        ("create", {
            "compet_id": 15,  # "Competitor A"
            "strengths": """
                1. Incumbent vendor - current system provider
                2. Strong relationship with IT director
                3. Lower pricing: $425K vs our $500K
                4. Government-specific features out of box
                5. Local implementation team
            """,
            "weaknesses": """
                1. Legacy technology - on-premise only
                2. Poor mobile capabilities
                3. Customer service issues (verified with references)
                4. Higher long-term maintenance costs
                5. No cloud migration path
                6. Limited API integration options
            """
        })
    ]
})

# Add strategic notes
compet_rec = get_model("sale.opportunity").browse(opport_id).competitors[0]
get_model("message").create({
    "related_id": f"opport.compet,{compet_rec.id}",
    "body": """
        STRATEGY:
        - Emphasize cloud benefits and TCO over 5 years
        - Highlight mobile/remote work capabilities (important post-COVID)
        - Provide reference from similar city implementation
        - Offer cloud migration roadmap
        - Counter price with value: better ROI long-term
    """
})

Use Case 2: Win/Loss Competitive Intelligence Report

# Analyze win rates against each competitor
competitor_stats = {}

# Get all closed opportunities (won or lost)
closed_comps = get_model("opport.compet").search_browse([
    ["opport_id.state", "in", ["won", "lost"]]
])

for comp in closed_comps:
    compet_name = comp.compet_id.name
    opport_state = comp.opport_id.state
    opport_amount = comp.opport_id.amount or 0

    if compet_name not in competitor_stats:
        competitor_stats[compet_name] = {
            "won": 0,
            "lost": 0,
            "won_value": 0,
            "lost_value": 0,
            "deals": []
        }

    stats = competitor_stats[compet_name]

    if opport_state == "won":
        stats["won"] += 1
        stats["won_value"] += opport_amount
    else:
        stats["lost"] += 1
        stats["lost_value"] += opport_amount

    stats["deals"].append({
        "opportunity": comp.opport_id.name,
        "amount": opport_amount,
        "result": opport_state,
        "our_strengths": comp.weaknesses,  # Their weaknesses = our strengths
        "their_strengths": comp.strengths
    })

# Calculate metrics
for compet, stats in competitor_stats.items():
    total = stats["won"] + stats["lost"]
    win_rate = (stats["won"] / total * 100) if total > 0 else 0

    print(f"\n{'='*60}")
    print(f"Competitor: {compet}")
    print(f"Win Rate: {win_rate:.1f}% ({stats['won']} wins / {stats['lost']} losses)")
    print(f"Won Value: ${stats['won_value']:,.0f}")
    print(f"Lost Value: ${stats['lost_value']:,.0f}")

    # Identify patterns
    if win_rate < 40:
        print("⚠️ LOW WIN RATE - Review competitive positioning")
    elif win_rate > 70:
        print("✅ STRONG PERFORMANCE - Document winning strategies")

Use Case 3: Sales Battle Card Generation

# Generate competitive battle card from historical data
competitor_id = 8  # "ACME Corp"

# Get all competitor records
comp_records = get_model("opport.compet").search_browse([
    ["compet_id", "=", competitor_id]
])

# Aggregate intelligence
all_strengths = []
all_weaknesses = []
deal_count = len(comp_records)

for comp in comp_records:
    if comp.strengths:
        all_strengths.append(comp.strengths)
    if comp.weaknesses:
        all_weaknesses.append(comp.weaknesses)

# Generate battle card document
compet = get_model("competitor").browse(competitor_id)
battle_card = f"""
COMPETITIVE BATTLE CARD: {compet.name}
{'='*60}

OVERVIEW:
- Total Competitive Deals: {deal_count}
- Last Updated: {datetime.today().strftime('%Y-%m-%d')}

COMPETITOR STRENGTHS (from {len(all_strengths)} deals):
{chr(10).join(f"Deal #{i+1}: {s}" for i, s in enumerate(all_strengths[:5]))}

COMPETITOR WEAKNESSES (from {len(all_weaknesses)} deals):
{chr(10).join(f"Deal #{i+1}: {w}" for i, w in enumerate(all_weaknesses[:5]))}

RECOMMENDED STRATEGY:
1. Lead with our advantages in areas they are weak
2. Acknowledge their strengths, but pivot to value/TCO
3. Use customer references where we won against them
4. Emphasize long-term partnership vs. transactional approach
"""

print(battle_card)

Use Case 4: Alert on High-Risk Competitive Deals

# Monitor opportunities against strongest competitors
strong_competitors = [5, 8, 12]  # IDs of top 3 competitors

at_risk = []

comp_records = get_model("opport.compet").search_browse([
    ["compet_id", "in", strong_competitors],
    ["opport_id.state", "=", "open"],
    ["opport_id.amount", ">", 100000]  # High-value deals only
])

for comp in comp_records:
    opport = comp.opport_id

    # Risk factors
    risks = []

    # High-value opportunity
    if opport.amount > 200000:
        risks.append(f"High value: ${opport.amount:,.0f}")

    # Competitor strength analysis
    if comp.strengths and len(comp.strengths) > 200:
        risks.append("Strong competitor position")

    # Low probability
    if (opport.probability or 0) < 50:
        risks.append(f"Low probability: {opport.probability}%")

    if risks:
        at_risk.append({
            "opportunity": opport.name,
            "competitor": comp.compet_id.name,
            "amount": opport.amount,
            "owner": opport.user_id.name,
            "risks": risks,
            "compet_strengths": comp.strengths[:200] + "..." if comp.strengths else "None"
        })

# Alert sales management
if at_risk:
    print(f"\n⚠️ {len(at_risk)} high-risk competitive deals require attention:\n")
    for deal in at_risk:
        print(f"- {deal['opportunity']} (${deal['amount']:,.0f})")
        print(f"  Competitor: {deal['competitor']}")
        print(f"  Owner: {deal['owner']}")
        print(f"  Risks: {', '.join(deal['risks'])}")
        print()

Best Practices

1. Document Strengths AND Weaknesses

# Bad: Only documenting weaknesses
get_model("opport.compet").create({
    "opport_id": opport_id,
    "compet_id": 5,
    "weaknesses": "Poor support, old technology"
})

# Good: Balanced competitive analysis
get_model("opport.compet").create({
    "opport_id": opport_id,
    "compet_id": 5,
    "strengths": """
        - Lower pricing by 15%
        - Incumbent vendor with existing relationship
        - Quick deployment timeline
    """,
    "weaknesses": """
        - Limited cloud capabilities
        - Poor customer support ratings (verified with references)
        - No mobile access
        - Higher long-term TCO due to licensing model
    """
})

Understanding competitor strengths helps you: - Prepare better responses and objection handling - Identify where you need to compete on value vs. price - Build credibility by acknowledging reality


2. Update Intelligence During Sales Cycle

# Initial intelligence
compet_rec_id = get_model("opport.compet").create({
    "opport_id": opport_id,
    "compet_id": 7,
    "strengths": "Lower price",
    "weaknesses": "Unknown"
})

# After discovery call - update with new information
get_model("opport.compet").write([compet_rec_id], {
    "strengths": """
        - Lower price: $80K vs our $100K
        - Existing relationship with purchasing dept
        - Faster implementation: 6 weeks vs our 12 weeks
    """,
    "weaknesses": """
        - No integration with customer's SAP system (major requirement)
        - Limited reporting capabilities
        - No dedicated account manager post-sale
    """
})

# Competitive intelligence evolves - keep it current

3. Use Comments for Team Collaboration

# Sales rep documents competitive situation
compet_rec_id = 125

get_model("message").create({
    "related_id": f"opport.compet,{compet_rec_id}",
    "body": "Customer mentioned ACME pricing is 20% lower. However, they're concerned "
            "about ACME's cloud strategy. Recommend we emphasize our cloud-native "
            "architecture and provide TCO analysis showing lower long-term costs."
})

# Sales engineer adds technical perspective
get_model("message").create({
    "related_id": f"opport.compet,{compet_rec_id}",
    "body": "Reviewed ACME's technical specs. They lack real-time sync and mobile API. "
            "Customer needs both per requirements doc. This is a strong differentiator."
})

# Sales manager provides strategy
get_model("message").create({
    "related_id": f"opport.compet,{compet_rec_id}",
    "body": "Don't discount to match ACME pricing. Instead: 1) Emphasize integration "
            "capabilities, 2) Provide reference from similar customer who switched from "
            "ACME to us, 3) Offer extended payment terms to address budget concerns."
})

4. Track Competitors Early

# Bad: Only adding competitor after learning you lost
# (too late to strategize)

# Good: Add competitor as soon as identified
# During qualification stage:
get_model("sale.opportunity").write([opport_id], {
    "competitors": [
        ("create", {
            "compet_id": 9,
            "strengths": "Customer mentioned evaluating them - need more intel",
            "weaknesses": "TBD - researching"
        })
    ]
})

# Update as you learn more through sales process
# This allows proactive competitive strategy vs. reactive

Performance Tips

1. Batch Queries for Reporting

# Bad: N+1 query problem
comp_records = get_model("opport.compet").search([])
for comp_id in comp_records:
    comp = get_model("opport.compet").browse(comp_id)
    print(comp.compet_id.name, comp.opport_id.state)

# Good: Single query with browse
comp_records = get_model("opport.compet").search_browse([])
for comp in comp_records:
    print(comp.compet_id.name, comp.opport_id.state)

2. Index Queries by Opportunity State

# Efficient: Filter at database level
active_comps = get_model("opport.compet").search_browse([
    ["opport_id.state", "=", "open"]
])

# Inefficient: Filter in Python
all_comps = get_model("opport.compet").search_browse([])
active_comps = [c for c in all_comps if c.opport_id.state == "open"]

Troubleshooting

Competitor Not Showing on Opportunity

Cause: Relationship not saved properly or form cache issue Solution: Verify record was created and refresh opportunity form

# Verify competitor record exists
comp_recs = get_model("opport.compet").search([
    ["opport_id", "=", opport_id],
    ["compet_id", "=", compet_id]
])
print(f"Found {len(comp_recs)} competitor records")

# If missing, recreate
if not comp_recs:
    get_model("opport.compet").create({
        "opport_id": opport_id,
        "compet_id": compet_id,
        "strengths": "...",
        "weaknesses": "..."
    })

Cannot Delete Competitor Record

Cause: May have associated comments or constraints Solution: Delete comments first, then competitor record

# Get competitor record with comments
comp = get_model("opport.compet").browse(compet_rec_id)

# Delete associated comments
for comment in comp.comments:
    comment.delete()

# Now delete competitor record
comp.delete()

Configuration Settings

Required Settings

Setting Location Description
Competitor Master Data competitor model Define competitor companies to track

Optional Settings

No specific settings required - inherits from opportunity and contact models.


Integration Points

Internal Modules

  • Opportunity Management: Parent relationship via sale.opportunity
  • Contact/Company: Competitor company records via competitor model
  • Comments: Team collaboration via message model
  • Reporting: Win/loss analysis and competitive intelligence

Version History

Last Updated: 2026-01-05 Model Version: opport_compet.py Framework: Netforce


Additional Resources

  • Opportunity Documentation: sale.opportunity
  • Competitor Master Data: competitor
  • Win/Loss Analysis Reporting
  • Sales Battle Card Templates

Support & Feedback

For issues or questions about this module: 1. Verify competitor records exist in competitor master data 2. Check opportunity relationship is properly set 3. Review comments for collaborative intelligence 4. Ensure data is updated throughout sales cycle


This documentation is generated for developer onboarding and reference purposes.