Merge pull request 'fix/tipo_cambio' (#170) from fix/tipo_cambio into development
Reviewed-on: ADUANASOFT/anexo76#170
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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,
|
||||||
@@ -172,7 +177,7 @@
|
|||||||
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,
|
||||||
@@ -239,8 +246,8 @@
|
|||||||
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 (errorStr.includes('No existe un Tipo de Cambio registrado') || errorStr.includes('financials.exchange_rate')) {
|
if (
|
||||||
// Interceptar error de tipo de cambio
|
errorStr.includes('No existe un Tipo de Cambio registrado') ||
|
||||||
error = null;
|
errorStr.includes('financials.exchange_rate')
|
||||||
|
) {
|
||||||
|
// Interceptar error de tipo de cambio
|
||||||
|
error = null;
|
||||||
|
|
||||||
const dateMatch = errorStr.match(/(\d{4}-\d{2}-\d{2})/);
|
const dateMatch = errorStr.match(/(\d{4}-\d{2}-\d{2})/);
|
||||||
missingExchangeRateDate = dateMatch ? dateMatch[0] : (formData.invoice_date || "");
|
missingExchangeRateDate = dateMatch ? dateMatch[0] : formData.invoice_date || '';
|
||||||
|
|
||||||
showExchangeRateDialog = true;
|
showExchangeRateDialog = true;
|
||||||
return;
|
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;
|
||||||
}
|
}
|
||||||
@@ -349,7 +360,7 @@
|
|||||||
// 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;
|
||||||
@@ -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,25 +694,20 @@
|
|||||||
</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>
|
||||||
@@ -725,5 +718,7 @@
|
|||||||
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 */
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
Reference in New Issue
Block a user