swiftops-backend / src /app /services /inventory_requirements_service.py
kamau1's picture
Implemented improved inventory requirements system with API endpoints, validation, and comprehensive documentation.
6b29be6
"""
Inventory Requirements Service
Helper service for working with project inventory requirements.
Used when receiving inventory batches to validate against project requirements.
"""
from sqlalchemy.orm import Session
from fastapi import HTTPException
from typing import List, Dict, Any, Optional
from uuid import UUID
import logging
from app.models.project import Project
logger = logging.getLogger(__name__)
class InventoryRequirementsService:
"""Service for managing project inventory requirements"""
@staticmethod
def get_project_inventory_requirements(
project_id: UUID,
db: Session,
usage_type: Optional[str] = None,
category: Optional[str] = None
) -> Dict[str, Dict[str, Any]]:
"""
Get inventory requirements for a project
Args:
project_id: Project ID
db: Database session
usage_type: Filter by usage type ('installed' or 'consumed')
category: Filter by category
Returns:
Dictionary of inventory requirements keyed by code
"""
project = db.query(Project).filter(Project.id == project_id).first()
if not project:
raise HTTPException(status_code=404, detail="Project not found")
inventory_reqs = project.inventory_requirements or {}
# Apply filters if provided
if usage_type or category:
filtered_reqs = {}
for code, req in inventory_reqs.items():
if usage_type and req.get('usage_type') != usage_type:
continue
if category and req.get('category') != category:
continue
filtered_reqs[code] = req
return filtered_reqs
return inventory_reqs
@staticmethod
def get_inventory_requirement_by_code(
project_id: UUID,
inventory_code: str,
db: Session
) -> Optional[Dict[str, Any]]:
"""
Get a specific inventory requirement by code
Args:
project_id: Project ID
inventory_code: Inventory code to look up
db: Database session
Returns:
Inventory requirement dict or None if not found
"""
inventory_reqs = InventoryRequirementsService.get_project_inventory_requirements(
project_id, db
)
return inventory_reqs.get(inventory_code)
@staticmethod
def validate_inventory_code(
project_id: UUID,
inventory_code: str,
db: Session
) -> bool:
"""
Validate that an inventory code exists in project requirements
Args:
project_id: Project ID
inventory_code: Inventory code to validate
db: Database session
Returns:
True if valid, raises HTTPException if invalid
"""
req = InventoryRequirementsService.get_inventory_requirement_by_code(
project_id, inventory_code, db
)
if not req:
raise HTTPException(
status_code=400,
detail=f"Invalid inventory code '{inventory_code}'. Not defined in project requirements."
)
return True
@staticmethod
def get_inventory_dropdown_options(
project_id: UUID,
db: Session,
usage_type: Optional[str] = None
) -> List[Dict[str, Any]]:
"""
Get inventory options formatted for dropdown selection
Used when receiving inventory batches
Args:
project_id: Project ID
db: Database session
usage_type: Filter by usage type
Returns:
List of dropdown options with code, name, description
"""
inventory_reqs = InventoryRequirementsService.get_project_inventory_requirements(
project_id, db, usage_type=usage_type
)
options = []
for code, req in inventory_reqs.items():
options.append({
'code': code,
'name': req.get('name'),
'description': req.get('description'),
'category': req.get('category'),
'unit': req.get('unit', 'pieces'),
'requires_serial_number': req.get('requires_serial_number', False),
'usage_type': req.get('usage_type')
})
# Sort by category then name
options.sort(key=lambda x: (x.get('category') or '', x.get('name') or ''))
return options
@staticmethod
def get_installed_inventory_for_completion(
project_id: UUID,
db: Session
) -> List[Dict[str, Any]]:
"""
Get inventory items that should appear in ticket completion forms
Only returns 'installed' items with include_in_completion=True
Args:
project_id: Project ID
db: Database session
Returns:
List of inventory items for completion form
"""
inventory_reqs = InventoryRequirementsService.get_project_inventory_requirements(
project_id, db, usage_type='installed'
)
completion_items = []
for code, req in inventory_reqs.items():
if req.get('include_in_completion', True):
completion_items.append({
'code': code,
'name': req.get('name'),
'description': req.get('description'),
'requires_serial_number': req.get('requires_serial_number', False),
'completion_field_label': req.get('completion_field_label') or req.get('name'),
'completion_required': req.get('completion_required', True),
'category': req.get('category')
})
return completion_items
@staticmethod
def get_inventory_categories(
project_id: UUID,
db: Session
) -> List[str]:
"""
Get list of unique inventory categories in project
Args:
project_id: Project ID
db: Database session
Returns:
List of category names
"""
inventory_reqs = InventoryRequirementsService.get_project_inventory_requirements(
project_id, db
)
categories = set()
for req in inventory_reqs.values():
category = req.get('category')
if category:
categories.add(category)
return sorted(list(categories))