feature/api-doda-update
This commit is contained in:
@@ -13,7 +13,7 @@ from typing import Any, List, Optional
|
||||
from sqlalchemy import and_
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from .models import Doda
|
||||
from .models import Doda, DodaPedimento
|
||||
|
||||
# Encabezados (orden legacy Clarion)
|
||||
EXPORT_HEADERS: List[str] = [
|
||||
@@ -231,3 +231,88 @@ def parse_export_params(
|
||||
if mode not in ("raw", "formatted"):
|
||||
raise ValueError("date_mode debe ser raw o formatted")
|
||||
return d0, d1, fmt, mode
|
||||
|
||||
|
||||
# --- Exportación de pedimentos (líneas) de un DODA específico (legacy / pantalla) ---
|
||||
|
||||
PEDIMENTO_EXPORT_HEADERS: List[str] = [
|
||||
"PATENTE",
|
||||
"DOCUMENTO",
|
||||
"ACUSE_VA",
|
||||
"REMESA",
|
||||
"CANTIDAD",
|
||||
"IMPORTE_USD",
|
||||
"IMPORTE_DIF_USD",
|
||||
"NIU",
|
||||
"ARTICULO",
|
||||
]
|
||||
|
||||
|
||||
def _as_decimal_text(value: Any) -> str:
|
||||
if value is None:
|
||||
return ""
|
||||
return _as_text(value)
|
||||
|
||||
|
||||
def pedimento_row_values(row: DodaPedimento) -> List[str]:
|
||||
"""
|
||||
ACUSE_VA = COVE; CANTIDAD = UMC (captura típica en listado);
|
||||
IMPORTE_USD / IMPORTE_DIF_USD = montos en USD; ARTICULO = art. 7 (0/1).
|
||||
"""
|
||||
return [
|
||||
_as_text(row.authorization_patent),
|
||||
_as_text(row.document),
|
||||
_as_text(row.cove),
|
||||
_as_text(row.shipment),
|
||||
_as_text(row.umc),
|
||||
_as_decimal_text(row.effective_amount_usd),
|
||||
_as_decimal_text(row.difference_amount_usd),
|
||||
_as_text(row.dta_niu),
|
||||
_as_text(row.article_7),
|
||||
]
|
||||
|
||||
|
||||
def list_pedimentos_for_doda_export(
|
||||
db: Session,
|
||||
*,
|
||||
tenant_id: int,
|
||||
company_id: int,
|
||||
doda_id: int,
|
||||
) -> List[DodaPedimento]:
|
||||
return (
|
||||
db.query(DodaPedimento)
|
||||
.join(Doda, DodaPedimento.doda_id == Doda.id)
|
||||
.filter(
|
||||
Doda.id == doda_id,
|
||||
Doda.tenant_id == tenant_id,
|
||||
Doda.company_id == company_id,
|
||||
)
|
||||
.order_by(DodaPedimento.pedimento_line.asc())
|
||||
.all()
|
||||
)
|
||||
|
||||
|
||||
def build_pedimentos_export_text(
|
||||
rows: List[DodaPedimento],
|
||||
*,
|
||||
export_format: DodaExportFormat,
|
||||
) -> str:
|
||||
delim = _delimiter_for_format(export_format)
|
||||
out = io.StringIO()
|
||||
w = csv.writer(
|
||||
out,
|
||||
delimiter=delim,
|
||||
quoting=csv.QUOTE_MINIMAL,
|
||||
lineterminator="\r\n",
|
||||
)
|
||||
w.writerow(PEDIMENTO_EXPORT_HEADERS)
|
||||
for r in rows:
|
||||
w.writerow(pedimento_row_values(r))
|
||||
return out.getvalue()
|
||||
|
||||
|
||||
def parse_pedimento_export_format(format_str: str) -> DodaExportFormat:
|
||||
try:
|
||||
return DodaExportFormat(format_str.lower().strip())
|
||||
except ValueError as e:
|
||||
raise ValueError("format debe ser csv, xls o txt") from e
|
||||
|
||||
@@ -49,8 +49,11 @@ from .print_cache import touch_invalidate_doda_report
|
||||
from .report_service import DodaReportPdfService
|
||||
from .export_service import (
|
||||
build_export_text,
|
||||
build_pedimentos_export_text,
|
||||
list_dodas_in_date_range,
|
||||
list_pedimentos_for_doda_export,
|
||||
parse_export_params,
|
||||
parse_pedimento_export_format,
|
||||
_content_type_and_filename,
|
||||
)
|
||||
from core.security import get_current_user, validate_access_to_resource
|
||||
@@ -108,6 +111,49 @@ async def export_doda_list(
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/export/pedimentos/{doda_id}",
|
||||
summary="Exportar líneas de pedimento de un DODA (CSV, TSV como XLS, TXT con |)",
|
||||
)
|
||||
async def export_doda_pedimentos(
|
||||
doda_id: int,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
file_format: str = Query("xls", alias="format", description="csv, xls o txt"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
Reporte por DODA seleccionado: columnas alineadas al listado de pedimentos (PATENTE, DOCUMENTO, COVE, etc.).
|
||||
Si no hay líneas, se devuelve el archivo solo con encabezados.
|
||||
"""
|
||||
tenant_id = int(validate_access_to_resource(db, company_id, current_user))
|
||||
try:
|
||||
fmt = parse_pedimento_export_format(file_format)
|
||||
except ValueError as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(e)
|
||||
) from e
|
||||
|
||||
doda = DodaService.get_by_id(db, doda_id, tenant_id, company_id)
|
||||
if not doda:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail="DODA no encontrado."
|
||||
)
|
||||
|
||||
rows = list_pedimentos_for_doda_export(
|
||||
db, tenant_id=tenant_id, company_id=company_id, doda_id=doda_id
|
||||
)
|
||||
text = build_pedimentos_export_text(rows, export_format=fmt)
|
||||
content_type, _ = _content_type_and_filename(fmt)
|
||||
fname = f"doda_pedimentos_{doda_id}.{fmt.value}"
|
||||
data = ("\ufeff" + text).encode("utf-8")
|
||||
return StreamingResponse(
|
||||
io.BytesIO(data),
|
||||
media_type=content_type,
|
||||
headers={"Content-Disposition": f'attachment; filename="{fname}"'},
|
||||
)
|
||||
|
||||
|
||||
# Incluir rutas CRUD (contiene GET /{id}, POST /, PUT /{id}, DELETE /{id}).
|
||||
# Se registra DESPUÉS de los endpoints literales para que /export, /alta-logs,
|
||||
# /alta-status no sean capturados por el parámetro /{id}.
|
||||
|
||||
Reference in New Issue
Block a user