summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package.json1
-rw-r--r--src/app/api/company/[companyId]/product/[productId]/compute-different/route.tsx133
-rw-r--r--src/app/api/stock-opname/route.tsx234
-rw-r--r--src/modules/result/components/MoreMenu.tsx17
-rw-r--r--src/modules/result/components/Table.tsx267
-rw-r--r--src/modules/result/index.tsx10
6 files changed, 336 insertions, 326 deletions
diff --git a/package.json b/package.json
index 3ed98f1..1b5ef6a 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
"bcrypt": "^5.1.1",
"clsx": "^2.0.0",
"cookies-next": "^4.0.0",
+ "dotenv": "^16.4.5",
"framer-motion": "^10.16.4",
"jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",
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 ff9cd74..acfb413 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
@@ -1,99 +1,104 @@
-import { StockOpnameLocationRes } from "@/common/types/stockOpname"
-import { NextRequest, NextResponse } from "next/server"
-import { prisma } from "prisma/client"
-import { Team } from "prisma/generated/client"
-import _ from "lodash"
+import { StockOpnameLocationRes } from "@/common/types/stockOpname";
+import { NextRequest, NextResponse } from "next/server";
+import { prisma } from "prisma/client";
+import { Team } from "prisma/generated/client";
+import _ from "lodash";
-type PostParams = { params: { companyId: string, productId: string } }
+type PostParams = { params: { companyId: string; productId: string } };
-const SELF_HOST = process.env.SELF_HOST as 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 | null } = {
- COUNT1: null,
- COUNT2: null,
- COUNT3: null,
- VERIFICATION: null
- }
+ const totalQty: { [key in keyof typeof Team]: number | 0 } = {
+ COUNT1: 0,
+ COUNT2: 0,
+ COUNT3: 0,
+ VERIFICATION: 0,
+ };
const searchParams = new URLSearchParams({
companyId: params.companyId.toString(),
- productId: params.productId.toString()
- })
+ productId: params.productId.toString(),
+ });
- const stockOpnamesFetch = await fetch(`${SELF_HOST}/api/stock-opname/location?${searchParams}`)
- const stockOpnames: StockOpnameLocationRes[] = await stockOpnamesFetch.json()
+ 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 isDifferent: boolean = false;
+ let verificationCounter: number = 0;
for (const opname of stockOpnames) {
- let { COUNT1, COUNT2, COUNT3, VERIFICATION } = opname
+ let { 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
+ 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;
- 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
+ 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;
- if (_.isNumber(VERIFICATION.quantity)) verificationCounter++
+ if (_.isNumber(VERIFICATION.quantity)) 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;
- const onhandQty = product.onhandQty
- const differenceQty = product.differenceQty
- const allQty = onhandQty + differenceQty
+ 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;
const conditional = {
verificationCheckAll: verificationCounter > 0 && verificationCounter === stockOpnames.length,
- anyCountEqWithOnhand: [totalQty['COUNT1'], totalQty['COUNT2'], totalQty['COUNT3']].includes(onhandQty),
- anyCountEqWithAllQty: [totalQty['COUNT1'], totalQty['COUNT2'], totalQty['COUNT3']].includes(allQty),
- count1EqWithCount2: totalQty['COUNT1'] !== null && totalQty['COUNT2'] !== null && totalQty['COUNT1'] === totalQty['COUNT2'],
- count1EqWithCount3: totalQty['COUNT1'] !== null && totalQty['COUNT3'] !== null && totalQty['COUNT1'] === totalQty['COUNT3'],
- count2EqWithCount3: totalQty['COUNT2'] !== null && totalQty['COUNT3'] !== null && totalQty['COUNT2'] === totalQty['COUNT3'],
+ // 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) {
+ isDifferent = false;
+ } else {
+ isDifferent = true;
}
- isDifferent = !(
- conditional.verificationCheckAll ||
- conditional.anyCountEqWithOnhand ||
- conditional.anyCountEqWithAllQty ||
- conditional.count1EqWithCount2 ||
- conditional.count1EqWithCount3 ||
- conditional.count2EqWithCount3
- )
-
for (const opname of stockOpnames) {
- let { COUNT1, COUNT2, COUNT3, VERIFICATION } = opname
+ let { COUNT1, COUNT2, COUNT3, VERIFICATION } = opname;
const detailCondition = {
verificationCheckAll: _.isNumber(VERIFICATION.quantity),
- count1EqWithCount2: COUNT1.quantity === COUNT2.quantity,
+ anyCountEqWithOnhand: (totalQty["COUNT1"] === onhandQty && zeroCount2 && zeroCount3) || (totalQty["COUNT2"] === onhandQty && zeroCount3) || totalQty["COUNT3"] === onhandQty,
+ count1EqWithCount2: totalQty["COUNT3"] === 0 && COUNT1.quantity === COUNT2.quantity && totalQty["COUNT1"] !== onhandQty,
count1EqWithCount3: COUNT1.quantity === COUNT3.quantity,
- count2EqWithCount3: COUNT2.quantity === COUNT3.quantity,
+ count2EqWithCount3: totalQty["COUNT2"] !== null && totalQty["COUNT3"] !== null && totalQty["COUNT2"] === totalQty["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;
}
- isDifferent = !(
- detailCondition.verificationCheckAll ||
- detailCondition.count1EqWithCount2 ||
- detailCondition.count1EqWithCount3 ||
- detailCondition.count2EqWithCount3
- )
-
- if (isDifferent) break
}
+
const payload = {
where: { id: product.id },
- data: { isDifferent }
- }
+ data: { isDifferent },
+ };
- await prisma.product.update(payload)
+ await prisma.product.update(payload);
- return NextResponse.json(payload)
-} \ No newline at end of file
+ return NextResponse.json(payload);
+}
diff --git a/src/app/api/stock-opname/route.tsx b/src/app/api/stock-opname/route.tsx
index 154ed14..69caca0 100644
--- a/src/app/api/stock-opname/route.tsx
+++ b/src/app/api/stock-opname/route.tsx
@@ -2,223 +2,213 @@ import { StockOpnameLocationRes, StockOpnameRequest } from "@/common/types/stock
import { Prisma, Team } from "prisma/generated/client";
import { NextRequest, NextResponse } from "next/server";
import { prisma } from "prisma/client";
-import _ from "lodash"
+import _ from "lodash";
import getServerCredential from "@/common/libs/getServerCredential";
type Quantity = {
- [key in keyof typeof Team]: number | null
-}
+ [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
+ 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 })
+ 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,
+ 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 ?? '' } } } } },
- ]
- }
- }
+ { 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
- })
+ where,
+ });
- const productCount = await prisma.product.count({ 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 }
+ type ProductWithSum = (typeof products)[0] & { quantity: Quantity };
- const productsWithSum: ProductWithSum[] = []
+ const productsWithSum: ProductWithSum[] = [];
for (const product of products) {
const quantity = await calculateOpnameQuantity({
productId: product.id,
- companyId: parseInt(companyId)
- })
- productsWithSum.push({ ...product, quantity })
+ companyId: parseInt(companyId),
+ });
+ productsWithSum.push({ ...product, quantity });
}
return NextResponse.json({
result: productsWithSum,
- ...pagination
- })
+ ...pagination,
+ });
}
-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({
- by: ['productId', 'team'],
+ by: ["productId", "team"],
_sum: { quantity: true },
- where: { team, ...where }
- })
- if (opnameQty.length === 0) continue
- quantity[team] = opnameQty[0]._sum.quantity
+ where: { team, ...where },
+ });
+ if (opnameQty.length === 0) continue;
+ quantity[team] = opnameQty[0]._sum.quantity;
}
- return quantity
-}
+ return quantity;
+};
export async function POST(request: NextRequest) {
- const credential = getServerCredential()
+ 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()
+ const body: StockOpnameRequest = await request.json();
- const { companyId, team } = credential
+ const { companyId, team } = credential;
const query = {
locationId: body.location,
productId: body.product,
companyId,
- team
- }
+ team,
+ };
- const stockOpname = await prisma.stockOpname.findFirst({ where: query })
+ const stockOpname = await prisma.stockOpname.findFirst({ where: query });
const data = {
...query,
userId: credential.id,
quantity: body.quantity,
- isDifferent: false
- }
+ isDifferent: false,
+ };
- let newStockOpname = null
+ let newStockOpname = null;
if (!stockOpname) {
- newStockOpname = await prisma.stockOpname.create({ data })
+ newStockOpname = await prisma.stockOpname.create({ data });
} else {
newStockOpname = await prisma.stockOpname.update({
where: { id: stockOpname.id },
- data
- })
+ data,
+ });
}
- await computeIsDifferent({ productId: body.product, companyId: companyId })
+ await computeIsDifferent({ productId: body.product, companyId: companyId });
- return NextResponse.json(newStockOpname)
+ 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 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 searchParams = new URLSearchParams({
companyId: companyId.toString(),
- productId: productId.toString()
- })
+ productId: productId.toString(),
+ });
- const stockOpnamesFetch = await fetch(`${SELF_HOST}/api/stock-opname/location?${searchParams}`)
- const stockOpnames: StockOpnameLocationRes[] = await stockOpnamesFetch.json()
+ 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 isDifferent: boolean = false;
+ let verificationCounter: number = 0;
for (const opname of stockOpnames) {
- let { COUNT1, COUNT2, COUNT3, VERIFICATION } = opname
+ let { 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
+ 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;
- 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
+ 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;
- if (_.isNumber(VERIFICATION.quantity)) verificationCounter++
+ if (_.isNumber(VERIFICATION.quantity)) verificationCounter++;
}
- const product = await prisma.product.findFirst({ where: { id: productId } })
- if (!product) return
+ const product = await prisma.product.findFirst({ where: { id: productId } });
+ if (!product) return;
- const onhandQty = product.onhandQty
- const differenceQty = product.differenceQty
- const allQty = onhandQty + differenceQty
+ 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 conditional = {
verificationCheckAll: verificationCounter > 0 && verificationCounter === stockOpnames.length,
- anyCountEqWithOnhand: [totalQty['COUNT1'], totalQty['COUNT2'], totalQty['COUNT3']].includes(onhandQty),
- anyCountEqWithAllQty: [totalQty['COUNT1'], totalQty['COUNT2'], totalQty['COUNT3']].includes(allQty),
- count1EqWithCount2: totalQty['COUNT1'] !== null && totalQty['COUNT2'] !== null && totalQty['COUNT1'] === totalQty['COUNT2'],
- count1EqWithCount3: totalQty['COUNT1'] !== null && totalQty['COUNT3'] !== null && totalQty['COUNT1'] === totalQty['COUNT3'],
- count2EqWithCount3: totalQty['COUNT2'] !== null && totalQty['COUNT3'] !== null && totalQty['COUNT2'] === totalQty['COUNT3'],
+ // anyCountEqWithOnhand: [totalQty['COUNT1'], totalQty['COUNT2'], totalQty['COUNT3']].includes(onhandQty),
+ anyCountEqWithOnhand: totalQty["COUNT1"] === onhandQty || totalQty["COUNT2"] === onhandQty || totalQty["COUNT1"] === 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"] !== null && totalQty["COUNT3"] !== null && totalQty["COUNT2"] === totalQty["COUNT3"],
+ };
+
+ if (conditional.verificationCheckAll || conditional.anyCountEqWithOnhand || conditional.count1EqWithCount2 || conditional.count1EqWithCount3 || conditional.count2EqWithCount3) {
+ isDifferent = false;
+ } else {
+ isDifferent = true;
}
- isDifferent = !(
- conditional.verificationCheckAll ||
- conditional.anyCountEqWithOnhand ||
- conditional.anyCountEqWithAllQty ||
- conditional.count1EqWithCount2 ||
- conditional.count1EqWithCount3 ||
- conditional.count2EqWithCount3
- )
-
for (const opname of stockOpnames) {
- let { COUNT1, COUNT2, COUNT3, VERIFICATION } = opname
+ let { COUNT1, COUNT2, COUNT3, VERIFICATION } = opname;
const detailCondition = {
verificationCheckAll: _.isNumber(VERIFICATION.quantity),
- count1EqWithCount2: COUNT1.quantity === COUNT2.quantity,
+ anyCountEqWithOnhand: totalQty["COUNT1"] === onhandQty || totalQty["COUNT2"] === onhandQty || totalQty["COUNT3"] === onhandQty,
+ count1EqWithCount2: totalQty["COUNT3"] === 0 && COUNT1.quantity === COUNT2.quantity,
count1EqWithCount3: COUNT1.quantity === COUNT3.quantity,
- count2EqWithCount3: COUNT2.quantity === COUNT3.quantity,
+ count2EqWithCount3: totalQty["COUNT2"] !== null && totalQty["COUNT3"] !== null && totalQty["COUNT2"] === totalQty["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;
}
- isDifferent = !(
- detailCondition.verificationCheckAll ||
- detailCondition.count1EqWithCount2 ||
- detailCondition.count1EqWithCount3 ||
- detailCondition.count2EqWithCount3
- )
-
- if (isDifferent) break
}
await prisma.product.update({
where: { id: product.id },
- data: { isDifferent }
- })
-} \ No newline at end of file
+ data: { isDifferent },
+ });
+};
diff --git a/src/modules/result/components/MoreMenu.tsx b/src/modules/result/components/MoreMenu.tsx
index 03c9786..cd4e8be 100644
--- a/src/modules/result/components/MoreMenu.tsx
+++ b/src/modules/result/components/MoreMenu.tsx
@@ -1,16 +1,29 @@
"use client";
import { Button, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger, useDisclosure } from '@nextui-org/react'
import { MoreVerticalIcon } from 'lucide-react'
-import React from 'react'
+import React, { useState, useEffect } from 'react'
import ImportModal from './ImportModal';
import ProductModal from './ProductModal';
import getClientCredential from '@/common/libs/getClientCredential';
+type Credential = {
+ team: string;
+};
+
const MoreMenu = () => {
- const credential = getClientCredential()
+ const [credential, setCredential] = useState<Credential | null>(null);
const importModal = useDisclosure();
const productModal = useDisclosure();
+ useEffect(() => {
+ const cred = getClientCredential();
+ setCredential(cred);
+ }, []);
+
+ if (!credential || credential.team !== 'VERIFICATION') {
+ return null;
+ }
+
return credential && credential.team == 'VERIFICATION' && (
<>
<Dropdown>
diff --git a/src/modules/result/components/Table.tsx b/src/modules/result/components/Table.tsx
index 082f988..555c8af 100644
--- a/src/modules/result/components/Table.tsx
+++ b/src/modules/result/components/Table.tsx
@@ -1,9 +1,9 @@
"use client";
import { useResultStore } from "@/common/stores/useResultStore";
import { StockOpnameRes } from "@/common/types/stockOpname";
-import { Button, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger, Pagination, Skeleton, Spacer, Spinner } from "@nextui-org/react"
+import { Button, Dropdown, DropdownItem, DropdownMenu, DropdownTrigger, Pagination, Skeleton, Spacer, Spinner } from "@nextui-org/react";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
-import styles from "./table.module.css"
+import styles from "./table.module.css";
import clsxm from "@/common/libs/clsxm";
import DetailRow from "./DetailRow";
import { useDebounce } from "usehooks-ts";
@@ -16,165 +16,170 @@ import toast from "@/common/libs/toast";
import { Product } from "prisma/generated/client";
const Table = () => {
- const credential = getClientCredential()
+ const credential = getClientCredential();
- const { filter: { company, search, show, page }, updateFilter } = useResultStore()
- const debouncedSearch = useDebounce(search, 500)
+ const {
+ filter: { company, search, show, page },
+ updateFilter,
+ } = useResultStore();
+ const debouncedSearch = useDebounce(search, 500);
useEffect(() => {
- updateFilter('page', 1)
- }, [company, debouncedSearch, show, updateFilter])
+ updateFilter("page", 1);
+ }, [company, debouncedSearch, show, updateFilter]);
const stockOpnames = useQuery({
- queryKey: ['stockOpnames', company, debouncedSearch, page, show],
+ queryKey: ["stockOpnames", company, debouncedSearch, page, show],
queryFn: async () => {
- const searchParams = new URLSearchParams()
- if (!company) return null
- searchParams.set('companyId', company)
+ const searchParams = new URLSearchParams();
+ if (!company) return null;
+ searchParams.set("companyId", company);
- const showValue = SHOWING_SELECTIONS.find((item) => item.key === show)?.value || ''
- searchParams.set('show', showValue);
+ const showValue = SHOWING_SELECTIONS.find((item) => item.key === show)?.value || "";
+ searchParams.set("show", showValue);
- if (debouncedSearch) searchParams.set('search', debouncedSearch)
+ if (debouncedSearch) searchParams.set("search", debouncedSearch);
- searchParams.set('page', page.toString());
+ searchParams.set("page", page.toString());
- return await fetch(`/api/stock-opname?${searchParams}`)
- .then(res => {
- window.scrollTo({ top: 0, 'behavior': 'smooth' })
- return res.json()
- })
+ return await fetch(`/api/stock-opname?${searchParams}`).then((res) => {
+ window.scrollTo({ top: 0, behavior: "smooth" });
+ return res.json();
+ });
},
- placeholderData: keepPreviousData
- })
+ placeholderData: keepPreviousData,
+ });
- const { filter } = useResultStore()
+ const { filter } = useResultStore();
- const [exportLoad, setExportLoad] = useState<boolean>(false)
+ const [exportLoad, setExportLoad] = useState<boolean>(false);
const exportResult = async () => {
- setExportLoad(true)
- const response = await fetch(`/api/stock-opname/export?companyId=${filter.company}`)
- const buffer = await response.arrayBuffer()
- console.log({ type: response.headers.get('type') });
-
- const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
- const url = window.URL.createObjectURL(blob)
- const a = document.createElement('a')
- a.href = url
- a.download = `export_${moment().format('MM_DD_HH_ss')}.xlsx`
- document.body.appendChild(a)
- a.click()
- document.body.removeChild(a)
- setExportLoad(false)
- }
+ setExportLoad(true);
+ const response = await fetch(`/api/stock-opname/export?companyId=${filter.company}`);
+ const buffer = await response.arrayBuffer();
+
+ const blob = new Blob([buffer], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
+ const url = window.URL.createObjectURL(blob);
+ const a = document.createElement("a");
+ a.href = url;
+ a.download = `export_${moment().format("MM_DD_HH_ss")}.xlsx`;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ setExportLoad(false);
+ };
const toggleDifferent = async (id: number) => {
- const response = await fetch(`/api/product/${id}/toggle-different`, { method: 'POST' })
- const product: Product = await response.json()
- toast(`Berhasil mengubah status barang ${product.itemCode} ${product.name} menjadi ${product.isDifferent ? 'selisih' : 'aman'}`, { duration: 10000 })
- stockOpnames.refetch()
- }
+ const response = await fetch(`/api/product/${id}/toggle-different`, { method: "POST" });
+ const product: Product = await response.json();
+ toast(`Berhasil mengubah status barang ${product.itemCode} ${product.name} menjadi ${product.isDifferent ? "selisih" : "aman"}`, { duration: 10000 });
+ stockOpnames.refetch();
+ };
const recompute = async (productId: number, companyId: string) => {
- const response = await fetch(`/api/company/${companyId}/product/${productId}/compute-different`, { method: 'POST' })
- const stockOpname = await response.json()
- toast(`Berhasil menghitung ulang status barang, hasilnya ${stockOpname.data.isDifferent ? 'selisih' : 'aman'}`)
- stockOpnames.refetch()
- }
+ const response = await fetch(`/api/company/${companyId}/product/${productId}/compute-different`, { method: "POST" });
+ const stockOpname = await response.json();
+ toast(`Berhasil menghitung ulang status barang, hasilnya ${stockOpname.data.isDifferent ? "selisih" : "aman"}`);
+ stockOpnames.refetch();
+ };
- const isLoading = stockOpnames.isLoading || stockOpnames.isRefetching
+ const isLoading = stockOpnames.isLoading || stockOpnames.isRefetching;
- const COL_LENGTH = 9
+ const COL_LENGTH = 9;
return (
<>
<div className="flex">
<Button type="button" onPress={exportResult} disabled={exportLoad} variant="flat" className="ml-auto mb-4">
- {exportLoad ? <><Spinner size="sm" />Exporting...</> : 'Export'}
+ {exportLoad ? (
+ <>
+ <Spinner size="sm" />
+ Exporting...
+ </>
+ ) : (
+ "Export"
+ )}
</Button>
</div>
<div className="w-full flex-1 overflow-auto pb-4">
<table className="w-full">
<thead className={styles.thead}>
- <th className={styles.th}>STATUS</th>
- <th className={clsxm(styles.th, '!text-left')}>NAMA PRODUK</th>
- <th className={styles.th}>TIM HITUNG 1</th>
- <th className={styles.th}>TIM HITUNG 2</th>
- <th className={styles.th}>TIM HITUNG 3</th>
- <th className={styles.th}>TIM VERIFIKASI</th>
- <th className={styles.th}>ON-HAND QTY</th>
- <th className={styles.th}>GUDANG SELISIH</th>
- <th className={styles.th}></th>
+ <tr>
+ <th className={styles.th}>STATUS</th>
+ <th className={clsxm(styles.th, "!text-left")}>NAMA PRODUK</th>
+ <th className={styles.th}>TIM HITUNG 1</th>
+ <th className={styles.th}>TIM HITUNG 2</th>
+ <th className={styles.th}>TIM HITUNG 3</th>
+ <th className={styles.th}>TIM VERIFIKASI</th>
+ <th className={styles.th}>ON-HAND QTY</th>
+ <th className={styles.th}>GUDANG SELISIH</th>
+ <th className={styles.th}></th>
+ </tr>
</thead>
<tbody className={styles.tbody}>
- {!isLoading && stockOpnames.data?.result.map((stockOpname: StockOpnameRes['result']) => (
- <>
- <tr key={stockOpname.id} className={clsxm("border-t border-neutral-200", {
- "text-danger-600": stockOpname.isDifferent,
- "text-success-600": !stockOpname.isDifferent
- })}
- >
- <td className={styles.td}>
- <div className={clsxm("w-full rounded-lg mr-1 p-1 text-xs text-white whitespace-nowrap", {
- "bg-danger-600": stockOpname.isDifferent,
- "bg-success-600": !stockOpname.isDifferent,
- })}>
- {stockOpname.isDifferent ? 'Selisih' : 'Aman'}
- </div>
- </td>
- <td className={clsxm(styles.td, '!text-left flex min-w-[250px]')} aria-label="name">
- {stockOpname.itemCode ? `[${stockOpname.itemCode}] ` : ''}
- {stockOpname.name}
- {stockOpname.barcode ? ` [${stockOpname.barcode}]` : ''}
- </td>
- <td className={styles.td}>
- {credential?.team == 'VERIFICATION' && typeof stockOpname.quantity.COUNT1 === 'number' ? stockOpname.quantity.COUNT1 : '-'}
- </td>
- <td className={styles.td}>
- {credential?.team == 'VERIFICATION' && typeof stockOpname.quantity.COUNT2 === 'number' ? stockOpname.quantity.COUNT2 : '-'}
- </td>
- <td className={styles.td}>
- {credential?.team == 'VERIFICATION' && typeof stockOpname.quantity.COUNT3 === 'number' ? stockOpname.quantity.COUNT3 : '-'}
- </td>
- <td className={styles.td}>
- {credential?.team == 'VERIFICATION' && typeof stockOpname.quantity.VERIFICATION === 'number' ? stockOpname.quantity.VERIFICATION : '-'}
- </td>
- <td className={styles.td}>
- {credential?.team == 'VERIFICATION' ? stockOpname.onhandQty : '-'}
- </td>
- <td className={styles.td}>
- {credential?.team == 'VERIFICATION' ? stockOpname.differenceQty : '-'}
- </td>
- <td>
- {credential?.team == 'VERIFICATION' && (
- <Dropdown>
- <DropdownTrigger>
- <Button variant="light" className="p-1 min-w-fit">
- <MoreVerticalIcon size={16} />
- </Button>
- </DropdownTrigger>
- <DropdownMenu>
- <DropdownItem key="recompute" onPress={() => recompute(stockOpname.id, company)}>
- Re-compute
- </DropdownItem>
- <DropdownItem key="toggleDifferent" onPress={() => toggleDifferent(stockOpname.id)}>
- Tandai {stockOpname.isDifferent ? 'aman' : 'selisih'}
- </DropdownItem>
- </DropdownMenu>
- </Dropdown>
- )}
- </td>
- </tr>
-
- <DetailRow productId={stockOpname.id} />
- </>
- ))}
+ {!isLoading &&
+ stockOpnames.data?.result.map((stockOpname: StockOpnameRes["result"]) => (
+ <>
+ <tr
+ key={stockOpname.id}
+ className={clsxm("border-t border-neutral-200", {
+ "text-danger-600": stockOpname.isDifferent,
+ "text-success-600": !stockOpname.isDifferent,
+ })}
+ >
+ <td className={styles.td}>
+ <div
+ className={clsxm("w-full rounded-lg mr-1 p-1 text-xs text-white whitespace-nowrap", {
+ "bg-danger-600": stockOpname.isDifferent,
+ "bg-success-600": !stockOpname.isDifferent,
+ })}
+ >
+ {stockOpname.isDifferent ? "Selisih" : "Aman"}
+ </div>
+ </td>
+ <td className={clsxm(styles.td, "!text-left flex min-w-[250px]")} aria-label="name">
+ {stockOpname.itemCode ? `[${stockOpname.itemCode}] ` : ""}
+ {stockOpname.name}
+ {stockOpname.barcode ? ` [${stockOpname.barcode}]` : ""}
+ </td>
+ <td className={styles.td}>{credential?.team == "VERIFICATION" && typeof stockOpname.quantity.COUNT1 === "number" ? stockOpname.quantity.COUNT1 : "-"}</td>
+ <td className={styles.td}>{credential?.team == "VERIFICATION" && typeof stockOpname.quantity.COUNT2 === "number" ? stockOpname.quantity.COUNT2 : "-"}</td>
+ <td className={styles.td}>{credential?.team == "VERIFICATION" && typeof stockOpname.quantity.COUNT3 === "number" ? stockOpname.quantity.COUNT3 : "-"}</td>
+ <td className={styles.td}>{credential?.team == "VERIFICATION" && typeof stockOpname.quantity.VERIFICATION === "number" ? stockOpname.quantity.VERIFICATION : "-"}</td>
+ <td className={styles.td}>{credential?.team == "VERIFICATION" ? stockOpname.onhandQty : "-"}</td>
+ <td className={styles.td}>{credential?.team == "VERIFICATION" ? stockOpname.differenceQty : "-"}</td>
+ <td>
+ {credential?.team == "VERIFICATION" && (
+ <Dropdown>
+ <DropdownTrigger>
+ <Button variant="light" className="p-1 min-w-fit">
+ <MoreVerticalIcon size={16} />
+ </Button>
+ </DropdownTrigger>
+ <DropdownMenu>
+ <DropdownItem key="recompute" onPress={() => recompute(stockOpname.id, company)}>
+ Re-compute
+ </DropdownItem>
+ <DropdownItem key="toggleDifferent" onPress={() => toggleDifferent(stockOpname.id)}>
+ Tandai {stockOpname.isDifferent ? "aman" : "selisih"}
+ </DropdownItem>
+ </DropdownMenu>
+ </Dropdown>
+ )}
+ </td>
+ </tr>
+
+ <DetailRow productId={stockOpname.id} />
+ </>
+ ))}
{!isLoading && stockOpnames.data?.result.length === 0 && (
<tr>
- <td colSpan={COL_LENGTH} className="text-center text-neutral-600 py-4">Belum ada data untuk ditampilkan</td>
+ <td colSpan={COL_LENGTH} className="text-center text-neutral-600 py-4">
+ Belum ada data untuk ditampilkan
+ </td>
</tr>
)}
@@ -191,19 +196,13 @@ const Table = () => {
)}
</tbody>
</table>
-
</div>
<Spacer y={4} />
- <Pagination
- page={stockOpnames.data?.page || 1}
- total={stockOpnames.data?.totalPage || 1}
- onChange={(page) => updateFilter('page', page)}
- className="min-h-[36px] m-0 p-0"
- />
+ <Pagination page={stockOpnames.data?.page || 1} total={stockOpnames.data?.totalPage || 1} onChange={(page) => updateFilter("page", page)} className="min-h-[36px] m-0 p-0" />
</>
- )
-}
+ );
+};
-export default Table \ No newline at end of file
+export default Table;
diff --git a/src/modules/result/index.tsx b/src/modules/result/index.tsx
index cbb2173..095b4db 100644
--- a/src/modules/result/index.tsx
+++ b/src/modules/result/index.tsx
@@ -1,15 +1,17 @@
import { Spacer } from "@nextui-org/react"
import Filter from "./components/Filter"
-import styles from "./result.module.css"
import Table from "./components/Table"
import MoreMenu from "./components/MoreMenu"
+import { Suspense } from "react"
const Result = () => {
return (
<>
- <div className={styles.wrapper}>
- <div className={styles.title}>Stock Opname Result</div>
- <MoreMenu />
+ <div className="flex justify-between items-center">
+ <div className="font-semibold text-xl">Stock Opname Result</div>
+ <Suspense fallback={<div>Loading...</div>}>
+ <MoreMenu />
+ </Suspense>
</div>
<Spacer y={4} />
<Filter />