import { NextRequest, NextResponse } from "next/server"; import { z } from "zod"; import { isValidObjectId } from "mongoose"; import { connectDb } from "@/lib/server/db"; import { Branch } from "@/lib/server/models/Branch"; import { User } from "@/lib/server/models/User"; import { authorize } from "@/lib/server/auth"; import { branchLimitError } from "@/lib/server/subscription"; import { getStockLevels } from "@/lib/server/stock"; export const runtime = "nodejs"; const updateSchema = z .object({ branchName: z.string().trim().min(1).max(120).optional(), locationCity: z.string().trim().min(1).max(120).optional(), isActive: z.boolean().optional(), }) .refine((v) => Object.keys(v).length > 0, { message: "No fields to update" }); /** * PATCH /api/branches/:id — owner renames a branch, or deactivates / * reactivates it. This is how an owner "removes" a branch: nothing is * deleted, so its sales, purchases and stock history stay in every report. * * A branch can only be deactivated once it is empty, so nothing is left * stranded in it: * - no stock on hand (transfer it out or count it to zero first), and * - no active staff (move or deactivate them first). * Reactivating counts against the plan's branch limit again. */ export async function PATCH(req: NextRequest, { params }: { params: { id: string } }) { await connectDb(); const gate = authorize(req, ["SUPER_ADMIN"]); if (!gate.ok) return gate.response; const auth = gate.auth; if (!isValidObjectId(params.id)) return NextResponse.json({ error: "Branch not found" }, { status: 404 }); const json = await req.json().catch(() => null); const parsed = updateSchema.safeParse(json); if (!parsed.success) { return NextResponse.json({ error: parsed.error.issues[0]?.message ?? "Invalid request" }, { status: 400 }); } const { isActive, ...details } = parsed.data; const branch = await Branch.findOne({ _id: params.id, businessId: auth.businessId }); if (!branch) return NextResponse.json({ error: "Branch not found" }, { status: 404 }); const wasActive = branch.isActive !== false; if (isActive === false && wasActive) { const stock = (await getStockLevels(auth.businessId, branch._id)).filter((l) => l.quantity !== 0); if (stock.length > 0) { const list = stock.map((l) => `${l.quantity} x ${l.productLabel}`).join(", "); return NextResponse.json( { error: `Move or count out this branch's stock first (${list}).`, stock }, { status: 409 } ); } const staff = await User.countDocuments({ businessId: auth.businessId, branchId: branch._id, isActive: true }); if (staff > 0) { return NextResponse.json( { error: `${staff} active staff member(s) still work at this branch. Move or deactivate them first.` }, { status: 409 } ); } } if (isActive === true && !wasActive) { const limitError = await branchLimitError(auth.businessId); if (limitError) return NextResponse.json({ error: limitError }, { status: 403 }); } Object.assign(branch, details); if (isActive !== undefined) branch.isActive = isActive; await branch.save(); return NextResponse.json({ branch }); }