Add traslations
This commit is contained in:
@@ -21,6 +21,9 @@ from app.schemas.inventory import (
|
||||
StockResponse,
|
||||
StockCreate,
|
||||
StockUpdate,
|
||||
BulkIngredientCreate,
|
||||
BulkIngredientResponse,
|
||||
BulkIngredientResult,
|
||||
)
|
||||
from shared.auth.decorators import get_current_user_dep
|
||||
from shared.auth.access_control import require_user_role, admin_role_required, owner_role_required
|
||||
@@ -157,6 +160,162 @@ async def create_ingredient(
|
||||
)
|
||||
|
||||
|
||||
@router.post(
|
||||
route_builder.build_base_route("ingredients/bulk"),
|
||||
response_model=BulkIngredientResponse,
|
||||
status_code=status.HTTP_201_CREATED
|
||||
)
|
||||
@require_user_role(['admin', 'owner'])
|
||||
async def bulk_create_ingredients(
|
||||
bulk_data: BulkIngredientCreate,
|
||||
tenant_id: UUID = Path(..., description="Tenant ID"),
|
||||
current_user: dict = Depends(get_current_user_dep),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""Create multiple ingredients in a single transaction (Admin/Manager only)"""
|
||||
import uuid
|
||||
transaction_id = str(uuid.uuid4())
|
||||
|
||||
try:
|
||||
# CRITICAL: Check subscription limit ONCE before creating any ingredients
|
||||
from app.core.config import settings
|
||||
total_requested = len(bulk_data.ingredients)
|
||||
|
||||
async with httpx.AsyncClient(timeout=5.0) as client:
|
||||
try:
|
||||
# Check if we can add this many products
|
||||
limit_check_response = await client.get(
|
||||
f"{settings.TENANT_SERVICE_URL}/api/v1/tenants/subscriptions/{tenant_id}/can-add-products/{total_requested}",
|
||||
headers={
|
||||
"x-user-id": str(current_user.get('user_id')),
|
||||
"x-tenant-id": str(tenant_id)
|
||||
}
|
||||
)
|
||||
|
||||
if limit_check_response.status_code == 200:
|
||||
limit_check = limit_check_response.json()
|
||||
|
||||
if not limit_check.get('can_add', False):
|
||||
logger.warning(
|
||||
"Bulk product limit exceeded",
|
||||
tenant_id=str(tenant_id),
|
||||
requested=total_requested,
|
||||
current=limit_check.get('current_count'),
|
||||
max=limit_check.get('max_allowed'),
|
||||
reason=limit_check.get('reason')
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_402_PAYMENT_REQUIRED,
|
||||
detail={
|
||||
"error": "product_limit_exceeded",
|
||||
"message": limit_check.get('reason', 'Product limit exceeded'),
|
||||
"requested": total_requested,
|
||||
"current_count": limit_check.get('current_count'),
|
||||
"max_allowed": limit_check.get('max_allowed'),
|
||||
"upgrade_required": True
|
||||
}
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
"Failed to check product limit, allowing bulk creation",
|
||||
tenant_id=str(tenant_id),
|
||||
status_code=limit_check_response.status_code
|
||||
)
|
||||
except httpx.TimeoutException:
|
||||
logger.warning(
|
||||
"Timeout checking product limit, allowing bulk creation",
|
||||
tenant_id=str(tenant_id)
|
||||
)
|
||||
except httpx.RequestError as e:
|
||||
logger.warning(
|
||||
"Error checking product limit, allowing bulk creation",
|
||||
tenant_id=str(tenant_id),
|
||||
error=str(e)
|
||||
)
|
||||
|
||||
# Extract user ID - handle service tokens
|
||||
raw_user_id = current_user.get('user_id')
|
||||
if current_user.get('type') == 'service':
|
||||
user_id = None
|
||||
else:
|
||||
try:
|
||||
user_id = UUID(raw_user_id)
|
||||
except (ValueError, TypeError):
|
||||
user_id = None
|
||||
|
||||
# Create all ingredients
|
||||
service = InventoryService()
|
||||
results: List[BulkIngredientResult] = []
|
||||
total_created = 0
|
||||
total_failed = 0
|
||||
|
||||
for index, ingredient_data in enumerate(bulk_data.ingredients):
|
||||
try:
|
||||
ingredient = await service.create_ingredient(ingredient_data, tenant_id, user_id)
|
||||
results.append(BulkIngredientResult(
|
||||
index=index,
|
||||
success=True,
|
||||
ingredient=IngredientResponse.from_orm(ingredient),
|
||||
error=None
|
||||
))
|
||||
total_created += 1
|
||||
|
||||
logger.debug(
|
||||
"Ingredient created in bulk operation",
|
||||
tenant_id=str(tenant_id),
|
||||
ingredient_id=str(ingredient.id),
|
||||
ingredient_name=ingredient.name,
|
||||
index=index,
|
||||
transaction_id=transaction_id
|
||||
)
|
||||
except Exception as e:
|
||||
results.append(BulkIngredientResult(
|
||||
index=index,
|
||||
success=False,
|
||||
ingredient=None,
|
||||
error=str(e)
|
||||
))
|
||||
total_failed += 1
|
||||
|
||||
logger.warning(
|
||||
"Failed to create ingredient in bulk operation",
|
||||
tenant_id=str(tenant_id),
|
||||
index=index,
|
||||
error=str(e),
|
||||
transaction_id=transaction_id
|
||||
)
|
||||
|
||||
logger.info(
|
||||
"Bulk ingredient creation completed",
|
||||
tenant_id=str(tenant_id),
|
||||
total_requested=total_requested,
|
||||
total_created=total_created,
|
||||
total_failed=total_failed,
|
||||
transaction_id=transaction_id
|
||||
)
|
||||
|
||||
return BulkIngredientResponse(
|
||||
total_requested=total_requested,
|
||||
total_created=total_created,
|
||||
total_failed=total_failed,
|
||||
results=results,
|
||||
transaction_id=transaction_id
|
||||
)
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"Failed to process bulk ingredient creation",
|
||||
tenant_id=str(tenant_id),
|
||||
error=str(e),
|
||||
transaction_id=transaction_id
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Failed to process bulk ingredient creation"
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
route_builder.build_base_route("ingredients/count"),
|
||||
response_model=dict
|
||||
|
||||
@@ -171,6 +171,30 @@ class IngredientResponse(InventoryBaseSchema):
|
||||
return None
|
||||
|
||||
|
||||
# ===== BULK INGREDIENT SCHEMAS =====
|
||||
|
||||
class BulkIngredientCreate(InventoryBaseSchema):
|
||||
"""Schema for bulk creating ingredients"""
|
||||
ingredients: List[IngredientCreate] = Field(..., description="List of ingredients to create")
|
||||
|
||||
|
||||
class BulkIngredientResult(InventoryBaseSchema):
|
||||
"""Schema for individual result in bulk operation"""
|
||||
index: int = Field(..., description="Index of the ingredient in the original request")
|
||||
success: bool = Field(..., description="Whether the creation succeeded")
|
||||
ingredient: Optional[IngredientResponse] = Field(None, description="Created ingredient (if successful)")
|
||||
error: Optional[str] = Field(None, description="Error message (if failed)")
|
||||
|
||||
|
||||
class BulkIngredientResponse(InventoryBaseSchema):
|
||||
"""Schema for bulk ingredient creation response"""
|
||||
total_requested: int = Field(..., description="Total number of ingredients requested")
|
||||
total_created: int = Field(..., description="Number of ingredients successfully created")
|
||||
total_failed: int = Field(..., description="Number of ingredients that failed")
|
||||
results: List[BulkIngredientResult] = Field(..., description="Detailed results for each ingredient")
|
||||
transaction_id: str = Field(..., description="Transaction ID for audit trail")
|
||||
|
||||
|
||||
# ===== STOCK SCHEMAS =====
|
||||
|
||||
class StockCreate(InventoryBaseSchema):
|
||||
|
||||
Reference in New Issue
Block a user