feat: update pedimento dates model, add PATCH method to API, and enhance edit forms

This commit is contained in:
2025-12-05 17:27:19 -06:00
parent 6eae26acf3
commit 0cb89ceeec
9 changed files with 310 additions and 277 deletions

View File

@@ -254,6 +254,12 @@ export const api = {
method: 'PUT',
body: JSON.stringify(body)
}),
patch: <T = any>(endpoint: string, body: any) =>
fetchApi<T>(endpoint, {
method: 'PATCH',
body: JSON.stringify(body)
}),
delete: <T = any>(endpoint: string) => fetchApi<T>(endpoint, { method: 'DELETE' }),

View File

@@ -114,38 +114,6 @@ export const clientsProvidersApi = {
);
},
/**
* Lista solo clientes (client_or_provider = 'client')
* @param companyId - ID de la compañía
* @param skip - Número de registros a saltar
* @param limit - Límite de registros
*/
listClients: (companyId: number, skip = 0, limit = 100) =>
api.get<ClientProviderBasic[]>(
`/v1/a76/clients-providers/clients?company_id=${companyId}&skip=${skip}&limit=${limit}`
),
/**
* Lista solo proveedores (client_or_provider = 'provider')
* @param companyId - ID de la compañía
* @param skip - Número de registros a saltar
* @param limit - Límite de registros
*/
listProviders: (companyId: number, skip = 0, limit = 100) =>
api.get<ClientProviderBasic[]>(
`/v1/a76/clients-providers/providers?company_id=${companyId}&skip=${skip}&limit=${limit}`
),
/**
* Busca clientes/proveedores por RFC
* @param companyId - ID de la compañía
* @param rfc - RFC a buscar
*/
searchByRfc: (companyId: number, rfc: string) =>
api.get<ClientProviderBasic[]>(
`/v1/a76/clients-providers/search/rfc/${rfc}?company_id=${companyId}`
),
/**
* Obtiene un cliente/proveedor por ID
* @param id - ID del cliente/proveedor
@@ -179,7 +147,7 @@ export const clientsProvidersApi = {
* @param data - Datos a actualizar
*/
update: (id: number, companyId: number, data: UpdateClientProviderData) =>
api.put<ClientProvider>(`/v1/a76/clients-providers/${id}?company_id=${companyId}`, data),
api.patch<ClientProvider>(`/v1/a76/clients-providers/${id}?company_id=${companyId}`, data),
/**
* Alterna el estado activo/inactivo de un cliente/proveedor

View File

@@ -62,57 +62,7 @@
<Card.Content>
<div class="space-y-6">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<!-- Fecha de Entrada -->
<div class="space-y-2">
<Label for="entry_date">Fecha de Entrada</Label>
<Input
id="entry_date"
type="date"
bind:value={formData.entry_date}
/>
</div>
<!-- Fecha de Presentación -->
<div class="space-y-2">
<Label for="pedimento_date">Fecha de Presentación</Label>
<Input
id="pedimento_date"
type="date"
bind:value={formData.pedimento_date}
/>
</div>
<!-- Fecha de Pago -->
<div class="space-y-2">
<Label for="payment_date">Fecha de Pago</Label>
<Input
id="payment_date"
type="date"
bind:value={formData.payment_date}
/>
</div>
<!-- Fecha de Pago Rectificación -->
<div class="space-y-2">
<Label for="rectification_payment_date">Fecha de Pago Rectificación</Label>
<Input
id="rectification_payment_date"
type="date"
bind:value={formData.rectification_payment_date}
/>
</div>
<!-- Fecha de Extracción -->
<div class="space-y-2">
<Label for="extraction_date">Fecha de Extracción</Label>
<Input
id="extraction_date"
type="date"
bind:value={formData.extraction_date}
/>
</div>
<!-- Fecha de Presentación (Submission) -->
<!-- Fecha de Envío -->
<div class="space-y-2">
<Label for="submission_date">Fecha de Envío</Label>
<Input
@@ -132,16 +82,6 @@
/>
</div>
<!-- Fecha Original -->
<div class="space-y-2">
<Label for="original_date">Fecha Original</Label>
<Input
id="original_date"
type="date"
bind:value={formData.original_date}
/>
</div>
<!-- Fecha de Inicio -->
<div class="space-y-2">
<Label for="start_date">Fecha de Inicio</Label>

View File

@@ -172,6 +172,7 @@
// Inicializar formData con los valores del pedimento (o vacío si es null)
if (!formData) {
const datesData = pedimento?.pedimento_dates;
formData = {
year: pedimento?.year || currentYear,
customs_office: pedimento?.customs_office || '',
@@ -186,7 +187,14 @@
usd_value: pedimento?.usd_value ?? null,
paid_price: pedimento?.paid_price ?? null,
gross_weight: pedimento?.gross_weight ?? null,
exchange_rate: pedimento?.exchange_rate ?? null
exchange_rate: pedimento?.exchange_rate ?? null,
// Date fields
entry_date: datesData?.entry_date ? datesData.entry_date.substring(0, 10) : '',
pedimento_date: datesData?.pedimento_date ? datesData.pedimento_date.substring(0, 10) : '',
extraction_date: datesData?.extraction_date ? datesData.extraction_date.substring(0, 10) : '',
rectification_payment_date: datesData?.rectification_payment_date ? datesData.rectification_payment_date.substring(0, 10) : '',
original_date: datesData?.original_date ? datesData.original_date.substring(0, 10) : '',
payment_date: datesData?.payment_date ? datesData.payment_date.substring(0, 10) : ''
};
}
@@ -229,35 +237,34 @@
</Card.Description>
</Card.Header>
<Card.Content>
<div class="space-y-6">
<!-- Fila 1: Año (2), Aduana (2), Patente (4), Número de Pedimento (7) -->
<div class="flex items-end gap-2">
<div class="space-y-6">
<div class="grid grid-cols-1 md:grid-cols-[auto_auto_auto_auto_auto_auto_auto_auto_auto_1fr_auto] md:items-end gap-4 md:gap-2">
<!-- Año -->
<div class="space-y-2 w-16">
<div class="space-y-2">
<Label for="year">Año</Label>
<Input
id="year"
bind:value={formData.year}
placeholder="23"
maxlength={2}
class="text-center"
class="text-center md:w-16"
disabled
readonly
/>
</div>
<!-- Separador -->
<div class="pb-2 text-2xl font-semibold text-muted-foreground">-</div>
<div class="hidden md:block pb-2 text-2xl font-semibold text-muted-foreground">-</div>
<!-- Aduana -->
<div class="space-y-2 w-20">
<div class="space-y-2">
<Label for="customs_office">Aduana</Label>
<Select.Root
type="single"
value={formData.customs_office || ''}
onValueChange={(v: string) => formData.customs_office = v ?? ''}
>
<Select.Trigger class="w-full">
<Select.Trigger class="w-full md:w-20">
<span class="truncate">
{formData.customs_office || 'Sel...'}
</span>
@@ -275,17 +282,17 @@
</div>
<!-- Separador -->
<div class="pb-2 text-2xl font-semibold text-muted-foreground">-</div>
<div class="hidden md:block pb-2 text-2xl font-semibold text-muted-foreground">-</div>
<!-- Patente -->
<div class="space-y-2 w-24">
<div class="space-y-2">
<Label for="license">Patente</Label>
<Select.Root
type="single"
value={formData.license || ''}
onValueChange={(v: string) => formData.license = v ?? ''}
>
<Select.Trigger class="w-full">
<Select.Trigger class="w-full md:w-24">
<span class="truncate">
{formData.license || 'Sel...'}
</span>
@@ -303,10 +310,10 @@
</div>
<!-- Separador -->
<div class="pb-2 text-2xl font-semibold text-muted-foreground">-</div>
<div class="hidden md:block pb-2 text-2xl font-semibold text-muted-foreground">-</div>
<!-- Número de Pedimento -->
<div class="space-y-2 flex-1">
<div class="space-y-2">
<Label for="pedimento_number">Número de Pedimento</Label>
<Input
id="pedimento_number"
@@ -316,6 +323,89 @@
class="text-left"
/>
</div>
<!-- Tipo de Cambio -->
<div class="space-y-2">
<Label for="exchange_rate">Tipo de Cambio</Label>
<Input
id="exchange_rate"
type="number"
step="0.0001"
bind:value={formData.exchange_rate}
placeholder="Ej: 17.5000"
/>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 md:gap-2">
<!-- Clave del Pedimento -->
<div class="space-y-2">
<Label for="pedimento_code">Clave</Label>
<Select.Root
type="single"
value={formData.pedimento_code || ''}
onValueChange={(v: string) => formData.pedimento_code = v ?? ''}
>
<Select.Trigger class="w-full">
<span class="truncate">
{pedimentoCodes.find(o => o.code === formData.pedimento_code)?.code || 'Sel...'}
</span>
</Select.Trigger>
<Select.Content class="max-w-[200px]">
{#each pedimentoCodes as code}
<Select.Item value={code.code}>
<span class="truncate overflow-hidden text-ellipsis whitespace-nowrap" title={`${code.code} - ${code.description}`}>
{code.code} - {code.description}
</span>
</Select.Item>
{/each}
</Select.Content>
</Select.Root>
</div>
<!-- Régimen -->
<div class="space-y-2">
<Label for="regime">Régimen</Label>
<Select.Root
type="single"
value={formData.regime || ''}
onValueChange={(v: string) => formData.regime = v ?? ''}
>
<Select.Trigger class="w-full">
<span class="truncate">
{formData.regime || 'Sel...'}
</span>
</Select.Trigger>
<Select.Content class="max-w-[200px]">
{#each filteredRegimens() as regimen}
<Select.Item value={regimen.code}>
<span class="truncate overflow-hidden text-ellipsis whitespace-nowrap" title={regimen.code}>
{regimen.code}
</span>
</Select.Item>
{/each}
</Select.Content>
</Select.Root>
</div>
<!-- Tipo de Operación -->
<div class="space-y-2">
<Label for="operation_type">Tipo de Operación</Label>
<Select.Root
type="single"
value={String(formData.operation_type ?? '')}
onValueChange={(v: string) => formData.operation_type = v ? Number(v) : null}
>
<Select.Trigger class="w-full">
{operationOptions.find(o => o.value === formData.operation_type)?.label || 'Seleccionar...'}
</Select.Trigger>
<Select.Content>
{#each filteredOperationTypes() as option}
<Select.Item value={String(option.value)} label={option.label} />
{/each}
</Select.Content>
</Select.Root>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
@@ -356,80 +446,8 @@
</div>
</div>
<!-- Fila: Clave (2), Régimen (3), Tipo de Operación (11) -->
<div class="flex items-end gap-2">
<!-- Clave del Pedimento -->
<div class="space-y-2 w-20">
<Label for="pedimento_code">Clave</Label>
<Select.Root
type="single"
value={formData.pedimento_code || ''}
onValueChange={(v: string) => formData.pedimento_code = v ?? ''}
>
<Select.Trigger class="w-full">
<span class="truncate">
{pedimentoCodes.find(o => o.code === formData.pedimento_code)?.code || 'Sel...'}
</span>
</Select.Trigger>
<Select.Content class="max-w-[200px]">
{#each pedimentoCodes as code}
<Select.Item value={code.code}>
<span class="truncate overflow-hidden text-ellipsis whitespace-nowrap" title={`${code.code} - ${code.description}`}>
{code.code} - {code.description}
</span>
</Select.Item>
{/each}
</Select.Content>
</Select.Root>
</div>
<!-- Régimen -->
<div class="space-y-2 w-24">
<Label for="regime">Régimen</Label>
<Select.Root
type="single"
value={formData.regime || ''}
onValueChange={(v: string) => formData.regime = v ?? ''}
>
<Select.Trigger class="w-full">
<span class="truncate">
{formData.regime || 'Sel...'}
</span>
</Select.Trigger>
<Select.Content class="max-w-[200px]">
{#each filteredRegimens() as regimen}
<Select.Item value={regimen.code}>
<span class="truncate overflow-hidden text-ellipsis whitespace-nowrap" title={regimen.code}>
{regimen.code}
</span>
</Select.Item>
{/each}
</Select.Content>
</Select.Root>
</div>
<!-- Tipo de Operación -->
<div class="space-y-2 flex-1">
<Label for="operation_type">Tipo de Operación</Label>
<Select.Root
type="single"
value={String(formData.operation_type ?? '')}
onValueChange={(v: string) => formData.operation_type = v ? Number(v) : null}
>
<Select.Trigger class="w-full">
{operationOptions.find(o => o.value === formData.operation_type)?.label || 'Seleccionar...'}
</Select.Trigger>
<Select.Content>
{#each filteredOperationTypes() as option}
<Select.Item value={String(option.value)} label={option.label} />
{/each}
</Select.Content>
</Select.Root>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- Estado -->
<div class="space-y-2">
@@ -484,17 +502,68 @@
bind:value={formData.gross_weight}
placeholder="Ej: 100.00"
/>
</div>
</div>
<!-- Fechas -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<!-- Fecha de Entrada -->
<div class="space-y-2">
<Label for="entry_date">Fecha de Entrada</Label>
<Input
id="entry_date"
type="date"
bind:value={formData.entry_date}
/>
</div>
<!-- Tipo de Cambio -->
<!-- Fecha de Presentación -->
<div class="space-y-2">
<Label for="exchange_rate">Tipo de Cambio</Label>
<Label for="pedimento_date">Fecha de Presentación</Label>
<Input
id="exchange_rate"
type="number"
step="0.0001"
bind:value={formData.exchange_rate}
placeholder="Ej: 17.5000"
id="pedimento_date"
type="date"
bind:value={formData.pedimento_date}
/>
</div>
<!-- Fecha de Extracción -->
<div class="space-y-2">
<Label for="extraction_date">Fecha de Extracción</Label>
<Input
id="extraction_date"
type="date"
bind:value={formData.extraction_date}
/>
</div>
<!-- Fecha de Pago Rectificación -->
<div class="space-y-2">
<Label for="rectification_payment_date">Fecha de Pago R1</Label>
<Input
id="rectification_payment_date"
type="date"
bind:value={formData.rectification_payment_date}
/>
</div>
<!-- Fecha Original -->
<div class="space-y-2">
<Label for="original_date">Fecha de Pago Original</Label>
<Input
id="original_date"
type="date"
bind:value={formData.original_date}
/>
</div>
<!-- Fecha de Pago -->
<div class="space-y-2">
<Label for="payment_date">Fecha de Pago</Label>
<Input
id="payment_date"
type="date"
bind:value={formData.payment_date}
/>
</div>
</div>

View File

@@ -1,7 +1,5 @@
<script lang="ts">
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
import * as Card from '$lib/components/ui/card';
import { goto } from '$app/navigation';
import * as Tabs from '$lib/components/ui/tabs';
import * as Alert from '$lib/components/ui/alert';
import { Button } from '$lib/components/ui/button';
@@ -18,8 +16,8 @@
ShieldCheck,
LoaderCircle,
Save
} from 'lucide-svelte';
import type { PageData } from './$types';
} from 'lucide-svelte';
import { useSidebar } from '$lib/components/ui/sidebar/context.svelte';
// Importar los componentes de cada pestaña (ahora sin botones de guardar propios)
import GeneralTabForm from '$lib/components/dashboard/pedimentos/edit/general-tab-form.svelte';
@@ -35,6 +33,9 @@
import type { CustomsBroker } from '$lib/api/dashboard/a76/customs-brokers';
import type { ClientProvider } from '$lib/api/dashboard/a76/clients-providers';
import type { CodePedimentoRegimen } from '$lib/api/dashboard/refrence_data/code_pedimento_regimens';
// Get sidebar context
const sidebar = useSidebar();
interface ExtendedPageData {
pedimentoId?: number | null;
@@ -146,7 +147,7 @@
// Solo agregar sub-recursos en modo UPDATE (no en CREATE)
// Y solo si tienen valores reales (no enviar objetos vacíos/null)
if (!data.isCreate) {
// Dates - solo enviar si hay al menos un campo con valor
if (datesFormData) {
const hasDateValue = datesFormData.entry_date || datesFormData.pedimento_date ||
@@ -227,8 +228,7 @@
responsible_id: validationFormData.responsible_id || null
};
}
}
}
}
// Eliminar campos undefined para no enviarlos
Object.keys(payload).forEach(key => {
@@ -381,32 +381,37 @@
</div>
<!-- Footer fijo en la parte inferior - Fuera del contenedor principal -->
<div class="fixed bottom-0 inset-x-0 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80 border-t shadow-lg z-50 group-has-[[data-sidebar]]/sidebar-wrapper:left-[var(--sidebar-width)]">
<div
class="fixed bottom-0 left-0 right-0 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80 border-t shadow-lg z-[5] transition-[left] duration-200 ease-linear"
style:left={sidebar.isMobile ? '0' : (sidebar.open ? 'var(--sidebar-width)' : '0')}
>
<div class="px-4 py-4 space-y-4 max-w-[1400px] mx-auto">
<!-- Tabs Navigation -->
<Tabs.Root bind:value={activeTab}>
<Tabs.List class="grid w-full grid-cols-5">
<Tabs.Trigger value="general" disabled={false}>
<FileText size={16} class="mr-2" />
General
</Tabs.Trigger>
<Tabs.Trigger value="dates" disabled={false}>
<Calendar size={16} class="mr-2" />
Fechas
</Tabs.Trigger>
<Tabs.Trigger value="payments" disabled={false}>
<CreditCard size={16} class="mr-2" />
Pagos
</Tabs.Trigger>
<Tabs.Trigger value="transport" disabled={false}>
<Truck size={16} class="mr-2" />
Transporte
</Tabs.Trigger>
<Tabs.Trigger value="validation" disabled={false}>
<ShieldCheck size={16} class="mr-2" />
Validación
</Tabs.Trigger>
</Tabs.List>
<div class="w-full overflow-x-auto pb-2">
<Tabs.List class="inline-flex md:grid md:w-full md:grid-cols-5">
<Tabs.Trigger value="general" disabled={false} class="whitespace-nowrap">
<FileText size={16} class="mr-2" />
General
</Tabs.Trigger>
<Tabs.Trigger value="dates" disabled={false} class="whitespace-nowrap">
<Calendar size={16} class="mr-2" />
Fechas
</Tabs.Trigger>
<Tabs.Trigger value="payments" disabled={false} class="whitespace-nowrap">
<CreditCard size={16} class="mr-2" />
Pagos
</Tabs.Trigger>
<Tabs.Trigger value="transport" disabled={false} class="whitespace-nowrap">
<Truck size={16} class="mr-2" />
Transporte
</Tabs.Trigger>
<Tabs.Trigger value="validation" disabled={false} class="whitespace-nowrap">
<ShieldCheck size={16} class="mr-2" />
Validación
</Tabs.Trigger>
</Tabs.List>
</div>
</Tabs.Root>
<!-- Botones de acción -->