# 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 ```python 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 ```python 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_id` to get the owner - Use `store.merchant.owner` to 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 account - `get_merchant_by_id()` - Get merchant with stores loaded - `get_merchants()` - Paginated list with filtering - `update_merchant()` - Update merchant fields - `toggle_verification()` - Verify/unverify merchant - `toggle_active()` - Activate/deactivate merchant - `transfer_ownership()` - Transfer to new owner - `delete_merchant()` - Delete merchant (requires no stores) ### AdminService (`app/services/admin_service.py`) Admin-specific store operations: - `create_store()` - Create store under existing merchant - `get_all_stores()` - Paginated list with merchant relationship - `update_store()` - Update store fields - `verify_store()` - Toggle verification - `toggle_store_status()` - Toggle active status - `delete_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 control - `get_store_by_code()` - Get single store ## Creating a New Merchant with Stores ### Via Admin API ```python # 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: 1. **Admin initiates transfer** via `/api/v1/admin/merchants/{id}/transfer-ownership` 2. **Merchant owner changes** - `Merchant.owner_user_id` updated 3. **All stores affected** - Stores inherit new owner via merchant relationship 4. **Audit trail** - Transfer reason logged ```python 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: ```python # 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: ```python # 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: ```python 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: 1. Run the migration: `alembic upgrade head` 2. This will drop the `owner_user_id` column from stores table 3. Ownership is now determined via `store.merchant.owner_user_id` If migrating from an older store-centric model: 1. Create merchants for existing stores (group by owner) 2. Assign stores to merchants via `merchant_id` 3. Copy contact info from stores to merchants 4. Run the migration to drop the old owner_user_id column ## Related Documentation - [Multi-Tenant Architecture](multi-tenant.md) - [Authentication & RBAC](auth-rbac.md) - [Models Structure](models-structure.md) - [Admin Integration Guide](../backend/admin-integration-guide.md)