Initial commit - production deployment
This commit is contained in:
175
services/procurement/app/api/internal_transfer.py
Normal file
175
services/procurement/app/api/internal_transfer.py
Normal file
@@ -0,0 +1,175 @@
|
||||
"""
|
||||
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)}")
|
||||
Reference in New Issue
Block a user