diff --git a/frontend/src/lib/server/api.ts b/frontend/src/lib/server/api.ts index 4c607d15..41ec018b 100644 --- a/frontend/src/lib/server/api.ts +++ b/frontend/src/lib/server/api.ts @@ -134,48 +134,63 @@ export async function authenticatedFetch( fetch: typeof globalThis.fetch, redirectUrl?: string ): Promise { - const baseUrl = getServerApiUrl(); - let { accessToken } = getAuthTokens(cookies); + try { + const baseUrl = getServerApiUrl(); + let { accessToken } = getAuthTokens(cookies); - // Si no hay token, redirigir o lanzar error - 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); - 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); - response = await fetch(url, { - ...options, - headers: newHeaders - }); - } else { - // No se pudo refrescar, limpiar y redirigir - clearAuthTokens(cookies); + // Si no hay token, redirigir o lanzar error + if (!accessToken) { if (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); + 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); + 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' } + }); + } } /** diff --git a/frontend/src/routes/api/invoices/[id]/edit-data/+server.ts b/frontend/src/routes/api/invoices/[id]/edit-data/+server.ts new file mode 100644 index 00000000..48c5cd3f --- /dev/null +++ b/frontend/src/routes/api/invoices/[id]/edit-data/+server.ts @@ -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'); + } +}; diff --git a/frontend/src/routes/api/invoices/reference-data/+server.ts b/frontend/src/routes/api/invoices/reference-data/+server.ts new file mode 100644 index 00000000..97c0e9ab --- /dev/null +++ b/frontend/src/routes/api/invoices/reference-data/+server.ts @@ -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 }); + } +}; diff --git a/frontend/src/routes/dashboard/invoices/edit/[id]/+page.server.ts b/frontend/src/routes/dashboard/invoices/edit/[id]/+page.server.ts index eb48c4d6..4321a3c7 100644 --- a/frontend/src/routes/dashboard/invoices/edit/[id]/+page.server.ts +++ b/frontend/src/routes/dashboard/invoices/edit/[id]/+page.server.ts @@ -97,57 +97,80 @@ export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => { // Si el ID es "new", es una creaci贸n if (params.id === 'new') { - const [ - invoiceTypesResponse, - customsBrokersResponse, - clientsResponse, - providersResponse, - currencyTypesResponse, - transportTypesResponse, - sealsResponse, - incotermsResponse, - pedimentosResponse - ] = await Promise.all([ - invoiceTypesPromise, - customsBrokersPromise, - clientsPromise, - providersPromise, - currencyTypesPromise, - transportTypesPromise, - sealsPromise, - incotermsPromise, - pedimentosPromise - ]); + try { + const [ + invoiceTypesResponse, + customsBrokersResponse, + clientsResponse, + providersResponse, + currencyTypesResponse, + transportTypesResponse, + sealsResponse, + incotermsResponse, + pedimentosResponse + ] = await Promise.all([ + invoiceTypesPromise, + customsBrokersPromise, + clientsPromise, + providersPromise, + currencyTypesPromise, + transportTypesPromise, + sealsPromise, + incotermsPromise, + pedimentosPromise + ]); - 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: [] }; + 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 { - invoice: null, - invoiceId: null, - isCreate: true, - 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 || [], - // Filtros desde query parameters para preselecci贸n - filters: { - operation_type: parsedOperationType, - invoice_type: invoiceTypeParam || null - } - }; + return { + invoice: null, + invoiceId: null, + isCreate: true, + 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 || [], + // Filtros desde query parameters para preselecci贸n + filters: { + operation_type: parsedOperationType, + 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); diff --git a/frontend/src/routes/dashboard/invoices/edit/[id]/+page.svelte b/frontend/src/routes/dashboard/invoices/edit/[id]/+page.svelte index 3bc2c203..3a35472a 100644 --- a/frontend/src/routes/dashboard/invoices/edit/[id]/+page.svelte +++ b/frontend/src/routes/dashboard/invoices/edit/[id]/+page.svelte @@ -1,4 +1,6 @@