Files
bakery-ia/services/tenant/app/schemas/tenants.py
2025-10-17 18:14:28 +02:00

169 lines
5.6 KiB
Python

# services/tenant/app/schemas/tenants.py
"""
Tenant schemas - FIXED VERSION
"""
from pydantic import BaseModel, Field, validator
from typing import Optional, List, Dict, Any
from datetime import datetime
from uuid import UUID
import re
class BakeryRegistration(BaseModel):
"""Bakery registration schema"""
name: str = Field(..., min_length=2, max_length=200)
address: str = Field(..., min_length=10, max_length=500)
city: str = Field(default="Madrid", max_length=100)
postal_code: str = Field(..., pattern=r"^\d{5}$")
phone: str = Field(..., min_length=9, max_length=20)
business_type: str = Field(default="bakery")
business_model: Optional[str] = Field(default="individual_bakery")
coupon_code: Optional[str] = Field(None, max_length=50, description="Promotional coupon code")
@validator('phone')
def validate_spanish_phone(cls, v):
"""Validate Spanish phone number"""
# Remove spaces and common separators
phone = re.sub(r'[\s\-\(\)]', '', v)
# Spanish mobile: +34 6/7/8/9 + 8 digits
# Spanish landline: +34 9 + 8 digits
patterns = [
r'^(\+34|0034|34)?[6789]\d{8}$', # Mobile
r'^(\+34|0034|34)?9\d{8}$', # Landline
]
if not any(re.match(pattern, phone) for pattern in patterns):
raise ValueError('Invalid Spanish phone number')
return v
@validator('business_type')
def validate_business_type(cls, v):
valid_types = ['bakery', 'coffee_shop', 'pastry_shop', 'restaurant']
if v not in valid_types:
raise ValueError(f'Business type must be one of: {valid_types}')
return v
@validator('business_model')
def validate_business_model(cls, v):
if v is None:
return v
valid_models = ['individual_bakery', 'central_baker_satellite', 'retail_bakery', 'hybrid_bakery']
if v not in valid_models:
raise ValueError(f'Business model must be one of: {valid_models}')
return v
class TenantResponse(BaseModel):
"""Tenant response schema - FIXED VERSION"""
id: str # ✅ Keep as str for Pydantic validation
name: str
subdomain: Optional[str]
business_type: str
business_model: Optional[str]
address: str
city: str
postal_code: str
phone: Optional[str]
is_active: bool
subscription_tier: str
ml_model_trained: bool
last_training_date: Optional[datetime]
owner_id: str # ✅ Keep as str for Pydantic validation
created_at: datetime
# ✅ FIX: Add custom validator to convert UUID to string
@validator('id', 'owner_id', pre=True)
def convert_uuid_to_string(cls, v):
"""Convert UUID objects to strings for JSON serialization"""
if isinstance(v, UUID):
return str(v)
return v
class Config:
from_attributes = True
class TenantAccessResponse(BaseModel):
"""Tenant access verification response"""
has_access: bool
role: str
permissions: List[str]
class TenantMemberResponse(BaseModel):
"""Tenant member response - FIXED VERSION"""
id: str
user_id: str
role: str
is_active: bool
joined_at: Optional[datetime]
# ✅ FIX: Add custom validator to convert UUID to string
@validator('id', 'user_id', pre=True)
def convert_uuid_to_string(cls, v):
"""Convert UUID objects to strings for JSON serialization"""
if isinstance(v, UUID):
return str(v)
return v
class Config:
from_attributes = True
class TenantUpdate(BaseModel):
"""Tenant update schema"""
name: Optional[str] = Field(None, min_length=2, max_length=200)
address: Optional[str] = Field(None, min_length=10, max_length=500)
phone: Optional[str] = None
business_type: Optional[str] = None
business_model: Optional[str] = None
class TenantListResponse(BaseModel):
"""Response schema for listing tenants"""
tenants: List[TenantResponse]
total: int
page: int
per_page: int
has_next: bool
has_prev: bool
class TenantMemberInvitation(BaseModel):
"""Schema for inviting a member to a tenant"""
email: str = Field(..., pattern=r'^[^@]+@[^@]+\.[^@]+$')
role: str = Field(..., pattern=r'^(admin|member|viewer)$')
message: Optional[str] = Field(None, max_length=500)
class TenantMemberUpdate(BaseModel):
"""Schema for updating tenant member"""
role: Optional[str] = Field(None, pattern=r'^(owner|admin|member|viewer)$')
is_active: Optional[bool] = None
class TenantSubscriptionUpdate(BaseModel):
"""Schema for updating tenant subscription"""
plan: str = Field(..., pattern=r'^(basic|professional|enterprise)$')
billing_cycle: str = Field(default="monthly", pattern=r'^(monthly|yearly)$')
class TenantStatsResponse(BaseModel):
"""Tenant statistics response"""
tenant_id: str
total_members: int
active_members: int
total_predictions: int
models_trained: int
last_training_date: Optional[datetime]
subscription_plan: str
subscription_status: str
@validator('tenant_id', pre=True)
def convert_uuid_to_string(cls, v):
"""Convert UUID objects to strings for JSON serialization"""
if isinstance(v, UUID):
return str(v)
return v
class TenantSearchRequest(BaseModel):
"""Tenant search request schema"""
query: Optional[str] = None
business_type: Optional[str] = None
city: Optional[str] = None
status: Optional[str] = None
limit: int = Field(default=50, ge=1, le=100)
offset: int = Field(default=0, ge=0)