From 6e276cd8a9da9a8b92dbb1aff64eafd09a8128ab Mon Sep 17 00:00:00 2001 From: Kevin_Ramirez Date: Wed, 11 Feb 2026 13:45:20 -0600 Subject: [PATCH] DODA mejorado --- .../dashboard/a76/general_catalogs/doda.ts | 14 +- .../reference_data/customs_sections.ts | 25 +- .../modals/broker-selector-dialog.svelte | 27 +- .../modals/port-selector-dialog.svelte | 17 +- .../modals/transporter-selector-dialog.svelte | 36 +- .../doda/child-detail-table.svelte | 105 +++ .../general_catalogs/doda/columns.ts | 108 ++- .../general_catalogs/doda/data-table.svelte | 110 +-- .../customs-section-selector-dialog.svelte | 135 +++ .../general_catalogs/doda/+page.svelte | 252 +++++- .../doda/edit/[[id]]/+page.svelte | 812 ++++++++++++------ 11 files changed, 1243 insertions(+), 398 deletions(-) create mode 100644 frontend/src/lib/components/dashboard/general_catalogs/doda/child-detail-table.svelte create mode 100644 frontend/src/lib/components/dashboard/shared/modals/customs-section-selector-dialog.svelte diff --git a/frontend/src/lib/api/dashboard/a76/general_catalogs/doda.ts b/frontend/src/lib/api/dashboard/a76/general_catalogs/doda.ts index e6ae28dc..bea5bf50 100644 --- a/frontend/src/lib/api/dashboard/a76/general_catalogs/doda.ts +++ b/frontend/src/lib/api/dashboard/a76/general_catalogs/doda.ts @@ -20,7 +20,7 @@ export interface DodaContainer { container_line: number; container_value?: string; seals?: string; - seals_detail?: DodaContainerSeal[]; + seals_detail?: DodaContainerSeal[]; } export interface DodaContainerCreate { @@ -79,13 +79,13 @@ export interface Doda { dispatch_customs?: string; customs_sections?: string; patent?: string; - pedimentos?: string; + pedimentos?: string; caat?: string; transport_identification?: string; fast_id?: string; operation_type?: string; status?: string; - + // Relaciones containers?: DodaContainer[]; american_pedimentos?: DodaAmericanPedimento[]; @@ -95,7 +95,7 @@ export interface Doda { tenant_id?: string; created_at?: string; updated_at?: string; - + // Campos extra del formulario que vimos en el frontend selected?: boolean; user_selected?: string; @@ -152,7 +152,7 @@ export interface DodaCreate { unique_badge_number?: string; } -export interface DodaUpdate extends Partial {} +export interface DodaUpdate extends Partial { } export interface DodaListResponse { items: Doda[]; @@ -187,7 +187,7 @@ export async function getDoda(id: number, companyId?: number): Promise { if (companyId) { params.append('company_id', companyId.toString()); } - const response = await api.get(`/v1/a76/doda/${id}/?${params.toString()}`); + const response = await api.get(`/v1/a76/doda/${id}/detail?${params.toString()}`); return response.data; } @@ -202,5 +202,5 @@ export async function updateDoda(id: number, data: DodaUpdate, companyId: number } export async function deleteDoda(id: number, companyId: number): Promise { - await api.delete(`/v1/a76/doda/${id}/?company_id=${companyId}`); + await api.delete(`/v1/a76/doda/${id}?company_id=${companyId}`); } \ No newline at end of file diff --git a/frontend/src/lib/api/dashboard/reference_data/customs_sections.ts b/frontend/src/lib/api/dashboard/reference_data/customs_sections.ts index 30811ca1..e73934d8 100644 --- a/frontend/src/lib/api/dashboard/reference_data/customs_sections.ts +++ b/frontend/src/lib/api/dashboard/reference_data/customs_sections.ts @@ -5,25 +5,25 @@ import { api } from '$lib/api'; export interface CustomsSection { - customs_code: string; - section_name: string; + customs_code: string; + section_name: string; } export interface CustomsSectionListResponse { - items: CustomsSection[]; - total: number; - page: number; - page_size: number; + items: CustomsSection[]; + total: number; + page: number; + page_size: number; } export interface CreateCustomsSectionData { - customs_code: string; - section_name: string; + customs_code: string; + section_name: string; } export interface UpdateCustomsSectionData { - customs_code?: string; - section_name?: string; + customs_code?: string; + section_name?: string; } /** @@ -37,7 +37,6 @@ export const customsSectionsApi = { */ list: (page = 1, pageSize = 50) => api.get( - // CORREGIDO: Añadido '/' antes de los parámetros `/v1/public/reference_data/customs-sections/?page=${page}&page_size=${pageSize}` ), @@ -45,7 +44,7 @@ export const customsSectionsApi = { * Obtiene una sección aduanera por código * @param customs_code - Código de la sección aduanera */ - get: (customs_code: string) => + get: (customs_code: string) => // CORREGIDO: Añadido '/' final api.get(`/v1/public/reference_data/customs-sections/${customs_code}/`), @@ -70,7 +69,7 @@ export const customsSectionsApi = { * Elimina una sección aduanera * @param customs_code - Código de la sección aduanera a eliminar */ - delete: (customs_code: string) => + delete: (customs_code: string) => // CORREGIDO: Añadido '/' después del código api.delete(`/v1/public/reference_data/customs-sections/${customs_code}/`) }; \ No newline at end of file diff --git a/frontend/src/lib/components/dashboard/export/manifest/modals/broker-selector-dialog.svelte b/frontend/src/lib/components/dashboard/export/manifest/modals/broker-selector-dialog.svelte index 39b7482e..afaf62e5 100644 --- a/frontend/src/lib/components/dashboard/export/manifest/modals/broker-selector-dialog.svelte +++ b/frontend/src/lib/components/dashboard/export/manifest/modals/broker-selector-dialog.svelte @@ -20,12 +20,14 @@ let loaded = $state(false); let filteredItems = $derived( - items.filter( - (i) => - i.name?.toLowerCase().includes(searchTerm.toLowerCase()) || - i.broker_key?.toLowerCase().includes(searchTerm.toLowerCase()) || - i.license?.toLowerCase().includes(searchTerm.toLowerCase()) - ) + items.filter((i) => { + const search = searchTerm.toLowerCase(); + return ( + (i.name?.toLowerCase() || '').includes(search) || + (i.broker_key?.toLowerCase() || '').includes(search) || + (i.license?.toLowerCase() || '').includes(search) + ); + }) ); $effect(() => { @@ -38,10 +40,15 @@ if (!companyStore.activeCompany?.id) return; loading = true; try { - const res = await customsBrokersApi.list(companyStore.activeCompany.id.toString()); - if (res.data?.items) { - items = res.data.items; - loaded = true; + const res = await customsBrokersApi.list(companyStore.activeCompany.id.toString(), 1, 100); + const data = (res.data || res) as any; + if (data) { + if (Array.isArray(data)) { + items = data; + } else if (data.items && Array.isArray(data.items)) { + items = data.items; + } + loaded = items.length > 0; } } catch (e) { console.error('Error loading customs brokers:', e); diff --git a/frontend/src/lib/components/dashboard/export/manifest/modals/port-selector-dialog.svelte b/frontend/src/lib/components/dashboard/export/manifest/modals/port-selector-dialog.svelte index 9227644b..7867bcb1 100644 --- a/frontend/src/lib/components/dashboard/export/manifest/modals/port-selector-dialog.svelte +++ b/frontend/src/lib/components/dashboard/export/manifest/modals/port-selector-dialog.svelte @@ -25,9 +25,9 @@ const search = searchTerm.toLowerCase(); return ( !searchTerm || - i.description?.toLowerCase().includes(search) || - i.port_code?.toLowerCase().includes(search) || - i.location_description?.toLowerCase().includes(search) + (i.description?.toLowerCase() || '').includes(search) || + (i.port_code?.toLowerCase() || '').includes(search) || + (i.location_description?.toLowerCase() || '').includes(search) ); }) ); @@ -47,9 +47,14 @@ page: 1, page_size: 100 }); - if (res.data && res.data.items) { - items = res.data.items; - loaded = true; + const data = (res.data || res) as any; + if (data) { + if (Array.isArray(data)) { + items = data; + } else if (data.items && Array.isArray(data.items)) { + items = data.items; + } + loaded = items.length > 0; } } catch (e) { console.error('Error loading ports:', e); diff --git a/frontend/src/lib/components/dashboard/export/manifest/modals/transporter-selector-dialog.svelte b/frontend/src/lib/components/dashboard/export/manifest/modals/transporter-selector-dialog.svelte index d7ce634c..49704472 100644 --- a/frontend/src/lib/components/dashboard/export/manifest/modals/transporter-selector-dialog.svelte +++ b/frontend/src/lib/components/dashboard/export/manifest/modals/transporter-selector-dialog.svelte @@ -3,6 +3,7 @@ import { Input } from '$lib/components/ui/input'; import * as Dialog from '$lib/components/ui/dialog'; import { Search, Loader2, Truck } from 'lucide-svelte'; + import { toast } from 'svelte-sonner'; import { transportersApi, type Transporter } from '$lib/api/dashboard/a76/transporters'; import { companyStore } from '$lib/stores/company.svelte'; @@ -20,12 +21,14 @@ let loaded = $state(false); let filteredItems = $derived( - items.filter( - (i) => - i.name?.toLowerCase().includes(searchTerm.toLowerCase()) || - i.transporter_key?.toLowerCase().includes(searchTerm.toLowerCase()) || - i.rfc?.toLowerCase().includes(searchTerm.toLowerCase()) - ) + items.filter((i) => { + const search = searchTerm.toLowerCase(); + return ( + (i.name?.toLowerCase() || '').includes(search) || + (i.transporter_key?.toLowerCase() || '').includes(search) || + (i.rfc?.toLowerCase() || '').includes(search) + ); + }) ); $effect(() => { @@ -38,14 +41,29 @@ if (!companyStore.activeCompany?.id) return; loading = true; try { - const res = await transportersApi.list(companyStore.activeCompany.id); + // Explicitly set pagination to avoid defaults + const res = await transportersApi.list(companyStore.activeCompany.id, { + page: 1, + page_size: 100 + }); const data = (res as any).data || res; - if (data && data.items) { - items = data.items; + if (data) { + if (Array.isArray(data)) { + items = data; + } else if (data.items) { + // Handle case where items might be an array or inside an object + items = Array.isArray(data.items) ? data.items : []; + } + loaded = true; + } else if ((res as any).error) { + console.error('API Error loading transporters:', (res as any).error); + toast.error('Error al cargar transportistas: ' + (res as any).error); loaded = true; } } catch (e) { console.error('Error loading transporters:', e); + toast.error('Error de conexión al cargar transportistas'); + loaded = true; } finally { loading = false; } diff --git a/frontend/src/lib/components/dashboard/general_catalogs/doda/child-detail-table.svelte b/frontend/src/lib/components/dashboard/general_catalogs/doda/child-detail-table.svelte new file mode 100644 index 00000000..0feed7a4 --- /dev/null +++ b/frontend/src/lib/components/dashboard/general_catalogs/doda/child-detail-table.svelte @@ -0,0 +1,105 @@ + + +
+
+ {#if title} +

{title}

+ {/if} +
+ +
+ + + + {#each columns as col} + {col.header} + {/each} + + + + {#if data.length === 0} + + + No hay registros. + + + {:else} + {#each data as row, i} + + {#each columns as col} + + {#if col.render} + {col.render(row[col.key])} + {:else} + {row[col.key] ?? '-'} + {/if} + + {/each} + + {/each} + {/if} + + +
+ +
+ + + +
+
diff --git a/frontend/src/lib/components/dashboard/general_catalogs/doda/columns.ts b/frontend/src/lib/components/dashboard/general_catalogs/doda/columns.ts index 2636ab51..add997d8 100644 --- a/frontend/src/lib/components/dashboard/general_catalogs/doda/columns.ts +++ b/frontend/src/lib/components/dashboard/general_catalogs/doda/columns.ts @@ -1,14 +1,57 @@ import type { ColumnDef } from '@tanstack/table-core'; import type { Doda } from '$lib/api/dashboard/a76/general_catalogs/doda'; -import { renderComponent } from '$lib/components/ui/data-table'; +import { renderComponent, renderSnippet } from '$lib/components/ui/data-table'; +import { createRawSnippet } from 'svelte'; import DataTableActions from './data-table-actions.svelte'; +function formatDate(date?: string | null): string { + if (!date) return '-'; + // Supposing created_at is an ISO string or similar + try { + return new Date(date).toLocaleDateString('es-MX', { + year: 'numeric', + month: '2-digit', + day: '2-digit' + }); + } catch (e) { + return date; + } +} + export function createColumns(onSuccess?: () => void): ColumnDef[] { return [ { - accessorKey: 'integration_number', - header: 'No. Integración', - cell: ({ row }) => row.original.integration_number || 'N/A' + accessorKey: 'id', + header: 'Folio', + cell: ({ row }) => { + const numberSnippet = createRawSnippet<[{ number: number }]>((getProps) => { + const { number } = getProps(); + return { + render: () => + `${number}` + }; + }); + return renderSnippet(numberSnippet, { number: row.original.id }); + } + }, + { + accessorKey: 'created_at', + header: 'Fecha doda', + cell: ({ row }) => { + const dateSnippet = createRawSnippet<[{ date: string }]>((getProps) => { + const { date } = getProps(); + return { + render: () => + `
${date}
` + }; + }); + return renderSnippet(dateSnippet, { date: formatDate(row.original.created_at) }); + } + }, + { + accessorKey: 'dispatch_customs', + header: 'Desp', + cell: ({ row }) => row.original.dispatch_customs || 'N/A' }, { accessorKey: 'patent', @@ -17,18 +60,65 @@ export function createColumns(onSuccess?: () => void): ColumnDef[] { }, { accessorKey: 'pedimentos', - header: 'Pedimentos', + header: 'Pedimento(s)', cell: ({ row }) => row.original.pedimentos || 'N/A' }, { - accessorKey: 'doda_date', - header: 'Fecha', - cell: ({ row }) => row.original.doda_date || 'N/A' + accessorKey: 'shipments', + header: 'Remesa(s)', + cell: ({ row }) => row.original.shipments || 'N/A' + }, + { + accessorKey: 'integration_number', + header: 'Integracion', + cell: ({ row }) => { + const numberSnippet = createRawSnippet<[{ number?: string | null }]>((getProps) => { + const { number } = getProps(); + return { + render: () => + `${number || 'N/A'}` + }; + }); + return renderSnippet(numberSnippet, { number: row.original.integration_number }); + } + }, + { + accessorKey: 'transaction_number', + header: 'No transaccion', + cell: ({ row }) => row.original.transaction_number || 'N/A' + }, + { + accessorKey: 'transport_identification', + header: 'Id transporte', + cell: ({ row }) => row.original.transport_identification || 'N/A' + }, + { + accessorKey: 'caat', + header: 'CAAT', + cell: ({ row }) => row.original.caat || 'N/A' + }, + { + accessorKey: 'last_user', + header: 'Usuario', + cell: ({ row }) => row.original.last_user || 'N/A' }, { accessorKey: 'status', header: 'Estatus', - cell: ({ row }) => row.original.status || 'N/A' + cell: ({ row }) => { + const status = row.original.status; + const statusSnippet = createRawSnippet<[{ status?: string | null }]>((getProps) => { + const { status } = getProps(); + const colorClass = status === 'VALIDADO' ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'; + return { + render: () => + ` + ${status || '-'} + ` + }; + }); + return renderSnippet(statusSnippet, { status }); + } }, { id: 'actions', diff --git a/frontend/src/lib/components/dashboard/general_catalogs/doda/data-table.svelte b/frontend/src/lib/components/dashboard/general_catalogs/doda/data-table.svelte index bcf39690..b96d47d8 100644 --- a/frontend/src/lib/components/dashboard/general_catalogs/doda/data-table.svelte +++ b/frontend/src/lib/components/dashboard/general_catalogs/doda/data-table.svelte @@ -1,16 +1,15 @@ -
-
+
+
- + {#each table.getHeaderGroups() as headerGroup (headerGroup.id)} {#each headerGroup.headers as header (header.id)} - + {#if !header.isPlaceholder} onRowClick?.(row.original)} - class="cursor-pointer hover:bg-muted/50 transition-colors {row.getIsSelected() - ? 'bg-primary/10' - : ''}" + class="cursor-pointer transition-colors {row.getIsSelected() + ? 'bg-gray-300 dark:bg-gray-600' + : 'hover:bg-gray-100 dark:hover:bg-gray-700'}" > {#each row.getVisibleCells() as cell (cell.id)} - + {/each} @@ -90,32 +108,26 @@ {/each} + + {#if hasMore} + + +
+ {#if loading} +
+
+ Cargando más... +
+ {:else} +
Desplázate para cargar más
+ {/if} +
+
+
+ {/if}
- - -
-
- Total: {totalItems} -
-
- - -
-
diff --git a/frontend/src/lib/components/dashboard/shared/modals/customs-section-selector-dialog.svelte b/frontend/src/lib/components/dashboard/shared/modals/customs-section-selector-dialog.svelte new file mode 100644 index 00000000..9d040b27 --- /dev/null +++ b/frontend/src/lib/components/dashboard/shared/modals/customs-section-selector-dialog.svelte @@ -0,0 +1,135 @@ + + + + + + Seleccionar Sección Aduanera + Catálogo general de aduanas y secciones. + + +
+ + +
+ +
+ {#if loading} +
+ +

Cargando catálogo...

+
+ {:else if filteredItems.length === 0} +
+

No se encontraron registros.

+
+ {:else} + + + + + + + + + {#each filteredItems as item} + handleSelect(item)} + > + + + + {/each} + +
CódigoDescripción
{item.customs_code} +
+ + {item.section_name || '-'} +
+
+ {/if} +
+ + +
+ Mostrando {filteredItems.length} registros +
+ +
+
+
diff --git a/frontend/src/routes/dashboard/general_catalogs/doda/+page.svelte b/frontend/src/routes/dashboard/general_catalogs/doda/+page.svelte index 7904aaa5..c808beea 100644 --- a/frontend/src/routes/dashboard/general_catalogs/doda/+page.svelte +++ b/frontend/src/routes/dashboard/general_catalogs/doda/+page.svelte @@ -2,17 +2,30 @@ import { page } from '$app/stores'; import { goto } from '$app/navigation'; import { browser } from '$app/environment'; + import { getDodas, deleteDoda, type Doda } from '$lib/api/dashboard/a76/general_catalogs/doda'; + import * as Card from '$lib/components/ui/card'; + import { toast } from 'svelte-sonner'; + import { + Plus, + RefreshCw, + Trash2, + Pencil, + Search, + RotateCcw, + FileText, + LayoutGrid, + Printer + } from 'lucide-svelte'; + import { companyStore } from '$lib/stores/company.svelte'; import DataTable from '$lib/components/dashboard/general_catalogs/doda/data-table.svelte'; import { createColumns } from '$lib/components/dashboard/general_catalogs/doda/columns'; import CreateEditDialog from '$lib/components/dashboard/general_catalogs/doda/create-edit-dialog.svelte'; import { Button } from '$lib/components/ui/button'; import { Input } from '$lib/components/ui/input'; - import { Plus } from 'lucide-svelte'; - - import { invalidateAll } from '$app/navigation'; + import { Label } from '$lib/components/ui/label'; + import { Separator } from '$lib/components/ui/separator'; import { useShortcuts } from '$lib/hooks/use-shortcuts'; import { obtenerAtajosListaDoda } from '$lib/config/shortcuts/dashboard/general_catalogs/doda/list'; - import type { Doda } from '$lib/api/dashboard/a76/general_catalogs/doda'; let { data } = $props(); let dialogOpen = $state(false); @@ -21,82 +34,227 @@ let searchIntegration = $state($page.url.searchParams.get('integration_number') || ''); let timeout: ReturnType; + // State for infinite scroll + let allItems = $state(data.dodas?.items || []); + let currentPage = $state(data.dodas?.page || 1); + let pageSize = $state(50); + let totalItems = $state(data.dodas?.total || 0); + let loading = $state(false); + let hasMore = $derived(allItems.length < totalItems); + let error = $state(data.error || null); + // Selection let selectedId = $state(null); + const selectedDoda = $derived(selectedId ? allItems.find((i) => i.id === selectedId) : null); - // Funciones de acción - function handleCreateClick() { - goto('/dashboard/general_catalogs/doda/edit'); - } + // Sincronizar con datos del servidor al cargar + $effect(() => { + if (data.dodas) { + allItems = data.dodas.items || []; + currentPage = data.dodas.page || 1; + totalItems = data.dodas.total || 0; + } + }); // Atajos useShortcuts( 'Lista DODA', obtenerAtajosListaDoda({ manejarNuevo: handleCreateClick, - manejarActualizar: handleSuccess + manejarActualizar: reloadData }) ); + async function loadMore() { + if (loading || !hasMore) return; + + loading = true; + try { + const companyId = companyStore.activeCompany?.id; + if (!companyId) return; + + const filters = searchIntegration ? { integration_number: searchIntegration } : {}; + const res = await getDodas(currentPage + 1, pageSize, filters, Number(companyId)); + + if (res.data) { + allItems = [...allItems, ...res.data.items]; + currentPage++; + totalItems = res.data.total; + } + } catch (e) { + console.error('Error loading more DODAs:', e); + } finally { + loading = false; + } + } + + async function reloadData() { + loading = true; + try { + const companyId = companyStore.activeCompany?.id; + if (!companyId) return; + + const filters = searchIntegration ? { integration_number: searchIntegration } : {}; + const res = await getDodas(1, pageSize, filters, Number(companyId)); + + if (res.data) { + allItems = res.data.items; + currentPage = 1; + totalItems = res.data.total; + selectedId = null; + } + } catch (e) { + console.error('Error reloading DODAs:', e); + toast.error('Error al recargar datos'); + } finally { + loading = false; + } + } + function handleSearch() { - if (!browser) return; clearTimeout(timeout); timeout = setTimeout(() => { - const url = new URL($page.url); - if (searchIntegration) url.searchParams.set('integration_number', searchIntegration); - else url.searchParams.delete('integration_number'); - - url.searchParams.set('page', '1'); - goto(url, { keepFocus: true, noScroll: true }); + reloadData(); }, 500); } - function handleSuccess() { - const url = new URL($page.url); - goto(url, { invalidateAll: true }); - selectedId = null; + function handleCreateClick() { + goto('/dashboard/general_catalogs/doda/edit'); + } + + function handleEdit() { + if (selectedId) { + goto(`/dashboard/general_catalogs/doda/edit/${selectedId}`); + } + } + + async function handleDelete() { + if (!selectedId || !companyStore.activeCompany) return; + + if (confirm('¿Estás seguro de eliminar este DODA?')) { + try { + await deleteDoda(selectedId, companyStore.activeCompany.id); + toast.success('DODA eliminado correctamente'); + reloadData(); + } catch (e) { + toast.error('Error al eliminar DODA'); + } + } } function handleRowClick(doda: Doda) { - selectedId = doda.id; + selectedId = selectedId === doda.id ? null : doda.id; } -
+
-

DODA

-

Gestión de Documentos de Operación de Aduana

+

DODA

+

Gestiona tus Documentos de Operación Aduanera (DODA)

- -
-
-
- + + Filtros + Búsqueda rápida por número de integración + + +
+ +
+ + +
+
+
+ + + + +
+
+ Listado de DODAs + + Mostrando {allItems.length} de {totalItems} registros + +
+ +
+
+ + + +
+ + +
+
+
+
+ {#if selectedDoda} + Seleccionado: {selectedDoda.integration_number || 'S/N'} + {:else} + Selecciona un registro para ver acciones + {/if} +
+
+ + + + + + + + + +
+
- -
- -
- - {#if dialogOpen} - - {/if}
+ +
+ + +{#if dialogOpen} + +{/if} diff --git a/frontend/src/routes/dashboard/general_catalogs/doda/edit/[[id]]/+page.svelte b/frontend/src/routes/dashboard/general_catalogs/doda/edit/[[id]]/+page.svelte index 6739e29a..45d9eb20 100644 --- a/frontend/src/routes/dashboard/general_catalogs/doda/edit/[[id]]/+page.svelte +++ b/frontend/src/routes/dashboard/general_catalogs/doda/edit/[[id]]/+page.svelte @@ -8,6 +8,9 @@ import { Switch } from '$lib/components/ui/switch'; import * as Tabs from '$lib/components/ui/tabs'; import * as Card from '$lib/components/ui/card'; + import * as Select from '$lib/components/ui/select'; + import * as RadioGroup from '$lib/components/ui/radio-group'; + import { Separator } from '$lib/components/ui/separator'; import { companyStore } from '$lib/stores/company.svelte'; import { createDoda, @@ -15,19 +18,42 @@ getDoda, type DodaCreate } from '$lib/api/dashboard/a76/general_catalogs/doda'; - import { ArrowLeft, LoaderCircle, Save } from 'lucide-svelte'; + import { + ArrowLeft, + Save, + RefreshCw, + FileText, + LayoutGrid, + Printer, + Trash2, + FolderSearch, + ShieldCheck, + LoaderCircle + } from 'lucide-svelte'; import { useShortcuts } from '$lib/hooks/use-shortcuts'; import { obtenerAtajosFormularioDoda } from '$lib/config/shortcuts/dashboard/general_catalogs/doda/edit'; + import ChildDetailTable from '$lib/components/dashboard/general_catalogs/doda/child-detail-table.svelte'; + + // Modales de Selección + import BrokerSelectorDialog from '$lib/components/dashboard/export/manifest/modals/broker-selector-dialog.svelte'; + import CustomsSectionSelectorDialog from '$lib/components/dashboard/shared/modals/customs-section-selector-dialog.svelte'; + import TransporterSelectorDialog from '$lib/components/dashboard/export/manifest/modals/transporter-selector-dialog.svelte'; // 1. Identificación reactiva let id = $derived($page.params.id); - let isEdit = $derived(!!id); + let isEdit = $derived(!!$page.params.id); let title = $derived(isEdit ? 'Editar DODA' : 'Nuevo DODA'); let loading = $state(false); let error = $state(null); let activeTab = $state('general'); + // Estados de Modales + let showBrokerSelector = $state(false); + let showAduanaSelector = $state(false); + let showSectionSelector = $state(false); + let showTransporterSelector = $state(false); + // Atajos useShortcuts( 'Formulario DODA', @@ -38,7 +64,12 @@ }) ); - function getEmptyForm(): DodaCreate { + function getEmptyForm(): DodaCreate & { + pedimentos_detail?: any[]; + containers?: any[]; + american_pedimentos?: any[]; + uuid_carta_porte?: string; + } { return { integration_number: '', doda_date: undefined, @@ -50,7 +81,7 @@ caat: '', transport_identification: '', fast_id: '', - operation_type: '', + operation_type: 'I', selected: false, user_selected: '', last_user: '', @@ -62,24 +93,35 @@ serial_number: '', electronic_signature: '', transaction_number: '', - status: '', + status: 'PENDIENTE', linq_sat_qr: '', sat_certificate: '', sat_digital_seal: '', xml_doda_sent_path: '', xml_doda_response_path: '', sat_original_chain: '', - customs_clearance: undefined, - unique_badge_number: '' + customs_clearance: 2, // 2 = DODA, 1 = PITA + unique_badge_number: '', + pedimentos_detail: [], + containers: [], + american_pedimentos: [], + uuid_carta_porte: '' }; } - let formData = $state(getEmptyForm()); + let formData = $state(getEmptyForm()); $effect(() => { - if (id) { - loadDoda(Number(id)); - } else { + const currentId = $page.params.id; + const companyId = companyStore.activeCompany?.id; + + console.log('DEBUG: Effect triggered', { currentId, companyId }); + + if (currentId && companyId) { + console.log('DEBUG: Calling loadDoda with', currentId); + loadDoda(Number(currentId)); + } else if (!currentId) { + console.log('DEBUG: No ID, resetting form'); formData = getEmptyForm(); error = null; } @@ -89,7 +131,15 @@ loading = true; try { const companyId = companyStore.activeCompany?.id; + console.log('DEBUG: loadDoda executing', { dodaId, companyId }); + + if (!companyId) { + console.error('DEBUG: No company ID available in loadDoda'); + return; + } + const data = await getDoda(dodaId, companyId); + console.log('DEBUG: getDoda response', data); if (data) { formData = { @@ -103,7 +153,7 @@ caat: data.caat || '', transport_identification: data.transport_identification || '', fast_id: data.fast_id || '', - operation_type: data.operation_type || '', + operation_type: data.operation_type || 'I', selected: data.selected || false, user_selected: data.user_selected || '', last_user: data.last_user || '', @@ -115,15 +165,19 @@ serial_number: data.serial_number || '', electronic_signature: data.electronic_signature || '', transaction_number: data.transaction_number || '', - status: data.status || '', + status: data.status || 'PENDIENTE', linq_sat_qr: data.linq_sat_qr || '', sat_certificate: data.sat_certificate || '', sat_digital_seal: data.sat_digital_seal || '', xml_doda_sent_path: data.xml_doda_sent_path || '', xml_doda_response_path: data.xml_doda_response_path || '', sat_original_chain: data.sat_original_chain || '', - customs_clearance: data.customs_clearance, - unique_badge_number: data.unique_badge_number || '' + customs_clearance: data.customs_clearance || 2, + unique_badge_number: data.unique_badge_number || '', + pedimentos_detail: data.pedimentos_detail || [], + containers: data.containers || [], + american_pedimentos: data.american_pedimentos || [], + uuid_carta_porte: '' // Simulated field }; } } catch (e) { @@ -139,13 +193,11 @@ try { const companyId = companyStore.activeCompany?.id; if (!companyId) throw new Error('Selecciona una compañía'); - if (!formData.integration_number?.trim()) - throw new Error('El número de integración es requerido'); + if (!formData.patent?.trim()) throw new Error('El Agente Aduanal (Patente) es requerido'); const payload: DodaCreate = { ...formData, - integration_number: formData.integration_number.trim(), - + integration_number: formData.integration_number?.trim() || '', doda_date: formData.doda_date || undefined, doda_time: formData.doda_time || undefined, customs_clearance: formData.customs_clearance || undefined @@ -164,260 +216,524 @@ loading = false; } } + + const pedimentosColumns = [ + { header: 'Doda Sysid', key: 'id' }, + { header: 'Línea Pedimento', key: 'pedimento_line' }, + { header: 'Patente Autorización', key: 'authorization_patent' }, + { header: 'Documento', key: 'document' }, + { header: 'Remesa', key: 'shipment' }, + { header: 'Cove', key: 'cove' }, + { header: 'UMC', key: 'umc' }, + { header: 'Importe Efectivo USD', key: 'effective_amount_usd' }, + { header: 'Importe Diferencia USD', key: 'difference_amount_usd' }, + { header: 'DTA NIU', key: 'dta_niu' }, + { header: 'Articulo 7', key: 'article_7', render: (v: any) => (v ? 'Sí' : 'No') } + ]; + + const containersColumns = [ + { header: 'Contenedor', key: 'container_value' }, + { header: 'Percinto', key: 'seals' } + ]; + + const americanPedimentosColumns = [ + { header: 'Tipo', key: 'american_pedimento_type' }, + { header: 'Pedido Americano', key: 'american_pedimento_value' } + ]; + + // Handlers de Selección + function handleBrokerSelect(broker: any) { + formData.responsible = broker.broker_key || ''; + formData.patent = broker.license || ''; + } + + function handleAduanaSelect(section: any) { + formData.dispatch_customs = section.customs_code || ''; + } + + function handleSectionSelect(section: any) { + formData.customs_sections = section.customs_code || ''; + } + + function handleTransporterSelect(transporter: any) { + formData.carrier = transporter.name || ''; + } -
-
- -
-

{title}

+
+ +
+
+
+ +

{title}

+
+

Catálogos Generales / Doda

- {#if error} -
- ⚠️ {error} -
- {/if} + - {#key id} -
{ - e.preventDefault(); - handleSubmit(); - }} - class="space-y-6" - > - - - -
- -
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
- - -
-
-
- - -
-
- - -
-
+ + +
+ +
+
+ +
+ (showBrokerSelector = true)} + readonly + /> + +
+
+
+ +
+ (showAduanaSelector = true)} + readonly + /> + +
+
+
+ +
+ (showSectionSelector = true)} + readonly + /> + +
+
+
+ + (formData.operation_type = v)} + > + + {formData.operation_type === 'E' + ? 'E' + : formData.operation_type === 'I' + ? 'I' + : '...'} + + + I - Importación + E - Exportación + + +
+
- -
-
- - -
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- - -
-
- - -
-
+ {#if error} +
+ ⚠️ + {error} +
+ {/if} - -
-
- - -
-
- - -
-
-
- -