diff --git a/src/app/api/po/actions.ts b/src/app/api/po/actions.ts index db872d1..f417fad 100644 --- a/src/app/api/po/actions.ts +++ b/src/app/api/po/actions.ts @@ -31,6 +31,7 @@ export interface PurchaseQcCheck { qty: number; } export interface StockInInput { + status: string productLotNo?: string, receiptDate: string acceptedQty: number @@ -39,18 +40,23 @@ export interface StockInInput { expiryDate: string } export interface PurchaseQCInput { + status: string sampleRate: number; sampleWeight: number; totalWeight: number; qcCheck: PurchaseQcCheck[]; } export interface EscalationInput { + status: string handler: string stockInLine: StockInLineEntry[] } export interface PutawayInput { - handler: string - stockInLine: StockInLineEntry[] + status: string + acceptedQty: number + warehouseId: number + // handler: string + // stockInLine: StockInLineEntry[] } export type ModalFormInput = Partial diff --git a/src/app/api/po/index.ts b/src/app/api/po/index.ts index 8c1e10b..4fa6a6e 100644 --- a/src/app/api/po/index.ts +++ b/src/app/api/po/index.ts @@ -20,6 +20,7 @@ export interface PurchaseOrderLine { itemNo: string itemName: string qty: number + processed: number uom?: string price: number status: string diff --git a/src/app/api/qc/actions.ts b/src/app/api/qc/actions.ts new file mode 100644 index 0000000..ee03960 --- /dev/null +++ b/src/app/api/qc/actions.ts @@ -0,0 +1,15 @@ +"use server"; +import { BASE_API_URL } from "@/config/api"; +import { revalidateTag } from "next/cache"; +import { cache } from "react"; +import { serverFetchJson } from "@/app/utils/fetchUtil"; +import { QcItemWithChecks } from "."; + +export const fetchQcItemCheck = cache(async (itemId?: number) => { + var url = `${BASE_API_URL}/qcCheck` + if (itemId) url +=`/${itemId}` + return serverFetchJson(url, { + next: { tags: ["qc"] }, + }); + }); + \ No newline at end of file diff --git a/src/app/api/qc/index.ts b/src/app/api/qc/index.ts index cb6f244..fd20cfd 100644 --- a/src/app/api/qc/index.ts +++ b/src/app/api/qc/index.ts @@ -1,12 +1,30 @@ import { cache } from "react"; import "server-only"; +import { serverFetchJson } from "@/app/utils/fetchUtil"; +import { BASE_API_URL } from "@/config/api"; export interface QcItemWithChecks { id: number; code: string; name: string; itemId: number; - lowerLimit: number; - upperLimit: number; - description: string; + lowerLimit: number | undefined; + upperLimit: number | undefined; + description: string | undefined; } + + export const fetchQcItemCheckList = cache(async () => { + return serverFetchJson(`${BASE_API_URL}/qc/list`, { + next: { tags: ["qc"] }, + }); + }); + + + +export const fetchQcItemCheck = cache(async (itemId?: number) => { + var url = `${BASE_API_URL}/qcCheck` + if (itemId) url +=`/${itemId}` + return serverFetchJson(url, { + next: { tags: ["qc"] }, + }); +}); \ No newline at end of file diff --git a/src/app/api/tasks/index.ts b/src/app/api/tasks/index.ts index f5889d7..b0c8888 100644 --- a/src/app/api/tasks/index.ts +++ b/src/app/api/tasks/index.ts @@ -1,7 +1,7 @@ -import { serverFetchJson } from "@/app/utils/fetchUtil"; -import { BASE_API_URL } from "@/config/api"; import { cache } from "react"; import "server-only"; +import { serverFetchJson } from "src/app/utils/fetchUtil"; +import { BASE_API_URL } from "src/config/api"; export interface TaskGroup { id: number; diff --git a/src/app/api/warehouse/index.ts b/src/app/api/warehouse/index.ts new file mode 100644 index 0000000..46c33bc --- /dev/null +++ b/src/app/api/warehouse/index.ts @@ -0,0 +1,17 @@ +import { cache } from "react"; +import "server-only"; +import { serverFetchJson } from "@/app/utils/fetchUtil"; +import { BASE_API_URL } from "@/config/api"; + +export interface WarehouseResult { + id: number + code: string + name: string + description: string +} + +export const fetchWarehouseList = cache(async () => { + return serverFetchJson(`${BASE_API_URL}/warehouse`, { + next: { tags: ["warehouse"] }, + }); + }); \ No newline at end of file diff --git a/src/components/PoDetail/EscalationForm.tsx b/src/components/PoDetail/EscalationForm.tsx index 1312013..5162936 100644 --- a/src/components/PoDetail/EscalationForm.tsx +++ b/src/components/PoDetail/EscalationForm.tsx @@ -14,7 +14,7 @@ import { import { useFormContext } from "react-hook-form"; import { useTranslation } from "react-i18next"; import StyledDataGrid from "../StyledDataGrid"; -import { useCallback, useMemo } from "react"; +import { useCallback, useEffect, useMemo } from "react"; import { GridColDef, GridRowIdGetter, @@ -31,6 +31,7 @@ import QcSelect from "./QcSelect"; import { QcItemWithChecks } from "@/app/api/qc"; import { GridEditInputCell } from "@mui/x-data-grid"; import { StockInLine } from "@/app/api/po"; +import { stockInLineStatusMap } from "@/app/utils/formatUtil"; interface Props { itemDetail: StockInLine; @@ -144,6 +145,12 @@ const EscalationForm: React.FC = ({ }, [] ); + + useEffect(() => { + console.log("triggered") + // setValue("status", stockInLineStatusMap.determine1.key) + }, []) + return ( diff --git a/src/components/PoDetail/PoDetail.tsx b/src/components/PoDetail/PoDetail.tsx index 42f1286..37053c5 100644 --- a/src/components/PoDetail/PoDetail.tsx +++ b/src/components/PoDetail/PoDetail.tsx @@ -1,6 +1,11 @@ "use client"; -import { fetchPoWithStockInLines, PoResult, PurchaseOrderLine, StockInLine } from "@/app/api/po"; +import { + fetchPoWithStockInLines, + PoResult, + PurchaseOrderLine, + StockInLine, +} from "@/app/api/po"; import { Box, Button, @@ -35,10 +40,12 @@ import InputDataGrid, { import PoInputGrid from "./PoInputGrid"; import { QcItemWithChecks } from "@/app/api/qc"; import { useSearchParams } from "next/navigation"; +import { WarehouseResult } from "@/app/api/warehouse"; type Props = { po: PoResult; - qc: QcItemWithChecks[] + qc: QcItemWithChecks[]; + warehouse: WarehouseResult[]; }; type EntryError = @@ -48,20 +55,17 @@ type EntryError = | undefined; // type PolRow = TableRow, EntryError>; -const PoDetail: React.FC = ({ - po, - // poLine, - qc - }) => { +const PoDetail: React.FC = ({ po, qc, warehouse }) => { const { t } = useTranslation(); const apiRef = useGridApiRef(); const [rows, setRows] = useState(po.pol || []); - const params = useSearchParams() + const params = useSearchParams(); function Row(props: { row: PurchaseOrderLine }) { const { row } = props; const [open, setOpen] = useState(false); - + const [processedQty, setProcessedQty] = useState(row.processed); + return ( <> *": { borderBottom: "unset" }, color: "black" }}> @@ -77,30 +81,30 @@ const PoDetail: React.FC = ({ {row.itemNo} {row.itemName} {row.qty} + {processedQty} {row.uom} {row.price} {/* {row.expiryDate} */} {row.status} - + {/* */} + - {/* */} - - + + + + @@ -133,6 +137,7 @@ const PoDetail: React.FC = ({ {t("itemNo")} {t("itemName")} {t("qty")} + processed {t("uom")} {t("price")} {/* {t("expiryDate")} */} diff --git a/src/components/PoDetail/PoDetailWrapper.tsx b/src/components/PoDetail/PoDetailWrapper.tsx index 83cef35..d16a76b 100644 --- a/src/components/PoDetail/PoDetailWrapper.tsx +++ b/src/components/PoDetail/PoDetailWrapper.tsx @@ -8,6 +8,8 @@ import { fetchPoWithStockInLines, PoResult } from "@/app/api/po"; import PoDetailLoading from "./PoDetailLoading"; import PoDetail from "./PoDetail"; import { QcItemWithChecks } from "@/app/api/qc"; +import { fetchWarehouseList } from "@/app/api/warehouse"; +import { fetchQcItemCheck } from "@/app/api/qc/actions"; interface SubComponents { Loading: typeof PoDetailLoading; @@ -19,35 +21,22 @@ type Props = { const PoDetailWrapper: React.FC & SubComponents = async ({ id }) => { const [ - poWithStockInLine + poWithStockInLine, + warehouse, + qc, ] = await Promise.all([ - fetchPoWithStockInLines(id) + fetchPoWithStockInLines(id), + fetchWarehouseList(), + fetchQcItemCheck() ]) // const poWithStockInLine = await fetchPoWithStockInLines(id) console.log(poWithStockInLine) - const qc: QcItemWithChecks[] = [ // just qc - { - id: 1, - code: "code1", - name: "name1", - itemId: 1, - lowerLimit: 1, - upperLimit: 3, - description: 'desc', - }, - { - id: 2, - code: "code2", - name: "name2", - itemId: 1, - lowerLimit: 1, - upperLimit: 3, - description: 'desc', - }, - ] + console.log(warehouse) + console.log(qc) + - return ; + return ; }; PoDetailWrapper.Loading = PoDetailLoading; diff --git a/src/components/PoDetail/PoInputGrid.tsx b/src/components/PoDetail/PoInputGrid.tsx index dde5b0e..e39e8ce 100644 --- a/src/components/PoDetail/PoInputGrid.tsx +++ b/src/components/PoDetail/PoInputGrid.tsx @@ -37,7 +37,8 @@ import { createStockInLine, testFetch } from "@/app/api/po/actions"; import { useSearchParams } from "next/navigation"; import { stockInLineStatusMap } from "@/app/utils/formatUtil"; import PoQcStockInModal from "./PoQcStockInModal"; -import NotificationImportantIcon from '@mui/icons-material/NotificationImportant'; +import NotificationImportantIcon from "@mui/icons-material/NotificationImportant"; +import { WarehouseResult } from "@/app/api/warehouse"; interface ResultWithId { id: number; } @@ -45,8 +46,10 @@ interface ResultWithId { interface Props { qc: QcItemWithChecks[]; setRows: Dispatch>; + setProcessedQty: Dispatch>; itemDetail: PurchaseOrderLine; stockInLine: StockInLine[]; + warehouse: WarehouseResult[]; } export type StockInLineEntryError = { @@ -64,7 +67,11 @@ export type StockInLineRow = Partial< class ProcessRowUpdateError extends Error { public readonly row: StockInLineRow; public readonly errors: StockInLineEntryError | undefined; - constructor(row: StockInLineRow, message?: string, errors?: StockInLineEntryError) { + constructor( + row: StockInLineRow, + message?: string, + errors?: StockInLineEntryError + ) { super(message); this.row = row; this.errors = errors; @@ -73,7 +80,14 @@ class ProcessRowUpdateError extends Error { } } -function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { +function PoInputGrid({ + qc, + setRows, + setProcessedQty, + itemDetail, + stockInLine, + warehouse, +}: Props) { const { t } = useTranslation("home"); const apiRef = useGridApiRef(); const [rowModesModel, setRowModesModel] = useState({}); @@ -83,25 +97,32 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { ); console.log(stockInLine); const [entries, setEntries] = useState(stockInLine || []); - const [modalInfo, setModalInfo] = useState() + const [modalInfo, setModalInfo] = useState(); const [qcOpen, setQcOpen] = useState(false); const [escalOpen, setEscalOpen] = useState(false); const [stockInOpen, setStockInOpen] = useState(false); const [putAwayOpen, setPutAwayOpen] = useState(false); - const [type, setType] = useState<"qc" | "stockIn">("qc"); - const [defaultQty, setDefaultQty] = useState(() => { - const total = entries.reduce((acc, curr) => acc + (curr.acceptedQty || 0), 0); - return itemDetail.qty - total; + const [currQty, setCurrQty] = useState(() => { + const total = entries.reduce( + (acc, curr) => acc + (curr.acceptedQty || 0), + 0 + ); + return total; }); - const params = useSearchParams() - // const refetchData = useCallback(async () => { - // const id = parseInt(params.get("id")!!) - // const res = await testFetch(id) - // const pol = res.pol!! - // console.log(pol) - // setRows(pol); - // }, [params]) + useEffect(() => { + + }, []) + useEffect(() => { + const completedList = entries.filter( + (e) => e.status === stockInLineStatusMap.completed.key + ); + const processedQty = completedList.reduce( + (acc, curr) => acc + (curr.acceptedQty || 0), + 0 + ); + setProcessedQty(processedQty); + }, [entries]); const handleDelete = useCallback( (id: GridRowId) => () => { @@ -119,8 +140,8 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { // post stock in line console.log("delayed"); console.log(params); - const oldId = params.row.id - console.log(oldId) + const oldId = params.row.id; + console.log(oldId); const postData = { itemId: params.row.itemId, itemNo: params.row.itemNo, @@ -128,10 +149,12 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { purchaseOrderId: params.row.purchaseOrderId, purchaseOrderLineId: params.row.purchaseOrderLineId, acceptedQty: params.row.acceptedQty, - } - const res = await createStockInLine(postData) - console.log(res) - setEntries((prev) => prev.map((p) => p.id === oldId ? res.entity as StockInLine : p)) + }; + const res = await createStockInLine(postData); + console.log(res); + setEntries((prev) => + prev.map((p) => (p.id === oldId ? (res.entity as StockInLine) : p)) + ); // do post directly to test // openStartModal(); }, 200); @@ -144,7 +167,7 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { ...prev, [id]: { mode: GridRowModes.View }, })); - setModalInfo(params.row) + setModalInfo(params.row); setTimeout(() => { // open qc modal console.log("delayed"); @@ -153,31 +176,31 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { }, [] ); - const handleEscalation = useCallback( - (id: GridRowId, params: any) => () => { - setRowModesModel((prev) => ({ - ...prev, - [id]: { mode: GridRowModes.View }, - })); - setModalInfo(params.row) - setTimeout(() => { - // open qc modal - console.log("delayed"); - openEscalationModal() - }, 200); - }, - [] - ); + // const handleEscalation = useCallback( + // (id: GridRowId, params: any) => () => { + // setRowModesModel((prev) => ({ + // ...prev, + // [id]: { mode: GridRowModes.View }, + // })); + // setModalInfo(params.row); + // setTimeout(() => { + // // open qc modal + // console.log("delayed"); + // openEscalationModal(); + // }, 200); + // }, + // [] + // ); const handleStockIn = useCallback( (id: GridRowId, params: any) => () => { setRowModesModel((prev) => ({ ...prev, [id]: { mode: GridRowModes.View }, })); - setModalInfo(params.row) + setModalInfo(params.row); setTimeout(() => { // open stock in modal - openStockInModal() + openStockInModal(); // return the record with its status as pending // update layout console.log("delayed"); @@ -192,10 +215,10 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { ...prev, [id]: { mode: GridRowModes.View }, })); - setModalInfo(params.row) + setModalInfo(params.row); setTimeout(() => { // open stock in modal - openPutAwayModal() + openPutAwayModal(); // return the record with its status as pending // update layout console.log("delayed"); @@ -236,7 +259,7 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { () => [ { field: "itemNo", - flex: .8, + flex: 0.8, }, { field: "itemName", @@ -253,24 +276,24 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { { field: "status", flex: 0.5, - editable: true + // editable: true, }, { field: "actions", type: "actions", - headerName: "start | qc | escalate | stock in | putaway | delete", + headerName: "start | qc | stock in | putaway | delete", flex: 1.5, cellClassName: "actions", getActions: (params) => { console.log(params.row.status); - const status = params.row.status.toLowerCase() + const status = params.row.status.toLowerCase(); return [ } label="start" sx={{ color: "primary.main", - marginRight: 2 + marginRight: 2, }} disabled={!(stockInLineStatusMap[status].value === 0)} // set _isNew to false after posting @@ -284,35 +307,41 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { label="qc" sx={{ color: "primary.main", - marginRight: 2 + marginRight: 2, }} - disabled={stockInLineStatusMap[status].value <= 0 || stockInLineStatusMap[status].value >= 5} + disabled={ + stockInLineStatusMap[status].value <= 0 || + stockInLineStatusMap[status].value >= 5 + } // set _isNew to false after posting // or check status onClick={handleQC(params.row.id, params)} color="inherit" key="edit" />, - } - label="escalation" - sx={{ - color: "primary.main", - marginRight: 2 - }} - disabled={stockInLineStatusMap[status].value <= 0 || stockInLineStatusMap[status].value >= 5} - // set _isNew to false after posting - // or check status - onClick={handleEscalation(params.row.id, params)} - color="inherit" - key="edit" - />, + // } + // label="escalation" + // sx={{ + // color: "primary.main", + // marginRight: 2, + // }} + // disabled={ + // stockInLineStatusMap[status].value <= 0 || + // stockInLineStatusMap[status].value >= 5 + // } + // // set _isNew to false after posting + // // or check status + // onClick={handleEscalation(params.row.id, params)} + // color="inherit" + // key="edit" + // />, } label="stockin" sx={{ color: "primary.main", - marginRight: 2 + marginRight: 2, }} disabled={stockInLineStatusMap[status].value !== 6} // set _isNew to false after posting @@ -326,7 +355,7 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { label="putaway" sx={{ color: "primary.main", - marginRight: 2 + marginRight: 2, }} disabled={stockInLineStatusMap[status].value !== 7} // set _isNew to false after posting @@ -363,7 +392,7 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { purchaseOrderLineId: itemDetail.id, itemNo: itemDetail.itemNo, itemName: itemDetail.itemName, - acceptedQty: defaultQty, + acceptedQty: itemDetail.qty - currQty, // this bug status: "draft", }; setEntries((e) => [...e, newEntry]); @@ -374,7 +403,7 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { // fieldToFocus: "projectId", }, })); - }, [getRowId]); + }, [currQty, getRowId]); const validation = useCallback( ( newRow: GridRowModel @@ -382,16 +411,19 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { ): StockInLineEntryError | undefined => { const error: StockInLineEntryError = {}; console.log(newRow); - console.log(defaultQty); - if (newRow.acceptedQty && newRow.acceptedQty > defaultQty) { + console.log(currQty); + if (newRow.acceptedQty && newRow.acceptedQty > itemDetail.qty) { error["acceptedQty"] = "qty cannot be greater than remaining qty"; } return Object.keys(error).length > 0 ? error : undefined; }, - [defaultQty] + [currQty] ); const processRowUpdate = useCallback( - (newRow: GridRowModel, originalRow: GridRowModel) => { + ( + newRow: GridRowModel, + originalRow: GridRowModel + ) => { const errors = validation(newRow); // change to validation if (errors) { throw new ProcessRowUpdateError( @@ -409,8 +441,11 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { ); setEntries(newEntries); //update remaining qty - const total = newEntries.reduce((acc, curr) => acc + (curr.acceptedQty || 0), 0); - setDefaultQty(itemDetail.qty - total); + const total = newEntries.reduce( + (acc, curr) => acc + (curr.acceptedQty || 0), + 0 + ); + setCurrQty(total); return rowToSave; }, [getRowId, entries] @@ -426,17 +461,20 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { [apiRef] ); - useEffect(() => { - const total = entries.reduce((acc, curr) => acc + (curr.acceptedQty || 0), 0); - setDefaultQty(itemDetail.qty - total); - }, [entries]); + // useEffect(() => { + // const total = entries.reduce( + // (acc, curr) => acc + (curr.acceptedQty || 0), + // 0 + // ); + // setDefaultQty(itemDetail.qty - total); + // }, [entries]); const footer = (