Cambio parcial en el formulario de invoices
This commit is contained in:
@@ -103,6 +103,7 @@ export interface InvoiceFinancials {
|
||||
insurance_value?: number | null;
|
||||
packaging?: number | null;
|
||||
other_increments?: number | null;
|
||||
other_deductibles?: number | null;
|
||||
total_increments_mn?: number | null;
|
||||
total_increments_me?: number | null;
|
||||
iva_mn?: number | null;
|
||||
@@ -158,6 +159,8 @@ export interface InvoiceLogistics {
|
||||
payment_date?: string | null;
|
||||
payment_receipt_num?: string | null;
|
||||
is_ctm_process?: string | null;
|
||||
is_subdivision?: boolean | null;
|
||||
acts_as_cd?: boolean | null;
|
||||
}
|
||||
|
||||
export interface InvoiceSalesDetails {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,11 +15,13 @@
|
||||
let {
|
||||
invoice,
|
||||
formData = $bindable(),
|
||||
exists = $bindable()
|
||||
exists = $bindable(),
|
||||
operationType = undefined
|
||||
}: {
|
||||
invoice: Invoice | null;
|
||||
formData?: any;
|
||||
exists?: boolean;
|
||||
operationType?: number;
|
||||
} = $props();
|
||||
|
||||
let imported = 0;
|
||||
@@ -49,6 +51,7 @@
|
||||
let isLoadingMore = $state(false);
|
||||
let isLoadingItems = $state(false);
|
||||
let isSaving = $state(false);
|
||||
let focusedLine = $state<any>(null);
|
||||
|
||||
// Sheet states
|
||||
let showItemSheet = $state(false);
|
||||
@@ -109,6 +112,10 @@
|
||||
isLoadingMore = false;
|
||||
}
|
||||
|
||||
function handleRowClick(line: any) {
|
||||
focusedLine = line;
|
||||
}
|
||||
|
||||
function handleScroll(e: Event) {
|
||||
const target = e.target as HTMLDivElement;
|
||||
const threshold = 100;
|
||||
@@ -698,11 +705,11 @@
|
||||
</script>
|
||||
|
||||
<div class="grid grid-cols-4 grid-rows-1 gap-3">
|
||||
<div class="border rounded-md p-3 space-y-3 col-span-3">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<div class="col-span-3 space-y-3 rounded-md border p-3">
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<h3 class="text-sm font-semibold">Items de la Factura</h3>
|
||||
<Button size="sm" onclick={handleAdd}>
|
||||
<Plus class="w-4 h-4 mr-1" />
|
||||
<Plus class="mr-1 h-4 w-4" />
|
||||
Agregar Item
|
||||
</Button>
|
||||
</div>
|
||||
@@ -710,49 +717,89 @@
|
||||
<div
|
||||
bind:this={tableContainer}
|
||||
onscroll={handleScroll}
|
||||
class="max-h-[500px] overflow-auto border rounded-md"
|
||||
class="max-h-[500px] overflow-auto rounded-md border"
|
||||
>
|
||||
<Table.Root>
|
||||
<Table.Header class="bg-background">
|
||||
<Table.Row>
|
||||
<Table.Head>Línea</Table.Head>
|
||||
<Table.Head>P/S</Table.Head>
|
||||
<Table.Head>Clase</Table.Head>
|
||||
<Table.Head>Descripcion Clase</Table.Head>
|
||||
<Table.Head>Cant. Importada</Table.Head>
|
||||
<Table.Head>U.M.</Table.Head>
|
||||
<Table.Head>Preferencia</Table.Head>
|
||||
<Table.Head>Contiene Subpartida</Table.Head>
|
||||
<Table.Head>Partida Principal</Table.Head>
|
||||
<Table.Head class="text-right w-[120px]">Acciones</Table.Head>
|
||||
{#if operationType === 1}
|
||||
<Table.Head>Línea</Table.Head>
|
||||
<Table.Head>Factura Impo</Table.Head>
|
||||
<Table.Head>Lin Impo</Table.Head>
|
||||
<Table.Head>P/S</Table.Head>
|
||||
<Table.Head>Cant. Exportada</Table.Head>
|
||||
<Table.Head>Clase</Table.Head>
|
||||
<Table.Head>Num Parte</Table.Head>
|
||||
<Table.Head>Descripción Esp.</Table.Head>
|
||||
<Table.Head>Subpartidas</Table.Head>
|
||||
<Table.Head>Partida Princ.</Table.Head>
|
||||
{:else}
|
||||
<Table.Head>Línea</Table.Head>
|
||||
<Table.Head>P/S</Table.Head>
|
||||
<Table.Head>Clase</Table.Head>
|
||||
<Table.Head>Descripcion Clase</Table.Head>
|
||||
<Table.Head>Cant. Importada</Table.Head>
|
||||
<Table.Head>U.M.</Table.Head>
|
||||
<Table.Head>Preferencia</Table.Head>
|
||||
<Table.Head>Contiene Subpartida</Table.Head>
|
||||
<Table.Head>Partida Principal</Table.Head>
|
||||
{/if}
|
||||
<Table.Head class="w-[120px] text-right">Acciones</Table.Head>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{#if displayedItems.length === 0}
|
||||
<Table.Row>
|
||||
<Table.Cell colspan={7} class="text-center text-muted-foreground py-8">
|
||||
<Table.Cell
|
||||
colspan={operationType === 1 ? 11 : 10}
|
||||
class="py-8 text-center text-muted-foreground"
|
||||
>
|
||||
No hay items disponibles
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{:else}
|
||||
{#each displayedItems as item (item.id)}
|
||||
<Table.Row>
|
||||
<Table.Cell>{item.line_number}</Table.Cell>
|
||||
<Table.Cell>{item.is_subitem ? 'S' : 'P'}</Table.Cell>
|
||||
<Table.Cell>{item.class_code || '-'}</Table.Cell>
|
||||
<Table.Cell>{item.class_description || '-'}</Table.Cell>
|
||||
<Table.Cell>{item.quantity?.quantity || '0'}</Table.Cell>
|
||||
<Table.Cell>{item.unit_of_measure_code || '-'}</Table.Cell>
|
||||
<Table.Cell>{item.reference_number || '-'}</Table.Cell>
|
||||
<Table.Cell>{item.fa_data?.contains_subitems ? 'Sí' : 'No'}</Table.Cell>
|
||||
<Table.Cell>{item.warehouse || '-'}</Table.Cell>
|
||||
<Table.Row
|
||||
onclick={() => handleRowClick(item)}
|
||||
class="cursor-pointer transition-colors hover:bg-muted/50 {focusedLine?.id ===
|
||||
item.id
|
||||
? 'bg-muted ring-1 ring-primary/20 ring-inset'
|
||||
: ''}"
|
||||
>
|
||||
{#if operationType === 1}
|
||||
<Table.Cell>{item.line_number}</Table.Cell>
|
||||
<Table.Cell>{item.fa_data?.search_invoice || '-'}</Table.Cell>
|
||||
<Table.Cell>{item.fa_data?.search_line || '-'}</Table.Cell>
|
||||
<Table.Cell>{item.is_subitem ? 'S' : 'P'}</Table.Cell>
|
||||
<Table.Cell>{item.quantity?.quantity || '0'}</Table.Cell>
|
||||
<Table.Cell>{item.class_code || '-'}</Table.Cell>
|
||||
<Table.Cell>{item.part_number || '-'}</Table.Cell>
|
||||
<Table.Cell
|
||||
class="max-w-[200px] truncate"
|
||||
title={item.description?.description_spanish}
|
||||
>
|
||||
{item.description?.description_spanish || '-'}
|
||||
</Table.Cell>
|
||||
<Table.Cell>{item.fa_data?.contains_subitems ? 'Sí' : 'No'}</Table.Cell>
|
||||
<Table.Cell>{item.warehouse || '-'}</Table.Cell>
|
||||
{:else}
|
||||
<Table.Cell>{item.line_number}</Table.Cell>
|
||||
<Table.Cell>{item.is_subitem ? 'S' : 'P'}</Table.Cell>
|
||||
<Table.Cell>{item.class_code || '-'}</Table.Cell>
|
||||
<Table.Cell>{item.class_description || '-'}</Table.Cell>
|
||||
<Table.Cell>{item.quantity?.quantity || '0'}</Table.Cell>
|
||||
<Table.Cell>{item.unit_of_measure_code || '-'}</Table.Cell>
|
||||
<Table.Cell>{item.reference_number || '-'}</Table.Cell>
|
||||
<Table.Cell>{item.fa_data?.contains_subitems ? 'Sí' : 'No'}</Table.Cell>
|
||||
<Table.Cell>{item.warehouse || '-'}</Table.Cell>
|
||||
{/if}
|
||||
<Table.Cell class="text-right">
|
||||
<div class="flex justify-end gap-2">
|
||||
<Button size="icon" variant="ghost" onclick={() => handleEdit(item)}>
|
||||
<Pencil class="w-4 h-4" />
|
||||
<Pencil class="h-4 w-4" />
|
||||
</Button>
|
||||
<Button size="icon" variant="ghost" onclick={() => handleDelete(item)}>
|
||||
<Trash2 class="w-4 h-4 text-destructive" />
|
||||
<Trash2 class="h-4 w-4 text-destructive" />
|
||||
</Button>
|
||||
</div>
|
||||
</Table.Cell>
|
||||
@@ -760,7 +807,7 @@
|
||||
{/each}
|
||||
{#if isLoadingMore}
|
||||
<Table.Row>
|
||||
<Table.Cell colspan={7} class="text-center py-4">
|
||||
<Table.Cell colspan={operationType === 1 ? 11 : 10} class="py-4 text-center">
|
||||
<span class="text-sm text-muted-foreground">Cargando más items...</span>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
@@ -771,43 +818,66 @@
|
||||
</div>
|
||||
|
||||
{#if flattenedLines.length > 0}
|
||||
<div class="text-xs text-muted-foreground text-right">
|
||||
<div class="text-right text-xs text-muted-foreground">
|
||||
Mostrando {displayedItems.length} de {flattenedLines.length} líneas
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="border rounded-md p-3 space-y-3 col-start-4">
|
||||
<div>
|
||||
<h4 class="text-xs font-semibold text-muted-foreground uppercase">Cantidades:</h4>
|
||||
<div>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
Partidas: <span class="text-blue-400">{items.length || 0}</span>
|
||||
</div>
|
||||
<div>
|
||||
Bultos: <span class="text-blue-400">0</span>
|
||||
</div>
|
||||
<div class="col-start-4 space-y-4">
|
||||
{#if operationType === 1}
|
||||
<!-- Top Box: Descripción en español -->
|
||||
<div class="flex h-[200px] flex-col rounded-md border p-3">
|
||||
<h4 class="mb-2 text-xs font-semibold text-muted-foreground uppercase">
|
||||
Descripción en español:
|
||||
</h4>
|
||||
<div class="flex-1 overflow-auto rounded border bg-muted/30 p-2">
|
||||
{#if focusedLine}
|
||||
<p class="text-xs leading-relaxed font-medium">
|
||||
{focusedLine.description?.description_spanish || 'Sin descripción disponible.'}
|
||||
</p>
|
||||
{:else}
|
||||
<p class="flex h-full items-center justify-center text-xs text-muted-foreground italic">
|
||||
Selecciona una fila para ver la descripción.
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
Importada:<span class="text-blue-400">{imported || 0}</span> <br />
|
||||
Peso neto: <span class="text-blue-400">{net_weight || 0}</span><br />
|
||||
Peso bruto: <span class="text-blue-400">{gross_weight || 0}</span> <br />
|
||||
{/if}
|
||||
|
||||
<!-- Bottom Box (or only box in Import): Quantities and Values -->
|
||||
<div class="space-y-3 rounded-md border p-3">
|
||||
<div>
|
||||
<h4 class="text-xs font-semibold text-muted-foreground uppercase">Cantidades:</h4>
|
||||
<div>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
Partidas: <span class="text-blue-400">{items.length || 0}</span>
|
||||
</div>
|
||||
<div>
|
||||
Bultos: <span class="text-blue-400">0</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Importada:<span class="text-blue-400">{imported || 0}</span> <br />
|
||||
Peso neto: <span class="text-blue-400">{net_weight || 0}</span><br />
|
||||
Peso bruto: <span class="text-blue-400">{gross_weight || 0}</span> <br />
|
||||
</div>
|
||||
|
||||
<h4 class="col-span-2 text-xs font-semibold text-muted-foreground uppercase">
|
||||
Valores de importacion:
|
||||
</h4>
|
||||
Dolares:<span class="text-blue-400">0</span> <span class="text-red-400">USD</span> <br />
|
||||
Pesos: <span class="text-blue-400">0</span> <span class="text-red-400">MXN</span><br />
|
||||
De Captura: <span class="text-blue-400">0</span> <span class="text-red-400">USD</span>
|
||||
|
||||
<h4 class="col-span-2 text-xs font-semibold text-muted-foreground uppercase opacity-0">
|
||||
spacer
|
||||
</h4>
|
||||
|
||||
Aduana:<span class="text-blue-400">0</span> <span class="text-red-400">USD</span><br />
|
||||
Aduana: <span class="text-blue-400">0</span> <span class="text-red-400">MXN</span><br />
|
||||
</div>
|
||||
|
||||
<h4 class="text-xs font-semibold text-muted-foreground uppercase col-span-2">
|
||||
Valores de importacion:
|
||||
</h4>
|
||||
Dolares:<span class="text-blue-400">0</span> <span class="text-red-400">USD</span> <br />
|
||||
Pesos: <span class="text-blue-400">0</span> <span class="text-red-400">MXN</span><br />
|
||||
De Captura: <span class="text-blue-400">0</span> <span class="text-red-400">USD</span>
|
||||
|
||||
<h4 class="text-xs font-semibold text-muted-foreground uppercase col-span-2 opacity-0">
|
||||
spacer
|
||||
</h4>
|
||||
|
||||
Aduana:<span class="text-blue-400">0</span> <span class="text-red-400">USD</span><br />
|
||||
Aduana: <span class="text-blue-400">0</span> <span class="text-red-400">MXN</span><br />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -850,7 +920,7 @@
|
||||
</Button>
|
||||
<Button variant="destructive" onclick={confirmDelete} disabled={isSaving}>
|
||||
{#if isSaving}
|
||||
<Loader2 class="w-4 h-4 mr-2 animate-spin" />
|
||||
<Loader2 class="mr-2 h-4 w-4 animate-spin" />
|
||||
Eliminando...
|
||||
{:else}
|
||||
Eliminar
|
||||
|
||||
@@ -1,31 +1,33 @@
|
||||
<script lang="ts">
|
||||
<script lang="ts">
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import { Textarea } from "$lib/components/ui/textarea/index.js";
|
||||
import type { Invoice } from '$lib/api/dashboard/a76/invoices';
|
||||
import { Textarea } from '$lib/components/ui/textarea/index.js';
|
||||
import type { Invoice } from '$lib/api/dashboard/a76/invoices';
|
||||
|
||||
let {
|
||||
let {
|
||||
invoice,
|
||||
formData = $bindable(),
|
||||
exists = $bindable(),
|
||||
seals = [],
|
||||
incoterms = [],
|
||||
enclosure = [],
|
||||
}: {
|
||||
operationType = undefined
|
||||
}: {
|
||||
invoice: Invoice | null;
|
||||
formData?: any;
|
||||
exists?: boolean;
|
||||
seals?: any[];
|
||||
incoterms?: any[];
|
||||
enclosure?: any[];
|
||||
operationType?: number;
|
||||
} = $props();
|
||||
|
||||
if (!formData && invoice) {
|
||||
formData = {
|
||||
// Campos de observaciones
|
||||
observation_es: invoice.observation_es || '',
|
||||
observation_en: invoice.observation_en || '',
|
||||
observation_en: invoice.observation_en || '',
|
||||
// Incrementables
|
||||
freight: invoice.financials?.freight || null,
|
||||
insurance_value: invoice.financials?.insurance_value || null,
|
||||
@@ -35,20 +37,30 @@
|
||||
total_increments_mn: invoice.financials?.total_increments_mn || null,
|
||||
total_increments_me: invoice.financials?.total_increments_me || null,
|
||||
// Incoterm y recinto
|
||||
incoterm: invoice.logistics?.[0]?.incoterm || null,
|
||||
incoterm: invoice.logistics?.incoterm || null,
|
||||
enclosure: invoice.compliance_mx?.enclosure || null,
|
||||
// Campos de esta pestaña
|
||||
num_seals: null,
|
||||
movement_type: invoice.compliance_mx?.movement_type || '',
|
||||
alternate_invoice: invoice.alternate_invoice || '',
|
||||
valuation_method: null
|
||||
valuation_method: invoice.compliance_mx?.value_method || null,
|
||||
// New fields for Export
|
||||
other_deductibles: null, // Need to confirm where this maps
|
||||
proforma_number: invoice.proforma_number || '',
|
||||
sub_division: invoice.compliance_mx?.subdivision || '',
|
||||
acts_as_cd: invoice.logistics?.acts_as_cd || false,
|
||||
identifier_1: invoice.logistics?.identifier_1 || '',
|
||||
complement_1: invoice.logistics?.complement_1 || '',
|
||||
identifier_2: invoice.logistics?.identifier_2 || '',
|
||||
complement_2: invoice.logistics?.complement_2 || '',
|
||||
office_document: invoice.compliance_mx?.office_document || ''
|
||||
};
|
||||
exists = true;
|
||||
} else if (!formData) {
|
||||
formData = {
|
||||
// Campos de observaciones
|
||||
observation_es: '',
|
||||
observation_en: '',
|
||||
observation_en: '',
|
||||
// Incrementables
|
||||
freight: null,
|
||||
insurance_value: null,
|
||||
@@ -64,245 +76,462 @@
|
||||
num_seals: null,
|
||||
movement_type: '',
|
||||
alternate_invoice: '',
|
||||
valuation_method: null
|
||||
valuation_method: null,
|
||||
// New fields for Export
|
||||
other_deductibles: null,
|
||||
proforma_number: '',
|
||||
sub_division: '',
|
||||
acts_as_cd: false,
|
||||
identifier_1: '',
|
||||
complement_1: '',
|
||||
identifier_2: '',
|
||||
complement_2: '',
|
||||
office_document: ''
|
||||
};
|
||||
exists = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<div class="grid grid-cols-2 grid-rows-5 gap-4">
|
||||
<div class="border rounded-md p-3 space-y-3 row-span-2">
|
||||
<h4 class="text-xs font-semibold text-muted-foreground uppercase">Observacion de la factura mexicana y bilingue:</h4>
|
||||
<div class="space-y-1">
|
||||
<Textarea
|
||||
id="observation_es"
|
||||
bind:value={formData.observation_es}
|
||||
class="min-h-[150px] max-h-[150px] text-sm font-medium"
|
||||
placeholder="Escribe tus observaciones aqui."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border rounded-md p-3 space-y-3 row-span-2 col-start-1 row-start-3">
|
||||
<h4 class="text-xs font-semibold text-muted-foreground uppercase">Observacion de la factura americana:</h4>
|
||||
<Textarea
|
||||
id="observation_en"
|
||||
bind:value={formData.observation_en}
|
||||
class="min-h-[150px] max-h-[150px] text-sm font-medium"
|
||||
placeholder="Escribe tus observaciones aqui."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="border rounded-md p-3 space-y-3 col-span-2 col-start-1 row-start-5">
|
||||
<div class="grid grid-cols-4 gap-3">
|
||||
<div class="space-y-1">
|
||||
<Label for="num_seals" class="text-xs">Num Precintos:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.num_seals ? String(formData.num_seals) : ''}
|
||||
onValueChange={(v) => {
|
||||
formData.num_seals = v ? parseInt(v) : null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="num_seals" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.num_seals
|
||||
? seals.find(p => p.id === formData.num_seals)?.name || 'Selecciona...'
|
||||
: 'Selecciona...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each seals as seal}
|
||||
<Select.Item value={String(seal.id)}>
|
||||
{seal.name}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="movement_type" class="text-xs">Tipo Movimiento:</Label>
|
||||
<Input
|
||||
id="movement_type"
|
||||
bind:value={formData.movement_type}
|
||||
class="h-8 text-sm font-medium"
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<!-- Left Column: Observations and Catalog Info -->
|
||||
<div class="space-y-4">
|
||||
<!-- Observations Section -->
|
||||
<div class="space-y-4 rounded-md border p-3">
|
||||
<div class="space-y-2">
|
||||
<h4 class="text-xs font-semibold text-muted-foreground uppercase">
|
||||
{operationType === 1
|
||||
? 'Observaciones de la factura mexicana:'
|
||||
: 'Observacion de la factura mexicana y bilingue:'}
|
||||
</h4>
|
||||
<Textarea
|
||||
id="observation_es"
|
||||
bind:value={formData.observation_es}
|
||||
class="max-h-[150px] min-h-[150px] text-sm font-medium"
|
||||
placeholder="Escribe tus observaciones aqui."
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="alternate_invoice" class="text-xs">Factura Alterna:</Label>
|
||||
<Input
|
||||
id="alternate_invoice"
|
||||
bind:value={formData.alternate_invoice}
|
||||
class="h-8 text-sm font-medium"
|
||||
|
||||
<div class="space-y-2">
|
||||
<h4 class="text-xs font-semibold text-muted-foreground uppercase">
|
||||
Observaciones de la factura americana:
|
||||
</h4>
|
||||
<Textarea
|
||||
id="observation_en"
|
||||
bind:value={formData.observation_en}
|
||||
class="max-h-[150px] min-h-[150px] text-sm font-medium"
|
||||
placeholder="Escribe tus observaciones aqui."
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="valuation_method" class="text-xs">Met. Valoracion:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.valuation_method ? String(formData.valuation_method) : ''}
|
||||
onValueChange={(v) => {
|
||||
formData.valuation_method = v;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="valuation_method" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.valuation_method || 'Selecciona...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
<Select.Item value="general">General</Select.Item>
|
||||
<Select.Item value="devalued">Devaluado</Select.Item>
|
||||
<Select.Item value="special">Especial</Select.Item>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if operationType === 1}
|
||||
<!-- Identificadores (Export Only) -->
|
||||
<div class="space-y-4 rounded-md border bg-muted/20 p-4">
|
||||
<h4 class="mb-1 border-b pb-2 text-xs font-bold text-muted-foreground uppercase">
|
||||
Identificadores
|
||||
</h4>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-1.5">
|
||||
<Label for="identifier_1" class="text-xs font-semibold">Primero:</Label>
|
||||
<Input
|
||||
id="identifier_1"
|
||||
bind:value={formData.identifier_1}
|
||||
class="h-8 text-xs"
|
||||
placeholder="Clave..."
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
<Label for="identifier_2" class="text-xs font-semibold">Segundo:</Label>
|
||||
<Input
|
||||
id="identifier_2"
|
||||
bind:value={formData.identifier_2}
|
||||
class="h-8 text-xs"
|
||||
placeholder="Clave..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Complementos (Export Only) -->
|
||||
<div class="space-y-4 rounded-md border bg-muted/20 p-4">
|
||||
<h4 class="mb-1 border-b pb-2 text-xs font-bold text-muted-foreground uppercase">
|
||||
Complementos
|
||||
</h4>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-1.5">
|
||||
<Label for="complement_1" class="text-xs font-semibold">1:</Label>
|
||||
<Input id="complement_1" bind:value={formData.complement_1} class="h-8 text-xs" />
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
<Label for="complement_2" class="text-xs font-semibold">2:</Label>
|
||||
<Input id="complement_2" bind:value={formData.complement_2} class="h-8 text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Oficio (Export Only) -->
|
||||
<div class="rounded-md border bg-muted/20 p-3">
|
||||
<div class="space-y-1.5">
|
||||
<Label for="office_document" class="text-xs font-semibold">Oficio:</Label>
|
||||
<Input id="office_document" bind:value={formData.office_document} class="h-8 text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="border rounded-md p-3 space-y-3 row-span-3 col-start-2 row-start-1">
|
||||
<h4 class="text-xs font-semibold text-muted-foreground uppercase">Incrementables:</h4>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="space-y-1">
|
||||
<Label for="freight" class="text-xs">Flete:</Label>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Input
|
||||
id="freight"
|
||||
<!-- Right Column: Values, Flags and Logistics -->
|
||||
<div class="space-y-4">
|
||||
<!-- Incrementables Section -->
|
||||
<div class="space-y-3 rounded-md border p-3">
|
||||
<h4 class="text-xs font-semibold text-muted-foreground uppercase">Incrementables:</h4>
|
||||
<div class="grid grid-cols-2 gap-x-4 gap-y-2">
|
||||
<div class="flex items-center">
|
||||
<Label for="freight" class="text-xs">Flete:</Label>
|
||||
</div>
|
||||
<Input
|
||||
id="freight"
|
||||
type="number"
|
||||
bind:value={formData.freight}
|
||||
class="h-8 text-sm font-medium"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1">
|
||||
<Label for="insurance_value" class="text-xs">Val. Seguros:</Label>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Input
|
||||
id="insurance_value"
|
||||
type="number"
|
||||
bind:value={formData.insurance_value}
|
||||
class="h-8 text-sm font-medium"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1">
|
||||
<Label for="insurance" class="text-xs">Seguros:</Label>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Input
|
||||
id="insurance"
|
||||
type="number"
|
||||
bind:value={formData.insurance}
|
||||
class="h-8 text-sm font-medium"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1">
|
||||
<Label for="packaging" class="text-xs">Embalajes:</Label>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Input
|
||||
id="packaging"
|
||||
type="number"
|
||||
bind:value={formData.packaging}
|
||||
class="h-8 text-sm font-medium"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1">
|
||||
<Label for="other_increments" class="text-xs">Otros incrementables:</Label>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Input
|
||||
id="other_increments"
|
||||
type="number"
|
||||
bind:value={formData.other_increments}
|
||||
class="h-8 text-sm font-medium"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1">
|
||||
<Label for="total_increments_mn" class="text-xs">Total Incrementables:</Label>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Input
|
||||
id="total_increments_mn"
|
||||
type="number"
|
||||
bind:value={formData.total_increments_mn}
|
||||
bind:value={formData.freight}
|
||||
class="h-8 text-sm font-medium"
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Input
|
||||
id="total_increments_me"
|
||||
|
||||
<div class="flex items-center">
|
||||
<Label for="insurance" class="text-xs">Seguros:</Label>
|
||||
</div>
|
||||
<Input
|
||||
id="insurance"
|
||||
type="number"
|
||||
bind:value={formData.total_increments_me}
|
||||
bind:value={formData.insurance}
|
||||
class="h-8 text-sm font-medium"
|
||||
disabled
|
||||
/>
|
||||
|
||||
<div class="flex items-center">
|
||||
<Label for="packaging" class="text-xs">Embalajes:</Label>
|
||||
</div>
|
||||
<Input
|
||||
id="packaging"
|
||||
type="number"
|
||||
bind:value={formData.packaging}
|
||||
class="h-8 text-sm font-medium"
|
||||
/>
|
||||
|
||||
<div class="flex items-center">
|
||||
<Label for="other_increments" class="text-xs leading-none">Otros increm.:</Label>
|
||||
</div>
|
||||
<Input
|
||||
id="other_increments"
|
||||
type="number"
|
||||
bind:value={formData.other_increments}
|
||||
class="h-8 text-sm font-medium"
|
||||
/>
|
||||
|
||||
{#if operationType === 1}
|
||||
<div class="flex items-center">
|
||||
<Label for="other_deductibles" class="text-xs leading-none">Otros deduc.:</Label>
|
||||
</div>
|
||||
<Input
|
||||
id="other_deductibles"
|
||||
type="number"
|
||||
bind:value={formData.other_deductibles}
|
||||
class="h-8 border-orange-200 text-sm font-medium focus-visible:ring-orange-200"
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border rounded-md p-3 space-y-3 col-start-2 row-start-4">
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div class="space-y-1">
|
||||
<Label for="incoterm" class="text-xs">Incoterm:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.incoterm ? String(formData.incoterm) : ''}
|
||||
onValueChange={(v) => {
|
||||
formData.incoterm = v ? parseInt(v) : null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="incoterm" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.incoterm
|
||||
? incoterms.find(p => p.id === formData.incoterm)?.name || 'Selecciona...'
|
||||
: 'Selecciona...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each incoterms as inco}
|
||||
<Select.Item value={String(inco.id)}>
|
||||
{inco.name}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
|
||||
{#if operationType === 1}
|
||||
<!-- Precintos & Tipo Mov Group (No Title) -->
|
||||
<div class="grid grid-cols-2 gap-4 rounded-md border bg-muted/20 p-3">
|
||||
<div class="space-y-1">
|
||||
<Label for="num_seals_exp" class="text-xs font-semibold">Número de Precinto:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.num_seals ? String(formData.num_seals) : ''}
|
||||
onValueChange={(v) => {
|
||||
formData.num_seals = v ? parseInt(v) : null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="num_seals_exp" class="h-8 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.num_seals
|
||||
? seals.find((p) => p.id === formData.num_seals)?.name || 'Selecciona...'
|
||||
: 'Selecciona...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each seals as seal}
|
||||
<Select.Item value={String(seal.id)}>
|
||||
{seal.name}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="movement_type_exp" class="text-xs font-semibold">Tipo Movimiento:</Label>
|
||||
<Input
|
||||
id="movement_type_exp"
|
||||
bind:value={formData.movement_type}
|
||||
class="h-8 text-xs font-medium"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="enclosure" class="text-xs">Recinto:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.enclosure ? String(formData.enclosure) : ''}
|
||||
onValueChange={(v) => {
|
||||
formData.enclosure = v ? parseInt(v) : null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="enclosure" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.enclosure
|
||||
? enclosure.find(p => p.id === formData.enclosure)?.name || 'Selecciona...'
|
||||
: 'Selecciona...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each enclosure as rec}
|
||||
<Select.Item value={String(rec.id)}>
|
||||
{rec.name}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
|
||||
<!-- Factura Alterna & Flags Section -->
|
||||
<div class="space-y-4 rounded-md border bg-muted/20 p-4">
|
||||
<h4 class="mb-1 border-b pb-2 text-xs font-bold text-muted-foreground uppercase">
|
||||
Factura Alterna & Flags
|
||||
</h4>
|
||||
<div class="grid grid-cols-2 gap-x-6 gap-y-4">
|
||||
<div class="space-y-1.5">
|
||||
<Label for="alternate_invoice_exp" class="text-xs font-semibold">Factura Alterna:</Label
|
||||
>
|
||||
<Input
|
||||
id="alternate_invoice_exp"
|
||||
bind:value={formData.alternate_invoice}
|
||||
class="h-8 text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
<Label for="proforma_number" class="text-xs font-semibold">Número de Proforma:</Label>
|
||||
<Input id="proforma_number" bind:value={formData.proforma_number} class="h-8 text-xs" />
|
||||
</div>
|
||||
|
||||
<div class="space-y-2 pt-1">
|
||||
<Label class="mb-2 block text-xs font-semibold">Sub División:</Label>
|
||||
<div class="flex items-center gap-4">
|
||||
<label class="group flex cursor-pointer items-center gap-2">
|
||||
<div
|
||||
class="relative flex h-4 w-4 items-center justify-center rounded-full border-2 border-zinc-400 transition-all {formData.sub_division ===
|
||||
'si'
|
||||
? 'border-zinc-800 bg-zinc-800'
|
||||
: ''}"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
value="si"
|
||||
bind:group={formData.sub_division}
|
||||
class="sr-only"
|
||||
/>
|
||||
{#if formData.sub_division === 'si'}<div
|
||||
class="h-1.5 w-1.5 rounded-full bg-white"
|
||||
></div>{/if}
|
||||
</div>
|
||||
<span class="text-xs font-medium transition-colors group-hover:text-zinc-800"
|
||||
>Si</span
|
||||
>
|
||||
</label>
|
||||
<label class="group flex cursor-pointer items-center gap-2">
|
||||
<div
|
||||
class="relative flex h-4 w-4 items-center justify-center rounded-full border-2 border-zinc-400 transition-all {formData.sub_division ===
|
||||
'no'
|
||||
? 'border-zinc-800 bg-zinc-800'
|
||||
: ''}"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
value="no"
|
||||
bind:group={formData.sub_division}
|
||||
class="sr-only"
|
||||
/>
|
||||
{#if formData.sub_division === 'no'}<div
|
||||
class="h-1.5 w-1.5 rounded-full bg-white"
|
||||
></div>{/if}
|
||||
</div>
|
||||
<span class="text-xs font-medium transition-colors group-hover:text-zinc-800"
|
||||
>No</span
|
||||
>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2 pt-1">
|
||||
<Label class="mb-2 block text-xs font-semibold">Funge como CD:</Label>
|
||||
<div class="flex items-center gap-4">
|
||||
<label class="group flex cursor-pointer items-center gap-2">
|
||||
<div
|
||||
class="relative flex h-4 w-4 items-center justify-center rounded-full border-2 border-zinc-400 transition-all {formData.acts_as_cd ===
|
||||
true
|
||||
? 'border-zinc-800 bg-zinc-800'
|
||||
: ''}"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
value={true}
|
||||
bind:group={formData.acts_as_cd}
|
||||
class="sr-only"
|
||||
/>
|
||||
{#if formData.acts_as_cd === true}
|
||||
<div class="h-1.5 w-1.5 rounded-full bg-white"></div>{/if}
|
||||
</div>
|
||||
<span class="text-xs font-medium transition-colors group-hover:text-zinc-800"
|
||||
>Si</span
|
||||
>
|
||||
</label>
|
||||
<label class="group flex cursor-pointer items-center gap-2">
|
||||
<div
|
||||
class="relative flex h-4 w-4 items-center justify-center rounded-full border-2 border-zinc-400 transition-all {formData.acts_as_cd ===
|
||||
false
|
||||
? 'border-zinc-800 bg-zinc-800'
|
||||
: ''}"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
value={false}
|
||||
bind:group={formData.acts_as_cd}
|
||||
class="sr-only"
|
||||
/>
|
||||
{#if formData.acts_as_cd === false}
|
||||
<div class="h-1.5 w-1.5 rounded-full bg-white"></div>{/if}
|
||||
</div>
|
||||
<span class="text-xs font-medium transition-colors group-hover:text-zinc-800"
|
||||
>No</span
|
||||
>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Incoterm & Recinto (Show for all, layout adjusted) -->
|
||||
<div class="space-y-3 rounded-md border p-3">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-1">
|
||||
<Label for="incoterm" class="text-xs">Incoterm:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.incoterm ? String(formData.incoterm) : ''}
|
||||
onValueChange={(v) => {
|
||||
formData.incoterm = v ? parseInt(v) : null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="incoterm" class="h-7 text-xs">
|
||||
<span class="truncate"
|
||||
>{formData.incoterm
|
||||
? incoterms.find((p) => p.id === formData.incoterm)?.name || 'Selecciona...'
|
||||
: 'Selecciona...'}</span
|
||||
>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each incoterms as inco}
|
||||
<Select.Item value={String(inco.id)}>{inco.name}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="enclosure" class="text-xs">Recinto:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.enclosure ? String(formData.enclosure) : ''}
|
||||
onValueChange={(v) => {
|
||||
formData.enclosure = v ? parseInt(v) : null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="enclosure" class="h-7 text-xs">
|
||||
<span class="truncate"
|
||||
>{formData.enclosure
|
||||
? enclosure.find((p) => p.id === formData.enclosure)?.name || 'Selecciona...'
|
||||
: 'Selecciona...'}</span
|
||||
>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each enclosure as rec}
|
||||
<Select.Item value={String(rec.id)}>{rec.name}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
{#if operationType === 1}
|
||||
<div class="space-y-1 pt-1">
|
||||
<Label for="valuation_method_exp" class="text-xs font-semibold"
|
||||
>Método de Valoración:</Label
|
||||
>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.valuation_method ? String(formData.valuation_method) : ''}
|
||||
onValueChange={(v) => {
|
||||
formData.valuation_method = v;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="valuation_method_exp" class="h-8 text-xs">
|
||||
<span class="truncate">{formData.valuation_method || 'Selecciona...'}</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Item value="general">General</Select.Item>
|
||||
<Select.Item value="devalued">Devaluado</Select.Item>
|
||||
<Select.Item value="special">Especial</Select.Item>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Extra Bottom Row for Import Mode specific fields (Original Layout preserved for Import) -->
|
||||
{#if operationType !== 1}
|
||||
<div class="col-span-2 space-y-3 rounded-md border p-3">
|
||||
<div class="grid grid-cols-4 gap-3">
|
||||
<div class="space-y-1">
|
||||
<Label for="num_seals" class="text-xs">Num Precintos:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.num_seals ? String(formData.num_seals) : ''}
|
||||
onValueChange={(v) => {
|
||||
formData.num_seals = v ? parseInt(v) : null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="num_seals" class="h-7 text-xs">
|
||||
<span class="truncate"
|
||||
>{formData.num_seals
|
||||
? seals.find((p) => p.id === formData.num_seals)?.name || 'Selecciona...'
|
||||
: 'Selecciona...'}</span
|
||||
>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each seals as seal}<Select.Item value={String(seal.id)}>{seal.name}</Select.Item
|
||||
>{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="movement_type" class="text-xs">Tipo Movimiento:</Label>
|
||||
<Input
|
||||
id="movement_type"
|
||||
bind:value={formData.movement_type}
|
||||
class="h-8 text-sm font-medium"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="alternate_invoice" class="text-xs">Factura Alterna:</Label>
|
||||
<Input
|
||||
id="alternate_invoice"
|
||||
bind:value={formData.alternate_invoice}
|
||||
class="h-8 text-sm font-medium"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="valuation_method" class="text-xs">Met. Valoracion:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.valuation_method ? String(formData.valuation_method) : ''}
|
||||
onValueChange={(v) => {
|
||||
formData.valuation_method = v;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="valuation_method" class="h-7 text-xs">
|
||||
<span class="truncate">{formData.valuation_method || 'Selecciona...'}</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
<Select.Item value="general">General</Select.Item>
|
||||
<Select.Item value="devalued">Devaluado</Select.Item>
|
||||
<Select.Item value="special">Especial</Select.Item>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@ export async function saveInvoice(options: SaveInvoiceOptions): Promise<SaveInvo
|
||||
};
|
||||
|
||||
const missingFields: string[] = [];
|
||||
|
||||
|
||||
// Validar campos de InvoiceTopFieldsFormData
|
||||
if (!InvoiceTopFieldsFormData?.operation_type) {
|
||||
missingFields.push(requiredFields.operation_type);
|
||||
@@ -73,11 +73,11 @@ export async function saveInvoice(options: SaveInvoiceOptions): Promise<SaveInvo
|
||||
|
||||
if (isCreate) {
|
||||
// Crear nueva factura con todos sus sub-recursos
|
||||
const response = await invoicesApi.create(companyId, payload as CreateInvoiceData);
|
||||
const response = await invoicesApi.create(companyId, payload as CreateInvoiceData);
|
||||
if (response.error) {
|
||||
const errorMsg = typeof response.error === 'string' ? response.error : 'Error al crear la factura';
|
||||
const error: any = new Error(errorMsg);
|
||||
error.validationErrors = response.validationErrors;
|
||||
error.validationErrors = response.validationErrors;
|
||||
throw error;
|
||||
}
|
||||
if (!response.data?.id) throw new Error('No se recibió el ID de la factura creada');
|
||||
@@ -88,10 +88,10 @@ export async function saveInvoice(options: SaveInvoiceOptions): Promise<SaveInvo
|
||||
} else {
|
||||
// Actualizar factura existente con todos sus sub-recursos
|
||||
const updatePayload = { ...payload, id: invoiceId } as UpdateInvoiceData;
|
||||
const response = await invoicesApi.update(invoiceId!, companyId, updatePayload);
|
||||
const response = await invoicesApi.update(invoiceId!, companyId, updatePayload);
|
||||
if (response.error) {
|
||||
const error: any = new Error(response.error);
|
||||
error.validationErrors = response.validationErrors;
|
||||
error.validationErrors = response.validationErrors;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -116,6 +116,7 @@ function buildInvoicePayload(formData: FormDataSet): CreateInvoiceData | UpdateI
|
||||
invoice_number: InvoiceTopFieldsFormData?.invoice_number || undefined,
|
||||
invoice_date: InvoiceTopFieldsFormData?.invoice_date || undefined,
|
||||
emission_date: InvoiceTopFieldsFormData?.emission_date || undefined,
|
||||
proforma_number: observationFormData?.proforma_number || undefined,
|
||||
// Observation fields from observationFormData
|
||||
observation_es: observationFormData?.observation_es || undefined,
|
||||
observation_en: observationFormData?.observation_en || undefined,
|
||||
@@ -138,7 +139,8 @@ function buildInvoicePayload(formData: FormDataSet): CreateInvoiceData | UpdateI
|
||||
othersFormData?.cove || othersFormData?.operation_num ||
|
||||
othersFormData?.adendas || othersFormData?.certified_number ||
|
||||
othersFormData?.code_signature || othersFormData?.electronic_signature ||
|
||||
continuationFormData?.puerto_entrada;
|
||||
continuationFormData?.puerto_entrada || generalFormData?.manifest_number ||
|
||||
generalFormData?.shipped_by_id || generalFormData?.shipped_by_header;
|
||||
|
||||
if (hasComplianceValue) {
|
||||
payload.compliance_mx = buildComplianceMxData(InvoiceTopFieldsFormData, generalFormData, othersFormData, observationFormData, continuationFormData);
|
||||
@@ -177,6 +179,7 @@ function buildComplianceMxData(InvoiceTopFieldsFormData: any, generalFormData: a
|
||||
is_pedimento_pending: Boolean(InvoiceTopFieldsFormData?.is_pedimento_pending || false),
|
||||
// Fields from generalFormData
|
||||
aduana: generalFormData?.aduana || null,
|
||||
manifest_number: generalFormData?.manifest_number || null,
|
||||
port_of_entry: continuationFormData?.puerto_entrada || null,
|
||||
provider_header: generalFormData?.provider_header || null,
|
||||
provider_id: generalFormData?.provider_id || null,
|
||||
@@ -184,6 +187,8 @@ function buildComplianceMxData(InvoiceTopFieldsFormData: any, generalFormData: a
|
||||
sold_to_id: generalFormData?.sold_to_id || null,
|
||||
shipped_to_header: generalFormData?.shipped_to_header || null,
|
||||
shipped_to_id: generalFormData?.shipped_to_id || null,
|
||||
shipped_by_header: generalFormData?.shipped_by_header || null,
|
||||
shipped_by_id: generalFormData?.shipped_by_id || null,
|
||||
customs_broker_id: generalFormData?.customs_broker_id ? Number(generalFormData.customs_broker_id) : null,
|
||||
customs_broker_us_id: generalFormData?.customs_broker_us_id ? Number(generalFormData.customs_broker_us_id) : null,
|
||||
// Fields from observationFormData
|
||||
@@ -198,6 +203,8 @@ function buildComplianceMxData(InvoiceTopFieldsFormData: any, generalFormData: a
|
||||
certificate_number: othersFormData?.certified_number || null,
|
||||
code_signature: othersFormData?.code_signature || null,
|
||||
electronic_signature: othersFormData?.electronic_signature || null,
|
||||
subdivision: observationFormData?.sub_division || null,
|
||||
office_document: observationFormData?.office_document || null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -206,13 +213,14 @@ function buildFinancialsData(generalFormData: any, observationFormData: any, oth
|
||||
// Currency from generalFormData
|
||||
currency_type: generalFormData?.currency_type || null,
|
||||
currency: generalFormData?.currency || null,
|
||||
iva_factor: generalFormData?.iva_factor || null,
|
||||
iva_factor: generalFormData?.iva_factor !== null && generalFormData?.iva_factor !== undefined ? String(generalFormData.iva_factor) : null,
|
||||
// Costs & increments from observationFormData
|
||||
freight: observationFormData?.freight || null,
|
||||
insurance: observationFormData?.insurance || null,
|
||||
insurance_value: observationFormData?.insurance_value || null,
|
||||
packaging: observationFormData?.packaging || null,
|
||||
other_increments: observationFormData?.other_increments || null,
|
||||
other_deductibles: observationFormData?.other_deductibles || null,
|
||||
total_increments_mn: observationFormData?.total_increments_mn || null,
|
||||
total_increments_me: observationFormData?.total_increments_me || null,
|
||||
// Seal value from othersFormData
|
||||
@@ -230,6 +238,13 @@ function buildLogisticsData(generalFormData: any, observationFormData: any, cont
|
||||
incoterm: observationFormData?.incoterm || null,
|
||||
// Campos de continuación mapeados a logistics
|
||||
transport_num: continuationFormData?.numero_tipo_transporte || null,
|
||||
// Identificadores y Complementos
|
||||
identifier_1: observationFormData?.identifier_1 || null,
|
||||
complement_1: observationFormData?.complement_1 || null,
|
||||
identifier_2: observationFormData?.identifier_2 || null,
|
||||
complement_2: observationFormData?.complement_2 || null,
|
||||
acts_as_cd: observationFormData?.acts_as_cd || continuationFormData?.funge_como_cd || false,
|
||||
is_subdivision: (observationFormData?.sub_division === 'si') || continuationFormData?.sub_division || false,
|
||||
is_rail: continuationFormData?.es_ferrocarril === 'si' ? true : false,
|
||||
bill_number: continuationFormData?.numero_bl || null,
|
||||
guide_number: continuationFormData?.cantidad_guias_embarque ? String(continuationFormData.cantidad_guias_embarque) : null,
|
||||
@@ -237,8 +252,6 @@ function buildLogisticsData(generalFormData: any, observationFormData: any, cont
|
||||
origin_location: continuationFormData?.puerto_entrada || null,
|
||||
// Checkboxes de continuación
|
||||
equipment_reviewed: continuationFormData?.fue_revisado_equipo || false,
|
||||
is_subdivision: continuationFormData?.sub_division || false,
|
||||
acts_as_cd: continuationFormData?.funge_como_cd || false,
|
||||
pedimento_arrived: continuationFormData?.llego_pedimento || false,
|
||||
// Semáforos de continuación
|
||||
green_light_mx: continuationFormData?.semaforo_verde_aduana_mexicana || false,
|
||||
|
||||
@@ -112,6 +112,27 @@
|
||||
let showExchangeRateDialog = $state(false);
|
||||
let missingExchangeRateDate = $state('');
|
||||
|
||||
// Derivados reactivos para el tipo de operación
|
||||
let operationTypeText = $derived.by(() => {
|
||||
const type =
|
||||
InvoiceTopFieldsFormData?.operation_type ||
|
||||
data.invoice?.operation_type ||
|
||||
data.filters?.operation_type;
|
||||
if (type === 'exp') return 'EXPORTACIÓN';
|
||||
if (type === 'imp') return 'IMPORTACIÓN';
|
||||
return 'TIPO NO DEFINIDO';
|
||||
});
|
||||
|
||||
let operationColorClass = $derived.by(() => {
|
||||
const type =
|
||||
InvoiceTopFieldsFormData?.operation_type ||
|
||||
data.invoice?.operation_type ||
|
||||
data.filters?.operation_type;
|
||||
if (type === 'exp') return 'bg-blue-500/10 text-blue-600 border-blue-500/20';
|
||||
if (type === 'imp') return 'bg-emerald-500/10 text-emerald-600 border-emerald-500/20';
|
||||
return 'bg-muted text-muted-foreground border-border';
|
||||
});
|
||||
|
||||
async function checkExchangeRate(date: string): Promise<boolean> {
|
||||
if (!date || !companyStore?.activeCompany?.id) return true;
|
||||
|
||||
@@ -293,7 +314,7 @@
|
||||
: null}
|
||||
Nueva Factura
|
||||
{#if invoiceTypeInfo}
|
||||
<span class="text-muted-foreground font-normal text-2xl">
|
||||
<span class="text-2xl font-normal text-muted-foreground">
|
||||
- {invoiceTypeInfo.description}
|
||||
</span>
|
||||
{/if}
|
||||
@@ -301,12 +322,11 @@
|
||||
Factura #{data.invoice.id}
|
||||
{/if}
|
||||
</h1>
|
||||
<Badge variant="outline" class="px-3 py-1 text-sm font-bold {operationColorClass}">
|
||||
{operationTypeText}
|
||||
</Badge>
|
||||
{#if data.isCreate}
|
||||
<Badge variant="default">Nueva</Badge>
|
||||
{:else if data.invoice.operation_type}
|
||||
<Badge variant={getOperationColor(data.invoice.operation_type)}>
|
||||
{data.invoice.operation_type === 'imp' ? 'Importación' : 'Exportación'}
|
||||
</Badge>
|
||||
<Badge variant="secondary">Borrador</Badge>
|
||||
{/if}
|
||||
</div>
|
||||
<p class="text-muted-foreground">
|
||||
@@ -324,6 +344,37 @@
|
||||
<!-- Contenido de las tabs con padding inferior para el footer flotante -->
|
||||
<div class="pb-48">
|
||||
<Tabs.Root bind:value={activeTab}>
|
||||
<div class="mb-4 rounded-lg border p-4 shadow-sm transition-colors {operationColorClass}">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="flex h-10 w-10 items-center justify-center rounded-full bg-background/50">
|
||||
{#if InvoiceTopFieldsFormData?.operation_type === 'exp'}
|
||||
<Package size={20} />
|
||||
{:else}
|
||||
<Truck size={20} />
|
||||
{/if}
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-sm font-semibold tracking-wider uppercase">
|
||||
Operación de {operationTypeText}
|
||||
</h3>
|
||||
<p class="text-xs opacity-80">
|
||||
{#if InvoiceTopFieldsFormData?.operation_type === 'exp'}
|
||||
Factura para salida de mercancías del territorio nacional.
|
||||
{:else if InvoiceTopFieldsFormData?.operation_type === 'imp'}
|
||||
Factura para entrada de mercancías al territorio nacional.
|
||||
{:else}
|
||||
Por favor, seleccione el tipo de operación para continuar.
|
||||
{/if}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Badge variant="outline" class="bg-background/20 font-mono">
|
||||
{InvoiceTopFieldsFormData?.operation_type?.toUpperCase() || '---'}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<InvoiceTopFields
|
||||
invoice={data.invoice}
|
||||
bind:formData={InvoiceTopFieldsFormData}
|
||||
@@ -372,6 +423,15 @@
|
||||
seals={data.seals || []}
|
||||
incoterms={data.incoterms || []}
|
||||
enclosure={data.enclosure || []}
|
||||
operationType={InvoiceTopFieldsFormData?.operation_type === 'exp'
|
||||
? 1
|
||||
: InvoiceTopFieldsFormData?.operation_type === 'imp'
|
||||
? 2
|
||||
: data.invoice?.operation_type === 'exp'
|
||||
? 1
|
||||
: data.invoice?.operation_type === 'imp'
|
||||
? 2
|
||||
: undefined}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
|
||||
@@ -380,6 +440,15 @@
|
||||
invoice={data.invoice}
|
||||
bind:formData={itemsFormData}
|
||||
bind:exists={itemsExists}
|
||||
operationType={InvoiceTopFieldsFormData?.operation_type === 'exp'
|
||||
? 1
|
||||
: InvoiceTopFieldsFormData?.operation_type === 'imp'
|
||||
? 2
|
||||
: data.invoice?.operation_type === 'exp'
|
||||
? 1
|
||||
: data.invoice?.operation_type === 'imp'
|
||||
? 2
|
||||
: undefined}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
|
||||
@@ -420,9 +489,9 @@
|
||||
}}
|
||||
/>
|
||||
<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] group-has-data-[state=collapsed]/sidebar-wrapper:ml-[calc(var(--sidebar-width-icon))] ml-[calc(var(--sidebar-width))]"
|
||||
class="fixed right-0 bottom-0 left-0 z-[5] ml-[calc(var(--sidebar-width))] border-t bg-background/95 shadow-lg backdrop-blur group-has-data-[state=collapsed]/sidebar-wrapper:ml-[calc(var(--sidebar-width-icon))] supports-[backdrop-filter]:bg-background/80"
|
||||
>
|
||||
<div class="px-4 py-4 space-y-4 max-w-[1400px] mx-auto">
|
||||
<div class="mx-auto max-w-[1400px] space-y-4 px-4 py-4">
|
||||
<!-- Tabs Navigation -->
|
||||
<Tabs.Root bind:value={activeTab}>
|
||||
<div class="w-full overflow-x-auto pb-2">
|
||||
|
||||
Reference in New Issue
Block a user