119 lines
3.2 KiB
TypeScript
119 lines
3.2 KiB
TypeScript
// src/lib/validators/reserveLimitValidator.ts
|
||
import { prisma } from "@/lib/prisma";
|
||
|
||
type LimitMethod = "block" | "all";
|
||
type LimitType = "set" | "person";
|
||
|
||
interface LimitCheckParams {
|
||
reserve_num: number;
|
||
// block のときのみ必要("HH:mm:ss")
|
||
reserve_time?: string;
|
||
/**
|
||
* この予約が占有するカウント
|
||
* limit_type=set のときは 1
|
||
* limit_type=person のときは (cust_parent + cust_child)
|
||
* フロントの事前確認なら未指定でもOK(現在の満席判定だけ返す)
|
||
*/
|
||
requestedCount?: number;
|
||
}
|
||
|
||
/**
|
||
* 開催設定の制限と現在の予約数を見て、満席か/受付可能かを判定する共通関数
|
||
* - ok=true: 受付可能
|
||
* - ok=false: 満席(message="予約定員に達しています")
|
||
* - extra: 現在カウント/上限/残数 も返すのでUIにも利用可
|
||
*/
|
||
export async function checkReserveLimit(params: LimitCheckParams) {
|
||
const { reserve_num, reserve_time, requestedCount } = params;
|
||
|
||
const setting = await prisma.reserveSetting.findUnique({
|
||
where: { autonum: reserve_num },
|
||
select: {
|
||
limit_method: true, // "block" | "all"
|
||
limit_type: true, // "set" | "person"
|
||
reserve_limit: true, // number
|
||
},
|
||
});
|
||
|
||
if (!setting) {
|
||
return { ok: false, message: "開催設定が存在しません" as const };
|
||
}
|
||
|
||
const method = setting.limit_method as LimitMethod;
|
||
const type = setting.limit_type as LimitType;
|
||
const limit = setting.reserve_limit;
|
||
|
||
let currentCount = 0;
|
||
|
||
// ---- 無制限処理を先に ----
|
||
if (limit === 0) {
|
||
return {
|
||
ok: true,
|
||
message: "",
|
||
currentCount,
|
||
limit,
|
||
remain: Infinity,
|
||
method,
|
||
type,
|
||
};
|
||
}
|
||
|
||
if (method === "block") {
|
||
if (!reserve_time) {
|
||
return { ok: false, message: "来場時間が必要です" as const };
|
||
}
|
||
if (type === "set") {
|
||
currentCount = await prisma.reserveCustomer.count({
|
||
where: { reserve_num, reserve_time, cancel_flag: 0 },
|
||
});
|
||
} else {
|
||
const agg = await prisma.reserveCustomer.aggregate({
|
||
where: { reserve_num, reserve_time, cancel_flag: 0 },
|
||
_sum: { cust_parent: true, cust_child: true },
|
||
});
|
||
currentCount = (agg._sum.cust_parent ?? 0) + (agg._sum.cust_child ?? 0);
|
||
}
|
||
} else {
|
||
// method === "all"
|
||
if (type === "set") {
|
||
currentCount = await prisma.reserveCustomer.count({
|
||
where: { reserve_num, cancel_flag: 0 },
|
||
});
|
||
} else {
|
||
const agg = await prisma.reserveCustomer.aggregate({
|
||
where: { reserve_num, cancel_flag: 0 },
|
||
_sum: { cust_parent: true, cust_child: true },
|
||
});
|
||
currentCount = (agg._sum.cust_parent ?? 0) + (agg._sum.cust_child ?? 0);
|
||
}
|
||
}
|
||
|
||
// 事前確認(requestedCount 未指定)のときは「現在満席かどうか」だけ返す
|
||
if (requestedCount == null) {
|
||
const ok = currentCount < limit;
|
||
return {
|
||
ok,
|
||
message: ok ? "" : ("予約定員に達しています" as const),
|
||
currentCount,
|
||
limit,
|
||
remain: Math.max(0, limit - currentCount),
|
||
method,
|
||
type,
|
||
};
|
||
}
|
||
|
||
// 予約実行時は今回の予約分も加味して判定
|
||
const willBe = currentCount + requestedCount;
|
||
const ok = willBe <= limit;
|
||
|
||
return {
|
||
ok,
|
||
message: ok ? "" : ("予約定員に達しています" as const),
|
||
currentCount,
|
||
limit,
|
||
remain: Math.max(0, limit - currentCount),
|
||
method,
|
||
type,
|
||
};
|
||
}
|