175 lines
6.6 KiB
Python
175 lines
6.6 KiB
Python
"""
|
|
Internal Transfer API Endpoints
|
|
"""
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Body
|
|
from typing import List, Optional, Dict, Any
|
|
from datetime import date
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from pydantic import BaseModel
|
|
|
|
from app.services.internal_transfer_service import InternalTransferService
|
|
from app.repositories.purchase_order_repository import PurchaseOrderRepository
|
|
from app.core.database import get_db
|
|
from shared.auth.tenant_access import verify_tenant_permission_dep
|
|
from shared.clients import get_recipes_client, get_production_client, get_inventory_client
|
|
from app.core.config import settings
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
# Pydantic models for request validation
|
|
class InternalTransferItem(BaseModel):
|
|
product_id: str
|
|
product_name: Optional[str] = None
|
|
quantity: float
|
|
unit_of_measure: str = 'units'
|
|
|
|
|
|
class InternalTransferRequest(BaseModel):
|
|
parent_tenant_id: str
|
|
items: List[InternalTransferItem]
|
|
delivery_date: str
|
|
notes: Optional[str] = None
|
|
|
|
|
|
class ApprovalRequest(BaseModel):
|
|
pass # Empty for now, might add approval notes later
|
|
|
|
|
|
def get_internal_transfer_service(db: AsyncSession = Depends(get_db)) -> InternalTransferService:
|
|
"""Dependency to get internal transfer service"""
|
|
purchase_order_repository = PurchaseOrderRepository(db)
|
|
recipe_client = get_recipes_client(config=settings, service_name="procurement-service")
|
|
production_client = get_production_client(config=settings, service_name="procurement-service")
|
|
inventory_client = get_inventory_client(config=settings, service_name="procurement-service")
|
|
|
|
return InternalTransferService(
|
|
purchase_order_repository=purchase_order_repository,
|
|
recipe_client=recipe_client,
|
|
production_client=production_client,
|
|
inventory_client=inventory_client
|
|
)
|
|
|
|
|
|
@router.post("/tenants/{tenant_id}/procurement/internal-transfers", response_model=None)
|
|
async def create_internal_purchase_order(
|
|
tenant_id: str,
|
|
transfer_request: InternalTransferRequest,
|
|
internal_transfer_service: InternalTransferService = Depends(get_internal_transfer_service),
|
|
verified_tenant: str = Depends(verify_tenant_permission_dep)
|
|
):
|
|
"""
|
|
Create an internal purchase order from child to parent tenant
|
|
|
|
**Enterprise Tier Feature**: Internal transfers require Enterprise subscription.
|
|
"""
|
|
try:
|
|
# Validate subscription tier for internal transfers
|
|
from shared.subscription.plans import PlanFeatures
|
|
from shared.clients import get_tenant_client
|
|
|
|
tenant_client = get_tenant_client(config=settings, service_name="procurement-service")
|
|
subscription = await tenant_client.get_tenant_subscription(tenant_id)
|
|
|
|
if not subscription:
|
|
raise HTTPException(
|
|
status_code=403,
|
|
detail="No active subscription found. Internal transfers require Enterprise tier."
|
|
)
|
|
|
|
# Check if tier supports internal transfers
|
|
if not PlanFeatures.validate_internal_transfers(subscription.get("plan", "starter")):
|
|
raise HTTPException(
|
|
status_code=403,
|
|
detail=f"Internal transfers require Enterprise tier. Current tier: {subscription.get('plan', 'starter')}"
|
|
)
|
|
|
|
# Parse delivery_date
|
|
from datetime import datetime
|
|
delivery_date = datetime.fromisoformat(transfer_request.delivery_date.split('T')[0]).date()
|
|
|
|
# Convert Pydantic items to dict
|
|
items = [item.model_dump() for item in transfer_request.items]
|
|
|
|
# Create the internal purchase order
|
|
result = await internal_transfer_service.create_internal_purchase_order(
|
|
child_tenant_id=tenant_id,
|
|
parent_tenant_id=transfer_request.parent_tenant_id,
|
|
items=items,
|
|
delivery_date=delivery_date,
|
|
requested_by_user_id="temp_user_id", # Would come from auth context
|
|
notes=transfer_request.notes
|
|
)
|
|
|
|
return result
|
|
except HTTPException:
|
|
raise
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Failed to create internal purchase order: {str(e)}")
|
|
|
|
|
|
@router.post("/tenants/{tenant_id}/procurement/internal-transfers/{po_id}/approve", response_model=None)
|
|
async def approve_internal_transfer(
|
|
tenant_id: str,
|
|
po_id: str,
|
|
approval_request: Optional[ApprovalRequest] = None,
|
|
internal_transfer_service: InternalTransferService = Depends(get_internal_transfer_service),
|
|
verified_tenant: str = Depends(verify_tenant_permission_dep)
|
|
):
|
|
"""
|
|
Approve an internal transfer request
|
|
"""
|
|
try:
|
|
approved_by_user_id = "temp_user_id" # Would come from auth context
|
|
|
|
result = await internal_transfer_service.approve_internal_transfer(
|
|
po_id=po_id,
|
|
approved_by_user_id=approved_by_user_id
|
|
)
|
|
|
|
return result
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Failed to approve internal transfer: {str(e)}")
|
|
|
|
|
|
@router.get("/tenants/{tenant_id}/procurement/internal-transfers/pending", response_model=None)
|
|
async def get_pending_internal_transfers(
|
|
tenant_id: str,
|
|
internal_transfer_service: InternalTransferService = Depends(get_internal_transfer_service),
|
|
verified_tenant: str = Depends(verify_tenant_permission_dep)
|
|
):
|
|
"""
|
|
Get pending internal transfers for a tenant
|
|
"""
|
|
try:
|
|
result = await internal_transfer_service.get_pending_internal_transfers(tenant_id=tenant_id)
|
|
return result
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Failed to get pending internal transfers: {str(e)}")
|
|
|
|
|
|
@router.get("/tenants/{tenant_id}/procurement/internal-transfers/history", response_model=None)
|
|
async def get_internal_transfer_history(
|
|
tenant_id: str,
|
|
parent_tenant_id: Optional[str] = None,
|
|
child_tenant_id: Optional[str] = None,
|
|
start_date: Optional[date] = None,
|
|
end_date: Optional[date] = None,
|
|
internal_transfer_service: InternalTransferService = Depends(get_internal_transfer_service),
|
|
verified_tenant: str = Depends(verify_tenant_permission_dep)
|
|
):
|
|
"""
|
|
Get internal transfer history with optional filtering
|
|
"""
|
|
try:
|
|
result = await internal_transfer_service.get_internal_transfer_history(
|
|
tenant_id=tenant_id,
|
|
parent_tenant_id=parent_tenant_id,
|
|
child_tenant_id=child_tenant_id,
|
|
start_date=start_date,
|
|
end_date=end_date
|
|
)
|
|
return result
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Failed to get internal transfer history: {str(e)}") |