1385 lines
48 KiB
Svelte
1385 lines
48 KiB
Svelte
<script lang="ts">
|
|
import { onMount, untrack } from 'svelte';
|
|
import * as Card from '$lib/components/ui/card';
|
|
import { Input } from '$lib/components/ui/input';
|
|
import { Label } from '$lib/components/ui/label';
|
|
import * as Select from '$lib/components/ui/select';
|
|
import { getLocale } from '$lib/paraglide/runtime';
|
|
import { getExchangeRateByDate } from '$lib/api/dashboard/a76/exchange-rate';
|
|
import { companyStore } from '$lib/stores/company.svelte';
|
|
import type { Pedimento } from '$lib/api/dashboard/a76/pedimentos';
|
|
import type { PedimentoCode } from '$lib/api/dashboard/reference_data/pedimento_codes';
|
|
import type { CustomsSection } from '$lib/api/dashboard/reference_data/customs_sections';
|
|
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/reference_data/code_pedimento_regimens';
|
|
import IdentificadoresTabForm from './identifiers-tab-form.svelte';
|
|
import ExchangeRateDialog from '$lib/components/dashboard/exchange_rate/create-edit-dialog.svelte';
|
|
import { Calendar, Clock } from 'lucide-svelte';
|
|
import {
|
|
loadServerDate,
|
|
addDaysLocal,
|
|
getCurrentLocalYear,
|
|
getCurrentLocalDate,
|
|
getCurrentLocalTime
|
|
} from '$lib/date-utils';
|
|
import { useShortcuts } from '$lib/hooks/use-shortcuts';
|
|
import { obtenerAtajosPestanaGeneral } from '$lib/config/shortcuts/dashboard/pedimentos/tabs/general';
|
|
import { shortcutStore } from '$lib/stores/shortcut-store';
|
|
import { focusStore, interactionMode } from '$lib/stores/focus-store';
|
|
|
|
type PedimentoTransportCatalog = {
|
|
code: string;
|
|
transport_en: string;
|
|
transport_es: string;
|
|
payment_date_code: 'E' | 'P' | string;
|
|
};
|
|
|
|
let {
|
|
pedimento,
|
|
formData = $bindable(),
|
|
identificadoresFormData = $bindable(),
|
|
pedimentoCodes = [],
|
|
customsSections = [],
|
|
customsBrokers = [],
|
|
clients = [],
|
|
codePedimentoRegimens = [],
|
|
pedimentoTransportCatalog = [],
|
|
isActive = false
|
|
}: {
|
|
pedimento: Pedimento | null;
|
|
formData?: any;
|
|
identificadoresFormData?: any;
|
|
pedimentoCodes?: PedimentoCode[];
|
|
customsSections?: CustomsSection[];
|
|
customsBrokers?: CustomsBroker[];
|
|
clients?: ClientProvider[];
|
|
codePedimentoRegimens?: CodePedimentoRegimen[];
|
|
pedimentoTransportCatalog?: PedimentoTransportCatalog[];
|
|
isActive?: boolean;
|
|
} = $props();
|
|
|
|
// Active navigation section state
|
|
let activeSection = $state('fechas');
|
|
|
|
// Focus first input when switching sections via mouse
|
|
// Shortcuts will override this by focusing the trigger themselves
|
|
|
|
$effect(() => {
|
|
// Accedemos a activeSection para que el efecto dependa de él
|
|
if (activeSection) {
|
|
// Usamos untrack para que no dependa de interactionMode
|
|
// Esto evita que se dispare el foco al empezar a escribir (cambio de mouse a keyboard)
|
|
const mode = untrack(() => {
|
|
let currentMode;
|
|
interactionMode.subscribe((v) => (currentMode = v))();
|
|
return currentMode;
|
|
});
|
|
|
|
if (mode === 'keyboard') {
|
|
focusStore.request('first-input');
|
|
}
|
|
}
|
|
});
|
|
|
|
$effect(() => {
|
|
if (isActive) {
|
|
shortcutStore.register(
|
|
'General Tab Navigation',
|
|
obtenerAtajosPestanaGeneral({
|
|
cambiarSeccion: (seccion) => (activeSection = seccion)
|
|
})
|
|
);
|
|
return () => {
|
|
shortcutStore.clear('General Tab Navigation');
|
|
};
|
|
}
|
|
});
|
|
|
|
// Exchange Rate Check State
|
|
let showExchangeRateDialog = $state(false);
|
|
let missingExchangeRateDate = $state('');
|
|
|
|
// Extraer regímenes únicos de codePedimentoRegimens
|
|
const uniqueRegimens = $derived(
|
|
Array.from(
|
|
new Set(
|
|
codePedimentoRegimens
|
|
.map((r) => r.regimen_code)
|
|
.filter((code): code is string => code !== null)
|
|
)
|
|
)
|
|
.sort()
|
|
.map((code) => ({ code, label: code }))
|
|
);
|
|
|
|
// Funciones de mapeo entre type_code (E/I) y operation_type (exp/imp)
|
|
function typeCodeToOperationType(typeCode: string | null | undefined): string | null {
|
|
if (!typeCode) return null;
|
|
// E = Exportación = exp, I = Importación = imp
|
|
if (typeCode.toUpperCase() === 'E') return 'exp';
|
|
if (typeCode.toUpperCase() === 'I') return 'imp';
|
|
return null;
|
|
}
|
|
|
|
function operationTypeToTypeCode(operationType: string | null | undefined): string | null {
|
|
if (operationType === null || operationType === undefined) return null;
|
|
// exp = Exportación = E, imp = Importación = I
|
|
if (operationType === 'exp') return 'E';
|
|
if (operationType === 'imp') return 'I';
|
|
return null;
|
|
}
|
|
|
|
// Opciones constantes
|
|
const operationOptions = [
|
|
{ value: 'exp', label: 'Exportación' },
|
|
{ value: 'imp', label: 'Importación' }
|
|
];
|
|
|
|
// Track previous values to detect changes
|
|
let previousPedimentoCode = $state('');
|
|
let previousOperationType = $state<string | null>(null);
|
|
|
|
// Opciones filtradas para Régimen y Tipo de Operación basadas en las selecciones actuales
|
|
// NOTA: La Clave NO se filtra, siempre muestra todas las opciones
|
|
|
|
const filteredRegimens = $derived.by(() => {
|
|
if (!formData) return uniqueRegimens;
|
|
|
|
// Si hay clave o tipo de operación seleccionado, filtrar
|
|
if (
|
|
formData.pedimento_code ||
|
|
(formData.operation_type !== null && formData.operation_type !== undefined)
|
|
) {
|
|
const expectedTypeCode = operationTypeToTypeCode(formData.operation_type);
|
|
|
|
const matches = codePedimentoRegimens.filter((r) => {
|
|
const matchesCode =
|
|
!formData.pedimento_code || r.pedimento_code === formData.pedimento_code;
|
|
const matchesType = !expectedTypeCode || r.type_code === expectedTypeCode;
|
|
return matchesCode && matchesType;
|
|
});
|
|
|
|
const validRegimens = new Set(
|
|
matches.map((m) => m.regimen_code).filter((code): code is string => code !== null)
|
|
);
|
|
return Array.from(validRegimens)
|
|
.sort()
|
|
.map((code) => ({ code, label: code }));
|
|
}
|
|
return uniqueRegimens;
|
|
});
|
|
|
|
// Determinar si el régimen debe ser seleccionable o readonly
|
|
const hasMultipleRegimens = $derived.by(() => {
|
|
if (!formData) return false;
|
|
return filteredRegimens.length > 1;
|
|
});
|
|
|
|
const filteredOperationTypes = $derived.by(() => {
|
|
if (!formData) return operationOptions;
|
|
// Solo filtrar por la clave del pedimento (no por régimen)
|
|
// Esto permite cambiar entre Exportación e Importación libremente
|
|
if (formData.pedimento_code) {
|
|
const matches = codePedimentoRegimens.filter(
|
|
(r) => r.pedimento_code === formData.pedimento_code
|
|
);
|
|
const validTypes = new Set(
|
|
matches.map((m) => typeCodeToOperationType(m.type_code)).filter((t) => t !== null)
|
|
);
|
|
return operationOptions.filter((opt) => validTypes.has(opt.value));
|
|
}
|
|
return operationOptions;
|
|
});
|
|
|
|
// Reactive synchronization between Clave, Régimen, and Tipo de Operación
|
|
// REGLA: La Clave es el campo principal y NUNCA se modifica automáticamente
|
|
// Solo se auto-llenan Régimen y Tipo de Operación basándose en la Clave
|
|
|
|
// Cuando cambia la Clave del Pedimento
|
|
$effect(() => {
|
|
if (!formData) return;
|
|
|
|
const currentCode = formData.pedimento_code;
|
|
|
|
// Detectar si la clave cambió
|
|
const codeChanged = currentCode !== previousPedimentoCode;
|
|
|
|
// Solo procesar si la clave cambió
|
|
if (!codeChanged) return;
|
|
|
|
// Actualizar el tracking
|
|
previousPedimentoCode = currentCode;
|
|
|
|
if (!currentCode) return;
|
|
|
|
const matches = codePedimentoRegimens.filter((r) => r.pedimento_code === currentCode);
|
|
|
|
if (matches.length === 0) return;
|
|
|
|
// Auto-llenar con el primer match
|
|
const firstMatch = matches[0];
|
|
if (firstMatch.regimen_code) {
|
|
formData.regime = firstMatch.regimen_code;
|
|
}
|
|
const expectedOpType = typeCodeToOperationType(firstMatch.type_code);
|
|
if (expectedOpType !== null) {
|
|
formData.operation_type = expectedOpType;
|
|
}
|
|
});
|
|
|
|
// Cuando cambia el Régimen
|
|
$effect(() => {
|
|
if (!formData) return;
|
|
|
|
const currentRegime = formData.regime;
|
|
if (!currentRegime) return;
|
|
|
|
const matches = codePedimentoRegimens.filter((r) => r.regimen_code === currentRegime);
|
|
|
|
if (matches.length === 0) return;
|
|
|
|
// Si hay clave seleccionada, solo validar (NO auto-llenar tipo de operación)
|
|
if (formData.pedimento_code) {
|
|
const exactMatch = matches.find((m) => m.pedimento_code === formData.pedimento_code);
|
|
}
|
|
// Si hay tipo de operación pero no clave, no hacer nada
|
|
// (el usuario debe seleccionar la clave primero)
|
|
});
|
|
|
|
// Cuando cambia el Tipo de Operación
|
|
// Optimizado: solo ejecutar cuando cambien los campos específicos relevantes
|
|
$effect(() => {
|
|
if (!formData) return;
|
|
|
|
// Solo rastrear los campos que realmente importan
|
|
const currentType = formData.operation_type;
|
|
const currentCode = formData.pedimento_code;
|
|
const currentRegime = formData.regime;
|
|
|
|
// Detectar si el tipo de operación cambió
|
|
const typeChanged = currentType !== previousOperationType;
|
|
previousOperationType = currentType;
|
|
|
|
// Si no hay clave seleccionada o tipo no definido, salir temprano
|
|
if (!currentCode || currentType === null || currentType === undefined) return;
|
|
|
|
const expectedTypeCode = operationTypeToTypeCode(currentType);
|
|
|
|
// Buscar matches para esta combinación de clave + tipo de operación
|
|
const matches = codePedimentoRegimens.filter(
|
|
(r) => r.pedimento_code === currentCode && r.type_code === expectedTypeCode
|
|
);
|
|
|
|
if (matches.length === 0) return;
|
|
|
|
// Obtener regímenes válidos para esta combinación
|
|
const validRegimens = new Set(
|
|
matches.map((m) => m.regimen_code).filter((code): code is string => code !== null)
|
|
);
|
|
|
|
// Si el tipo de operación cambió O el régimen actual no es válido, actualizar el régimen
|
|
if (typeChanged || !currentRegime || !validRegimens.has(currentRegime)) {
|
|
const firstMatch = matches[0];
|
|
if (firstMatch?.regimen_code) {
|
|
formData.regime = firstMatch.regimen_code;
|
|
}
|
|
}
|
|
});
|
|
|
|
// --- Inicialización ---
|
|
const currentYear = getCurrentLocalYear();
|
|
const currentDate = getCurrentLocalDate();
|
|
const currentTime = getCurrentLocalTime();
|
|
|
|
const end_date = addDaysLocal(currentDate, 4);
|
|
const payment_date = addDaysLocal(end_date, 2);
|
|
|
|
// Inicializar formData con los valores del pedimento (o vacío si es null)
|
|
if (!formData) {
|
|
const datesData = pedimento?.pedimento_dates;
|
|
const incrementablesData = pedimento?.pedimento_incrementables;
|
|
const decrementablesData = pedimento?.pedimento_decrementables;
|
|
const indexesData = pedimento?.pedimento_indexes;
|
|
const configAdditionalData = pedimento?.pedimento_config_additional;
|
|
|
|
formData = {
|
|
year: pedimento?.year || currentYear,
|
|
customs_office: pedimento?.customs_office || '',
|
|
license: pedimento?.license || '',
|
|
pedimento_number: pedimento?.pedimento_number || '',
|
|
client_id: pedimento?.client_id ?? null,
|
|
operation_type: pedimento?.operation_type ?? null,
|
|
pedimento_type: pedimento?.pedimento_type || 'consolidated',
|
|
pedimento_code: pedimento?.pedimento_code || '',
|
|
regime: pedimento?.regime || '',
|
|
status: pedimento?.status || 'MODIFICABLE',
|
|
usd_value: pedimento?.usd_value ?? null,
|
|
paid_price: pedimento?.paid_price ?? null,
|
|
gross_weight: pedimento?.gross_weight ?? null,
|
|
exchange_rate: pedimento?.exchange_rate ?? null,
|
|
// Date fields - Cargamos convirtiendo a local para el usuario
|
|
entry_date: loadServerDate(datesData?.entry_date) || currentDate,
|
|
end_date: loadServerDate(datesData?.end_date) || end_date,
|
|
payment_date: loadServerDate(datesData?.payment_date) || payment_date,
|
|
extraction_date: loadServerDate(datesData?.entry_date) || null,
|
|
original_date: loadServerDate(datesData?.original_date) || null,
|
|
// Mapeos de sub-recursos
|
|
pedimento_customs_offices: {
|
|
dispatch_customs: pedimento?.pedimento_customs_offices?.dispatch_customs || '',
|
|
entry_exit_customs: pedimento?.pedimento_customs_offices?.entry_exit_customs || ''
|
|
},
|
|
pedimento_transport_means: {
|
|
destination: pedimento?.pedimento_transport_means?.destination || null,
|
|
entry_exit: pedimento?.pedimento_transport_means?.entry_exit || '',
|
|
arrival: pedimento?.pedimento_transport_means?.arrival || '',
|
|
departure: pedimento?.pedimento_transport_means?.departure || ''
|
|
},
|
|
// Campos de captura automática (Local)
|
|
fecha_captura: currentDate,
|
|
hora_captura: currentTime
|
|
};
|
|
}
|
|
|
|
// Flag para evitar loops en actualización de año
|
|
let yearInitialized = false;
|
|
|
|
// Asegurar que el año siempre esté actualizado con el año actual (solo una vez)
|
|
$effect(() => {
|
|
if (formData && !pedimento?.year && !yearInitialized) {
|
|
formData.year = currentYear;
|
|
yearInitialized = true;
|
|
}
|
|
});
|
|
|
|
// Track para evitar loops en obtención de tipo de cambio
|
|
let lastFetchedDate: string | null = null;
|
|
let lastCompanyId: number | null = null;
|
|
|
|
function getTransportByCode(code: string | null | undefined): PedimentoTransportCatalog | undefined {
|
|
if (!code) return undefined;
|
|
return pedimentoTransportCatalog.find((m) => m.code === code);
|
|
}
|
|
|
|
function getTransportLabel(mode: PedimentoTransportCatalog | undefined): string {
|
|
if (!mode) return '';
|
|
const locale = getLocale();
|
|
return locale === 'en' ? mode.transport_en : mode.transport_es;
|
|
}
|
|
|
|
function getEffectiveExchangeDate(): string | null {
|
|
const entryMethod = getTransportByCode(formData?.pedimento_transport_means?.entry_exit);
|
|
const paymentCode = (entryMethod?.payment_date_code || 'E').toUpperCase();
|
|
if (paymentCode === 'P') {
|
|
return formData?.payment_date || null;
|
|
}
|
|
return formData?.entry_date || null;
|
|
}
|
|
|
|
function getEffectiveDateLabel(): string {
|
|
const entryMethod = getTransportByCode(formData?.pedimento_transport_means?.entry_exit);
|
|
const paymentCode = (entryMethod?.payment_date_code || 'E').toUpperCase();
|
|
return paymentCode === 'P' ? 'fecha de pago' : 'fecha de entrada';
|
|
}
|
|
|
|
// Obtener automáticamente el tipo de cambio cuando cambie la fecha efectiva
|
|
$effect(() => {
|
|
const effectiveDate = getEffectiveExchangeDate();
|
|
const companyId = companyStore.activeCompany?.id;
|
|
|
|
// Solo ejecutar si los valores clave cambiaron
|
|
if (
|
|
formData &&
|
|
effectiveDate &&
|
|
companyId &&
|
|
(effectiveDate !== lastFetchedDate || companyId !== lastCompanyId)
|
|
) {
|
|
lastFetchedDate = effectiveDate;
|
|
lastCompanyId = companyId;
|
|
|
|
getExchangeRateByDate(effectiveDate, companyId)
|
|
.then((usdRate) => {
|
|
if (usdRate && formData) {
|
|
formData.exchange_rate = usdRate.value;
|
|
} else {
|
|
console.warn('⚠️ [TIPO CAMBIO] No encontrado para fecha:', effectiveDate);
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
console.error('❌ [TIPO CAMBIO] Error:', err);
|
|
});
|
|
}
|
|
});
|
|
|
|
const pedimentoTypeOptions = [
|
|
{ value: 'automobile', label: 'Automóvil' },
|
|
{ value: 'complementary', label: 'Complementario' },
|
|
{ value: 'consolidated', label: 'Consolidado' },
|
|
{ value: 'normal', label: 'Normal (Individual)' }
|
|
];
|
|
|
|
const tipoOperacionOptions = [
|
|
{ value: 'incrementables', label: 'Incrementables' },
|
|
{ value: 'identificadores', label: 'Identificadores' },
|
|
{ value: 'indices', label: 'Indices' },
|
|
{ value: 'adicional', label: 'Adicional' },
|
|
{ value: 'decrementable', label: 'Decrementable' }
|
|
];
|
|
|
|
export async function checkPaymentDateRate(date: string): Promise<boolean> {
|
|
const effectiveDate = date || getEffectiveExchangeDate();
|
|
if (!effectiveDate || !companyStore.activeCompany?.id) return true;
|
|
|
|
try {
|
|
const rate = await getExchangeRateByDate(effectiveDate, companyStore.activeCompany.id);
|
|
if (!rate) {
|
|
// Abrir modal preventivamente
|
|
missingExchangeRateDate = effectiveDate;
|
|
showExchangeRateDialog = true;
|
|
return false;
|
|
}
|
|
return true;
|
|
} catch (error) {
|
|
console.error('Error checking payment date rate:', error);
|
|
// Si hay error de red, asumimos que falta para forzar reintento/captura segura
|
|
missingExchangeRateDate = effectiveDate;
|
|
showExchangeRateDialog = true;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export function openExchangeRateDialog(date: string) {
|
|
missingExchangeRateDate = date;
|
|
showExchangeRateDialog = true;
|
|
}
|
|
</script>
|
|
|
|
<Card.Root>
|
|
<Card.Header>
|
|
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
|
<div class="space-y-1">
|
|
<Card.Title>Información General</Card.Title>
|
|
<Card.Description>Edita los datos principales del pedimento</Card.Description>
|
|
</div>
|
|
|
|
<!-- Captura Info Badge -->
|
|
<div
|
|
class="flex flex-row items-center gap-4 rounded-md border border-border/40 bg-secondary/50 px-3 py-2 text-sm text-muted-foreground"
|
|
>
|
|
<div class="flex items-center gap-2" title="Fecha de Captura">
|
|
<Calendar class="h-4 w-4 text-primary/70" />
|
|
<span class="font-medium tabular-nums">{formData.fecha_captura}</span>
|
|
</div>
|
|
<div class="h-4 w-px bg-border"></div>
|
|
<div class="flex items-center gap-2" title="Hora de Captura">
|
|
<Clock class="h-4 w-4 text-primary/70" />
|
|
<span class="font-medium tabular-nums">{formData.hora_captura}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Card.Header>
|
|
<Card.Content>
|
|
<div class="space-y-6">
|
|
<div
|
|
class="grid grid-cols-1 gap-4 md:grid-cols-[auto_auto_auto_auto_auto_auto_auto_auto_auto_1fr_auto] md:items-end md:gap-2"
|
|
>
|
|
<!-- Año -->
|
|
<div class="space-y-2">
|
|
<Label for="year">Año <span class="text-red-500">*</span></Label>
|
|
<Input
|
|
id="year"
|
|
bind:value={formData.year}
|
|
placeholder="25"
|
|
maxlength={4}
|
|
class="text-center md:w-20"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Separador -->
|
|
<div class="hidden pb-2 text-2xl font-semibold text-muted-foreground md:block">-</div>
|
|
|
|
<!-- Aduana -->
|
|
<div class="space-y-2">
|
|
<Label for="customs_office">Aduana <span class="text-red-500">*</span></Label>
|
|
<Select.Root
|
|
type="single"
|
|
value={formData.customs_office || ''}
|
|
onValueChange={(v: string) => (formData.customs_office = v ?? '')}
|
|
>
|
|
<Select.Trigger class="w-full md:w-20">
|
|
<span class="truncate">
|
|
{formData.customs_office ? formData.customs_office.substring(0, 2) : 'Sel...'}
|
|
</span>
|
|
</Select.Trigger>
|
|
<Select.Content class="max-h-[300px] max-w-[300px]">
|
|
{#if customsSections.length === 0}
|
|
<div class="px-2 py-1.5 text-sm text-muted-foreground">
|
|
No hay aduanas disponibles
|
|
</div>
|
|
{:else}
|
|
{#each customsSections as section}
|
|
<Select.Item value={section.customs_code}>
|
|
<span
|
|
class="truncate overflow-hidden text-ellipsis whitespace-nowrap"
|
|
title={`${section.customs_code} - ${section.section_name}`}
|
|
>
|
|
{section.customs_code} - {section.section_name}
|
|
</span>
|
|
</Select.Item>
|
|
{/each}
|
|
{/if}
|
|
</Select.Content>
|
|
</Select.Root>
|
|
</div>
|
|
|
|
<!-- Separador -->
|
|
<div class="hidden pb-2 text-2xl font-semibold text-muted-foreground md:block">-</div>
|
|
|
|
<!-- Patente -->
|
|
<div class="space-y-2">
|
|
<Label for="license">Patente <span class="text-red-500">*</span></Label>
|
|
<Select.Root
|
|
type="single"
|
|
value={formData.license || ''}
|
|
onValueChange={(v: string | undefined) => (formData.license = v || null)}
|
|
>
|
|
<Select.Trigger class="w-full md:w-24">
|
|
<span class="truncate">
|
|
{formData.license || 'Sel...'}
|
|
</span>
|
|
</Select.Trigger>
|
|
<Select.Content class="max-h-[300px] max-w-[300px]">
|
|
{@const validBrokers = customsBrokers.filter((b) => b.license)}
|
|
{#if customsBrokers.length === 0}
|
|
<div class="px-2 py-1.5 text-sm text-muted-foreground">
|
|
No hay agentes aduanales registrados
|
|
</div>
|
|
{:else if validBrokers.length === 0}
|
|
<div class="px-2 py-1.5 text-sm text-muted-foreground">
|
|
No hay patentes válidas. Edita el agente aduanal para agregar su patente.
|
|
</div>
|
|
{:else}
|
|
{#each validBrokers as broker}
|
|
{@const displayName = broker.name || broker.broker_key || ''}
|
|
<Select.Item value={broker.license}>
|
|
<span
|
|
class="truncate overflow-hidden text-ellipsis whitespace-nowrap"
|
|
title={displayName ? `${displayName} - ${broker.license}` : broker.license}
|
|
>
|
|
{displayName ? `${displayName} - ${broker.license}` : broker.license}
|
|
</span>
|
|
</Select.Item>
|
|
{/each}
|
|
{/if}
|
|
</Select.Content>
|
|
</Select.Root>
|
|
</div>
|
|
|
|
<!-- Separador -->
|
|
<div class="hidden pb-2 text-2xl font-semibold text-muted-foreground md:block">-</div>
|
|
|
|
<!-- Número de Pedimento -->
|
|
<div class="space-y-2">
|
|
<Label for="pedimento_number"
|
|
>Número de Pedimento <span class="text-red-500">*</span></Label
|
|
>
|
|
<Input
|
|
id="pedimento_number"
|
|
bind:value={formData.pedimento_number}
|
|
placeholder="0000001"
|
|
maxlength={7}
|
|
class="text-left"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Tipo de Cambio -->
|
|
<div class="space-y-2">
|
|
<Label for="exchange_rate">Tipo de Cambio <span class="text-red-500">*</span></Label>
|
|
<Input
|
|
id="exchange_rate"
|
|
type="text"
|
|
value={formData.exchange_rate ? Number(formData.exchange_rate).toFixed(6) : ''}
|
|
placeholder={`Se obtiene automáticamente de la ${getEffectiveDateLabel()}`}
|
|
readonly
|
|
disabled
|
|
class="cursor-not-allowed bg-muted"
|
|
/>
|
|
<p class="text-xs text-muted-foreground">
|
|
Tipo de fecha para TC: {getEffectiveDateLabel() === 'fecha de pago'
|
|
? 'FECHA PAGO'
|
|
: 'FECHA ENTRADA'}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-3 md:gap-2">
|
|
<!-- Clave del Pedimento -->
|
|
<div class="space-y-2">
|
|
<Label for="pedimento_code">Clave <span class="text-red-500">*</span></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-h-[300px] max-w-[400px]">
|
|
{#each pedimentoCodes as code}
|
|
<Select.Item value={code.code}>
|
|
<div class="flex items-center gap-2">
|
|
<span class="font-medium">{code.code}</span>
|
|
<span class="text-muted-foreground">-</span>
|
|
<span class="flex-1 truncate">{code.description}</span>
|
|
</div>
|
|
</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 <span class="text-red-500">*</span></Label>
|
|
<Select.Root
|
|
type="single"
|
|
value={formData.operation_type || ''}
|
|
onValueChange={(v: string) => (formData.operation_type = 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={option.value} label={option.label} />
|
|
{/each}
|
|
</Select.Content>
|
|
</Select.Root>
|
|
</div>
|
|
|
|
<!-- Régimen -->
|
|
<div class="space-y-2">
|
|
<Label for="regime">Régimen <span class="text-red-500">*</span></Label>
|
|
{#if hasMultipleRegimens}
|
|
<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>
|
|
{:else}
|
|
<Input
|
|
id="regime"
|
|
bind:value={formData.regime}
|
|
placeholder="Automático"
|
|
class="text-left"
|
|
readonly
|
|
disabled
|
|
/>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
<!-- ID del Cliente (opcional) -->
|
|
<div class="space-y-2">
|
|
<Label for="client_id">Cliente</Label>
|
|
<Select.Root
|
|
type="single"
|
|
value={String(formData.client_id ?? '')}
|
|
onValueChange={(v: string) => (formData.client_id = v ? Number(v) : null)}
|
|
>
|
|
<Select.Trigger class="w-full">
|
|
<span class="truncate">
|
|
{clients.find((c) => c.id === formData.client_id)?.name || 'Seleccionar cliente...'}
|
|
</span>
|
|
</Select.Trigger>
|
|
<Select.Content class="max-h-[300px]">
|
|
{#if clients.length === 0}
|
|
<div class="px-2 py-1.5 text-sm text-muted-foreground">
|
|
No hay clientes disponibles
|
|
</div>
|
|
{:else}
|
|
{#each clients as client}
|
|
<Select.Item value={String(client.id)} label={client.name}>
|
|
<span
|
|
class="truncate overflow-hidden text-ellipsis whitespace-nowrap"
|
|
title={client.name}
|
|
>
|
|
{client.name}
|
|
</span>
|
|
</Select.Item>
|
|
{/each}
|
|
{/if}
|
|
</Select.Content>
|
|
</Select.Root>
|
|
</div>
|
|
|
|
<!-- Tipo de Pedimento -->
|
|
<div class="space-y-2">
|
|
<Label for="pedimento_type">Tipo de Pedimento <span class="text-red-500">*</span></Label>
|
|
<Select.Root
|
|
type="single"
|
|
value={formData.pedimento_type || ''}
|
|
onValueChange={(v: string) => (formData.pedimento_type = v ?? '')}
|
|
>
|
|
<Select.Trigger class="w-full">
|
|
{pedimentoTypeOptions.find((o) => o.value === formData.pedimento_type)?.label ||
|
|
'Seleccionar...'}
|
|
</Select.Trigger>
|
|
<Select.Content>
|
|
{#each pedimentoTypeOptions as option}
|
|
<Select.Item value={option.value} label={option.label} />
|
|
{/each}
|
|
</Select.Content>
|
|
</Select.Root>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
<!-- Valor USD -->
|
|
<div class="space-y-2">
|
|
<Label for="usd_value">Valor USD</Label>
|
|
<Input
|
|
id="usd_value"
|
|
type="number"
|
|
step="0.01"
|
|
bind:value={formData.usd_value}
|
|
placeholder="Ej: 1000.00"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Precio Pagado -->
|
|
<div class="space-y-2">
|
|
<Label for="paid_price">Precio Pagado</Label>
|
|
<Input
|
|
id="paid_price"
|
|
type="number"
|
|
step="0.01"
|
|
bind:value={formData.paid_price}
|
|
placeholder="Ej: 1000.00"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Peso Bruto -->
|
|
<div class="space-y-2">
|
|
<Label for="gross_weight">Peso Bruto (Kg)</Label>
|
|
<Input
|
|
id="gross_weight"
|
|
type="number"
|
|
step="0.01"
|
|
bind:value={formData.gross_weight}
|
|
placeholder="Ej: 100.00"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Despacho -->
|
|
<div class="space-y-2">
|
|
<Label for="despacho">Despacho</Label>
|
|
<Select.Root
|
|
type="single"
|
|
value={formData.pedimento_customs_offices.dispatch_customs || ''}
|
|
onValueChange={(v: string) =>
|
|
(formData.pedimento_customs_offices.dispatch_customs = v || '')}
|
|
>
|
|
<Select.Trigger id="despacho" class="w-full">
|
|
{formData.pedimento_customs_offices.dispatch_customs || 'Sel...'}
|
|
</Select.Trigger>
|
|
<Select.Content>
|
|
{#each customsSections.filter((s) => !formData.customs_office || s.customs_code.startsWith(formData.customs_office.substring(0, 2))) as section}
|
|
<Select.Item value={section.customs_code}>
|
|
{section.customs_code} - {section.section_name}
|
|
</Select.Item>
|
|
{/each}
|
|
</Select.Content>
|
|
</Select.Root>
|
|
</div>
|
|
|
|
<!-- Destino -->
|
|
<div class="space-y-2">
|
|
<Label for="destino">Destino <span class="text-red-500">*</span></Label>
|
|
<Select.Root
|
|
type="single"
|
|
value={formData.pedimento_transport_means.destination
|
|
? String(formData.pedimento_transport_means.destination)
|
|
: ''}
|
|
onValueChange={(v: string) =>
|
|
(formData.pedimento_transport_means.destination = v ? Number(v) : null)}
|
|
>
|
|
<Select.Trigger id="destino" class="w-full">
|
|
{formData.pedimento_transport_means.destination === 1
|
|
? '1 - REGION FRONTERIZA'
|
|
: formData.pedimento_transport_means.destination === 8
|
|
? '8 - RECTIFICACION REGION FRONTERIZA'
|
|
: formData.pedimento_transport_means.destination === 9
|
|
? '9 - INTERIOR DEL PAIS'
|
|
: 'Seleccionar...'}
|
|
</Select.Trigger>
|
|
<Select.Content>
|
|
<Select.Item value="1">1 - REGION FRONTERIZA</Select.Item>
|
|
<Select.Item value="8">8 - RECTIFICACION REGION FRONTERIZA</Select.Item>
|
|
<Select.Item value="9">9 - INTERIOR DEL PAIS</Select.Item>
|
|
</Select.Content>
|
|
</Select.Root>
|
|
</div>
|
|
|
|
<!-- Aduana E/S -->
|
|
<div class="space-y-2">
|
|
<Label for="entry_exit_customs">Aduana E/S</Label>
|
|
<Input
|
|
id="entry_exit_customs"
|
|
type="text"
|
|
maxlength={3}
|
|
bind:value={formData.pedimento_customs_offices.entry_exit_customs}
|
|
placeholder="000"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Método de Entrada/Salida -->
|
|
<div class="space-y-2">
|
|
<Label for="entry_exit">Método de Entrada</Label>
|
|
<Select.Root
|
|
type="single"
|
|
value={formData.pedimento_transport_means.entry_exit || ''}
|
|
onValueChange={(v: string) => (formData.pedimento_transport_means.entry_exit = v || '')}
|
|
>
|
|
<Select.Trigger id="entry_exit" class="w-full">
|
|
<span class="truncate">
|
|
{getTransportLabel(
|
|
getTransportByCode(formData.pedimento_transport_means.entry_exit)
|
|
) ||
|
|
formData.pedimento_transport_means.entry_exit ||
|
|
'Seleccionar...'}
|
|
</span>
|
|
</Select.Trigger>
|
|
<Select.Content class="max-h-[300px]">
|
|
{#each pedimentoTransportCatalog as mode}
|
|
<Select.Item value={mode.code}>{mode.code} - {getTransportLabel(mode)}</Select.Item>
|
|
{/each}
|
|
</Select.Content>
|
|
</Select.Root>
|
|
</div>
|
|
|
|
<!-- Arribo -->
|
|
<div class="space-y-2">
|
|
<Label for="arrival">Arribo</Label>
|
|
<Select.Root
|
|
type="single"
|
|
value={formData.pedimento_transport_means.arrival || ''}
|
|
onValueChange={(v: string) => (formData.pedimento_transport_means.arrival = v || '')}
|
|
>
|
|
<Select.Trigger id="arrival" class="w-full">
|
|
<span class="truncate">
|
|
{getTransportLabel(getTransportByCode(formData.pedimento_transport_means.arrival)) ||
|
|
formData.pedimento_transport_means.arrival ||
|
|
'Seleccionar...'}
|
|
</span>
|
|
</Select.Trigger>
|
|
<Select.Content class="max-h-[300px]">
|
|
{#each pedimentoTransportCatalog as mode}
|
|
<Select.Item value={mode.code}>{mode.code} - {getTransportLabel(mode)}</Select.Item>
|
|
{/each}
|
|
</Select.Content>
|
|
</Select.Root>
|
|
</div>
|
|
|
|
<!-- Salida -->
|
|
<div class="space-y-2">
|
|
<Label for="departure">Salida</Label>
|
|
<Select.Root
|
|
type="single"
|
|
value={formData.pedimento_transport_means.departure || ''}
|
|
onValueChange={(v: string) => (formData.pedimento_transport_means.departure = v || '')}
|
|
>
|
|
<Select.Trigger id="departure" class="w-full">
|
|
<span class="truncate">
|
|
{getTransportLabel(getTransportByCode(formData.pedimento_transport_means.departure)) ||
|
|
formData.pedimento_transport_means.departure ||
|
|
'Seleccionar...'}
|
|
</span>
|
|
</Select.Trigger>
|
|
<Select.Content class="max-h-[300px]">
|
|
{#each pedimentoTransportCatalog as mode}
|
|
<Select.Item value={mode.code}>{mode.code} - {getTransportLabel(mode)}</Select.Item>
|
|
{/each}
|
|
</Select.Content>
|
|
</Select.Root>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Lista de navegación rápida -->
|
|
<div class="w-full overflow-x-auto pb-2">
|
|
<div
|
|
class="inline-flex rounded-md bg-muted p-1 text-muted-foreground md:grid md:w-full md:grid-cols-6"
|
|
>
|
|
<button
|
|
type="button"
|
|
onclick={() => (activeSection = 'fechas')}
|
|
data-state={activeSection === 'fechas' ? 'active' : ''}
|
|
class="inline-flex cursor-pointer items-center justify-center rounded-sm px-3 py-1.5 text-sm font-medium whitespace-nowrap ring-offset-background transition-all hover:bg-background/50 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-none data-[state=active]:border data-[state=active]:border-white/60 data-[state=active]:text-foreground"
|
|
>
|
|
Fechas
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onclick={() => (activeSection = 'incrementables')}
|
|
data-state={activeSection === 'incrementables' ? 'active' : ''}
|
|
class="inline-flex cursor-pointer items-center justify-center rounded-sm px-3 py-1.5 text-sm font-medium whitespace-nowrap ring-offset-background transition-all hover:bg-background/50 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-none data-[state=active]:border data-[state=active]:border-white/60 data-[state=active]:text-foreground"
|
|
>
|
|
Incrementables
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onclick={() => (activeSection = 'identificadores')}
|
|
data-state={activeSection === 'identificadores' ? 'active' : ''}
|
|
class="inline-flex cursor-pointer items-center justify-center rounded-sm px-3 py-1.5 text-sm font-medium whitespace-nowrap ring-offset-background transition-all hover:bg-background/50 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-none data-[state=active]:border data-[state=active]:border-white/60 data-[state=active]:text-foreground"
|
|
>
|
|
Identificadores
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onclick={() => (activeSection = 'indices')}
|
|
data-state={activeSection === 'indices' ? 'active' : ''}
|
|
class="inline-flex cursor-pointer items-center justify-center rounded-sm px-3 py-1.5 text-sm font-medium whitespace-nowrap ring-offset-background transition-all hover:bg-background/50 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-none data-[state=active]:border data-[state=active]:border-white/60 data-[state=active]:text-foreground"
|
|
>
|
|
Indices
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onclick={() => (activeSection = 'adicional')}
|
|
data-state={activeSection === 'adicional' ? 'active' : ''}
|
|
class="inline-flex cursor-pointer items-center justify-center rounded-sm px-3 py-1.5 text-sm font-medium whitespace-nowrap ring-offset-background transition-all hover:bg-background/50 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-none data-[state=active]:border data-[state=active]:border-white/60 data-[state=active]:text-foreground"
|
|
>
|
|
Adicional
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onclick={() => (activeSection = 'decrementable')}
|
|
data-state={activeSection === 'decrementable' ? 'active' : ''}
|
|
class="inline-flex cursor-pointer items-center justify-center rounded-sm px-3 py-1.5 text-sm font-medium whitespace-nowrap ring-offset-background transition-all hover:bg-background/50 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-none data-[state=active]:border data-[state=active]:border-white/60 data-[state=active]:text-foreground"
|
|
>
|
|
Decrementable
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Contenido dinámico según la sección activa -->
|
|
{#if activeSection === 'fechas'}
|
|
<!-- Fechas -->
|
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
<!-- Fecha de Entrada -->
|
|
<div class="space-y-2">
|
|
<Label for="entry_date">Fecha de Entrada <span class="text-red-500">*</span></Label>
|
|
<Input id="entry_date" type="date" bind:value={formData.entry_date} />
|
|
</div>
|
|
|
|
<!-- Fecha Fin (Solo Consolidado) -->
|
|
{#if formData.pedimento_type === 'consolidated'}
|
|
<div class="space-y-2">
|
|
<Label for="end_date">Fecha Fin <span class="text-red-500">*</span></Label>
|
|
<Input id="end_date" type="date" bind:value={formData.end_date} />
|
|
</div>
|
|
{/if}
|
|
|
|
<!-- Fecha de Pago -->
|
|
<div class="space-y-2">
|
|
<Label for="payment_date">Fecha de Pago <span class="text-red-500">*</span></Label>
|
|
<Input id="payment_date" type="date" bind:value={formData.payment_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 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>
|
|
|
|
{#if formData.pedimento_code === 'R1'}
|
|
<!-- 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>
|
|
{/if}
|
|
|
|
<!-- 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>
|
|
</div>
|
|
{:else if activeSection === 'incrementables'}
|
|
<!-- Incrementables -->
|
|
<div class="space-y-4">
|
|
<!-- Factor informativo -->
|
|
<div class="text-sm font-medium">Factor: 1.00000</div>
|
|
|
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
<!-- Valor Seguro -->
|
|
<div class="space-y-2">
|
|
<Label for="valor_seguro">Valor Seguro</Label>
|
|
<Input
|
|
id="valor_seguro"
|
|
type="number"
|
|
bind:value={formData.valor_seguro}
|
|
placeholder="0.00"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Embalajes -->
|
|
<div class="space-y-2">
|
|
<Label for="embalajes">Embalajes</Label>
|
|
<Input
|
|
id="embalajes"
|
|
type="number"
|
|
bind:value={formData.embalajes}
|
|
placeholder="0.00"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Fletes -->
|
|
<div class="space-y-2">
|
|
<Label for="fletes">Fletes</Label>
|
|
<Input id="fletes" type="number" bind:value={formData.fletes} placeholder="0.00" />
|
|
</div>
|
|
|
|
<!-- Deducibles -->
|
|
<div class="space-y-2">
|
|
<Label for="deducibles">Deducibles</Label>
|
|
<Input
|
|
id="deducibles"
|
|
type="number"
|
|
bind:value={formData.deducibles}
|
|
placeholder="0.00"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Moneda -->
|
|
<div class="space-y-2">
|
|
<Label for="moneda_incrementables">Moneda</Label>
|
|
<Input
|
|
id="moneda_incrementables"
|
|
type="text"
|
|
maxlength={3}
|
|
bind:value={formData.moneda_incrementables}
|
|
placeholder="USD"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Checkboxes agrupados -->
|
|
<div class="space-y-2">
|
|
<Label class="invisible">Opciones</Label>
|
|
<div class="space-y-2">
|
|
<div class="flex items-center space-x-2">
|
|
<input
|
|
id="no_afectar_valor_dolares_inc"
|
|
type="checkbox"
|
|
bind:checked={formData.no_afectar_valor_dolares_inc}
|
|
class="h-4 w-4 rounded border-gray-300 text-primary focus:ring-0"
|
|
/>
|
|
<Label for="no_afectar_valor_dolares_inc" class="!m-0 cursor-pointer">
|
|
No Afectar Valor en Dólares del Pedimento
|
|
</Label>
|
|
</div>
|
|
<div class="flex items-center space-x-2">
|
|
<input
|
|
id="no_afectar_valor_aduana"
|
|
type="checkbox"
|
|
bind:checked={formData.no_afectar_valor_aduana}
|
|
class="h-4 w-4 rounded border-gray-300 text-primary focus:ring-0"
|
|
/>
|
|
<Label for="no_afectar_valor_aduana" class="!m-0 cursor-pointer">
|
|
No Afectar Valor Aduana del Pedimento
|
|
</Label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{:else if activeSection === 'identificadores'}
|
|
<!-- Identificadores -->
|
|
<IdentificadoresTabForm bind:formData={identificadoresFormData} />
|
|
{:else if activeSection === 'indices'}
|
|
<!-- Indices -->
|
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
<!-- Tipo de Factor a Actualizar -->
|
|
<div class="space-y-2">
|
|
<Label for="tipo_factor">Tipo de Factor a Actualizar</Label>
|
|
<Select.Root
|
|
type="single"
|
|
value={formData.tipo_factor || ''}
|
|
onValueChange={(v: string) => (formData.tipo_factor = v ?? '')}
|
|
>
|
|
<Select.Trigger id="tipo_factor" class="w-full">
|
|
<span class="truncate">
|
|
{formData.tipo_factor === 'INPC'
|
|
? 'I.N.P.C'
|
|
: formData.tipo_factor === 'variacion_cambiaria'
|
|
? 'Variación Cambiaria'
|
|
: 'Seleccionar'}
|
|
</span>
|
|
</Select.Trigger>
|
|
<Select.Content>
|
|
<Select.Item value="INPC">I.N.P.C</Select.Item>
|
|
<Select.Item value="variacion_cambiaria">Variación Cambiaria</Select.Item>
|
|
</Select.Content>
|
|
</Select.Root>
|
|
</div>
|
|
|
|
<!-- Factor Actualización -->
|
|
<div class="space-y-2">
|
|
<Label for="factor_actualizacion">Factor Actualización</Label>
|
|
<Input
|
|
id="factor_actualizacion"
|
|
type="number"
|
|
bind:value={formData.factor_actualizacion}
|
|
placeholder="0.00"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Factor Actualización Manual -->
|
|
<div class="space-y-2">
|
|
<Label for="factor_actualizacion_manual" class="invisible">Checkbox</Label>
|
|
<div class="flex h-10 items-center space-x-2">
|
|
<input
|
|
id="factor_actualizacion_manual"
|
|
type="checkbox"
|
|
bind:checked={formData.factor_actualizacion_manual}
|
|
class="h-4 w-4 rounded border-gray-300 text-primary focus:ring-0"
|
|
/>
|
|
<Label for="factor_actualizacion_manual" class="!m-0 cursor-pointer">
|
|
Factor Actualización Manual
|
|
</Label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{:else if activeSection === 'adicional'}
|
|
<!-- Adicional -->
|
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
<!-- Año para la impresión del pedimento -->
|
|
<div class="space-y-2">
|
|
<Label for="anio_impresion">Año para la Impresión del Pedimento</Label>
|
|
<Input
|
|
id="anio_impresion"
|
|
type="text"
|
|
bind:value={formData.anio_impresion}
|
|
placeholder="25"
|
|
maxlength={2}
|
|
class="w-40 text-center focus-visible:ring-0 focus-visible:ring-offset-0"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Agregar Identificador PO Automáticamente -->
|
|
<div class="space-y-2">
|
|
<Label for="agregar_po_auto" class="invisible">Checkbox</Label>
|
|
<div class="flex h-10 items-center space-x-2">
|
|
<input
|
|
id="agregar_po_auto"
|
|
type="checkbox"
|
|
bind:checked={formData.agregar_po_auto}
|
|
class="h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary"
|
|
/>
|
|
<Label for="agregar_po_auto" class="!m-0 cursor-pointer">
|
|
Agregar Identificador PO Automáticamente
|
|
</Label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- No Eximir Normas ComplementoX -->
|
|
<div class="space-y-2">
|
|
<Label for="no_eximir_normas" class="invisible">Checkbox</Label>
|
|
<div class="flex h-10 items-center space-x-2">
|
|
<input
|
|
id="no_eximir_normas"
|
|
type="checkbox"
|
|
bind:checked={formData.no_eximir_normas}
|
|
class="h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary"
|
|
/>
|
|
<Label for="no_eximir_normas" class="!m-0 cursor-pointer">
|
|
No Eximir Normas ComplementoX
|
|
</Label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Activar Destinatario en Facturas -->
|
|
<div class="space-y-2">
|
|
<Label for="activar_destinatario" class="invisible">Checkbox</Label>
|
|
<div class="flex h-10 items-center space-x-2">
|
|
<input
|
|
id="activar_destinatario"
|
|
type="checkbox"
|
|
bind:checked={formData.activar_destinatario}
|
|
class="h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary"
|
|
/>
|
|
<Label for="activar_destinatario" class="!m-0 cursor-pointer">
|
|
Activar Destinatario en Facturas
|
|
</Label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Agregar Registro 502 en Archivo de Validación (Consolidados) -->
|
|
<div class="space-y-2">
|
|
<Label for="agregar_registro_502" class="invisible">Checkbox</Label>
|
|
<div class="flex h-10 items-center space-x-2">
|
|
<input
|
|
id="agregar_registro_502"
|
|
type="checkbox"
|
|
bind:checked={formData.agregar_registro_502}
|
|
class="h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary"
|
|
/>
|
|
<Label for="agregar_registro_502" class="!m-0 cursor-pointer">
|
|
Agregar Registro 502 en Archivo de Validación (Consolidados)
|
|
</Label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Agregar y Quitar Normas -->
|
|
<div class="space-y-2">
|
|
<Label for="agregar_quitar_normas" class="invisible">Checkbox</Label>
|
|
<div class="flex h-10 items-center space-x-2">
|
|
<input
|
|
id="agregar_quitar_normas"
|
|
type="checkbox"
|
|
bind:checked={formData.agregar_quitar_normas}
|
|
class="h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary"
|
|
/>
|
|
<Label for="agregar_quitar_normas" class="!m-0 cursor-pointer">
|
|
Agregar y Quitar Normas
|
|
</Label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Elegir Facturar con COVE en Partidas -->
|
|
<div class="space-y-2">
|
|
<Label for="facturar_cove" class="invisible">Checkbox</Label>
|
|
<div class="flex h-10 items-center space-x-2">
|
|
<input
|
|
id="facturar_cove"
|
|
type="checkbox"
|
|
bind:checked={formData.facturar_cove}
|
|
class="h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary"
|
|
/>
|
|
<Label for="facturar_cove" class="!m-0 cursor-pointer">
|
|
Elegir Facturar con COVE en Partidas
|
|
</Label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{:else if activeSection === 'decrementable'}
|
|
<!-- Decrementable -->
|
|
<div class="space-y-4">
|
|
<!-- Factor informativo -->
|
|
<div class="text-sm font-medium">Factor: 0.05000</div>
|
|
|
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
<!-- Fletes -->
|
|
<div class="space-y-2">
|
|
<Label for="fletes_decrementable">Fletes</Label>
|
|
<Input
|
|
id="fletes_decrementable"
|
|
type="number"
|
|
bind:value={formData.fletes_decrementable}
|
|
placeholder="0.00"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Seguros -->
|
|
<div class="space-y-2">
|
|
<Label for="seguros">Seguros</Label>
|
|
<Input id="seguros" type="number" bind:value={formData.seguros} placeholder="0.00" />
|
|
</div>
|
|
|
|
<!-- Carga -->
|
|
<div class="space-y-2">
|
|
<Label for="carga">Carga</Label>
|
|
<Input id="carga" type="number" bind:value={formData.carga} placeholder="0.00" />
|
|
</div>
|
|
|
|
<!-- Descarga -->
|
|
<div class="space-y-2">
|
|
<Label for="descarga">Descarga</Label>
|
|
<Input
|
|
id="descarga"
|
|
type="number"
|
|
bind:value={formData.descarga}
|
|
placeholder="0.00"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Otros -->
|
|
<div class="space-y-2">
|
|
<Label for="otros">Otros</Label>
|
|
<Input id="otros" type="number" bind:value={formData.otros} placeholder="0.00" />
|
|
</div>
|
|
|
|
<!-- Moneda -->
|
|
<div class="space-y-2">
|
|
<Label for="moneda_decrementable">Moneda</Label>
|
|
<Input
|
|
id="moneda_decrementable"
|
|
type="text"
|
|
bind:value={formData.moneda_decrementable}
|
|
placeholder="USD"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Afectar Valor en Dólares del Pedimento -->
|
|
<div class="space-y-2">
|
|
<Label for="afectar_valor_dolares" class="invisible">Checkbox</Label>
|
|
<div class="flex h-10 items-center space-x-2">
|
|
<input
|
|
id="afectar_valor_dolares"
|
|
type="checkbox"
|
|
bind:checked={formData.afectar_valor_dolares}
|
|
class="h-4 w-4 rounded border-gray-300 text-primary focus:ring-0"
|
|
/>
|
|
<Label for="afectar_valor_dolares" class="!m-0 cursor-pointer">
|
|
Afectar Valor en Dólares del Pedimento
|
|
</Label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</Card.Content>
|
|
</Card.Root>
|
|
|
|
<ExchangeRateDialog
|
|
bind:open={showExchangeRateDialog}
|
|
initialDate={missingExchangeRateDate}
|
|
overlayClass="bg-black/20"
|
|
onSuccess={() => {
|
|
/* Optional: maybe refresh something or just let user continue */
|
|
}}
|
|
/>
|