REFACTOR data service
This commit is contained in:
25
services/sales/app/schemas/__init__.py
Normal file
25
services/sales/app/schemas/__init__.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# services/sales/app/schemas/__init__.py
|
||||
|
||||
from .sales import (
|
||||
SalesDataCreate,
|
||||
SalesDataUpdate,
|
||||
SalesDataResponse,
|
||||
SalesDataQuery,
|
||||
ProductCreate,
|
||||
ProductUpdate,
|
||||
ProductResponse,
|
||||
SalesAnalytics,
|
||||
ProductSalesAnalytics
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"SalesDataCreate",
|
||||
"SalesDataUpdate",
|
||||
"SalesDataResponse",
|
||||
"SalesDataQuery",
|
||||
"ProductCreate",
|
||||
"ProductUpdate",
|
||||
"ProductResponse",
|
||||
"SalesAnalytics",
|
||||
"ProductSalesAnalytics"
|
||||
]
|
||||
198
services/sales/app/schemas/sales.py
Normal file
198
services/sales/app/schemas/sales.py
Normal file
@@ -0,0 +1,198 @@
|
||||
# services/sales/app/schemas/sales.py
|
||||
"""
|
||||
Sales Service Pydantic Schemas
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel, Field, validator
|
||||
from typing import Optional, List
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
class SalesDataBase(BaseModel):
|
||||
"""Base sales data schema"""
|
||||
product_name: str = Field(..., min_length=1, max_length=255, description="Product name")
|
||||
product_category: Optional[str] = Field(None, max_length=100, description="Product category")
|
||||
product_sku: Optional[str] = Field(None, max_length=100, description="Product SKU")
|
||||
|
||||
quantity_sold: int = Field(..., gt=0, description="Quantity sold")
|
||||
unit_price: Optional[Decimal] = Field(None, ge=0, description="Unit price")
|
||||
revenue: Decimal = Field(..., gt=0, description="Total revenue")
|
||||
cost_of_goods: Optional[Decimal] = Field(None, ge=0, description="Cost of goods sold")
|
||||
discount_applied: Optional[Decimal] = Field(0, ge=0, le=100, description="Discount percentage")
|
||||
|
||||
location_id: Optional[str] = Field(None, max_length=100, description="Location identifier")
|
||||
sales_channel: Optional[str] = Field("in_store", description="Sales channel")
|
||||
source: str = Field("manual", description="Data source")
|
||||
|
||||
notes: Optional[str] = Field(None, description="Additional notes")
|
||||
weather_condition: Optional[str] = Field(None, max_length=50, description="Weather condition")
|
||||
is_holiday: bool = Field(False, description="Holiday flag")
|
||||
is_weekend: bool = Field(False, description="Weekend flag")
|
||||
|
||||
@validator('sales_channel')
|
||||
def validate_sales_channel(cls, v):
|
||||
allowed_channels = ['in_store', 'online', 'delivery', 'wholesale']
|
||||
if v not in allowed_channels:
|
||||
raise ValueError(f'Sales channel must be one of: {allowed_channels}')
|
||||
return v
|
||||
|
||||
@validator('source')
|
||||
def validate_source(cls, v):
|
||||
allowed_sources = ['manual', 'pos', 'online', 'import', 'api', 'csv']
|
||||
if v not in allowed_sources:
|
||||
raise ValueError(f'Source must be one of: {allowed_sources}')
|
||||
return v
|
||||
|
||||
|
||||
class SalesDataCreate(SalesDataBase):
|
||||
"""Schema for creating sales data"""
|
||||
tenant_id: Optional[UUID] = Field(None, description="Tenant ID (set automatically)")
|
||||
date: datetime = Field(..., description="Sale date and time")
|
||||
|
||||
|
||||
class SalesDataUpdate(BaseModel):
|
||||
"""Schema for updating sales data"""
|
||||
product_name: Optional[str] = Field(None, min_length=1, max_length=255)
|
||||
product_category: Optional[str] = Field(None, max_length=100)
|
||||
product_sku: Optional[str] = Field(None, max_length=100)
|
||||
|
||||
quantity_sold: Optional[int] = Field(None, gt=0)
|
||||
unit_price: Optional[Decimal] = Field(None, ge=0)
|
||||
revenue: Optional[Decimal] = Field(None, gt=0)
|
||||
cost_of_goods: Optional[Decimal] = Field(None, ge=0)
|
||||
discount_applied: Optional[Decimal] = Field(None, ge=0, le=100)
|
||||
|
||||
location_id: Optional[str] = Field(None, max_length=100)
|
||||
sales_channel: Optional[str] = None
|
||||
|
||||
notes: Optional[str] = None
|
||||
weather_condition: Optional[str] = Field(None, max_length=50)
|
||||
is_holiday: Optional[bool] = None
|
||||
is_weekend: Optional[bool] = None
|
||||
|
||||
validation_notes: Optional[str] = None
|
||||
is_validated: Optional[bool] = None
|
||||
|
||||
|
||||
class SalesDataResponse(SalesDataBase):
|
||||
"""Schema for sales data responses"""
|
||||
id: UUID
|
||||
tenant_id: UUID
|
||||
date: datetime
|
||||
|
||||
is_validated: bool = False
|
||||
validation_notes: Optional[str] = None
|
||||
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
created_by: Optional[UUID] = None
|
||||
|
||||
profit_margin: Optional[float] = Field(None, description="Calculated profit margin")
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class SalesDataQuery(BaseModel):
|
||||
"""Schema for sales data queries"""
|
||||
start_date: Optional[datetime] = None
|
||||
end_date: Optional[datetime] = None
|
||||
product_name: Optional[str] = None
|
||||
product_category: Optional[str] = None
|
||||
location_id: Optional[str] = None
|
||||
sales_channel: Optional[str] = None
|
||||
source: Optional[str] = None
|
||||
is_validated: Optional[bool] = None
|
||||
|
||||
limit: int = Field(50, ge=1, le=1000, description="Number of records to return")
|
||||
offset: int = Field(0, ge=0, description="Number of records to skip")
|
||||
|
||||
order_by: str = Field("date", description="Field to order by")
|
||||
order_direction: str = Field("desc", description="Order direction")
|
||||
|
||||
@validator('order_direction')
|
||||
def validate_order_direction(cls, v):
|
||||
if v.lower() not in ['asc', 'desc']:
|
||||
raise ValueError('Order direction must be "asc" or "desc"')
|
||||
return v.lower()
|
||||
|
||||
|
||||
# Product schemas
|
||||
class ProductBase(BaseModel):
|
||||
"""Base product schema"""
|
||||
name: str = Field(..., min_length=1, max_length=255, description="Product name")
|
||||
sku: Optional[str] = Field(None, max_length=100, description="Stock Keeping Unit")
|
||||
category: Optional[str] = Field(None, max_length=100, description="Product category")
|
||||
subcategory: Optional[str] = Field(None, max_length=100, description="Product subcategory")
|
||||
|
||||
description: Optional[str] = Field(None, description="Product description")
|
||||
unit_of_measure: str = Field("unit", description="Unit of measure")
|
||||
weight: Optional[float] = Field(None, gt=0, description="Weight in grams")
|
||||
volume: Optional[float] = Field(None, gt=0, description="Volume in ml")
|
||||
|
||||
base_price: Optional[Decimal] = Field(None, ge=0, description="Base selling price")
|
||||
cost_price: Optional[Decimal] = Field(None, ge=0, description="Cost price")
|
||||
|
||||
is_seasonal: bool = Field(False, description="Seasonal product flag")
|
||||
seasonal_start: Optional[datetime] = Field(None, description="Season start date")
|
||||
seasonal_end: Optional[datetime] = Field(None, description="Season end date")
|
||||
|
||||
|
||||
class ProductCreate(ProductBase):
|
||||
"""Schema for creating products"""
|
||||
tenant_id: Optional[UUID] = Field(None, description="Tenant ID (set automatically)")
|
||||
|
||||
|
||||
class ProductUpdate(BaseModel):
|
||||
"""Schema for updating products"""
|
||||
name: Optional[str] = Field(None, min_length=1, max_length=255)
|
||||
sku: Optional[str] = Field(None, max_length=100)
|
||||
category: Optional[str] = Field(None, max_length=100)
|
||||
subcategory: Optional[str] = Field(None, max_length=100)
|
||||
description: Optional[str] = None
|
||||
unit_of_measure: Optional[str] = None
|
||||
weight: Optional[float] = Field(None, gt=0)
|
||||
volume: Optional[float] = Field(None, gt=0)
|
||||
base_price: Optional[Decimal] = Field(None, ge=0)
|
||||
cost_price: Optional[Decimal] = Field(None, ge=0)
|
||||
is_active: Optional[bool] = None
|
||||
is_seasonal: Optional[bool] = None
|
||||
seasonal_start: Optional[datetime] = None
|
||||
seasonal_end: Optional[datetime] = None
|
||||
|
||||
|
||||
class ProductResponse(ProductBase):
|
||||
"""Schema for product responses"""
|
||||
id: UUID
|
||||
tenant_id: UUID
|
||||
is_active: bool = True
|
||||
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
# Analytics schemas
|
||||
class SalesAnalytics(BaseModel):
|
||||
"""Sales analytics response"""
|
||||
total_revenue: Decimal
|
||||
total_quantity: int
|
||||
total_transactions: int
|
||||
average_transaction_value: Decimal
|
||||
top_products: List[dict]
|
||||
sales_by_channel: dict
|
||||
sales_by_day: List[dict]
|
||||
|
||||
|
||||
class ProductSalesAnalytics(BaseModel):
|
||||
"""Product-specific sales analytics"""
|
||||
product_name: str
|
||||
total_revenue: Decimal
|
||||
total_quantity: int
|
||||
total_transactions: int
|
||||
average_price: Decimal
|
||||
growth_rate: Optional[float] = None
|
||||
Reference in New Issue
Block a user