summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app/api/company/[companyId]/product/[productId]/compute-different/route.tsx141
-rw-r--r--src/app/api/stock-opname/route.tsx158
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({