From ec8c3a7c2d429f04ef7ab31ea79f039450bdf9d7 Mon Sep 17 00:00:00 2001 From: hreyes Date: Tue, 3 Mar 2026 10:13:35 -0700 Subject: [PATCH] feature/csv-templates --- .../v1/modules/a76/csv_templates/__init__.py | 1 + .../v1/modules/a76/csv_templates/registry.py | 147 ++++++++++++++++++ .../v1/modules/a76/csv_templates/routes.py | 35 +++++ backend/api/v1/modules/a76/router.py | 2 + frontend/src/lib/api.ts | 29 ++++ .../csv-upload/UploadLauncherGrid.svelte | 28 ++-- frontend/src/lib/config/csv-upload.ts | 41 ++--- frontend/static/csv/EstructuraBOMS.xlsx | Bin 10171 -> 0 bytes .../static/csv/EstructuraCatAgenteAduanal.xls | 1 - frontend/static/csv/EstructuraCatClasesAF.xls | 1 - .../static/csv/EstructuraCatClienteProv.xls | 1 - .../static/csv/EstructuraCatConductor.xls | 1 - frontend/static/csv/EstructuraCatFraccAme.xls | 1 - .../static/csv/EstructuraCatNumerosParte.xlsx | Bin 10712 -> 0 bytes frontend/static/csv/EstructuraCatPartesAF.xls | 1 - .../static/csv/EstructuraCatPedimentos.xls | 1 - .../static/csv/EstructuraCatTiposCambio.xls | 1 - frontend/static/csv/EstructuraCatTrailers.xls | 1 - .../static/csv/EstructuraCatTransportes.xls | 1 - .../csv/EstructuraCatTransportistas.xlsx | Bin 10253 -> 0 bytes .../static/csv/EstructuraEncFacExpoCamReg.xls | 1 - .../static/csv/EstructuraEncFacImpoDef.xls | 1 - .../static/csv/EstructuraEncFacImpoTemp.xls | 1 - .../static/csv/EstructuraParExpoCamReg.xls | 1 - .../static/csv/EstructuraParFacImpoDefAF.xls | 1 - .../static/csv/EstructuraParFacImpoTempAF.xls | 1 - 26 files changed, 253 insertions(+), 46 deletions(-) create mode 100644 backend/api/v1/modules/a76/csv_templates/__init__.py create mode 100644 backend/api/v1/modules/a76/csv_templates/registry.py create mode 100644 backend/api/v1/modules/a76/csv_templates/routes.py delete mode 100644 frontend/static/csv/EstructuraBOMS.xlsx delete mode 100644 frontend/static/csv/EstructuraCatAgenteAduanal.xls delete mode 100644 frontend/static/csv/EstructuraCatClasesAF.xls delete mode 100644 frontend/static/csv/EstructuraCatClienteProv.xls delete mode 100644 frontend/static/csv/EstructuraCatConductor.xls delete mode 100644 frontend/static/csv/EstructuraCatFraccAme.xls delete mode 100644 frontend/static/csv/EstructuraCatNumerosParte.xlsx delete mode 100644 frontend/static/csv/EstructuraCatPartesAF.xls delete mode 100644 frontend/static/csv/EstructuraCatPedimentos.xls delete mode 100644 frontend/static/csv/EstructuraCatTiposCambio.xls delete mode 100644 frontend/static/csv/EstructuraCatTrailers.xls delete mode 100644 frontend/static/csv/EstructuraCatTransportes.xls delete mode 100644 frontend/static/csv/EstructuraCatTransportistas.xlsx delete mode 100644 frontend/static/csv/EstructuraEncFacExpoCamReg.xls delete mode 100644 frontend/static/csv/EstructuraEncFacImpoDef.xls delete mode 100644 frontend/static/csv/EstructuraEncFacImpoTemp.xls delete mode 100644 frontend/static/csv/EstructuraParExpoCamReg.xls delete mode 100644 frontend/static/csv/EstructuraParFacImpoDefAF.xls delete mode 100644 frontend/static/csv/EstructuraParFacImpoTempAF.xls diff --git a/backend/api/v1/modules/a76/csv_templates/__init__.py b/backend/api/v1/modules/a76/csv_templates/__init__.py new file mode 100644 index 00000000..0bb939f9 --- /dev/null +++ b/backend/api/v1/modules/a76/csv_templates/__init__.py @@ -0,0 +1 @@ +# CSV templates: generate CSV from code (no physical XLS/XLSX files) diff --git a/backend/api/v1/modules/a76/csv_templates/registry.py b/backend/api/v1/modules/a76/csv_templates/registry.py new file mode 100644 index 00000000..e6486244 --- /dev/null +++ b/backend/api/v1/modules/a76/csv_templates/registry.py @@ -0,0 +1,147 @@ +""" +Registro central de plantillas CSV: template_id -> lista de cabeceras canónicas. +Construido a partir de los TEMPLATE_COLUMNS de cada módulo de imports. +""" +import csv +import io +from typing import Dict, List, Optional + +# Importar configs de cada módulo +from api.v1.modules.a76.imports.template_config import ( + TEMPLATE_COLUMNS as IMPORTS_TEMPLATE_COLUMNS, + _resolve_template_columns as resolve_imports_template, +) +from api.v1.modules.a76.parts.imports.template_config import TEMPLATE_COLUMNS as PARTS_TEMPLATE_COLUMNS +from api.v1.modules.a76.boms.imports.template_config import TEMPLATE_COLUMNS as BOMS_TEMPLATE_COLUMNS +from api.v1.modules.a76.classes.imports.template_config import TEMPLATE_COLUMNS as CLASSES_TEMPLATE_COLUMNS +from api.v1.modules.a76.customs_brokers.imports.template_config import ( + TEMPLATE_COLUMNS as CUSTOMS_BROKERS_TEMPLATE_COLUMNS, +) +from api.v1.modules.a76.clients_and_providers.imports.template_config import ( + TEMPLATE_COLUMNS as CLIENTS_PROVIDERS_TEMPLATE_COLUMNS, +) +from api.v1.modules.a76.general_catalogs.exchange_rate.imports.template_config import ( + TEMPLATE_COLUMNS as EXCHANGE_RATE_TEMPLATE_COLUMNS, +) +from api.v1.modules.a76.general_catalogs.fractions.us_tariff_fractions.imports.template_config import ( + TEMPLATE_COLUMNS as US_TARIFF_FRACTIONS_TEMPLATE_COLUMNS, +) +from api.v1.modules.a76.pedmientos.imports.template_config import ( + TEMPLATE_COLUMNS as PEDIMENTOS_TEMPLATE_COLUMNS, +) +from api.v1.modules.a76.transportation.vehicles.imports.template_config import ( + TEMPLATE_COLUMNS as VEHICLES_TEMPLATE_COLUMNS, +) +from api.v1.modules.a76.transportation.drivers.imports.template_config import ( + TEMPLATE_COLUMNS as DRIVERS_TEMPLATE_COLUMNS, +) +from api.v1.modules.a76.transportation.trailers.imports.template_config import ( + TEMPLATE_COLUMNS as TRAILERS_TEMPLATE_COLUMNS, +) + + +def _canonicals_from_columns(cols: Optional[List[Dict]]) -> List[str]: + """Extrae la lista de nombres canónicos en orden a partir de una lista de columnas.""" + if not cols: + return [] + return [item["canonical"] for item in cols] + + +def _build_registry() -> Dict[str, List[str]]: + registry: Dict[str, List[str]] = {} + + # a76/imports (facturas): imp_temp_header, imp_temp_details, imp_def_*, exp_def_* + for tid in ("imp_temp_header", "imp_temp_details", "imp_def_header", "imp_def_details", "exp_def_header", "exp_def_details"): + cols = resolve_imports_template(tid) + registry[tid] = _canonicals_from_columns(cols) + + # part_numbers (parts); "items" usa la misma plantilla + part_cols = PARTS_TEMPLATE_COLUMNS.get("part_numbers") + registry["part_numbers"] = _canonicals_from_columns(part_cols) + registry["items"] = _canonicals_from_columns(part_cols) + + # boms + registry["boms"] = _canonicals_from_columns(BOMS_TEMPLATE_COLUMNS.get("boms")) + + # material_classes + registry["material_classes"] = _canonicals_from_columns(CLASSES_TEMPLATE_COLUMNS.get("material_classes")) + + # customs_brokers + registry["customs_brokers"] = _canonicals_from_columns(CUSTOMS_BROKERS_TEMPLATE_COLUMNS.get("customs_brokers")) + + # clients_providers + registry["clients_providers"] = _canonicals_from_columns(CLIENTS_PROVIDERS_TEMPLATE_COLUMNS.get("client_providers")) + + # exchange_rates + registry["exchange_rates"] = _canonicals_from_columns(EXCHANGE_RATE_TEMPLATE_COLUMNS.get("exchange_rates")) + + # american_fractions + registry["american_fractions"] = _canonicals_from_columns( + US_TARIFF_FRACTIONS_TEMPLATE_COLUMNS.get("us_tariff_fractions") + ) + + # pedimentos + registry["pedimentos"] = _canonicals_from_columns(PEDIMENTOS_TEMPLATE_COLUMNS.get("pedimentos")) + + # transports (vehicles) + registry["transports"] = _canonicals_from_columns(VEHICLES_TEMPLATE_COLUMNS.get("vehicles")) + + # drivers + registry["drivers"] = _canonicals_from_columns(DRIVERS_TEMPLATE_COLUMNS.get("drivers")) + + # trailers + registry["trailers"] = _canonicals_from_columns(TRAILERS_TEMPLATE_COLUMNS.get("trailers")) + + return registry + + +_TEMPLATE_HEADERS: Dict[str, List[str]] = _build_registry() + +# Nombre de archivo sugerido para descarga (sin path) +TEMPLATE_FILENAMES: Dict[str, str] = { + "customs_brokers": "EstructuraCatAgenteAduanal.csv", + "clients_providers": "EstructuraCatClienteProv.csv", + "exchange_rates": "EstructuraCatTiposCambio.csv", + "american_fractions": "EstructuraCatFraccAme.csv", + "material_classes": "EstructuraCatClasesAF.csv", + "part_numbers": "EstructuraCatPartesAF.csv", + "items": "EstructuraCatPartesAF.csv", + "boms": "EstructuraBOMS.csv", + "pedimentos": "EstructuraCatPedimentos.csv", + "transports": "EstructuraCatTransportes.csv", + "drivers": "EstructuraCatConductor.csv", + "trailers": "EstructuraCatTrailers.csv", + "imp_temp_header": "EstructuraEncFacImpoTemp.csv", + "imp_temp_details": "EstructuraParFacImpoTempAF.csv", + "imp_def_header": "EstructuraEncFacImpoDef.csv", + "imp_def_details": "EstructuraParFacImpoDefAF.csv", + "exp_def_header": "EstructuraEncFacExpoCamReg.csv", + "exp_def_details": "EstructuraParExpoCamReg.csv", +} + + +def get_template_headers(template_id: str) -> Optional[List[str]]: + """Devuelve la lista de cabeceras canónicas para el template_id, o None si no existe.""" + return _TEMPLATE_HEADERS.get(template_id) + + +def get_template_filename(template_id: str) -> str: + """Nombre de archivo sugerido para la descarga.""" + return TEMPLATE_FILENAMES.get(template_id, f"plantilla_{template_id}.csv") + + +def generate_csv_content(template_id: str, include_bom: bool = True) -> Optional[bytes]: + """ + Genera el contenido CSV (solo fila de cabeceras) para el template_id. + UTF-8, opcionalmente con BOM para Excel. + """ + headers = get_template_headers(template_id) + if not headers: + return None + buf = io.StringIO() + writer = csv.writer(buf, lineterminator="\n") + writer.writerow(headers) + content = buf.getvalue().encode("utf-8") + if include_bom: + content = b"\xef\xbb\xbf" + content + return content diff --git a/backend/api/v1/modules/a76/csv_templates/routes.py b/backend/api/v1/modules/a76/csv_templates/routes.py new file mode 100644 index 00000000..26f1e264 --- /dev/null +++ b/backend/api/v1/modules/a76/csv_templates/routes.py @@ -0,0 +1,35 @@ +""" +Rutas para descargar plantillas CSV generadas desde código (sin archivos XLS/XLSX). +""" +from typing import Any, Dict + +from fastapi import APIRouter, Depends, HTTPException +from fastapi.responses import Response + +from core.security import get_current_user + +from .registry import generate_csv_content, get_template_filename + +router = APIRouter() + + +@router.get("/{template_id}", response_class=Response) +async def download_csv_template( + template_id: str, + current_user: Dict[str, Any] = Depends(get_current_user), +): + """ + Devuelve un CSV con solo la fila de cabeceras para la plantilla indicada. + Las cabeceras son los nombres canónicos definidos en cada template_config. + """ + content = generate_csv_content(template_id, include_bom=True) + if content is None: + raise HTTPException(status_code=404, detail=f"Plantilla desconocida: {template_id}") + filename = get_template_filename(template_id) + return Response( + content=content, + media_type="text/csv; charset=utf-8", + headers={ + "Content-Disposition": f'attachment; filename="{filename}"', + }, + ) diff --git a/backend/api/v1/modules/a76/router.py b/backend/api/v1/modules/a76/router.py index d7057536..e30a47df 100644 --- a/backend/api/v1/modules/a76/router.py +++ b/backend/api/v1/modules/a76/router.py @@ -15,6 +15,7 @@ from .classes import router as classes_router from .clients_and_providers import router as client_and_provider_router from .imports.routes import router as imports_router +from .csv_templates.routes import router as csv_templates_router from .invoice_settings.routes import router as invoice_settings_router from .item_presets.routes import router as item_presets_router from .general_catalogs.company import router as company_router @@ -54,6 +55,7 @@ router.include_router(general_catalogs_router, prefix="/a76", tags=["a76 / gener router.include_router(invoices_router, prefix="/a76", tags=["a76 / invoices"]) router.include_router(items_router, prefix="/a76", tags=["a76 / items"]) router.include_router(imports_router, prefix="/a76/imports", tags=["a76 / imports"]) +router.include_router(csv_templates_router, prefix="/a76/csv-templates", tags=["a76 / csv_templates"]) router.include_router(invoice_settings_router) router.include_router(item_presets_router, prefix="/a76/item-presets", tags=["a76 / item_presets"]) router.include_router(pedimentos_router, prefix="/a76") diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 0e8b57f9..ca30b42e 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -315,6 +315,35 @@ export const api = { delete: (endpoint: string, options: RequestInit = {}) => fetchApi(endpoint, { method: 'DELETE', ...options }), + /** + * Download CSV template by template_id (generated from code, no static file). + * Returns blob and suggested filename for the browser download. + */ + async getCsvTemplateDownload( + templateId: string + ): Promise<{ blob: Blob; filename: string }> { + const token = getToken(); + const headers: Record = {}; + if (token) headers['Authorization'] = `Bearer ${token}`; + const response = await fetch(`${API_BASE_URL}/v1/a76/csv-templates/${templateId}`, { + method: 'GET', + headers, + credentials: 'include' + }); + if (!response.ok) { + const msg = response.status === 404 ? 'Plantilla no encontrada' : `Error ${response.status}`; + throw new Error(msg); + } + const blob = await response.blob(); + let filename = `plantilla_${templateId}.csv`; + const disposition = response.headers.get('Content-Disposition'); + if (disposition) { + const match = /filename="?([^";\n]+)"?/.exec(disposition); + if (match) filename = match[1].trim(); + } + return { blob, filename }; + }, + // Endpoints específicos auth: { login: (credentials: { username: string; password: string; tenant_slug: string }) => diff --git a/frontend/src/lib/components/dashboard/csv-upload/UploadLauncherGrid.svelte b/frontend/src/lib/components/dashboard/csv-upload/UploadLauncherGrid.svelte index 3f17fc78..35d1f218 100644 --- a/frontend/src/lib/components/dashboard/csv-upload/UploadLauncherGrid.svelte +++ b/frontend/src/lib/components/dashboard/csv-upload/UploadLauncherGrid.svelte @@ -4,6 +4,7 @@ import { UploadCloud, Lock } from 'lucide-svelte'; import { cn } from '$lib/utils'; import { toast } from 'svelte-sonner'; + import { api } from '$lib/api'; let { items, @@ -88,7 +89,7 @@ } } - function handleContextMenu(e: MouseEvent, item: CsvUploadItem) { + async function handleContextMenu(e: MouseEvent, item: CsvUploadItem) { if (item.disabled) { e.preventDefault(); return; @@ -96,16 +97,23 @@ e.preventDefault(); - if (!item.templateUrl) return; + if (!item.templateId) return; - const link = document.createElement('a'); - link.href = item.templateUrl; - link.download = item.templateUrl.split('/').pop() || 'plantilla.xls'; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - - toast.info(`Descargando plantilla para ${item.title}...`); + try { + toast.info(`Descargando plantilla para ${item.title}...`); + const { blob, filename } = await api.getCsvTemplateDownload(item.templateId); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + toast.success(`Plantilla descargada: ${filename}`); + } catch (err) { + toast.error(err instanceof Error ? err.message : 'Error al descargar la plantilla'); + } } diff --git a/frontend/src/lib/config/csv-upload.ts b/frontend/src/lib/config/csv-upload.ts index 9ab11d00..c51ceaae 100644 --- a/frontend/src/lib/config/csv-upload.ts +++ b/frontend/src/lib/config/csv-upload.ts @@ -28,7 +28,8 @@ export interface CsvUploadItem { group?: string; // For grouping within a tab modelTarget?: string; // The backend model this maps to description?: string; - templateUrl?: string; // Path to the template file in static/ + /** Backend template id for CSV download (e.g. customs_brokers, part_numbers). No physical file. */ + templateId?: string; disabled?: boolean; // New property to mark items as "Coming Soon" } @@ -131,49 +132,49 @@ export const catalogosConfig: CsvUploadItem[] = [ title: 'Agentes Aduanales', icon: User, modelTarget: 'CustomsBroker', - templateUrl: '/csv/EstructuraCatAgenteAduanal.xls' + templateId: 'customs_brokers' }, { id: 'clients_providers', title: 'Clientes y Proveedores', icon: Users, modelTarget: 'ClientProvider', - templateUrl: '/csv/EstructuraCatClienteProv.xls' + templateId: 'clients_providers' }, { id: 'exchange_rates', title: 'Tipo de Cambios', icon: DollarSign, modelTarget: 'ExchangeRate', - templateUrl: '/csv/EstructuraCatTiposCambio.xls' + templateId: 'exchange_rates' }, { id: 'american_fractions', title: 'Fracc. Ame.', icon: Globe, modelTarget: 'AmericanFraction', - templateUrl: '/csv/EstructuraCatFraccAme.xls' + templateId: 'american_fractions' }, { id: 'material_classes', title: 'Clases de Materiales', icon: Package, modelTarget: 'MaterialClass', - templateUrl: '/csv/EstructuraCatClasesAF.xls' + templateId: 'material_classes' }, { id: 'part_numbers', title: 'Números de parte', icon: Hash, modelTarget: 'Part', - templateUrl: '/csv/EstructuraCatPartesAF.xls', + templateId: 'part_numbers', }, { id: 'boms', title: 'BOMs', icon: Briefcase, modelTarget: 'Bom', - templateUrl: '/csv/EstructuraBOMS.xlsx', + templateId: 'boms', }, { id: 'items', @@ -181,7 +182,7 @@ export const catalogosConfig: CsvUploadItem[] = [ icon: FileText, group: 'Permisos', modelTarget: 'ItemPermission', - templateUrl: '/csv/EstructuraCatPartesAF.xls' + templateId: 'part_numbers' }, { id: 'headers', @@ -203,7 +204,7 @@ export const catalogosConfig: CsvUploadItem[] = [ title: 'Pedimentos', icon: FileDigit, modelTarget: 'Pedimento', - templateUrl: '/csv/EstructuraCatPedimentos.xls' + templateId: 'pedimentos' }, ]; @@ -213,28 +214,28 @@ export const transportesConfig: CsvUploadItem[] = [ title: 'Transportistas', icon: Ship, modelTarget: 'Transporter', - templateUrl: '/csv/EstructuraCatTransportistas.xlsx', + // No templateId: backend transporters/imports not implemented yet }, { id: 'transports', title: 'Transportes', icon: Truck, modelTarget: 'Transport', - templateUrl: '/csv/EstructuraCatTransportes.xls' + templateId: 'transports' }, { id: 'drivers', title: 'Conductores', icon: User, modelTarget: 'Driver', - templateUrl: '/csv/EstructuraCatConductor.xls' + templateId: 'drivers' }, { id: 'trailers', title: 'Trailers y Cajas', icon: Container, modelTarget: 'Trailer', - templateUrl: '/csv/EstructuraCatTrailers.xls' + templateId: 'trailers' }, ]; @@ -246,7 +247,7 @@ export const importacionConfig: CsvUploadItem[] = [ icon: FileText, group: 'Impo. Temp.', modelTarget: 'invoice_header', - templateUrl: '/csv/EstructuraEncFacImpoTemp.xls' + templateId: 'imp_temp_header' }, { id: 'imp_temp_details', @@ -254,7 +255,7 @@ export const importacionConfig: CsvUploadItem[] = [ icon: Package, group: 'Impo. Temp.', modelTarget: 'invoice_details', - templateUrl: '/csv/EstructuraParFacImpoTempAF.xls' + templateId: 'imp_temp_details' }, { id: 'imp_temp_series', @@ -271,7 +272,7 @@ export const importacionConfig: CsvUploadItem[] = [ icon: FileText, group: 'Impo. Def.', modelTarget: 'invoice_header', - templateUrl: '/csv/EstructuraEncFacImpoDef.xls' + templateId: 'imp_def_header' }, { id: 'imp_def_details', @@ -279,7 +280,7 @@ export const importacionConfig: CsvUploadItem[] = [ icon: Package, group: 'Impo. Def.', modelTarget: 'invoice_details', - templateUrl: '/csv/EstructuraParFacImpoDefAF.xls' + templateId: 'imp_def_details' }, { id: 'imp_def_series', @@ -324,7 +325,7 @@ export const exportacionConfig: CsvUploadItem[] = [ icon: FileText, group: 'Expo. Def./Cam. Reg.', modelTarget: 'invoice_header', - templateUrl: '/csv/EstructuraEncFacExpoCamReg.xls' + templateId: 'exp_def_header' }, { id: 'exp_def_details', @@ -332,7 +333,7 @@ export const exportacionConfig: CsvUploadItem[] = [ icon: Package, group: 'Expo. Def./Cam. Reg.', modelTarget: 'invoice_details', - templateUrl: '/csv/EstructuraParExpoCamReg.xls' + templateId: 'exp_def_details' }, { id: 'exp_def_series', diff --git a/frontend/static/csv/EstructuraBOMS.xlsx b/frontend/static/csv/EstructuraBOMS.xlsx deleted file mode 100644 index 4a998d0d564f9536ea168724142c267af5dca851..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10171 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 CS{dJNstFJMS0l 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 diff --git a/frontend/static/csv/EstructuraCatPartesAF.xls b/frontend/static/csv/EstructuraCatPartesAF.xls deleted file mode 100644 index e989d04f..00000000 --- a/frontend/static/csv/EstructuraCatPartesAF.xls +++ /dev/null @@ -1 +0,0 @@ -NUMERO DE PARTE DESCRIPCION EN ESPAÑOL DESCRIPCION EN INGLES CLASE UNIDAD DE MEDIDA COMERCIAL COSTO UNITARIO TIPO MONEDA COSTO CLAVE MONEDA PESO UNITARIO TIPO PESO FRACCION PAIS PREFERENCIA SECTOR RUTA DE LA IMAGEN diff --git a/frontend/static/csv/EstructuraCatPedimentos.xls b/frontend/static/csv/EstructuraCatPedimentos.xls deleted file mode 100644 index b2e2f7f2..00000000 --- a/frontend/static/csv/EstructuraCatPedimentos.xls +++ /dev/null @@ -1 +0,0 @@ -NUMERO DE PEDIMENTO (##-####-######) TIPO MOV(I=Impotación,E=Expotación) CLAVE PEDIMENTO REGIMEN FECHA INICIO FECHA FINAL FECHA DE PAGO ADUANA Y SECCION DE CRUCE ACUSE ELECTRONICO INDIVIDUAL o CONSOLIDADO (IND,CON) MET TRANS ENTRADA MET TRANS ARRIVO MET TRANS SALIDA IEPS DTA CNT PREVALIDACION MONTO TIGIE PAGO IMPUESTO? (S/N) ES MIXTO (SI/NO) OBS RECTIFICA OPCION DESTINO(Interior del Pais/Región Fronteriza/Franja Fronteriza) VALOR IVA VALOR ME VALOR ADUANAS FLETE VALOR SEGUROS SEGUROS EMBALAJES OTROS INCREMENTABLES ESTATUS (ABIERTO/CERRADO) PERSONA REV FECHA CIERRE FECHA REVISION FECHA AUTORIZACION FECHA RECIBIDO REPRESENTANTE AA CLAVE DEST ORIGEN FECHA ENTRADA RECINTO FECHA EXTRACCION RECINTO ERRORES FORMA PAGO DTA FORMA PAGO IGI FORMA PAGO PREVAL FORMA PAGO IVA RECARGOS MULTAS IVA DE PREV CUOTAS CONPENSATORIAS IDENTIFICADORES IEPS 2 FORMA DE PAGO IEPS 2 DTA 2 FORMA DE PAGO DTA 2 IVA 2 FORMA DE PAGO IVA 2 IGI 2 FORMA DE PAGO IGI 2 PREVALIDACION FORMA DE PAGO PREVALIDACION 2 CNT 2 FORMA DE PAGO CNT 2 diff --git a/frontend/static/csv/EstructuraCatTiposCambio.xls b/frontend/static/csv/EstructuraCatTiposCambio.xls deleted file mode 100644 index 916d24d3..00000000 --- a/frontend/static/csv/EstructuraCatTiposCambio.xls +++ /dev/null @@ -1 +0,0 @@ -FECHA (##/##/####) TIPO DE CAMBIO diff --git a/frontend/static/csv/EstructuraCatTrailers.xls b/frontend/static/csv/EstructuraCatTrailers.xls deleted file mode 100644 index 52f1e1d0..00000000 --- a/frontend/static/csv/EstructuraCatTrailers.xls +++ /dev/null @@ -1 +0,0 @@ -CLAVE TRAILER/CAJA NUMERO ACE TIPO DE TRAILER PRECINTO CODIGO DE ENTIDAD PLACAS ESTADO PAIS diff --git a/frontend/static/csv/EstructuraCatTransportes.xls b/frontend/static/csv/EstructuraCatTransportes.xls deleted file mode 100644 index 32093ebf..00000000 --- a/frontend/static/csv/EstructuraCatTransportes.xls +++ /dev/null @@ -1 +0,0 @@ -CLAVE CLAVE ACE CLAVE TRANSPORTE VIN TIPO TRANSPORTE CODIGO DE ENTIDAD TRANSPONDEDOR NUMERO DOT PLACAS CIUDAD ESTADO PAIS PRECINTO EMPRESA ASEGURADORA NUM. ASEGURADORA MONTO ASEGURADO FECHA DE ASEGURADORA diff --git a/frontend/static/csv/EstructuraCatTransportistas.xlsx b/frontend/static/csv/EstructuraCatTransportistas.xlsx deleted file mode 100644 index 5db522695c002ac38fe9e8437c003a61c7ae7317..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10253 zcmeHtg;yQf^7X|bxVt;SoghI1!QI{6-Q67m1P>nEB?NcBxLa_y;Qrm2H}5wydGq}R z@AX})`*g3~wfdgkRlBGq0|HU&DUwNGsdI;x#+s5k)u`(~_$OZG6Rx^xZoB8Y=g2$u!E=B>Ie{1+ACvWmUb>88FswdlE|x4&Hi|+w;kTv-ku#au~Ap@58pX4&Gi_x zvIPT31E&@M@>WsxSQV_c<37FbG4rKI&bvugA1OD`33}J@jlXBd#T(1wklE1xC>+TC zAjiW+7wG$SaOfL%r`(AVfwyhZB+NFX=-n&E4eG)7%T8gGp-xf?pRki3g9p4`#}hkR zL8lif2Y#$C&;Y>eD+ECPZ*JM3!c2Yz;+izb>)=6dspn{7?Zm|R+x~y<_+RXUfBEa> zi869M%m|@p;!k0Nx3jD9D8f>1LgFo?%D(;*E65Eo`4sr89n=IU%6LH#AN|^VpN5xL z`C<+SNpCjU%cIaRc*z^xDndU$IJm&jQ8}iFIh1eoqPosr&)%ecl=Pr+ZHuET`&OJQ zHMCAHHhuA-24$R44G#{zkRTL`KP^DBUq)li@U9YUR#@qzBDA`RE9dajc!uA6O3?x0 z`*0rVvK_^4A#Y*o%Ytrj`JQXE#9*b(jY{z$G9(u;sotGanI?$iJ8DujD70KAq z?pVh^4p8P?`)Ss*UyNjV^m4)VlntH_1x67UuYnZmKb<6Qu`{s=g>nv5{_)e_o6xP2W?Pd6QMCIsOPzPj9RhmfR?9RHKAK z?i_;a&id(o`atZUrBKlg7EmBzZ(u|C*go_|m&Ga1l99Al@w*{biswm&rc+8rpp#az zub}w8nzVSUNcgoD^Nb}WtAL7zl7odCN&4qea37`cC1-P$8?0}U2Q5W6tSYmB8jQkf zWCj{B6|C`jMT$>3Dq9-?k{LKfwt7Jgf=P>(r@!sZy@71S$M9V1F}b*@h-^u@=F}Z4ZBb=S(T-TUW%8*{pomXhd}f>&ZVKdo*k1*`S_JRfS9j{=V2Y*mSz9osG05kSK+0|Q zHGtj2n{vEXU@0{u6b7O9H+cCm5HB2jWBXA!3%j;AT{RK> z<(NnlJZAis+7*c-&X+&Ye&I;Y6oSb`#BU+0)%+cWZq?^R^SP)g32skt%y-djeRx{O z%g4;PpG8rU4SMp`Qp-ZxB9AaakA;ujHDKr8mD%MP2O9c zKJfutBmQv<_+T^;|5O$#+XJZ4dw;V{D5a`!kZ;K<*AU)Bo&v>Ei#xBPoU=2?yhyp)Cp`#ri9+jQI zBc(f`Hwz>V(r06E@7~c+;)dlHk$t#oo(g5R1Twp@S~^V-4uj(2Nd30|L$K0uMPPkX z6G=L)>CS7j$np*Jmp~8V$xB=~2|h#*z~uf6>D#+5(Hq#Gtxu*h9zd(E{0D>`f-)(h zQVxFaP-l{`!H>j#w24kTd1);wXB*DKkHfSb%RlKyuFg!ihX!WXL2vp$!y*SJ*`z#( zE_)zC;ehV=BP=?Zo0vE|G5vmE`7J(XCTW4x?*f=O*IysUN7<)B(eqr;mr@B88PJ%fVG0CNuiNx}L&?WR)2K&9F zHbLXyjzk8Vl%kM7(IK=)t#?G-NZO5R!bS<^GLyG9HX^{Wcr!SfE|p?H#Va3VYTz7V zvJ_&`^Ux2oZF=d(ZA)X_6{8$#u`8!bG8RSJIVmjUhhyQ^p>$}BOl8hE`bKF3uS~TX zzYR;ps}D;r%?Htl@gQTD30pLdlAK{L84$FYyL-C_O>>65s`2h5TA3(YF6T(cvgmz% zPa)EMr*ZJ^HN)6E%)I843Ivnl%d&9ZYF&ySD*PJF9B-7$&eVn~75Niy4!_?LzN}{5 zkoC$T>B)KfdO#(;LJDA0vX1)HFyLrcjBd&(jRiKEk{{B^ugUt`FAk zxFx{ZLLCdSlpZ%LTPn$DcQRFJN>Z<#h=a8&7{Z-N_e+lqe!jyD z4O$f}I&m8pl1?*mD3~P@I?|U@sy!r`i0{t5f1TpqoZV&_h30^;I5lJ<;|-5_HM(^X z)8Q;(TQ`}02d~XpRu?<6>oVuE820X{`8XH&z^FB|*l9w1WW8T2Cwm&x?d__c&l-nh zowOSTf!}a{*eT|aO|Vc513j&CPGxMO?Ef-q=XM*O(4>q8xSY9 z*>BibF0K8kOHALaJXiX}uGtep*D0@yAFE!P#E(eV^rdS5j_Ds+c%xr0^bH08KqdR_ z!}50)I-8r=m@xgm|DAxxnqzVJO_&`pgRg?4I&-7@UIsi9U%75hJvWkdw#6lqLUn=m zf{HC~F~HvJ%}51+TTDs?ta;KzLYw?UofDyYu)0h`%<@epX$FFg3RrJxD1meQ#5V!koB}~L0cEO#yqk780@tia zWT;MemwR--J6^WdqSXh}jH2XaEahl)`FUgq{PJ>Qls0Ahq~L|ddOoPa7ee0>o6GX( zwL$=*vksTfqQ9`5&7A$&HPuKe=Xff&W%`XJXL~_MV-L?h`=i0?l$~JO%fl&KEN{L+ zPdC1NsNL}oFW-f;8TwW*le=A{c7el&?t~DoobbtQZ|qg;hUGDWwh(%5UgvJ!v7&Xn z-E(lL$?6zJ{=PtJOTF^GJ6vodfH_G|B$arDQf((&_@-{@w+|w8N}=;ak`CRJ2#*FR zg8Q@2_K$Y-vB~pMA5IZ01jK=mGAi?40L?bE?H#W5R`-|dsJ&N@i#}|o>{ocg zNs|Irm6R1y*%Xsz;+L0!*XL`u{f^g{Wn{gVp_tDmPi1~DFSAYS&qYLbU>kOr5J~@I z2#*cBQqCG(05jf1yYsQk3~Rnlyd1-2^oco=;RnlJ`FO}Q0Qp**5?)SqXo|mCJ=r0S zrQLpvzcXUz$0S|Im81?G3~G&eHfttLD+Qc__-p?-oCDvgS(2tJe-T?3r5?={uBeZ6 z7FF6r38r|+1ZhkclNGj*eS=BH*4N_kw4;)+?QXH|+RjlSU*S}f{zvyh`(s6(E|$0W zPq1Z!qCc$}OifnA>CUebnPbJA$O@%)6a4oWc%Df^dv~25S+oW?6=^(Ar#ymrn8?GP zyiqo|T^x%fl>2?@#z{Z0yxSRudcS+~X7ct2Oy;r6t&$|YTZaHDUjU?9kuqfXiwPw` zd#l{HHllg!fQYvvS0AiR=CgBv`}?qS@AyBbtBrv{|Rn)O}kEic3?YXX9zS@_F%4HbV;l07DxCuLNkR_XU zgpe9e3#8mfn{$Va0*bI@y0$@G#cZ(3lDZf8-gaBVg)y;F)?&VLmQZ3UBGyE+MrD+L zx~DFRgzYt7xxTuz>j|KwiZI@OSMBUqvMt>9!`EWF^QiIjkfD%i>GADUc%!o`JgtBf z7EmwdZaJudLNB0YJnx<|9i%4Fq`i0*f?jU2lmx_(X=QsDz@Emm^6(BTUZw2k3Xw`O zVxtr>?sdiCMZTa6L=&lj6&oA*?;~Gu5x!e>A1IzxpS7N}`&P}RyuBHat+CLw5VgxV zwSL%ey+(UmLY6vZxZk#Qe5gnr%`3t^+cn6lzf{Or@+yawY>{({ zTW!n;o<_54xei*nE}rG>EvjY@utNflm@RuhAS4c;$fByz^PZT(t)xw{@?-WlDGBK9 zeU>(jpt@{kl&n*JNu@{0(sOB>P~q7P8}Q)hL=((N$5vfXozOU04D_rU5v_x2!2}L3 zSY4Mgv?ebQ)Y*o}SWSv*>Db85xK!Ah&LNFtFBKRV(SBQMAgdpO3Z3HN9UXII+D?0$ zthQtyq8jMhw*C;K>x04xjoGn^qjK2rUC)Bcvq7?IvKT5~><9AW#-5;@sKdlyyEjgc z1&Y?*`uo(2EIX3Tv4Ht3+_MY!FIs5pJLf^Ze+-wdiCYK2T0Eqt*dM9TOYZIs6a?DZp4t9uibx`W@Q>aXMs}VO2HRf>OcJ4*huRp zw!f<8T1!C}RjMBUwv!{Wk1S$y{`4W#E*Mn=eEXJEBimp}c$I61H$gi;x`Q%Rz=u^6 zgQujvSbYyN5w0Y^q0JW`u}-nF`0GgSQW@e!-YeqbtQh=y(|Zw7W7N%-U0wgJ|> z+)BB)g2k`iba1jsW;+ijt$LJuQtdg zErzSAsDfjMJ>N5gRGj2>O%q)rSDT=uwpcZ0F2!L-~<%%@j%6!4>!Pm_FbpGAp^zG=6-B~?Icgw((GEC2MB!V}u4`@x96zfvZ1 zM7RuzG4}gvfEAMV_~|@)My#~i1)qjlmDT*xTE=@=^8Oq5zGj@wDIdaa)7_Cd4uny? zPXa?|?6d6CiBSINyprN6`+2d}7jliU+HlS99HA(V29x*0MK28zTNIek2_QwL3+nPe z-PaPIpO_OdT)xZx7^@1VRal`L7dGpMA!-2i1xx9=FfT870Irlbz@nqwhyX%qFh79!<#*z|)$$8?2H;4{pY&g~sf3Jf()o-S zkcXI*@5#niH?Oe5f!zq$dv7jDBUC6^w3l)VYfw7-w0Qh=n=%;3-xb^sgLg+T-gT0J zv)bdTI;O*Os?ZR9e3L{r%jnNrsv|%1tVw_^U!fc(q<}nnsjwfojupWOBisrv{MlOw z+np8mIXRsf_BjL(d*&#=LXwz=wbQlH(xh4ij)F@>u_~^S&S!flN0-D#1$-z$s7+w0 z^eTZm$TPrs2APEL`e8p)gBa1bt6o=NM^*O1y6*t~>yqhYWsah^;I^u1pUdK0duSL= z|96|0g6sMWn!sdvSU*G)Qo?zOH(LAQ(KF$5n8%VbM;T*aaZS}Eu^cHej^hRaSJ{zJ zu!vZtP+?FEx61U)dPWMhEcEL20=V!PtzE(ld)0z!a@X*Y?COTc{6h;Zk3Yp>__bR} zx>$WL)am0E%pBF_ca&<>i4+`p=!<`r{C3T-c_Ap_w`(;b+tvQIUkw{PoE7A7Q>bwt zU)K2Ym>n+?x9Q^ge$qJ3Cj2qLETczfB8u8kn1NGMq;{uI`KUmxEs^yDWTbDwE3w9} zq?l~WMxuY7hTe^Aeq+q5#2qxWA{!x;NC6G>$U1Z+d_e=HywY!prHc8?V;K2Il45Rn zK)2GjX#FV&L-f-5E^oeyZ=7#e9$_2=UNWRnv3N!1o7q6OlrY3E5LM$RgMDA_l<4K7 z5N)0%+C;S2=q>O{wC}(W#V1Dvqef;$dVWLgy1`sMhs5{+NsGF>-bM%knXxl#Q)U_G z!^=j(ypsOX97!Zy>=$Ewft@m!3{nNDnxm9}yUgwJ3vJ_A9D~f`VUpDWE5%TTS6$c? zWBpSHtw0OUPcaUGg!ay3@}@4@^SOrgCg2%xdmR{ z78z(2C9N# z{E?EF8#tO6D>*w_*qZ&GnK&mZ$n}t5gsjUvAqTq+>;|zEoj86x5LT3ea6TK0wEC0` z?2dl%n#3huI!U<9>S=C9sd)cL{=l4n1dYEnYZ#GG^O$>ZOxm9eB+6!SY;TnDAt;Zy z`0U`GwX!Y>^uVZ{>-%>7awQzT+*DMxYRHX?Ke)_%(||m1A-H0}?y{w6o3vfBqnl%X5q~h4x0}O-b_<3hI{++c0|cA?~ZrsB#qUXvDJT4_5G;g6-(edp!_L^2R8q#blc>O8YJn!8 z_!E@T{bz+S{Tb8hBWQljLGz0WDoPvM8Ob}^**h^A**Tj0v1H(XwP+CWG81*AdYExS z&Y)gJhI}%72XS)8CWa~rU}Xu`Lbg{`Z3M*gR-dkmPE@(BBnM?2fsgiUBEyTioRie* zYE~+_^b!h?kyZ+2HX+$+QSOmsdueFplW*vp0b!W9^l4 zvu;E6kMEEuoFwdvNo?zrN5jfi4SVi*m%)M4&ViKfRHQ7r_lry(E* zCj23H2!V+RukQTn2QLKs$!uTCpSyyyVjad81CclP*IGQ;8*MV`MTyC`$Im_Q&u6Oe z=1Y$vA@T};1Z(N*Y7MuBaOrH;eXVQ{<>^3>v9m;RgYjJM6u3g*Os``y?Ml9DqSb$7 zfaVOR*(U43qTVS?U9Z9l-?b2km?q0qaTWPVI}|m~*wg3z*~vYAfZ~-Z8&E&)bI-NU z^^e{iqTw$e3t}HS1OR~YSN0j$+y9Szpeg%f%S@E9TmG#<3~@t@u!_&KDvuCQZYa)s zRc!#a*=QzPWRx3g93Gu;JRjFMIiumW6hq9U%TH!u8~4b0#F7w~D?cJy$%??JDscSf zTHUwhA&=D%7PFp3KUY@#aL)HhpOe-#b%ET(5Vcqy(Wz-FT|3AtC!{{e`7rgSGqETu z*5!n7P1J3{NZ%)Gj|(p@Iv4a6#*1G%KeqsdI>cuV~_pSFJBM(Pkx#v_zj!X)um6` z&0GJeP+;H;AT|2ug;)Q6Xa7F_!!j&+slOZe`x23V1AjYy206xG7L5D~{IzZVC$tsR zNdKjY{ww(J{m4I|06-4h@8JJWck)*|zc#7e-H5=EvsLx{Muyr(~1VtKeilx zHSlZk;!gv@Sic+ivy|~G^w%WgPv|Vszo5US9KTxlyNv#c2LL!o0D%7x)nDO%kAr`O g*MWL2{|EmgD#}Yif!;0v@CNkr2kEvO