Create new services: inventory, recipes, suppliers
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
# services/sales/app/repositories/__init__.py
|
||||
|
||||
from .sales_repository import SalesRepository
|
||||
from .product_repository import ProductRepository
|
||||
|
||||
__all__ = ["SalesRepository", "ProductRepository"]
|
||||
__all__ = ["SalesRepository"]
|
||||
@@ -1,193 +0,0 @@
|
||||
# services/sales/app/repositories/product_repository.py
|
||||
"""
|
||||
Product Repository using Repository Pattern
|
||||
"""
|
||||
|
||||
from typing import List, Optional
|
||||
from uuid import UUID
|
||||
from sqlalchemy import select, and_, or_, desc, asc
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
import structlog
|
||||
|
||||
from app.models.sales import Product
|
||||
from app.schemas.sales import ProductCreate, ProductUpdate
|
||||
from shared.database.repository import BaseRepository
|
||||
|
||||
logger = structlog.get_logger()
|
||||
|
||||
|
||||
class ProductRepository(BaseRepository[Product, ProductCreate, ProductUpdate]):
|
||||
"""Repository for product operations"""
|
||||
|
||||
def __init__(self, db_session: AsyncSession):
|
||||
super().__init__(Product, db_session)
|
||||
|
||||
async def create_product(self, product_data: ProductCreate, tenant_id: UUID) -> Product:
|
||||
"""Create a new product"""
|
||||
try:
|
||||
# Prepare data
|
||||
create_data = product_data.model_dump()
|
||||
create_data['tenant_id'] = tenant_id
|
||||
|
||||
# Create product
|
||||
product = await self.create(create_data)
|
||||
logger.info(
|
||||
"Created product",
|
||||
product_id=product.id,
|
||||
name=product.name,
|
||||
tenant_id=tenant_id
|
||||
)
|
||||
return product
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to create product", error=str(e), tenant_id=tenant_id)
|
||||
raise
|
||||
|
||||
async def get_by_tenant(self, tenant_id: UUID, include_inactive: bool = False) -> List[Product]:
|
||||
"""Get all products for a tenant"""
|
||||
try:
|
||||
stmt = select(Product).where(Product.tenant_id == tenant_id)
|
||||
|
||||
if not include_inactive:
|
||||
stmt = stmt.where(Product.is_active == True)
|
||||
|
||||
stmt = stmt.order_by(Product.category, Product.name)
|
||||
|
||||
result = await self.db_session.execute(stmt)
|
||||
products = result.scalars().all()
|
||||
|
||||
logger.info(
|
||||
"Retrieved products",
|
||||
count=len(products),
|
||||
tenant_id=tenant_id,
|
||||
include_inactive=include_inactive
|
||||
)
|
||||
return list(products)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to get products", error=str(e), tenant_id=tenant_id)
|
||||
raise
|
||||
|
||||
async def get_by_category(self, tenant_id: UUID, category: str) -> List[Product]:
|
||||
"""Get products by category"""
|
||||
try:
|
||||
stmt = select(Product).where(
|
||||
and_(
|
||||
Product.tenant_id == tenant_id,
|
||||
Product.category == category,
|
||||
Product.is_active == True
|
||||
)
|
||||
).order_by(Product.name)
|
||||
|
||||
result = await self.db_session.execute(stmt)
|
||||
products = result.scalars().all()
|
||||
|
||||
return list(products)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to get products by category", error=str(e), tenant_id=tenant_id, category=category)
|
||||
raise
|
||||
|
||||
async def get_by_name(self, tenant_id: UUID, name: str) -> Optional[Product]:
|
||||
"""Get product by name"""
|
||||
try:
|
||||
stmt = select(Product).where(
|
||||
and_(
|
||||
Product.tenant_id == tenant_id,
|
||||
Product.name == name
|
||||
)
|
||||
)
|
||||
|
||||
result = await self.db_session.execute(stmt)
|
||||
product = result.scalar_one_or_none()
|
||||
|
||||
return product
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to get product by name", error=str(e), tenant_id=tenant_id, name=name)
|
||||
raise
|
||||
|
||||
async def get_by_sku(self, tenant_id: UUID, sku: str) -> Optional[Product]:
|
||||
"""Get product by SKU"""
|
||||
try:
|
||||
stmt = select(Product).where(
|
||||
and_(
|
||||
Product.tenant_id == tenant_id,
|
||||
Product.sku == sku
|
||||
)
|
||||
)
|
||||
|
||||
result = await self.db_session.execute(stmt)
|
||||
product = result.scalar_one_or_none()
|
||||
|
||||
return product
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to get product by SKU", error=str(e), tenant_id=tenant_id, sku=sku)
|
||||
raise
|
||||
|
||||
async def search_products(self, tenant_id: UUID, query: str, limit: int = 50) -> List[Product]:
|
||||
"""Search products by name or SKU"""
|
||||
try:
|
||||
stmt = select(Product).where(
|
||||
and_(
|
||||
Product.tenant_id == tenant_id,
|
||||
Product.is_active == True,
|
||||
or_(
|
||||
Product.name.ilike(f"%{query}%"),
|
||||
Product.sku.ilike(f"%{query}%"),
|
||||
Product.description.ilike(f"%{query}%")
|
||||
)
|
||||
)
|
||||
).order_by(Product.name).limit(limit)
|
||||
|
||||
result = await self.db_session.execute(stmt)
|
||||
products = result.scalars().all()
|
||||
|
||||
return list(products)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to search products", error=str(e), tenant_id=tenant_id, query=query)
|
||||
raise
|
||||
|
||||
async def get_categories(self, tenant_id: UUID) -> List[str]:
|
||||
"""Get distinct product categories for a tenant"""
|
||||
try:
|
||||
stmt = select(Product.category).where(
|
||||
and_(
|
||||
Product.tenant_id == tenant_id,
|
||||
Product.is_active == True,
|
||||
Product.category.is_not(None)
|
||||
)
|
||||
).distinct()
|
||||
|
||||
result = await self.db_session.execute(stmt)
|
||||
categories = [row[0] for row in result if row[0]]
|
||||
|
||||
return sorted(categories)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to get product categories", error=str(e), tenant_id=tenant_id)
|
||||
raise
|
||||
|
||||
async def deactivate_product(self, product_id: UUID) -> Product:
|
||||
"""Deactivate a product"""
|
||||
try:
|
||||
product = await self.update(product_id, {'is_active': False})
|
||||
logger.info("Deactivated product", product_id=product_id)
|
||||
return product
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to deactivate product", error=str(e), product_id=product_id)
|
||||
raise
|
||||
|
||||
async def activate_product(self, product_id: UUID) -> Product:
|
||||
"""Activate a product"""
|
||||
try:
|
||||
product = await self.update(product_id, {'is_active': True})
|
||||
logger.info("Activated product", product_id=product_id)
|
||||
return product
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to activate product", error=str(e), product_id=product_id)
|
||||
raise
|
||||
@@ -108,19 +108,19 @@ class SalesRepository(BaseRepository[SalesData, SalesDataCreate, SalesDataUpdate
|
||||
logger.error("Failed to get sales records", error=str(e), tenant_id=tenant_id)
|
||||
raise
|
||||
|
||||
async def get_by_product(
|
||||
async def get_by_inventory_product(
|
||||
self,
|
||||
tenant_id: UUID,
|
||||
product_name: str,
|
||||
inventory_product_id: UUID,
|
||||
start_date: Optional[datetime] = None,
|
||||
end_date: Optional[datetime] = None
|
||||
) -> List[SalesData]:
|
||||
"""Get sales records for a specific product"""
|
||||
"""Get sales records for a specific inventory product"""
|
||||
try:
|
||||
stmt = select(SalesData).where(
|
||||
and_(
|
||||
SalesData.tenant_id == tenant_id,
|
||||
SalesData.product_name == product_name
|
||||
SalesData.inventory_product_id == inventory_product_id
|
||||
)
|
||||
)
|
||||
|
||||
@@ -137,7 +137,7 @@ class SalesRepository(BaseRepository[SalesData, SalesDataCreate, SalesDataUpdate
|
||||
return list(records)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to get product sales", error=str(e), tenant_id=tenant_id, product=product_name)
|
||||
logger.error("Failed to get product sales", error=str(e), tenant_id=tenant_id, inventory_product_id=inventory_product_id)
|
||||
raise
|
||||
|
||||
async def get_analytics(
|
||||
|
||||
Reference in New Issue
Block a user