docs: update company-vendor management documentation
- Document all admin UI pages (list, detail, edit) - Document transfer ownership modal with user search - Add user management API endpoints section - Update ownership transfer description - Fix encoding issue in marketplace-integration.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
331
docs/architecture/company-vendor-management.md
Normal file
331
docs/architecture/company-vendor-management.md
Normal file
@@ -0,0 +1,331 @@
|
||||
# Company-Vendor Management Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
The Wizamart platform implements a hierarchical multi-tenant architecture where **Companies** are the primary business entities and **Vendors** are storefronts/brands that operate under companies.
|
||||
|
||||
```
|
||||
Company (Business Entity)
|
||||
├── Owner (User)
|
||||
├── Contact Information
|
||||
├── Business Details
|
||||
└── Vendors (Storefronts/Brands)
|
||||
├── Vendor 1
|
||||
├── Vendor 2
|
||||
└── Vendor N
|
||||
```
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Company
|
||||
A **Company** represents a business entity on the platform. It contains:
|
||||
|
||||
- **Owner**: A user account that has full control over the company
|
||||
- **Contact Information**: Business email, phone, website
|
||||
- **Business Details**: Address, tax number
|
||||
- **Status**: Active/Inactive, Verified/Pending
|
||||
|
||||
### Vendor
|
||||
A **Vendor** (also called storefront or brand) represents a specific storefront operating under a company. A company can have multiple vendors.
|
||||
|
||||
- **Unique Identity**: Vendor code and subdomain
|
||||
- **Marketplace Integration**: CSV URLs for product feeds (FR, EN, DE)
|
||||
- **Status**: Active/Inactive, Verified/Pending
|
||||
- **Products**: Each vendor has its own product catalog
|
||||
|
||||
## Data Model
|
||||
|
||||
### Company Model
|
||||
```python
|
||||
class Company:
|
||||
id: int
|
||||
name: str
|
||||
description: str | None
|
||||
|
||||
# Owner (at company 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 # Company owner
|
||||
vendors: list[Vendor] # Storefronts under this company
|
||||
```
|
||||
|
||||
### Vendor Model
|
||||
```python
|
||||
class Vendor:
|
||||
id: int
|
||||
company_id: int # FK to Company
|
||||
vendor_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
|
||||
company: Company # Parent company (owner is accessed via company.owner)
|
||||
```
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
### 1. Owner at Company Level Only
|
||||
Previously, each vendor had its own `owner_user_id`. This has been refactored:
|
||||
|
||||
- **Before**: `Vendor.owner_user_id` - each vendor had a separate owner
|
||||
- **After**: `Company.owner_user_id` - ownership is at company level
|
||||
|
||||
**Rationale**: A business entity (company) should have a single owner who can manage all storefronts. This simplifies:
|
||||
- User management
|
||||
- Permission handling
|
||||
- Ownership transfer operations
|
||||
|
||||
### 2. Contact Information at Company Level
|
||||
Business contact information (email, phone, website, address, tax number) is now stored at the company level:
|
||||
|
||||
- **Before**: Vendors had contact fields
|
||||
- **After**: Contact info on Company model, vendors reference parent company
|
||||
|
||||
**Rationale**: Business details are typically the same across all storefronts of a company.
|
||||
|
||||
### 3. Clean Architecture
|
||||
The `Vendor.owner_user_id` field has been completely removed:
|
||||
|
||||
- Ownership is determined solely via the company relationship
|
||||
- Use `vendor.company.owner_user_id` to get the owner
|
||||
- Use `vendor.company.owner` to get the owner User object
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Company Management (Admin)
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| POST | `/api/v1/admin/companies` | Create company with owner |
|
||||
| GET | `/api/v1/admin/companies` | List all companies |
|
||||
| GET | `/api/v1/admin/companies/{id}` | Get company details |
|
||||
| PUT | `/api/v1/admin/companies/{id}` | Update company |
|
||||
| PUT | `/api/v1/admin/companies/{id}/verification` | Toggle verification |
|
||||
| PUT | `/api/v1/admin/companies/{id}/status` | Toggle active status |
|
||||
| POST | `/api/v1/admin/companies/{id}/transfer-ownership` | Transfer ownership |
|
||||
| DELETE | `/api/v1/admin/companies/{id}` | Delete company |
|
||||
|
||||
### Vendor Management (Admin)
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| POST | `/api/v1/admin/vendors` | Create vendor under company |
|
||||
| GET | `/api/v1/admin/vendors` | List all vendors |
|
||||
| GET | `/api/v1/admin/vendors/{id}` | Get vendor details |
|
||||
| PUT | `/api/v1/admin/vendors/{id}` | Update vendor |
|
||||
| PUT | `/api/v1/admin/vendors/{id}/verification` | Toggle verification |
|
||||
| PUT | `/api/v1/admin/vendors/{id}/status` | Toggle active status |
|
||||
| DELETE | `/api/v1/admin/vendors/{id}` | Delete vendor |
|
||||
|
||||
### 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
|
||||
|
||||
### CompanyService (`app/services/company_service.py`)
|
||||
Primary service for company operations:
|
||||
|
||||
- `create_company_with_owner()` - Creates company and owner user account
|
||||
- `get_company_by_id()` - Get company with vendors loaded
|
||||
- `get_companies()` - Paginated list with filtering
|
||||
- `update_company()` - Update company fields
|
||||
- `toggle_verification()` - Verify/unverify company
|
||||
- `toggle_active()` - Activate/deactivate company
|
||||
- `transfer_ownership()` - Transfer to new owner
|
||||
- `delete_company()` - Delete company (requires no vendors)
|
||||
|
||||
### AdminService (`app/services/admin_service.py`)
|
||||
Admin-specific vendor operations:
|
||||
|
||||
- `create_vendor()` - Create vendor under existing company
|
||||
- `get_all_vendors()` - Paginated list with company relationship
|
||||
- `update_vendor()` - Update vendor fields
|
||||
- `verify_vendor()` - Toggle verification
|
||||
- `toggle_vendor_status()` - Toggle active status
|
||||
- `delete_vendor()` - Delete vendor
|
||||
|
||||
### VendorService (`app/services/vendor_service.py`)
|
||||
Self-service vendor operations (for company owners):
|
||||
|
||||
- `create_vendor()` - Create vendor (requires company_id, validates ownership)
|
||||
- `get_vendors()` - Get vendors with access control
|
||||
- `get_vendor_by_code()` - Get single vendor
|
||||
|
||||
## Creating a New Company with Vendors
|
||||
|
||||
### Via Admin API
|
||||
```python
|
||||
# 1. Create company with owner
|
||||
POST /api/v1/admin/companies
|
||||
{
|
||||
"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 vendor under company
|
||||
POST /api/v1/admin/vendors
|
||||
{
|
||||
"company_id": 1,
|
||||
"vendor_code": "TECHSTORE",
|
||||
"subdomain": "tech-store",
|
||||
"name": "Tech Store",
|
||||
"description": "Consumer electronics storefront"
|
||||
}
|
||||
```
|
||||
|
||||
## Ownership Transfer
|
||||
|
||||
Ownership transfer is a critical operation available at the company level:
|
||||
|
||||
1. **Admin initiates transfer** via `/api/v1/admin/companies/{id}/transfer-ownership`
|
||||
2. **Company owner changes** - `Company.owner_user_id` updated
|
||||
3. **All vendors affected** - Vendors inherit new owner via company relationship
|
||||
4. **Audit trail** - Transfer reason logged
|
||||
|
||||
```python
|
||||
POST /api/v1/admin/companies/1/transfer-ownership
|
||||
{
|
||||
"new_owner_user_id": 42,
|
||||
"confirm_transfer": true,
|
||||
"transfer_reason": "Business acquisition"
|
||||
}
|
||||
```
|
||||
|
||||
## Admin UI Pages
|
||||
|
||||
### Company List Page (`/admin/companies`)
|
||||
- Lists all companies with owner, vendor count, and status
|
||||
- **Actions**: View, Edit, Delete (disabled if company has vendors)
|
||||
- Stats cards showing total, verified, active companies
|
||||
|
||||
### Company Detail Page (`/admin/companies/{id}`)
|
||||
- **Quick Actions** - Edit Company, Delete Company
|
||||
- **Status Cards** - Verification, Active status, Vendor count, Created date
|
||||
- **Information Sections** - Basic info, Contact info, Business details, Owner info
|
||||
- **Vendors Table** - Lists all vendors under this company with links
|
||||
- **More Actions** - Create Vendor button
|
||||
|
||||
### Company Edit Page (`/admin/companies/{id}/edit`)
|
||||
- **Quick Actions** - Verify/Unverify, Activate/Deactivate
|
||||
- **Edit Form** - Company details, contact info, business details
|
||||
- **Statistics** - Vendor counts (readonly)
|
||||
- **More Actions** - Transfer Ownership, Delete Company
|
||||
|
||||
### Vendor Detail Page (`/admin/vendors/{code}`)
|
||||
- **Quick Actions** - Edit Vendor, Delete Vendor
|
||||
- **Status Cards** - Verification, Active status, Created/Updated dates
|
||||
- **Information Sections** - Basic info, Contact info, Business details, Owner info
|
||||
- **More Actions** - View Parent Company link, Customize Theme
|
||||
|
||||
### Transfer Ownership Modal
|
||||
A modal dialog for transferring company 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 Vendors
|
||||
Always use `company_id` when creating vendors:
|
||||
```python
|
||||
# Correct
|
||||
vendor_data = VendorCreate(
|
||||
company_id=company.id,
|
||||
vendor_code="MYSTORE",
|
||||
subdomain="my-store",
|
||||
name="My Store"
|
||||
)
|
||||
|
||||
# Incorrect (old pattern - don't use)
|
||||
vendor_data = VendorCreate(
|
||||
owner_email="owner@example.com", # No longer supported
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
### Accessing Owner Information
|
||||
Use the company relationship:
|
||||
```python
|
||||
# Get owner User object
|
||||
owner = vendor.company.owner
|
||||
|
||||
# Get owner details
|
||||
owner_email = vendor.company.owner.email
|
||||
owner_id = vendor.company.owner_user_id
|
||||
```
|
||||
|
||||
### Checking Permissions
|
||||
For vendor operations, check company ownership:
|
||||
```python
|
||||
def can_manage_vendor(user, vendor):
|
||||
if user.role == "admin":
|
||||
return True
|
||||
return vendor.company.owner_user_id == user.id
|
||||
```
|
||||
|
||||
## Migration Notes
|
||||
|
||||
The `Vendor.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 vendors table
|
||||
3. Ownership is now determined via `vendor.company.owner_user_id`
|
||||
|
||||
If migrating from an older vendor-centric model:
|
||||
|
||||
1. Create companies for existing vendors (group by owner)
|
||||
2. Assign vendors to companies via `company_id`
|
||||
3. Copy contact info from vendors to companies
|
||||
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)
|
||||
@@ -163,7 +163,7 @@ All three frontends share the same core technologies:
|
||||
| Backend | FastAPI | REST API + routing |
|
||||
| Templates | Jinja2 | Server-side rendering |
|
||||
| Interactivity | Alpine.js 3.x | Client-side reactivity |
|
||||
| Styling | Tailwind CSS 2.x | Utility-first CSS (CDN + fallback) |
|
||||
| Styling | Tailwind CSS 2.2.x | Utility-first CSS (CDN + fallback) - [Build Guide](tailwind-css.md) |
|
||||
| Icons | Heroicons | SVG icon system |
|
||||
| HTTP Client | Fetch API | API requests |
|
||||
| State Management | Alpine.js reactive | No external state lib |
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user