Shop Documentation¶
Overview¶
The Shop module (shop) defines physical or virtual stores, retail locations, sales branches, or distribution points within the organization. It serves as a master data model for organizing sales operations by location, enabling territory management, performance tracking, and location-based reporting.
Model Information¶
Model Name: shop
Display Name: Shop
Key Fields: name, code
Features¶
- ❌ Audit logging enabled (
_audit_log) - ❌ Multi-company support (
company_id) - ❌ Full-text content search (
_content_search) - ❌ Unique key constraint
- ✅ Simple location management
- ✅ Searchable name and code fields
- ✅ Territory/branch organization
Key Fields Reference¶
Basic Information Fields¶
| Field | Type | Required | Description |
|---|---|---|---|
name |
Char | ✅ | Shop name or location identifier (searchable) |
code |
Char | ❌ | Unique short code for the shop (searchable) |
description |
Text | ❌ | Additional details about the shop location (searchable) |
Understanding Shop Types¶
While the model doesn't enforce specific shop types, organizations typically use the shop model for:
Physical Retail Locations¶
- Retail Stores: Traditional brick-and-mortar shops
- Showrooms: Display centers for products
- Outlets: Discount or factory outlet stores
- Kiosks: Small sales points in malls or public spaces
Sales Territories¶
- Regional Offices: Branch offices serving specific territories
- Distribution Centers: Warehouses with sales functions
- Sales Zones: Geographic areas for performance tracking
Virtual/Online Channels¶
- E-commerce: Online store or website sales
- Mobile App: Sales through mobile applications
- Phone Sales: Call center or telesales operations
- Partner Channels: Third-party sales channels
API Methods¶
1. Create Shop¶
Method: create(vals, context)
Creates a new shop record.
Parameters:
vals = {
"name": "Downtown Store", # Required: Shop name
"code": "DT-001", # Optional: Shop code
"description": "Main retail location in downtown district" # Optional
}
Returns: int - New shop record ID
Example:
# Create a retail store
shop_id = get_model("shop").create({
"name": "Downtown Store",
"code": "DT-001",
"description": "Main retail location in downtown district"
})
2. Update Shop¶
Method: write(ids, vals, context)
Updates existing shop records.
Example:
# Update shop details
get_model("shop").write([shop_id], {
"description": "Flagship retail location - recently renovated"
})
3. Delete Shop¶
Method: delete(ids, context)
Deletes shop records.
Example:
Warning: Ensure no active records reference this shop before deletion.
Common Use Cases¶
Use Case 1: Setting Up Retail Store Network¶
Scenario: Configure shops for a multi-location retail chain.
# Define store locations
stores = [
{
"name": "Downtown Store",
"code": "DT-001",
"description": "Flagship location in central business district"
},
{
"name": "Westside Mall",
"code": "WM-002",
"description": "Retail location in Westside Shopping Mall"
},
{
"name": "Airport Branch",
"code": "AP-003",
"description": "Small outlet at International Airport Terminal 2"
},
{
"name": "Suburb Plaza",
"code": "SP-004",
"description": "Community shopping center location"
}
]
# Create all shops
shop_ids = []
for store in stores:
shop_id = get_model("shop").create(store)
shop_ids.append(shop_id)
print(f"Created shop: {store['name']} ({store['code']})")
print(f"\nTotal shops created: {len(shop_ids)}")
Use Case 2: Sales Territory Organization¶
Scenario: Define shops as sales territories for commission tracking.
# Create regional territories
territories = [
{
"name": "North Region",
"code": "NORTH",
"description": "Sales territory covering northern districts"
},
{
"name": "South Region",
"code": "SOUTH",
"description": "Sales territory covering southern districts"
},
{
"name": "East Region",
"code": "EAST",
"description": "Sales territory covering eastern districts"
},
{
"name": "West Region",
"code": "WEST",
"description": "Sales territory covering western districts"
}
]
# Create territories
for territory in territories:
shop_id = get_model("shop").create(territory)
print(f"Created territory: {territory['name']}")
Use Case 3: Multi-Channel Sales Setup¶
Scenario: Track sales across different channels (online, retail, wholesale).
# Define sales channels as shops
channels = [
{
"name": "E-Commerce Website",
"code": "ONLINE",
"description": "Online store - www.company.com"
},
{
"name": "Mobile App Sales",
"code": "MOBILE",
"description": "Sales through company mobile application"
},
{
"name": "Retail Stores",
"code": "RETAIL",
"description": "Combined physical retail locations"
},
{
"name": "Wholesale Channel",
"code": "WHOLESALE",
"description": "B2B wholesale distribution"
},
{
"name": "Partner Network",
"code": "PARTNER",
"description": "Third-party reseller channel"
}
]
# Create channels
for channel in channels:
shop_id = get_model("shop").create(channel)
print(f"Created channel: {channel['name']}")
Use Case 4: Assigning Sellers to Shops¶
Scenario: Link sellers to specific shop locations for performance tracking.
# Get shop
downtown_shop = get_model("shop").search_browse([
["code", "=", "DT-001"]
])[0]
# Create sellers assigned to this shop
sellers = [
{
"name": "John Smith",
"code": "JS-DT-001",
"type": "internal",
"description": f"Sales rep at {downtown_shop.name}"
},
{
"name": "Jane Doe",
"code": "JD-DT-002",
"type": "internal",
"description": f"Sales manager at {downtown_shop.name}"
}
]
for seller_data in sellers:
seller_id = get_model("seller").create(seller_data)
print(f"Created seller {seller_data['name']} for shop {downtown_shop.name}")
Note: The current shop model doesn't have a direct relationship field to sellers, but you can reference the shop in seller descriptions or extend the models to add explicit relationships.
Use Case 5: Shop Performance Reporting¶
Scenario: Generate sales report comparing shop performance.
# Get all shops
shops = get_model("shop").search_browse([])
# Define reporting period
date_from = "2025-01-01"
date_to = "2025-01-31"
print("Shop Performance Report")
print("=" * 80)
print(f"Period: {date_from} to {date_to}")
print()
# For each shop, find associated sellers and calculate performance
# (This assumes sellers are tagged with shop codes in their descriptions)
for shop in shops:
print(f"\n{shop.name} ({shop.code})")
print("-" * 60)
# Find sellers associated with this shop (by code in description)
sellers = get_model("seller").search_browse([
["description", "ilike", f"%{shop.code}%"]
])
if not sellers:
print(" No sellers assigned to this shop")
continue
total_shop_sales = 0
total_shop_commission = 0
for seller in sellers:
# Get sales for this seller in the period
sales = get_model("sale.order").search_browse([
["seller_id", "=", seller.id],
["state", "in", ["confirmed", "done"]],
["date", ">=", date_from],
["date", "<=", date_to]
])
seller_total = sum(sale.amount_total for sale in sales)
seller_commission = get_model("seller").calc_commission(
[seller.id],
date_from=date_from,
date_to=date_to
)
total_shop_sales += seller_total
total_shop_commission += seller_commission
print(f" {seller.name:20s}: ${seller_total:10,.2f} "
f"(Commission: ${seller_commission:,.2f})")
print(f" {'TOTAL':20s}: ${total_shop_sales:10,.2f} "
f"(Commission: ${total_shop_commission:,.2f})")
Use Case 6: Shop Code Naming Conventions¶
Scenario: Establish consistent shop code naming for easy identification.
# Good: Consistent naming convention
shops = [
{"name": "Downtown Store", "code": "RET-DT-001"}, # Retail-Downtown-001
{"name": "Westside Mall", "code": "RET-WM-002"}, # Retail-Westside-002
{"name": "Online Store", "code": "ONL-WEB-001"}, # Online-Web-001
{"name": "Mobile App", "code": "ONL-APP-001"}, # Online-App-001
{"name": "North Territory", "code": "TER-N-001"}, # Territory-North-001
{"name": "Wholesale Hub", "code": "WHO-HQ-001"} # Wholesale-HQ-001
]
for shop in shops:
shop_id = get_model("shop").create(shop)
print(f"Created: {shop['code']} - {shop['name']}")
Code Structure: - Prefix: Channel type (RET=Retail, ONL=Online, TER=Territory, WHO=Wholesale) - Location: Location code (DT=Downtown, WM=Westside Mall, etc.) - Number: Sequential identifier
Use Case 7: Searching and Filtering Shops¶
Scenario: Find specific shops using various search criteria.
# Search by exact code
downtown = get_model("shop").search_browse([
["code", "=", "DT-001"]
])
# Search by name (partial match)
mall_shops = get_model("shop").search_browse([
["name", "ilike", "%mall%"]
])
# Search by description keywords
online_channels = get_model("shop").search_browse([
["description", "ilike", "%online%"]
])
# Get all shops (for dropdown lists, etc.)
all_shops = get_model("shop").search_browse([])
print(f"Found {len(all_shops)} total shops")
print("\nShop List:")
for shop in all_shops:
print(f" {shop.code:15s} - {shop.name}")
Integration with Other Models¶
Seller Assignment¶
While not directly linked in the current model, shops can be associated with sellers through:
- Description Field: Include shop code in seller description
- Custom Extensions: Add shop_id field to seller model
- Naming Convention: Use shop code prefix in seller codes
# Example: Linking seller to shop via description
shop = get_model("shop").browse(shop_id)
seller_id = get_model("seller").create({
"name": "John Smith",
"code": f"{shop.code}-JS001",
"description": f"Sales representative at {shop.name} (Shop: {shop.code})"
})
Sale Order Tracking¶
Track which shop generated each sale:
# If sale.order model has shop_id field (custom extension)
order_id = get_model("sale.order").create({
"contact_id": customer_id,
"seller_id": seller_id,
"shop_id": shop_id, # Custom field
"date": "2025-01-15",
"lines": [...]
})
# Generate sales report by shop
shop_sales = {}
sales = get_model("sale.order").search_browse([
["state", "in", ["confirmed", "done"]],
["date", ">=", date_from],
["date", "<=", date_to]
])
for sale in sales:
if hasattr(sale, 'shop_id') and sale.shop_id:
shop_name = sale.shop_id.name
shop_sales.setdefault(shop_name, 0)
shop_sales[shop_name] += sale.amount_total
Search Functions¶
Search by Code¶
Search by Name¶
# Find shops with "downtown" in name (case-insensitive)
shops = get_model("shop").search_browse([["name", "ilike", "%downtown%"]])
Search by Description¶
# Find shops by description keywords
retail_shops = get_model("shop").search_browse([["description", "ilike", "%retail%"]])
Get All Shops¶
Best Practices¶
1. Use Meaningful Shop Codes¶
# Good: Descriptive, structured codes
shops = [
{"name": "Downtown Store", "code": "RET-DT-001"},
{"name": "Online Store", "code": "ONL-WEB-001"},
{"name": "North Region", "code": "TER-NORTH"}
]
# Bad: Cryptic or inconsistent codes
shops = [
{"name": "Downtown Store", "code": "A1"},
{"name": "Online Store", "code": "websales"},
{"name": "North Region", "code": "N"}
]
Why This Matters: Clear codes improve reporting, filtering, and system integration.
2. Maintain Shop Master Data¶
# Regularly review and update shop information
def audit_shop_data():
shops = get_model("shop").search_browse([])
print("Shop Master Data Audit")
print("=" * 60)
for shop in shops:
# Check for missing information
issues = []
if not shop.code:
issues.append("Missing code")
if not shop.description:
issues.append("Missing description")
if issues:
print(f"{shop.name:30s} - ISSUES: {', '.join(issues)}")
else:
print(f"{shop.name:30s} - OK")
audit_shop_data()
3. Document Shop Purpose¶
# Good: Clear, informative descriptions
get_model("shop").create({
"name": "E-Commerce Website",
"code": "ONL-WEB-001",
"description": "Primary online sales channel - www.company.com - Launched 2024"
})
# Bad: Vague or missing description
get_model("shop").create({
"name": "E-Commerce Website",
"code": "ONL-WEB-001",
"description": "website"
})
4. Standardize Shop Types¶
# Define standard shop types for your organization
SHOP_TYPES = {
"RETAIL": "Physical retail store",
"ONLINE": "E-commerce or digital channel",
"WHOLESALE": "B2B wholesale operation",
"TERRITORY": "Geographic sales territory",
"PARTNER": "Third-party partner channel"
}
# Use consistent prefixes
def create_shop(shop_type, location, number, description):
if shop_type not in SHOP_TYPES:
raise ValueError(f"Invalid shop type: {shop_type}")
code = f"{shop_type}-{location}-{number:03d}"
name = f"{location} {SHOP_TYPES[shop_type].title()}"
return get_model("shop").create({
"name": name,
"code": code,
"description": f"{description} | Type: {SHOP_TYPES[shop_type]}"
})
# Usage
shop_id = create_shop("RETAIL", "Downtown", 1, "Flagship store location")
Related Models¶
| Model | Relationship | Description |
|---|---|---|
seller |
Indirect | Sellers can be associated with shops via description or custom fields |
sale.order |
Indirect | Sales orders can reference shop through custom extension |
stock.location |
Potential | Shops may correspond to inventory locations |
Performance Tips¶
1. Index Frequently Searched Fields¶
- Ensure
nameandcodefields are properly indexed (already marked searchable) - Use exact match searches when possible for better performance
2. Cache Shop Lists¶
# Cache shop list for dropdowns/lookups
_shop_cache = None
def get_shop_list(force_refresh=False):
global _shop_cache
if _shop_cache is None or force_refresh:
_shop_cache = get_model("shop").search_browse([], order="name")
return _shop_cache
3. Limit Result Sets¶
# When displaying shops, limit results if there are many
shops = get_model("shop").search_browse([], limit=100, order="name")
Troubleshooting¶
"Duplicate shop codes"¶
Cause: Multiple shops created with same code.
Solution:
# Check for duplicate codes before creating
code = "DT-001"
existing = get_model("shop").search([["code", "=", code]])
if existing:
print(f"ERROR: Shop code {code} already exists!")
else:
shop_id = get_model("shop").create({
"name": "New Shop",
"code": code
})
"Cannot find shop"¶
Cause: Shop code or name spelled incorrectly, or shop deleted.
Solution:
# Search with flexible matching
search_term = "downtown"
shops = get_model("shop").search_browse([
"|",
["name", "ilike", f"%{search_term}%"],
["code", "ilike", f"%{search_term}%"],
["description", "ilike", f"%{search_term}%"]
])
if shops:
print(f"Found {len(shops)} matching shops:")
for shop in shops:
print(f" {shop.code} - {shop.name}")
else:
print("No shops found matching search term")
Testing Examples¶
Unit Test: Shop Creation¶
def test_shop_creation():
# Create test shop
shop_id = get_model("shop").create({
"name": "Test Shop",
"code": "TEST-001",
"description": "Shop for unit testing"
})
# Verify shop was created
shop = get_model("shop").browse(shop_id)
assert shop.name == "Test Shop"
assert shop.code == "TEST-001"
# Verify searchable
found = get_model("shop").search([["code", "=", "TEST-001"]])
assert len(found) == 1
print("Shop creation test passed!")
# Cleanup
get_model("shop").delete([shop_id])
Security Considerations¶
Permission Model¶
- Create/Edit Shops: Typically restricted to system administrators or sales managers
- View Shops: Generally accessible to all sales and reporting users
- Delete Shops: Requires administrative approval
Data Access¶
- Shop data is typically non-confidential master data
- Shop codes may be used in public-facing URLs or identifiers
- Ensure deleted shops don't break historical reporting
Version History¶
Last Updated: 2025-01-05 Model Version: shop.py Framework: Netforce
Additional Resources¶
- Seller Documentation:
seller - Sale Order Documentation:
sale.order - Stock Location Documentation:
stock.location(if applicable)
Support & Feedback¶
For issues or questions about this module: 1. Verify shop codes are unique and follow naming conventions 2. Check that shop data is properly maintained 3. Ensure deleted shops don't have active references 4. Review integration with seller and sales order models 5. Test shop searches with various criteria
This documentation is generated for developer onboarding and reference purposes.