Files
plantillas-proyectos/frontend/src/lib/components/dashboard/goods/fractions/HistoricalFractionDialog.svelte

263 lines
8.0 KiB
Svelte

<script lang="ts">
import * as Dialog from '$lib/components/ui/dialog';
import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label';
import { Button } from '$lib/components/ui/button';
import { Switch } from '$lib/components/ui/switch';
import {
createHistoricalFraction,
updateHistoricalFraction,
type HistoricalFraction,
type HistoricalFractionCreate,
type HistoricalFractionUpdate
} from '$lib/api/dashboard/a76/general_catalogs/historical-tariff-fractions';
import { companyStore } from '$lib/stores/company.svelte';
import { toast } from 'svelte-sonner';
import { Loader2 } from 'lucide-svelte';
let {
open = $bindable(false),
fraction = null, // If null, create mode. If set, edit mode.
onSuccess
}: {
open: boolean;
fraction?: HistoricalFraction | null;
onSuccess: () => void;
} = $props();
let isLoading = $state(false);
// Form fields
let historicalFractionCode = $state('');
let unitOfMeasureCode = $state('');
let country = $state('');
let fractionType = $state('');
let sector = $state('');
let importTaxRate = $state('');
let exportTaxRate = $state('');
let publicationDate = $state('');
let endDate = $state('');
let isImmex = $state(false);
let normalTemporality = $state(false);
let servicesTemporality = $state(false);
let certifiedTemporality = $state(false);
let byLog = $state(false);
// Load data on open/fraction change
$effect(() => {
if (open) {
if (fraction) {
// Edit mode
historicalFractionCode = fraction.historical_fraction || '';
unitOfMeasureCode = fraction.unit_of_measure_code || '';
country = fraction.country || '';
fractionType = fraction.fraction_type || '';
sector = fraction.sector || '';
importTaxRate = fraction.import_tax_rate?.toString() || '';
exportTaxRate = fraction.export_tax_rate?.toString() || '';
publicationDate = fraction.publication_date ? fraction.publication_date.split('T')[0] : '';
endDate = fraction.end_date ? fraction.end_date.split('T')[0] : '';
isImmex = fraction.is_immex || false;
normalTemporality = fraction.normal_temporality || false;
servicesTemporality = fraction.services_temporality || false;
certifiedTemporality = fraction.certified_temporality || false;
byLog = fraction.by_log || false;
} else {
// Create mode - reset
historicalFractionCode = '';
unitOfMeasureCode = '';
country = '';
fractionType = '';
sector = '';
importTaxRate = '';
exportTaxRate = '';
publicationDate = '';
endDate = '';
isImmex = false;
normalTemporality = false;
servicesTemporality = false;
certifiedTemporality = false;
byLog = false;
}
}
});
async function handleSubmit() {
const companyId = companyStore.activeCompany?.id;
if (!companyId) return;
// Validation
if (!historicalFractionCode) {
toast.error('La fracción es requerida');
return;
}
isLoading = true;
try {
const importRateNum = importTaxRate ? parseFloat(importTaxRate) : undefined;
const exportRateNum = exportTaxRate ? parseFloat(exportTaxRate) : undefined;
const baseData = {
historical_fraction: historicalFractionCode,
unit_of_measure_code: unitOfMeasureCode || null,
country: country || null,
fraction_type: fractionType || null,
sector: sector || null,
import_tax_rate: importRateNum,
export_tax_rate: exportRateNum,
publication_date: publicationDate || null,
end_date: endDate || null,
is_immex: isImmex,
normal_temporality: normalTemporality,
services_temporality: servicesTemporality,
certified_temporality: certifiedTemporality,
by_log: byLog
};
if (fraction) {
// Update
const updateData: HistoricalFractionUpdate = baseData;
await updateHistoricalFraction(companyId, fraction.id, updateData);
toast.success('Fracción actualizada correctamente');
} else {
// Create
const createData: HistoricalFractionCreate = {
...baseData,
historical_fraction: historicalFractionCode // Required in create
};
await createHistoricalFraction(companyId, createData);
toast.success('Fracción creada correctamente');
}
onSuccess();
open = false;
} catch (error) {
console.error('Error saving historical fraction:', error);
toast.error('Error al guardar la fracción');
} finally {
isLoading = false;
}
}
</script>
<Dialog.Root bind:open>
<Dialog.Content class="sm:max-w-[700px]">
<Dialog.Header>
<Dialog.Title>{fraction ? 'Editar' : 'Crear'} Fracción Histórica</Dialog.Title>
<Dialog.Description>
{fraction
? 'Modifica los detalles de la fracción seleccionada.'
: 'Ingresa los datos para la nueva fracción.'}
</Dialog.Description>
</Dialog.Header>
<div class="grid max-h-[70vh] gap-4 overflow-y-auto py-4 pr-2">
<div class="grid grid-cols-2 gap-4">
<div class="space-y-2">
<Label for="fraction">Fracción</Label>
<Input
id="fraction"
bind:value={historicalFractionCode}
placeholder="Ej. 01010101"
maxlength={8}
/>
</div>
<div class="space-y-2">
<Label for="unit">Unidad de Medida</Label>
<Input id="unit" bind:value={unitOfMeasureCode} placeholder="Ej. 01" />
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div class="space-y-2">
<Label for="country">País</Label>
<Input id="country" bind:value={country} placeholder="Ej. MEX" />
</div>
<div class="space-y-2">
<Label for="type">Tipo</Label>
<Input id="type" bind:value={fractionType} placeholder="Ej. General" maxlength={7} />
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div class="space-y-2">
<Label for="sector">Sector</Label>
<Input id="sector" bind:value={sector} placeholder="Sectores..." maxlength={5} />
</div>
<div class="space-y-2">
<Label for="by-log">Por Bitácora</Label>
<div class="flex items-center space-x-2 pt-2">
<Switch id="by-log" bind:checked={byLog} />
<span class="text-sm text-muted-foreground">{byLog ? 'Sí' : 'No'}</span>
</div>
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div class="space-y-2">
<Label for="import-tax">Tasa IGI (%)</Label>
<Input
id="import-tax"
type="number"
step="0.01"
bind:value={importTaxRate}
placeholder="0.00"
/>
</div>
<div class="space-y-2">
<Label for="export-tax">Tasa IGE (%)</Label>
<Input
id="export-tax"
type="number"
step="0.01"
bind:value={exportTaxRate}
placeholder="0.00"
/>
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div class="space-y-2">
<Label for="pub-date">Fecha Publicación</Label>
<Input id="pub-date" type="date" bind:value={publicationDate} />
</div>
<div class="space-y-2">
<Label for="end-date">Fecha Fin</Label>
<Input id="end-date" type="date" bind:value={endDate} />
</div>
</div>
<div class="grid grid-cols-2 gap-4 border-t pt-4">
<div class="flex items-center justify-between space-x-2">
<Label for="is-immex">IMMEX</Label>
<Switch id="is-immex" bind:checked={isImmex} />
</div>
<div class="flex items-center justify-between space-x-2">
<Label for="normal-temp">Temp. Normal</Label>
<Switch id="normal-temp" bind:checked={normalTemporality} />
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div class="flex items-center justify-between space-x-2">
<Label for="services-temp">Temp. Servicios</Label>
<Switch id="services-temp" bind:checked={servicesTemporality} />
</div>
<div class="flex items-center justify-between space-x-2">
<Label for="certified-temp">Temp. Certificada</Label>
<Switch id="certified-temp" bind:checked={certifiedTemporality} />
</div>
</div>
</div>
<Dialog.Footer>
<Button variant="outline" onclick={() => (open = false)}>Cancelar</Button>
<Button onclick={handleSubmit} disabled={isLoading}>
{#if isLoading}
<Loader2 class="mr-2 h-4 w-4 animate-spin" />
{/if}
Guardar
</Button>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Root>