feat: Enhance invoice management with operation and invoice type filters, update dialog defaults, and modify routes for improved functionality
This commit is contained in:
@@ -15,7 +15,8 @@ ServiceType = TypeVar("ServiceType")
|
||||
|
||||
|
||||
class TenantCRUDRoutes(
|
||||
Generic[CreateSchemaType, UpdateSchemaType, ResponseSchemaType, ServiceType]
|
||||
Generic[CreateSchemaType, UpdateSchemaType,
|
||||
ResponseSchemaType, ServiceType]
|
||||
):
|
||||
"""
|
||||
Generic CRUD routes factory for tenant-scoped resources
|
||||
@@ -74,7 +75,8 @@ class TenantCRUDRoutes(
|
||||
prefix: str,
|
||||
tags: list[str],
|
||||
resource_name: str = "Resource",
|
||||
id_name: Optional[str] = None, # For parent resources (e.g., "pedimento_id")
|
||||
# For parent resources (e.g., "pedimento_id")
|
||||
id_name: Optional[str] = None,
|
||||
id_type: Type = int, # Type of the ID (int, str, etc.)
|
||||
parent_id_name: Optional[
|
||||
str
|
||||
@@ -128,9 +130,15 @@ class TenantCRUDRoutes(
|
||||
le=self.max_page_size,
|
||||
description="Page size",
|
||||
),
|
||||
status: Optional[str] = Query(None, description="Filter by status"),
|
||||
status: Optional[str] = Query(
|
||||
None, description="Filter by status"),
|
||||
operation_type: Optional[str] = Query(
|
||||
None, description="Filter by operation type"),
|
||||
invoice_type: Optional[str] = Query(
|
||||
None, description="Filter by invoice type"),
|
||||
db: Session = Depends(self.db_dependency),
|
||||
current_user: Dict[str, Any] = Depends(self.auth_dependency),
|
||||
current_user: Dict[str, Any] = Depends(
|
||||
self.auth_dependency),
|
||||
):
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user
|
||||
@@ -140,6 +148,10 @@ class TenantCRUDRoutes(
|
||||
filters = {}
|
||||
if status:
|
||||
filters["status"] = status
|
||||
if operation_type:
|
||||
filters["operation_type"] = operation_type
|
||||
if invoice_type:
|
||||
filters["invoice_type"] = invoice_type
|
||||
|
||||
items, total = self.service.get_all(
|
||||
db, tenant_id, company_id, skip, page_size, filters
|
||||
@@ -172,7 +184,8 @@ class TenantCRUDRoutes(
|
||||
description="Page size",
|
||||
),
|
||||
db: Session = Depends(self.db_dependency),
|
||||
current_user: Dict[str, Any] = Depends(self.auth_dependency),
|
||||
current_user: Dict[str, Any] = Depends(
|
||||
self.auth_dependency),
|
||||
):
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user
|
||||
@@ -211,7 +224,8 @@ class TenantCRUDRoutes(
|
||||
**path_params,
|
||||
):
|
||||
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user)
|
||||
parent_id = path_params.get(self.parent_id_name)
|
||||
|
||||
# Try method with 4 params (pedimento_id, tenant_id, company_id)
|
||||
@@ -225,7 +239,8 @@ class TenantCRUDRoutes(
|
||||
db, parent_id, tenant_id, company_id
|
||||
)
|
||||
else:
|
||||
resource = self.service.get(db, parent_id, tenant_id, company_id)
|
||||
resource = self.service.get(
|
||||
db, parent_id, tenant_id, company_id)
|
||||
|
||||
if not resource:
|
||||
raise HTTPException(
|
||||
@@ -249,7 +264,8 @@ class TenantCRUDRoutes(
|
||||
db: Session = Depends(self.db_dependency),
|
||||
current_user: Dict[str, Any] = Depends(self.auth_dependency),
|
||||
):
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user)
|
||||
|
||||
resource = self.service.get_by_id(
|
||||
db, resource_id, tenant_id, company_id
|
||||
@@ -264,10 +280,10 @@ class TenantCRUDRoutes(
|
||||
# POST route
|
||||
if self.parent_id_name:
|
||||
# Child resource - needs parent_id from path
|
||||
|
||||
|
||||
# Create a closure to capture the schema type
|
||||
create_schema = self.create_schema
|
||||
|
||||
|
||||
@self.router.post(
|
||||
"/",
|
||||
response_model=self.response_schema,
|
||||
@@ -281,17 +297,18 @@ class TenantCRUDRoutes(
|
||||
db: Session = Depends(self.db_dependency),
|
||||
current_user: Dict[str, Any] = Depends(self.auth_dependency),
|
||||
):
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user)
|
||||
|
||||
# For child resources, parent_id validation would go here
|
||||
resource = self.service.create(db, data, tenant_id, company_id)
|
||||
return resource
|
||||
else:
|
||||
# Parent resource - no parent_id needed
|
||||
|
||||
|
||||
# Create a closure to capture the schema type
|
||||
create_schema = self.create_schema
|
||||
|
||||
|
||||
@self.router.post(
|
||||
"/",
|
||||
response_model=self.response_schema,
|
||||
@@ -305,7 +322,8 @@ class TenantCRUDRoutes(
|
||||
db: Session = Depends(self.db_dependency),
|
||||
current_user: Dict[str, Any] = Depends(self.auth_dependency),
|
||||
):
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user)
|
||||
resource = self.service.create(db, data, tenant_id, company_id)
|
||||
return resource
|
||||
|
||||
@@ -314,10 +332,10 @@ class TenantCRUDRoutes(
|
||||
# For child resources: PUT / (parent_id comes from path)
|
||||
if self.parent_id_name:
|
||||
# Child resource
|
||||
|
||||
|
||||
# Create a closure to capture the schema type
|
||||
update_schema = self.update_schema
|
||||
|
||||
|
||||
@self.router.put(
|
||||
"/",
|
||||
response_model=self.response_schema,
|
||||
@@ -331,7 +349,8 @@ class TenantCRUDRoutes(
|
||||
current_user: Dict[str, Any] = Depends(self.auth_dependency),
|
||||
**path_params,
|
||||
):
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user)
|
||||
parent_id = path_params.get(self.parent_id_name)
|
||||
|
||||
resource = self.service.update(
|
||||
@@ -346,10 +365,10 @@ class TenantCRUDRoutes(
|
||||
|
||||
else:
|
||||
# Parent resource
|
||||
|
||||
|
||||
# Create a closure to capture the schema type
|
||||
update_schema = self.update_schema
|
||||
|
||||
|
||||
@self.router.put(
|
||||
f"/{{{self.id_name}}}",
|
||||
response_model=self.response_schema,
|
||||
@@ -366,7 +385,8 @@ class TenantCRUDRoutes(
|
||||
current_user: Dict[str, Any] = Depends(self.auth_dependency),
|
||||
):
|
||||
f"""Update {self.resource_name}"""
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user)
|
||||
|
||||
resource = self.service.update(
|
||||
db, resource_id, tenant_id, data, company_id
|
||||
@@ -395,10 +415,12 @@ class TenantCRUDRoutes(
|
||||
current_user: Dict[str, Any] = Depends(self.auth_dependency),
|
||||
**path_params,
|
||||
):
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user)
|
||||
parent_id = path_params.get(self.parent_id_name)
|
||||
|
||||
success = self.service.delete(db, parent_id, tenant_id, company_id)
|
||||
success = self.service.delete(
|
||||
db, parent_id, tenant_id, company_id)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(
|
||||
@@ -422,9 +444,11 @@ class TenantCRUDRoutes(
|
||||
db: Session = Depends(self.db_dependency),
|
||||
current_user: Dict[str, Any] = Depends(self.auth_dependency),
|
||||
):
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user)
|
||||
|
||||
success = self.service.delete(db, resource_id, tenant_id, company_id)
|
||||
success = self.service.delete(
|
||||
db, resource_id, tenant_id, company_id)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(
|
||||
|
||||
@@ -44,6 +44,9 @@ class InvoiceService:
|
||||
if filters.get("operation_type"):
|
||||
query = query.filter(
|
||||
models.InvoiceHeader.operation_type == filters["operation_type"])
|
||||
if filters.get("invoice_type"):
|
||||
query = query.filter(
|
||||
models.InvoiceHeader.invoice_type == filters["invoice_type"])
|
||||
if filters.get("invoice_number"):
|
||||
query = query.filter(models.InvoiceHeader.invoice_number.ilike(
|
||||
f"%{filters['invoice_number']}%"))
|
||||
@@ -53,6 +56,10 @@ class InvoiceService:
|
||||
f"%{filters['pedimento']}%")
|
||||
)
|
||||
|
||||
if not filters.get("invoice_type") and filters.get("operation_type") == "exp":
|
||||
query = query.filter(
|
||||
models.InvoiceHeader.operation_type != "REPAR")
|
||||
|
||||
total = query.count()
|
||||
items = query.offset(skip).limit(limit).all()
|
||||
return items, total
|
||||
|
||||
@@ -8,5 +8,6 @@ class InvoiceTypeDTO(BaseModel):
|
||||
description: str
|
||||
note: Optional[str] = None
|
||||
type: Optional[str] = None
|
||||
operation: Optional[str] = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Any, Dict
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from core.database import get_core_db
|
||||
from core.security import get_current_user, has_role
|
||||
@@ -11,17 +11,28 @@ from .models import InvoiceType
|
||||
router = APIRouter(prefix="/invoice-types")
|
||||
|
||||
|
||||
@router.get("/", response_model=Dict[str, Any])
|
||||
@router.get("/", response_model=dict)
|
||||
def list_invoice_types(
|
||||
page: int = Query(1, ge=1, description="Número de página"),
|
||||
page_size: int = Query(50, ge=1, le=100, description="Tamaño de página"),
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(50, ge=1, le=100),
|
||||
type: Optional[str] = Query(None, description="Filter by type"),
|
||||
operation: Optional[str] = Query(None, description="Filter by operation type (imp, exp, both)"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
skip = (page - 1) * page_size
|
||||
query = db.query(InvoiceType)
|
||||
items = query.offset(skip).limit(page_size).all()
|
||||
|
||||
# Filter by operation if provided
|
||||
if operation:
|
||||
query = query.filter(
|
||||
(InvoiceType.operation == operation) | (
|
||||
InvoiceType.operation == "both")
|
||||
)
|
||||
|
||||
if type == "imp" and operation == "CR":
|
||||
query = query.filter(InvoiceType.operation != "exp")
|
||||
|
||||
total = query.count()
|
||||
items = query.offset((page - 1) * page_size).limit(page_size).all()
|
||||
return {
|
||||
"items": [InvoiceTypeDTO.model_validate(obj) for obj in items],
|
||||
"total": total,
|
||||
|
||||
@@ -30,47 +30,47 @@ seed = [
|
||||
),
|
||||
|
||||
# === TIPOS DE EXPORTACION ===
|
||||
("DONAC", "DONACION", "", "both", "both"),
|
||||
("EXDEF", "EXPORTACION DEFINITIVA", "", "material", "both"),
|
||||
("DONAC", "DONACION", "", "both", "exp"),
|
||||
("EXDEF", "EXPORTACION DEFINITIVA", "", "material", "exp"),
|
||||
(
|
||||
"MATDE",
|
||||
"MATERIA PRIMA O MATERIAL DEVUELTO",
|
||||
"ESTE PROCESO CONSISTE EN SOLO DESCARGAR LAS PARTES DADAS DE ALTA EN MATERIALES QUE SON RETORNADAS SIN NINGUNA MODIFICACION (A1)",
|
||||
"material",
|
||||
"both",
|
||||
"exp",
|
||||
),
|
||||
(
|
||||
"NODES",
|
||||
"NO HACE DESCARGA",
|
||||
"ESTE PROCESO DE ACTUALIZACION CONSISTE EN EXPORTAR UNA MERCANCIA Y NO DESCARGAR, POR LO TANTO NO EXISTE REPORTE DE DESCARGAS Y NO AFECTA SALDOS.",
|
||||
"both",
|
||||
"both",
|
||||
"exp",
|
||||
),
|
||||
(
|
||||
"PTERM",
|
||||
"PRODUCTO TERMINADO Y VIRTUALES",
|
||||
"EL PRODUCTO TERMINADO Y VIRTUALES DESCARGARAN: 1) APARTIR DE LOS COMPONENTES DE CADA PRODUCTO TERMINADO REGISTRADO EN LAS PARTIDAS DE EXPORTACION. 2) POR PARTE, CON LAS OPCIONES DE PODER DESCARGAR POR SUSTITUTO Y POR CLASE EN CASO DE INSUFICIENCIAS DEL COMPONENTE.",
|
||||
"material",
|
||||
"both",
|
||||
"exp",
|
||||
),
|
||||
(
|
||||
"REPAR",
|
||||
"REPARACION",
|
||||
"PROCESO QUE CONSISTE EN DOS ETAPAS: 1) DESCARGA EL PRODUCTO DE REPARACION QUE SE IMPORTO PARA REPARA, 2) DESCARGA EL LISTADO DE COMPONENTES QUE SE AGREGO AL PRODUCTO DE REPARACION",
|
||||
"material",
|
||||
"both",
|
||||
"exp",
|
||||
),
|
||||
("SCRAP", "SCRAP", "", "both", "both"),
|
||||
("SCRAP", "SCRAP", "", "both", "exp"),
|
||||
(
|
||||
"VEMEX",
|
||||
"VENTAS EN MEXICO",
|
||||
"ESTE PROCESO CONSISTE EN LA VENTA EN EL MERCADO NACIONAL DE LOS PRODUCTOS.",
|
||||
"both",
|
||||
"both",
|
||||
"exp",
|
||||
),
|
||||
("VIRTU", "VIRTUALES", "", "material", "both"),
|
||||
("VIRTU", "VIRTUALES", "", "material", "exp"),
|
||||
|
||||
# === ACTIVOS FIJOS (AMBAS OPERACIONES) ===
|
||||
("AFIJO", "ACTIVO FIJO", "", "fixed asset", "both"),
|
||||
("REEXP", "REEXPEDICION", "", "fixed asset", "both"),
|
||||
("AFIJO", "ACTIVO FIJO", "", "fixed asset", "exp"),
|
||||
("REEXP", "REEXPEDICION", "", "fixed asset", "exp"),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user