feat: add pedimento fields and selection functionality to invoice forms

This commit is contained in:
AlexeerCT
2025-12-24 14:42:23 -06:00
parent 18325a2925
commit 6c1b555f08
4 changed files with 138 additions and 9 deletions

View File

@@ -1,7 +1,8 @@
<script lang="ts">
import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label';
import * as Select from '$lib/components/ui/select';
import * as Select from '$lib/components/ui/select';
import * as RadioGroup from '$lib/components/ui/radio-group';
import type { Invoice } from '$lib/api/dashboard/a76/invoices';
import type { InvoiceType } from '$lib/api/dashboard/refrence_data/invoice_types';
import type { CustomsBroker } from '$lib/api/dashboard/a76/customs-brokers';
@@ -51,6 +52,12 @@
// Extra fields
operation_type: operationType,
// RANGO DE FECHAS fields
fecha_pedimento_del: '',
fecha_pedimento_al: '',
clave_pedimento: '',
regimen_pedimento: '',
// LEFT fields
provider_header: invoice.compliance_mx?.provider_header || '',
provider_id: invoice.compliance_mx?.provider_id || null,
@@ -63,6 +70,7 @@
// RIGHT fields
currency_type: invoice.financials?.currency_type || '',
currency_mode: 'extranjera', // extranjera, nacional, captura
weight_type: '',
iva_factor: invoice.financials?.iva_factor || null,
carrier_id: invoice.logistics?.[0]?.carrier_id || null,
@@ -72,6 +80,7 @@
transport_num: invoice.logistics?.[0]?.vehicle_num || '',
aduana: invoice.compliance_mx?.aduana || '',
invoice_type: invoice.invoice_type || '',
clave_regimen_aduanero: '',
};
} else {
// Creando una nueva factura
@@ -87,6 +96,12 @@
// Extra fields
operation_type: defaultOperationType ?? null,
// RANGO DE FECHAS fields
fecha_pedimento_del: '',
fecha_pedimento_al: '',
clave_pedimento: '',
regimen_pedimento: '',
// LEFT fields
provider_header: '',
provider_id: null,
@@ -99,6 +114,7 @@
// RIGHT fields
currency_type: '',
currency_mode: 'extranjera', // extranjera, nacional, captura
weight_type: '',
iva_factor: null,
carrier_id: null,
@@ -108,8 +124,14 @@
transport_num: '',
aduana: '',
invoice_type: defaultInvoiceType ?? '',
clave_regimen_aduanero: '',
};
}
} else {
// Si formData ya existe, asegurar que tiene currency_mode
if (formData.currency_mode === undefined) {
formData.currency_mode = 'extranjera';
}
}
// Opciones de tipo de peso
@@ -154,14 +176,33 @@
const allClientsProviders = [...clients, ...providers];
</script>
<!-- Top fields moved to shared component -->
<!-- Layout de 2 columnas compacto -->
<div class="grid grid-cols-2 gap-4">
<!-- Columna Izquierda: Clientes - Proveedores - Agente Aduanal -->
<div class="border rounded-md p-3 space-y-3">
<div class="space-y-2">
<h4 class="text-xs font-semibold text-muted-foreground uppercase">Datos del pedimento</h4>
<div class="grid grid-cols-4 gap-3 text-xs">
<div>
<span class="text-muted-foreground">Fecha del:</span>
<p class="font-medium">{formData.fecha_pedimento_del || '-'}</p>
</div>
<div>
<span class="text-muted-foreground">Fecha al:</span>
<p class="font-medium">{formData.fecha_pedimento_al || '-'}</p>
</div>
<div>
<span class="text-muted-foreground">Clave:</span>
<p class="font-medium">{formData.clave_pedimento || '-'}</p>
</div>
<div>
<span class="text-muted-foreground">Régimen:</span>
<p class="font-medium">{formData.regimen_pedimento || '-'}</p>
</div>
</div>
</div>
<h4 class="text-xs font-semibold text-muted-foreground uppercase">Clientes - Proveedores - Agente Aduanal</h4>
<div class="space-y-1.5">
<Label for="provider_id" class="text-xs">Proveedor:</Label>
<Select.Root
@@ -367,6 +408,24 @@
<div class="border rounded-md p-3 space-y-2">
<h4 class="text-xs font-semibold text-muted-foreground uppercase">Tipo de Moneda - Pesos Netos y Brutos</h4>
<!-- Radio buttons para tipo de moneda -->
<div class="space-y-1.5">
<RadioGroup.Root bind:value={formData.currency_mode} class="flex gap-4">
<div class="flex items-center space-x-2">
<RadioGroup.Item value="extranjera" id="currency-extranjera" class="h-4 w-4" />
<Label for="currency-extranjera" class="text-xs font-normal cursor-pointer">Extranjera (Dlls)</Label>
</div>
<div class="flex items-center space-x-2">
<RadioGroup.Item value="nacional" id="currency-nacional" class="h-4 w-4" />
<Label for="currency-nacional" class="text-xs font-normal cursor-pointer">Nacional (Pesos)</Label>
</div>
<div class="flex items-center space-x-2">
<RadioGroup.Item value="captura" id="currency-captura" class="h-4 w-4" />
<Label for="currency-captura" class="text-xs font-normal cursor-pointer">De Captura</Label>
</div>
</RadioGroup.Root>
</div>
<div class="grid grid-cols-3 gap-2">
<div class="space-y-1.5">
<Label for="currency_type" class="text-xs">Moneda:</Label>
@@ -505,6 +564,11 @@
<Label for="aduana" class="text-xs">Aduana y Sección de Despacho:</Label>
<Input id="aduana" bind:value={formData.aduana} class="h-7 text-xs" />
</div>
<div class="space-y-1.5">
<Label for="clave_regimen_aduanero" class="text-xs">Clave de Régimen Aduanero:</Label>
<Input id="clave_regimen_aduanero" bind:value={formData.clave_regimen_aduanero} class="h-7 text-xs" />
</div>
</div>
</div>
</div>

View File

@@ -5,21 +5,41 @@
import { Switch } from '$lib/components/ui/switch';
import type { Invoice } from '$lib/api/dashboard/a76/invoices';
import type { InvoiceType } from '$lib/api/dashboard/refrence_data/invoice_types';
import type { Pedimento } from '$lib/api/dashboard/a76/pedimentos';
let {
invoice,
formData = $bindable(),
invoiceTypes = [],
pedimentos = [],
defaultOperationType = undefined,
defaultInvoiceType = undefined,
}: {
invoice: Invoice | null;
formData?: any;
invoiceTypes?: InvoiceType[];
pedimentos?: Pedimento[];
defaultOperationType?: number | null;
defaultInvoiceType?: string | null;
} = $props();
function handlePedimentoChange(pedimentoId: string) {
if (!pedimentoId) return;
const selectedPedimento = pedimentos.find(p => p.id === parseInt(pedimentoId));
if (!selectedPedimento) return;
// Actualizar los campos del pedimento en formData
formData.fecha_pedimento_del = selectedPedimento.pedimento_dates?.start_date || '';
formData.fecha_pedimento_al = selectedPedimento.pedimento_dates?.end_date || '';
formData.clave_pedimento = selectedPedimento.pedimento_code || '';
formData.regimen_pedimento = selectedPedimento.regime || '';
// Construir el número de pedimento completo
const pedimentoNumber = `${selectedPedimento.year || ''}-${selectedPedimento.customs_office || ''}-${selectedPedimento.license || ''}-${selectedPedimento.pedimento_number || ''}`.replace(/^-+|-+$/g, '');
formData.pedimento = pedimentoNumber;
}
if (!formData) {
let operationType: number | null = null;
if (invoice?.operation_type) {
@@ -30,6 +50,7 @@
formData = {
is_pedimento_pending: false,
pedimento_id: null,
pedimento: invoice?.compliance_mx?.pedimento || '',
remesa: invoice?.compliance_mx?.remesa || '',
invoice_number: invoice?.invoice_number || '',
@@ -37,6 +58,11 @@
emission_date: '',
operation_type: operationType,
invoice_type: invoice?.invoice_type || (defaultInvoiceType ?? ''),
// Campos del pedimento (se llenarán al seleccionar un pedimento)
fecha_pedimento_del: '',
fecha_pedimento_al: '',
clave_pedimento: '',
regimen_pedimento: '',
};
}
</script>
@@ -103,7 +129,29 @@
</div>
<div class="col-span-2 space-y-1">
<Label for="pedimento" class="text-xs">Pedimento</Label>
<Input id="pedimento" bind:value={formData.pedimento} class="h-8 text-sm" />
<Select.Root
type="single"
value={formData.pedimento_id ? String(formData.pedimento_id) : ''}
onValueChange={(v) => {
formData.pedimento_id = v ? parseInt(v) : null;
if (v) {
handlePedimentoChange(v);
}
}}
>
<Select.Trigger id="pedimento" class="h-8 text-sm">
<span class="truncate">
{formData.pedimento || 'Selecciona pedimento...'}
</span>
</Select.Trigger>
<Select.Content class="max-h-[300px]">
{#each pedimentos as pedimento}
<Select.Item value={String(pedimento.id)}>
{pedimento.year}-{pedimento.customs_office}-{pedimento.license}-{pedimento.pedimento_number}
</Select.Item>
{/each}
</Select.Content>
</Select.Root>
</div>
<div class="col-span-1 space-y-1">

View File

@@ -95,6 +95,13 @@ export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
fetch
);
const pedimentosPromise = authenticatedFetch(
`v1/a76/pedimentos?company_id=${companyId}&page=1&page_size=100`,
{},
cookies,
fetch
);
// Si el ID es "new", es una creación
if (params.id === 'new') {
const [
@@ -106,7 +113,8 @@ export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
transportTypesResponse,
sealsResponse,
incotermsResponse,
enclosureResponse
enclosureResponse,
pedimentosResponse
] = await Promise.all([
invoiceTypesPromise,
customsBrokersPromise,
@@ -116,7 +124,8 @@ export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
transportTypesPromise,
sealsPromise,
incotermsPromise,
enclosurePromise
enclosurePromise,
pedimentosPromise
]);
const invoiceTypes = invoiceTypesResponse.ok ? await invoiceTypesResponse.json() : { items: [] };
@@ -128,6 +137,7 @@ export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
const seals = sealsResponse.ok ? await sealsResponse.json() : { items: [] };
const incoterms = incotermsResponse.ok ? await incotermsResponse.json() : { items: [] };
const enclosure = enclosureResponse.ok ? await enclosureResponse.json() : { items: [] };
const pedimentos = pedimentosResponse.ok ? await pedimentosResponse.json() : { items: [] };
return {
invoice: null,
@@ -142,6 +152,7 @@ export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
seals: seals.items || [],
incoterms: incoterms.items || [],
enclosure: enclosure.items || [],
pedimentos: pedimentos.items || [],
// Filtros desde query parameters para preselección
filters: {
operation_type: parsedOperationType,
@@ -180,7 +191,8 @@ export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
transportTypesResponse,
sealsResponse,
incotermsResponse,
enclosureResponse
enclosureResponse,
pedimentosResponse
] = await Promise.all([
invoiceTypesPromise,
customsBrokersPromise,
@@ -190,7 +202,8 @@ export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
transportTypesPromise,
sealsPromise,
incotermsPromise,
enclosurePromise
enclosurePromise,
pedimentosPromise
]);
const invoiceTypes = invoiceTypesResponse.ok ? await invoiceTypesResponse.json() : { items: [] };
@@ -202,6 +215,7 @@ export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
const seals = sealsResponse.ok ? await sealsResponse.json() : { items: [] };
const incoterms = incotermsResponse.ok ? await incotermsResponse.json() : { items: [] };
const enclosure = enclosureResponse.ok ? await enclosureResponse.json() : { items: [] };
const pedimentos = pedimentosResponse.ok ? await pedimentosResponse.json() : { items: [] };
return {
invoice,
@@ -216,6 +230,7 @@ export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
seals: seals.items || [],
incoterms: incoterms.items || [],
enclosure: enclosure.items || [],
pedimentos: pedimentos.items || [],
// Filtros desde query parameters para preselección
filters: {
operation_type: parsedOperationType,

View File

@@ -57,6 +57,7 @@
operation_type?: number | null;
invoice_type?: string | null;
};
pedimentos?: any[];
}
let { data }: { data: ExtendedPageData } = $props();
@@ -573,6 +574,7 @@
invoice={data.invoice}
bind:formData={generalFormData}
invoiceTypes={data.invoiceTypes || []}
pedimentos={data.pedimentos || []}
defaultOperationType={data.filters?.operation_type ?? undefined}
defaultInvoiceType={data.filters?.invoice_type ?? undefined}
/>