Merge pull request 'fix/tipo_cambio' (#170) from fix/tipo_cambio into development

Reviewed-on: ADUANASOFT/anexo76#170
This commit is contained in:
2026-02-26 19:23:19 +00:00
3 changed files with 201 additions and 206 deletions

View File

@@ -166,7 +166,7 @@
<Dialog.Overlay class="fixed inset-0 z-[9999] {overlayClass}" /> <Dialog.Overlay class="fixed inset-0 z-[9999] {overlayClass}" />
<Dialog.Content <Dialog.Content
class="fixed left-[50%] top-[50%] z-[10000] w-full max-w-[480px] translate-x-[-50%] translate-y-[-50%] overflow-hidden border-0 bg-transparent p-0 shadow-2xl sm:rounded-xl" class="fixed top-[50%] left-[50%] z-[10000] w-full max-w-[480px] translate-x-[-50%] translate-y-[-50%] overflow-hidden border-0 bg-transparent p-0 shadow-2xl sm:rounded-xl"
> >
<div <div
class="flex h-full flex-col overflow-hidden rounded-xl border border-border bg-background" class="flex h-full flex-col overflow-hidden rounded-xl border border-border bg-background"
@@ -181,7 +181,7 @@
<AlertCircle class="text-amber-600 dark:text-amber-400" size={24} /> <AlertCircle class="text-amber-600 dark:text-amber-400" size={24} />
</div> </div>
<div class="space-y-1"> <div class="space-y-1">
<h3 class="text-lg font-semibold leading-tight text-amber-900 dark:text-amber-100"> <h3 class="text-lg leading-tight font-semibold text-amber-900 dark:text-amber-100">
{title} {title}
</h3> </h3>
<p class="text-sm leading-relaxed text-amber-800/80 dark:text-amber-200/80"> <p class="text-sm leading-relaxed text-amber-800/80 dark:text-amber-200/80">
@@ -232,7 +232,7 @@
/> />
{#if isMissingRateContext} {#if isMissingRateContext}
<div <div
class="absolute right-3 top-1/2 -translate-y-1/2 rounded-full bg-amber-100 px-2 py-0.5 text-xs font-medium text-amber-600" class="absolute top-1/2 right-3 -translate-y-1/2 rounded-full bg-amber-100 px-2 py-0.5 text-xs font-medium text-amber-600"
> >
Requerida Requerida
</div> </div>
@@ -241,28 +241,12 @@
</div> </div>
<div class="grid gap-2"> <div class="grid gap-2">
<div class="flex items-center justify-between"> <Label for="value" class="ml-1 text-sm font-medium text-muted-foreground"
<Label for="value" class="ml-1 text-sm font-medium text-muted-foreground" >Tipo de Cambio (MXN/USD)</Label
>Tipo de Cambio (MXN/USD)</Label >
>
<Button
type="button"
variant="ghost"
size="sm"
class="-mr-2 h-6 px-2 text-xs text-primary hover:text-primary/80"
disabled={scraping || loading || !formData.date}
onclick={() => fetchFromDof(formData.date)}
>
{#if scraping}
<span class="mr-1.5 animate-spin"></span> Consultando...
{:else}
<CloudDownload size={14} class="mr-1.5" /> Consultar DOF
{/if}
</Button>
</div>
<div class="group relative"> <div class="group relative">
<div <div
class="absolute left-3 top-1/2 -translate-y-1/2 font-semibold text-muted-foreground" class="absolute top-1/2 left-3 -translate-y-1/2 font-semibold text-muted-foreground"
> >
$ $
</div> </div>
@@ -270,6 +254,7 @@
id="value" id="value"
type="number" type="number"
step="0.0001" step="0.0001"
lang="en"
bind:value={formData.value} bind:value={formData.value}
disabled={loading} disabled={loading}
required required
@@ -278,18 +263,30 @@
autofocus autofocus
/> />
</div> </div>
<p class="px-1 text-right text-[11px] text-muted-foreground">Ej. 24.1234</p> <div class="mt-1 flex items-start justify-between">
<Button
type="button"
variant="secondary"
size="sm"
class="h-8 font-medium shadow-sm transition-all"
disabled={scraping || loading || !formData.date}
onclick={() => fetchFromDof(formData.date)}
>
{#if scraping}
<span class="mr-2 animate-spin"></span> Consultando...
{:else}
<CloudDownload size={15} class="mr-2" /> Consultar DOF
{/if}
</Button>
<p class="mt-2 px-1 text-right text-[11px] text-muted-foreground">Ej. 24.1234</p>
</div>
</div> </div>
</div> </div>
<Dialog.Footer class="grid grid-cols-4 gap-2 pt-2"> <Dialog.Footer class="mt-4 grid grid-cols-2 gap-3 pt-2">
<Button type="button" variant="outline" class="w-full text-xs" disabled>
Cargar TC
</Button>
<Button type="button" variant="outline" class="w-full text-xs" disabled>Ayuda</Button>
<Button <Button
type="button" type="button"
variant="ghost" variant="outline"
onclick={() => (open = false)} onclick={() => (open = false)}
disabled={loading} disabled={loading}
class="w-full hover:bg-muted/50" class="w-full hover:bg-muted/50"

View File

@@ -1,15 +1,20 @@
<script lang="ts"> <script lang="ts">
import { Button } from "$lib/components/ui/button"; import { Button } from '$lib/components/ui/button';
import * as Dialog from "$lib/components/ui/dialog"; import * as Dialog from '$lib/components/ui/dialog';
import { Input } from "$lib/components/ui/input"; import { Input } from '$lib/components/ui/input';
import { Label } from "$lib/components/ui/label"; import { Label } from '$lib/components/ui/label';
import * as Select from "$lib/components/ui/select"; import * as Select from '$lib/components/ui/select';
import { invoicesApi, type Invoice, type CreateInvoiceData, type UpdateInvoiceData } from "$lib/api/dashboard/a76/invoices"; import {
import { getExchangeRates } from "$lib/api/dashboard/a76/general_catalogs/exchange-rate"; invoicesApi,
import { companyStore } from "$lib/stores/company.svelte"; type Invoice,
type CreateInvoiceData,
type UpdateInvoiceData
} from '$lib/api/dashboard/a76/invoices';
import { getExchangeRates } from '$lib/api/dashboard/a76/general_catalogs/exchange-rate';
import { companyStore } from '$lib/stores/company.svelte';
import { LoaderCircle } from 'lucide-svelte'; import { LoaderCircle } from 'lucide-svelte';
import * as Tabs from "$lib/components/ui/tabs"; import * as Tabs from '$lib/components/ui/tabs';
import ExchangeRateDialog from "$lib/components/dashboard/exchange_rate/create-edit-dialog.svelte"; import ExchangeRateDialog from '$lib/components/dashboard/exchange_rate/create-edit-dialog.svelte';
let { let {
open = $bindable(false), open = $bindable(false),
@@ -27,34 +32,34 @@
let formData = $state({ let formData = $state({
// Header fields // Header fields
operation_type: "imp" as "imp" | "exp", operation_type: 'imp' as 'imp' | 'exp',
invoice_type: "", invoice_type: '',
invoice_number: "", invoice_number: '',
project_number: "", project_number: '',
purchase_order: "", purchase_order: '',
related_doc_id: null as number | null, related_doc_id: null as number | null,
invoice_date: "", invoice_date: '',
traffic_light_status: "", traffic_light_status: '',
observation_es: "", observation_es: '',
observation_en: "", observation_en: '',
comments_status: "", comments_status: '',
cfdi_uuid: "", cfdi_uuid: '',
path_pdf: "", path_pdf: '',
path_xml: "", path_xml: '',
// Compliance MX fields // Compliance MX fields
remesa: null as number | null, remesa: null as number | null,
aduana: "", aduana: '',
customs_broker_id: "", customs_broker_id: '',
provider_id: "", provider_id: '',
sold_to_id: "", sold_to_id: '',
shipped_to_id: "", shipped_to_id: '',
shipped_by_id: "", shipped_by_id: '',
is_mixed: false, is_mixed: false,
waste_type: "", waste_type: '',
appendix_17: null as number | null, appendix_17: null as number | null,
edocument: "", edocument: '',
// Financials fields // Financials fields
currency: "MXN", currency: 'MXN',
exchange_rate: null as number | null, exchange_rate: null as number | null,
value_mn: null as number | null, value_mn: null as number | null,
value_me: null as number | null, value_me: null as number | null,
@@ -73,38 +78,38 @@
let error = $state<string | null>(null); let error = $state<string | null>(null);
let showExchangeRateDialog = $state(false); let showExchangeRateDialog = $state(false);
let missingExchangeRateDate = $state(""); let missingExchangeRateDate = $state('');
// Actualizar formData cuando item cambia // Actualizar formData cuando item cambia
$effect(() => { $effect(() => {
if (item) { if (item) {
formData = { formData = {
operation_type: item.operation_type || "imp", operation_type: item.operation_type || 'imp',
invoice_type: item.invoice_type || "", invoice_type: item.invoice_type || '',
invoice_number: item.invoice_number || "", invoice_number: item.invoice_number || '',
project_number: item.project_number || "", project_number: item.project_number || '',
purchase_order: item.purchase_order || "", purchase_order: item.purchase_order || '',
related_doc_id: item.related_doc_id || null, related_doc_id: item.related_doc_id || null,
invoice_date: item.invoice_date || "", invoice_date: item.invoice_date || '',
traffic_light_status: item.traffic_light_status || "", traffic_light_status: item.traffic_light_status || '',
observation_es: item.observation_es || "", observation_es: item.observation_es || '',
observation_en: item.observation_en || "", observation_en: item.observation_en || '',
comments_status: item.comments_status || "", comments_status: item.comments_status || '',
cfdi_uuid: item.cfdi_uuid || "", cfdi_uuid: item.cfdi_uuid || '',
path_pdf: item.path_pdf || "", path_pdf: item.path_pdf || '',
path_xml: item.path_xml || "", path_xml: item.path_xml || '',
remesa: item.compliance_mx?.remesa || null, remesa: item.compliance_mx?.remesa || null,
aduana: item.compliance_mx?.aduana || "", aduana: item.compliance_mx?.aduana || '',
customs_broker_id: item.compliance_mx?.customs_broker_id?.toString() || "", customs_broker_id: item.compliance_mx?.customs_broker_id?.toString() || '',
provider_id: item.compliance_mx?.provider_id?.toString() || "", provider_id: item.compliance_mx?.provider_id?.toString() || '',
sold_to_id: item.compliance_mx?.sold_to_id?.toString() || "", sold_to_id: item.compliance_mx?.sold_to_id?.toString() || '',
shipped_to_id: item.compliance_mx?.shipped_to_id?.toString() || "", shipped_to_id: item.compliance_mx?.shipped_to_id?.toString() || '',
shipped_by_id: item.compliance_mx?.shipped_by_id?.toString() || "", shipped_by_id: item.compliance_mx?.shipped_by_id?.toString() || '',
is_mixed: item.compliance_mx?.is_mixed || false, is_mixed: item.compliance_mx?.is_mixed || false,
waste_type: item.compliance_mx?.waste_type || "", waste_type: item.compliance_mx?.waste_type || '',
appendix_17: item.compliance_mx?.appendix_17 || null, appendix_17: item.compliance_mx?.appendix_17 || null,
edocument: item.compliance_mx?.edocument || "", edocument: item.compliance_mx?.edocument || '',
currency: item.financials?.currency || "MXN", currency: item.financials?.currency || 'MXN',
exchange_rate: item.financials?.exchange_rate || null, exchange_rate: item.financials?.exchange_rate || null,
value_mn: item.financials?.value_mn || null, value_mn: item.financials?.value_mn || null,
value_me: item.financials?.value_me || null, value_me: item.financials?.value_me || null,
@@ -127,32 +132,32 @@
function resetForm() { function resetForm() {
formData = { formData = {
operation_type: defaultOperationType || "imp", operation_type: defaultOperationType || 'imp',
invoice_type: defaultInvoiceType || "", invoice_type: defaultInvoiceType || '',
invoice_number: "", invoice_number: '',
project_number: "", project_number: '',
purchase_order: "", purchase_order: '',
related_doc_id: null, related_doc_id: null,
invoice_date: "", invoice_date: '',
traffic_light_status: "", traffic_light_status: '',
observation_es: "", observation_es: '',
observation_en: "", observation_en: '',
comments_status: "", comments_status: '',
cfdi_uuid: "", cfdi_uuid: '',
path_pdf: "", path_pdf: '',
path_xml: "", path_xml: '',
remesa: null, remesa: null,
aduana: "", aduana: '',
customs_broker_id: "", customs_broker_id: '',
provider_id: "", provider_id: '',
sold_to_id: "", sold_to_id: '',
shipped_to_id: "", shipped_to_id: '',
shipped_by_id: "", shipped_by_id: '',
is_mixed: false, is_mixed: false,
waste_type: "", waste_type: '',
appendix_17: null, appendix_17: null,
edocument: "", edocument: '',
currency: "MXN", currency: 'MXN',
exchange_rate: null, exchange_rate: null,
value_mn: null, value_mn: null,
value_me: null, value_me: null,
@@ -170,9 +175,9 @@
async function handleSubmit(e: Event) { async function handleSubmit(e: Event) {
e.preventDefault(); e.preventDefault();
if (!companyStore.activeCompany) { if (!companyStore.activeCompany) {
error = "No hay compañía seleccionada"; error = 'No hay compañía seleccionada';
return; return;
} }
@@ -180,14 +185,14 @@
error = null; error = null;
try { try {
// Verificar tipo de cambio antes de guardar // Verificar tipo de cambio antes de guardar
if (formData.invoice_date) { if (formData.invoice_date) {
const rateExists = await checkExchangeRate(formData.invoice_date); const rateExists = await checkExchangeRate(formData.invoice_date);
if (!rateExists) { if (!rateExists) {
loading = false; loading = false;
return; return;
} }
} }
let response; let response;
if (isEditing && item) { if (isEditing && item) {
@@ -210,7 +215,9 @@
compliance_mx: { compliance_mx: {
remesa: formData.remesa, remesa: formData.remesa,
aduana: formData.aduana || null, aduana: formData.aduana || null,
customs_broker_id: formData.customs_broker_id ? parseInt(formData.customs_broker_id) : null, customs_broker_id: formData.customs_broker_id
? parseInt(formData.customs_broker_id)
: null,
provider_id: formData.provider_id ? parseInt(formData.provider_id) : null, provider_id: formData.provider_id ? parseInt(formData.provider_id) : null,
sold_to_id: formData.sold_to_id ? parseInt(formData.sold_to_id) : null, sold_to_id: formData.sold_to_id ? parseInt(formData.sold_to_id) : null,
shipped_to_id: formData.shipped_to_id ? parseInt(formData.shipped_to_id) : null, shipped_to_id: formData.shipped_to_id ? parseInt(formData.shipped_to_id) : null,
@@ -238,9 +245,9 @@
}; };
response = await invoicesApi.update(item.id, companyStore.activeCompany.id, payload); response = await invoicesApi.update(item.id, companyStore.activeCompany.id, payload);
} else { } else {
const payload: CreateInvoiceData = { const payload: CreateInvoiceData = {
system: "a76", system: 'a76',
document_type: "invoice", document_type: 'invoice',
operation_type: formData.operation_type, operation_type: formData.operation_type,
invoice_type: formData.invoice_type, invoice_type: formData.invoice_type,
invoice_number: formData.invoice_number, invoice_number: formData.invoice_number,
@@ -258,7 +265,9 @@
compliance_mx: { compliance_mx: {
remesa: formData.remesa, remesa: formData.remesa,
aduana: formData.aduana || null, aduana: formData.aduana || null,
customs_broker_id: formData.customs_broker_id ? parseInt(formData.customs_broker_id) : null, customs_broker_id: formData.customs_broker_id
? parseInt(formData.customs_broker_id)
: null,
provider_id: formData.provider_id ? parseInt(formData.provider_id) : null, provider_id: formData.provider_id ? parseInt(formData.provider_id) : null,
sold_to_id: formData.sold_to_id ? parseInt(formData.sold_to_id) : null, sold_to_id: formData.sold_to_id ? parseInt(formData.sold_to_id) : null,
shipped_to_id: formData.shipped_to_id ? parseInt(formData.shipped_to_id) : null, shipped_to_id: formData.shipped_to_id ? parseInt(formData.shipped_to_id) : null,
@@ -294,21 +303,23 @@
window.location.reload(); window.location.reload();
}, 1500); }, 1500);
} else { } else {
// Convertir el error a string para buscar mensajes específicos (maneja objetos/arrays de DRF) // Convertir el error a string para buscar mensajes específicos (maneja objetos/arrays de DRF)
const errorStr = typeof response.error === 'string' const errorStr =
? response.error typeof response.error === 'string' ? response.error : JSON.stringify(response.error);
: JSON.stringify(response.error);
if (
if (errorStr.includes('No existe un Tipo de Cambio registrado') || errorStr.includes('financials.exchange_rate')) { errorStr.includes('No existe un Tipo de Cambio registrado') ||
// Interceptar error de tipo de cambio errorStr.includes('financials.exchange_rate')
error = null; ) {
// Interceptar error de tipo de cambio
const dateMatch = errorStr.match(/(\d{4}-\d{2}-\d{2})/); error = null;
missingExchangeRateDate = dateMatch ? dateMatch[0] : (formData.invoice_date || "");
const dateMatch = errorStr.match(/(\d{4}-\d{2}-\d{2})/);
showExchangeRateDialog = true; missingExchangeRateDate = dateMatch ? dateMatch[0] : formData.invoice_date || '';
return;
} showExchangeRateDialog = true;
return;
}
error = response.error; error = response.error;
} }
@@ -316,13 +327,13 @@
} }
// Check exchange rate BEFORE calling API to avoid 400 error // Check exchange rate BEFORE calling API to avoid 400 error
if (formData.invoice_date) { if (formData.invoice_date) {
const rateExists = await checkExchangeRate(formData.invoice_date); const rateExists = await checkExchangeRate(formData.invoice_date);
if (!rateExists) { if (!rateExists) {
loading = false; loading = false;
return; return;
} }
} }
// Éxito // Éxito
open = false; open = false;
@@ -330,8 +341,8 @@
onSuccess(); onSuccess();
} }
} catch (e) { } catch (e) {
error = e instanceof Error ? e.message : "Error al guardar"; error = e instanceof Error ? e.message : 'Error al guardar';
console.error("Error saving:", e); console.error('Error saving:', e);
} finally { } finally {
loading = false; loading = false;
} }
@@ -345,11 +356,11 @@
date: date, date: date,
page_size: 1 page_size: 1
}); });
// api.get returns { data: ..., status: ... } and types now reflect that // api.get returns { data: ..., status: ... } and types now reflect that
const items = response.data?.items || []; const items = response.data?.items || [];
// Verificar estrictamente que haya items // Verificar estrictamente que haya items
if (items.length === 0) { if (items.length === 0) {
missingExchangeRateDate = date; missingExchangeRateDate = date;
showExchangeRateDialog = true; showExchangeRateDialog = true;
@@ -360,7 +371,7 @@
console.error('Error checking exchange rate:', error); console.error('Error checking exchange rate:', error);
missingExchangeRateDate = date; missingExchangeRateDate = date;
showExchangeRateDialog = true; showExchangeRateDialog = true;
return false; return false;
} }
} }
@@ -374,15 +385,13 @@
</script> </script>
<Dialog.Root {open} onOpenChange={handleOpenChange}> <Dialog.Root {open} onOpenChange={handleOpenChange}>
<Dialog.Content class="sm:max-w-full max-h-[90vh] overflow-y-auto"> <Dialog.Content class="max-h-[90vh] overflow-y-auto sm:max-w-full">
<Dialog.Header> <Dialog.Header>
<Dialog.Title> <Dialog.Title>
{isEditing ? "Editar Factura" : "Nueva Factura"} {isEditing ? 'Editar Factura' : 'Nueva Factura'}
</Dialog.Title> </Dialog.Title>
<Dialog.Description> <Dialog.Description>
{isEditing {isEditing ? 'Modifica los datos de la factura' : 'Ingresa los datos de la nueva factura'}
? "Modifica los datos de la factura"
: "Ingresa los datos de la nueva factura"}
</Dialog.Description> </Dialog.Description>
</Dialog.Header> </Dialog.Header>
@@ -397,24 +406,29 @@
<!-- General Tab --> <!-- General Tab -->
<Tabs.Content value="general" class="space-y-4"> <Tabs.Content value="general" class="space-y-4">
<div class="grid grid-cols-2 gap-4"> <div class="grid grid-cols-2 gap-4">
<div class="space-y-2"> <div class="space-y-2">
<Label for="operation_type">Tipo de Operación *</Label> <Label for="operation_type">Tipo de Operación *</Label>
<Select.Root <Select.Root
type="single" type="single"
value={formData.operation_type} value={formData.operation_type}
onValueChange={(v: string) => { onValueChange={(v: string) => {
if (v) formData.operation_type = v as "imp" | "exp"; if (v) formData.operation_type = v as 'imp' | 'exp';
}} }}
> >
<Select.Trigger> <Select.Trigger>
{formData.operation_type === 'imp' ? 'Importación' : formData.operation_type === 'exp' ? 'Exportación' : 'Seleccionar tipo'} {formData.operation_type === 'imp'
</Select.Trigger> ? 'Importación'
<Select.Content> : formData.operation_type === 'exp'
<Select.Item value="imp">Importación</Select.Item> ? 'Exportación'
<Select.Item value="exp">Exportación</Select.Item> : 'Seleccionar tipo'}
</Select.Content> </Select.Trigger>
</Select.Root> <Select.Content>
</div> <div class="space-y-2"> <Select.Item value="imp">Importación</Select.Item>
<Select.Item value="exp">Exportación</Select.Item>
</Select.Content>
</Select.Root>
</div>
<div class="space-y-2">
<Label for="invoice_number">Número de Factura</Label> <Label for="invoice_number">Número de Factura</Label>
<Input <Input
id="invoice_number" id="invoice_number"
@@ -452,11 +466,7 @@
<div class="space-y-2"> <div class="space-y-2">
<Label for="invoice_date">Fecha de Factura</Label> <Label for="invoice_date">Fecha de Factura</Label>
<Input <Input id="invoice_date" type="date" bind:value={formData.invoice_date} />
id="invoice_date"
type="date"
bind:value={formData.invoice_date}
/>
</div> </div>
<div class="space-y-2"> <div class="space-y-2">
@@ -470,11 +480,7 @@
<div class="space-y-2"> <div class="space-y-2">
<Label for="cfdi_uuid">CFDI UUID</Label> <Label for="cfdi_uuid">CFDI UUID</Label>
<Input <Input id="cfdi_uuid" bind:value={formData.cfdi_uuid} placeholder="UUID del CFDI" />
id="cfdi_uuid"
bind:value={formData.cfdi_uuid}
placeholder="UUID del CFDI"
/>
</div> </div>
</div> </div>
@@ -514,11 +520,7 @@
<div class="space-y-2"> <div class="space-y-2">
<Label for="aduana">Aduana</Label> <Label for="aduana">Aduana</Label>
<Input <Input id="aduana" bind:value={formData.aduana} placeholder="Código de aduana" />
id="aduana"
bind:value={formData.aduana}
placeholder="Código de aduana"
/>
</div> </div>
<div class="space-y-2"> <div class="space-y-2">
@@ -548,7 +550,7 @@
/> />
</div> </div>
<div class="space-y-2 flex items-center gap-2 pt-8"> <div class="flex items-center gap-2 space-y-2 pt-8">
<input <input
id="is_mixed" id="is_mixed"
type="checkbox" type="checkbox"
@@ -565,11 +567,7 @@
<div class="grid grid-cols-2 gap-4"> <div class="grid grid-cols-2 gap-4">
<div class="space-y-2"> <div class="space-y-2">
<Label for="currency">Moneda</Label> <Label for="currency">Moneda</Label>
<Input <Input id="currency" bind:value={formData.currency} placeholder="MXN, USD, etc." />
id="currency"
bind:value={formData.currency}
placeholder="MXN, USD, etc."
/>
</div> </div>
<div class="space-y-2"> <div class="space-y-2">
@@ -696,34 +694,31 @@
</Tabs.Root> </Tabs.Root>
{#if error} {#if error}
<div class="p-3 text-sm text-destructive bg-destructive/10 rounded-md"> <div class="rounded-md bg-destructive/10 p-3 text-sm text-destructive">
{error} {error}
</div> </div>
{/if} {/if}
<Dialog.Footer> <Dialog.Footer>
<Button <Button type="button" variant="outline" onclick={() => (open = false)} disabled={loading}>
type="button"
variant="outline"
onclick={() => (open = false)}
disabled={loading}
>
Cancelar Cancelar
</Button> </Button>
<Button type="submit" disabled={loading}> <Button type="submit" disabled={loading}>
{#if loading} {#if loading}
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" /> <LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
{/if} {/if}
{isEditing ? "Actualizar" : "Crear"} {isEditing ? 'Actualizar' : 'Crear'}
</Button> </Button>
</Dialog.Footer> </Dialog.Footer>
</form> </form>
</Dialog.Content> </Dialog.Content>
</Dialog.Root> </Dialog.Root>
<ExchangeRateDialog <ExchangeRateDialog
bind:open={showExchangeRateDialog} bind:open={showExchangeRateDialog}
initialDate={missingExchangeRateDate} initialDate={missingExchangeRateDate}
overlayClass="bg-black/20" overlayClass="bg-black/20"
onSuccess={() => {/* Optional: maybe refresh something or just let user continue */}} onSuccess={() => {
/* Optional: maybe refresh something or just let user continue */
}}
/> />

View File

@@ -8,6 +8,7 @@
import * as Sidebar from '$lib/components/ui/sidebar/index.js'; import * as Sidebar from '$lib/components/ui/sidebar/index.js';
import { companyStore } from '$lib/stores/company.svelte'; import { companyStore } from '$lib/stores/company.svelte';
import ExchangeRateGuard from '$lib/components/dashboard/exchange_rate/exchange-rate-guard.svelte'; import ExchangeRateGuard from '$lib/components/dashboard/exchange_rate/exchange-rate-guard.svelte';
import { page } from '$app/state';
let { data, children }: { data: LayoutData; children: any } = $props(); let { data, children }: { data: LayoutData; children: any } = $props();
@@ -63,4 +64,6 @@
</Sidebar.Inset> </Sidebar.Inset>
</Sidebar.Provider> </Sidebar.Provider>
<ExchangeRateGuard overlayClass="bg-black/5" /> {#if !page.url.pathname.includes('/dashboard/invoices') && !page.url.pathname.includes('/dashboard/pedimentos')}
<ExchangeRateGuard overlayClass="bg-black/5" />
{/if}