feat: implement package and payment method dialogs with API integration

This commit is contained in:
2026-02-16 11:52:34 -06:00
parent 5ca98ad0bc
commit 9e37340dd5
13 changed files with 674 additions and 33 deletions

View File

@@ -211,18 +211,18 @@ def validate_create(
# Calcular peso neto en kilogramos (estándar interno)
if unit_is_kgs:
if invoice_weight_type == "kgs":
if invoice_weight_type == "KGS":
line.quantity.net_weight = quantity
else: # invoice en libras
line.quantity.net_weight = quantity * Decimal("2.204624")
elif unit_is_lbs:
if invoice_weight_type == "kgs":
if invoice_weight_type == "KGS":
line.quantity.net_weight = quantity / Decimal("2.204624")
else: # invoice en libras
line.quantity.net_weight = quantity
else:
# Otra unidad de medida - usar peso capturado y convertir si es necesario
if invoice_weight_type == "kgs":
if invoice_weight_type == "KGS":
# El peso capturado está en kilos
line.quantity.net_weight = net_weight_input
else:
@@ -252,7 +252,7 @@ def validate_create(
# Si no se proporcionó peso bruto, calcularlo
if not gross_weight_input or gross_weight_input == 0:
if invoice_weight_type == "kgs":
if invoice_weight_type == "KGS":
line.quantity.gross_weight = line.quantity.net_weight + (
package_weight_unit * package_quantity
)
@@ -262,7 +262,7 @@ def validate_create(
)
else:
# Convertir peso bruto capturado según tipo de factura
if invoice_weight_type == "kgs":
if invoice_weight_type == "KGS":
line.quantity.gross_weight = gross_weight_input
else: # libras
line.quantity.gross_weight = gross_weight_input / Decimal("2.204624")

View File

@@ -73,7 +73,7 @@ def validate_update(
# Se proporcionó nuevo peso neto, convertir según tipo
net_weight_input = line.quantity.net_weight
if invoice_weight_type == "kgs":
if invoice_weight_type == "KGS":
line.quantity.net_weight = net_weight_input
else: # libras, convertir a kilos
line.quantity.net_weight = net_weight_input / Decimal("2.204624")
@@ -85,7 +85,7 @@ def validate_update(
if line.quantity.gross_weight is not None:
gross_weight_input = line.quantity.gross_weight
if invoice_weight_type == "kgs":
if invoice_weight_type == "KGS":
line.quantity.gross_weight = gross_weight_input
else: # libras, convertir a kilos
line.quantity.gross_weight = gross_weight_input / Decimal("2.204624")
@@ -98,8 +98,8 @@ def validate_update(
line.quantity.package_quantity = existing_line.quantity.package_quantity
# Clave de bultos
if not line.quantity.package_key:
line.quantity.package_key = existing_line.quantity.package_key
if not line.quantity.package_id:
line.quantity.package_id = existing_line.quantity.package_id
# País de origen
if not line.customs.origin_country:

View File

@@ -53,9 +53,9 @@ export interface LineQuantities {
gross_weight?: number;
// Packaging
package_key?: string;
package_id?: number; // Changed from string to number - this is the ID from packages catalog
package_quantity?: number;
package_description?: string;
package_description?: string; // This is for display purposes only
}
export interface LineDescriptions {

View File

@@ -36,7 +36,7 @@
}
function handlePartSelect(part: any) {
lineItem.part_number_id = part.id;
lineItem.part_number = part.id;
// Store part number for display
(lineItem as any).part_number = part.part_number;
(lineItem as any).part_description_es = part.description_spanish;
@@ -110,9 +110,6 @@
{#if (lineItem as any).part_description_es}
<p class="text-xs text-muted-foreground truncate">{(lineItem as any).part_description_es}</p>
{/if}
{#if lineItem.part_number_id}
<p class="text-xs text-muted-foreground italic">ID: {lineItem.part_number_id}</p>
{/if}
</div>
<div class="space-y-1">

View File

@@ -65,7 +65,7 @@
</div>
</header>
<div class="flex-1 overflow-y-auto px-2 py-1.5">
<div class="flex-1 overflow-y-auto px-2">
<div class="space-y-2">
{#if line}
@@ -129,6 +129,7 @@
bind:descriptions={editingItem.lines![0].description!}
bind:customs={editingItem.lines![0].customs!}
bind:quantities={editingItem.lines![0].quantity!}
invoice={invoice}
/>
<SummarySection
bind:financials={editingItem.lines![0].financial!}

View File

@@ -151,9 +151,6 @@
{#if (lineItem as any).class_description}
<p class="text-xs text-muted-foreground">{(lineItem as any).class_description}</p>
{/if}
{#if lineItem.class_id}
<p class="text-xs text-muted-foreground italic">ID: {lineItem.class_id}</p>
{/if}
</div>
<!-- Quantity and U.M. on the same row -->
@@ -183,9 +180,6 @@
<Folder class="w-4 h-4" />
</Button>
</div>
{#if (lineItem as any).unit_description}
<p class="text-xs text-muted-foreground truncate">{(lineItem as any).unit_description}</p>
{/if}
</div>
<!-- Unit Cost and Fraction -->
@@ -217,9 +211,6 @@
<Folder class="w-4 h-4" />
</Button>
</div>
{#if (customs as any).fraction_description}
<p class="text-xs text-muted-foreground truncate">{(customs as any).fraction_description}</p>
{/if}
</div>
<!-- Origin Country and Tariff Type -->

View File

@@ -0,0 +1,163 @@
<script lang="ts">
import * as Dialog from '$lib/components/ui/dialog';
import { Button } from '$lib/components/ui/button';
import { Input } from '$lib/components/ui/input';
import { Loader2, Search } from 'lucide-svelte';
import { onMount } from 'svelte';
let {
open = $bindable(),
onSelect
}: {
open: boolean;
onSelect: (pkg: any) => void;
} = $props();
let packages: any[] = $state([]);
let filteredPackages: any[] = $state([]);
let loading = $state(false);
let searchTerm = $state('');
let error = $state('');
async function loadPackages() {
loading = true;
error = '';
try {
const response = await fetch('/api-sveltekit/packages', {
credentials: 'include'
});
if (response.ok) {
const data = await response.json();
// El backend puede devolver { items: [...] } o directamente un array
if (Array.isArray(data)) {
packages = data;
} else if (data.items && Array.isArray(data.items)) {
packages = data.items;
} else if (data.data && Array.isArray(data.data)) {
packages = data.data;
} else {
console.error('Unexpected data format:', data);
packages = [];
}
filteredPackages = packages;
} else {
error = `Error: ${response.status} - ${response.statusText}`;
console.error('Error response:', await response.text());
}
} catch (err) {
error = 'Error loading packages';
console.error('Error loading packages:', err);
} finally {
loading = false;
}
}
function filterPackages() {
if (!searchTerm.trim()) {
filteredPackages = packages;
} else {
const term = searchTerm.toLowerCase();
filteredPackages = packages.filter(
(pkg) =>
pkg.key?.toLowerCase().includes(term) ||
pkg.description_es?.toLowerCase().includes(term) ||
pkg.description_en?.toLowerCase().includes(term)
);
}
}
function handleSelect(pkg: any) {
onSelect(pkg);
open = false;
}
$effect(() => {
if (open) {
loadPackages();
}
});
$effect(() => {
filterPackages();
});
</script>
<Dialog.Root bind:open>
<Dialog.Content class="!max-w-[70vw] w-[70vw] max-h-[90vh] p-0 flex flex-col">
<Dialog.Header class="px-6 py-4 border-b">
<Dialog.Title class="text-lg font-semibold">CATALOGO DE BULTOS</Dialog.Title>
</Dialog.Header>
<div class="px-6 py-3 border-b bg-zinc-50 dark:bg-zinc-900">
<div class="flex items-center gap-2">
<Search class="w-4 h-4 text-zinc-400" />
<Input
bind:value={searchTerm}
placeholder="Buscando..."
class="flex-1 h-9"
/>
</div>
</div>
<div class="flex-1 overflow-auto px-6 py-4">
{#if loading}
<div class="flex items-center justify-center py-20">
<Loader2 class="w-8 h-8 animate-spin text-zinc-900 dark:text-zinc-100" />
</div>
{:else if error}
<div class="flex items-center justify-center py-20 text-red-600">
<p>{error}</p>
</div>
{:else}
<div class="border rounded-md overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-zinc-900 dark:bg-zinc-800 text-white">
<tr>
<th class="px-3 py-2 text-left font-semibold border-r border-zinc-700"
>Clave</th
>
<th class="px-3 py-2 text-left font-semibold border-r border-zinc-700"
>Descripción Español</th
>
<th class="px-3 py-2 text-left font-semibold border-r border-zinc-700"
>Descripción Inglés</th
>
<th class="px-3 py-2 text-left font-semibold border-r border-zinc-700"
>Peso Unitario</th
>
<th class="px-3 py-2 text-left font-semibold">Código ACE</th>
</tr>
</thead>
<tbody>
{#each filteredPackages as pkg, i}
<tr
class="border-b hover:bg-zinc-100 dark:hover:bg-zinc-800 cursor-pointer transition-colors"
onclick={() => handleSelect(pkg)}
>
<td class="px-3 py-2 border-r">{pkg.key || ''}</td>
<td class="px-3 py-2 border-r">{pkg.description_es || ''}</td>
<td class="px-3 py-2 border-r">{pkg.description_en || ''}</td>
<td class="px-3 py-2 border-r text-right">{pkg.weight_unit || ''}</td>
<td class="px-3 py-2 text-center">{pkg.code_ace || ''}</td>
</tr>
{/each}
{#if filteredPackages.length === 0}
<tr>
<td colspan="5" class="px-3 py-8 text-center text-zinc-500">
No se encontraron resultados
</td>
</tr>
{/if}
</tbody>
</table>
</div>
{/if}
</div>
<div class="px-6 py-4 border-t flex justify-end gap-2">
<Button variant="outline" onclick={() => (open = false)}>
Cancelar
</Button>
</div>
</Dialog.Content>
</Dialog.Root>

View File

@@ -1,21 +1,95 @@
<script lang="ts">
import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label';
import { Button } from '$lib/components/ui/button';
import { Folder } from 'lucide-svelte';
import type { Item, LineItem, LineDescriptions, LineCustoms, LineQuantities } from '$lib/api/dashboard/a76/items';
import type { Invoice } from '$lib/api/dashboard/a76/invoices';
import PackageDialog from './package-dialog.svelte';
let {
item = $bindable(),
lineItem = $bindable(),
descriptions = $bindable(),
customs = $bindable(),
quantities = $bindable()
quantities = $bindable(),
invoice
}: {
item: Partial<Item>;
lineItem: LineItem;
descriptions: LineDescriptions;
customs: LineCustoms;
quantities: LineQuantities;
invoice: Invoice | null;
} = $props();
let packageDialogOpen = $state(false);
let package_key = $state('');
let package_weight_unit = $state<number>(0);
let isLoadingPackage = $state(false);
// Get weight unit from invoice logistics or default to 'KILOS'
const weightUnitLabel = $derived.by(() => {
const weightType = invoice?.logistics?.weight_type?.toUpperCase();
if (weightType === 'KGS') return 'KILOS';
if (weightType === 'LBS') return 'LIBRAS';
return 'KILOS'; // Default
});
// Calculate total package weight
const totalPackageWeight = $derived.by(() => {
const qty = quantities.package_quantity || 0;
const weightPerUnit = package_weight_unit || 0;
return (qty * weightPerUnit).toFixed(4);
});
// Load or sync package data when package_id exists
$effect(() => {
async function loadPackageData() {
// Si ya tiene package_key en quantities (cargado por enrichItemData), usarlo
if ((quantities as any).package_key) {
package_key = (quantities as any).package_key;
package_weight_unit = (quantities as any).package_weight_unit || 0;
return;
}
// Si no, pero tiene package_id, cargar los datos
if (quantities.package_id && !package_key && !isLoadingPackage) {
isLoadingPackage = true;
try {
const response = await fetch('/api-sveltekit/packages', {
credentials: 'include'
});
if (response.ok) {
const data = await response.json();
const packages = data.items || data.data || data;
if (Array.isArray(packages)) {
const pkg = packages.find((p: any) => p.id === quantities.package_id);
if (pkg) {
package_key = pkg.key;
package_weight_unit = pkg.weight_unit || 0;
if (!quantities.package_description) {
quantities.package_description = pkg.description_es || pkg.description_en || pkg.key;
}
}
}
}
} catch (error) {
console.error('Error loading package data:', error);
} finally {
isLoadingPackage = false;
}
}
}
loadPackageData();
});
function handlePackageSelect(pkg: any) {
quantities.package_id = pkg.id;
package_key = pkg.key;
package_weight_unit = pkg.weight_unit || 0;
quantities.package_description = pkg.description_es || pkg.description_en || pkg.key;
}
</script>
<fieldset class="border rounded-md p-2 space-y-2">
@@ -28,7 +102,24 @@
</div>
<div class="col-span-3 space-y-1">
<Label for="clave_bultos" class="text-xs">Package Code:</Label>
<Input id="clave_bultos" bind:value={quantities.package_key} class="h-7 text-xs" />
<div class="flex gap-1">
<Input
id="clave_bultos"
bind:value={package_key}
class="h-7 text-xs flex-1"
readonly
placeholder="Seleccionar..."
/>
<Button
size="icon"
variant="outline"
class="h-7 w-7 shrink-0"
onclick={() => packageDialogOpen = true}
type="button"
>
<Folder class="h-3 w-3" />
</Button>
</div>
</div>
<div class="col-span-1 flex items-end">
</div>
@@ -36,10 +127,10 @@
<div class="grid grid-cols-12 gap-2 items-end">
<div class="col-span-2 space-y-1">
<Label for="peso_bultos" class="text-xs">Weight: 0.0000</Label>
<Label for="peso_bultos" class="text-xs">Weight: {totalPackageWeight}</Label>
</div>
<div class="col-span-4 space-y-1">
<Label for="descripcion_bultos" class="text-xs">Description: {quantities.package_description}</Label>
<Label for="descripcion_bultos" class="text-xs">Description: {quantities.package_description || ''}</Label>
</div>
</div>
@@ -57,7 +148,7 @@
</div>
<div class="col-span-2 space-y-1">
<Label class="text-xs invisible">Space</Label>
<span class="text-xs text-gray-900 dark:text-gray-100 font-semibold">KILOS</span>
<span class="text-xs text-gray-900 dark:text-gray-100 font-semibold">{weightUnitLabel}</span>
</div>
</div>
</div>
@@ -99,3 +190,5 @@
</div>
</div>
</fieldset>
<PackageDialog bind:open={packageDialogOpen} onSelect={handlePackageSelect} />

View File

@@ -0,0 +1,151 @@
<script lang="ts">
import * as Dialog from '$lib/components/ui/dialog';
import { Button } from '$lib/components/ui/button';
import { Input } from '$lib/components/ui/input';
import { Loader2, Search } from 'lucide-svelte';
let {
open = $bindable(),
onSelect
}: {
open: boolean;
onSelect: (method: any) => void;
} = $props();
let paymentMethods: any[] = $state([]);
let filteredMethods: any[] = $state([]);
let loading = $state(false);
let searchTerm = $state('');
let error = $state('');
async function loadPaymentMethods() {
loading = true;
error = '';
try {
const response = await fetch('/api-sveltekit/payment-methods', {
credentials: 'include'
});
if (response.ok) {
const data = await response.json();
// El backend puede devolver { items: [...] } o directamente un array
if (Array.isArray(data)) {
paymentMethods = data;
} else if (data.items && Array.isArray(data.items)) {
paymentMethods = data.items;
} else if (data.data && Array.isArray(data.data)) {
paymentMethods = data.data;
} else {
console.error('Unexpected data format:', data);
paymentMethods = [];
}
filteredMethods = paymentMethods;
} else {
error = `Error: ${response.status} - ${response.statusText}`;
console.error('Error response:', await response.text());
}
} catch (err) {
error = 'Error loading payment methods';
console.error('Error loading payment methods:', err);
} finally {
loading = false;
}
}
function filterMethods() {
if (!searchTerm.trim()) {
filteredMethods = paymentMethods;
} else {
const term = searchTerm.toLowerCase();
filteredMethods = paymentMethods.filter(
(method) =>
method.key?.toLowerCase().includes(term) ||
method.description?.toLowerCase().includes(term)
);
}
}
function handleSelect(method: any) {
onSelect(method);
open = false;
}
$effect(() => {
if (open) {
loadPaymentMethods();
}
});
$effect(() => {
filterMethods();
});
</script>
<Dialog.Root bind:open>
<Dialog.Content class="!max-w-[60vw] w-[60vw] max-h-[90vh] p-0 flex flex-col">
<Dialog.Header class="px-6 py-4 border-b">
<Dialog.Title class="text-lg font-semibold">CATALOGO DE FORMAS DE PAGO</Dialog.Title>
</Dialog.Header>
<div class="px-6 py-3 border-b bg-zinc-50 dark:bg-zinc-900">
<div class="flex items-center gap-2">
<Search class="w-4 h-4 text-zinc-400" />
<Input
bind:value={searchTerm}
placeholder="Buscando..."
class="flex-1 h-9"
/>
</div>
</div>
<div class="flex-1 overflow-auto px-6 py-4">
{#if loading}
<div class="flex items-center justify-center py-20">
<Loader2 class="w-8 h-8 animate-spin text-zinc-900 dark:text-zinc-100" />
</div>
{:else if error}
<div class="flex items-center justify-center py-20 text-red-600">
<p>{error}</p>
</div>
{:else}
<div class="border rounded-md overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-zinc-900 dark:bg-zinc-800 text-white">
<tr>
<th class="px-3 py-2 text-left font-semibold border-r border-zinc-700"
>Clave</th
>
<th class="px-3 py-2 text-left font-semibold"
>Descripción</th
>
</tr>
</thead>
<tbody>
{#each filteredMethods as method, i}
<tr
class="border-b hover:bg-zinc-100 dark:hover:bg-zinc-800 cursor-pointer transition-colors"
onclick={() => handleSelect(method)}
>
<td class="px-3 py-2 border-r text-center">{method.key || ''}</td>
<td class="px-3 py-2">{method.description || ''}</td>
</tr>
{/each}
{#if filteredMethods.length === 0}
<tr>
<td colspan="2" class="px-3 py-8 text-center text-zinc-500">
No se encontraron resultados
</td>
</tr>
{/if}
</tbody>
</table>
</div>
{/if}
</div>
<div class="px-6 py-4 border-t flex justify-end gap-2">
<Button variant="outline" onclick={() => (open = false)}>
Cancelar
</Button>
</div>
</Dialog.Content>
</Dialog.Root>

View File

@@ -3,7 +3,10 @@
import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label';
import { Checkbox } from '$lib/components/ui/checkbox';
import { Button } from '$lib/components/ui/button';
import { Folder } from 'lucide-svelte';
import type { LineItem, LineDescriptions } from '$lib/api/dashboard/a76/items';
import PaymentMethodDialog from './payment-method-dialog.svelte';
let {
lineItem = $bindable(),
@@ -22,6 +25,47 @@
function setHasCertificate(val: string) {
lineItem.has_certificate = val === 'si';
}
let paymentMethodDialogOpen = $state(false);
let payment_method_description = $state('');
// Load payment method description when payment_method exists
$effect(() => {
async function loadPaymentMethodData() {
// Si ya tiene la descripción cargada por enrichItemData, usarla
if ((lineItem as any).payment_method_description) {
payment_method_description = (lineItem as any).payment_method_description;
return;
}
// Si tiene payment_method pero no descripción, cargarla
if (lineItem.payment_method && !payment_method_description) {
try {
const response = await fetch('/api-sveltekit/payment-methods', {
credentials: 'include'
});
if (response.ok) {
const data = await response.json();
const methods = data.items || data.data || data;
if (Array.isArray(methods)) {
const method = methods.find((m: any) => m.key === lineItem.payment_method);
if (method) {
payment_method_description = method.description;
}
}
}
} catch (error) {
console.error('Error loading payment method data:', error);
}
}
}
loadPaymentMethodData();
});
function handlePaymentMethodSelect(method: any) {
lineItem.payment_method = method.key;
payment_method_description = method.description;
}
</script>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-2">
@@ -49,10 +93,19 @@
<div class="space-y-0.5">
<Label for="forma_pago" class="text-xs">Payment Method:</Label>
<div class="flex gap-1">
<Input id="forma_pago" bind:value={lineItem.payment_method} class="h-6 text-xs" />
<Input id="forma_pago" bind:value={lineItem.payment_method} class="h-6 text-xs flex-1" />
<Button
size="icon"
variant="outline"
class="h-6 w-6 shrink-0"
onclick={() => paymentMethodDialogOpen = true}
type="button"
>
<Folder class="h-3 w-3" />
</Button>
</div>
</div>
<Label for="credito_iva" class="text-xs">VAT AND EXCISE TAX CREDITS.</Label>
<Label for="credito_iva" class="text-xs">{payment_method_description || ''}</Label>
</div>
</div>
@@ -158,3 +211,4 @@
</div>
</div>
</div>
<PaymentMethodDialog bind:open={paymentMethodDialogOpen} onSelect={handlePaymentMethodSelect} />

View File

@@ -290,7 +290,7 @@
quantity_returned: undefined,
net_weight: undefined,
gross_weight: undefined,
package_key: undefined,
package_id: undefined,
package_quantity: undefined,
package_description: undefined
},
@@ -664,6 +664,53 @@
console.error('Error loading fraction data:', error);
}
}
// Load package data (if needed)
const packageId = line.quantity?.package_id;
if (packageId && line.quantity) {
try {
const response = await fetch('/api-sveltekit/packages', {
method: 'GET',
headers: { 'Content-Type': 'application/json' }
});
if (response.ok) {
const data = await response.json();
const packages = data.items || data.data || data;
if (Array.isArray(packages)) {
const pkg = packages.find((p: any) => p.id === packageId);
if (pkg) {
(line.quantity as any).package_description = pkg.description_es || pkg.description_en || pkg.key;
(line.quantity as any).package_key = pkg.key;
(line.quantity as any).package_weight_unit = pkg.weight_unit || 0;
}
}
}
} catch (error) {
console.error('Error loading package data:', error);
}
}
// Load payment method description (if needed)
if (line.payment_method) {
try {
const response = await fetch('/api-sveltekit/payment-methods', {
method: 'GET',
headers: { 'Content-Type': 'application/json' }
});
if (response.ok) {
const data = await response.json();
const methods = data.items || data.data || data;
if (Array.isArray(methods)) {
const method = methods.find((m: any) => m.key === line.payment_method);
if (method) {
(line as any).payment_method_description = method.description;
}
}
}
} catch (error) {
console.error('Error loading payment method data:', error);
}
}
}
// Normalize numeric values from strings to numbers

View File

@@ -0,0 +1,80 @@
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async ({ cookies, url }) => {
const token = cookies.get('access_token');
// Obtener company_id de la cookie
const companyId = cookies.get('active_company_id');
if (!companyId) {
return new Response(
JSON.stringify({ error: 'No company selected' }),
{
status: 400,
headers: {
'Content-Type': 'application/json'
}
}
);
}
// Configurar la URL de la API usando las variables de entorno
let apiUrl = process.env.INTERNAL_API_URL;
if (!apiUrl) {
apiUrl = process.env.VITE_API_URL;
// Reemplazar 'localhost' con 'backend' para llamadas desde el servidor (SSR)
apiUrl = apiUrl?.replace('localhost', 'backend').replace('127.0.0.1', 'backend');
}
// Normalizar la URL
const baseUrl = apiUrl?.endsWith('/') ? apiUrl : `${apiUrl}/`;
// Get query parameters and add company_id
const searchParams = new URLSearchParams(url.search);
searchParams.set('company_id', companyId);
const queryString = searchParams.toString();
try {
const fetchUrl = `${baseUrl}v1/a76/packages?${queryString}`;
const response = await fetch(
fetchUrl,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
}
}
);
const data = await response.json();
if (!response.ok) {
return new Response(JSON.stringify(data), {
status: response.status,
headers: {
'Content-Type': 'application/json'
}
});
}
return new Response(JSON.stringify(data), {
status: 200,
headers: {
'Content-Type': 'application/json'
}
});
} catch (error) {
console.error('Error fetching packages:', error);
return new Response(
JSON.stringify({ error: 'Failed to fetch packages' }),
{
status: 500,
headers: {
'Content-Type': 'application/json'
}
}
);
}
};

View File

@@ -0,0 +1,64 @@
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async ({ cookies, url }) => {
const token = cookies.get('access_token');
// Configurar la URL de la API usando las variables de entorno
let apiUrl = process.env.INTERNAL_API_URL;
if (!apiUrl) {
apiUrl = process.env.VITE_API_URL;
// Reemplazar 'localhost' con 'backend' para llamadas desde el servidor (SSR)
apiUrl = apiUrl?.replace('localhost', 'backend').replace('127.0.0.1', 'backend');
}
// Normalizar la URL
const baseUrl = apiUrl?.endsWith('/') ? apiUrl : `${apiUrl}/`;
// Get query parameters
const searchParams = new URLSearchParams(url.search);
const queryString = searchParams.toString();
try {
const fetchUrl = `${baseUrl}v1/public/reference_data/payment-methods${queryString ? `?${queryString}` : ''}`;
const response = await fetch(
fetchUrl,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
}
}
);
const data = await response.json();
if (!response.ok) {
return new Response(JSON.stringify(data), {
status: response.status,
headers: {
'Content-Type': 'application/json'
}
});
}
return new Response(JSON.stringify(data), {
status: 200,
headers: {
'Content-Type': 'application/json'
}
});
} catch (error) {
console.error('Error fetching payment methods:', error);
return new Response(
JSON.stringify({ error: 'Failed to fetch payment methods' }),
{
status: 500,
headers: {
'Content-Type': 'application/json'
}
}
);
}
};