import { StockOpnameLocationRes, StockOpnameRequest, } from "@/common/types/stockOpname"; import { Prisma, Team } from "prisma/generated/client"; import { NextRequest, NextResponse } from "next/server"; import { prisma } from "prisma/client"; import _ from "lodash"; import getServerCredential from "@/common/libs/getServerCredential"; type Quantity = { [key in keyof typeof Team]: number | null; }; export async function GET(request: NextRequest) { const PAGE_SIZE = 30; const params = request.nextUrl.searchParams; const companyId = params.get("companyId"); const search = params.get("search"); const page = params.get("page") ?? null; const show = params.get("show"); const intPage = page ? parseInt(page) : 1; if (!companyId) { return NextResponse.json( { error: "Bad Request. Missing companyId" }, { status: 400 }, ); } const where: Prisma.ProductWhereInput = { AND: { stockOpnames: { some: {} }, companyId: parseInt(companyId), isDifferent: show ? (show == "diff" ? true : false) : undefined, OR: [ { name: { mode: "insensitive", contains: search ?? "" } }, { itemCode: { mode: "insensitive", contains: search ?? "" } }, { barcode: { mode: "insensitive", contains: search ?? "" } }, { stockOpnames: { some: { location: { name: { mode: "insensitive", contains: search ?? "" }, }, }, }, }, ], }, }; const products = await prisma.product.findMany({ skip: (intPage - 1) * PAGE_SIZE, take: PAGE_SIZE, where, }); const productCount = await prisma.product.count({ where }); const pagination = { page: intPage, totalPage: Math.ceil(productCount / PAGE_SIZE), }; type ProductWithSum = (typeof products)[0] & { quantity: Quantity }; const productsWithSum: ProductWithSum[] = []; for (const product of products) { const quantity = await calculateOpnameQuantity({ productId: product.id, companyId: parseInt(companyId), }); productsWithSum.push({ ...product, quantity }); } return NextResponse.json({ result: productsWithSum, ...pagination, }); } const calculateOpnameQuantity = async (where: { productId: number; companyId: number; }): Promise => { const quantity: Quantity = { COUNT1: null, COUNT2: null, COUNT3: null, VERIFICATION: null, }; for (const team of Object.values(Team)) { const opnameQty = await prisma.stockOpname.groupBy({ by: ["productId", "team"], _sum: { quantity: true }, where: { team, ...where }, }); if (opnameQty.length === 0) continue; quantity[team] = opnameQty[0]._sum.quantity; } return quantity; }; export async function POST(request: NextRequest) { const credential = getServerCredential(); if (!credential) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); const body: StockOpnameRequest = await request.json(); const { companyId, team } = credential; const query = { locationId: body.location, productId: body.product, companyId, team, }; const stockOpname = await prisma.stockOpname.findFirst({ where: query }); const data = { ...query, userId: credential.id, quantity: body.quantity, isDifferent: false, }; let newStockOpname = null; if (!stockOpname) { newStockOpname = await prisma.stockOpname.create({ data }); } else { newStockOpname = await prisma.stockOpname.update({ where: { id: stockOpname.id }, data, }); } await computeIsDifferent({ productId: body.product, companyId: companyId }); return NextResponse.json(newStockOpname); } const SELF_HOST = process.env.SELF_HOST as string; const computeIsDifferent = async ({ companyId, productId, }: { companyId: number; productId: number; }) => { const totalQty: { [key in keyof typeof Team]: number | null } = { COUNT1: null, COUNT2: null, COUNT3: null, VERIFICATION: null, }; const searchParams = new URLSearchParams({ companyId: companyId.toString(), productId: productId.toString(), }); const stockOpnamesFetch = await fetch( `${SELF_HOST}/api/stock-opname/location?${searchParams}`, ); const stockOpnames: StockOpnameLocationRes[] = await stockOpnamesFetch.json(); let verificationExists = false; for (const opname of stockOpnames) { const { COUNT1, COUNT2, COUNT3, VERIFICATION } = opname; const teams = { COUNT1, COUNT2, COUNT3, VERIFICATION }; for (const key of Object.keys(teams) as (keyof typeof teams)[]) { const qty = teams[key]?.quantity; if (_.isNumber(qty)) { totalQty[key] = (totalQty[key] ?? 0) + qty; if (key === "VERIFICATION") { verificationExists = true; } } } } const product = await prisma.product.findFirst({ where: { id: productId }, }); if (!product) return; const onhandQty = product.onhandQty; let isDifferent = true; const countValues = [ totalQty.COUNT1, totalQty.COUNT2, totalQty.COUNT3, ].filter((v): v is number => v !== null); const uniqueCounts = [...new Set(countValues)]; if (verificationExists) { isDifferent = false; } else if (onhandQty === 0 && countValues.includes(0)) { isDifferent = false; } else if (countValues.length === 1) { isDifferent = countValues[0] !== onhandQty; } else if (uniqueCounts.length === 1) { isDifferent = uniqueCounts[0] !== onhandQty; } else { isDifferent = true; } await prisma.product.update({ where: { id: product.id }, data: { isDifferent }, }); };