diff --git a/frontend/src/lib/config/shortcuts.ts b/frontend/src/lib/config/shortcuts.ts index cb2ab56a..16249465 100644 --- a/frontend/src/lib/config/shortcuts.ts +++ b/frontend/src/lib/config/shortcuts.ts @@ -18,6 +18,10 @@ export const GLOBAL_NAV = { 'r': '/dashboard/reference_data/pedimento_regimens', 'e': '/dashboard/reference_data/customs_sections#', 'd': '/dashboard/reference_data/customs_sections', + + // Invoices + 'i': '/dashboard/invoices', + // Common Actions 'b': 'SEARCH_FOCUS', // Special case for generic focus 'h': '/', // Home diff --git a/frontend/src/lib/config/shortcuts/goods-edit-shortcuts.ts b/frontend/src/lib/config/shortcuts/goods-edit-shortcuts.ts index 572ef494..98044a3f 100644 --- a/frontend/src/lib/config/shortcuts/goods-edit-shortcuts.ts +++ b/frontend/src/lib/config/shortcuts/goods-edit-shortcuts.ts @@ -6,12 +6,9 @@ export const obtenerAtajosFormularioMercancia = (acciones: { manejarRegresar: () => void; }): ShortcutDef[] => [ // Pestañas - { key: 'Alt+G', description: 'Tab General', action: () => acciones.cambiarPestana('general') }, - { key: 'Alt+I', description: 'Tab Imágenes', action: () => acciones.cambiarPestana('cont1') }, - { key: 'Alt+E', description: 'Tab Exportación', action: () => acciones.cambiarPestana('cont2') }, - { key: 'Alt+O', description: 'Tab Opciones', action: () => acciones.cambiarPestana('opciones') }, - { key: 'Alt+S', description: 'Tab Sanitarias', action: () => acciones.cambiarPestana('opcionales2') }, - { key: 'Alt+X', description: 'Tab Otros', action: () => acciones.cambiarPestana('otros') }, + { key: 'Alt+Digit1', description: 'Tab General', action: () => acciones.cambiarPestana('general') }, + { key: 'Alt+Digit2', description: 'Tab Imágenes', action: () => acciones.cambiarPestana('cont1') }, + { key: 'Alt+Digit3', description: 'Tab Exportación', action: () => acciones.cambiarPestana('cont2') }, // Acciones Globales { key: 'Ctrl+S', description: 'Guardar cambios', action: acciones.manejarGuardar }, diff --git a/frontend/src/lib/config/shortcuts/goods-shortcuts.ts.bak b/frontend/src/lib/config/shortcuts/goods-shortcuts.ts.bak deleted file mode 100644 index 5ef7e93e..00000000 --- a/frontend/src/lib/config/shortcuts/goods-shortcuts.ts.bak +++ /dev/null @@ -1,73 +0,0 @@ -import type { ShortcutDef } from '../../stores/shortcut-store'; - -export const obtenerAtajosListaPartes = (acciones: { - manejarNuevo: () => void; - manejarActualizar: () => void; - manejarBorrar: () => void; -}): ShortcutDef[] => [ - { - key: 'Alt+Shift+N', - description: 'Nueva Parte', - action: acciones.manejarNuevo - }, - { - key: 'Alt+Shift+R', - description: 'Actualizar Lista', - action: acciones.manejarActualizar - }, - { - key: 'Alt+Shift+D', - description: 'Borrar Seleccionado', - action: acciones.manejarBorrar - } - ]; - -export const obtenerAtajosListaClasesActivoFijo = (acciones: { - manejarNuevo: () => void; - manejarActualizar: () => void; - manejarBorrar: () => void; -}): ShortcutDef[] => [ - { - key: 'Alt+Shift+N', - description: 'Nueva Clase', - action: acciones.manejarNuevo - }, - { - key: 'Alt+Shift+R', - description: 'Actualizar Lista', - action: acciones.manejarActualizar - }, - { - key: 'Alt+Shift+D', - description: 'Borrar Seleccionado', - action: acciones.manejarBorrar - } - ]; - -export const obtenerAtajosFormularioParte = (acciones: { - cambiarPestana: (pestana: string) => void; - manejarGuardar: () => void; - manejarCancelar: () => void; - esActivoFijo: boolean; -}): ShortcutDef[] => { - const common = [ - { key: 'Alt+Digit1', description: 'Pest. General', action: () => acciones.cambiarPestana('general') }, - { key: 'Ctrl+S', description: 'Guardar cambios', action: acciones.manejarGuardar }, - { key: 'Escape', description: 'Cancelar / Regresar', action: acciones.manejarCancelar } - ]; - - if (acciones.esActivoFijo) { - return [ - ...common, - { key: 'Alt+Digit2', description: 'Pest. Continuación 1', action: () => acciones.cambiarPestana('cont1') }, - { key: 'Alt+Digit3', description: 'Pest. Continuación 2', action: () => acciones.cambiarPestana('cont2') } - ]; - } - - return [ - ...common, - { key: 'Alt+Digit2', description: 'Pest. Opciones', action: () => acciones.cambiarPestana('opciones') }, - { key: 'Alt+Digit3', description: 'Pest. Opcionales 2', action: () => acciones.cambiarPestana('opcionales2') }, - { key: 'Alt+Digit4', description: 'Pest. Otros', action: () => acciones.cambiarPestana('otros') } - ]; -}; diff --git a/frontend/src/lib/config/shortcuts/invoice-edit-shortcuts.ts b/frontend/src/lib/config/shortcuts/invoice-edit-shortcuts.ts new file mode 100644 index 00000000..2f7c71c5 --- /dev/null +++ b/frontend/src/lib/config/shortcuts/invoice-edit-shortcuts.ts @@ -0,0 +1,15 @@ +import type { ShortcutDef } from '$lib/stores/shortcut-store'; + +export const obtenerAtajosEdicionFactura = (acciones: { + cambiarPestana: (pestana: string) => void; + manejarGuardar: () => void; + manejarRegresar: () => void; +}): ShortcutDef[] => [ + { key: 'Alt+Q', description: 'Tab General', action: () => acciones.cambiarPestana('general') }, + { key: 'Alt+O', description: 'Tab Observ.', action: () => acciones.cambiarPestana('observations') }, + { key: 'Alt+A', description: 'Tab Partidas (Artículos)', action: () => acciones.cambiarPestana('items') }, + { key: 'Alt+X', description: 'Tab Otros', action: () => acciones.cambiarPestana('others') }, + { key: 'Alt+K', description: 'Tab Cont.', action: () => acciones.cambiarPestana('continuation') }, + { key: 'Ctrl+S', description: 'Guardar cambios', action: acciones.manejarGuardar }, + { key: 'Escape', description: 'Regresar / Cancelar', action: acciones.manejarRegresar } + ]; diff --git a/frontend/src/lib/config/shortcuts/invoice-list-shortcuts.ts b/frontend/src/lib/config/shortcuts/invoice-list-shortcuts.ts new file mode 100644 index 00000000..b9f9778e --- /dev/null +++ b/frontend/src/lib/config/shortcuts/invoice-list-shortcuts.ts @@ -0,0 +1,48 @@ +import type { ShortcutDef } from '$lib/stores/shortcut-store'; + +export const obtenerAtajosListaFacturas = (acciones: { + manejarCrear: () => void; + manejarActualizar: () => void; + manejarEditar: () => void; + manejarDescargarPdf: () => void; + manejarDescargarConsolidado: () => void; + // Filtros de navegación + irTemporal: () => void; + irDefinitiva: () => void; + irNacional: () => void; + irCambioRegimen: () => void; + irExportacion: () => void; + irReparacion: () => void; +}): ShortcutDef[] => [ + { key: 'Alt+V', description: 'Ver Temporales', action: acciones.irTemporal }, + { key: 'Alt+F', description: 'Ver Definitivas', action: acciones.irDefinitiva }, + { key: 'Alt+N', description: 'Ver Nacionales', action: acciones.irNacional }, + { key: 'Alt+Z', description: 'Ver Cambio Régimen', action: acciones.irCambioRegimen }, + { key: 'Alt+L', description: 'Ver Exportaciones', action: acciones.irExportacion }, + { key: 'Alt+Y', description: 'Ver Reparaciones', action: acciones.irReparacion }, + { + key: 'Alt+Shift+N', + description: 'Nueva Factura', + action: acciones.manejarCrear + }, + { + key: 'Alt+Shift+R', + description: 'Actualizar Lista', + action: acciones.manejarActualizar + }, + { + key: 'Alt+Shift+E', + description: 'Editar Seleccionada', + action: acciones.manejarEditar + }, + { + key: 'Alt+Shift+P', + description: 'Descargar PDF', + action: acciones.manejarDescargarPdf + }, + { + key: 'Alt+Shift+C', + description: 'Descargar Consolidado', + action: acciones.manejarDescargarConsolidado + } + ]; diff --git a/frontend/src/routes/dashboard/invoices/+page.svelte b/frontend/src/routes/dashboard/invoices/+page.svelte index 61138a73..5835dbcd 100644 --- a/frontend/src/routes/dashboard/invoices/+page.svelte +++ b/frontend/src/routes/dashboard/invoices/+page.svelte @@ -1,634 +1,687 @@
-
-
-

Facturas

-

- Gestiona las facturas del sistema -

-
- -
+
+
+

Facturas

+

Gestiona las facturas del sistema

+
+ +
- - - Filtros - Filtra las facturas por diferentes criterios (los filtros se aplican automáticamente) - - -
-
- - -
+ + + Filtros + Filtra las facturas por diferentes criterios (los filtros se aplican automáticamente) + + +
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
-
-
-
+
+ + +
+
+
+
- {#if error} - - - Error - {error} - - - {/if} + {#if error} + + + Error + {error} + + + {/if} - - -
-
- Listado de Facturas - - Mostrando {allItems.length} de {totalItems} registros - -
- -
-
- - - -
+ + +
+
+ Listado de Facturas + + Mostrando {allItems.length} de {totalItems} registros + +
+ +
+
+ + + +
+ - - - -
-
- -
- - - - -
-
-
-
\ No newline at end of file + +
+
+ +
+ + + + +
+
+
+ diff --git a/frontend/src/routes/dashboard/invoices/edit/[id]/+page.svelte b/frontend/src/routes/dashboard/invoices/edit/[id]/+page.svelte index 9567cb94..a28a8c3f 100644 --- a/frontend/src/routes/dashboard/invoices/edit/[id]/+page.svelte +++ b/frontend/src/routes/dashboard/invoices/edit/[id]/+page.svelte @@ -2,29 +2,29 @@ import { onMount } from 'svelte'; import { browser } from '$app/environment'; import { goto } from '$app/navigation'; - import { page } from '$app/stores'; + import { page } from '$app/stores'; import * as Tabs from '$lib/components/ui/tabs'; import { Button } from '$lib/components/ui/button'; import { Badge } from '$lib/components/ui/badge'; import { Separator } from '$lib/components/ui/separator'; import { toast } from 'svelte-sonner'; - import { - ArrowLeft, - FileText, - DollarSign, - Truck, - Package, + import { + ArrowLeft, + FileText, + DollarSign, + Truck, + Package, Eye, - LoaderCircle, - Save - } from 'lucide-svelte'; - + LoaderCircle, + Save + } from 'lucide-svelte'; + // Importar los componentes de cada pestaña import GeneralTabForm from '$lib/components/dashboard/invoices/edit/general-tab-form.svelte'; import ObservationsTabForm from '$lib/components/dashboard/invoices/edit/observations-tab-form.svelte'; import ItemsTabForm from '$lib/components/dashboard/invoices/edit/items/items-tab-form.svelte'; import OthersTabForm from '$lib/components/dashboard/invoices/edit/others-tab-form.svelte'; - import InvoiceTopFields from '$lib/components/dashboard/invoices/edit/invoice-top-fields.svelte'; + import InvoiceTopFields from '$lib/components/dashboard/invoices/edit/invoice-top-fields.svelte'; import ContinuationTabForm from '$lib/components/dashboard/invoices/edit/continuation-tab-form.svelte'; // Importar la API de facturas @@ -35,11 +35,13 @@ import { getExchangeRateByDate } from '$lib/api/dashboard/a76/exchange-rate'; import { getExchangeRates } from '$lib/api/dashboard/a76/general_catalogs/exchange-rate'; import ExchangeRateDialog from '$lib/components/dashboard/exchange_rate/create-edit-dialog.svelte'; - + import { useShortcuts } from '$lib/hooks/use-shortcuts'; + import { obtenerAtajosEdicionFactura } from '$lib/config/shortcuts/invoice-edit-shortcuts'; + // Cargar companyStore solo en el cliente - no usamos sidebar en esta página let companyStore: any = $state(undefined); let mounted = $state(false); - + onMount(async () => { try { const companyStoreModule = await import('$lib/stores/company.svelte'); @@ -85,7 +87,7 @@ let activeTab = $state('general'); let saving = $state(false); - + // ID de la factura let invoiceId = $state(data.invoiceId ?? null); @@ -103,10 +105,12 @@ let othersExists = $state(false); let continuationExists = $state(false); - let calculatedExchangeRate = $state(data.invoice?.financials?.exchange_rate ?? null); - + let calculatedExchangeRate = $state( + data.invoice?.financials?.exchange_rate ?? null + ); + let showExchangeRateDialog = $state(false); - let missingExchangeRateDate = $state(""); + let missingExchangeRateDate = $state(''); async function checkExchangeRate(date: string): Promise { if (!date || !companyStore?.activeCompany?.id) return true; @@ -120,10 +124,10 @@ date: date, page_size: 1 }); - + const actualResponse = response as any; const items = actualResponse.data?.items || []; - + if (items.length === 0) { console.log('No exchange rate found for', date); missingExchangeRateDate = date; @@ -139,7 +143,7 @@ // Por seguridad, abrimos modal. missingExchangeRateDate = date; showExchangeRateDialog = true; - return false; + return false; } } @@ -147,14 +151,14 @@ $effect(() => { if (mounted && companyStore?.activeCompany?.id && InvoiceTopFieldsFormData?.invoice_date) { getExchangeRateByDate(InvoiceTopFieldsFormData.invoice_date, companyStore.activeCompany.id) - .then(rate => { + .then((rate) => { if (rate) { - calculatedExchangeRate = rate.value; + calculatedExchangeRate = rate.value; } else { calculatedExchangeRate = 0; } }) - .catch(err => console.error('Error auto-updating exchange rate:', err)); + .catch((err) => console.error('Error auto-updating exchange rate:', err)); } }); @@ -173,7 +177,7 @@ try { // Validar ID si es edición if (!data.isCreate && !invoiceId) { - toast.error("Error interrrno: No se encuentra el ID de la factura para actualizar."); + toast.error('Error interrrno: No se encuentra el ID de la factura para actualizar.'); saving = false; return; } @@ -199,7 +203,7 @@ othersFormData, continuationFormData } - }); + }); if (!result.success) { // Crear un error con validationErrors si existen @@ -212,8 +216,8 @@ toast.success('Todos los cambios se guardaron correctamente'); } catch (e) { - console.error('Error saving all:', e); - + console.error('Error saving all:', e); + if (e instanceof Error && e.message.includes('401')) { toast.error('Sesión expirada. Recargando página...'); setTimeout(() => { @@ -221,26 +225,34 @@ }, 1500); } else { const errorMessage = e instanceof Error ? e.message : 'Error al guardar los cambios'; - + // Intercept exchange rate error - if (errorMessage.includes('No existe un Tipo de Cambio registrado') || errorMessage.includes('financials.exchange_rate')) { - console.log("Interceptor: Exchange rate missing error caught (Invoice Page)."); - + if ( + errorMessage.includes('No existe un Tipo de Cambio registrado') || + errorMessage.includes('financials.exchange_rate') + ) { + console.log('Interceptor: Exchange rate missing error caught (Invoice Page).'); + const dateMatch = errorMessage.match(/(\d{4}-\d{2}-\d{2})/); - const missingDate = dateMatch ? dateMatch[0] : (InvoiceTopFieldsFormData?.invoice_date || ""); - + const missingDate = dateMatch + ? dateMatch[0] + : InvoiceTopFieldsFormData?.invoice_date || ''; + missingExchangeRateDate = missingDate; showExchangeRateDialog = true; return; } // Si hay errores de validación, mostrarlos en detalle - if (e && typeof e === 'object' && 'validationErrors' in e && Array.isArray((e as any).validationErrors)) { - const validationErrors = (e as any).validationErrors; - const errorList = validationErrors.map((err: any) => - `• ${err.message}` - ).join('\n'); - + if ( + e && + typeof e === 'object' && + 'validationErrors' in e && + Array.isArray((e as any).validationErrors) + ) { + const validationErrors = (e as any).validationErrors; + const errorList = validationErrors.map((err: any) => `• ${err.message}`).join('\n'); + toast.error(errorMessage, { description: errorList }); @@ -254,6 +266,15 @@ saving = false; } } + + useShortcuts( + 'Invoice Edit', + obtenerAtajosEdicionFactura({ + cambiarPestana: (pestana) => (activeTab = pestana), + manejarGuardar: handleSaveAll, + manejarRegresar: handleBack + }) + );
@@ -267,7 +288,9 @@

{#if data.isCreate} {@const invoiceType = generalFormData?.invoice_type || data.filters?.invoice_type} - {@const invoiceTypeInfo = invoiceType ? data.invoiceTypes?.find(t => t.key === invoiceType) : null} + {@const invoiceTypeInfo = invoiceType + ? data.invoiceTypes?.find((t) => t.key === invoiceType) + : null} Nueva Factura {#if invoiceTypeInfo} @@ -301,18 +324,18 @@
- - - + - - - - +

- { // Actualizar el tipo de cambio mostrado if (InvoiceTopFieldsFormData.invoice_date && companyStore?.activeCompany?.id) { - getExchangeRateByDate(InvoiceTopFieldsFormData.invoice_date, companyStore.activeCompany.id) - .then(rate => { + getExchangeRateByDate( + InvoiceTopFieldsFormData.invoice_date, + companyStore.activeCompany.id + ).then((rate) => { if (rate) calculatedExchangeRate = rate.value; }); } }} /> -
@@ -413,7 +439,7 @@ Partidas - + Otros