semillero-special-hotel/src/app/api/settlements/calculate/route.ts

124 lines
4.2 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import { withAuth } from '@/lib/api-guards';
import { setupLogRedaction } from '@/lib/log-redactor';
setupLogRedaction();
export const POST = withAuth(async (req, { session, prisma }) => {
try {
const body = await req.json();
const { period, simulateOnly } = body;
if (!period || !/^\d{4}-\d{2}$/.test(period)) {
return NextResponse.json(
{ error: 'El período es obligatorio y debe tener formato YYYY-MM.' },
{ status: 400 }
);
}
const isDev = process.env.IS_E2E_TEST === 'true' || process.env.NODE_ENV === 'development';
const signature = isDev
? (process.env.N8N_WEBHOOK_SECRET_DEV || 'qUHuPqPjA65psdtwQU7zgp/DVkvd1xXk2WP/vzbnEdc=')
: (process.env.N8N_WEBHOOK_SECRET || 'Ecjb2s33tHJppNBDJ/DxXEjHWKow8bNWmsQrk1sQKyQ=');
// Local bypass for E2E tests to prevent cloud n8n routing back to localhost
if (process.env.IS_E2E_TEST === 'true') {
console.log('E2E Test Mode: Running calculation locally via n8n endpoints...');
const localPort = process.env.PORT || '3015';
const localBase = `http://localhost:${localPort}`;
// 1. Fetch calculation data locally
const fetchRes = await fetch(`${localBase}/api/n8n/fetch-calculation-data?period=${period}`, {
headers: { 'x-n8n-signature': signature }
});
if (!fetchRes.ok) {
throw new Error(`Fetch calculation data failed: ${await fetchRes.text()}`);
}
const data = await fetchRes.json();
// 2. Process formula locally
const processRes = await fetch(`${localBase}/api/n8n/process-formula`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-n8n-signature': signature
},
body: JSON.stringify({
period,
simulateOnly,
...data
})
});
if (!processRes.ok) {
throw new Error(`Process formula failed: ${await processRes.text()}`);
}
const processData = await processRes.json();
// 3. Save settlements locally (if not simulated)
if (!simulateOnly) {
const saveRes = await fetch(`${localBase}/api/n8n/save-settlements`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-n8n-signature': signature
},
body: JSON.stringify({
period,
simulateOnly: false,
results: processData.results,
uploaderId: session.userId
})
});
if (!saveRes.ok) {
throw new Error(`Save settlements failed: ${await saveRes.text()}`);
}
}
return NextResponse.json({
success: true,
code: 'SETTLEMENTS_CALCULATED',
metadata: processData.metadata,
simulated: processData.results
}, { status: 201 });
}
// Determine target n8n webhook URL for dev/prod environment
const webhookBase = process.env.N8N_API_URL || 'https://n8n.gaboggamer.online';
const webhookPath = isDev ? 'webhook-test/calculate-settlements' : 'webhook/calculate-settlements';
const webhookUrl = `${webhookBase}/${webhookPath}`;
console.log(`Triggering n8n settlement calculation webhook at ${webhookUrl}...`);
const n8nRes = await fetch(webhookUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-n8n-signature': signature
},
body: JSON.stringify({
period,
simulateOnly,
uploaderId: session.userId
})
});
if (!n8nRes.ok) {
const errText = await n8nRes.text();
console.error(`n8n webhook calculation failed with status ${n8nRes.status}:`, errText);
return NextResponse.json(
{ error: `Error del motor de cálculo externo (n8n): ${n8nRes.status}` },
{ status: 502 }
);
}
const n8nData = await n8nRes.json();
return NextResponse.json(n8nData, { status: 201 });
} catch (err: any) {
console.error('Settlement calculation trigger error:', err);
return NextResponse.json(
{ error: err.message || 'Error al calcular las liquidaciones.' },
{ status: 500 }
);
}
}, ['admin', 'analyst']);