Skip to content

Sale Event Documentation

Overview

The Sale Event module (sale.event) manages sales and marketing events such as trade shows, exhibitions, conferences, product launches, and customer appreciation events. This master data model serves as a central registry for tracking event-based marketing initiatives, lead generation campaigns, and event ROI measurement within the sales process.


Model Information

Model Name: sale.event Display Name: Event Key Fields: code

Features

  • ❌ Audit logging enabled (_audit_log)
  • ❌ Multi-company support (company_id)
  • ❌ Full-text content search (_content_search)
  • ✅ Unique key constraint per event code

Understanding Key Fields

What are Key Fields?

In Netforce models, Key Fields are a combination of fields that together create a composite unique identifier for a record. Think of them as a business key that ensures data integrity across the system.

For the sale.event model, the key field is:

_key = ["code"]

This means each event must have a unique code: - code - Unique identifier for the event (e.g., "EXPO2026", "LAUNCH-Q1", "CONF-TECH-2026")

Why Key Fields Matter

Uniqueness Guarantee - Key fields prevent duplicate records by ensuring unique event codes:

# Examples of valid event codes:
Event(code="EXPO2026", name="Annual Expo 2026")            Valid
Event(code="LAUNCH-Q1", name="Q1 Product Launch")          Valid
Event(code="CONF-TECH-2026", name="Tech Conference 2026")  Valid

# This would fail - duplicate key:
Event(code="EXPO2026", name="Different Event")             ERROR: Key already exists!

Database Implementation

The key field is enforced at the database level using a unique constraint:

_key = ["code"]

This translates to:

CREATE UNIQUE INDEX sale_event_code
    ON sale_event (code);

Event Lifecycle

Sales events typically follow a three-phase lifecycle:

Pre-Event → During Event → Post-Event
(Planning)   (Execution)    (Follow-up)

Pre-Event Phase

  • Event creation and setup
  • Product selection and catalog preparation
  • Marketing campaign planning
  • Staff assignment and training

During Event Phase

  • Lead capture and registration
  • Product demonstrations
  • Sales interactions and inquiries
  • Real-time engagement tracking

Post-Event Phase

  • Lead qualification and distribution
  • Follow-up campaigns
  • ROI analysis and reporting
  • Performance evaluation

Key Fields Reference

Header Fields

Field Type Required Description
name Char Event name - descriptive title displayed to users
code Char Event code - unique identifier for system reference

Relationship Fields

Field Type Description
products Many2Many Published products featured at this event, filtered by is_published=True

API Methods

1. Create Event

Method: create(vals, context)

Creates a new sales event record.

Parameters:

vals = {
    "name": str,                      # Required: Event name
    "code": str,                      # Required: Unique event code
    "products": [                     # Optional: Featured products
        (4, product_id),              # Link existing product
        (4, product_id2)
    ]
}

context = {
    "company_id": int                 # Company context if needed
}

Returns: int - New event record ID

Example:

# Create annual trade show event
event_id = get_model("sale.event").create({
    "name": "Annual Tech Expo 2026",
    "code": "TECHEXPO2026",
    "products": [
        (4, 101),  # Link Laptop Pro
        (4, 102),  # Link Tablet Plus
        (4, 103)   # Link Smart Watch
    ]
})


2. Update Event

Method: write(ids, vals, context)

Updates existing event records.

Parameters: - ids (list): Event record IDs to update - vals (dict): Fields to update

Example:

# Add more products to the event
get_model("sale.event").write([event_id], {
    "products": [
        (4, 104),  # Add new product
        (4, 105)
    ]
})


3. Search Events

Method: search(condition, context)

Searches for events matching criteria.

Example:

# Find events with specific code
event_ids = get_model("sale.event").search([
    ["code", "=", "TECHEXPO2026"]
])

# Search events by name
event_ids = get_model("sale.event").search([
    ["name", "ilike", "Expo"]
])


Search Functions

Search by Event Code

# Find event by unique code
condition = [["code", "=", "TECHEXPO2026"]]
event_ids = get_model("sale.event").search(condition)

Search by Event Name

# Find events containing text in name
condition = [["name", "ilike", "conference"]]
event_ids = get_model("sale.event").search(condition)

Search Events with Specific Products

# Find events featuring a specific product
condition = [["products", "in", [product_id]]]
event_ids = get_model("sale.event").search(condition)

Event Types and Examples

Event Type Code Pattern Description Typical Use
Trade Show EXPO-YYYY Large industry exhibitions Lead generation, brand awareness
Conference CONF-TOPIC-YYYY Professional conferences Thought leadership, networking
Product Launch LAUNCH-PRODUCT-QN New product introduction Market entry, media coverage
Webinar WEB-TOPIC-MMYY Online seminars Education, lead nurturing
Customer Event CUST-TYPE-YYYY Customer appreciation Retention, upselling
Roadshow ROAD-REGION-YYYY Multi-city tour Regional market penetration

Model Relationship Description
product Many2Many Products featured at the event (published only)
sale.lead Reference Leads captured during the event (via related_id)
sale.quot Reference Quotations generated from event leads
sale.order Reference Sales orders resulting from event follow-up
marketing.campaign External Marketing campaigns tied to event promotion

Common Use Cases

Use Case 1: Create Trade Show Event with Product Catalog

# Step-by-step event setup for annual trade show

# 1. Create the event record
event_id = get_model("sale.event").create({
    "name": "Annual Tech Expo 2026",
    "code": "TECHEXPO2026"
})

# 2. Get all published products in specific category
product_ids = get_model("product").search([
    ["is_published", "=", True],
    ["categ_id.name", "=", "Electronics"]
])

# 3. Link products to event
get_model("sale.event").write([event_id], {
    "products": [(4, pid) for pid in product_ids]
})

# 4. Event is now ready for lead capture
print(f"Event {event_id} created with {len(product_ids)} products")

Use Case 2: Track Leads from Conference Event

# Capture leads during conference with event reference

event = get_model("sale.event").browse([
    ["code", "=", "CONF-TECH-2026"]
])[0]

# Create lead with event reference
lead_id = get_model("sale.lead").create({
    "name": "John Smith - Tech Conference Lead",
    "contact_name": "John Smith",
    "company": "ABC Technologies",
    "email": "john@abc.com",
    "phone": "+1-555-0123",
    "related_id": "sale.event,%d" % event.id,  # Link to event
    "source": "Trade Show",
    "description": "Interested in enterprise solutions - met at booth #42"
})

# Lead is now tracked back to this event for ROI analysis

Use Case 3: Product Launch Event with Promotional Offers

# Setup product launch event with special pricing

# 1. Create launch event
event_id = get_model("sale.event").create({
    "name": "SmartWatch Pro Launch Event",
    "code": "LAUNCH-SMARTWATCH-Q1"
})

# 2. Link the new product
new_product_id = get_model("product").search([
    ["code", "=", "SMARTWATCH-PRO"]
])[0]

get_model("sale.event").write([event_id], {
    "products": [(4, new_product_id)]
})

# 3. Create promotional price list for event attendees
pricelist_id = get_model("price.list").create({
    "name": "SmartWatch Launch Special",
    "code": "LAUNCH-SPECIAL-Q1",
    "items": [
        ("create", {
            "product_id": new_product_id,
            "price": 299.00,  # Special launch price
            "date_from": "2026-03-01",
            "date_to": "2026-03-31"
        })
    ]
})

# Event ready with promotional pricing

Use Case 4: Event ROI Analysis and Reporting

# Analyze event performance and calculate ROI

event = get_model("sale.event").browse([
    ["code", "=", "TECHEXPO2026"]
])[0]

# 1. Find all leads from this event
lead_ids = get_model("sale.lead").search([
    ["related_id", "=", "sale.event,%d" % event.id]
])

total_leads = len(lead_ids)

# 2. Find converted leads (those with quotations)
converted_count = 0
total_quoted_value = 0

for lead_id in lead_ids:
    quot_ids = get_model("sale.quot").search([
        ["related_id", "=", "sale.lead,%d" % lead_id]
    ])
    if quot_ids:
        converted_count += 1
        for quot_id in quot_ids:
            quot = get_model("sale.quot").browse(quot_id)
            total_quoted_value += quot.amount_total or 0

# 3. Find actual sales from event leads
won_count = 0
total_revenue = 0

for lead_id in lead_ids:
    order_ids = get_model("sale.order").search([
        ["related_id", "=", "sale.lead,%d" % lead_id],
        ["state", "in", ["confirmed", "done"]]
    ])
    if order_ids:
        won_count += 1
        for order_id in order_ids:
            order = get_model("sale.order").browse(order_id)
            total_revenue += order.amount_total or 0

# 4. Calculate metrics
conversion_rate = (converted_count / total_leads * 100) if total_leads > 0 else 0
win_rate = (won_count / total_leads * 100) if total_leads > 0 else 0
avg_deal_size = (total_revenue / won_count) if won_count > 0 else 0

# 5. Report results
print(f"""
Event ROI Report: {event.name}
================================
Total Leads: {total_leads}
Converted to Quotes: {converted_count} ({conversion_rate:.1f}%)
Won Deals: {won_count} ({win_rate:.1f}%)
Total Pipeline Value: ${total_quoted_value:,.2f}
Total Revenue: ${total_revenue:,.2f}
Average Deal Size: ${avg_deal_size:,.2f}
""")

Use Case 5: Multi-Event Campaign Management

# Manage a series of regional roadshow events

events_data = [
    {"name": "West Coast Roadshow", "code": "ROAD-WEST-2026", "region": "West"},
    {"name": "East Coast Roadshow", "code": "ROAD-EAST-2026", "region": "East"},
    {"name": "Midwest Roadshow", "code": "ROAD-MIDWEST-2026", "region": "Midwest"}
]

# 1. Get standard product catalog for all events
product_ids = get_model("product").search([
    ["is_published", "=", True],
    ["categ_id.name", "=", "Enterprise Solutions"]
])

# 2. Create all roadshow events
event_ids = []
for event_data in events_data:
    event_id = get_model("sale.event").create({
        "name": event_data["name"],
        "code": event_data["code"],
        "products": [(4, pid) for pid in product_ids]
    })
    event_ids.append(event_id)
    print(f"Created {event_data['region']} event: {event_id}")

# 3. Track performance across all events
for event_id in event_ids:
    event = get_model("sale.event").browse(event_id)
    lead_count = len(get_model("sale.lead").search([
        ["related_id", "=", "sale.event,%d" % event.id]
    ]))
    print(f"{event.name}: {lead_count} leads captured")

Best Practices

1. Event Code Naming Conventions

# Use consistent, descriptive code patterns

# Bad examples - unclear, inconsistent:
event1 = {"code": "E001", "name": "Trade Show"}          # Too generic
event2 = {"code": "event", "name": "Conference"}         # Not unique
event3 = {"code": "ABC", "name": "Product Launch"}       # No context

# Good examples - clear, informative:
event1 = {"code": "EXPO-TECH-2026", "name": "Tech Expo 2026"}
event2 = {"code": "CONF-SALES-Q2", "name": "Sales Conference Q2"}
event3 = {"code": "LAUNCH-WIDGET-APR", "name": "Widget Launch April"}

Recommended Code Format: - Trade Shows: EXPO-[INDUSTRY]-[YEAR] - Conferences: CONF-[TOPIC]-[PERIOD] - Product Launches: LAUNCH-[PRODUCT]-[QUARTER] - Webinars: WEB-[TOPIC]-[MMYY] - Roadshows: ROAD-[REGION]-[YEAR]


2. Product Selection Strategy

Only published products should be featured at events:

# Bad - Including unpublished or internal products:
all_products = get_model("product").search([])  # Gets everything

# Good - Only published, customer-facing products:
published_products = get_model("product").search([
    ["is_published", "=", True],
    ["active", "=", True]
])

event_id = get_model("sale.event").create({
    "name": "Customer Appreciation Event",
    "code": "CUST-APPR-2026",
    "products": [(4, pid) for pid in published_products]
})

Benefits: - Ensures only market-ready products are shown - Prevents accidental exposure of internal/prototype items - Maintains professional event presentation - Aligns with marketing and branding guidelines


3. Lead Attribution and Tracking

Always link leads to their source event for accurate ROI tracking:

# Bad - No event reference:
lead_id = get_model("sale.lead").create({
    "name": "Jane Doe",
    "source": "Trade Show"  # Vague, no specific event
})

# Good - Clear event attribution:
event = get_model("sale.event").search([
    ["code", "=", "EXPO-TECH-2026"]
])[0]

lead_id = get_model("sale.lead").create({
    "name": "Jane Doe - Tech Expo 2026",
    "contact_name": "Jane Doe",
    "source": "Trade Show",
    "related_id": "sale.event,%d" % event,  # Direct link to event
    "description": "Met at booth #42, interested in enterprise package"
})

Benefits: - Enables event ROI calculation - Tracks lead source accurately - Supports event performance comparison - Facilitates follow-up campaign targeting


4. Event Lifecycle Management

Organize events with clear lifecycle stages:

# Consider using event description field to track status

# Pre-event setup
event_id = get_model("sale.event").create({
    "name": "Annual Customer Summit 2026",
    "code": "SUMMIT-2026",
    "description": """
    Status: Planning
    Date: June 15-17, 2026
    Location: Convention Center, San Francisco
    Expected Attendees: 500
    Budget: $100,000
    Goals: 200 qualified leads, 50 demos
    """
})

# During event - update with real-time metrics
get_model("sale.event").write([event_id], {
    "description": """
    Status: In Progress
    Date: June 15-17, 2026
    Location: Convention Center, San Francisco
    Actual Attendees: 487
    Leads Captured: 156
    Demos Given: 38
    """
})

# Post-event - final metrics
get_model("sale.event").write([event_id], {
    "description": """
    Status: Completed
    Date: June 15-17, 2026
    Location: Convention Center, San Francisco
    Final Attendees: 487
    Total Leads: 203
    Qualified Leads: 167
    Demos: 52
    Follow-up Required: 167
    ROI: $450,000 pipeline generated
    """
})

Database Constraints

Unique Event Code

CREATE UNIQUE INDEX sale_event_code
    ON sale_event (code);

Ensures each event has a unique code identifier. This prevents: - Duplicate event registrations - Confusion in lead attribution - Data integrity issues in reporting - Ambiguity in event references


Integration Points

Internal Modules

  • sale.lead: Leads reference events via related_id field for source tracking
  • sale.quot: Quotations can trace back to originating events through lead relationships
  • sale.order: Orders maintain event attribution through the lead-quot-order chain
  • product: Many2Many relationship for event product catalogs (published products only)
  • marketing.campaign: External campaigns can reference events for promotion
  • crm.activity: Activities and follow-ups can be linked to event leads

External Systems

  • Event Management Platforms: Export event codes and product lists to event software
  • Marketing Automation: Sync event attendees and triggers for follow-up campaigns
  • CRM Systems: Import event leads with proper attribution
  • Analytics Tools: Event performance data for ROI dashboards
  • Email Marketing: Segment attendees by event for targeted campaigns

Performance Tips

1. Limit Product Associations

  • Only link products that will actually be featured
  • Use published products filter to reduce dataset size
  • Remove outdated products after event completion
# Bad: Linking all products
all_products = get_model("product").search([])  # Thousands of records

# Good: Link only relevant, published products
relevant_products = get_model("product").search([
    ["is_published", "=", True],
    ["categ_id.name", "in", ["Electronics", "Enterprise"]],
    ["active", "=", True]
])  # Much smaller dataset

2. Efficient Lead Attribution

# Bad: Searching by event name (slow)
leads = get_model("sale.lead").search([
    ["related_id", "ilike", "Tech Expo"]  # String matching
])

# Good: Search by exact event reference (fast)
event = get_model("sale.event").search([["code", "=", "EXPO-TECH-2026"]])[0]
leads = get_model("sale.lead").search([
    ["related_id", "=", "sale.event,%d" % event]  # Exact match
])

3. Batch Processing for Multiple Events

# Bad: Create events one at a time with multiple database calls
for event_data in events_list:
    event_id = get_model("sale.event").create(event_data)
    # Separate call to link products
    get_model("sale.event").write([event_id], {"products": product_links})

# Good: Create events with products in single operation
for event_data in events_list:
    event_data["products"] = [(4, pid) for pid in product_ids]
    event_id = get_model("sale.event").create(event_data)  # One call

Troubleshooting

"Key already exists" Error

Cause: Attempting to create an event with a code that already exists Solution: Use a unique event code or search for existing event first

# Check if event code exists
existing = get_model("sale.event").search([["code", "=", "EXPO2026"]])
if existing:
    print("Event code already in use")
else:
    event_id = get_model("sale.event").create({
        "name": "New Event",
        "code": "EXPO2026"
    })

Products Not Appearing in Event

Cause: Products may not be published or the Many2Many link wasn't established Solution: Verify product publication status and use correct link syntax

# Verify product is published
product = get_model("product").browse(product_id)
if not product.is_published:
    print("Product is not published - cannot add to event")
else:
    # Use (4, id) to link existing record in Many2Many
    get_model("sale.event").write([event_id], {
        "products": [(4, product_id)]
    })

Lead Attribution Not Working

Cause: Incorrect related_id format when creating leads Solution: Use proper model reference format: "model.name,id"

# Wrong format:
lead = get_model("sale.lead").create({
    "related_id": event_id  # Wrong - just the ID
})

# Correct format:
lead = get_model("sale.lead").create({
    "related_id": "sale.event,%d" % event_id  # Correct - model,id
})

Security Considerations

Permission Model

  • sale_event_create - Create new events
  • sale_event_read - View event details
  • sale_event_write - Modify existing events
  • sale_event_delete - Remove events

Data Access

  • Event records are typically accessible across the organization for marketing and sales teams
  • Product visibility controlled by is_published flag ensures only approved items appear
  • Lead attribution data should be protected to maintain customer privacy
  • Event ROI metrics may be restricted to management-level access

Testing Examples

Unit Test: Create Event and Track Leads

def test_event_lead_tracking():
    # Create test event
    event_id = get_model("sale.event").create({
        "name": "Test Conference 2026",
        "code": "TEST-CONF-2026"
    })

    # Verify event created
    assert event_id is not None
    event = get_model("sale.event").browse(event_id)
    assert event.code == "TEST-CONF-2026"

    # Create lead linked to event
    lead_id = get_model("sale.lead").create({
        "name": "Test Lead",
        "related_id": "sale.event,%d" % event_id
    })

    # Verify lead attribution
    lead = get_model("sale.lead").browse(lead_id)
    assert lead.related_id == "sale.event,%d" % event_id

    # Verify search finds lead by event
    leads = get_model("sale.lead").search([
        ["related_id", "=", "sale.event,%d" % event_id]
    ])
    assert lead_id in leads

    # Cleanup
    get_model("sale.lead").delete([lead_id])
    get_model("sale.event").delete([event_id])

Version History

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


Additional Resources

  • Sale Lead Documentation: sale.lead
  • Sale Quotation Documentation: sale.quot
  • Product Catalog Documentation: product
  • Marketing Campaign Guide: External marketing module
  • CRM Integration Guide: Customer relationship management

Support & Feedback

For issues or questions about this module: 1. Check related model documentation for lead and quotation management 2. Review system logs for detailed error messages 3. Verify product publication status before linking to events 4. Test event creation and lead attribution in development environment first 5. Consult marketing team for event code naming conventions


This documentation is generated for developer onboarding and reference purposes.