Refactor invoice edit flow: enhance error handling, load reference data, and improve token management
This commit is contained in:
@@ -134,48 +134,63 @@ export async function authenticatedFetch(
|
|||||||
fetch: typeof globalThis.fetch,
|
fetch: typeof globalThis.fetch,
|
||||||
redirectUrl?: string
|
redirectUrl?: string
|
||||||
): Promise<Response> {
|
): Promise<Response> {
|
||||||
const baseUrl = getServerApiUrl();
|
try {
|
||||||
let { accessToken } = getAuthTokens(cookies);
|
const baseUrl = getServerApiUrl();
|
||||||
|
let { accessToken } = getAuthTokens(cookies);
|
||||||
|
|
||||||
// Si no hay token, redirigir o lanzar error
|
// Si no hay token, redirigir o lanzar error
|
||||||
if (!accessToken) {
|
if (!accessToken) {
|
||||||
if (redirectUrl) {
|
|
||||||
throw redirect(303, redirectUrl);
|
|
||||||
}
|
|
||||||
throw new Error('No access token available');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construir URL completa
|
|
||||||
const url = endpoint.startsWith('http') ? endpoint : `${baseUrl}${endpoint}`;
|
|
||||||
|
|
||||||
// Realizar la petición inicial
|
|
||||||
const headers = createAuthHeaders(accessToken, options.headers as Record<string, string>);
|
|
||||||
let response = await fetch(url, {
|
|
||||||
...options,
|
|
||||||
headers
|
|
||||||
});
|
|
||||||
|
|
||||||
// Si es 401, intentar refrescar el token
|
|
||||||
if (response.status === 401) {
|
|
||||||
const newToken = await refreshAccessToken(cookies, fetch);
|
|
||||||
|
|
||||||
if (newToken) {
|
|
||||||
// Reintentar la petición con el nuevo token
|
|
||||||
const newHeaders = createAuthHeaders(newToken, options.headers as Record<string, string>);
|
|
||||||
response = await fetch(url, {
|
|
||||||
...options,
|
|
||||||
headers: newHeaders
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// No se pudo refrescar, limpiar y redirigir
|
|
||||||
clearAuthTokens(cookies);
|
|
||||||
if (redirectUrl) {
|
if (redirectUrl) {
|
||||||
throw redirect(303, redirectUrl);
|
throw redirect(303, redirectUrl);
|
||||||
}
|
}
|
||||||
|
throw new Error('No access token available');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
// Construir URL completa
|
||||||
|
const url = endpoint.startsWith('http') ? endpoint : `${baseUrl}${endpoint}`;
|
||||||
|
|
||||||
|
// Realizar la petición inicial
|
||||||
|
const headers = createAuthHeaders(accessToken, options.headers as Record<string, string>);
|
||||||
|
let response = await fetch(url, {
|
||||||
|
...options,
|
||||||
|
headers
|
||||||
|
});
|
||||||
|
|
||||||
|
// Si es 401, intentar refrescar el token
|
||||||
|
if (response.status === 401) {
|
||||||
|
const newToken = await refreshAccessToken(cookies, fetch);
|
||||||
|
|
||||||
|
if (newToken) {
|
||||||
|
// Reintentar la petición con el nuevo token
|
||||||
|
const newHeaders = createAuthHeaders(newToken, options.headers as Record<string, string>);
|
||||||
|
response = await fetch(url, {
|
||||||
|
...options,
|
||||||
|
headers: newHeaders
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// No se pudo refrescar, limpiar y redirigir
|
||||||
|
clearAuthTokens(cookies);
|
||||||
|
if (redirectUrl) {
|
||||||
|
throw redirect(303, redirectUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
// Si es un redirect, re-lanzarlo
|
||||||
|
if (error && typeof error === 'object' && 'status' in error && 'location' in error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error('🔴 [API] Error en authenticatedFetch:', endpoint, error);
|
||||||
|
|
||||||
|
// Retornar una respuesta de error simulada en lugar de lanzar
|
||||||
|
return new Response(JSON.stringify({ error: 'Network error', details: String(error) }), {
|
||||||
|
status: 500,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
80
frontend/src/routes/api/invoices/[id]/edit-data/+server.ts
Normal file
80
frontend/src/routes/api/invoices/[id]/edit-data/+server.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import type { RequestHandler } from './$types';
|
||||||
|
import { json, error } from '@sveltejs/kit';
|
||||||
|
import { getAuthTokens, getActiveCompanyId, authenticatedFetch } from '$lib/server/api';
|
||||||
|
|
||||||
|
export const GET: RequestHandler = async ({ params, cookies, fetch }) => {
|
||||||
|
const { accessToken } = getAuthTokens(cookies);
|
||||||
|
|
||||||
|
if (!accessToken) {
|
||||||
|
throw error(401, 'Not authenticated');
|
||||||
|
}
|
||||||
|
|
||||||
|
const companyId = await getActiveCompanyId(cookies, fetch);
|
||||||
|
|
||||||
|
if (!companyId) {
|
||||||
|
throw error(400, 'No company selected');
|
||||||
|
}
|
||||||
|
|
||||||
|
const invoiceId = parseInt(params.id);
|
||||||
|
if (isNaN(invoiceId)) {
|
||||||
|
throw error(400, 'Invalid invoice ID');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Cargar la factura y datos de referencia en paralelo
|
||||||
|
const [
|
||||||
|
invoiceResponse,
|
||||||
|
invoiceTypesResponse,
|
||||||
|
customsBrokersResponse,
|
||||||
|
clientsResponse,
|
||||||
|
providersResponse,
|
||||||
|
currencyTypesResponse,
|
||||||
|
transportTypesResponse,
|
||||||
|
sealsResponse,
|
||||||
|
incotermsResponse,
|
||||||
|
pedimentosResponse
|
||||||
|
] = await Promise.all([
|
||||||
|
authenticatedFetch(`v1/a76/invoices/${invoiceId}?company_id=${companyId}`, {}, cookies, fetch),
|
||||||
|
authenticatedFetch('v1/public/refrence_data/invoice-types/?page=1&page_size=100', {}, cookies, fetch),
|
||||||
|
authenticatedFetch(`v1/a76/customs-brokers/?company_id=${companyId}&page=1&page_size=100`, {}, cookies, fetch),
|
||||||
|
authenticatedFetch(`v1/a76/clients-providers/?company_id=${companyId}&type=client&page=1&page_size=1000`, {}, cookies, fetch),
|
||||||
|
authenticatedFetch(`v1/a76/clients-providers/?company_id=${companyId}&type=provider&page=1&page_size=1000`, {}, cookies, fetch),
|
||||||
|
authenticatedFetch('v1/public/refrence_data/currency-types/?page=1&page_size=100', {}, cookies, fetch),
|
||||||
|
authenticatedFetch('v1/public/refrence_data/transport-types/?page=1&page_size=100', {}, cookies, fetch),
|
||||||
|
authenticatedFetch(`v1/a76/seals/?company_id=${companyId}&page=1&page_size=100`, {}, cookies, fetch),
|
||||||
|
authenticatedFetch('v1/public/refrence_data/incoterms/?page=1&page_size=100', {}, cookies, fetch),
|
||||||
|
authenticatedFetch(`v1/a76/pedimentos/?company_id=${companyId}&page=1&page_size=100`, {}, cookies, fetch)
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!invoiceResponse.ok) {
|
||||||
|
throw error(invoiceResponse.status, 'Error loading invoice');
|
||||||
|
}
|
||||||
|
|
||||||
|
const invoice = await invoiceResponse.json();
|
||||||
|
const invoiceTypes = invoiceTypesResponse.ok ? await invoiceTypesResponse.json() : { items: [] };
|
||||||
|
const customsBrokers = customsBrokersResponse.ok ? await customsBrokersResponse.json() : { items: [] };
|
||||||
|
const clients = clientsResponse.ok ? await clientsResponse.json() : { items: [] };
|
||||||
|
const providers = providersResponse.ok ? await providersResponse.json() : { items: [] };
|
||||||
|
const currencyTypes = currencyTypesResponse.ok ? await currencyTypesResponse.json() : { items: [] };
|
||||||
|
const transportTypes = transportTypesResponse.ok ? await transportTypesResponse.json() : { items: [] };
|
||||||
|
const seals = sealsResponse.ok ? await sealsResponse.json() : { items: [] };
|
||||||
|
const incoterms = incotermsResponse.ok ? await incotermsResponse.json() : { items: [] };
|
||||||
|
const pedimentos = pedimentosResponse.ok ? await pedimentosResponse.json() : { items: [] };
|
||||||
|
|
||||||
|
return json({
|
||||||
|
invoice,
|
||||||
|
invoiceTypes: invoiceTypes.items || [],
|
||||||
|
customsBrokers: customsBrokers.items || [],
|
||||||
|
clients: clients.items || [],
|
||||||
|
providers: providers.items || [],
|
||||||
|
currencyTypes: currencyTypes.items || [],
|
||||||
|
transportTypes: transportTypes.items || [],
|
||||||
|
seals: seals.items || [],
|
||||||
|
incoterms: incoterms.items || [],
|
||||||
|
pedimentos: pedimentos.items || []
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error loading invoice edit data:', err);
|
||||||
|
throw error(500, 'Error loading invoice');
|
||||||
|
}
|
||||||
|
};
|
||||||
67
frontend/src/routes/api/invoices/reference-data/+server.ts
Normal file
67
frontend/src/routes/api/invoices/reference-data/+server.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import type { RequestHandler } from './$types';
|
||||||
|
import { json } from '@sveltejs/kit';
|
||||||
|
import { getAuthTokens, getActiveCompanyId, authenticatedFetch } from '$lib/server/api';
|
||||||
|
|
||||||
|
export const GET: RequestHandler = async ({ cookies, fetch }) => {
|
||||||
|
const { accessToken } = getAuthTokens(cookies);
|
||||||
|
|
||||||
|
if (!accessToken) {
|
||||||
|
return json({ error: 'Not authenticated' }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const companyId = await getActiveCompanyId(cookies, fetch);
|
||||||
|
|
||||||
|
if (!companyId) {
|
||||||
|
return json({ error: 'No company selected' }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Cargar todos los datos de referencia en paralelo
|
||||||
|
const [
|
||||||
|
invoiceTypesResponse,
|
||||||
|
customsBrokersResponse,
|
||||||
|
clientsResponse,
|
||||||
|
providersResponse,
|
||||||
|
currencyTypesResponse,
|
||||||
|
transportTypesResponse,
|
||||||
|
sealsResponse,
|
||||||
|
incotermsResponse,
|
||||||
|
pedimentosResponse
|
||||||
|
] = await Promise.all([
|
||||||
|
authenticatedFetch('v1/public/refrence_data/invoice-types/?page=1&page_size=100', {}, cookies, fetch),
|
||||||
|
authenticatedFetch(`v1/a76/customs-brokers/?company_id=${companyId}&page=1&page_size=100`, {}, cookies, fetch),
|
||||||
|
authenticatedFetch(`v1/a76/clients-providers/?company_id=${companyId}&type=client&page=1&page_size=1000`, {}, cookies, fetch),
|
||||||
|
authenticatedFetch(`v1/a76/clients-providers/?company_id=${companyId}&type=provider&page=1&page_size=1000`, {}, cookies, fetch),
|
||||||
|
authenticatedFetch('v1/public/refrence_data/currency-types/?page=1&page_size=100', {}, cookies, fetch),
|
||||||
|
authenticatedFetch('v1/public/refrence_data/transport-types/?page=1&page_size=100', {}, cookies, fetch),
|
||||||
|
authenticatedFetch(`v1/a76/seals/?company_id=${companyId}&page=1&page_size=100`, {}, cookies, fetch),
|
||||||
|
authenticatedFetch('v1/public/refrence_data/incoterms/?page=1&page_size=100', {}, cookies, fetch),
|
||||||
|
authenticatedFetch(`v1/a76/pedimentos/?company_id=${companyId}&page=1&page_size=100`, {}, cookies, fetch)
|
||||||
|
]);
|
||||||
|
|
||||||
|
const invoiceTypes = invoiceTypesResponse.ok ? await invoiceTypesResponse.json() : { items: [] };
|
||||||
|
const customsBrokers = customsBrokersResponse.ok ? await customsBrokersResponse.json() : { items: [] };
|
||||||
|
const clients = clientsResponse.ok ? await clientsResponse.json() : { items: [] };
|
||||||
|
const providers = providersResponse.ok ? await providersResponse.json() : { items: [] };
|
||||||
|
const currencyTypes = currencyTypesResponse.ok ? await currencyTypesResponse.json() : { items: [] };
|
||||||
|
const transportTypes = transportTypesResponse.ok ? await transportTypesResponse.json() : { items: [] };
|
||||||
|
const seals = sealsResponse.ok ? await sealsResponse.json() : { items: [] };
|
||||||
|
const incoterms = incotermsResponse.ok ? await incotermsResponse.json() : { items: [] };
|
||||||
|
const pedimentos = pedimentosResponse.ok ? await pedimentosResponse.json() : { items: [] };
|
||||||
|
|
||||||
|
return json({
|
||||||
|
invoiceTypes: invoiceTypes.items || [],
|
||||||
|
customsBrokers: customsBrokers.items || [],
|
||||||
|
clients: clients.items || [],
|
||||||
|
providers: providers.items || [],
|
||||||
|
currencyTypes: currencyTypes.items || [],
|
||||||
|
transportTypes: transportTypes.items || [],
|
||||||
|
seals: seals.items || [],
|
||||||
|
incoterms: incoterms.items || [],
|
||||||
|
pedimentos: pedimentos.items || []
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error loading reference data:', err);
|
||||||
|
return json({ error: 'Error loading reference data' }, { status: 500 });
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -97,57 +97,80 @@ export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
|
|||||||
|
|
||||||
// Si el ID es "new", es una creación
|
// Si el ID es "new", es una creación
|
||||||
if (params.id === 'new') {
|
if (params.id === 'new') {
|
||||||
const [
|
try {
|
||||||
invoiceTypesResponse,
|
const [
|
||||||
customsBrokersResponse,
|
invoiceTypesResponse,
|
||||||
clientsResponse,
|
customsBrokersResponse,
|
||||||
providersResponse,
|
clientsResponse,
|
||||||
currencyTypesResponse,
|
providersResponse,
|
||||||
transportTypesResponse,
|
currencyTypesResponse,
|
||||||
sealsResponse,
|
transportTypesResponse,
|
||||||
incotermsResponse,
|
sealsResponse,
|
||||||
pedimentosResponse
|
incotermsResponse,
|
||||||
] = await Promise.all([
|
pedimentosResponse
|
||||||
invoiceTypesPromise,
|
] = await Promise.all([
|
||||||
customsBrokersPromise,
|
invoiceTypesPromise,
|
||||||
clientsPromise,
|
customsBrokersPromise,
|
||||||
providersPromise,
|
clientsPromise,
|
||||||
currencyTypesPromise,
|
providersPromise,
|
||||||
transportTypesPromise,
|
currencyTypesPromise,
|
||||||
sealsPromise,
|
transportTypesPromise,
|
||||||
incotermsPromise,
|
sealsPromise,
|
||||||
pedimentosPromise
|
incotermsPromise,
|
||||||
]);
|
pedimentosPromise
|
||||||
|
]);
|
||||||
|
|
||||||
const invoiceTypes = invoiceTypesResponse.ok ? await invoiceTypesResponse.json() : { items: [] };
|
const invoiceTypes = invoiceTypesResponse.ok ? await invoiceTypesResponse.json() : { items: [] };
|
||||||
const customsBrokers = customsBrokersResponse.ok ? await customsBrokersResponse.json() : { items: [] };
|
const customsBrokers = customsBrokersResponse.ok ? await customsBrokersResponse.json() : { items: [] };
|
||||||
const clients = clientsResponse.ok ? await clientsResponse.json() : { items: [] };
|
const clients = clientsResponse.ok ? await clientsResponse.json() : { items: [] };
|
||||||
const providers = providersResponse.ok ? await providersResponse.json() : { items: [] };
|
const providers = providersResponse.ok ? await providersResponse.json() : { items: [] };
|
||||||
const currencyTypes = currencyTypesResponse.ok ? await currencyTypesResponse.json() : { items: [] };
|
const currencyTypes = currencyTypesResponse.ok ? await currencyTypesResponse.json() : { items: [] };
|
||||||
const transportTypes = transportTypesResponse.ok ? await transportTypesResponse.json() : { items: [] };
|
const transportTypes = transportTypesResponse.ok ? await transportTypesResponse.json() : { items: [] };
|
||||||
const seals = sealsResponse.ok ? await sealsResponse.json() : { items: [] };
|
const seals = sealsResponse.ok ? await sealsResponse.json() : { items: [] };
|
||||||
const incoterms = incotermsResponse.ok ? await incotermsResponse.json() : { items: [] };
|
const incoterms = incotermsResponse.ok ? await incotermsResponse.json() : { items: [] };
|
||||||
const pedimentos = pedimentosResponse.ok ? await pedimentosResponse.json() : { items: [] };
|
const pedimentos = pedimentosResponse.ok ? await pedimentosResponse.json() : { items: [] };
|
||||||
|
|
||||||
return {
|
return {
|
||||||
invoice: null,
|
invoice: null,
|
||||||
invoiceId: null,
|
invoiceId: null,
|
||||||
isCreate: true,
|
isCreate: true,
|
||||||
invoiceTypes: invoiceTypes.items || [],
|
invoiceTypes: invoiceTypes.items || [],
|
||||||
customsBrokers: customsBrokers.items || [],
|
customsBrokers: customsBrokers.items || [],
|
||||||
clients: clients.items || [],
|
clients: clients.items || [],
|
||||||
providers: providers.items || [],
|
providers: providers.items || [],
|
||||||
currencyTypes: currencyTypes.items || [],
|
currencyTypes: currencyTypes.items || [],
|
||||||
transportTypes: transportTypes.items || [],
|
transportTypes: transportTypes.items || [],
|
||||||
seals: seals.items || [],
|
seals: seals.items || [],
|
||||||
incoterms: incoterms.items || [],
|
incoterms: incoterms.items || [],
|
||||||
pedimentos: pedimentos.items || [],
|
pedimentos: pedimentos.items || [],
|
||||||
// Filtros desde query parameters para preselección
|
// Filtros desde query parameters para preselección
|
||||||
filters: {
|
filters: {
|
||||||
operation_type: parsedOperationType,
|
operation_type: parsedOperationType,
|
||||||
invoice_type: invoiceTypeParam || null
|
invoice_type: invoiceTypeParam || null
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error loading data for new invoice:', err);
|
||||||
|
// En caso de error, devolver estructura mínima para que la página pueda cargar
|
||||||
|
return {
|
||||||
|
invoice: null,
|
||||||
|
invoiceId: null,
|
||||||
|
isCreate: true,
|
||||||
|
invoiceTypes: [],
|
||||||
|
customsBrokers: [],
|
||||||
|
clients: [],
|
||||||
|
providers: [],
|
||||||
|
currencyTypes: [],
|
||||||
|
transportTypes: [],
|
||||||
|
seals: [],
|
||||||
|
incoterms: [],
|
||||||
|
pedimentos: [],
|
||||||
|
filters: {
|
||||||
|
operation_type: parsedOperationType,
|
||||||
|
invoice_type: invoiceTypeParam || null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const invoiceId = parseInt(params.id);
|
const invoiceId = parseInt(params.id);
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { browser } from '$app/environment';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import * as Tabs from '$lib/components/ui/tabs';
|
import * as Tabs from '$lib/components/ui/tabs';
|
||||||
import * as Alert from '$lib/components/ui/alert';
|
import * as Alert from '$lib/components/ui/alert';
|
||||||
@@ -17,8 +19,6 @@
|
|||||||
LoaderCircle,
|
LoaderCircle,
|
||||||
Save
|
Save
|
||||||
} from 'lucide-svelte';
|
} from 'lucide-svelte';
|
||||||
import { useSidebar } from '$lib/components/ui/sidebar/context.svelte';
|
|
||||||
import { companyStore } from '$lib/stores/company.svelte';
|
|
||||||
|
|
||||||
// Importar los componentes de cada pestaña
|
// Importar los componentes de cada pestaña
|
||||||
import GeneralTabForm from '$lib/components/dashboard/invoices/edit/general-tab-form.svelte';
|
import GeneralTabForm from '$lib/components/dashboard/invoices/edit/general-tab-form.svelte';
|
||||||
@@ -34,8 +34,20 @@
|
|||||||
import type { CustomsBroker } from '$lib/api/dashboard/a76/customs-brokers';
|
import type { CustomsBroker } from '$lib/api/dashboard/a76/customs-brokers';
|
||||||
import type { ClientProvider } from '$lib/api/dashboard/a76/clients-providers';
|
import type { ClientProvider } from '$lib/api/dashboard/a76/clients-providers';
|
||||||
|
|
||||||
// Get sidebar context
|
// Cargar companyStore solo en el cliente - no usamos sidebar en esta página
|
||||||
const sidebar = useSidebar();
|
let companyStore: any = $state(undefined);
|
||||||
|
let mounted = $state(false);
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
try {
|
||||||
|
const companyStoreModule = await import('$lib/stores/company.svelte');
|
||||||
|
companyStore = companyStoreModule.companyStore;
|
||||||
|
mounted = true;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error loading client modules:', err);
|
||||||
|
mounted = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
interface ExtendedPageData {
|
interface ExtendedPageData {
|
||||||
invoiceId?: number | null;
|
invoiceId?: number | null;
|
||||||
@@ -471,7 +483,7 @@
|
|||||||
|
|
||||||
if (data.isCreate) {
|
if (data.isCreate) {
|
||||||
// Crear nueva factura con todos sus sub-recursos
|
// Crear nueva factura con todos sus sub-recursos
|
||||||
const response = await invoicesApi.create(companyStore.activeCompany?.id || 0, payload as CreateInvoiceData);
|
const response = await invoicesApi.create(companyStore?.activeCompany?.id || 0, payload as CreateInvoiceData);
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
const errorMsg = typeof response.error === 'string' ? response.error : 'Error al crear la factura';
|
const errorMsg = typeof response.error === 'string' ? response.error : 'Error al crear la factura';
|
||||||
throw new Error(errorMsg);
|
throw new Error(errorMsg);
|
||||||
@@ -484,7 +496,7 @@
|
|||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
// Actualizar factura existente con todos sus sub-recursos
|
// Actualizar factura existente con todos sus sub-recursos
|
||||||
const response = await invoicesApi.update(invoiceId!, companyStore.activeCompany?.id || 0, payload as UpdateInvoiceData);
|
const response = await invoicesApi.update(invoiceId!, companyStore?.activeCompany?.id || 0, payload as UpdateInvoiceData);
|
||||||
if (response.error) throw new Error(response.error);
|
if (response.error) throw new Error(response.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -635,8 +647,7 @@
|
|||||||
|
|
||||||
<!-- Footer fijo en la parte inferior -->
|
<!-- Footer fijo en la parte inferior -->
|
||||||
<div
|
<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"
|
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]"
|
||||||
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">
|
<div class="px-4 py-4 space-y-4 max-w-[1400px] mx-auto">
|
||||||
<!-- Tabs Navigation -->
|
<!-- Tabs Navigation -->
|
||||||
|
|||||||
132
frontend/src/routes/dashboard/invoices/edit/[id]/+page.ts
Normal file
132
frontend/src/routes/dashboard/invoices/edit/[id]/+page.ts
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import type { PageLoad } from './$types';
|
||||||
|
import { error } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
// Deshabilitar SSR para esta página debido al layout de dashboard que usa stores del cliente
|
||||||
|
export const ssr = false;
|
||||||
|
|
||||||
|
export const load: PageLoad = async ({ params, url, parent }) => {
|
||||||
|
// Obtener datos del layout padre
|
||||||
|
const parentData = await parent();
|
||||||
|
|
||||||
|
const operationTypeParam = url.searchParams.get('operation_type');
|
||||||
|
const invoiceTypeParam = url.searchParams.get('invoice_type');
|
||||||
|
|
||||||
|
let parsedOperationType: number | null = null;
|
||||||
|
if (operationTypeParam) {
|
||||||
|
const parsed = parseInt(operationTypeParam, 10);
|
||||||
|
if (!isNaN(parsed)) {
|
||||||
|
parsedOperationType = parsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si el ID es "new", cargar datos de referencia
|
||||||
|
if (params.id === 'new') {
|
||||||
|
try {
|
||||||
|
// Llamar a la ruta de servidor que ya existe
|
||||||
|
const response = await fetch(`/api/invoices/reference-data`);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
console.error('Error loading reference data:', response.status);
|
||||||
|
// Retornar estructura vacía en caso de error
|
||||||
|
return {
|
||||||
|
invoice: null,
|
||||||
|
invoiceId: null,
|
||||||
|
isCreate: true,
|
||||||
|
invoiceTypes: [],
|
||||||
|
customsBrokers: [],
|
||||||
|
clients: [],
|
||||||
|
providers: [],
|
||||||
|
currencyTypes: [],
|
||||||
|
transportTypes: [],
|
||||||
|
seals: [],
|
||||||
|
incoterms: [],
|
||||||
|
pedimentos: [],
|
||||||
|
filters: {
|
||||||
|
operation_type: parsedOperationType,
|
||||||
|
invoice_type: invoiceTypeParam || null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
return {
|
||||||
|
invoice: null,
|
||||||
|
invoiceId: null,
|
||||||
|
isCreate: true,
|
||||||
|
invoiceTypes: data.invoiceTypes || [],
|
||||||
|
customsBrokers: data.customsBrokers || [],
|
||||||
|
clients: data.clients || [],
|
||||||
|
providers: data.providers || [],
|
||||||
|
currencyTypes: data.currencyTypes || [],
|
||||||
|
transportTypes: data.transportTypes || [],
|
||||||
|
seals: data.seals || [],
|
||||||
|
incoterms: data.incoterms || [],
|
||||||
|
pedimentos: data.pedimentos || [],
|
||||||
|
filters: {
|
||||||
|
operation_type: parsedOperationType,
|
||||||
|
invoice_type: invoiceTypeParam || null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error loading data for new invoice:', err);
|
||||||
|
return {
|
||||||
|
invoice: null,
|
||||||
|
invoiceId: null,
|
||||||
|
isCreate: true,
|
||||||
|
invoiceTypes: [],
|
||||||
|
customsBrokers: [],
|
||||||
|
clients: [],
|
||||||
|
providers: [],
|
||||||
|
currencyTypes: [],
|
||||||
|
transportTypes: [],
|
||||||
|
seals: [],
|
||||||
|
incoterms: [],
|
||||||
|
pedimentos: [],
|
||||||
|
filters: {
|
||||||
|
operation_type: parsedOperationType,
|
||||||
|
invoice_type: invoiceTypeParam || null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si es un ID numérico, cargar la factura
|
||||||
|
const invoiceId = parseInt(params.id);
|
||||||
|
if (isNaN(invoiceId)) {
|
||||||
|
throw error(400, 'ID de factura inválido');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/invoices/${invoiceId}/edit-data`);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw error(response.status, 'Error al cargar la factura');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
return {
|
||||||
|
invoice: data.invoice,
|
||||||
|
invoiceId,
|
||||||
|
isCreate: false,
|
||||||
|
invoiceTypes: data.invoiceTypes || [],
|
||||||
|
customsBrokers: data.customsBrokers || [],
|
||||||
|
clients: data.clients || [],
|
||||||
|
providers: data.providers || [],
|
||||||
|
currencyTypes: data.currencyTypes || [],
|
||||||
|
transportTypes: data.transportTypes || [],
|
||||||
|
seals: data.seals || [],
|
||||||
|
incoterms: data.incoterms || [],
|
||||||
|
pedimentos: data.pedimentos || [],
|
||||||
|
filters: {
|
||||||
|
operation_type: parsedOperationType,
|
||||||
|
invoice_type: invoiceTypeParam || null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error loading invoice:', err);
|
||||||
|
throw error(500, 'Error al cargar la factura');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Reference in New Issue
Block a user