semillero-special-hotel/src/app/api/plans/[id]/route.ts

121 lines
4.4 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import { withAuth } from '@/lib/api-guards';
// GET: Fetch a single plan by ID
export const GET = withAuth(async (req, { prisma, params }) => {
const unwrappedParams = await params;
const id = parseInt(unwrappedParams.id);
if (isNaN(id)) {
return NextResponse.json({ error: 'Invalid ID' }, { status: 400 });
}
const plan = await prisma.compensationPlan.findUnique({
where: { id },
include: { rules: true }
});
if (!plan) {
return NextResponse.json({ error: 'Plan not found' }, { status: 404 });
}
return NextResponse.json({ plan });
});
// PUT: Update or Version a plan (restricted to ADMIN and DIRECTOR)
export const PUT = withAuth(async (req, { session, prisma, params }) => {
const unwrappedParams = await params;
const id = parseInt(unwrappedParams.id);
const body = await req.json();
if (isNaN(id)) {
return NextResponse.json({ error: 'Invalid ID' }, { status: 400 });
}
const plan = await prisma.compensationPlan.findUnique({
where: { id }
});
if (!plan) {
return NextResponse.json({ error: 'Plan not found' }, { status: 404 });
}
// Versioning replication if the plan is currently ACTIVE
if (plan.status === 'ACTIVE') {
const newPlan = await prisma.$transaction(async (tx: any) => {
// Set RLS variables directly on the transaction client context
await tx.$executeRawUnsafe(`SET LOCAL app.current_user_id = '${session.userId}';`);
await tx.$executeRawUnsafe(`SET LOCAL app.current_user_role = '${session.role}';`);
await tx.$executeRawUnsafe(`SET LOCAL app.current_hotel_id = '${session.hotelId}';`);
await tx.$executeRawUnsafe(`SET LOCAL app.current_region_id = '${session.regionId}';`);
// 1. Mark current plan version as INACTIVE
await tx.compensationPlan.update({
where: { id },
data: {
status: 'INACTIVE',
validityEnd: new Date()
}
});
// 2. Clone to a new version record
const clonedPlan = await tx.compensationPlan.create({
data: {
name: body.name || plan.name,
code: body.code || plan.code,
validityStart: new Date(), // starts now
validityEnd: body.validityEnd ? new Date(body.validityEnd) : null,
type: body.type || plan.type,
formula: body.formula !== undefined ? body.formula : plan.formula,
metaAmount: body.metaAmount !== undefined ? parseFloat(body.metaAmount) : plan.metaAmount,
percentageRate: body.percentageRate !== undefined ? parseFloat(body.percentageRate) : plan.percentageRate,
maxCap: body.maxCap !== undefined ? parseFloat(body.maxCap) : plan.maxCap,
status: 'ACTIVE',
version: plan.version + 1,
createdBy: session.userId
}
});
// 3. Clone rules associated with the original plan
const originalRules = await tx.calculationRule.findMany({
where: { planId: id }
});
if (originalRules.length > 0) {
await tx.calculationRule.createMany({
data: originalRules.map((r: any) => ({
planId: clonedPlan.id,
type: r.type,
minAchievement: r.minAchievement,
maxAchievement: r.maxAchievement,
rate: r.rate,
payoutAmount: r.payoutAmount
}))
});
}
return clonedPlan;
});
return NextResponse.json({ plan: newPlan, versioned: true });
} else {
// In-place update for DRAFT / INACTIVE plans
const updatedPlan = await prisma.compensationPlan.update({
where: { id },
data: {
name: body.name,
code: body.code,
validityStart: body.validityStart ? new Date(body.validityStart) : undefined,
validityEnd: body.validityEnd !== undefined ? (body.validityEnd ? new Date(body.validityEnd) : null) : undefined,
type: body.type,
formula: body.formula,
metaAmount: body.metaAmount !== undefined ? (body.metaAmount ? parseFloat(body.metaAmount) : null) : undefined,
percentageRate: body.percentageRate !== undefined ? (body.percentageRate ? parseFloat(body.percentageRate) : null) : undefined,
maxCap: body.maxCap !== undefined ? (body.maxCap ? parseFloat(body.maxCap) : null) : undefined,
status: body.status
}
});
return NextResponse.json({ plan: updatedPlan, versioned: false });
}
}, ['ADMIN', 'DIRECTOR']);