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:
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:
This translates to:
Event Lifecycle¶
Sales events typically follow a three-phase lifecycle:
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 |
Related Models¶
| 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¶
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_idfield 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 eventssale_event_read- View event detailssale_event_write- Modify existing eventssale_event_delete- Remove events
Data Access¶
- Event records are typically accessible across the organization for marketing and sales teams
- Product visibility controlled by
is_publishedflag 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.