diff --git a/frontend/src/lib/components/dashboard/invoices/create-edit-dialog.svelte b/frontend/src/lib/components/dashboard/invoices/create-edit-dialog.svelte index 546ccfa4..c6a4e708 100644 --- a/frontend/src/lib/components/dashboard/invoices/create-edit-dialog.svelte +++ b/frontend/src/lib/components/dashboard/invoices/create-edit-dialog.svelte @@ -1,15 +1,20 @@ - + - {isEditing ? "Editar Factura" : "Nueva Factura"} + {isEditing ? 'Editar Factura' : 'Nueva Factura'} - {isEditing - ? "Modifica los datos de la factura" - : "Ingresa los datos de la nueva factura"} + {isEditing ? 'Modifica los datos de la factura' : 'Ingresa los datos de la nueva factura'} @@ -397,24 +406,29 @@
-
- - { - if (v) formData.operation_type = v as "imp" | "exp"; - }} - > - - {formData.operation_type === 'imp' ? 'Importación' : formData.operation_type === 'exp' ? 'Exportación' : 'Seleccionar tipo'} - - - Importación - Exportación - - -
+
+ + { + if (v) formData.operation_type = v as 'imp' | 'exp'; + }} + > + + {formData.operation_type === 'imp' + ? 'Importación' + : formData.operation_type === 'exp' + ? 'Exportación' + : 'Seleccionar tipo'} + + + Importación + Exportación + + +
+
- +
@@ -470,11 +480,7 @@
- +
@@ -514,11 +520,7 @@
- +
@@ -548,7 +550,7 @@ />
-
+
- +
@@ -696,34 +694,31 @@ {#if error} -
+
{error}
{/if} - - {/* Optional: maybe refresh something or just let user continue */}} + onSuccess={() => { + /* Optional: maybe refresh something or just let user continue */ + }} /> diff --git a/frontend/src/lib/components/dashboard/pedimentos/edit/general-tab-form.svelte b/frontend/src/lib/components/dashboard/pedimentos/edit/general-tab-form.svelte index adf9f65b..c5a4886a 100644 --- a/frontend/src/lib/components/dashboard/pedimentos/edit/general-tab-form.svelte +++ b/frontend/src/lib/components/dashboard/pedimentos/edit/general-tab-form.svelte @@ -15,8 +15,8 @@ import IdentificadoresTabForm from './identifiers-tab-form.svelte'; import ExchangeRateDialog from '$lib/components/dashboard/exchange_rate/create-edit-dialog.svelte'; import { Calendar, Clock } from 'lucide-svelte'; - import { - loadServerDate, + import { + loadServerDate, addDaysLocal, getCurrentLocalYear, getCurrentLocalDate, @@ -27,7 +27,7 @@ import { shortcutStore } from '$lib/stores/shortcut-store'; import { focusStore, interactionMode } from '$lib/stores/focus-store'; - let { + let { pedimento, formData = $bindable(), identificadoresFormData = $bindable(), @@ -37,7 +37,7 @@ clients = [], codePedimentoRegimens = [], isActive = false - }: { + }: { pedimento: Pedimento | null; formData?: any; identificadoresFormData?: any; @@ -77,13 +77,19 @@ // Exchange Rate Check State let showExchangeRateDialog = $state(false); - let missingExchangeRateDate = $state(""); + let missingExchangeRateDate = $state(''); // Extraer regímenes únicos de codePedimentoRegimens const uniqueRegimens = $derived( - Array.from(new Set(codePedimentoRegimens.map(r => r.regimen_code).filter((code): code is string => code !== null))) + Array.from( + new Set( + codePedimentoRegimens + .map((r) => r.regimen_code) + .filter((code): code is string => code !== null) + ) + ) .sort() - .map(code => ({ code, label: code })) + .map((code) => ({ code, label: code })) ); // Funciones de mapeo entre type_code (E/I) y operation_type (exp/imp) @@ -106,7 +112,7 @@ // Opciones constantes const operationOptions = [ { value: 'exp', label: 'Exportación' }, - { value: 'imp', label: 'Importación' }, + { value: 'imp', label: 'Importación' } ]; // Track previous values to detect changes @@ -115,22 +121,30 @@ // Opciones filtradas para Régimen y Tipo de Operación basadas en las selecciones actuales // NOTA: La Clave NO se filtra, siempre muestra todas las opciones - + const filteredRegimens = $derived.by(() => { if (!formData) return uniqueRegimens; - + // Si hay clave o tipo de operación seleccionado, filtrar - if (formData.pedimento_code || formData.operation_type !== null && formData.operation_type !== undefined) { + if ( + formData.pedimento_code || + (formData.operation_type !== null && formData.operation_type !== undefined) + ) { const expectedTypeCode = operationTypeToTypeCode(formData.operation_type); - - const matches = codePedimentoRegimens.filter(r => { - const matchesCode = !formData.pedimento_code || r.pedimento_code === formData.pedimento_code; + + const matches = codePedimentoRegimens.filter((r) => { + const matchesCode = + !formData.pedimento_code || r.pedimento_code === formData.pedimento_code; const matchesType = !expectedTypeCode || r.type_code === expectedTypeCode; return matchesCode && matchesType; }); - - const validRegimens = new Set(matches.map(m => m.regimen_code).filter((code): code is string => code !== null)); - return Array.from(validRegimens).sort().map(code => ({ code, label: code })); + + const validRegimens = new Set( + matches.map((m) => m.regimen_code).filter((code): code is string => code !== null) + ); + return Array.from(validRegimens) + .sort() + .map((code) => ({ code, label: code })); } return uniqueRegimens; }); @@ -146,9 +160,13 @@ // Solo filtrar por la clave del pedimento (no por régimen) // Esto permite cambiar entre Exportación e Importación libremente if (formData.pedimento_code) { - const matches = codePedimentoRegimens.filter(r => r.pedimento_code === formData.pedimento_code); - const validTypes = new Set(matches.map(m => typeCodeToOperationType(m.type_code)).filter(t => t !== null)); - return operationOptions.filter(opt => validTypes.has(opt.value)); + const matches = codePedimentoRegimens.filter( + (r) => r.pedimento_code === formData.pedimento_code + ); + const validTypes = new Set( + matches.map((m) => typeCodeToOperationType(m.type_code)).filter((t) => t !== null) + ); + return operationOptions.filter((opt) => validTypes.has(opt.value)); } return operationOptions; }); @@ -156,30 +174,30 @@ // Reactive synchronization between Clave, Régimen, and Tipo de Operación // REGLA: La Clave es el campo principal y NUNCA se modifica automáticamente // Solo se auto-llenan Régimen y Tipo de Operación basándose en la Clave - + // Cuando cambia la Clave del Pedimento $effect(() => { if (!formData) return; - + const currentCode = formData.pedimento_code; - + // Detectar si la clave cambió const codeChanged = currentCode !== previousPedimentoCode; - + // Solo procesar si la clave cambió if (!codeChanged) return; - + // Actualizar el tracking previousPedimentoCode = currentCode; - - if (!currentCode) return; - - const matches = codePedimentoRegimens.filter(r => r.pedimento_code === currentCode); - + + if (!currentCode) return; + + const matches = codePedimentoRegimens.filter((r) => r.pedimento_code === currentCode); + if (matches.length === 0) return; - + // Auto-llenar con el primer match - const firstMatch = matches[0]; + const firstMatch = matches[0]; if (firstMatch.regimen_code) { formData.regime = firstMatch.regimen_code; } @@ -192,17 +210,17 @@ // Cuando cambia el Régimen $effect(() => { if (!formData) return; - + const currentRegime = formData.regime; - if (!currentRegime) return; - - const matches = codePedimentoRegimens.filter(r => r.regimen_code === currentRegime); - + if (!currentRegime) return; + + const matches = codePedimentoRegimens.filter((r) => r.regimen_code === currentRegime); + if (matches.length === 0) return; - + // Si hay clave seleccionada, solo validar (NO auto-llenar tipo de operación) if (formData.pedimento_code) { - const exactMatch = matches.find(m => m.pedimento_code === formData.pedimento_code); + const exactMatch = matches.find((m) => m.pedimento_code === formData.pedimento_code); } // Si hay tipo de operación pero no clave, no hacer nada // (el usuario debe seleccionar la clave primero) @@ -212,32 +230,33 @@ // Optimizado: solo ejecutar cuando cambien los campos específicos relevantes $effect(() => { if (!formData) return; - + // Solo rastrear los campos que realmente importan const currentType = formData.operation_type; const currentCode = formData.pedimento_code; const currentRegime = formData.regime; - + // Detectar si el tipo de operación cambió const typeChanged = currentType !== previousOperationType; previousOperationType = currentType; - + // Si no hay clave seleccionada o tipo no definido, salir temprano if (!currentCode || currentType === null || currentType === undefined) return; - + const expectedTypeCode = operationTypeToTypeCode(currentType); - + // Buscar matches para esta combinación de clave + tipo de operación - const matches = codePedimentoRegimens.filter(r => - r.pedimento_code === currentCode && - r.type_code === expectedTypeCode + const matches = codePedimentoRegimens.filter( + (r) => r.pedimento_code === currentCode && r.type_code === expectedTypeCode ); - + if (matches.length === 0) return; - + // Obtener regímenes válidos para esta combinación - const validRegimens = new Set(matches.map(m => m.regimen_code).filter((code): code is string => code !== null)); - + const validRegimens = new Set( + matches.map((m) => m.regimen_code).filter((code): code is string => code !== null) + ); + // Si el tipo de operación cambió O el régimen actual no es válido, actualizar el régimen if (typeChanged || !currentRegime || !validRegimens.has(currentRegime)) { const firstMatch = matches[0]; @@ -255,7 +274,6 @@ const end_date = addDaysLocal(currentDate, 4); const payment_date = addDaysLocal(end_date, 2); - // Inicializar formData con los valores del pedimento (o vacío si es null) if (!formData) { const datesData = pedimento?.pedimento_dates; @@ -263,7 +281,7 @@ const decrementablesData = pedimento?.pedimento_decrementables; const indexesData = pedimento?.pedimento_indexes; const configAdditionalData = pedimento?.pedimento_config_additional; - + formData = { year: pedimento?.year || currentYear, customs_office: pedimento?.customs_office || '', @@ -282,11 +300,11 @@ // Date fields - Cargamos convirtiendo a local para el usuario entry_date: loadServerDate(datesData?.entry_date) || currentDate, end_date: loadServerDate(datesData?.end_date) || end_date, - payment_date: loadServerDate(datesData?.payment_date) || payment_date, + payment_date: loadServerDate(datesData?.payment_date) || payment_date, pedimento_date: loadServerDate(datesData?.pedimento_date), extraction_date: loadServerDate(datesData?.extraction_date), rectification_payment_date: loadServerDate(datesData?.rectification_payment_date), - original_date: loadServerDate(datesData?.original_date), + original_date: loadServerDate(datesData?.original_date), // Campos de captura automática (Local) fecha_captura: currentDate, hora_captura: currentTime @@ -312,26 +330,29 @@ $effect(() => { const entryDate = formData?.entry_date; const companyId = companyStore.activeCompany?.id; - + // Solo ejecutar si los valores clave cambiaron - if (formData && entryDate && companyId && - (entryDate !== lastFetchedDate || companyId !== lastCompanyId)) { - + if ( + formData && + entryDate && + companyId && + (entryDate !== lastFetchedDate || companyId !== lastCompanyId) + ) { lastFetchedDate = entryDate; - lastCompanyId = companyId; - + lastCompanyId = companyId; + getExchangeRateByDate(entryDate, companyId) - .then(usdRate => { + .then((usdRate) => { if (usdRate && formData) { - formData.exchange_rate = usdRate.value; + formData.exchange_rate = usdRate.value; } else { console.warn('⚠️ [TIPO CAMBIO] No encontrado para fecha:', entryDate); } }) - .catch(err => { + .catch((err) => { console.error('❌ [TIPO CAMBIO] Error:', err); }); - } + } }); const pedimentoTypeOptions = [ @@ -351,11 +372,11 @@ export async function checkPaymentDateRate(date: string): Promise { if (!date || !companyStore.activeCompany?.id) return true; - + try { const rate = await getExchangeRateByDate(date, companyStore.activeCompany.id); if (!rate) { - // Abrir modal preventivamente + // Abrir modal preventivamente missingExchangeRateDate = date; showExchangeRateDialog = true; return false; @@ -363,17 +384,17 @@ return true; } catch (error) { console.error('Error checking payment date rate:', error); - // Si hay error de red, asumimos que falta para forzar reintento/captura segura - missingExchangeRateDate = date; - showExchangeRateDialog = true; + // Si hay error de red, asumimos que falta para forzar reintento/captura segura + missingExchangeRateDate = date; + showExchangeRateDialog = true; return false; } } - export function openExchangeRateDialog(date: string) { - missingExchangeRateDate = date; - showExchangeRateDialog = true; - } + export function openExchangeRateDialog(date: string) { + missingExchangeRateDate = date; + showExchangeRateDialog = true; + } @@ -381,18 +402,18 @@
Información General - - Edita los datos principales del pedimento - + Edita los datos principales del pedimento
- + -
+
{formData.fecha_captura}
-
+
{formData.hora_captura} @@ -401,8 +422,10 @@
-
-
+
+
@@ -418,22 +441,22 @@
- +
- formData.customs_office = v ?? ''} + onValueChange={(v: string) => (formData.customs_office = v ?? '')} > {formData.customs_office ? formData.customs_office.substring(0, 2) : 'Sel...'} - + {#if customsSections.length === 0}
No hay aduanas disponibles @@ -441,7 +464,10 @@ {:else} {#each customsSections as section} - + {section.customs_code} - {section.section_name} @@ -452,23 +478,23 @@
- +
- formData.license = v || null} + onValueChange={(v: string | undefined) => (formData.license = v || null)} > {formData.license || 'Sel...'} - - {@const validBrokers = customsBrokers.filter(b => b.license)} + + {@const validBrokers = customsBrokers.filter((b) => b.license)} {#if customsBrokers.length === 0}
No hay agentes aduanales registrados @@ -481,7 +507,10 @@ {#each validBrokers as broker} {@const displayName = broker.name || broker.broker_key || ''} - + {displayName ? `${displayName} - ${broker.license}` : broker.license} @@ -492,11 +521,13 @@
- +
- +
-
+
- formData.pedimento_code = v ?? ''} + onValueChange={(v: string) => (formData.pedimento_code = v ?? '')} > - {pedimentoCodes.find(o => o.code === formData.pedimento_code)?.code || 'Sel...'} + {pedimentoCodes.find((o) => o.code === formData.pedimento_code)?.code || 'Sel...'} - + {#each pedimentoCodes as code}
@@ -552,13 +583,14 @@
- formData.operation_type = v || null} + onValueChange={(v: string) => (formData.operation_type = v || null)} > - {operationOptions.find(o => o.value === formData.operation_type)?.label || 'Seleccionar...'} + {operationOptions.find((o) => o.value === formData.operation_type)?.label || + 'Seleccionar...'} {#each filteredOperationTypes as option} @@ -571,11 +603,11 @@
- {#if hasMultipleRegimens} - formData.regime = v ?? ''} + onValueChange={(v: string) => (formData.regime = v ?? '')} > @@ -583,9 +615,12 @@ - {#each filteredRegimens as regimen} + {#each filteredRegimens as regimen} - + {regimen.code} @@ -605,18 +640,18 @@
-
+
- formData.client_id = v ? Number(v) : null} + onValueChange={(v: string) => (formData.client_id = v ? Number(v) : null)} > - {clients.find(c => c.id === formData.client_id)?.name || 'Seleccionar cliente...'} + {clients.find((c) => c.id === formData.client_id)?.name || 'Seleccionar cliente...'} @@ -627,7 +662,10 @@ {:else} {#each clients as client} - + {client.name} @@ -640,13 +678,14 @@
- formData.pedimento_type = v ?? ''} + onValueChange={(v: string) => (formData.pedimento_type = v ?? '')} > - {pedimentoTypeOptions.find(o => o.value === formData.pedimento_type)?.label || 'Seleccionar...'} + {pedimentoTypeOptions.find((o) => o.value === formData.pedimento_type)?.label || + 'Seleccionar...'} {#each pedimentoTypeOptions as option} @@ -655,12 +694,9 @@
-
- - -
+
@@ -700,145 +736,112 @@
- +
- +
- -
+ +
-
-
- - - - - - + + + + + + +
-
{#if activeSection === 'fechas'} -
+
- +
{#if formData.pedimento_type === 'consolidated'}
- +
{/if}
- +
- +
- +
{#if formData.pedimento_code === 'R1'} @@ -856,125 +859,118 @@
- -
+ +
{:else if activeSection === 'incrementables'}
-
- Factor: 1.00000 -
+
Factor: 1.00000
-
- -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- +
+
-
- - -
-
- - + + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+
+ + +
+
+ + +
-
{:else if activeSection === 'identificadores'} {:else if activeSection === 'indices'} -
+
- formData.tipo_factor = v ?? ''} + onValueChange={(v: string) => (formData.tipo_factor = v ?? '')} > - {formData.tipo_factor === 'INPC' ? 'I.N.P.C' : formData.tipo_factor === 'variacion_cambiaria' ? 'Variación Cambiaria' : 'Seleccionar'} + {formData.tipo_factor === 'INPC' + ? 'I.N.P.C' + : formData.tipo_factor === 'variacion_cambiaria' + ? 'Variación Cambiaria' + : 'Seleccionar'} @@ -998,7 +994,7 @@
-
+
{:else if activeSection === 'adicional'} -
+
@@ -1023,14 +1019,14 @@ bind:value={formData.anio_impresion} placeholder="25" maxlength={2} - class="text-center w-40 focus-visible:ring-0 focus-visible:ring-offset-0" + class="w-40 text-center focus-visible:ring-0 focus-visible:ring-offset-0" />
-
+
-
+
-
+
-
+
-
+
-
+
-
- Factor: 0.05000 -
+
Factor: 0.05000
-
- -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- - -
- -
- + +
+ + -
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+ + +
@@ -1220,9 +1199,11 @@ - {/* Optional: maybe refresh something or just let user continue */}} + onSuccess={() => { + /* Optional: maybe refresh something or just let user continue */ + }} /> diff --git a/frontend/src/routes/dashboard/+layout.svelte b/frontend/src/routes/dashboard/+layout.svelte index 44acac62..adc8e934 100644 --- a/frontend/src/routes/dashboard/+layout.svelte +++ b/frontend/src/routes/dashboard/+layout.svelte @@ -8,6 +8,7 @@ import * as Sidebar from '$lib/components/ui/sidebar/index.js'; import { companyStore } from '$lib/stores/company.svelte'; import ExchangeRateGuard from '$lib/components/dashboard/exchange_rate/exchange-rate-guard.svelte'; + import { page } from '$app/state'; let { data, children }: { data: LayoutData; children: any } = $props(); @@ -63,4 +64,6 @@ - +{#if !page.url.pathname.includes('/dashboard/invoices') && !page.url.pathname.includes('/dashboard/pedimentos')} + +{/if}