Files
bakery-ia/services/procurement/app/repositories/procurement_plan_repository.py
2025-11-01 21:35:03 +01:00

255 lines
9.1 KiB
Python

# ================================================================
# services/procurement/app/repositories/procurement_plan_repository.py
# ================================================================
"""
Procurement Plan Repository - Database operations for procurement plans and requirements
"""
import uuid
from datetime import datetime, date
from typing import List, Optional, Dict, Any
from sqlalchemy import select, and_, desc, func
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
from app.models.procurement_plan import ProcurementPlan, ProcurementRequirement
from app.repositories.base_repository import BaseRepository
class ProcurementPlanRepository(BaseRepository):
"""Repository for procurement plan operations"""
def __init__(self, db: AsyncSession):
super().__init__(ProcurementPlan)
self.db = db
async def create_plan(self, plan_data: Dict[str, Any]) -> ProcurementPlan:
"""Create a new procurement plan"""
plan = ProcurementPlan(**plan_data)
self.db.add(plan)
await self.db.flush()
return plan
async def get_plan_by_id(self, plan_id: uuid.UUID, tenant_id: uuid.UUID) -> Optional[ProcurementPlan]:
"""Get procurement plan by ID"""
stmt = select(ProcurementPlan).where(
and_(
ProcurementPlan.id == plan_id,
ProcurementPlan.tenant_id == tenant_id
)
).options(selectinload(ProcurementPlan.requirements))
result = await self.db.execute(stmt)
return result.scalar_one_or_none()
async def get_plan_by_date(self, plan_date: date, tenant_id: uuid.UUID) -> Optional[ProcurementPlan]:
"""Get procurement plan for a specific date"""
stmt = select(ProcurementPlan).where(
and_(
ProcurementPlan.plan_date == plan_date,
ProcurementPlan.tenant_id == tenant_id
)
).options(selectinload(ProcurementPlan.requirements))
result = await self.db.execute(stmt)
return result.scalar_one_or_none()
async def get_current_plan(self, tenant_id: uuid.UUID) -> Optional[ProcurementPlan]:
"""Get the current day's procurement plan"""
today = date.today()
return await self.get_plan_by_date(today, tenant_id)
async def list_plans(
self,
tenant_id: uuid.UUID,
status: Optional[str] = None,
start_date: Optional[date] = None,
end_date: Optional[date] = None,
limit: int = 50,
offset: int = 0
) -> List[ProcurementPlan]:
"""List procurement plans with filters"""
conditions = [ProcurementPlan.tenant_id == tenant_id]
if status:
conditions.append(ProcurementPlan.status == status)
if start_date:
conditions.append(ProcurementPlan.plan_date >= start_date)
if end_date:
conditions.append(ProcurementPlan.plan_date <= end_date)
stmt = (
select(ProcurementPlan)
.where(and_(*conditions))
.order_by(desc(ProcurementPlan.plan_date))
.limit(limit)
.offset(offset)
.options(selectinload(ProcurementPlan.requirements))
)
result = await self.db.execute(stmt)
return result.scalars().all()
async def get_plans_by_tenant(
self,
tenant_id: uuid.UUID,
start_date: Optional[datetime] = None,
end_date: Optional[datetime] = None
) -> List[ProcurementPlan]:
"""Get all procurement plans for a tenant with optional date filters"""
conditions = [ProcurementPlan.tenant_id == tenant_id]
if start_date:
conditions.append(ProcurementPlan.created_at >= start_date)
if end_date:
conditions.append(ProcurementPlan.created_at <= end_date)
stmt = (
select(ProcurementPlan)
.where(and_(*conditions))
.order_by(desc(ProcurementPlan.created_at))
.options(selectinload(ProcurementPlan.requirements))
)
result = await self.db.execute(stmt)
return result.scalars().all()
async def update_plan(self, plan_id: uuid.UUID, tenant_id: uuid.UUID, updates: Dict[str, Any]) -> Optional[ProcurementPlan]:
"""Update procurement plan"""
plan = await self.get_plan_by_id(plan_id, tenant_id)
if not plan:
return None
for key, value in updates.items():
if hasattr(plan, key):
setattr(plan, key, value)
plan.updated_at = datetime.utcnow()
await self.db.flush()
return plan
async def delete_plan(self, plan_id: uuid.UUID, tenant_id: uuid.UUID) -> bool:
"""Delete procurement plan"""
plan = await self.get_plan_by_id(plan_id, tenant_id)
if not plan:
return False
await self.db.delete(plan)
return True
async def generate_plan_number(self, tenant_id: uuid.UUID, plan_date: date) -> str:
"""Generate unique plan number"""
date_str = plan_date.strftime("%Y%m%d")
# Count existing plans for the same date
stmt = select(func.count(ProcurementPlan.id)).where(
and_(
ProcurementPlan.tenant_id == tenant_id,
ProcurementPlan.plan_date == plan_date
)
)
result = await self.db.execute(stmt)
count = result.scalar() or 0
return f"PP-{date_str}-{count + 1:03d}"
class ProcurementRequirementRepository(BaseRepository):
"""Repository for procurement requirement operations"""
def __init__(self, db: AsyncSession):
super().__init__(ProcurementRequirement)
self.db = db
async def create_requirement(self, requirement_data: Dict[str, Any]) -> ProcurementRequirement:
"""Create a new procurement requirement"""
requirement = ProcurementRequirement(**requirement_data)
self.db.add(requirement)
await self.db.flush()
return requirement
async def create_requirements_batch(self, requirements_data: List[Dict[str, Any]]) -> List[ProcurementRequirement]:
"""Create multiple procurement requirements"""
requirements = [ProcurementRequirement(**data) for data in requirements_data]
self.db.add_all(requirements)
await self.db.flush()
return requirements
async def get_requirement_by_id(self, requirement_id: uuid.UUID, tenant_id: uuid.UUID) -> Optional[ProcurementRequirement]:
"""Get procurement requirement by ID"""
stmt = select(ProcurementRequirement).join(ProcurementPlan).where(
and_(
ProcurementRequirement.id == requirement_id,
ProcurementPlan.tenant_id == tenant_id
)
)
result = await self.db.execute(stmt)
return result.scalar_one_or_none()
async def get_requirements_by_plan(self, plan_id: uuid.UUID) -> List[ProcurementRequirement]:
"""Get all requirements for a specific plan"""
stmt = select(ProcurementRequirement).where(
ProcurementRequirement.plan_id == plan_id
).order_by(ProcurementRequirement.priority.desc(), ProcurementRequirement.required_by_date)
result = await self.db.execute(stmt)
return result.scalars().all()
async def update_requirement(
self,
requirement_id: uuid.UUID,
updates: Dict[str, Any]
) -> Optional[ProcurementRequirement]:
"""Update procurement requirement"""
stmt = select(ProcurementRequirement).where(
ProcurementRequirement.id == requirement_id
)
result = await self.db.execute(stmt)
requirement = result.scalar_one_or_none()
if not requirement:
return None
for key, value in updates.items():
if hasattr(requirement, key):
setattr(requirement, key, value)
requirement.updated_at = datetime.utcnow()
await self.db.flush()
return requirement
async def generate_requirement_number(self, plan_id: uuid.UUID) -> str:
"""Generate unique requirement number within a plan"""
stmt = select(func.count(ProcurementRequirement.id)).where(
ProcurementRequirement.plan_id == plan_id
)
result = await self.db.execute(stmt)
count = result.scalar() or 0
return f"REQ-{count + 1:05d}"
async def get_requirements_by_tenant(
self,
tenant_id: uuid.UUID,
start_date: Optional[datetime] = None,
end_date: Optional[datetime] = None
) -> List[ProcurementRequirement]:
"""Get all procurement requirements for a tenant with optional date filters"""
conditions = [ProcurementPlan.tenant_id == tenant_id]
if start_date:
conditions.append(ProcurementRequirement.created_at >= start_date)
if end_date:
conditions.append(ProcurementRequirement.created_at <= end_date)
stmt = (
select(ProcurementRequirement)
.join(ProcurementPlan)
.where(and_(*conditions))
.order_by(desc(ProcurementRequirement.created_at))
)
result = await self.db.execute(stmt)
return result.scalars().all()