121 lines
4.4 KiB
TypeScript
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']);
|