diff options
Diffstat (limited to 'src/app')
| -rw-r--r-- | src/app/api/company/[companyId]/product/[productId]/compute-different/route.tsx | 141 | ||||
| -rw-r--r-- | src/app/api/stock-opname/route.tsx | 158 |
2 files changed, 148 insertions, 151 deletions
diff --git a/src/app/api/company/[companyId]/product/[productId]/compute-different/route.tsx b/src/app/api/company/[companyId]/product/[productId]/compute-different/route.tsx index 1b641d3..8c7b377 100644 --- a/src/app/api/company/[companyId]/product/[productId]/compute-different/route.tsx +++ b/src/app/api/company/[companyId]/product/[productId]/compute-different/route.tsx @@ -9,11 +9,12 @@ type PostParams = { params: { companyId: string; productId: string } }; const SELF_HOST = process.env.SELF_HOST as string; export async function POST(request: NextRequest, { params }: PostParams) { - const totalQty: { [key in keyof typeof Team]: number | 0 } = { - COUNT1: 0, - COUNT2: 0, - COUNT3: 0, - VERIFICATION: 0, + // Initial Condition + const totalQty: { [key in keyof typeof Team]: number | null } = { + COUNT1: null, + COUNT2: null, + COUNT3: null, + VERIFICATION: null, }; const searchParams = new URLSearchParams({ @@ -21,93 +22,89 @@ export async function POST(request: NextRequest, { params }: PostParams) { productId: params.productId.toString(), }); - const stockOpnamesFetch = await fetch(`${SELF_HOST}/api/stock-opname/location?${searchParams}`); + const stockOpnamesFetch = await fetch( + `${SELF_HOST}/api/stock-opname/location?${searchParams}`, + ); const stockOpnames: StockOpnameLocationRes[] = await stockOpnamesFetch.json(); - let isDifferent: boolean = false; - let verificationCounter: number = 0; + let verificationCounter = 0; + // ============================= + // 2️⃣ ACCUMULATE QTY + // ============================= for (const opname of stockOpnames) { - let { COUNT1, COUNT2, COUNT3, VERIFICATION } = opname; + const { COUNT1, COUNT2, COUNT3, VERIFICATION } = opname; - if (totalQty["COUNT1"] === null && _.isNumber(COUNT1.quantity)) totalQty["COUNT1"] = 0; - if (totalQty["COUNT2"] === null && _.isNumber(COUNT2.quantity)) totalQty["COUNT2"] = 0; - if (totalQty["COUNT3"] === null && _.isNumber(COUNT3.quantity)) totalQty["COUNT3"] = 0; - if (totalQty["VERIFICATION"] === null && _.isNumber(VERIFICATION.quantity)) totalQty["VERIFICATION"] = 0; + const teams = { COUNT1, COUNT2, COUNT3, VERIFICATION }; - if (_.isNumber(COUNT1.quantity)) totalQty["COUNT1"] = (totalQty["COUNT1"] || 0) + COUNT1.quantity; - if (_.isNumber(COUNT2.quantity)) totalQty["COUNT2"] = (totalQty["COUNT2"] || 0) + COUNT2.quantity; - if (_.isNumber(COUNT3.quantity)) totalQty["COUNT3"] = (totalQty["COUNT3"] || 0) + COUNT3.quantity; - if (_.isNumber(VERIFICATION.quantity)) totalQty["VERIFICATION"] = (totalQty["VERIFICATION"] || 0) + VERIFICATION.quantity; + for (const key of Object.keys(teams) as (keyof typeof teams)[]) { + const qty = teams[key]?.quantity; - if (_.isNumber(VERIFICATION.quantity)) verificationCounter++; + if (_.isNumber(qty)) { + totalQty[key] = (totalQty[key] ?? 0) + qty; + + if (key === "VERIFICATION") { + verificationCounter++; + } + } + } } - const product = await prisma.product.findFirst({ where: { id: parseInt(params.productId) } }); - if (!product) return; + const product = await prisma.product.findFirst({ + where: { id: parseInt(params.productId) }, + }); + + if (!product) { + return NextResponse.json({ message: "Product not found" }, { status: 404 }); + } const onhandQty = product.onhandQty; - // const differenceQty = product.differenceQty - const allQty = totalQty["COUNT1"] + totalQty["COUNT2"] + totalQty["COUNT3"] + totalQty["VERIFICATION"]; - const differenceQty = onhandQty - allQty; - // const allQty = onhandQty + differenceQty - const zeroCount1: boolean = totalQty["COUNT1"] === 0; - const zeroCount2: boolean = totalQty["COUNT2"] === 0; - const zeroCount3: boolean = totalQty["COUNT3"] === 0; - - if (verificationCounter > 0){ - isDifferent = false - } else { - const conditional = { - verificationCheckAll: verificationCounter > 0 && verificationCounter === stockOpnames.length, - // anyCountEqWithOnhand: [totalQty['COUNT1'], totalQty['COUNT2'], totalQty['COUNT3']].includes(onhandQty), - anyCountEqWithOnhand: (totalQty["COUNT1"] === onhandQty && zeroCount2 && zeroCount3) || (totalQty["COUNT2"] === onhandQty && zeroCount3) || (totalQty["COUNT3"] === onhandQty), - anyCountEqWithAllQty: [totalQty["COUNT1"], totalQty["COUNT2"], totalQty["COUNT3"]].includes(allQty), - count1EqWithCount2: totalQty["COUNT1"] !== 0 && totalQty["COUNT2"] !== 0 && totalQty["COUNT1"] === totalQty["COUNT2"] && totalQty["COUNT3"] === null && totalQty["COUNT1"] !== onhandQty, - count1EqWithCount3: totalQty["COUNT1"] !== 0 && totalQty["COUNT3"] !== 0 && totalQty["COUNT1"] === totalQty["COUNT3"], - count2EqWithCount3: totalQty["COUNT2"] !== 0 && totalQty["COUNT3"] !== 0 && totalQty["COUNT2"] === totalQty["COUNT3"], - }; - if (conditional.verificationCheckAll || conditional.anyCountEqWithOnhand || conditional.count1EqWithCount2 || conditional.count1EqWithCount3 || conditional.count2EqWithCount3) { + let isDifferent = true; + + const countValues = [ + totalQty.COUNT1, + totalQty.COUNT2, + totalQty.COUNT3, + ].filter((v): v is number => v !== null); + // SPECIAL RULE: + // Kalau onhand = 0 dan ada salah satu tim input 0 → dianggap tidak selisih + if (onhandQty === 0 && countValues.includes(0)) { isDifferent = false; - } else { - isDifferent = true; - } } - for (const opname of stockOpnames) { - let { COUNT1, COUNT2, COUNT3, VERIFICATION } = opname; - - const count1 = _.isNumber(COUNT1.quantity) ? COUNT1.quantity : 0; - const count2 = _.isNumber(COUNT2.quantity) ? COUNT2.quantity : 0; - const count3 = _.isNumber(COUNT3.quantity) ? COUNT3.quantity : 0; - - const detailCondition = { - verificationCheckAll: _.isNumber(VERIFICATION.quantity), - anyCountEqWithOnhand: (totalQty["COUNT1"] === onhandQty && zeroCount2 && zeroCount3) || (totalQty["COUNT2"] === onhandQty && zeroCount3) || (totalQty["COUNT3"] === onhandQty), - count1EqWithCount2: totalQty["COUNT3"] === 0 && count1 === count2, - // count1EqWithCount3: count1 == count3, - count1EqWithCount3: count1 != 0 && count1 === count3, - count2EqWithCount3: totalQty["COUNT2"] !== 0 && totalQty["COUNT3"] !== 0 && count2 === count3, - // count3EqWithCount1_2: (COUNT3.quantity !== COUNT1.quantity) && (COUNT3.quantity !== COUNT2.quantity) && (COUNT1.quantity === COUNT2.quantity) && (COUNT3.quantity !== onhandQty) - }; - - if (detailCondition.verificationCheckAll || detailCondition.anyCountEqWithOnhand || detailCondition.count1EqWithCount2 || detailCondition.count1EqWithCount3 || detailCondition.count2EqWithCount3) { - isDifferent = false; - } else { - isDifferent = true; - break; - } + const uniqueCounts = [...new Set(countValues)]; + + const verificationExists = verificationCounter > 0; + if (verificationExists) { + isDifferent = false; } + // SPECIAL RULE + 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; + } - const payload = { + // ============================= + // 5️⃣ UPDATE PRODUCT + // ============================= + await prisma.product.update({ where: { id: product.id }, data: { isDifferent }, - }; - - await prisma.product.update(payload); + }); - return NextResponse.json(payload); + return NextResponse.json({ + data: { + productId: product.id, + totalQty, + isDifferent, + }, + }); } diff --git a/src/app/api/stock-opname/route.tsx b/src/app/api/stock-opname/route.tsx index 8e09351..e202d4d 100644 --- a/src/app/api/stock-opname/route.tsx +++ b/src/app/api/stock-opname/route.tsx @@ -1,4 +1,7 @@ -import { StockOpnameLocationRes, StockOpnameRequest } from "@/common/types/stockOpname"; +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"; @@ -20,7 +23,10 @@ export async function GET(request: NextRequest) { const intPage = page ? parseInt(page) : 1; if (!companyId) { - return NextResponse.json({ error: "Bad Request. Missing companyId" }, { status: 400 }); + return NextResponse.json( + { error: "Bad Request. Missing companyId" }, + { status: 400 }, + ); } const where: Prisma.ProductWhereInput = { @@ -32,7 +38,15 @@ export async function GET(request: NextRequest) { { name: { mode: "insensitive", contains: search ?? "" } }, { itemCode: { mode: "insensitive", contains: search ?? "" } }, { barcode: { mode: "insensitive", contains: search ?? "" } }, - { stockOpnames: { some: { location: { name: { mode: "insensitive", contains: search ?? "" } } } } }, + { + stockOpnames: { + some: { + location: { + name: { mode: "insensitive", contains: search ?? "" }, + }, + }, + }, + }, ], }, }; @@ -68,8 +82,16 @@ export async function GET(request: NextRequest) { }); } -const calculateOpnameQuantity = async (where: { productId: number; companyId: number }): Promise<Quantity> => { - const quantity: Quantity = { COUNT1: null, COUNT2: null, COUNT3: null, VERIFICATION: null }; +const calculateOpnameQuantity = async (where: { + productId: number; + companyId: number; +}): Promise<Quantity> => { + const quantity: Quantity = { + COUNT1: null, + COUNT2: null, + COUNT3: null, + VERIFICATION: null, + }; for (const team of Object.values(Team)) { const opnameQty = await prisma.stockOpname.groupBy({ @@ -86,7 +108,8 @@ const calculateOpnameQuantity = async (where: { productId: number; companyId: nu export async function POST(request: NextRequest) { const credential = getServerCredential(); - if (!credential) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + if (!credential) + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); const body: StockOpnameRequest = await request.json(); @@ -126,12 +149,18 @@ export async function POST(request: NextRequest) { 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 | 0 } = { - COUNT1: 0, - COUNT2: 0, - COUNT3: 0, - VERIFICATION: 0, +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({ @@ -139,89 +168,60 @@ const computeIsDifferent = async ({ companyId, productId }: { companyId: number; productId: productId.toString(), }); - const stockOpnamesFetch = await fetch(`${SELF_HOST}/api/stock-opname/location?${searchParams}`); + const stockOpnamesFetch = await fetch( + `${SELF_HOST}/api/stock-opname/location?${searchParams}`, + ); + const stockOpnames: StockOpnameLocationRes[] = await stockOpnamesFetch.json(); - let isDifferent: boolean = false; - let verificationCounter: number = 0; + let verificationExists = false; for (const opname of stockOpnames) { - let { COUNT1, COUNT2, COUNT3, VERIFICATION } = opname; + const { COUNT1, COUNT2, COUNT3, VERIFICATION } = opname; - if (totalQty["COUNT1"] === null && _.isNumber(COUNT1.quantity)) totalQty["COUNT1"] = 0; - if (totalQty["COUNT2"] === null && _.isNumber(COUNT2.quantity)) totalQty["COUNT2"] = 0; - if (totalQty["COUNT3"] === null && _.isNumber(COUNT3.quantity)) totalQty["COUNT3"] = 0; - if (totalQty["VERIFICATION"] === null && _.isNumber(VERIFICATION.quantity)) totalQty["VERIFICATION"] = 0; + const teams = { COUNT1, COUNT2, COUNT3, VERIFICATION }; - if (_.isNumber(totalQty["COUNT1"]) && _.isNumber(COUNT1.quantity)) totalQty["COUNT1"] += COUNT1.quantity; - if (_.isNumber(totalQty["COUNT2"]) && _.isNumber(COUNT2.quantity)) totalQty["COUNT2"] += COUNT2.quantity; - if (_.isNumber(totalQty["COUNT3"]) && _.isNumber(COUNT3.quantity)) totalQty["COUNT3"] += COUNT3.quantity; - if (_.isNumber(totalQty["VERIFICATION"]) && _.isNumber(VERIFICATION.quantity)) totalQty["VERIFICATION"] += VERIFICATION.quantity; + for (const key of Object.keys(teams) as (keyof typeof teams)[]) { + const qty = teams[key]?.quantity; - if (_.isNumber(VERIFICATION.quantity)) verificationCounter++; + if (_.isNumber(qty)) { + totalQty[key] = (totalQty[key] ?? 0) + qty; + + if (key === "VERIFICATION") { + verificationExists = true; + } + } + } } - const product = await prisma.product.findFirst({ where: { id: productId } }); + const product = await prisma.product.findFirst({ + where: { id: productId }, + }); + if (!product) return; const onhandQty = product.onhandQty; - const allQty = totalQty["COUNT1"] + totalQty["COUNT2"] + totalQty["COUNT3"] + totalQty["VERIFICATION"]; - const differenceQty = onhandQty - allQty; - // const differenceQty = product.differenceQty - // const allQty = onhandQty + differenceQty - const zeroCount1: boolean = totalQty["COUNT1"] === 0; - const zeroCount2: boolean = totalQty["COUNT2"] === 0; - const zeroCount3: boolean = totalQty["COUNT3"] === 0; + let isDifferent = true; - // Jika ada verifikasi qty, langsung AMAN - if (verificationCounter > 0) { - isDifferent = false; - } else { - // Jika tidak ada qty verifikasi, cek kondisi tim 1/2/3 - const conditional = { - // anyCountEqWithOnhand: [totalQty['COUNT1'], totalQty['COUNT2'], totalQty['COUNT3']].includes(onhandQty), - anyCountEqWithOnhand: (totalQty["COUNT1"] === onhandQty && zeroCount2 && zeroCount3) || (totalQty["COUNT2"] === onhandQty && zeroCount3) || (totalQty["COUNT3"] === onhandQty), - anyCountEqWithAllQty: [totalQty["COUNT1"], totalQty["COUNT2"], totalQty["COUNT3"]].includes(allQty), - count1EqWithCount2: totalQty["COUNT1"] !== null && totalQty["COUNT2"] !== null && totalQty["COUNT1"] === totalQty["COUNT2"] && totalQty["COUNT3"] === null, - count1EqWithCount3: totalQty["COUNT1"] !== null && totalQty["COUNT3"] !== null && totalQty["COUNT1"] === totalQty["COUNT3"], - count2EqWithCount3: totalQty["COUNT2"] !== 0 && totalQty["COUNT3"] !== 0 && totalQty["COUNT2"] === totalQty["COUNT3"], - }; - - if (conditional.anyCountEqWithOnhand || conditional.count1EqWithCount2 || conditional.count1EqWithCount3 || conditional.count2EqWithCount3) { - isDifferent = false; - } - else { - isDifferent = true; - } - } + const countValues = [ + totalQty.COUNT1, + totalQty.COUNT2, + totalQty.COUNT3, + ].filter((v): v is number => v !== null); - for (const opname of stockOpnames) { - let { COUNT1, COUNT2, COUNT3, VERIFICATION } = opname; - - // Normalize: treat missing (null/undefined) same as 0 - const count1 = _.isNumber(COUNT1.quantity) ? COUNT1.quantity : 0; - const count2 = _.isNumber(COUNT2.quantity) ? COUNT2.quantity : 0; - const count3 = _.isNumber(COUNT3.quantity) ? COUNT3.quantity : 0; - - const detailCondition = { - verificationCheckAll: _.isNumber(VERIFICATION.quantity), - anyCountEqWithOnhand: (totalQty["COUNT1"] === onhandQty && zeroCount2 && zeroCount3) || (totalQty["COUNT2"] === onhandQty && zeroCount3) || (totalQty["COUNT3"] === onhandQty), - count1EqWithCount2: totalQty["COUNT3"] === 0 && count1 === count2, - // count1EqWithCount3: count1 === count3, - count1EqWithCount3: count1 != 0 && count1 === count3, - count2EqWithCount3: totalQty["COUNT2"] !== 0 && totalQty["COUNT3"] !== 0 && count2 === count3, - // count3EqWithCount1_2: (COUNT3.quantity !== COUNT1.quantity) && (COUNT3.quantity !== COUNT2.quantity) && (COUNT1.quantity === COUNT2.quantity) && (COUNT3.quantity !== onhandQty) - }; - - if (detailCondition.verificationCheckAll || detailCondition.anyCountEqWithOnhand || detailCondition.count1EqWithCount2 || detailCondition.count1EqWithCount3 || detailCondition.count2EqWithCount3) { - isDifferent = false; - } - else { - isDifferent = true; - break; - } + 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({ |
