// 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, }; }