feat: add partidas tab form component with CRUD functionality
- Implemented the partidas tab form in Svelte for managing partidas. - Added functionality to create, edit, delete, and list partidas. - Integrated a dialog for adding and editing partidas with validation. - Included a checkbox component for selecting partidas. - Added total calculations for partidas in the UI. - Enhanced UI with buttons for various actions and a responsive table layout.
This commit is contained in:
@@ -14,7 +14,7 @@ router = TenantCRUDRoutes(
|
||||
resource_name="Exchange Rate",
|
||||
id_name="id", # Using numeric ID
|
||||
enable_list=True, # Enable GET /exchange-rate with pagination
|
||||
enable_filters=True, # Enable filtering by date, local_currency, foreign_currency
|
||||
enable_filters=False,
|
||||
default_page_size=50,
|
||||
max_page_size=100,
|
||||
).router
|
||||
|
||||
@@ -24,16 +24,20 @@ export interface ExchangeRateListResponse {
|
||||
/**
|
||||
* Get exchange rate by date
|
||||
*/
|
||||
export async function getExchangeRateByDate(date: string): Promise<ExchangeRate | null> {
|
||||
export async function getExchangeRateByDate(date: string, companyId: number): Promise<ExchangeRate | null> {
|
||||
try {
|
||||
// Convert date to ISO format with time for backend datetime field
|
||||
const dateWithTime = `${date}T00:00:00`;
|
||||
const response = await api.get<ExchangeRateListResponse>(`/v1/a76/exchange-rate?date=${dateWithTime}`);
|
||||
// Get all exchange rates and filter by date on client side
|
||||
const response = await api.get<ExchangeRateListResponse>(`/v1/a76/exchange-rate/?company_id=${companyId}`);
|
||||
|
||||
if (response.data && response.data.items && response.data.items.length > 0) {
|
||||
// Find USD exchange rate
|
||||
const usdRate = response.data.items.find(rate => rate.foreign_currency === 'USD');
|
||||
return usdRate || null;
|
||||
// Filter by date and find USD exchange rate
|
||||
const dateOnly = date.split('T')[0]; // Get YYYY-MM-DD part
|
||||
const matchingRates = response.data.items.filter(rate => {
|
||||
const rateDate = rate.date.split('T')[0];
|
||||
return rateDate === dateOnly && rate.foreign_currency === 'USD';
|
||||
});
|
||||
|
||||
return matchingRates.length > 0 ? matchingRates[0] : null;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -50,6 +50,41 @@ export interface PedimentoValidation {
|
||||
responsible_id?: number | null;
|
||||
}
|
||||
|
||||
export interface PedimentoIncrementables {
|
||||
insured_value?: number | null;
|
||||
packaging?: number | null;
|
||||
freight?: number | null;
|
||||
deductibles?: number | null;
|
||||
currency?: string | null;
|
||||
not_affect_usd_value?: number | null;
|
||||
not_affect_customs_value?: number | null;
|
||||
}
|
||||
|
||||
export interface PedimentoDecrementables {
|
||||
freight?: number | null;
|
||||
insurance?: number | null;
|
||||
loading?: number | null;
|
||||
unloading?: number | null;
|
||||
others?: number | null;
|
||||
currency?: string | null;
|
||||
not_affect_usd_value?: number | null;
|
||||
}
|
||||
|
||||
export interface PedimentoIndexes {
|
||||
update_factor_type?: number | null;
|
||||
update_factor?: number | null;
|
||||
manual_update_factor?: number | null;
|
||||
}
|
||||
|
||||
export interface PedimentoConfigAdditional {
|
||||
manual_pedimento_year?: string | null;
|
||||
add_po_identifier?: number | null;
|
||||
do_not_exempt_norms_complement_x?: number | null;
|
||||
enable_import_invoice_recipient?: number | null;
|
||||
send_502_validation_file_for_consolidated?: number | null;
|
||||
add_remove_norms?: number | null;
|
||||
}
|
||||
|
||||
export interface Pedimento {
|
||||
id: number;
|
||||
tenant_id: number;
|
||||
@@ -67,12 +102,17 @@ export interface Pedimento {
|
||||
paid_price?: number | null;
|
||||
gross_weight?: number | null;
|
||||
exchange_rate?: number | null;
|
||||
observaciones?: string | null;
|
||||
created_at: string;
|
||||
// Sub-resources
|
||||
pedimento_dates?: PedimentoDates | null;
|
||||
pedimento_payments?: PedimentoPayments | null;
|
||||
pedimento_transport_means?: PedimentoTransportMeans | null;
|
||||
pedimento_validation?: PedimentoValidation | null;
|
||||
pedimento_validation?: PedimentoValidation | null;
|
||||
pedimento_incrementables?: PedimentoIncrementables | null;
|
||||
pedimento_decrementables?: PedimentoDecrementables | null;
|
||||
pedimento_indexes?: PedimentoIndexes | null;
|
||||
pedimento_config_additional?: PedimentoConfigAdditional | null;
|
||||
}
|
||||
|
||||
export interface PedimentoListResponse {
|
||||
@@ -97,11 +137,16 @@ export interface CreatePedimentoData {
|
||||
paid_price?: number | null;
|
||||
gross_weight?: number | null;
|
||||
exchange_rate?: number | null;
|
||||
observaciones?: string | null;
|
||||
// Sub-resources
|
||||
pedimento_dates?: PedimentoDates | null;
|
||||
pedimento_payments?: PedimentoPayments | null;
|
||||
pedimento_transport_means?: PedimentoTransportMeans | null;
|
||||
pedimento_validation?: PedimentoValidation | null;
|
||||
pedimento_incrementables?: PedimentoIncrementables | null;
|
||||
pedimento_decrementables?: PedimentoDecrementables | null;
|
||||
pedimento_indexes?: PedimentoIndexes | null;
|
||||
pedimento_config_additional?: PedimentoConfigAdditional | null;
|
||||
}
|
||||
|
||||
export interface UpdatePedimentoData {
|
||||
@@ -119,11 +164,16 @@ export interface UpdatePedimentoData {
|
||||
paid_price?: number | null;
|
||||
gross_weight?: number | null;
|
||||
exchange_rate?: number | null;
|
||||
observaciones?: string | null;
|
||||
// Sub-resources
|
||||
pedimento_dates?: PedimentoDates | null;
|
||||
pedimento_payments?: PedimentoPayments | null;
|
||||
pedimento_transport_means?: PedimentoTransportMeans | null;
|
||||
pedimento_validation?: PedimentoValidation | null;
|
||||
pedimento_incrementables?: PedimentoIncrementables | null;
|
||||
pedimento_decrementables?: PedimentoDecrementables | null;
|
||||
pedimento_indexes?: PedimentoIndexes | null;
|
||||
pedimento_config_additional?: PedimentoConfigAdditional | null;
|
||||
}
|
||||
|
||||
export interface PedimentoFilters {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,547 @@
|
||||
<script lang="ts">
|
||||
import { Card, CardContent } from '$lib/components/ui/card';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow
|
||||
} from '$lib/components/ui/table';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogFooter
|
||||
} from '$lib/components/ui/dialog';
|
||||
import Checkbox from '$lib/components/ui/checkbox/checkbox.svelte';
|
||||
import {
|
||||
Plus,
|
||||
Pencil,
|
||||
Trash2
|
||||
} from 'lucide-svelte';
|
||||
|
||||
interface Contribucion {
|
||||
id?: number;
|
||||
contribucion: string;
|
||||
tipo_tasa: string;
|
||||
tasa: number;
|
||||
forma_pago: string;
|
||||
importe: number;
|
||||
gravamen: string;
|
||||
abreviacion: string;
|
||||
forma_pago_2: string;
|
||||
importe_2: number;
|
||||
}
|
||||
|
||||
let {
|
||||
formData = $bindable({
|
||||
// Campos por pestaña
|
||||
dta: {
|
||||
forma_pago_recargo: 0,
|
||||
tasa_recargo: 0.0,
|
||||
importe_recargo: 0
|
||||
},
|
||||
prev: {
|
||||
forma_pago_prevalidacion: 0
|
||||
},
|
||||
eci: {
|
||||
forma_pago_eci: 0,
|
||||
importe_eci: 0
|
||||
},
|
||||
mult: {
|
||||
forma_pago_multa: 0,
|
||||
importe_multa: 0
|
||||
},
|
||||
rec: {
|
||||
forma_pago_uia: 0,
|
||||
importe_compensar: 0
|
||||
},
|
||||
// Configuración
|
||||
calculo_manual: false,
|
||||
operaciones_regla_31_40: false,
|
||||
// Contribuciones (tabla compartida)
|
||||
contribuciones: [] as Contribucion[]
|
||||
})
|
||||
}: {
|
||||
formData: {
|
||||
dta: {
|
||||
forma_pago_recargo: number;
|
||||
tasa_recargo: number;
|
||||
importe_recargo: number;
|
||||
};
|
||||
prev: {
|
||||
forma_pago_prevalidacion: number;
|
||||
};
|
||||
eci: {
|
||||
forma_pago_eci: number;
|
||||
importe_eci: number;
|
||||
};
|
||||
mult: {
|
||||
forma_pago_multa: number;
|
||||
importe_multa: number;
|
||||
};
|
||||
rec: {
|
||||
forma_pago_uia: number;
|
||||
importe_compensar: number;
|
||||
};
|
||||
calculo_manual: boolean;
|
||||
operaciones_regla_31_40: boolean;
|
||||
contribuciones: Contribucion[];
|
||||
};
|
||||
} = $props();
|
||||
|
||||
// Pestaña activa
|
||||
let activeTab = $state('DTA');
|
||||
|
||||
// Estados para diálogo
|
||||
let isDialogOpen = $state(false);
|
||||
let editingIndex = $state<number | null>(null);
|
||||
|
||||
let currentContribucion = $state<Contribucion>({
|
||||
contribucion: '',
|
||||
tipo_tasa: '',
|
||||
tasa: 0,
|
||||
forma_pago: '',
|
||||
importe: 0,
|
||||
gravamen: '',
|
||||
abreviacion: '',
|
||||
forma_pago_2: '',
|
||||
importe_2: 0
|
||||
});
|
||||
|
||||
function openNewContribucion() {
|
||||
editingIndex = null;
|
||||
currentContribucion = {
|
||||
contribucion: '',
|
||||
tipo_tasa: '',
|
||||
tasa: 0,
|
||||
forma_pago: '',
|
||||
importe: 0,
|
||||
gravamen: '',
|
||||
abreviacion: '',
|
||||
forma_pago_2: '',
|
||||
importe_2: 0
|
||||
};
|
||||
isDialogOpen = true;
|
||||
}
|
||||
|
||||
function openEditContribucion(index: number) {
|
||||
if (!formData?.contribuciones) return;
|
||||
editingIndex = index;
|
||||
currentContribucion = { ...formData.contribuciones[index] };
|
||||
isDialogOpen = true;
|
||||
}
|
||||
|
||||
function saveContribucion() {
|
||||
if (!formData?.contribuciones) return;
|
||||
if (editingIndex !== null) {
|
||||
formData.contribuciones[editingIndex] = { ...currentContribucion };
|
||||
} else {
|
||||
formData.contribuciones = [...formData.contribuciones, { ...currentContribucion }];
|
||||
}
|
||||
isDialogOpen = false;
|
||||
}
|
||||
|
||||
function deleteContribucion(index: number) {
|
||||
if (!formData?.contribuciones) return;
|
||||
if (confirm('¿Está seguro de eliminar esta contribución?')) {
|
||||
formData.contribuciones = formData.contribuciones.filter((_, i) => i !== index);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card>
|
||||
<CardContent class="p-6 space-y-4">
|
||||
<!-- Pestañas de navegación -->
|
||||
<div class="flex gap-1 border-b">
|
||||
<button
|
||||
class="px-4 py-2 text-sm font-medium border-b-2 transition-colors {activeTab === 'DTA'
|
||||
? 'border-primary text-primary'
|
||||
: 'border-transparent text-muted-foreground hover:text-foreground'}"
|
||||
onclick={() => (activeTab = 'DTA')}
|
||||
>
|
||||
DTA
|
||||
</button>
|
||||
<button
|
||||
class="px-4 py-2 text-sm font-medium border-b-2 transition-colors {activeTab === 'PREV'
|
||||
? 'border-primary text-primary'
|
||||
: 'border-transparent text-muted-foreground hover:text-foreground'}"
|
||||
onclick={() => (activeTab = 'PREV')}
|
||||
>
|
||||
PREV
|
||||
</button>
|
||||
<button
|
||||
class="px-4 py-2 text-sm font-medium border-b-2 transition-colors {activeTab === 'ECI'
|
||||
? 'border-primary text-primary'
|
||||
: 'border-transparent text-muted-foreground hover:text-foreground'}"
|
||||
onclick={() => (activeTab = 'ECI')}
|
||||
>
|
||||
ECI
|
||||
</button>
|
||||
<button
|
||||
class="px-4 py-2 text-sm font-medium border-b-2 transition-colors {activeTab === 'MULT'
|
||||
? 'border-primary text-primary'
|
||||
: 'border-transparent text-muted-foreground hover:text-foreground'}"
|
||||
onclick={() => (activeTab = 'MULT')}
|
||||
>
|
||||
MULT
|
||||
</button>
|
||||
<button
|
||||
class="px-4 py-2 text-sm font-medium border-b-2 transition-colors {activeTab === 'REC'
|
||||
? 'border-primary text-primary'
|
||||
: 'border-transparent text-muted-foreground hover:text-foreground'}"
|
||||
onclick={() => (activeTab = 'REC')}
|
||||
>
|
||||
REC
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Contenido dinámico según pestaña -->
|
||||
<div class="border rounded-lg p-4 bg-muted/30">
|
||||
{#if activeTab === 'DTA'}
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="forma_pago_recargo">Forma de pago Recargo:</Label>
|
||||
<Input
|
||||
id="forma_pago_recargo"
|
||||
type="number"
|
||||
bind:value={formData.dta.forma_pago_recargo}
|
||||
placeholder="0"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="tasa_recargo">Tasa Recargo:</Label>
|
||||
<Input
|
||||
id="tasa_recargo"
|
||||
type="number"
|
||||
step="0.00001"
|
||||
bind:value={formData.dta.tasa_recargo}
|
||||
placeholder="0.00000"
|
||||
/>
|
||||
<p class="text-xs text-muted-foreground">(dejar en cero para calcular automático)</p>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="importe_recargo">Importe Recargo:</Label>
|
||||
<Input
|
||||
id="importe_recargo"
|
||||
type="number"
|
||||
bind:value={formData.dta.importe_recargo}
|
||||
placeholder="0"
|
||||
/>
|
||||
<p class="text-xs text-muted-foreground">(dejar en cero para calcular automático)</p>
|
||||
</div>
|
||||
</div>
|
||||
{:else if activeTab === 'PREV'}
|
||||
<div class="grid grid-cols-1 gap-4 max-w-md">
|
||||
<div class="space-y-2">
|
||||
<Label for="forma_pago_prevalidacion">Forma de pago Prevalidación:</Label>
|
||||
<Input
|
||||
id="forma_pago_prevalidacion"
|
||||
type="number"
|
||||
bind:value={formData.prev.forma_pago_prevalidacion}
|
||||
placeholder="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{:else if activeTab === 'ECI'}
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="forma_pago_eci">Forma de pago ECI:</Label>
|
||||
<Input
|
||||
id="forma_pago_eci"
|
||||
type="number"
|
||||
bind:value={formData.eci.forma_pago_eci}
|
||||
placeholder="0"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="importe_eci">Importe ECI:</Label>
|
||||
<Input
|
||||
id="importe_eci"
|
||||
type="number"
|
||||
bind:value={formData.eci.importe_eci}
|
||||
placeholder="0"
|
||||
/>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
(dejar en cero para que el sistema calcule el importe)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{:else if activeTab === 'MULT'}
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="forma_pago_multa">Forma de pago Multa:</Label>
|
||||
<Input
|
||||
id="forma_pago_multa"
|
||||
type="number"
|
||||
bind:value={formData.mult.forma_pago_multa}
|
||||
placeholder="0"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="importe_multa">Importe Multa:</Label>
|
||||
<Input
|
||||
id="importe_multa"
|
||||
type="number"
|
||||
bind:value={formData.mult.importe_multa}
|
||||
placeholder="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{:else if activeTab === 'REC'}
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="forma_pago_uia">Forma de pago U I A:</Label>
|
||||
<Input
|
||||
id="forma_pago_uia"
|
||||
type="number"
|
||||
bind:value={formData.rec.forma_pago_uia}
|
||||
placeholder="0"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="importe_compensar">Importe a Compensar:</Label>
|
||||
<Input
|
||||
id="importe_compensar"
|
||||
type="number"
|
||||
bind:value={formData.rec.importe_compensar}
|
||||
placeholder="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Tabla de contribuciones -->
|
||||
<div class="border rounded-lg overflow-hidden">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Contribución</TableHead>
|
||||
<TableHead>T.T.</TableHead>
|
||||
<TableHead>Tasa</TableHead>
|
||||
<TableHead>F.P.</TableHead>
|
||||
<TableHead class="text-right">Importe</TableHead>
|
||||
<TableHead>Gravamen</TableHead>
|
||||
<TableHead>Abreviación</TableHead>
|
||||
<TableHead>F.P.</TableHead>
|
||||
<TableHead class="text-right">Importe</TableHead>
|
||||
<TableHead class="w-[100px]">Acciones</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#if !formData?.contribuciones || formData.contribuciones.length === 0}
|
||||
<TableRow>
|
||||
<TableCell colspan={10} class="text-center text-muted-foreground py-8">
|
||||
No hay contribuciones registradas
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{:else}
|
||||
{#each formData.contribuciones as contribucion, index}
|
||||
<TableRow>
|
||||
<TableCell>{contribucion.contribucion}</TableCell>
|
||||
<TableCell>{contribucion.tipo_tasa}</TableCell>
|
||||
<TableCell class="text-right"
|
||||
>{contribucion.tasa.toLocaleString('es-MX', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 5
|
||||
})}</TableCell
|
||||
>
|
||||
<TableCell>{contribucion.forma_pago}</TableCell>
|
||||
<TableCell class="text-right"
|
||||
>{contribucion.importe.toLocaleString('es-MX', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}</TableCell
|
||||
>
|
||||
<TableCell>{contribucion.gravamen}</TableCell>
|
||||
<TableCell>{contribucion.abreviacion}</TableCell>
|
||||
<TableCell>{contribucion.forma_pago_2}</TableCell>
|
||||
<TableCell class="text-right"
|
||||
>{contribucion.importe_2.toLocaleString('es-MX', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}</TableCell
|
||||
>
|
||||
<TableCell>
|
||||
<div class="flex gap-1">
|
||||
<Button size="sm" variant="ghost" onclick={() => openEditContribucion(index)}>
|
||||
<Pencil size={14} />
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost" onclick={() => deleteContribucion(index)}>
|
||||
<Trash2 size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
{/if}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<!-- Controles inferiores -->
|
||||
<div class="flex items-center justify-between border-t pt-4">
|
||||
<div class="flex items-center gap-6">
|
||||
<div class="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="calculo_manual"
|
||||
checked={formData?.calculo_manual ?? false}
|
||||
onCheckedChange={(checked: boolean | 'indeterminate') =>
|
||||
formData && (formData.calculo_manual = checked === true)}
|
||||
/>
|
||||
<Label for="calculo_manual" class="font-normal cursor-pointer text-sm"
|
||||
>Cálculo Manual</Label
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="operaciones_regla"
|
||||
checked={formData?.operaciones_regla_31_40 ?? false}
|
||||
onCheckedChange={(checked: boolean | 'indeterminate') =>
|
||||
formData && (formData.operaciones_regla_31_40 = checked === true)}
|
||||
/>
|
||||
<Label for="operaciones_regla" class="font-normal cursor-pointer text-sm"
|
||||
>Operaciones al amparo de la regla 31.40</Label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<Button size="sm" onclick={openNewContribucion}>
|
||||
<Plus class="mr-1.5" size={14} />
|
||||
Nuevo
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={formData?.contribuciones?.length === 0}
|
||||
onclick={() => {
|
||||
const index = 0;
|
||||
if (formData?.contribuciones?.[index]) openEditContribucion(index);
|
||||
}}
|
||||
>
|
||||
<Pencil class="mr-1.5" size={14} />
|
||||
Editar
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
disabled={formData?.contribuciones?.length === 0}
|
||||
onclick={() => {
|
||||
const index = 0;
|
||||
if (formData?.contribuciones?.[index]) deleteContribucion(index);
|
||||
}}
|
||||
>
|
||||
<Trash2 class="mr-1.5" size={14} />
|
||||
Borrar
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Dialog para Contribuciones -->
|
||||
<Dialog bind:open={isDialogOpen}>
|
||||
<DialogContent class="max-w-3xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{editingIndex !== null ? 'Editar Contribución' : 'Nueva Contribución'}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div class="grid gap-4 py-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="contribucion">Contribución *</Label>
|
||||
<Input
|
||||
id="contribucion"
|
||||
bind:value={currentContribucion.contribucion}
|
||||
placeholder="Nombre de la contribución"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="tipo_tasa">T.T. (Tipo de Tasa) *</Label>
|
||||
<Input id="tipo_tasa" bind:value={currentContribucion.tipo_tasa} placeholder="Tipo" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="tasa">Tasa *</Label>
|
||||
<Input
|
||||
id="tasa"
|
||||
type="number"
|
||||
step="0.00001"
|
||||
bind:value={currentContribucion.tasa}
|
||||
placeholder="0.00000"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="forma_pago">F.P. (Forma de Pago) *</Label>
|
||||
<Input id="forma_pago" bind:value={currentContribucion.forma_pago} placeholder="FP" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="importe">Importe *</Label>
|
||||
<Input
|
||||
id="importe"
|
||||
type="number"
|
||||
step="0.01"
|
||||
bind:value={currentContribucion.importe}
|
||||
placeholder="0.00"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="gravamen">Gravamen</Label>
|
||||
<Input id="gravamen" bind:value={currentContribucion.gravamen} placeholder="Gravamen" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="abreviacion">Abreviación</Label>
|
||||
<Input
|
||||
id="abreviacion"
|
||||
bind:value={currentContribucion.abreviacion}
|
||||
placeholder="Abreviación"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="forma_pago_2">F.P. 2</Label>
|
||||
<Input id="forma_pago_2" bind:value={currentContribucion.forma_pago_2} placeholder="FP" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="importe_2">Importe 2</Label>
|
||||
<Input
|
||||
id="importe_2"
|
||||
type="number"
|
||||
step="0.01"
|
||||
bind:value={currentContribucion.importe_2}
|
||||
placeholder="0.00"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onclick={() => (isDialogOpen = false)}>Cancelar</Button>
|
||||
<Button
|
||||
onclick={saveContribucion}
|
||||
disabled={!currentContribucion.contribucion ||
|
||||
!currentContribucion.tipo_tasa ||
|
||||
!currentContribucion.forma_pago}
|
||||
>
|
||||
{editingIndex !== null ? 'Actualizar' : 'Guardar'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
@@ -0,0 +1,632 @@
|
||||
<script lang="ts">
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '$lib/components/ui/card';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow
|
||||
} from '$lib/components/ui/table';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogFooter
|
||||
} from '$lib/components/ui/dialog';
|
||||
import {
|
||||
Plus,
|
||||
Pencil,
|
||||
Trash2
|
||||
} from 'lucide-svelte';
|
||||
|
||||
interface CuentaGarantia {
|
||||
id?: number;
|
||||
institucion_emisora: string;
|
||||
numero_contrato: string;
|
||||
total_garantia: number;
|
||||
folio_constancia: string;
|
||||
fecha_constancia: string;
|
||||
um: string;
|
||||
valor_unitario: number;
|
||||
}
|
||||
|
||||
interface Compensacion {
|
||||
id?: number;
|
||||
patente_original: string;
|
||||
pedimento_original: string;
|
||||
}
|
||||
|
||||
interface DocumentoPago {
|
||||
id?: number;
|
||||
forma_pago: string;
|
||||
dependencia: string;
|
||||
}
|
||||
|
||||
let {
|
||||
formData = $bindable({
|
||||
cuentas_garantia: [] as CuentaGarantia[],
|
||||
compensaciones: [] as Compensacion[],
|
||||
documentos_pago: [] as DocumentoPago[]
|
||||
})
|
||||
}: {
|
||||
formData: {
|
||||
cuentas_garantia: CuentaGarantia[];
|
||||
compensaciones: Compensacion[];
|
||||
documentos_pago: DocumentoPago[];
|
||||
};
|
||||
} = $props();
|
||||
|
||||
// Estados para diálogos
|
||||
let isCuentaDialogOpen = $state(false);
|
||||
let isCompensacionDialogOpen = $state(false);
|
||||
let isDocumentoDialogOpen = $state(false);
|
||||
|
||||
// Estados para edición
|
||||
let editingCuentaIndex = $state<number | null>(null);
|
||||
let editingCompensacionIndex = $state<number | null>(null);
|
||||
let editingDocumentoIndex = $state<number | null>(null);
|
||||
|
||||
// Estados actuales
|
||||
let currentCuenta = $state<CuentaGarantia>({
|
||||
institucion_emisora: '',
|
||||
numero_contrato: '',
|
||||
total_garantia: 0,
|
||||
folio_constancia: '',
|
||||
fecha_constancia: '',
|
||||
um: '',
|
||||
valor_unitario: 0
|
||||
});
|
||||
|
||||
let currentCompensacion = $state<Compensacion>({
|
||||
patente_original: '',
|
||||
pedimento_original: ''
|
||||
});
|
||||
|
||||
let currentDocumento = $state<DocumentoPago>({
|
||||
forma_pago: '',
|
||||
dependencia: ''
|
||||
});
|
||||
|
||||
const unidadesMedida = ['KG', 'LT', 'PZ', 'MT', 'M2', 'M3', 'TON', 'CAJ', 'PAR'];
|
||||
|
||||
// Funciones para Cuentas Garantía
|
||||
function openNewCuenta() {
|
||||
editingCuentaIndex = null;
|
||||
currentCuenta = {
|
||||
institucion_emisora: '',
|
||||
numero_contrato: '',
|
||||
total_garantia: 0,
|
||||
folio_constancia: '',
|
||||
fecha_constancia: '',
|
||||
um: '',
|
||||
valor_unitario: 0
|
||||
};
|
||||
isCuentaDialogOpen = true;
|
||||
}
|
||||
|
||||
function openEditCuenta(index: number) {
|
||||
if (!formData?.cuentas_garantia) return;
|
||||
editingCuentaIndex = index;
|
||||
currentCuenta = { ...formData.cuentas_garantia[index] };
|
||||
isCuentaDialogOpen = true;
|
||||
}
|
||||
|
||||
function saveCuenta() {
|
||||
if (!formData?.cuentas_garantia) return;
|
||||
if (editingCuentaIndex !== null) {
|
||||
formData.cuentas_garantia[editingCuentaIndex] = { ...currentCuenta };
|
||||
} else {
|
||||
formData.cuentas_garantia = [...formData.cuentas_garantia, { ...currentCuenta }];
|
||||
}
|
||||
isCuentaDialogOpen = false;
|
||||
}
|
||||
|
||||
function deleteCuenta(index: number) {
|
||||
if (!formData?.cuentas_garantia) return;
|
||||
if (confirm('¿Está seguro de eliminar esta cuenta?')) {
|
||||
formData.cuentas_garantia = formData.cuentas_garantia.filter((_, i) => i !== index);
|
||||
}
|
||||
}
|
||||
|
||||
// Funciones para Compensaciones
|
||||
function openNewCompensacion() {
|
||||
editingCompensacionIndex = null;
|
||||
currentCompensacion = {
|
||||
patente_original: '',
|
||||
pedimento_original: ''
|
||||
};
|
||||
isCompensacionDialogOpen = true;
|
||||
}
|
||||
|
||||
function openEditCompensacion(index: number) {
|
||||
if (!formData?.compensaciones) return;
|
||||
editingCompensacionIndex = index;
|
||||
currentCompensacion = { ...formData.compensaciones[index] };
|
||||
isCompensacionDialogOpen = true;
|
||||
}
|
||||
|
||||
function saveCompensacion() {
|
||||
if (!formData?.compensaciones) return;
|
||||
if (editingCompensacionIndex !== null) {
|
||||
formData.compensaciones[editingCompensacionIndex] = { ...currentCompensacion };
|
||||
} else {
|
||||
formData.compensaciones = [...formData.compensaciones, { ...currentCompensacion }];
|
||||
}
|
||||
isCompensacionDialogOpen = false;
|
||||
}
|
||||
|
||||
function deleteCompensacion(index: number) {
|
||||
if (!formData?.compensaciones) return;
|
||||
if (confirm('¿Está seguro de eliminar esta compensación?')) {
|
||||
formData.compensaciones = formData.compensaciones.filter((_, i) => i !== index);
|
||||
}
|
||||
}
|
||||
|
||||
// Funciones para Documentos de Pago
|
||||
function openNewDocumento() {
|
||||
editingDocumentoIndex = null;
|
||||
currentDocumento = {
|
||||
forma_pago: '',
|
||||
dependencia: ''
|
||||
};
|
||||
isDocumentoDialogOpen = true;
|
||||
}
|
||||
|
||||
function openEditDocumento(index: number) {
|
||||
if (!formData?.documentos_pago) return;
|
||||
editingDocumentoIndex = index;
|
||||
currentDocumento = { ...formData.documentos_pago[index] };
|
||||
isDocumentoDialogOpen = true;
|
||||
}
|
||||
|
||||
function saveDocumento() {
|
||||
if (!formData?.documentos_pago) return;
|
||||
if (editingDocumentoIndex !== null) {
|
||||
formData.documentos_pago[editingDocumentoIndex] = { ...currentDocumento };
|
||||
} else {
|
||||
formData.documentos_pago = [...formData.documentos_pago, { ...currentDocumento }];
|
||||
}
|
||||
isDocumentoDialogOpen = false;
|
||||
}
|
||||
|
||||
function deleteDocumento(index: number) {
|
||||
if (!formData?.documentos_pago) return;
|
||||
if (confirm('¿Está seguro de eliminar este documento?')) {
|
||||
formData.documentos_pago = formData.documentos_pago.filter((_, i) => i !== index);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="space-y-4">
|
||||
<!-- Sección de Cuentas Garantía -->
|
||||
<Card>
|
||||
<CardContent class="p-6 space-y-4">
|
||||
<div class="border rounded-lg overflow-hidden">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Institución Emisora</TableHead>
|
||||
<TableHead>Número Contrato</TableHead>
|
||||
<TableHead class="text-right">Total Garantía</TableHead>
|
||||
<TableHead>Folio Constancia</TableHead>
|
||||
<TableHead>Fecha Constancia</TableHead>
|
||||
<TableHead>UM</TableHead>
|
||||
<TableHead class="text-right">Valor Unitario</TableHead>
|
||||
<TableHead class="w-[100px]">Acciones</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#if !formData?.cuentas_garantia || formData.cuentas_garantia.length === 0}
|
||||
<TableRow>
|
||||
<TableCell colspan={8} class="text-center text-muted-foreground py-8">
|
||||
No hay cuentas de garantía registradas
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{:else}
|
||||
{#each formData.cuentas_garantia as cuenta, index}
|
||||
<TableRow>
|
||||
<TableCell>{cuenta.institucion_emisora}</TableCell>
|
||||
<TableCell>{cuenta.numero_contrato}</TableCell>
|
||||
<TableCell class="text-right"
|
||||
>{cuenta.total_garantia.toLocaleString('es-MX', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}</TableCell
|
||||
>
|
||||
<TableCell>{cuenta.folio_constancia}</TableCell>
|
||||
<TableCell>{cuenta.fecha_constancia}</TableCell>
|
||||
<TableCell>{cuenta.um}</TableCell>
|
||||
<TableCell class="text-right"
|
||||
>{cuenta.valor_unitario.toLocaleString('es-MX', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}</TableCell
|
||||
>
|
||||
<TableCell>
|
||||
<div class="flex gap-1">
|
||||
<Button size="sm" variant="ghost" onclick={() => openEditCuenta(index)}>
|
||||
<Pencil size={14} />
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost" onclick={() => deleteCuenta(index)}>
|
||||
<Trash2 size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
{/if}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<Button size="sm" onclick={openNewCuenta}>
|
||||
<Plus class="mr-1.5" size={14} />
|
||||
Nuevo
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={formData?.cuentas_garantia?.length === 0}
|
||||
onclick={() => {
|
||||
const index = 0;
|
||||
if (formData?.cuentas_garantia?.[index]) openEditCuenta(index);
|
||||
}}
|
||||
>
|
||||
<Pencil class="mr-1.5" size={14} />
|
||||
Editar
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
disabled={formData?.cuentas_garantia?.length === 0}
|
||||
onclick={() => {
|
||||
const index = 0;
|
||||
if (formData?.cuentas_garantia?.[index]) deleteCuenta(index);
|
||||
}}
|
||||
>
|
||||
<Trash2 class="mr-1.5" size={14} />
|
||||
Borrar
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Secciones en grid -->
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<!-- Compensaciones -->
|
||||
<Card>
|
||||
<CardHeader class="pb-3">
|
||||
<CardTitle class="text-base">Compensaciones</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-4">
|
||||
<div class="border rounded-lg overflow-hidden">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Patente Original</TableHead>
|
||||
<TableHead>Pedimento Original</TableHead>
|
||||
<TableHead class="w-[100px]">Acciones</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#if !formData?.compensaciones || formData.compensaciones.length === 0}
|
||||
<TableRow>
|
||||
<TableCell colspan={3} class="text-center text-muted-foreground py-8">
|
||||
No hay compensaciones registradas
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{:else}
|
||||
{#each formData.compensaciones as compensacion, index}
|
||||
<TableRow>
|
||||
<TableCell>{compensacion.patente_original}</TableCell>
|
||||
<TableCell>{compensacion.pedimento_original}</TableCell>
|
||||
<TableCell>
|
||||
<div class="flex gap-1">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onclick={() => openEditCompensacion(index)}
|
||||
>
|
||||
<Pencil size={14} />
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onclick={() => deleteCompensacion(index)}
|
||||
>
|
||||
<Trash2 size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
{/if}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<Button size="sm" onclick={openNewCompensacion}>
|
||||
<Plus class="mr-1.5" size={14} />
|
||||
Nuevo
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={formData?.compensaciones?.length === 0}
|
||||
onclick={() => {
|
||||
const index = 0;
|
||||
if (formData?.compensaciones?.[index]) openEditCompensacion(index);
|
||||
}}
|
||||
>
|
||||
<Pencil class="mr-1.5" size={14} />
|
||||
Editar
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
disabled={formData?.compensaciones?.length === 0}
|
||||
onclick={() => {
|
||||
const index = 0;
|
||||
if (formData?.compensaciones?.[index]) deleteCompensacion(index);
|
||||
}}
|
||||
>
|
||||
<Trash2 class="mr-1.5" size={14} />
|
||||
Borrar
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Documentos que amparan las Formas de Pago -->
|
||||
<Card>
|
||||
<CardHeader class="pb-3">
|
||||
<CardTitle class="text-base">Documentos que amparan las Formas de Pago</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-4">
|
||||
<div class="border rounded-lg overflow-hidden">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Forma de Pago</TableHead>
|
||||
<TableHead>Dependencia</TableHead>
|
||||
<TableHead class="w-[100px]">Acciones</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#if !formData?.documentos_pago || formData.documentos_pago.length === 0}
|
||||
<TableRow>
|
||||
<TableCell colspan={3} class="text-center text-muted-foreground py-8">
|
||||
No hay documentos registrados
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{:else}
|
||||
{#each formData.documentos_pago as documento, index}
|
||||
<TableRow>
|
||||
<TableCell>{documento.forma_pago}</TableCell>
|
||||
<TableCell>{documento.dependencia}</TableCell>
|
||||
<TableCell>
|
||||
<div class="flex gap-1">
|
||||
<Button size="sm" variant="ghost" onclick={() => openEditDocumento(index)}>
|
||||
<Pencil size={14} />
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost" onclick={() => deleteDocumento(index)}>
|
||||
<Trash2 size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
{/if}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<Button size="sm" onclick={openNewDocumento}>
|
||||
<Plus class="mr-1.5" size={14} />
|
||||
Nuevo
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={formData?.documentos_pago?.length === 0}
|
||||
onclick={() => {
|
||||
const index = 0;
|
||||
if (formData?.documentos_pago?.[index]) openEditDocumento(index);
|
||||
}}
|
||||
>
|
||||
<Pencil class="mr-1.5" size={14} />
|
||||
Editar
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
disabled={formData?.documentos_pago?.length === 0}
|
||||
onclick={() => {
|
||||
const index = 0;
|
||||
if (formData?.documentos_pago?.[index]) deleteDocumento(index);
|
||||
}}
|
||||
>
|
||||
<Trash2 class="mr-1.5" size={14} />
|
||||
Borrar
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dialog para Cuentas Garantía -->
|
||||
<Dialog bind:open={isCuentaDialogOpen}>
|
||||
<DialogContent class="max-w-3xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{editingCuentaIndex !== null ? 'Editar Cuenta Garantía' : 'Nueva Cuenta Garantía'}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div class="grid gap-4 py-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="institucion_emisora">Institución Emisora *</Label>
|
||||
<Input
|
||||
id="institucion_emisora"
|
||||
bind:value={currentCuenta.institucion_emisora}
|
||||
placeholder="Nombre de la institución"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="numero_contrato">Número Contrato *</Label>
|
||||
<Input
|
||||
id="numero_contrato"
|
||||
bind:value={currentCuenta.numero_contrato}
|
||||
placeholder="Número de contrato"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="total_garantia">Total Garantía *</Label>
|
||||
<Input
|
||||
id="total_garantia"
|
||||
type="number"
|
||||
step="0.01"
|
||||
bind:value={currentCuenta.total_garantia}
|
||||
placeholder="0.00"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="folio_constancia">Folio Constancia *</Label>
|
||||
<Input
|
||||
id="folio_constancia"
|
||||
bind:value={currentCuenta.folio_constancia}
|
||||
placeholder="Folio"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="fecha_constancia">Fecha Constancia *</Label>
|
||||
<Input id="fecha_constancia" type="date" bind:value={currentCuenta.fecha_constancia} />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="um">UM *</Label>
|
||||
<Input id="um" bind:value={currentCuenta.um} placeholder="Unidad de medida" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="valor_unitario">Valor Unitario *</Label>
|
||||
<Input
|
||||
id="valor_unitario"
|
||||
type="number"
|
||||
step="0.01"
|
||||
bind:value={currentCuenta.valor_unitario}
|
||||
placeholder="0.00"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onclick={() => (isCuentaDialogOpen = false)}>Cancelar</Button>
|
||||
<Button
|
||||
onclick={saveCuenta}
|
||||
disabled={!currentCuenta.institucion_emisora ||
|
||||
!currentCuenta.numero_contrato ||
|
||||
!currentCuenta.folio_constancia ||
|
||||
!currentCuenta.fecha_constancia ||
|
||||
!currentCuenta.um}
|
||||
>
|
||||
{editingCuentaIndex !== null ? 'Actualizar' : 'Guardar'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<!-- Dialog para Compensaciones -->
|
||||
<Dialog bind:open={isCompensacionDialogOpen}>
|
||||
<DialogContent class="max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{editingCompensacionIndex !== null ? 'Editar Compensación' : 'Nueva Compensación'}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div class="grid gap-4 py-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="patente_original">Patente Original *</Label>
|
||||
<Input
|
||||
id="patente_original"
|
||||
bind:value={currentCompensacion.patente_original}
|
||||
placeholder="0000"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="pedimento_original">Pedimento Original *</Label>
|
||||
<Input
|
||||
id="pedimento_original"
|
||||
bind:value={currentCompensacion.pedimento_original}
|
||||
placeholder="00 00 0000 0000000"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onclick={() => (isCompensacionDialogOpen = false)}
|
||||
>Cancelar</Button
|
||||
>
|
||||
<Button
|
||||
onclick={saveCompensacion}
|
||||
disabled={!currentCompensacion.patente_original || !currentCompensacion.pedimento_original}
|
||||
>
|
||||
{editingCompensacionIndex !== null ? 'Actualizar' : 'Guardar'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<!-- Dialog para Documentos de Pago -->
|
||||
<Dialog bind:open={isDocumentoDialogOpen}>
|
||||
<DialogContent class="max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{editingDocumentoIndex !== null ? 'Editar Documento' : 'Nuevo Documento'}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div class="grid gap-4 py-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="forma_pago">Forma de Pago *</Label>
|
||||
<Input id="forma_pago" bind:value={currentDocumento.forma_pago} placeholder="Forma de pago" />
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="dependencia">Dependencia *</Label>
|
||||
<Input
|
||||
id="dependencia"
|
||||
bind:value={currentDocumento.dependencia}
|
||||
placeholder="Dependencia"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onclick={() => (isDocumentoDialogOpen = false)}>Cancelar</Button>
|
||||
<Button
|
||||
onclick={saveDocumento}
|
||||
disabled={!currentDocumento.forma_pago || !currentDocumento.dependencia}
|
||||
>
|
||||
{editingDocumentoIndex !== null ? 'Actualizar' : 'Guardar'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
@@ -1,107 +1,83 @@
|
||||
<script lang="ts">
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import type { Pedimento } from '$lib/api/dashboard/a76/pedimentos';
|
||||
|
||||
let {
|
||||
pedimento,
|
||||
formData = $bindable(),
|
||||
exists = $bindable()
|
||||
exists = $bindable(),
|
||||
pedimentoType = '',
|
||||
pedimentoNumber = ''
|
||||
}: {
|
||||
pedimento: Pedimento | null;
|
||||
formData?: any;
|
||||
exists?: boolean;
|
||||
pedimentoType?: string;
|
||||
pedimentoNumber?: string;
|
||||
} = $props();
|
||||
|
||||
// Inicializar formData inmediatamente
|
||||
const datesData = pedimento?.pedimento_dates;
|
||||
if (datesData) {
|
||||
exists = true;
|
||||
if (!formData) {
|
||||
formData = {
|
||||
entry_date: datesData.entry_date ? datesData.entry_date.substring(0, 10) : '',
|
||||
pedimento_date: datesData.pedimento_date ? datesData.pedimento_date.substring(0, 10) : '',
|
||||
payment_date: datesData.payment_date ? datesData.payment_date.substring(0, 10) : '',
|
||||
rectification_payment_date: datesData.rectification_payment_date ? datesData.rectification_payment_date.substring(0, 10) : '',
|
||||
extraction_date: datesData.extraction_date ? datesData.extraction_date.substring(0, 10) : '',
|
||||
submission_date: datesData.submission_date ? datesData.submission_date.substring(0, 10) : '',
|
||||
eucan_date: datesData.eucan_date ? datesData.eucan_date.substring(0, 10) : '',
|
||||
original_date: datesData.original_date ? datesData.original_date.substring(0, 10) : '',
|
||||
start_date: datesData.start_date ? datesData.start_date.substring(0, 10) : '',
|
||||
end_date: datesData.end_date ? datesData.end_date.substring(0, 10) : '',
|
||||
};
|
||||
}
|
||||
} else {
|
||||
exists = false;
|
||||
if (!formData) {
|
||||
formData = {
|
||||
entry_date: '',
|
||||
pedimento_date: '',
|
||||
payment_date: '',
|
||||
rectification_payment_date: '',
|
||||
extraction_date: '',
|
||||
submission_date: '',
|
||||
eucan_date: '',
|
||||
original_date: '',
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
capture_time: ''
|
||||
};
|
||||
}
|
||||
// Inicializar formData
|
||||
if (!formData) {
|
||||
formData = {
|
||||
observaciones: pedimento?.observaciones || ''
|
||||
};
|
||||
}
|
||||
|
||||
exists = !!pedimento?.observaciones;
|
||||
|
||||
// Mapear el tipo de pedimento a un nombre legible
|
||||
const tipoPedimentoNombre = $derived.by(() => {
|
||||
switch(pedimentoType?.toLowerCase()) {
|
||||
case 'automovil':
|
||||
return 'Automóvil';
|
||||
case 'complementario':
|
||||
return 'Complementario';
|
||||
case 'consolidado':
|
||||
return 'Consolidado';
|
||||
case 'normal':
|
||||
return 'Normal';
|
||||
default:
|
||||
return pedimentoType || 'No especificado';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>Fechas del Pedimento</Card.Title>
|
||||
<Card.Title>Observaciones</Card.Title>
|
||||
<Card.Description>
|
||||
Gestiona las fechas importantes del pedimento
|
||||
Agrega notas y observaciones sobre el pedimento
|
||||
</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<div class="space-y-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<!-- Fecha de Envío -->
|
||||
<div class="space-y-2">
|
||||
<Label for="submission_date">Fecha de Envío</Label>
|
||||
<Input
|
||||
id="submission_date"
|
||||
type="date"
|
||||
bind:value={formData.submission_date}
|
||||
/>
|
||||
<div class="space-y-4">
|
||||
<!-- Mostrar el tipo de pedimento y número -->
|
||||
<div class="rounded-lg border bg-card p-4">
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm font-medium text-muted-foreground">Tipo de Pedimento:</span>
|
||||
<span class="text-sm font-semibold">{tipoPedimentoNombre}</span>
|
||||
</div>
|
||||
|
||||
<!-- Fecha EUCAN -->
|
||||
<div class="space-y-2">
|
||||
<Label for="eucan_date">Fecha EUCAN</Label>
|
||||
<Input
|
||||
id="eucan_date"
|
||||
type="date"
|
||||
bind:value={formData.eucan_date}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Fecha de Inicio -->
|
||||
<div class="space-y-2">
|
||||
<Label for="start_date">Fecha de Inicio</Label>
|
||||
<Input
|
||||
id="start_date"
|
||||
type="date"
|
||||
bind:value={formData.start_date}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Fecha de Fin -->
|
||||
<div class="space-y-2">
|
||||
<Label for="end_date">Fecha de Fin</Label>
|
||||
<Input
|
||||
id="end_date"
|
||||
type="date"
|
||||
bind:value={formData.end_date}
|
||||
/>
|
||||
</div>
|
||||
{#if pedimentoNumber}
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm font-medium text-muted-foreground">Número de Pedimento:</span>
|
||||
<span class="text-sm font-semibold">{pedimentoNumber}</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="observaciones">Observaciones</Label>
|
||||
<textarea
|
||||
id="observaciones"
|
||||
bind:value={formData.observaciones}
|
||||
placeholder="Escribe tus observaciones aquí..."
|
||||
rows={10}
|
||||
class="flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 resize-y"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
@@ -0,0 +1,495 @@
|
||||
<script lang="ts">
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '$lib/components/ui/card';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow
|
||||
} from '$lib/components/ui/table';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogFooter
|
||||
} from '$lib/components/ui/dialog';
|
||||
import {
|
||||
Plus,
|
||||
Pencil,
|
||||
Trash2,
|
||||
Upload,
|
||||
Trash
|
||||
} from 'lucide-svelte';
|
||||
|
||||
interface Descargo {
|
||||
id?: number;
|
||||
patente: string;
|
||||
pedimento: string;
|
||||
aduana: string;
|
||||
clave: string;
|
||||
fecha_pago: string;
|
||||
fraccion: string;
|
||||
umt: string;
|
||||
cantidad_tarifa: number;
|
||||
}
|
||||
|
||||
interface Destinatario {
|
||||
id?: number;
|
||||
clave: string;
|
||||
identificacion_fiscal: string;
|
||||
nombre: string;
|
||||
}
|
||||
|
||||
let {
|
||||
formData = $bindable({
|
||||
descargos: [] as Descargo[],
|
||||
destinatarios: [] as Destinatario[]
|
||||
})
|
||||
}: {
|
||||
formData: {
|
||||
descargos: Descargo[];
|
||||
destinatarios: Destinatario[];
|
||||
};
|
||||
} = $props();
|
||||
|
||||
// Estados para diálogos
|
||||
let isDescargoDialogOpen = $state(false);
|
||||
let isDestinatarioDialogOpen = $state(false);
|
||||
|
||||
// Estados para edición
|
||||
let editingDescargoIndex = $state<number | null>(null);
|
||||
let editingDestinatarioIndex = $state<number | null>(null);
|
||||
|
||||
// Estados actuales
|
||||
let currentDescargo = $state<Descargo>({
|
||||
patente: '',
|
||||
pedimento: '',
|
||||
aduana: '',
|
||||
clave: '',
|
||||
fecha_pago: '',
|
||||
fraccion: '',
|
||||
umt: '',
|
||||
cantidad_tarifa: 0
|
||||
});
|
||||
|
||||
let currentDestinatario = $state<Destinatario>({
|
||||
clave: '',
|
||||
identificacion_fiscal: '',
|
||||
nombre: ''
|
||||
});
|
||||
|
||||
const unidadesMedida = ['KG', 'LT', 'PZ', 'MT', 'M2', 'M3', 'TON', 'CAJ', 'PAR'];
|
||||
|
||||
// Funciones para Descargos
|
||||
function openNewDescargo() {
|
||||
editingDescargoIndex = null;
|
||||
currentDescargo = {
|
||||
patente: '',
|
||||
pedimento: '',
|
||||
aduana: '',
|
||||
clave: '',
|
||||
fecha_pago: '',
|
||||
fraccion: '',
|
||||
umt: '',
|
||||
cantidad_tarifa: 0
|
||||
};
|
||||
isDescargoDialogOpen = true;
|
||||
}
|
||||
|
||||
function openEditDescargo(index: number) {
|
||||
if (!formData?.descargos) return;
|
||||
editingDescargoIndex = index;
|
||||
currentDescargo = { ...formData.descargos[index] };
|
||||
isDescargoDialogOpen = true;
|
||||
}
|
||||
|
||||
function saveDescargo() {
|
||||
if (!formData?.descargos) return;
|
||||
if (editingDescargoIndex !== null) {
|
||||
formData.descargos[editingDescargoIndex] = { ...currentDescargo };
|
||||
} else {
|
||||
formData.descargos = [...formData.descargos, { ...currentDescargo }];
|
||||
}
|
||||
isDescargoDialogOpen = false;
|
||||
}
|
||||
|
||||
function deleteDescargo(index: number) {
|
||||
if (!formData?.descargos) return;
|
||||
if (confirm('¿Está seguro de eliminar este descargo?')) {
|
||||
formData.descargos = formData.descargos.filter((_, i) => i !== index);
|
||||
}
|
||||
}
|
||||
|
||||
function deleteAllDescargos() {
|
||||
if (!formData?.descargos) return;
|
||||
if (formData.descargos.length === 0) {
|
||||
alert('No hay descargos para eliminar');
|
||||
return;
|
||||
}
|
||||
if (confirm(`¿Está seguro de eliminar todos los descargos (${formData.descargos.length})?`)) {
|
||||
formData.descargos = [];
|
||||
}
|
||||
}
|
||||
|
||||
function importarDescargos() {
|
||||
alert('Importar Descargos (Formato CSV) no implementado aún');
|
||||
}
|
||||
|
||||
// Funciones para Destinatarios
|
||||
function openNewDestinatario() {
|
||||
editingDestinatarioIndex = null;
|
||||
currentDestinatario = {
|
||||
clave: '',
|
||||
identificacion_fiscal: '',
|
||||
nombre: ''
|
||||
};
|
||||
isDestinatarioDialogOpen = true;
|
||||
}
|
||||
|
||||
function openEditDestinatario(index: number) {
|
||||
if (!formData?.destinatarios) return;
|
||||
editingDestinatarioIndex = index;
|
||||
currentDestinatario = { ...formData.destinatarios[index] };
|
||||
isDestinatarioDialogOpen = true;
|
||||
}
|
||||
|
||||
function saveDestinatario() {
|
||||
if (!formData?.destinatarios) return;
|
||||
if (editingDestinatarioIndex !== null) {
|
||||
formData.destinatarios[editingDestinatarioIndex] = { ...currentDestinatario };
|
||||
} else {
|
||||
formData.destinatarios = [...formData.destinatarios, { ...currentDestinatario }];
|
||||
}
|
||||
isDestinatarioDialogOpen = false;
|
||||
}
|
||||
|
||||
function deleteDestinatario(index: number) {
|
||||
if (!formData?.destinatarios) return;
|
||||
if (confirm('¿Está seguro de eliminar este destinatario?')) {
|
||||
formData.destinatarios = formData.destinatarios.filter((_, i) => i !== index);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="space-y-4">
|
||||
<!-- Sección de Descargos -->
|
||||
<Card>
|
||||
<CardHeader class="pb-3">
|
||||
<CardTitle class="text-base">Descargos</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-4">
|
||||
<div class="border rounded-lg overflow-hidden">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Patente</TableHead>
|
||||
<TableHead>Pedimento</TableHead>
|
||||
<TableHead>Aduana</TableHead>
|
||||
<TableHead>Clave</TableHead>
|
||||
<TableHead>Fecha Pago</TableHead>
|
||||
<TableHead>Fracción</TableHead>
|
||||
<TableHead>UMT</TableHead>
|
||||
<TableHead class="text-right">Cantidad Tarifa</TableHead>
|
||||
<TableHead class="w-[100px]">Acciones</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#if !formData?.descargos || formData.descargos.length === 0}
|
||||
<TableRow>
|
||||
<TableCell colspan={9} class="text-center text-muted-foreground py-8">
|
||||
No hay descargos registrados
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{:else}
|
||||
{#each formData.descargos as descargo, index}
|
||||
<TableRow>
|
||||
<TableCell>{descargo.patente}</TableCell>
|
||||
<TableCell>{descargo.pedimento}</TableCell>
|
||||
<TableCell>{descargo.aduana}</TableCell>
|
||||
<TableCell>{descargo.clave}</TableCell>
|
||||
<TableCell>{descargo.fecha_pago}</TableCell>
|
||||
<TableCell>{descargo.fraccion}</TableCell>
|
||||
<TableCell>{descargo.umt}</TableCell>
|
||||
<TableCell class="text-right"
|
||||
>{descargo.cantidad_tarifa.toLocaleString('es-MX', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}</TableCell
|
||||
>
|
||||
<TableCell>
|
||||
<div class="flex gap-1">
|
||||
<Button size="sm" variant="ghost" onclick={() => openEditDescargo(index)}>
|
||||
<Pencil size={14} />
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost" onclick={() => deleteDescargo(index)}>
|
||||
<Trash2 size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
{/if}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<Button size="sm" variant="outline" onclick={importarDescargos}>
|
||||
<Upload class="mr-1.5" size={14} />
|
||||
Importar Descargos (Formato CSV)
|
||||
</Button>
|
||||
<Button size="sm" onclick={openNewDescargo}>
|
||||
<Plus class="mr-1.5" size={14} />
|
||||
Nuevo
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={formData?.descargos?.length === 0}
|
||||
onclick={() => {
|
||||
const index = 0;
|
||||
if (formData?.descargos?.[index]) openEditDescargo(index);
|
||||
}}
|
||||
>
|
||||
<Pencil class="mr-1.5" size={14} />
|
||||
Editar
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
disabled={formData?.descargos?.length === 0}
|
||||
onclick={() => {
|
||||
const index = 0;
|
||||
if (formData?.descargos?.[index]) deleteDescargo(index);
|
||||
}}
|
||||
>
|
||||
<Trash2 class="mr-1.5" size={14} />
|
||||
Borrar
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
disabled={formData?.descargos?.length === 0}
|
||||
onclick={deleteAllDescargos}
|
||||
>
|
||||
<Trash class="mr-1.5" size={14} />
|
||||
Borrar Todo
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Sección de Destinatarios -->
|
||||
<Card>
|
||||
<CardHeader class="pb-3">
|
||||
<CardTitle class="text-base">Destinatarios</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-4">
|
||||
<div class="border rounded-lg overflow-hidden">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Clave</TableHead>
|
||||
<TableHead>Identificación Fiscal</TableHead>
|
||||
<TableHead>Nombre</TableHead>
|
||||
<TableHead class="w-[100px]">Acciones</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#if !formData?.destinatarios || formData.destinatarios.length === 0}
|
||||
<TableRow>
|
||||
<TableCell colspan={4} class="text-center text-muted-foreground py-8">
|
||||
No hay destinatarios registrados
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{:else}
|
||||
{#each formData.destinatarios as destinatario, index}
|
||||
<TableRow>
|
||||
<TableCell>{destinatario.clave}</TableCell>
|
||||
<TableCell>{destinatario.identificacion_fiscal}</TableCell>
|
||||
<TableCell>{destinatario.nombre}</TableCell>
|
||||
<TableCell>
|
||||
<div class="flex gap-1">
|
||||
<Button size="sm" variant="ghost" onclick={() => openEditDestinatario(index)}>
|
||||
<Pencil size={14} />
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost" onclick={() => deleteDestinatario(index)}>
|
||||
<Trash2 size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
{/if}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<Button size="sm" onclick={openNewDestinatario}>
|
||||
<Plus class="mr-1.5" size={14} />
|
||||
Nuevo
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={formData?.destinatarios?.length === 0}
|
||||
onclick={() => {
|
||||
const index = 0;
|
||||
if (formData?.destinatarios?.[index]) openEditDestinatario(index);
|
||||
}}
|
||||
>
|
||||
<Pencil class="mr-1.5" size={14} />
|
||||
Editar
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
disabled={formData?.destinatarios?.length === 0}
|
||||
onclick={() => {
|
||||
const index = 0;
|
||||
if (formData?.destinatarios?.[index]) deleteDestinatario(index);
|
||||
}}
|
||||
>
|
||||
<Trash2 class="mr-1.5" size={14} />
|
||||
Borrar
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<!-- Dialog para Descargos -->
|
||||
<Dialog bind:open={isDescargoDialogOpen}>
|
||||
<DialogContent class="max-w-3xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{editingDescargoIndex !== null ? 'Editar Descargo' : 'Nuevo Descargo'}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div class="grid gap-4 py-4">
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="d_patente">Patente *</Label>
|
||||
<Input id="d_patente" bind:value={currentDescargo.patente} placeholder="0000" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="d_pedimento">Pedimento *</Label>
|
||||
<Input
|
||||
id="d_pedimento"
|
||||
bind:value={currentDescargo.pedimento}
|
||||
placeholder="00 00 0000 0000000"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="d_aduana">Aduana *</Label>
|
||||
<Input id="d_aduana" bind:value={currentDescargo.aduana} placeholder="00" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="d_clave">Clave *</Label>
|
||||
<Input id="d_clave" bind:value={currentDescargo.clave} placeholder="Clave" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="d_fecha_pago">Fecha Pago *</Label>
|
||||
<Input id="d_fecha_pago" type="date" bind:value={currentDescargo.fecha_pago} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="d_fraccion">Fracción *</Label>
|
||||
<Input id="d_fraccion" bind:value={currentDescargo.fraccion} placeholder="0000.00.00" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="d_umt">UMT *</Label>
|
||||
<Input id="d_umt" bind:value={currentDescargo.umt} placeholder="Ej: KG" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="d_cantidad_tarifa">Cantidad Tarifa *</Label>
|
||||
<Input
|
||||
id="d_cantidad_tarifa"
|
||||
type="number"
|
||||
step="0.01"
|
||||
bind:value={currentDescargo.cantidad_tarifa}
|
||||
placeholder="0.00"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onclick={() => (isDescargoDialogOpen = false)}>Cancelar</Button>
|
||||
<Button
|
||||
onclick={saveDescargo}
|
||||
disabled={!currentDescargo.patente ||
|
||||
!currentDescargo.pedimento ||
|
||||
!currentDescargo.aduana ||
|
||||
!currentDescargo.clave ||
|
||||
!currentDescargo.fecha_pago ||
|
||||
!currentDescargo.fraccion ||
|
||||
!currentDescargo.umt}
|
||||
>
|
||||
{editingDescargoIndex !== null ? 'Actualizar' : 'Guardar'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<!-- Dialog para Destinatarios -->
|
||||
<Dialog bind:open={isDestinatarioDialogOpen}>
|
||||
<DialogContent class="max-w-2xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{editingDestinatarioIndex !== null ? 'Editar Destinatario' : 'Nuevo Destinatario'}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div class="grid gap-4 py-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="dest_clave">Clave *</Label>
|
||||
<Input id="dest_clave" bind:value={currentDestinatario.clave} placeholder="Clave" />
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="dest_identificacion">Identificación Fiscal *</Label>
|
||||
<Input
|
||||
id="dest_identificacion"
|
||||
bind:value={currentDestinatario.identificacion_fiscal}
|
||||
placeholder="RFC o identificación fiscal"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="dest_nombre">Nombre *</Label>
|
||||
<Input
|
||||
id="dest_nombre"
|
||||
bind:value={currentDestinatario.nombre}
|
||||
placeholder="Nombre del destinatario"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onclick={() => (isDestinatarioDialogOpen = false)}
|
||||
>Cancelar</Button
|
||||
>
|
||||
<Button
|
||||
onclick={saveDestinatario}
|
||||
disabled={!currentDestinatario.clave ||
|
||||
!currentDestinatario.identificacion_fiscal ||
|
||||
!currentDestinatario.nombre}
|
||||
>
|
||||
{editingDestinatarioIndex !== null ? 'Actualizar' : 'Guardar'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
@@ -0,0 +1,414 @@
|
||||
<script lang="ts">
|
||||
import { Card, CardContent } from '$lib/components/ui/card';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow
|
||||
} from '$lib/components/ui/table';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogFooter
|
||||
} from '$lib/components/ui/dialog';
|
||||
import { Textarea } from '$lib/components/ui/textarea';
|
||||
import {
|
||||
Plus,
|
||||
Pencil,
|
||||
Trash2,
|
||||
Upload,
|
||||
FileText,
|
||||
Search,
|
||||
FolderOpen,
|
||||
CheckCircle,
|
||||
ChevronsLeft,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
ChevronsRight
|
||||
} from 'lucide-svelte';
|
||||
|
||||
interface Digitalizacion {
|
||||
id?: number;
|
||||
linea: number;
|
||||
clave: string;
|
||||
documento: string;
|
||||
e_document: string;
|
||||
operacion: string;
|
||||
observaciones: string;
|
||||
}
|
||||
|
||||
let {
|
||||
formData = $bindable({
|
||||
digitalizaciones: [] as Digitalizacion[]
|
||||
})
|
||||
}: {
|
||||
formData: {
|
||||
digitalizaciones: Digitalizacion[];
|
||||
};
|
||||
} = $props();
|
||||
|
||||
// Estados para diálogo
|
||||
let isDialogOpen = $state(false);
|
||||
let editingIndex = $state<number | null>(null);
|
||||
|
||||
// Paginación
|
||||
let currentPage = $state(0);
|
||||
let pageSize = 10;
|
||||
|
||||
let currentDigitalizacion = $state<Digitalizacion>({
|
||||
linea: 0,
|
||||
clave: '',
|
||||
documento: '',
|
||||
e_document: '',
|
||||
operacion: '',
|
||||
observaciones: ''
|
||||
});
|
||||
|
||||
const totalPages = $derived(
|
||||
Math.ceil((formData?.digitalizaciones?.length ?? 0) / pageSize)
|
||||
);
|
||||
|
||||
const paginatedDigitalizaciones = $derived(
|
||||
formData?.digitalizaciones?.slice(
|
||||
currentPage * pageSize,
|
||||
(currentPage + 1) * pageSize
|
||||
) ?? []
|
||||
);
|
||||
|
||||
function openNewDigitalizacion() {
|
||||
editingIndex = null;
|
||||
const nextLinea = formData?.digitalizaciones?.length
|
||||
? Math.max(...formData.digitalizaciones.map((d) => d.linea)) + 1
|
||||
: 1;
|
||||
currentDigitalizacion = {
|
||||
linea: nextLinea,
|
||||
clave: '',
|
||||
documento: '',
|
||||
e_document: '',
|
||||
operacion: '',
|
||||
observaciones: ''
|
||||
};
|
||||
isDialogOpen = true;
|
||||
}
|
||||
|
||||
function openEditDigitalizacion(index: number) {
|
||||
if (!formData?.digitalizaciones) return;
|
||||
const realIndex = currentPage * pageSize + index;
|
||||
editingIndex = realIndex;
|
||||
currentDigitalizacion = { ...formData.digitalizaciones[realIndex] };
|
||||
isDialogOpen = true;
|
||||
}
|
||||
|
||||
function saveDigitalizacion() {
|
||||
if (!formData?.digitalizaciones) return;
|
||||
if (editingIndex !== null) {
|
||||
formData.digitalizaciones[editingIndex] = { ...currentDigitalizacion };
|
||||
} else {
|
||||
formData.digitalizaciones = [...formData.digitalizaciones, { ...currentDigitalizacion }];
|
||||
}
|
||||
isDialogOpen = false;
|
||||
}
|
||||
|
||||
function deleteDigitalizacion(index: number) {
|
||||
if (!formData?.digitalizaciones) return;
|
||||
const realIndex = currentPage * pageSize + index;
|
||||
if (confirm('¿Está seguro de eliminar esta digitalización?')) {
|
||||
formData.digitalizaciones = formData.digitalizaciones.filter((_, i) => i !== realIndex);
|
||||
// Reordenar líneas
|
||||
formData.digitalizaciones = formData.digitalizaciones.map((d, idx) => ({
|
||||
...d,
|
||||
linea: idx + 1
|
||||
}));
|
||||
// Ajustar página si es necesario
|
||||
if (paginatedDigitalizaciones.length === 0 && currentPage > 0) {
|
||||
currentPage--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function digitalizarVU() {
|
||||
alert('Digitalizar en VU no implementado aún');
|
||||
}
|
||||
|
||||
function acuseDigitalizacion() {
|
||||
alert('Acuse de Digitalización no implementado aún');
|
||||
}
|
||||
|
||||
function procesarDigitalizacion() {
|
||||
alert('Procesar Digitalización no implementado aún');
|
||||
}
|
||||
|
||||
function consultarOperacion() {
|
||||
alert('Consultar Operación no implementado aún');
|
||||
}
|
||||
|
||||
function abrirDocumento() {
|
||||
alert('Abrir Documento no implementado aún');
|
||||
}
|
||||
|
||||
function cargarEDocument() {
|
||||
alert('Cargar E-Document no implementado aún');
|
||||
}
|
||||
|
||||
function goToFirstPage() {
|
||||
currentPage = 0;
|
||||
}
|
||||
|
||||
function goToPreviousPage() {
|
||||
if (currentPage > 0) currentPage--;
|
||||
}
|
||||
|
||||
function goToNextPage() {
|
||||
if (currentPage < totalPages - 1) currentPage++;
|
||||
}
|
||||
|
||||
function goToLastPage() {
|
||||
currentPage = totalPages - 1;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card>
|
||||
<CardContent class="p-6 space-y-4">
|
||||
<!-- Tabla de digitalizaciones -->
|
||||
<div class="border rounded-lg overflow-hidden">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead class="w-[80px]">Línea</TableHead>
|
||||
<TableHead>Clave</TableHead>
|
||||
<TableHead>Documento</TableHead>
|
||||
<TableHead>E-Document</TableHead>
|
||||
<TableHead>Operación</TableHead>
|
||||
<TableHead>Observaciones</TableHead>
|
||||
<TableHead class="w-[100px]">Acciones</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#if !formData?.digitalizaciones || formData.digitalizaciones.length === 0}
|
||||
<TableRow>
|
||||
<TableCell colspan={7} class="text-center text-muted-foreground py-8">
|
||||
No hay digitalizaciones registradas
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{:else if paginatedDigitalizaciones.length === 0}
|
||||
<TableRow>
|
||||
<TableCell colspan={7} class="text-center text-muted-foreground py-8">
|
||||
No hay datos en esta página
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{:else}
|
||||
{#each paginatedDigitalizaciones as digitalizacion, index}
|
||||
<TableRow>
|
||||
<TableCell class="font-medium">{digitalizacion.linea}</TableCell>
|
||||
<TableCell>{digitalizacion.clave}</TableCell>
|
||||
<TableCell>{digitalizacion.documento}</TableCell>
|
||||
<TableCell>{digitalizacion.e_document}</TableCell>
|
||||
<TableCell>{digitalizacion.operacion}</TableCell>
|
||||
<TableCell class="max-w-[200px] truncate" title={digitalizacion.observaciones}>
|
||||
{digitalizacion.observaciones}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div class="flex gap-1">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onclick={() => openEditDigitalizacion(index)}
|
||||
>
|
||||
<Pencil size={14} />
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost" onclick={() => deleteDigitalizacion(index)}>
|
||||
<Trash2 size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
{/if}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<!-- Controles de paginación -->
|
||||
{#if formData?.digitalizaciones && formData.digitalizaciones.length > 0}
|
||||
<div class="flex items-center justify-center gap-2 border-t pt-3">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onclick={goToFirstPage}
|
||||
disabled={currentPage === 0}
|
||||
class="w-8 h-8 p-0"
|
||||
>
|
||||
<ChevronsLeft size={16} />
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onclick={goToPreviousPage}
|
||||
disabled={currentPage === 0}
|
||||
class="w-8 h-8 p-0"
|
||||
>
|
||||
<ChevronLeft size={16} />
|
||||
</Button>
|
||||
<span class="text-sm text-muted-foreground px-2">
|
||||
Página {currentPage + 1} de {totalPages || 1}
|
||||
</span>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onclick={goToNextPage}
|
||||
disabled={currentPage >= totalPages - 1}
|
||||
class="w-8 h-8 p-0"
|
||||
>
|
||||
<ChevronRight size={16} />
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onclick={goToLastPage}
|
||||
disabled={currentPage >= totalPages - 1}
|
||||
class="w-8 h-8 p-0"
|
||||
>
|
||||
<ChevronsRight size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Botones de acción -->
|
||||
<div class="flex flex-wrap gap-2 border-t pt-4">
|
||||
<Button size="sm" variant="outline" onclick={digitalizarVU}>
|
||||
<Upload class="mr-1.5" size={14} />
|
||||
Digitalizar en VU
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onclick={acuseDigitalizacion}>
|
||||
<CheckCircle class="mr-1.5" size={14} />
|
||||
Acuse de Digitalización
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onclick={procesarDigitalizacion}>
|
||||
<FileText class="mr-1.5" size={14} />
|
||||
Procesar Digitalización
|
||||
</Button>
|
||||
<Button size="sm" onclick={openNewDigitalizacion}>
|
||||
<Plus class="mr-1.5" size={14} />
|
||||
Nuevo
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={formData?.digitalizaciones?.length === 0}
|
||||
onclick={() => {
|
||||
if (paginatedDigitalizaciones[0]) openEditDigitalizacion(0);
|
||||
}}
|
||||
>
|
||||
<Pencil class="mr-1.5" size={14} />
|
||||
Editar
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
disabled={formData?.digitalizaciones?.length === 0}
|
||||
onclick={() => {
|
||||
if (paginatedDigitalizaciones[0]) deleteDigitalizacion(0);
|
||||
}}
|
||||
>
|
||||
<Trash2 class="mr-1.5" size={14} />
|
||||
Borrar
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onclick={consultarOperacion}>
|
||||
<Search class="mr-1.5" size={14} />
|
||||
Consultar Operación
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onclick={abrirDocumento}>
|
||||
<FolderOpen class="mr-1.5" size={14} />
|
||||
Abrir Documento
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onclick={cargarEDocument}>
|
||||
<Upload class="mr-1.5" size={14} />
|
||||
Cargar E-Document
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Dialog para Digitalización -->
|
||||
<Dialog bind:open={isDialogOpen}>
|
||||
<DialogContent class="max-w-2xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{editingIndex !== null ? 'Editar Digitalización' : 'Nueva Digitalización'}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div class="grid gap-4 py-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="linea">Línea *</Label>
|
||||
<Input
|
||||
id="linea"
|
||||
type="number"
|
||||
bind:value={currentDigitalizacion.linea}
|
||||
placeholder="1"
|
||||
readonly
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="clave">Clave *</Label>
|
||||
<Input id="clave" bind:value={currentDigitalizacion.clave} placeholder="Clave" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="documento">Documento *</Label>
|
||||
<Input
|
||||
id="documento"
|
||||
bind:value={currentDigitalizacion.documento}
|
||||
placeholder="Nombre o tipo del documento"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="e_document">E-Document</Label>
|
||||
<Input
|
||||
id="e_document"
|
||||
bind:value={currentDigitalizacion.e_document}
|
||||
placeholder="Referencia del documento electrónico"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="operacion">Operación</Label>
|
||||
<Input
|
||||
id="operacion"
|
||||
bind:value={currentDigitalizacion.operacion}
|
||||
placeholder="Tipo de operación"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="observaciones">Observaciones</Label>
|
||||
<Textarea
|
||||
id="observaciones"
|
||||
bind:value={currentDigitalizacion.observaciones}
|
||||
placeholder="Observaciones adicionales"
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onclick={() => (isDialogOpen = false)}>Cancelar</Button>
|
||||
<Button
|
||||
onclick={saveDigitalizacion}
|
||||
disabled={!currentDigitalizacion.clave || !currentDigitalizacion.documento}
|
||||
>
|
||||
{editingIndex !== null ? 'Actualizar' : 'Guardar'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
@@ -0,0 +1,530 @@
|
||||
<script lang="ts">
|
||||
import { Card, CardContent } from '$lib/components/ui/card';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow
|
||||
} from '$lib/components/ui/table';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogFooter
|
||||
} from '$lib/components/ui/dialog';
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import Checkbox from '$lib/components/ui/checkbox/checkbox.svelte';
|
||||
import { Textarea } from '$lib/components/ui/textarea';
|
||||
import {
|
||||
Plus,
|
||||
Pencil,
|
||||
Trash2,
|
||||
FileSpreadsheet,
|
||||
FileText,
|
||||
Check,
|
||||
RotateCw,
|
||||
ArrowUpDown
|
||||
} from 'lucide-svelte';
|
||||
|
||||
interface Factura {
|
||||
id?: number;
|
||||
numero_factura: string;
|
||||
fecha: string;
|
||||
proveedor: string;
|
||||
pais: string;
|
||||
moneda: string;
|
||||
valor_factura: number;
|
||||
valor_usd: number;
|
||||
es_consolidado: boolean;
|
||||
arribo_consolidado: boolean;
|
||||
inicio?: string;
|
||||
fin?: string;
|
||||
detalle_e_document?: string;
|
||||
}
|
||||
|
||||
let {
|
||||
formData = $bindable({
|
||||
facturas: [] as Factura[]
|
||||
})
|
||||
}: {
|
||||
formData: {
|
||||
facturas: Factura[];
|
||||
};
|
||||
} = $props();
|
||||
|
||||
let isDialogOpen = $state(false);
|
||||
let editingIndex = $state<number | null>(null);
|
||||
let selectedFacturas = $state<Set<number>>(new Set());
|
||||
|
||||
let currentFactura = $state<Factura>({
|
||||
numero_factura: '',
|
||||
fecha: '',
|
||||
proveedor: '',
|
||||
pais: '',
|
||||
moneda: 'USD',
|
||||
valor_factura: 0,
|
||||
valor_usd: 0,
|
||||
es_consolidado: false,
|
||||
arribo_consolidado: false,
|
||||
inicio: '',
|
||||
fin: '',
|
||||
detalle_e_document: ''
|
||||
});
|
||||
|
||||
const monedas = ['USD', 'MXN', 'EUR', 'CAD', 'JPY', 'GBP', 'CHF'];
|
||||
const paises = [
|
||||
'Estados Unidos',
|
||||
'México',
|
||||
'Canadá',
|
||||
'China',
|
||||
'Alemania',
|
||||
'Japón',
|
||||
'Reino Unido',
|
||||
'Francia',
|
||||
'Italia',
|
||||
'España',
|
||||
'Corea del Sur',
|
||||
'India'
|
||||
];
|
||||
|
||||
const totalFacturas = $derived(formData?.facturas?.length ?? 0);
|
||||
const sumaPesos = $derived(
|
||||
formData?.facturas?.reduce((sum, f) => sum + (f.valor_factura || 0), 0) ?? 0
|
||||
);
|
||||
const sumaDolares = $derived(formData?.facturas?.reduce((sum, f) => sum + (f.valor_usd || 0), 0) ?? 0);
|
||||
|
||||
function openNewFactura() {
|
||||
editingIndex = null;
|
||||
currentFactura = {
|
||||
numero_factura: '',
|
||||
fecha: '',
|
||||
proveedor: '',
|
||||
pais: '',
|
||||
moneda: 'USD',
|
||||
valor_factura: 0,
|
||||
valor_usd: 0,
|
||||
es_consolidado: false,
|
||||
arribo_consolidado: false,
|
||||
inicio: '',
|
||||
fin: '',
|
||||
detalle_e_document: ''
|
||||
};
|
||||
isDialogOpen = true;
|
||||
}
|
||||
|
||||
function openEditFactura(index: number) {
|
||||
if (!formData?.facturas) return;
|
||||
editingIndex = index;
|
||||
currentFactura = { ...formData.facturas[index] };
|
||||
isDialogOpen = true;
|
||||
}
|
||||
|
||||
function saveFactura() {
|
||||
if (!formData?.facturas) return;
|
||||
if (editingIndex !== null) {
|
||||
formData.facturas[editingIndex] = { ...currentFactura };
|
||||
} else {
|
||||
formData.facturas = [...formData.facturas, { ...currentFactura }];
|
||||
}
|
||||
isDialogOpen = false;
|
||||
}
|
||||
|
||||
function deleteFactura(index: number) {
|
||||
if (!formData?.facturas) return;
|
||||
if (confirm('¿Está seguro de eliminar esta factura?')) {
|
||||
formData.facturas = formData.facturas.filter((_, i) => i !== index);
|
||||
}
|
||||
}
|
||||
|
||||
function clearForm() {
|
||||
currentFactura = {
|
||||
numero_factura: '',
|
||||
fecha: '',
|
||||
proveedor: '',
|
||||
pais: '',
|
||||
moneda: 'USD',
|
||||
valor_factura: 0,
|
||||
valor_usd: 0,
|
||||
es_consolidado: false,
|
||||
arribo_consolidado: false,
|
||||
inicio: '',
|
||||
fin: '',
|
||||
detalle_e_document: ''
|
||||
};
|
||||
}
|
||||
|
||||
function toggleSelectFactura(index: number) {
|
||||
if (selectedFacturas.has(index)) {
|
||||
selectedFacturas.delete(index);
|
||||
} else {
|
||||
selectedFacturas.add(index);
|
||||
}
|
||||
selectedFacturas = new Set(selectedFacturas);
|
||||
}
|
||||
|
||||
function selectAllWithoutCove() {
|
||||
// Esta función seleccionaría facturas sin COVE
|
||||
// Por ahora seleccionamos todas
|
||||
if (!formData?.facturas) return;
|
||||
selectedFacturas = new Set(formData.facturas.map((_, i) => i));
|
||||
}
|
||||
|
||||
function deleteSelectedFacturas() {
|
||||
if (!formData?.facturas) return;
|
||||
if (selectedFacturas.size === 0) {
|
||||
alert('No hay facturas seleccionadas');
|
||||
return;
|
||||
}
|
||||
if (confirm(`¿Está seguro de eliminar ${selectedFacturas.size} factura(s)?`)) {
|
||||
formData.facturas = formData.facturas.filter((_, i) => !selectedFacturas.has(i));
|
||||
selectedFacturas = new Set();
|
||||
}
|
||||
}
|
||||
|
||||
function validateVentanillaUnica() {
|
||||
alert('Validación en Ventanilla Única no implementada aún');
|
||||
}
|
||||
|
||||
function importFacturas() {
|
||||
alert('Importación de facturas no implementada aún');
|
||||
}
|
||||
|
||||
function overwriteFacturas() {
|
||||
alert('Sobre escribir facturas no implementado aún');
|
||||
}
|
||||
|
||||
function reorderFacturas() {
|
||||
alert('Reordenar facturas no implementado aún');
|
||||
}
|
||||
|
||||
function datosSonana() {
|
||||
alert('Datos Sonana no implementado aún');
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card>
|
||||
<CardContent class="p-6 space-y-4">
|
||||
<!-- Barra de herramientas superior -->
|
||||
<div class="flex flex-wrap gap-2 border-b pb-4">
|
||||
<Button size="sm" variant="outline" onclick={clearForm}>
|
||||
<RotateCw class="mr-1.5" size={14} />
|
||||
Limpiar
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onclick={selectAllWithoutCove}>
|
||||
<Check class="mr-1.5" size={14} />
|
||||
Selec. sin cove
|
||||
</Button>
|
||||
<Button size="sm" onclick={openNewFactura}>
|
||||
<Plus class="mr-1.5" size={14} />
|
||||
Nuevo
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={selectedFacturas.size !== 1}
|
||||
onclick={() => {
|
||||
const index = Array.from(selectedFacturas)[0];
|
||||
if (index !== undefined) openEditFactura(index);
|
||||
}}
|
||||
>
|
||||
<Pencil class="mr-1.5" size={14} />
|
||||
Editar
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
disabled={selectedFacturas.size !== 1}
|
||||
onclick={() => {
|
||||
const index = Array.from(selectedFacturas)[0];
|
||||
if (index !== undefined) deleteFactura(index);
|
||||
}}
|
||||
>
|
||||
<Trash2 class="mr-1.5" size={14} />
|
||||
Borrar
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
disabled={selectedFacturas.size === 0}
|
||||
onclick={deleteSelectedFacturas}
|
||||
>
|
||||
<Trash2 class="mr-1.5" size={14} />
|
||||
Borrar Facturas Selec.
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onclick={validateVentanillaUnica}>
|
||||
<Check class="mr-1.5" size={14} />
|
||||
Validar en Ventanilla Única
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onclick={importFacturas}>
|
||||
<FileSpreadsheet class="mr-1.5" size={14} />
|
||||
Imp Facturas
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onclick={overwriteFacturas}>
|
||||
<FileText class="mr-1.5" size={14} />
|
||||
Sobre escribir
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onclick={reorderFacturas}>
|
||||
<ArrowUpDown class="mr-1.5" size={14} />
|
||||
Reordenar
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onclick={datosSonana}>
|
||||
<FileText class="mr-1.5" size={14} />
|
||||
Datos Sonana
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<!-- Tabla de facturas -->
|
||||
<div class="border rounded-lg overflow-hidden">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead class="w-[50px]">Sel.</TableHead>
|
||||
<TableHead>Núm. Factura</TableHead>
|
||||
<TableHead>Fecha</TableHead>
|
||||
<TableHead>Proveedor</TableHead>
|
||||
<TableHead>País</TableHead>
|
||||
<TableHead>Moneda</TableHead>
|
||||
<TableHead class="text-right">Valor Factura</TableHead>
|
||||
<TableHead class="text-right">Valor USD</TableHead>
|
||||
<TableHead class="text-center">Consolidado</TableHead>
|
||||
<TableHead class="text-center">Arribo Consol.</TableHead>
|
||||
<TableHead class="w-[100px]">Acciones</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#if !formData?.facturas || formData.facturas.length === 0}
|
||||
<TableRow>
|
||||
<TableCell colspan={11} class="text-center text-muted-foreground py-8">
|
||||
No hay facturas registradas. Haga clic en "Nuevo" para agregar una.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{:else}
|
||||
{#each formData.facturas as factura, index}
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Checkbox
|
||||
checked={selectedFacturas.has(index)}
|
||||
onCheckedChange={() => toggleSelectFactura(index)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell class="font-medium">{factura.numero_factura}</TableCell>
|
||||
<TableCell>{factura.fecha}</TableCell>
|
||||
<TableCell>{factura.proveedor}</TableCell>
|
||||
<TableCell>{factura.pais}</TableCell>
|
||||
<TableCell>{factura.moneda}</TableCell>
|
||||
<TableCell class="text-right"
|
||||
>{factura.valor_factura.toLocaleString('es-MX', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}</TableCell
|
||||
>
|
||||
<TableCell class="text-right"
|
||||
>{factura.valor_usd.toLocaleString('es-MX', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}</TableCell
|
||||
>
|
||||
<TableCell class="text-center">
|
||||
{#if factura.es_consolidado}
|
||||
<Check class="inline" size={16} />
|
||||
{/if}
|
||||
</TableCell>
|
||||
<TableCell class="text-center">
|
||||
{#if factura.arribo_consolidado}
|
||||
<Check class="inline" size={16} />
|
||||
{/if}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div class="flex gap-1">
|
||||
<Button size="sm" variant="ghost" onclick={() => openEditFactura(index)}>
|
||||
<Pencil size={14} />
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost" onclick={() => deleteFactura(index)}>
|
||||
<Trash2 size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
{/if}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<!-- Totales -->
|
||||
<div class="flex gap-6 text-sm font-medium border-t pt-4">
|
||||
<div>Facturas: <span class="text-primary">{totalFacturas}</span></div>
|
||||
<div>
|
||||
Suma Pesos: <span class="text-primary"
|
||||
>{sumaPesos.toLocaleString('es-MX', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
||||
MXN</span
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
Suma USD: <span class="text-primary"
|
||||
>{sumaDolares.toLocaleString('es-MX', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})} USD</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Dialog para agregar/editar factura -->
|
||||
<Dialog bind:open={isDialogOpen}>
|
||||
<DialogContent class="max-w-2xl max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editingIndex !== null ? 'Editar Factura' : 'Nueva Factura'}</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div class="grid gap-4 py-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="numero_factura">Núm. de Factura *</Label>
|
||||
<Input
|
||||
id="numero_factura"
|
||||
bind:value={currentFactura.numero_factura}
|
||||
placeholder="Ej: INV-2024-001"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="fecha">Fecha *</Label>
|
||||
<Input id="fecha" type="date" bind:value={currentFactura.fecha} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="proveedor">Proveedor *</Label>
|
||||
<Input
|
||||
id="proveedor"
|
||||
bind:value={currentFactura.proveedor}
|
||||
placeholder="Nombre del proveedor"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="pais">País *</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={currentFactura.pais}
|
||||
onValueChange={(v: string | undefined) => v && (currentFactura.pais = v)}
|
||||
>
|
||||
<Select.Trigger id="pais">
|
||||
{currentFactura.pais || 'Seleccionar país'}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each paises as pais}
|
||||
<Select.Item value={pais}>{pais}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="moneda">Moneda *</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={currentFactura.moneda}
|
||||
onValueChange={(v: string | undefined) => v && (currentFactura.moneda = v)}
|
||||
>
|
||||
<Select.Trigger id="moneda">
|
||||
{currentFactura.moneda || 'Seleccionar moneda'}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each monedas as moneda}
|
||||
<Select.Item value={moneda}>{moneda}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="valor_factura">Valor Factura *</Label>
|
||||
<Input
|
||||
id="valor_factura"
|
||||
type="number"
|
||||
step="0.01"
|
||||
bind:value={currentFactura.valor_factura}
|
||||
placeholder="0.00"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="valor_usd">Valor USD *</Label>
|
||||
<Input
|
||||
id="valor_usd"
|
||||
type="number"
|
||||
step="0.01"
|
||||
bind:value={currentFactura.valor_usd}
|
||||
placeholder="0.00"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="es_consolidado"
|
||||
checked={currentFactura.es_consolidado}
|
||||
onCheckedChange={(checked: boolean | 'indeterminate') => (currentFactura.es_consolidado = checked === true)}
|
||||
/>
|
||||
<Label for="es_consolidado" class="font-normal cursor-pointer">Es Consolidado</Label>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="arribo_consolidado"
|
||||
checked={currentFactura.arribo_consolidado}
|
||||
onCheckedChange={(checked: boolean | 'indeterminate') => (currentFactura.arribo_consolidado = checked === true)}
|
||||
/>
|
||||
<Label for="arribo_consolidado" class="font-normal cursor-pointer"
|
||||
>Arribo Consolidado</Label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="inicio">Inicio</Label>
|
||||
<Input id="inicio" type="date" bind:value={currentFactura.inicio} />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="fin">Fin</Label>
|
||||
<Input id="fin" type="date" bind:value={currentFactura.fin} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="detalle_e_document">Detalle de E_Document</Label>
|
||||
<Textarea
|
||||
id="detalle_e_document"
|
||||
bind:value={currentFactura.detalle_e_document}
|
||||
placeholder="Detalles adicionales del documento electrónico"
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onclick={() => (isDialogOpen = false)}>Cancelar</Button>
|
||||
<Button
|
||||
onclick={saveFactura}
|
||||
disabled={!currentFactura.numero_factura ||
|
||||
!currentFactura.fecha ||
|
||||
!currentFactura.proveedor ||
|
||||
!currentFactura.pais ||
|
||||
!currentFactura.moneda}
|
||||
>
|
||||
{editingIndex !== null ? 'Actualizar' : 'Guardar'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
@@ -4,6 +4,7 @@
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
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/refrence_data/pedimento_codes';
|
||||
import type { CustomsSection } from '$lib/api/dashboard/refrence_data/customs_sections';
|
||||
@@ -29,6 +30,9 @@
|
||||
codePedimentoRegimens?: CodePedimentoRegimen[];
|
||||
} = $props();
|
||||
|
||||
// Estado para controlar la sección activa de la navegación
|
||||
let activeSection = $state('fechas');
|
||||
|
||||
// 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)))
|
||||
@@ -198,6 +202,11 @@
|
||||
|
||||
// Obtener el año actual (últimos 2 dígitos)
|
||||
const currentYear = String(new Date().getFullYear()).slice(-2);
|
||||
|
||||
// Obtener fecha y hora actual para captura
|
||||
const now = new Date();
|
||||
const currentDate = now.toISOString().substring(0, 10); // YYYY-MM-DD
|
||||
const currentTime = now.toTimeString().substring(0, 5); // HH:MM
|
||||
|
||||
// Inicializar formData con los valores del pedimento (o vacío si es null)
|
||||
if (!formData) {
|
||||
@@ -223,7 +232,10 @@
|
||||
extraction_date: datesData?.extraction_date ? datesData.extraction_date.substring(0, 10) : '',
|
||||
rectification_payment_date: datesData?.rectification_payment_date ? datesData.rectification_payment_date.substring(0, 10) : '',
|
||||
original_date: datesData?.original_date ? datesData.original_date.substring(0, 10) : '',
|
||||
payment_date: datesData?.payment_date ? datesData.payment_date.substring(0, 10) : ''
|
||||
payment_date: datesData?.payment_date ? datesData.payment_date.substring(0, 10) : '',
|
||||
// Campos de captura automática
|
||||
fecha_captura: currentDate,
|
||||
hora_captura: currentTime
|
||||
};
|
||||
}
|
||||
|
||||
@@ -236,10 +248,10 @@
|
||||
|
||||
// Obtener automáticamente el tipo de cambio cuando cambie la fecha de entrada
|
||||
$effect(() => {
|
||||
if (formData && formData.entry_date) {
|
||||
if (formData && formData.entry_date && companyStore.activeCompany) {
|
||||
console.log('Buscando tipo de cambio para fecha:', formData.entry_date);
|
||||
|
||||
getExchangeRateByDate(formData.entry_date)
|
||||
getExchangeRateByDate(formData.entry_date, companyStore.activeCompany.id)
|
||||
.then(usdRate => {
|
||||
console.log('Tipo de cambio USD encontrado:', usdRate);
|
||||
if (usdRate && formData) {
|
||||
@@ -277,6 +289,14 @@
|
||||
{ value: 'consolidado', label: 'Consolidado' },
|
||||
{ value: 'normal', label: 'Normal' }
|
||||
];
|
||||
|
||||
const tipoOperacionOptions = [
|
||||
{ value: 'incrementables', label: 'Incrementables' },
|
||||
{ value: 'identificadores', label: 'Identificadores' },
|
||||
{ value: 'indices', label: 'Indices' },
|
||||
{ value: 'adicional', label: 'Adicional' },
|
||||
{ value: 'decrementable', label: 'Decrementable' }
|
||||
];
|
||||
</script>
|
||||
|
||||
<Card.Root>
|
||||
@@ -346,7 +366,7 @@
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.license || ''}
|
||||
onValueChange={(v: string) => formData.license = v ?? ''}
|
||||
onValueChange={(v: string | undefined) => formData.license = v || null}
|
||||
>
|
||||
<Select.Trigger class="w-full md:w-24">
|
||||
<span class="truncate">
|
||||
@@ -354,12 +374,17 @@
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-w-[300px] max-h-[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 patentes disponibles
|
||||
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 customsBrokers as broker}
|
||||
{#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}>
|
||||
@@ -560,7 +585,9 @@
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<!-- Valor USD -->
|
||||
<div class="space-y-2">
|
||||
<Label for="usd_value">Valor USD</Label>
|
||||
@@ -595,71 +622,540 @@
|
||||
bind:value={formData.gross_weight}
|
||||
placeholder="Ej: 100.00"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Fechas -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<!-- Fecha de Entrada -->
|
||||
<!-- Despacho -->
|
||||
<div class="space-y-2">
|
||||
<Label for="entry_date">Fecha de Entrada</Label>
|
||||
<Label for="despacho">Despacho</Label>
|
||||
<Input
|
||||
id="entry_date"
|
||||
type="date"
|
||||
bind:value={formData.entry_date}
|
||||
id="despacho"
|
||||
type="number"
|
||||
bind:value={formData.despacho}
|
||||
placeholder="0"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Fecha de Presentación -->
|
||||
<!-- Aduana E/S -->
|
||||
<div class="space-y-2">
|
||||
<Label for="pedimento_date">Fecha de Presentación</Label>
|
||||
<Label for="aduana_es">Aduana E/S</Label>
|
||||
<Input
|
||||
id="pedimento_date"
|
||||
type="date"
|
||||
bind:value={formData.pedimento_date}
|
||||
id="aduana_es"
|
||||
type="number"
|
||||
bind:value={formData.aduana_es}
|
||||
placeholder="0"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Fecha de Extracción -->
|
||||
<!-- E/S -->
|
||||
<div class="space-y-2">
|
||||
<Label for="extraction_date">Fecha de Extracción</Label>
|
||||
<Label for="es">E/S</Label>
|
||||
<Input
|
||||
id="extraction_date"
|
||||
type="date"
|
||||
bind:value={formData.extraction_date}
|
||||
id="es"
|
||||
type="number"
|
||||
bind:value={formData.es}
|
||||
placeholder="0"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Fecha de Pago Rectificación -->
|
||||
<!-- Fecha de Captura -->
|
||||
<div class="space-y-2">
|
||||
<Label for="rectification_payment_date">Fecha de Pago R1</Label>
|
||||
<Label for="fecha_captura">Fecha de Captura</Label>
|
||||
<Input
|
||||
id="rectification_payment_date"
|
||||
id="fecha_captura"
|
||||
type="date"
|
||||
bind:value={formData.rectification_payment_date}
|
||||
bind:value={formData.fecha_captura}
|
||||
readonly
|
||||
disabled
|
||||
class="bg-muted cursor-not-allowed"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Fecha Original -->
|
||||
<!-- Hora de Captura -->
|
||||
<div class="space-y-2">
|
||||
<Label for="original_date">Fecha de Pago Original</Label>
|
||||
<Label for="hora_captura">Hora de Captura</Label>
|
||||
<Input
|
||||
id="original_date"
|
||||
type="date"
|
||||
bind:value={formData.original_date}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Fecha de Pago -->
|
||||
<div class="space-y-2">
|
||||
<Label for="payment_date">Fecha de Pago</Label>
|
||||
<Input
|
||||
id="payment_date"
|
||||
type="date"
|
||||
bind:value={formData.payment_date}
|
||||
id="hora_captura"
|
||||
type="time"
|
||||
bind:value={formData.hora_captura}
|
||||
readonly
|
||||
disabled
|
||||
class="bg-muted cursor-not-allowed"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lista de navegación rápida -->
|
||||
<div class="w-full overflow-x-auto pb-2">
|
||||
<div class="inline-flex md:grid md:w-full md:grid-cols-6 bg-muted p-1 text-muted-foreground rounded-md">
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => activeSection = 'fechas'}
|
||||
data-state={activeSection === 'fechas' ? 'active' : ''}
|
||||
class="inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 cursor-pointer hover:bg-background/50 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 items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 cursor-pointer hover:bg-background/50 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 items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 cursor-pointer hover:bg-background/50 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 items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 cursor-pointer hover:bg-background/50 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 items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 cursor-pointer hover:bg-background/50 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 items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 cursor-pointer hover:bg-background/50 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 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<!-- Fecha de Entrada -->
|
||||
<div class="space-y-2">
|
||||
<Label for="entry_date">Fecha de Entrada</Label>
|
||||
<Input
|
||||
id="entry_date"
|
||||
type="date"
|
||||
bind:value={formData.entry_date}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Fecha de Presentación -->
|
||||
<div class="space-y-2">
|
||||
<Label for="pedimento_date">Fecha de Presentación</Label>
|
||||
<Input
|
||||
id="pedimento_date"
|
||||
type="date"
|
||||
bind:value={formData.pedimento_date}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Fecha de Extracción -->
|
||||
<div class="space-y-2">
|
||||
<Label for="extraction_date">Fecha de Extracción</Label>
|
||||
<Input
|
||||
id="extraction_date"
|
||||
type="date"
|
||||
bind:value={formData.extraction_date}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Fecha de Pago Rectificación -->
|
||||
<div class="space-y-2">
|
||||
<Label for="rectification_payment_date">Fecha de Pago R1</Label>
|
||||
<Input
|
||||
id="rectification_payment_date"
|
||||
type="date"
|
||||
bind:value={formData.rectification_payment_date}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Fecha Original -->
|
||||
<div class="space-y-2">
|
||||
<Label for="original_date">Fecha de Pago Original</Label>
|
||||
<Input
|
||||
id="original_date"
|
||||
type="date"
|
||||
bind:value={formData.original_date}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Fecha de Pago -->
|
||||
<div class="space-y-2">
|
||||
<Label for="payment_date">Fecha de Pago</Label>
|
||||
<Input
|
||||
id="payment_date"
|
||||
type="date"
|
||||
bind:value={formData.payment_date}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{: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 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<!-- 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="number"
|
||||
bind:value={formData.moneda_incrementables}
|
||||
placeholder="0.00"
|
||||
/>
|
||||
</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 -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<p class="text-muted-foreground col-span-full">Campos de Identificadores - Por configurar</p>
|
||||
</div>
|
||||
{:else if activeSection === 'indices'}
|
||||
<!-- Indices -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<!-- 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 items-center space-x-2 h-10">
|
||||
<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 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<!-- 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="text-center w-40 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 items-center space-x-2 h-10">
|
||||
<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 items-center space-x-2 h-10">
|
||||
<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 items-center space-x-2 h-10">
|
||||
<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 items-center space-x-2 h-10">
|
||||
<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 items-center space-x-2 h-10">
|
||||
<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 items-center space-x-2 h-10">
|
||||
<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 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<!-- 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 items-center space-x-2 h-10">
|
||||
<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>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,543 @@
|
||||
<script lang="ts">
|
||||
import { Card, CardContent } from '$lib/components/ui/card';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow
|
||||
} from '$lib/components/ui/table';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogFooter
|
||||
} from '$lib/components/ui/dialog';
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import Checkbox from '$lib/components/ui/checkbox/checkbox.svelte';
|
||||
import { Textarea } from '$lib/components/ui/textarea';
|
||||
import {
|
||||
Plus,
|
||||
Pencil,
|
||||
Trash2,
|
||||
FileText,
|
||||
RotateCw,
|
||||
Info
|
||||
} from 'lucide-svelte';
|
||||
|
||||
interface Partida {
|
||||
id?: number;
|
||||
linea: number;
|
||||
fraccion: string;
|
||||
sub: string;
|
||||
descripcion: string;
|
||||
tipo: string;
|
||||
sector: string;
|
||||
origen: string;
|
||||
cantidad_comercial: number;
|
||||
umc: string;
|
||||
dolares: number;
|
||||
}
|
||||
|
||||
let {
|
||||
formData = $bindable({
|
||||
partidas: [] as Partida[],
|
||||
convertir_uma_umc: false
|
||||
})
|
||||
}: {
|
||||
formData: {
|
||||
partidas: Partida[];
|
||||
convertir_uma_umc: boolean;
|
||||
};
|
||||
} = $props();
|
||||
|
||||
let isDialogOpen = $state(false);
|
||||
let editingIndex = $state<number | null>(null);
|
||||
let selectedPartidas = $state<Set<number>>(new Set());
|
||||
|
||||
let currentPartida = $state<Partida>({
|
||||
linea: 0,
|
||||
fraccion: '',
|
||||
sub: '',
|
||||
descripcion: '',
|
||||
tipo: '',
|
||||
sector: '',
|
||||
origen: '',
|
||||
cantidad_comercial: 0,
|
||||
umc: '',
|
||||
dolares: 0
|
||||
});
|
||||
|
||||
const unidadesMedida = ['KG', 'LT', 'PZ', 'MT', 'M2', 'M3', 'TON', 'CAJ', 'PAR'];
|
||||
const tipos = ['Importación', 'Exportación', 'Retorno'];
|
||||
const sectores = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'];
|
||||
const origenes = [
|
||||
'Estados Unidos',
|
||||
'México',
|
||||
'Canadá',
|
||||
'China',
|
||||
'Alemania',
|
||||
'Japón',
|
||||
'Reino Unido',
|
||||
'Francia',
|
||||
'Italia',
|
||||
'España'
|
||||
];
|
||||
|
||||
const totalPartidas = $derived(formData?.partidas?.length ?? 0);
|
||||
const totalDolares = $derived(
|
||||
formData?.partidas?.reduce((sum, p) => sum + (p.dolares || 0), 0) ?? 0
|
||||
);
|
||||
const totalCantidadComercial = $derived(
|
||||
formData?.partidas?.reduce((sum, p) => sum + (p.cantidad_comercial || 0), 0) ?? 0
|
||||
);
|
||||
|
||||
function openNewPartida() {
|
||||
editingIndex = null;
|
||||
const nextLinea = formData?.partidas?.length ? Math.max(...formData.partidas.map(p => p.linea)) + 1 : 1;
|
||||
currentPartida = {
|
||||
linea: nextLinea,
|
||||
fraccion: '',
|
||||
sub: '',
|
||||
descripcion: '',
|
||||
tipo: '',
|
||||
sector: '',
|
||||
origen: '',
|
||||
cantidad_comercial: 0,
|
||||
umc: '',
|
||||
dolares: 0
|
||||
};
|
||||
isDialogOpen = true;
|
||||
}
|
||||
|
||||
function openEditPartida(index: number) {
|
||||
if (!formData?.partidas) return;
|
||||
editingIndex = index;
|
||||
currentPartida = { ...formData.partidas[index] };
|
||||
isDialogOpen = true;
|
||||
}
|
||||
|
||||
function savePartida() {
|
||||
if (!formData?.partidas) return;
|
||||
if (editingIndex !== null) {
|
||||
formData.partidas[editingIndex] = { ...currentPartida };
|
||||
} else {
|
||||
formData.partidas = [...formData.partidas, { ...currentPartida }];
|
||||
}
|
||||
isDialogOpen = false;
|
||||
}
|
||||
|
||||
function deletePartida(index: number) {
|
||||
if (!formData?.partidas) return;
|
||||
if (confirm('¿Está seguro de eliminar esta partida?')) {
|
||||
formData.partidas = formData.partidas.filter((_, i) => i !== index);
|
||||
// Reordenar líneas
|
||||
formData.partidas = formData.partidas.map((p, idx) => ({ ...p, linea: idx + 1 }));
|
||||
}
|
||||
}
|
||||
|
||||
function clearForm() {
|
||||
currentPartida = {
|
||||
linea: 0,
|
||||
fraccion: '',
|
||||
sub: '',
|
||||
descripcion: '',
|
||||
tipo: '',
|
||||
sector: '',
|
||||
origen: '',
|
||||
cantidad_comercial: 0,
|
||||
umc: '',
|
||||
dolares: 0
|
||||
};
|
||||
}
|
||||
|
||||
function toggleSelectPartida(index: number) {
|
||||
if (selectedPartidas.has(index)) {
|
||||
selectedPartidas.delete(index);
|
||||
} else {
|
||||
selectedPartidas.add(index);
|
||||
}
|
||||
selectedPartidas = new Set(selectedPartidas);
|
||||
}
|
||||
|
||||
function deleteSelectedPartidas() {
|
||||
if (!formData?.partidas) return;
|
||||
if (selectedPartidas.size === 0) {
|
||||
alert('No hay partidas seleccionadas');
|
||||
return;
|
||||
}
|
||||
if (confirm(`¿Está seguro de eliminar ${selectedPartidas.size} partida(s)?`)) {
|
||||
formData.partidas = formData.partidas.filter((_, i) => !selectedPartidas.has(i));
|
||||
// Reordenar líneas
|
||||
formData.partidas = formData.partidas.map((p, idx) => ({ ...p, linea: idx + 1 }));
|
||||
selectedPartidas = new Set();
|
||||
}
|
||||
}
|
||||
|
||||
function detallesConsolidacion() {
|
||||
alert('Detalles de Consolidación no implementado aún');
|
||||
}
|
||||
|
||||
function datosSonana() {
|
||||
alert('Datos Sonana no implementado aún');
|
||||
}
|
||||
|
||||
function refreshPartidas() {
|
||||
alert('Actualizar partidas no implementado aún');
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card>
|
||||
<CardContent class="p-6 space-y-4">
|
||||
<!-- Barra de herramientas superior -->
|
||||
<div class="flex flex-wrap gap-2 border-b pb-4">
|
||||
<Button size="sm" variant="outline" onclick={clearForm}>
|
||||
<RotateCw class="mr-1.5" size={14} />
|
||||
Limpiar
|
||||
</Button>
|
||||
<Button size="sm" onclick={openNewPartida}>
|
||||
<Plus class="mr-1.5" size={14} />
|
||||
Nuevo
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={selectedPartidas.size !== 1}
|
||||
onclick={() => {
|
||||
const index = Array.from(selectedPartidas)[0];
|
||||
if (index !== undefined) openEditPartida(index);
|
||||
}}
|
||||
>
|
||||
<Pencil class="mr-1.5" size={14} />
|
||||
Editar
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
disabled={selectedPartidas.size !== 1}
|
||||
onclick={() => {
|
||||
const index = Array.from(selectedPartidas)[0];
|
||||
if (index !== undefined) deletePartida(index);
|
||||
}}
|
||||
>
|
||||
<Trash2 class="mr-1.5" size={14} />
|
||||
Borrar
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
disabled={selectedPartidas.size === 0}
|
||||
onclick={deleteSelectedPartidas}
|
||||
>
|
||||
<Trash2 class="mr-1.5" size={14} />
|
||||
Borrar Partidas
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onclick={detallesConsolidacion}>
|
||||
<Info class="mr-1.5" size={14} />
|
||||
Detalles de Consolidación
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onclick={datosSonana}>
|
||||
<FileText class="mr-1.5" size={14} />
|
||||
Datos Sonana
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onclick={refreshPartidas}>
|
||||
<RotateCw size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<!-- Tabla de partidas -->
|
||||
<div class="border rounded-lg overflow-hidden">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead class="w-[50px]">Sel.</TableHead>
|
||||
<TableHead class="w-[80px]">Línea</TableHead>
|
||||
<TableHead>Fracción</TableHead>
|
||||
<TableHead>Sub</TableHead>
|
||||
<TableHead>Descripción</TableHead>
|
||||
<TableHead>Tipo</TableHead>
|
||||
<TableHead>Sector</TableHead>
|
||||
<TableHead>Origen</TableHead>
|
||||
<TableHead class="text-right">Cant. Comercial</TableHead>
|
||||
<TableHead>UMC</TableHead>
|
||||
<TableHead class="text-right">Dólares</TableHead>
|
||||
<TableHead class="w-[100px]">Acciones</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{#if !formData?.partidas || formData.partidas.length === 0}
|
||||
<TableRow>
|
||||
<TableCell colspan={12} class="text-center text-muted-foreground py-8">
|
||||
No hay partidas registradas. Haga clic en "Nuevo" para agregar una.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{:else}
|
||||
{#each formData.partidas as partida, index}
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Checkbox
|
||||
checked={selectedPartidas.has(index)}
|
||||
onCheckedChange={() => toggleSelectPartida(index)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell class="font-medium">{partida.linea}</TableCell>
|
||||
<TableCell>{partida.fraccion}</TableCell>
|
||||
<TableCell>{partida.sub}</TableCell>
|
||||
<TableCell class="max-w-[200px] truncate" title={partida.descripcion}>
|
||||
{partida.descripcion}
|
||||
</TableCell>
|
||||
<TableCell>{partida.tipo}</TableCell>
|
||||
<TableCell>{partida.sector}</TableCell>
|
||||
<TableCell>{partida.origen}</TableCell>
|
||||
<TableCell class="text-right"
|
||||
>{partida.cantidad_comercial.toLocaleString('es-MX', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}</TableCell
|
||||
>
|
||||
<TableCell>{partida.umc}</TableCell>
|
||||
<TableCell class="text-right"
|
||||
>{partida.dolares.toLocaleString('es-MX', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}</TableCell
|
||||
>
|
||||
<TableCell>
|
||||
<div class="flex gap-1">
|
||||
<Button size="sm" variant="ghost" onclick={() => openEditPartida(index)}>
|
||||
<Pencil size={14} />
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost" onclick={() => deletePartida(index)}>
|
||||
<Trash2 size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/each}
|
||||
{/if}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<!-- Totales y opciones -->
|
||||
<div class="grid grid-cols-2 gap-6 border-t pt-4">
|
||||
<div class="space-y-2 text-sm">
|
||||
<div class="flex justify-between">
|
||||
<span>Partidas:</span>
|
||||
<span class="font-medium text-primary">{totalPartidas}</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span>Dólares:</span>
|
||||
<span class="font-medium text-primary"
|
||||
>{totalDolares.toLocaleString('es-MX', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})} USD</span
|
||||
>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span>Pesos:</span>
|
||||
<span class="font-medium text-primary">0.00 MXN</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span>Valor Comercial:</span>
|
||||
<span class="font-medium text-primary">0.00</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2 text-sm">
|
||||
<div class="flex justify-between">
|
||||
<span>Cant. UMC:</span>
|
||||
<span class="font-medium text-primary"
|
||||
>{totalCantidadComercial.toLocaleString('es-MX', {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2
|
||||
})}</span
|
||||
>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span>Cant. UMT:</span>
|
||||
<span class="font-medium text-primary">0.00</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span>Val Agreg. Pesos:</span>
|
||||
<span class="font-medium text-primary">0.00 MXN</span>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<span>Val Agreg. Dólares:</span>
|
||||
<span class="font-medium text-primary">0.00 USD</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Opciones adicionales -->
|
||||
<div class="flex items-center space-x-2 border-t pt-4">
|
||||
<Checkbox
|
||||
id="convertir_uma_umc"
|
||||
checked={formData?.convertir_uma_umc ?? false}
|
||||
onCheckedChange={(checked: boolean | 'indeterminate') =>
|
||||
formData && (formData.convertir_uma_umc = checked === true)}
|
||||
/>
|
||||
<Label for="convertir_uma_umc" class="font-normal cursor-pointer text-sm"
|
||||
>Captura Partidas: Convertir de UMA a UMC</Label
|
||||
>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- Dialog para agregar/editar partida -->
|
||||
<Dialog bind:open={isDialogOpen}>
|
||||
<DialogContent class="max-w-3xl max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editingIndex !== null ? 'Editar Partida' : 'Nueva Partida'}</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div class="grid gap-4 py-4">
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="linea">Línea *</Label>
|
||||
<Input
|
||||
id="linea"
|
||||
type="number"
|
||||
bind:value={currentPartida.linea}
|
||||
placeholder="1"
|
||||
readonly
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="fraccion">Fracción *</Label>
|
||||
<Input id="fraccion" bind:value={currentPartida.fraccion} placeholder="0000.00.00" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="sub">Sub</Label>
|
||||
<Input id="sub" bind:value={currentPartida.sub} placeholder="00" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="descripcion">Descripción *</Label>
|
||||
<Textarea
|
||||
id="descripcion"
|
||||
bind:value={currentPartida.descripcion}
|
||||
placeholder="Descripción detallada de la partida"
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="tipo">Tipo *</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={currentPartida.tipo}
|
||||
onValueChange={(v: string | undefined) => v && (currentPartida.tipo = v)}
|
||||
>
|
||||
<Select.Trigger id="tipo">
|
||||
{currentPartida.tipo || 'Seleccionar tipo'}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each tipos as tipo}
|
||||
<Select.Item value={tipo}>{tipo}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="sector">Sector *</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={currentPartida.sector}
|
||||
onValueChange={(v: string | undefined) => v && (currentPartida.sector = v)}
|
||||
>
|
||||
<Select.Trigger id="sector">
|
||||
{currentPartida.sector || 'Seleccionar sector'}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each sectores as sector}
|
||||
<Select.Item value={sector}>{sector}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="origen">Origen *</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={currentPartida.origen}
|
||||
onValueChange={(v: string | undefined) => v && (currentPartida.origen = v)}
|
||||
>
|
||||
<Select.Trigger id="origen">
|
||||
{currentPartida.origen || 'Seleccionar origen'}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each origenes as origen}
|
||||
<Select.Item value={origen}>{origen}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="cantidad_comercial">Cant. Comercial *</Label>
|
||||
<Input
|
||||
id="cantidad_comercial"
|
||||
type="number"
|
||||
step="0.01"
|
||||
bind:value={currentPartida.cantidad_comercial}
|
||||
placeholder="0.00"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="umc">UMC *</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={currentPartida.umc}
|
||||
onValueChange={(v: string | undefined) => v && (currentPartida.umc = v)}
|
||||
>
|
||||
<Select.Trigger id="umc">
|
||||
{currentPartida.umc || 'Seleccionar UMC'}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each unidadesMedida as unidad}
|
||||
<Select.Item value={unidad}>{unidad}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="dolares">Dólares *</Label>
|
||||
<Input
|
||||
id="dolares"
|
||||
type="number"
|
||||
step="0.01"
|
||||
bind:value={currentPartida.dolares}
|
||||
placeholder="0.00"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onclick={() => (isDialogOpen = false)}>Cancelar</Button>
|
||||
<Button
|
||||
onclick={savePartida}
|
||||
disabled={!currentPartida.fraccion ||
|
||||
!currentPartida.descripcion ||
|
||||
!currentPartida.tipo ||
|
||||
!currentPartida.sector ||
|
||||
!currentPartida.origen ||
|
||||
!currentPartida.umc}
|
||||
>
|
||||
{editingIndex !== null ? 'Actualizar' : 'Guardar'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
36
frontend/src/lib/components/ui/checkbox/checkbox.svelte
Normal file
36
frontend/src/lib/components/ui/checkbox/checkbox.svelte
Normal file
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import { Checkbox as CheckboxPrimitive } from "bits-ui";
|
||||
import CheckIcon from "@lucide/svelte/icons/check";
|
||||
import MinusIcon from "@lucide/svelte/icons/minus";
|
||||
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
checked = $bindable(false),
|
||||
indeterminate = $bindable(false),
|
||||
class: className,
|
||||
...restProps
|
||||
}: WithoutChildrenOrChild<CheckboxPrimitive.RootProps> = $props();
|
||||
</script>
|
||||
|
||||
<CheckboxPrimitive.Root
|
||||
bind:ref
|
||||
data-slot="checkbox"
|
||||
class={cn(
|
||||
"border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive peer flex size-4 shrink-0 items-center justify-center rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
bind:checked
|
||||
bind:indeterminate
|
||||
{...restProps}
|
||||
>
|
||||
{#snippet children({ checked, indeterminate })}
|
||||
<div data-slot="checkbox-indicator" class="text-current transition-none">
|
||||
{#if checked}
|
||||
<CheckIcon class="size-3.5" />
|
||||
{:else if indeterminate}
|
||||
<MinusIcon class="size-3.5" />
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
</CheckboxPrimitive.Root>
|
||||
6
frontend/src/lib/components/ui/checkbox/index.ts
Normal file
6
frontend/src/lib/components/ui/checkbox/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import Root from "./checkbox.svelte";
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as Checkbox,
|
||||
};
|
||||
@@ -10,10 +10,15 @@
|
||||
CircleAlert,
|
||||
CircleCheck,
|
||||
FileText,
|
||||
Calendar,
|
||||
CreditCard,
|
||||
Truck,
|
||||
ShieldCheck,
|
||||
MessageSquare,
|
||||
FileSpreadsheet,
|
||||
Package,
|
||||
Box,
|
||||
Download,
|
||||
Coins,
|
||||
Wallet,
|
||||
FileStack,
|
||||
Scan,
|
||||
LoaderCircle,
|
||||
Save
|
||||
} from 'lucide-svelte';
|
||||
@@ -22,6 +27,14 @@
|
||||
// Importar los componentes de cada pestaña (ahora sin botones de guardar propios)
|
||||
import GeneralTabForm from '$lib/components/dashboard/pedimentos/edit/general-tab-form.svelte';
|
||||
import DatesTabForm from '$lib/components/dashboard/pedimentos/edit/dates-tab-form.svelte';
|
||||
import FacturasTabForm from '$lib/components/dashboard/pedimentos/edit/facturas-tab-form.svelte';
|
||||
import PartidasTabForm from '$lib/components/dashboard/pedimentos/edit/partidas-tab-form.svelte';
|
||||
import BultosTransportesTabForm from '$lib/components/dashboard/pedimentos/edit/bultos-transportes-tab-form.svelte';
|
||||
import DescargasTabForm from '$lib/components/dashboard/pedimentos/edit/descargas-tab-form.svelte';
|
||||
import ContribucionesTabForm from '$lib/components/dashboard/pedimentos/edit/contribuciones-tab-form.svelte';
|
||||
import CuentasCompensacionTabForm from '$lib/components/dashboard/pedimentos/edit/cuentas-compensacion-tab-form.svelte';
|
||||
import DigitalizacionTabForm from '$lib/components/dashboard/pedimentos/edit/digitalizacion-tab-form.svelte';
|
||||
import OtrosDatosTabForm from '$lib/components/dashboard/pedimentos/edit/otros-datos-tab-form.svelte';
|
||||
import PaymentsTabForm from '$lib/components/dashboard/pedimentos/edit/payments-tab-form.svelte';
|
||||
import TransportTabForm from '$lib/components/dashboard/pedimentos/edit/transport-tab-form.svelte';
|
||||
import ValidationTabForm from '$lib/components/dashboard/pedimentos/edit/validation-tab-form.svelte';
|
||||
@@ -63,16 +76,83 @@
|
||||
|
||||
// Referencias a los componentes de formulario para obtener sus datos
|
||||
let generalFormData = $state<any>(null);
|
||||
let datesFormData = $state<any>(null);
|
||||
let paymentsFormData = $state<any>(null);
|
||||
let transportFormData = $state<any>(null);
|
||||
let validationFormData = $state<any>(null);
|
||||
let observacionesFormData = $state<any>(null);
|
||||
let facturasFormData = $state<any>(null);
|
||||
let partidasFormData = $state<any>({ partidas: [], convertir_uma_umc: false });
|
||||
let bultosTransportesFormData = $state<any>({
|
||||
bultos: { cantidad: 0, marcas: 'S/M', numero: 'S/N', vehiculos: 1 },
|
||||
transportes: [],
|
||||
precintos: [],
|
||||
contenedores: []
|
||||
});
|
||||
let descargasFormData = $state<any>({ descargos: [], destinatarios: [] });
|
||||
let contribucionesFormData = $state<any>({
|
||||
dta: { forma_pago_recargo: 0, tasa_recargo: 0.0, importe_recargo: 0 },
|
||||
prev: { forma_pago_prevalidacion: 0 },
|
||||
eci: { forma_pago_eci: 0, importe_eci: 0 },
|
||||
mult: { forma_pago_multa: 0, importe_multa: 0 },
|
||||
rec: { forma_pago_uia: 0, importe_compensar: 0 },
|
||||
calculo_manual: false,
|
||||
operaciones_regla_31_40: false,
|
||||
contribuciones: []
|
||||
});
|
||||
let cuentasCompensacionFormData = $state<any>({
|
||||
cuentas_garantia: [],
|
||||
compensaciones: [],
|
||||
documentos_pago: []
|
||||
});
|
||||
let digitalizacionFormData = $state({
|
||||
digitalizaciones: []
|
||||
});
|
||||
let otrosDatosFormData = $state({
|
||||
tipo_calculo: 'ninguno',
|
||||
dta_por_operacion_ag_facturas: false,
|
||||
dta_por_numero_vehiculos: false,
|
||||
paga_iva: false,
|
||||
paga_prevalidacion: false,
|
||||
aplicar_dta_8_millar_partida: false,
|
||||
incluir_eci: false,
|
||||
deducible_recargos: false,
|
||||
cuota_fija_adicional_vehiculo: false,
|
||||
cuota_fija_adicional_fp: '0',
|
||||
actualizar_iva: false,
|
||||
actualizar_advalorem: false,
|
||||
actualizar_dta: false,
|
||||
actualizar_cc: false,
|
||||
actualizar_ieps: false,
|
||||
es_embajada: false,
|
||||
embajada_dta: '0.00',
|
||||
regla_3_1_21_factores: false,
|
||||
cambio_tarifa_anterior: '',
|
||||
actualizar_iva_rect: false,
|
||||
actualizar_advalorem_rect: false,
|
||||
actualizar_dta_rect: false,
|
||||
actualizar_cc_rect: false,
|
||||
actualizar_ieps_rect: false,
|
||||
calcular_recargos_diferencias: false,
|
||||
proveedor_nacional_modifico_dta: false,
|
||||
calculo_valor_aduana_v2: false,
|
||||
calculo_2_decimales_valor_unitario: false,
|
||||
calcular_valor_aduana_base_partidas: false,
|
||||
recargo_igi: false,
|
||||
recargo_iva: false,
|
||||
recargo_ieps: false,
|
||||
recargo_isan: false,
|
||||
recargo_cc: false,
|
||||
factor_incrementable_manual: '0.00000000000000000000',
|
||||
agregar_entidad_federativa_proveedor: false
|
||||
});
|
||||
|
||||
// Estados para saber si existen datos previos (para compatibilidad con componentes hijos)
|
||||
let datesExists = $state(false);
|
||||
let paymentsExists = $state(false);
|
||||
let transportExists = $state(false);
|
||||
let validationExists = $state(false);
|
||||
let observacionesExists = $state(false);
|
||||
let facturasExists = $state(false);
|
||||
let partidasExists = $state(false);
|
||||
let bultosTransportesExists = $state(false);
|
||||
let descargasExists = $state(false);
|
||||
let contribucionesExists = $state(false);
|
||||
let cuentasCompensacionExists = $state(false);
|
||||
let otrosDatosExists = $state(false);
|
||||
let digitalizacionExists = $state(false);
|
||||
|
||||
function handleBack() {
|
||||
goto('/dashboard/pedimentos');
|
||||
@@ -149,87 +229,86 @@
|
||||
// Solo agregar sub-recursos en modo UPDATE (no en CREATE)
|
||||
// Y solo si tienen valores reales (no enviar objetos vacíos/null)
|
||||
|
||||
// Dates - solo enviar si hay al menos un campo con valor
|
||||
if (datesFormData) {
|
||||
const hasDateValue = datesFormData.entry_date || datesFormData.pedimento_date ||
|
||||
datesFormData.payment_date || datesFormData.rectification_payment_date ||
|
||||
datesFormData.extraction_date || datesFormData.submission_date ||
|
||||
datesFormData.eucan_date || datesFormData.original_date ||
|
||||
datesFormData.start_date || datesFormData.end_date;
|
||||
|
||||
if (hasDateValue) {
|
||||
payload.pedimento_dates = {
|
||||
entry_date: datesFormData.entry_date || null,
|
||||
pedimento_date: datesFormData.pedimento_date || null,
|
||||
payment_date: datesFormData.payment_date || null,
|
||||
rectification_payment_date: datesFormData.rectification_payment_date || null,
|
||||
extraction_date: datesFormData.extraction_date || null,
|
||||
submission_date: datesFormData.submission_date || null,
|
||||
eucan_date: datesFormData.eucan_date || null,
|
||||
original_date: datesFormData.original_date || null,
|
||||
start_date: datesFormData.start_date || null,
|
||||
end_date: datesFormData.end_date || null,
|
||||
// Observaciones - solo enviar si hay valor
|
||||
if (observacionesFormData?.observaciones) {
|
||||
payload.observaciones = observacionesFormData.observaciones;
|
||||
}
|
||||
|
||||
// Incrementables - solo enviar si hay al menos un campo con valor
|
||||
if (generalFormData) {
|
||||
const hasIncrementablesValue = generalFormData.valor_seguro || generalFormData.embalajes ||
|
||||
generalFormData.fletes || generalFormData.deducibles || generalFormData.moneda_incrementables ||
|
||||
generalFormData.no_afectar_valor_dolares_inc !== undefined || generalFormData.no_afectar_valor_aduana !== undefined;
|
||||
if (hasIncrementablesValue) {
|
||||
payload.pedimento_incrementables = {
|
||||
insured_value: generalFormData.valor_seguro || null,
|
||||
packaging: generalFormData.embalajes || null,
|
||||
freight: generalFormData.fletes || null,
|
||||
deductibles: generalFormData.deducibles || null,
|
||||
currency: generalFormData.moneda_incrementables || null,
|
||||
not_affect_usd_value: generalFormData.no_afectar_valor_dolares_inc ? 1 : 0,
|
||||
not_affect_customs_value: generalFormData.no_afectar_valor_aduana ? 1 : 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Payments - solo enviar si hay al menos un campo con valor
|
||||
if (paymentsFormData) {
|
||||
const hasPaymentValue = paymentsFormData.acknowledgment || paymentsFormData.operation_number ||
|
||||
paymentsFormData.bank_code || paymentsFormData.cashier || paymentsFormData.date ||
|
||||
paymentsFormData.time || paymentsFormData.shift || paymentsFormData.total_cash_paid ||
|
||||
paymentsFormData.total_contributions || paymentsFormData.counter_payment ||
|
||||
paymentsFormData.pece_code;
|
||||
if (hasPaymentValue) {
|
||||
payload.pedimento_payments = {
|
||||
acknowledgment: paymentsFormData.acknowledgment || null,
|
||||
operation_number: paymentsFormData.operation_number || null,
|
||||
bank_code: paymentsFormData.bank_code || null,
|
||||
cashier: paymentsFormData.cashier || null,
|
||||
date: paymentsFormData.date || null,
|
||||
time: paymentsFormData.time || null,
|
||||
shift: paymentsFormData.shift || null,
|
||||
total_cash_paid: paymentsFormData.total_cash_paid || null,
|
||||
total_contributions: paymentsFormData.total_contributions || null,
|
||||
counter_payment: paymentsFormData.counter_payment || null,
|
||||
pece_code: paymentsFormData.pece_code || null,
|
||||
// Decrementables - solo enviar si hay al menos un campo con valor
|
||||
if (generalFormData) {
|
||||
const hasDecrementablesValue = generalFormData.fletes_decrementable || generalFormData.seguros ||
|
||||
generalFormData.carga || generalFormData.descarga || generalFormData.otros ||
|
||||
generalFormData.moneda_decrementable || generalFormData.afectar_valor_dolares !== undefined;
|
||||
if (hasDecrementablesValue) {
|
||||
payload.pedimento_decrementables = {
|
||||
freight: generalFormData.fletes_decrementable || null,
|
||||
insurance: generalFormData.seguros || null,
|
||||
loading: generalFormData.carga || null,
|
||||
unloading: generalFormData.descarga || null,
|
||||
others: generalFormData.otros || null,
|
||||
currency: generalFormData.moneda_decrementable || null,
|
||||
not_affect_usd_value: generalFormData.afectar_valor_dolares ? 1 : 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Transport - solo enviar si hay al menos un campo con valor
|
||||
if (transportFormData) {
|
||||
const hasTransportValue = transportFormData.destination || transportFormData.entry_exit ||
|
||||
transportFormData.arrival || transportFormData.departure;
|
||||
if (hasTransportValue) {
|
||||
payload.pedimento_transport_means = {
|
||||
destination: transportFormData.destination || null,
|
||||
entry_exit: transportFormData.entry_exit || null,
|
||||
arrival: transportFormData.arrival || null,
|
||||
departure: transportFormData.departure || null
|
||||
// Indexes - solo enviar si hay al menos un campo con valor
|
||||
if (generalFormData) {
|
||||
const hasIndexesValue = generalFormData.tipo_factor || generalFormData.factor_actualizacion ||
|
||||
generalFormData.factor_actualizacion_manual !== undefined;
|
||||
if (hasIndexesValue) {
|
||||
// Mapear tipo_factor de string a número
|
||||
let updateFactorType = null;
|
||||
if (generalFormData.tipo_factor === 'INPC') {
|
||||
updateFactorType = 1;
|
||||
} else if (generalFormData.tipo_factor === 'variacion_cambiaria') {
|
||||
updateFactorType = 2;
|
||||
}
|
||||
|
||||
payload.pedimento_indexes = {
|
||||
update_factor_type: updateFactorType,
|
||||
update_factor: generalFormData.factor_actualizacion || null,
|
||||
manual_update_factor: generalFormData.factor_actualizacion_manual ? 1 : 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Validation - solo enviar si hay al menos un campo con valor
|
||||
if (validationFormData) {
|
||||
const hasValidationValue = validationFormData.validator || validationFormData.validation_ack ||
|
||||
validationFormData.pre_ack || validationFormData.line_signature ||
|
||||
validationFormData.electronic_signature || validationFormData.certificate_number ||
|
||||
validationFormData.validator_id || validationFormData.responsible_id;
|
||||
if (hasValidationValue) {
|
||||
payload.pedimento_validation = {
|
||||
validator: validationFormData.validator || null,
|
||||
validation_ack: validationFormData.validation_ack || null,
|
||||
pre_ack: validationFormData.pre_ack || null,
|
||||
line_signature: validationFormData.line_signature || null,
|
||||
electronic_signature: validationFormData.electronic_signature || null,
|
||||
certificate_number: validationFormData.certificate_number || null,
|
||||
validator_id: validationFormData.validator_id || null,
|
||||
responsible_id: validationFormData.responsible_id || null
|
||||
// Config Additional - solo enviar si hay al menos un campo con valor
|
||||
if (generalFormData) {
|
||||
const hasConfigAdditionalValue = generalFormData.anio_impresion ||
|
||||
generalFormData.agregar_po_auto !== undefined || generalFormData.no_eximir_normas !== undefined ||
|
||||
generalFormData.activar_destinatario !== undefined || generalFormData.agregar_registro_502 !== undefined ||
|
||||
generalFormData.agregar_quitar_normas !== undefined || generalFormData.facturar_cove !== undefined;
|
||||
if (hasConfigAdditionalValue) {
|
||||
payload.pedimento_config_additional = {
|
||||
manual_pedimento_year: generalFormData.anio_impresion || null,
|
||||
add_po_identifier: generalFormData.agregar_po_auto ? 1 : 0,
|
||||
do_not_exempt_norms_complement_x: generalFormData.no_eximir_normas ? 1 : 0,
|
||||
enable_import_invoice_recipient: generalFormData.activar_destinatario ? 1 : 0,
|
||||
send_502_validation_file_for_consolidated: generalFormData.agregar_registro_502 ? 1 : 0,
|
||||
add_remove_norms: generalFormData.agregar_quitar_normas ? 1 : 0
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Eliminar campos undefined para no enviarlos
|
||||
Object.keys(payload).forEach(key => {
|
||||
@@ -296,14 +375,17 @@
|
||||
</h1>
|
||||
{#if data.isCreate}
|
||||
<Badge variant="default">Nuevo</Badge>
|
||||
{:else}
|
||||
<Badge variant={getStatusColor(data.pedimento.status)}>
|
||||
{data.pedimento.status || 'Sin estado'}
|
||||
{/if}
|
||||
{#if generalFormData?.status || data.pedimento?.status}
|
||||
<Badge variant={getStatusColor(generalFormData?.status || data.pedimento?.status)}>
|
||||
{generalFormData?.status || data.pedimento?.status}
|
||||
</Badge>
|
||||
{/if}
|
||||
</div>
|
||||
<p class="text-muted-foreground">
|
||||
{#if !data.isCreate && data.pedimento.pedimento_number}
|
||||
{#if generalFormData?.year && generalFormData?.customs_office && generalFormData?.license && generalFormData?.pedimento_number}
|
||||
Número: {generalFormData.year}-{generalFormData.customs_office}-{generalFormData.license}-{generalFormData.pedimento_number}
|
||||
{:else if !data.isCreate && data.pedimento?.pedimento_number}
|
||||
Número: {data.pedimento.year}-{data.pedimento.customs_office}-{data.pedimento.license}-{data.pedimento.pedimento_number}
|
||||
{:else}
|
||||
Edita los detalles del pedimento
|
||||
@@ -332,7 +414,7 @@
|
||||
{/if}
|
||||
|
||||
<!-- Contenido de las tabs con padding inferior para el footer flotante -->
|
||||
<div class="pb-48">
|
||||
<div class="pb-56">
|
||||
<Tabs.Root bind:value={activeTab} class="space-y-4">
|
||||
<Tabs.Content value="general">
|
||||
<GeneralTabForm
|
||||
@@ -349,86 +431,130 @@
|
||||
<Tabs.Content value="dates">
|
||||
<DatesTabForm
|
||||
pedimento={data.pedimento}
|
||||
bind:formData={datesFormData}
|
||||
bind:exists={datesExists}
|
||||
/>
|
||||
bind:formData={observacionesFormData}
|
||||
bind:exists={observacionesExists}
|
||||
pedimentoType={generalFormData?.pedimento_type || data.pedimento?.pedimento_type || ''}
|
||||
pedimentoNumber={
|
||||
generalFormData?.year && generalFormData?.customs_office && generalFormData?.license && generalFormData?.pedimento_number
|
||||
? `${generalFormData.year}-${generalFormData.customs_office}-${generalFormData.license}-${generalFormData.pedimento_number}`
|
||||
: (!data.isCreate && data.pedimento?.pedimento_number
|
||||
? `${data.pedimento.year}-${data.pedimento.customs_office}-${data.pedimento.license}-${data.pedimento.pedimento_number}`
|
||||
: '')
|
||||
}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
|
||||
<!-- <Tabs.Content value="facturas">
|
||||
<FacturasTabForm
|
||||
bind:formData={facturasFormData}
|
||||
/>
|
||||
</Tabs.Content> -->
|
||||
|
||||
<!-- <!-- <Tabs.Content value="partidas">
|
||||
<PartidasTabForm
|
||||
bind:formData={partidasFormData}
|
||||
/>
|
||||
</Tabs.Content> -->
|
||||
|
||||
<Tabs.Content value="bultos-transportes">
|
||||
<BultosTransportesTabForm
|
||||
bind:formData={bultosTransportesFormData}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
<!-- <Tabs.Content value="descargas">
|
||||
<DescargasTabForm
|
||||
bind:formData={descargasFormData}
|
||||
/>
|
||||
</Tabs.Content> -->
|
||||
<Tabs.Content value="contribuciones">
|
||||
<ContribucionesTabForm
|
||||
bind:formData={contribucionesFormData}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="cuentas-compensacion">
|
||||
<CuentasCompensacionTabForm
|
||||
bind:formData={cuentasCompensacionFormData}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="otros-datos">
|
||||
<OtrosDatosTabForm bind:formData={otrosDatosFormData} />
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="payments">
|
||||
<PaymentsTabForm
|
||||
pedimento={data.pedimento}
|
||||
bind:formData={paymentsFormData}
|
||||
bind:exists={paymentsExists}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="transport">
|
||||
<TransportTabForm
|
||||
pedimento={data.pedimento}
|
||||
bind:formData={transportFormData}
|
||||
bind:exists={transportExists}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="validation">
|
||||
<ValidationTabForm
|
||||
pedimento={data.pedimento}
|
||||
bind:formData={validationFormData}
|
||||
bind:exists={validationExists}
|
||||
/>
|
||||
<Tabs.Content value="digitalizacion">
|
||||
<DigitalizacionTabForm bind:formData={digitalizacionFormData} />
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer fijo en la parte inferior - Fuera del contenedor principal -->
|
||||
<div
|
||||
class="fixed bottom-0 left-0 right-0 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80 border-t shadow-lg z-[5] transition-[left] duration-200 ease-linear"
|
||||
style:left={sidebar.isMobile ? '0' : (sidebar.open ? 'var(--sidebar-width)' : '0')}
|
||||
>
|
||||
<div class="px-4 py-4 space-y-4 max-w-[1400px] mx-auto">
|
||||
<!-- Tabs Navigation -->
|
||||
<Tabs.Root bind:value={activeTab}>
|
||||
<Tabs.Root bind:value={activeTab}>
|
||||
<div
|
||||
class="fixed bottom-0 left-0 right-0 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80 border-t shadow-lg z-[5] transition-[left] duration-200 ease-linear"
|
||||
style:left={sidebar.isMobile ? '0' : (sidebar.open ? 'var(--sidebar-width)' : '0')}
|
||||
>
|
||||
<div class="px-4 py-4 space-y-4 max-w-[1400px] mx-auto">
|
||||
<!-- Tabs Navigation -->
|
||||
<div class="w-full overflow-x-auto pb-2">
|
||||
<Tabs.List class="inline-flex md:grid md:w-full md:grid-cols-5">
|
||||
<Tabs.Trigger value="general" disabled={false} class="whitespace-nowrap">
|
||||
<FileText size={16} class="mr-2" />
|
||||
General
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger value="dates" disabled={false} class="whitespace-nowrap">
|
||||
<Calendar size={16} class="mr-2" />
|
||||
Fechas
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger value="payments" disabled={false} class="whitespace-nowrap">
|
||||
<CreditCard size={16} class="mr-2" />
|
||||
Pagos
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger value="transport" disabled={false} class="whitespace-nowrap">
|
||||
<Truck size={16} class="mr-2" />
|
||||
Transporte
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger value="validation" disabled={false} class="whitespace-nowrap">
|
||||
<ShieldCheck size={16} class="mr-2" />
|
||||
Validación
|
||||
</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
<Tabs.List class="inline-flex w-full gap-1">
|
||||
<Tabs.Trigger value="general" class="flex items-center gap-1.5 px-2.5 py-1.5 text-sm">
|
||||
<FileText size={14} />
|
||||
<span>General</span>
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger value="dates" class="flex items-center gap-1.5 px-2.5 py-1.5 text-sm">
|
||||
<MessageSquare size={14} />
|
||||
<span>Observ.</span>
|
||||
</Tabs.Trigger>
|
||||
<!-- <Tabs.Trigger value="facturas" class="flex items-center gap-1.5 px-2.5 py-1.5 text-sm">
|
||||
<FileSpreadsheet size={14} />
|
||||
<span>Facturas</span>
|
||||
</Tabs.Trigger> -->
|
||||
<!-- <Tabs.Trigger value="partidas" class="flex items-center gap-1.5 px-2.5 py-1.5 text-sm">
|
||||
<Package size={14} />
|
||||
<span>Partidas</span>
|
||||
</Tabs.Trigger> -->
|
||||
<Tabs.Trigger value="bultos-transportes" class="flex items-center gap-1.5 px-2.5 py-1.5 text-sm">
|
||||
<Box size={14} />
|
||||
<span>Bultos/Trans.</span>
|
||||
</Tabs.Trigger>
|
||||
<!-- <Tabs.Trigger value="descargas" class="flex items-center gap-1.5 px-2.5 py-1.5 text-sm">
|
||||
<Download size={14} />
|
||||
<span>Descargas</span>
|
||||
</Tabs.Trigger> -->
|
||||
<Tabs.Trigger value="contribuciones" class="flex items-center gap-1.5 px-2.5 py-1.5 text-sm">
|
||||
<Coins size={14} />
|
||||
<span>Contribuc.</span>
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger value="cuentas-compensacion" class="flex items-center gap-1.5 px-2.5 py-1.5 text-sm">
|
||||
<Wallet size={14} />
|
||||
<span>Cuentas/Comp.</span>
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger value="otros-datos" class="flex items-center gap-1.5 px-2.5 py-1.5 text-sm">
|
||||
<FileStack size={14} />
|
||||
<span>Otros</span>
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger value="digitalizacion" class="flex items-center gap-1.5 px-2.5 py-1.5 text-sm">
|
||||
<Scan size={14} />
|
||||
<span>Digital.</span>
|
||||
</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
</div>
|
||||
</Tabs.Root>
|
||||
|
||||
<!-- Botones de acción -->
|
||||
<div class="flex justify-end gap-3">
|
||||
<Button type="button" variant="outline" onclick={handleBack} disabled={saving}>
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button onclick={handleSaveAll} disabled={saving}>
|
||||
{#if saving}
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
Guardando todos los cambios...
|
||||
{:else}
|
||||
<Save size={16} class="mr-2" />
|
||||
Guardar Todos los Cambios
|
||||
{/if}
|
||||
</Button>
|
||||
<!-- Botones de acción -->
|
||||
<div class="flex justify-end gap-3">
|
||||
<Button type="button" variant="outline" onclick={handleBack} disabled={saving}>
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button onclick={handleSaveAll} disabled={saving}>
|
||||
{#if saving}
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
Guardando todos los cambios...
|
||||
{:else}
|
||||
<Save size={16} class="mr-2" />
|
||||
Guardar Todos los Cambios
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.Root>
|
||||
Reference in New Issue
Block a user