Skip to content

Sales Channel Documentation

Overview

The Sales Channel module (sale.channel) provides a classification system for tracking and managing different sales distribution channels. This master data model enables companies to organize sales by channel (retail, online, wholesale, etc.), assign channel-specific pricing, and analyze performance across different go-to-market strategies.


Model Information

Model Name: sale.channel Display Name: Sales Channel Key Fields: None (no unique constraints)

Features

  • Simple channel classification
  • Price list integration per channel
  • Search-enabled fields
  • Minimal master data structure

Common Sales Channel Types

Channel Type Example Code Typical Use Case
Retail Store RETAIL Physical storefront sales
E-commerce ONLINE Website and mobile app sales
Wholesale WHOLESALE B2B distributor sales
Direct Sales DIRECT Field sales team
Partner/Reseller PARTNER Channel partner sales
Marketplace MARKETPLACE Third-party platform sales (Amazon, eBay)

Field Reference

Basic Fields

Field Type Required Description
name Char Yes Channel name (e.g., "E-commerce Website")
code Char No Short code for identification (e.g., "ECOM")

Configuration Fields

Field Type Description
pricelist_id Many2One Default price list for this channel

API Methods

1. Create Sales Channel

Method: create(vals, context)

Example:

# Create an e-commerce channel
channel_id = get_model("sale.channel").create({
    "name": "E-commerce Website",
    "code": "ECOM",
    "pricelist_id": online_pricelist_id
})

2. Search Channels

# Find channel by code
channel_ids = get_model("sale.channel").search([["code", "=", "ECOM"]])

# Get channel with price list
channel = get_model("sale.channel").browse(channel_id)
pricelist = channel.pricelist_id

Model Relationship Description
sale.order Referenced by Orders reference sales channel via sale_channel_id
price.list Many2One Each channel can have default pricing

Common Use Cases

Use Case 1: Initial Channel Setup

# Create standard sales channels
channels = [
    {"name": "Retail Stores", "code": "RETAIL", "pricelist_id": retail_price_id},
    {"name": "E-commerce", "code": "ECOM", "pricelist_id": online_price_id},
    {"name": "Wholesale", "code": "WHOLESALE", "pricelist_id": wholesale_price_id},
    {"name": "Direct Sales", "code": "DIRECT", "pricelist_id": standard_price_id}
]

for channel in channels:
    get_model("sale.channel").create(channel)

Use Case 2: Channel-Based Pricing

# Apply channel-specific pricing when creating orders
def create_order_with_channel_pricing(customer_id, channel_code, products):
    # Get channel
    channel_ids = get_model("sale.channel").search([["code", "=", channel_code]])
    if not channel_ids:
        raise Exception(f"Channel {channel_code} not found")

    channel = get_model("sale.channel").browse(channel_ids[0])

    # Create order with channel's price list
    order_id = get_model("sale.order").create({
        "contact_id": customer_id,
        "sale_channel_id": channel.id,
        "price_list_id": channel.pricelist_id.id if channel.pricelist_id else None,
        "lines": [("create", line) for line in products]
    })

    return order_id

Use Case 3: Channel Performance Reporting

# Sales by channel report
def get_channel_performance(date_from, date_to):
    results = []

    channel_ids = get_model("sale.channel").search([])
    channels = get_model("sale.channel").browse(channel_ids)

    for channel in channels:
        # Find orders in this channel
        order_ids = get_model("sale.order").search([
            ["sale_channel_id", "=", channel.id],
            ["date", ">=", date_from],
            ["date", "<=", date_to],
            ["state", "in", ["confirmed", "done"]]
        ])

        orders = get_model("sale.order").browse(order_ids)
        total_revenue = sum(o.amount_total for o in orders)

        results.append({
            "channel": channel.name,
            "orders": len(orders),
            "revenue": total_revenue
        })

    return results

Use Case 4: Multi-Channel Customer

# Track customer purchases across channels
def get_customer_channel_history(customer_id):
    # Find all orders for customer
    order_ids = get_model("sale.order").search([
        ["contact_id", "=", customer_id]
    ])

    orders = get_model("sale.order").browse(order_ids)

    # Group by channel
    by_channel = {}
    for order in orders:
        channel_name = order.sale_channel_id.name if order.sale_channel_id else "No Channel"

        if channel_name not in by_channel:
            by_channel[channel_name] = {
                "count": 0,
                "total": 0
            }

        by_channel[channel_name]["count"] += 1
        by_channel[channel_name]["total"] += order.amount_total

    return by_channel

Use Case 5: Channel-Specific Promotions

# Apply channel-specific discounts
def apply_channel_discount(order_id):
    order = get_model("sale.order").browse(order_id)

    if not order.sale_channel_id:
        return

    # Different discounts per channel
    channel_discounts = {
        "ECOM": 0.05,      # 5% online discount
        "WHOLESALE": 0.15,  # 15% wholesale discount
        "PARTNER": 0.20     # 20% partner discount
    }

    channel_code = order.sale_channel_id.code
    discount = channel_discounts.get(channel_code, 0)

    if discount > 0:
        # Apply discount to order lines
        for line in order.lines:
            line_discount = line.amount * discount
            # Update line with discount

Best Practices

1. Naming Conventions

# Good: Clear, business-oriented names
{"name": "E-commerce Website", "code": "ECOM"}
{"name": "Retail Stores", "code": "RETAIL"}
{"name": "Partner Network", "code": "PARTNER"}

# Bad: Technical or vague names
{"name": "Channel 1", "code": "CH1"}
{"name": "Online", "code": "online"}  # Inconsistent casing

Guidelines: - Use business-friendly channel names - Uppercase codes for consistency - Be specific (not just "Online" but "E-commerce Website")


2. Price List Assignment

# Good: Each channel has appropriate pricing
channels_pricing = [
    {"name": "Retail", "pricelist": "Retail Price List"},
    {"name": "E-commerce", "pricelist": "Online Promotional Prices"},
    {"name": "Wholesale", "pricelist": "Bulk Pricing"}
]

# Each channel targets different customer segments with appropriate pricing

3. Channel Organization

Start with core channels: - Physical retail - Online/E-commerce - Wholesale/Distribution

Add specialized channels only when needed: - Marketplace platforms - Partner/reseller programs - Corporate direct sales


4. Data Governance

Channel ownership: - Sales operations manager owns channel master data - Marketing reviews channel definitions quarterly - Finance validates price list assignments

Regular review: - Analyze channel performance monthly - Retire underperforming channels - Update pricing strategies per channel


Performance Tips

1. Cache Channel Lookups

_channel_cache = {}

def get_channel_by_code(code):
    if code not in _channel_cache:
        ids = get_model("sale.channel").search([["code", "=", code]])
        if ids:
            _channel_cache[code] = ids[0]
    return _channel_cache.get(code)

2. Use Codes for Integration

# API or external systems should use codes, not names
channel_code = "ECOM"  # Stable identifier
# Not: channel_name = "E-commerce"  # May change

Troubleshooting

"Orders missing channel information"

Cause: Channel not set on order creation. Solution:

# Set default channel based on order source
def set_default_channel(order_id, source):
    channel_mapping = {
        "website": "ECOM",
        "pos": "RETAIL",
        "api": "PARTNER"
    }

    channel_code = channel_mapping.get(source)
    if channel_code:
        channel_ids = get_model("sale.channel").search([["code", "=", channel_code]])
        if channel_ids:
            get_model("sale.order").write([order_id], {
                "sale_channel_id": channel_ids[0]
            })


Security Considerations

Permission Model

  • All sales users need read access
  • Sales managers can create/modify channels
  • Finance can update price list assignments

Data Access

  • Channels are company-wide master data
  • No row-level security typically needed

Integration Points

Internal Modules

  • sale.order: Orders track which channel they came from
  • price.list: Each channel can have default pricing
  • report: Channel analysis and performance metrics

Version History

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


Additional Resources

  • Sales Order Documentation: sale.order
  • Price List Configuration: price.list
  • Channel Analytics Guide

This documentation is generated for developer onboarding and reference purposes.