Replace all ~1,086 occurrences of Wizamart/wizamart/WIZAMART/WizaMart with Orion/orion/ORION across 184 files. This includes database identifiers, email addresses, domain references, R2 bucket names, DNS prefixes, encryption salt, Celery app name, config defaults, Docker configs, CI configs, documentation, seed data, and templates. Renames homepage-wizamart.html template to homepage-orion.html. Fixes duplicate file_pattern key in api.yaml architecture rule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10 KiB
Merchant-Store Management Architecture
Overview
The Orion platform implements a hierarchical multi-tenant architecture where Merchants are the primary business entities and Stores are storefronts/brands that operate under merchants.
Merchant (Business Entity)
├── Owner (User)
├── Contact Information
├── Business Details
└── Stores (Storefronts/Brands)
├── Store 1
├── Store 2
└── Store N
Key Concepts
Merchant
A Merchant represents a business entity on the platform. It contains:
- Owner: A user account that has full control over the merchant
- Contact Information: Business email, phone, website
- Business Details: Address, tax number
- Status: Active/Inactive, Verified/Pending
Store
A Store (also called storefront or brand) represents a specific storefront operating under a merchant. A merchant can have multiple stores.
- Unique Identity: Store code and subdomain
- Marketplace Integration: CSV URLs for product feeds (FR, EN, DE)
- Status: Active/Inactive, Verified/Pending
- Products: Each store has its own product catalog
Data Model
Merchant Model
class Merchant:
id: int
name: str
description: str | None
# Owner (at merchant level)
owner_user_id: int # FK to User
# Contact Information
contact_email: str
contact_phone: str | None
website: str | None
# Business Details
business_address: str | None
tax_number: str | None
# Status
is_active: bool
is_verified: bool
# Timestamps
created_at: datetime
updated_at: datetime
# Relationships
owner: User # Merchant owner
stores: list[Store] # Storefronts under this merchant
Store Model
class Store:
id: int
merchant_id: int # FK to Merchant
store_code: str # Unique identifier (e.g., "TECHSTORE")
subdomain: str # URL subdomain (e.g., "tech-store")
name: str
description: str | None
# Marketplace URLs (brand-specific)
letzshop_csv_url_fr: str | None
letzshop_csv_url_en: str | None
letzshop_csv_url_de: str | None
# Status
is_active: bool
is_verified: bool
# Timestamps
created_at: datetime
updated_at: datetime
# Relationships
merchant: Merchant # Parent merchant (owner is accessed via merchant.owner)
Key Design Decisions
1. Owner at Merchant Level Only
Previously, each store had its own owner_user_id. This has been refactored:
- Before:
Store.owner_user_id- each store had a separate owner - After:
Merchant.owner_user_id- ownership is at merchant level
Rationale: A business entity (merchant) should have a single owner who can manage all storefronts. This simplifies:
- User management
- Permission handling
- Ownership transfer operations
2. Contact Information at Merchant Level
Business contact information (email, phone, website, address, tax number) is now stored at the merchant level:
- Before: Stores had contact fields
- After: Contact info on Merchant model, stores reference parent merchant
Rationale: Business details are typically the same across all storefronts of a merchant.
3. Clean Architecture
The Store.owner_user_id field has been completely removed:
- Ownership is determined solely via the merchant relationship
- Use
store.merchant.owner_user_idto get the owner - Use
store.merchant.ownerto get the owner User object
API Endpoints
Merchant Management (Admin)
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/admin/merchants |
Create merchant with owner |
| GET | /api/v1/admin/merchants |
List all merchants |
| GET | /api/v1/admin/merchants/{id} |
Get merchant details |
| PUT | /api/v1/admin/merchants/{id} |
Update merchant |
| PUT | /api/v1/admin/merchants/{id}/verification |
Toggle verification |
| PUT | /api/v1/admin/merchants/{id}/status |
Toggle active status |
| POST | /api/v1/admin/merchants/{id}/transfer-ownership |
Transfer ownership |
| DELETE | /api/v1/admin/merchants/{id} |
Delete merchant |
Store Management (Admin)
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/admin/stores |
Create store under merchant |
| GET | /api/v1/admin/stores |
List all stores |
| GET | /api/v1/admin/stores/{id} |
Get store details |
| PUT | /api/v1/admin/stores/{id} |
Update store |
| PUT | /api/v1/admin/stores/{id}/verification |
Toggle verification |
| PUT | /api/v1/admin/stores/{id}/status |
Toggle active status |
| DELETE | /api/v1/admin/stores/{id} |
Delete store |
User Management (Admin)
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/admin/users |
List all users |
| GET | /api/v1/admin/users/search?q={query} |
Search users by name/email |
| PUT | /api/v1/admin/users/{id}/status |
Toggle user status |
| GET | /api/v1/admin/users/stats |
Get user statistics |
Service Layer
MerchantService (app/services/merchant_service.py)
Primary service for merchant operations:
create_merchant_with_owner()- Creates merchant and owner user accountget_merchant_by_id()- Get merchant with stores loadedget_merchants()- Paginated list with filteringupdate_merchant()- Update merchant fieldstoggle_verification()- Verify/unverify merchanttoggle_active()- Activate/deactivate merchanttransfer_ownership()- Transfer to new ownerdelete_merchant()- Delete merchant (requires no stores)
AdminService (app/services/admin_service.py)
Admin-specific store operations:
create_store()- Create store under existing merchantget_all_stores()- Paginated list with merchant relationshipupdate_store()- Update store fieldsverify_store()- Toggle verificationtoggle_store_status()- Toggle active statusdelete_store()- Delete store
StoreService (app/services/store_service.py)
Self-service store operations (for merchant owners):
create_store()- Create store (requires merchant_id, validates ownership)get_stores()- Get stores with access controlget_store_by_code()- Get single store
Creating a New Merchant with Stores
Via Admin API
# 1. Create merchant with owner
POST /api/v1/admin/merchants
{
"name": "Tech Solutions Ltd",
"owner_email": "owner@techsolutions.com",
"contact_email": "info@techsolutions.com",
"contact_phone": "+352 123 456",
"website": "https://techsolutions.com",
"business_address": "123 Tech Street, Luxembourg",
"tax_number": "LU12345678"
}
# Response includes temporary password for owner
# 2. Create store under merchant
POST /api/v1/admin/stores
{
"merchant_id": 1,
"store_code": "TECHSTORE",
"subdomain": "tech-store",
"name": "Tech Store",
"description": "Consumer electronics storefront"
}
Ownership Transfer
Ownership transfer is a critical operation available at the merchant level:
- Admin initiates transfer via
/api/v1/admin/merchants/{id}/transfer-ownership - Merchant owner changes -
Merchant.owner_user_idupdated - All stores affected - Stores inherit new owner via merchant relationship
- Audit trail - Transfer reason logged
POST /api/v1/admin/merchants/1/transfer-ownership
{
"new_owner_user_id": 42,
"confirm_transfer": true,
"transfer_reason": "Business acquisition"
}
Admin UI Pages
Merchant List Page (/admin/merchants)
- Lists all merchants with owner, store count, and status
- Actions: View, Edit, Delete (disabled if merchant has stores)
- Stats cards showing total, verified, active merchants
Merchant Detail Page (/admin/merchants/{id})
- Quick Actions - Edit Merchant, Delete Merchant
- Status Cards - Verification, Active status, Store count, Created date
- Information Sections - Basic info, Contact info, Business details, Owner info
- Stores Table - Lists all stores under this merchant with links
- More Actions - Create Store button
Merchant Edit Page (/admin/merchants/{id}/edit)
- Quick Actions - Verify/Unverify, Activate/Deactivate
- Edit Form - Merchant details, contact info, business details
- Statistics - Store counts (readonly)
- More Actions - Transfer Ownership, Delete Merchant
Store Detail Page (/admin/stores/{code})
- Quick Actions - Edit Store, Delete Store
- Status Cards - Verification, Active status, Created/Updated dates
- Information Sections - Basic info, Contact info, Business details, Owner info
- More Actions - View Parent Merchant link, Customize Theme
Transfer Ownership Modal
A modal dialog for transferring merchant ownership:
- User Search - Autocomplete search by username or email
- Selected User Display - Shows selected user with option to clear
- Transfer Reason - Optional field for audit trail
- Confirmation Checkbox - Required before transfer
- Validation - Inline error messages for missing fields
Best Practices
Creating Stores
Always use merchant_id when creating stores:
# Correct
store_data = StoreCreate(
merchant_id=merchant.id,
store_code="MYSTORE",
subdomain="my-store",
name="My Store"
)
# Incorrect (old pattern - don't use)
store_data = StoreCreate(
owner_email="owner@example.com", # No longer supported
...
)
Accessing Owner Information
Use the merchant relationship:
# Get owner User object
owner = store.merchant.owner
# Get owner details
owner_email = store.merchant.owner.email
owner_id = store.merchant.owner_user_id
Checking Permissions
For store operations, check merchant ownership:
def can_manage_store(user, store):
if user.role == "admin":
return True
return store.merchant.owner_user_id == user.id
Migration Notes
The Store.owner_user_id column has been removed. If you have an existing database:
- Run the migration:
alembic upgrade head - This will drop the
owner_user_idcolumn from stores table - Ownership is now determined via
store.merchant.owner_user_id
If migrating from an older store-centric model:
- Create merchants for existing stores (group by owner)
- Assign stores to merchants via
merchant_id - Copy contact info from stores to merchants
- Run the migration to drop the old owner_user_id column