|
|
|
|
@@ -3,15 +3,19 @@
|
|
|
|
|
import * as Dialog from '$lib/components/ui/dialog';
|
|
|
|
|
import { Input } from '$lib/components/ui/input';
|
|
|
|
|
import { Label } from '$lib/components/ui/label';
|
|
|
|
|
// 👇 Verifica la ruta de tu archivo TS
|
|
|
|
|
import { Search } from 'lucide-svelte';
|
|
|
|
|
import {
|
|
|
|
|
createMultiCurrencyType,
|
|
|
|
|
updateMultiCurrencyType,
|
|
|
|
|
type MultiCurrencyType
|
|
|
|
|
} from '$lib/api/dashboard/a76/general_catalogs/multi-currency-types';
|
|
|
|
|
import { companyStore } from '$lib/stores/company.svelte';
|
|
|
|
|
import { obtenerAtajosFormularioMonedas } from '$lib/config/shortcuts/dashboard/general_catalogs/multi_currency_types/edit';
|
|
|
|
|
import { m } from '$lib/i18n/messages';
|
|
|
|
|
import CurrencySelectorDialog from '$lib/components/dashboard/goods/modales/currency-selector-dialog.svelte';
|
|
|
|
|
import CountrySelectorDialog from '$lib/components/dashboard/goods/modales/country-selector-dialog.svelte';
|
|
|
|
|
import type { CurrencyType } from '$lib/api/dashboard/reference_data/currency_types';
|
|
|
|
|
import type { Country } from '$lib/api/dashboard/reference_data/countries';
|
|
|
|
|
import { toast } from 'svelte-sonner';
|
|
|
|
|
|
|
|
|
|
let {
|
|
|
|
|
open = $bindable(false),
|
|
|
|
|
@@ -23,45 +27,36 @@
|
|
|
|
|
onSuccess?: () => void;
|
|
|
|
|
} = $props();
|
|
|
|
|
|
|
|
|
|
// Atajos
|
|
|
|
|
|
|
|
|
|
const isEdit = $derived(!!item);
|
|
|
|
|
const title = $derived(
|
|
|
|
|
isEdit ? m.multi_currency_edit_title() : m.multi_currency_new_title()
|
|
|
|
|
);
|
|
|
|
|
const title = $derived(isEdit ? m.multi_currency_edit_title() : m.multi_currency_new_title());
|
|
|
|
|
|
|
|
|
|
// Estado del formulario
|
|
|
|
|
let formData = $state({
|
|
|
|
|
currency_type_code: '',
|
|
|
|
|
country_key: '',
|
|
|
|
|
conversion_factor: null as number | null,
|
|
|
|
|
date_str: '' // Usamos un string temporal para el input type="date"
|
|
|
|
|
date_str: ''
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let loading = $state(false);
|
|
|
|
|
let error = $state<string | null>(null);
|
|
|
|
|
let showCurrencyDialog = $state(false);
|
|
|
|
|
let showCountryDialog = $state(false);
|
|
|
|
|
|
|
|
|
|
// Cargar datos al abrir
|
|
|
|
|
$effect(() => {
|
|
|
|
|
if (open) {
|
|
|
|
|
if (item) {
|
|
|
|
|
// Truco: Convertir Entero (20251231) -> String ("2025-12-31")
|
|
|
|
|
const s = item.publication_date?.toString();
|
|
|
|
|
let dateFormatted = '';
|
|
|
|
|
if (item.publication_date) {
|
|
|
|
|
const s = item.publication_date.toString();
|
|
|
|
|
if (s.length === 8) {
|
|
|
|
|
dateFormatted = `${s.slice(0, 4)}-${s.slice(4, 6)}-${s.slice(6, 8)}`;
|
|
|
|
|
}
|
|
|
|
|
if (s?.length === 8) {
|
|
|
|
|
dateFormatted = `${s.slice(0, 4)}-${s.slice(4, 6)}-${s.slice(6, 8)}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
formData = {
|
|
|
|
|
currency_type_code: item.currency_type_code,
|
|
|
|
|
country_key: item.country_key || '',
|
|
|
|
|
conversion_factor: item.conversion_factor,
|
|
|
|
|
date_str: dateFormatted
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
// Default: Fecha de hoy
|
|
|
|
|
} else {
|
|
|
|
|
formData = {
|
|
|
|
|
currency_type_code: '',
|
|
|
|
|
country_key: '',
|
|
|
|
|
@@ -73,6 +68,16 @@
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
function handleCurrencySelect(currency: CurrencyType) {
|
|
|
|
|
formData.currency_type_code = currency.code;
|
|
|
|
|
toast.success(`${currency.code} — ${currency.currency_name}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleCountrySelect(country: Country) {
|
|
|
|
|
formData.country_key = country.m3_key;
|
|
|
|
|
toast.success(`${country.m3_key} — ${country.description_es || country.description_en || ''}`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function handleSubmit() {
|
|
|
|
|
loading = true;
|
|
|
|
|
error = null;
|
|
|
|
|
@@ -80,33 +85,23 @@
|
|
|
|
|
try {
|
|
|
|
|
const companyId = companyStore.activeCompany?.id;
|
|
|
|
|
if (!companyId) throw new Error(m.exchange_rate_error_no_company());
|
|
|
|
|
|
|
|
|
|
// Validaciones
|
|
|
|
|
if (!formData.currency_type_code.trim()) throw new Error(m.multi_currency_error_currency_required());
|
|
|
|
|
if (!formData.date_str) throw new Error(m.multi_currency_error_date_required());
|
|
|
|
|
|
|
|
|
|
// Truco: Convertir String ("2025-12-31") -> Entero (20251231)
|
|
|
|
|
// Quitamos los guiones y parseamos a int
|
|
|
|
|
const dateInt = parseInt(formData.date_str.replaceAll('-', ''), 10);
|
|
|
|
|
|
|
|
|
|
// Preparar datos
|
|
|
|
|
const dataToSend = {
|
|
|
|
|
currency_type_code: formData.currency_type_code.trim().toUpperCase(),
|
|
|
|
|
country_key: formData.country_key.trim().toUpperCase() || null,
|
|
|
|
|
conversion_factor: formData.conversion_factor ? Number(formData.conversion_factor) : null,
|
|
|
|
|
publication_date: dateInt // Mandamos el INT que espera Python
|
|
|
|
|
publication_date: dateInt
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let response;
|
|
|
|
|
if (isEdit && item) {
|
|
|
|
|
response = await updateMultiCurrencyType(item.id, dataToSend, companyId);
|
|
|
|
|
} else {
|
|
|
|
|
response = await createMultiCurrencyType(dataToSend, companyId);
|
|
|
|
|
}
|
|
|
|
|
const response = isEdit && item
|
|
|
|
|
? await updateMultiCurrencyType(item.id, dataToSend, companyId)
|
|
|
|
|
: await createMultiCurrencyType(dataToSend, companyId);
|
|
|
|
|
|
|
|
|
|
if (response?.error) {
|
|
|
|
|
throw new Error(response.error);
|
|
|
|
|
}
|
|
|
|
|
if (response?.error) throw new Error(response.error);
|
|
|
|
|
|
|
|
|
|
open = false;
|
|
|
|
|
if (onSuccess) onSuccess();
|
|
|
|
|
@@ -119,16 +114,16 @@
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<Dialog.Root bind:open>
|
|
|
|
|
<Dialog.Content class="sm:max-w-[500px]">
|
|
|
|
|
<Dialog.Content
|
|
|
|
|
class="sm:max-w-[500px]"
|
|
|
|
|
onInteractOutside={(e) => e.preventDefault()}
|
|
|
|
|
>
|
|
|
|
|
<Dialog.Header>
|
|
|
|
|
<Dialog.Title>{title}</Dialog.Title>
|
|
|
|
|
</Dialog.Header>
|
|
|
|
|
|
|
|
|
|
<form
|
|
|
|
|
onsubmit={(e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
handleSubmit();
|
|
|
|
|
}}
|
|
|
|
|
onsubmit={(e) => { e.preventDefault(); handleSubmit(); }}
|
|
|
|
|
class="space-y-4 py-4"
|
|
|
|
|
>
|
|
|
|
|
{#if error}
|
|
|
|
|
@@ -138,41 +133,77 @@
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
<div class="grid gap-4">
|
|
|
|
|
<!-- Código Moneda -->
|
|
|
|
|
<div class="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
<Label for="currency_code" class="text-right"
|
|
|
|
|
>{m.multi_currency_currency_label()} <span class="text-destructive">*</span></Label
|
|
|
|
|
>
|
|
|
|
|
<div class="col-span-3">
|
|
|
|
|
<Label for="currency_code" class="text-right">
|
|
|
|
|
{m.multi_currency_currency_label()} <span class="text-destructive">*</span>
|
|
|
|
|
</Label>
|
|
|
|
|
<div class="col-span-3 flex gap-2">
|
|
|
|
|
<Input
|
|
|
|
|
id="currency_code"
|
|
|
|
|
bind:value={formData.currency_type_code}
|
|
|
|
|
placeholder="{m.exchange_rate_example_suffix()} USD"
|
|
|
|
|
oninput={(e) => {
|
|
|
|
|
formData.currency_type_code = e.currentTarget.value
|
|
|
|
|
.toUpperCase()
|
|
|
|
|
.replace(/[^A-Z]/g, '')
|
|
|
|
|
.slice(0, 3);
|
|
|
|
|
e.currentTarget.value = formData.currency_type_code;
|
|
|
|
|
}}
|
|
|
|
|
placeholder="Ej. USD"
|
|
|
|
|
maxlength={3}
|
|
|
|
|
disabled={loading}
|
|
|
|
|
required
|
|
|
|
|
class="flex-1 font-mono"
|
|
|
|
|
/>
|
|
|
|
|
<p class="text-[10px] text-muted-foreground mt-1">{m.multi_currency_currency_help()}</p>
|
|
|
|
|
<Button
|
|
|
|
|
type="button"
|
|
|
|
|
variant="outline"
|
|
|
|
|
size="icon"
|
|
|
|
|
class="h-10 w-10 shrink-0"
|
|
|
|
|
onclick={() => (showCurrencyDialog = true)}
|
|
|
|
|
disabled={loading}
|
|
|
|
|
>
|
|
|
|
|
<Search class="h-4 w-4" />
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- País -->
|
|
|
|
|
<div class="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
<Label for="country_key" class="text-right">{m.multi_currency_country_label()}</Label>
|
|
|
|
|
<div class="col-span-3">
|
|
|
|
|
<div class="col-span-3 flex gap-2">
|
|
|
|
|
<Input
|
|
|
|
|
id="country_key"
|
|
|
|
|
bind:value={formData.country_key}
|
|
|
|
|
placeholder="{m.exchange_rate_example_suffix()} MEX"
|
|
|
|
|
oninput={(e) => {
|
|
|
|
|
formData.country_key = e.currentTarget.value
|
|
|
|
|
.toUpperCase()
|
|
|
|
|
.replace(/[^A-Z]/g, '')
|
|
|
|
|
.slice(0, 3);
|
|
|
|
|
e.currentTarget.value = formData.country_key;
|
|
|
|
|
}}
|
|
|
|
|
placeholder="Ej. MEX"
|
|
|
|
|
maxlength={3}
|
|
|
|
|
disabled={loading}
|
|
|
|
|
class="flex-1 font-mono"
|
|
|
|
|
/>
|
|
|
|
|
<p class="text-[10px] text-muted-foreground mt-1">{m.multi_currency_country_help()}</p>
|
|
|
|
|
<Button
|
|
|
|
|
type="button"
|
|
|
|
|
variant="outline"
|
|
|
|
|
size="icon"
|
|
|
|
|
class="h-10 w-10 shrink-0"
|
|
|
|
|
onclick={() => (showCountryDialog = true)}
|
|
|
|
|
disabled={loading}
|
|
|
|
|
>
|
|
|
|
|
<Search class="h-4 w-4" />
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Fecha Publicación -->
|
|
|
|
|
<div class="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
<Label for="pub_date" class="text-right"
|
|
|
|
|
>{m.multi_currency_date_label()} <span class="text-destructive">*</span></Label
|
|
|
|
|
>
|
|
|
|
|
<Label for="pub_date" class="text-right">
|
|
|
|
|
{m.multi_currency_date_label()} <span class="text-destructive">*</span>
|
|
|
|
|
</Label>
|
|
|
|
|
<div class="col-span-3">
|
|
|
|
|
<Input
|
|
|
|
|
id="pub_date"
|
|
|
|
|
@@ -181,10 +212,11 @@
|
|
|
|
|
disabled={loading}
|
|
|
|
|
required
|
|
|
|
|
/>
|
|
|
|
|
<p class="text-[10px] text-muted-foreground mt-1">{m.multi_currency_date_help()}</p>
|
|
|
|
|
<p class="mt-1 text-[10px] text-muted-foreground">{m.multi_currency_date_help()}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Factor de Conversión -->
|
|
|
|
|
<div class="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
<Label for="factor" class="text-right">{m.multi_currency_factor_label()}</Label>
|
|
|
|
|
<div class="col-span-3">
|
|
|
|
|
@@ -211,3 +243,6 @@
|
|
|
|
|
</form>
|
|
|
|
|
</Dialog.Content>
|
|
|
|
|
</Dialog.Root>
|
|
|
|
|
|
|
|
|
|
<CurrencySelectorDialog bind:open={showCurrencyDialog} onSelect={handleCurrencySelect} />
|
|
|
|
|
<CountrySelectorDialog bind:open={showCountryDialog} onSelect={handleCountrySelect} />
|
|
|
|
|
|