feature/csv-templates

This commit is contained in:
hreyes
2026-03-03 10:13:35 -07:00
parent ededff9dce
commit ec8c3a7c2d
26 changed files with 253 additions and 46 deletions

View File

@@ -315,6 +315,35 @@ export const api = {
delete: <T = any>(endpoint: string, options: RequestInit = {}) => fetchApi<T>(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<string, string> = {};
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 }) =>

View File

@@ -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');
}
}
</script>

View File

@@ -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',