Files
plantillas-proyectos/backend/api/v1/modules/a76/items/routes.py
acazares b94d0ac7fb refactor: restructure item interfaces and update related components
- Renamed LineItem interface to Item and adjusted properties accordingly.
- Updated CreateItemData and UpdateItemData interfaces to reflect new structure.
- Modified components to use the new Item interface, removing nested lines.
- Adjusted data binding in item configuration, main data, and other related components.
- Simplified item creation and editing logic by removing unnecessary nesting.
- Ensured all references to line items are updated to reflect the new structure.
2026-02-18 09:56:50 -06:00

240 lines
7.7 KiB
Python

"""
API Endpoints for Items management
Handles CRUD operations for Item with one-to-many relationships to LineItems
"""
from typing import Dict, Any, Optional
from fastapi import APIRouter, Depends, HTTPException, Query, Path, status
from sqlalchemy.orm import Session
from core.database import get_core_db
from core.security import get_current_user, validate_access_to_resource
from .schemas import (
LineItemCreate,
LineItemUpdate,
LineItemResponse,
LineItemListResponse,
)
from .service import ItemService
router = APIRouter(prefix="/items", tags=["Items"])
# ============================================================================
# ITEM CRUD ENDPOINTS
# ============================================================================
@router.post("/", response_model=LineItemResponse, status_code=status.HTTP_201_CREATED)
async def create_item(
item_data: LineItemCreate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: Dict[str, Any] = Depends(get_current_user),
):
"""
Create a new item with multiple line items and their nested data
The item follows a one-to-many relationship structure:
- Each LineItem has one LineFinancial
- Each LineItem has one LineQuantity
- Each LineItem has one LineCustoms
- Each LineItem has one LineDescription
- Each LineItem has one LineReference
"""
tenant_id = validate_access_to_resource(db, company_id, current_user)
service = ItemService()
return service.create(db, item_data, tenant_id, company_id)
@router.get("/{item_id}", response_model=LineItemResponse)
async def get_item(
item_id: int = Path(..., description="Item ID"),
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: Dict[str, Any] = Depends(get_current_user),
):
"""
Get a specific item by ID with all nested data
"""
tenant_id = validate_access_to_resource(db, company_id, current_user)
service = ItemService()
item = service.get_by_id(db, item_id, tenant_id, company_id)
if not item:
raise HTTPException(status_code=404, detail="Item not found")
return item
@router.get("/", response_model=LineItemListResponse)
async def list_items(
company_id: int = Query(..., description="Company ID"),
skip: int = Query(0, ge=0, description="Number of records to skip"),
limit: int = Query(100, ge=1, le=1000,
description="Maximum records to return"),
invoice_id: Optional[int] = Query(
None, description="Filter by invoice ID"),
item_type: Optional[str] = Query(None, description="Filter by item type"),
system_origin: Optional[str] = Query(
None, description="Filter by system origin (SCAF/SCAII)"),
search: Optional[str] = Query(
None, description="Search term for invoice number, reference, order, or guide"),
db: Session = Depends(get_core_db),
current_user: Dict[str, Any] = Depends(get_current_user),
):
"""
List items with optional filtering and pagination
Filters:
- invoice_id: Filter by specific invoice
- item_type: Filter by item type (IMPORT_TEMP, IMPORT_DEF, EXPORT, REPAIR)
- system_origin: Filter by system (SCAF, SCAII)
- search: Search across multiple fields
"""
tenant_id = validate_access_to_resource(db, company_id, current_user)
service = ItemService()
filters = {
"invoice_id": invoice_id,
"item_type": item_type,
"system_origin": system_origin,
"search": search,
}
items, total = service.get_all(
db, tenant_id, company_id, skip, limit, filters)
return LineItemListResponse(
total=total,
items=items,
skip=skip,
limit=limit
)
@router.put("/{item_id}", response_model=LineItemResponse)
async def update_item(
item_id: int = Path(..., description="Item ID"),
item_data: LineItemUpdate = ...,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: Dict[str, Any] = Depends(get_current_user),
):
"""
Update an item and optionally its nested line data
"""
tenant_id = validate_access_to_resource(db, company_id, current_user)
# Verify the item exists and belongs to the tenant/company
service = ItemService()
existing_item = service.get_by_id(db, item_id, tenant_id, company_id)
if not existing_item:
raise HTTPException(status_code=404, detail="Item not found")
return service.update(db, item_id, item_data, tenant_id, company_id)
@router.delete("/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_item(
item_id: int = Path(..., description="Item ID"),
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: Dict[str, Any] = Depends(get_current_user),
):
"""
Delete an item and all its related data (cascade delete)
"""
tenant_id = validate_access_to_resource(db, company_id, current_user)
service = ItemService()
# Verify the item exists and belongs to the tenant/company
existing_item = service.get_by_id(db, item_id, tenant_id, company_id)
if not existing_item:
raise HTTPException(status_code=404, detail="Item not found")
success = service.delete(db, item_id, tenant_id, company_id)
if not success:
raise HTTPException(status_code=404, detail="Item not found")
return None
# ============================================================================
# ADDITIONAL ENDPOINTS FOR INVOICE
# ============================================================================
@router.get("/invoice/{invoice_id}/items", response_model=LineItemListResponse)
async def get_items_by_invoice(
invoice_id: int = Path(..., description="Invoice ID"),
company_id: int = Query(..., description="Company ID"),
skip: int = Query(0, ge=0),
limit: int = Query(100, ge=1, le=1000),
db: Session = Depends(get_core_db),
current_user: Dict[str, Any] = Depends(get_current_user),
):
"""
Get all items for a specific invoice
"""
tenant_id = validate_access_to_resource(db, company_id, current_user)
service = ItemService()
items, total = service.get_by_invoice(
db, invoice_id, tenant_id, company_id, skip, limit)
return LineItemListResponse(
total=total,
items=items,
skip=skip,
limit=limit
)
# STATISTICS & UTILITIES
# ============================================================================
@router.get("/stats/summary")
async def get_items_summary(
company_id: int = Query(..., description="Company ID"),
invoice_id: Optional[int] = Query(
None, description="Filter by invoice ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user),
):
"""
Get summary statistics for items
"""
tenant_id = validate_access_to_resource(db, company_id, current_user)
filters = {"invoice_id": invoice_id} if invoice_id else None
items, total = ItemService.get_all(
db=db,
tenant_id=tenant_id,
company_id=company_id,
skip=0,
limit=10000, # Get all for stats
filters=filters
)
# Calculate stats
stats = {
"total_items": total,
"by_type": {},
"by_system": {},
}
for item in items:
# Count by type
if item.item_type:
stats["by_type"][item.item_type] = stats["by_type"].get(
item.item_type, 0) + 1
# Count by system
if item.system_origin:
stats["by_system"][item.system_origin] = stats["by_system"].get(
item.system_origin, 0) + 1
return stats