diff --git a/src/app/api/inventory/actions.ts b/src/app/api/inventory/actions.ts index e69de29..860c4bb 100644 --- a/src/app/api/inventory/actions.ts +++ b/src/app/api/inventory/actions.ts @@ -0,0 +1,23 @@ +"use server"; +import { BASE_API_URL } from "@/config/api"; +// import { ServerFetchError, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil"; +import { revalidateTag } from "next/cache"; +import { cache } from "react"; +import { serverFetchJson } from "@/app/utils/fetchUtil"; +import { QcItemResult } from "../settings/qcItem"; +import { RecordsRes } from "../utils"; +// import { BASE_API_URL } from "@/config/api"; + +export interface LotLineInfo { + inventoryLotLineId: number, + lotNo: string, + remainingQty: number, + uom: string +} + + export const fetchLotDetail = cache(async (stockInLineId: number) => { + return serverFetchJson(`${BASE_API_URL}/inventoryLotLine/lot-detail/${stockInLineId}`, { + method: 'GET', + next: { tags: ["inventory"] }, + }); + }); diff --git a/src/app/api/pickOrder/actions.ts b/src/app/api/pickOrder/actions.ts index 888f994..592df57 100644 --- a/src/app/api/pickOrder/actions.ts +++ b/src/app/api/pickOrder/actions.ts @@ -9,6 +9,10 @@ import { RecordsRes } from "../utils"; import { ConsoPickOrderResult, PickOrderLineWithSuggestedLot, PickOrderResult, PreReleasePickOrderSummary } from "."; // import { BASE_API_URL } from "@/config/api"; +export interface ReleasePickOrderInputs { + consoCode: string + assignTo: number, +} export const consolidatePickOrder = async (ids: number[]) => { const pickOrder = await serverFetchJson(`${BASE_API_URL}/pickOrder/conso`, { @@ -61,7 +65,7 @@ export const consolidatePickOrder_revert = async (ids: number[]) => { } }); - export const fetchConsoPickOrderLineClient = cache(async (queryParams?: Record) => { + export const fetchPickOrderLineClient = cache(async (queryParams?: Record) => { if (queryParams) { const queryString = new URLSearchParams(queryParams).toString(); return serverFetchJson>(`${BASE_API_URL}/pickOrder/get-pickorder-line-byPage?${queryString}`, { @@ -77,8 +81,21 @@ export const consolidatePickOrder_revert = async (ids: number[]) => { }); export const fetchConsoDetail = cache(async (consoCode: string) => { - return serverFetchJson(`${BASE_API_URL}/pickOrder/releaseConso/${consoCode}`, { + return serverFetchJson(`${BASE_API_URL}/pickOrder/pre-release-info/${consoCode}`, { method: 'GET', next: { tags: ["pickorder"] }, }); - }); \ No newline at end of file + }); + + + export const releasePickOrder = async (data: ReleasePickOrderInputs) => { + console.log(data) + console.log(JSON.stringify(data)) + const po = await serverFetchJson(`${BASE_API_URL}/pickOrder/releaseConso`, { + method: "POST", + body: JSON.stringify(data), + headers: { "Content-Type": "application/json" }, + }); + revalidateTag("pickorder"); + return po + } \ No newline at end of file diff --git a/src/app/api/pickOrder/index.ts b/src/app/api/pickOrder/index.ts index 29b3fc2..285b606 100644 --- a/src/app/api/pickOrder/index.ts +++ b/src/app/api/pickOrder/index.ts @@ -66,9 +66,12 @@ export interface PreReleasePickOrderSummary { } export interface PickOrderLineWithSuggestedLot { + id: number, itemName: string, qty: number, + uom: string status: string + warehouse: string suggestedLotNo: string } diff --git a/src/app/api/user/actions.ts b/src/app/api/user/actions.ts index 759bef5..1b52b2a 100644 --- a/src/app/api/user/actions.ts +++ b/src/app/api/user/actions.ts @@ -20,12 +20,23 @@ export interface PasswordInputs { newPasswordCheck: string; } +export interface NameList { + id: number + name: string +} + export const fetchUserDetails = cache(async (id: number) => { return serverFetchJson(`${BASE_API_URL}/user/${id}`, { next: { tags: ["user"] }, }); }); +export const fetchNameList = cache(async () => { + return serverFetchJson(`${BASE_API_URL}/user/name-list`, { + next: { tags: ["user"] }, + }); + }); + export const editUser = async (id: number, data: UserInputs) => { const newUser = serverFetchWithNoContent(`${BASE_API_URL}/user/${id}`, { method: "PUT", diff --git a/src/app/utils/formatUtil.ts b/src/app/utils/formatUtil.ts index 206f0e3..7d2fb40 100644 --- a/src/app/utils/formatUtil.ts +++ b/src/app/utils/formatUtil.ts @@ -67,6 +67,13 @@ export const stockInLineStatusMap: { [status: string]: number } = { "rejected": 9, }; +export const pickOrderStatusMap: { [status: string]: number } = { + "pending": 1, + "consolidated": 2, + "released": 3, + "completed": 4, +}; + export const calculateWeight = (qty: number, uom: Uom) => { return qty * (uom.unit2Qty || 1) * (uom.unit3Qty || 1) * (uom.unit4Qty || 1); } diff --git a/src/components/PickOrderDetail/PickOrderDetail.tsx b/src/components/PickOrderDetail/PickOrderDetail.tsx index beec513..fa077b5 100644 --- a/src/components/PickOrderDetail/PickOrderDetail.tsx +++ b/src/components/PickOrderDetail/PickOrderDetail.tsx @@ -19,9 +19,13 @@ import { PlayArrow } from "@mui/icons-material"; import DoneIcon from "@mui/icons-material/Done"; import { GridRowSelectionModel } from "@mui/x-data-grid"; import { useQcCodeScanner } from "../QrCodeScannerProvider/QrCodeScannerProvider"; -import { fetchConsoPickOrderLineClient } from "@/app/api/pickorder/actions"; +import { fetchPickOrderLineClient } from "@/app/api/pickorder/actions"; import { PickOrderLineWithSuggestedLot } from "@/app/api/pickorder"; import { Pageable } from "@/app/utils/fetchUtil"; +import { QrCodeInfo } from "@/app/api/qrcode"; +import { QrCode } from "../QrCode"; +import { fetchLotDetail, LotLineInfo } from "@/app/api/inventory/actions"; +import { GridRowModesModel } from "@mui/x-data-grid"; interface Props { consoCode: string; @@ -30,6 +34,7 @@ interface IsLoadingModel { pickOrderLineTable: boolean; stockOutLineTable: boolean; } + const PickOrderDetail: React.FC = ({ consoCode }) => { const { t } = useTranslation("pickOrder"); const [selectedRow, setSelectRow] = useState(); @@ -37,18 +42,23 @@ const PickOrderDetail: React.FC = ({ consoCode }) => { pickOrderLineTable: false, stockOutLineTable: false, }); - const [criteriaArgs, setCriteriaArgs] = useState({ + const [polCriteriaArgs, setPolCriteriaArgs] = useState({ + pageNum: 1, + pageSize: 10, + }); + const [solCriteriaArgs, setSolCriteriaArgs] = useState({ pageNum: 1, pageSize: 10, }); const [polTotalCount, setPolTotalCount] = useState(0); const [solTotalCount, setSolTotalCount] = useState(0); + const [rowModesModel, setRowModesModel] = useState({}); - const [suggestedList, setSuggestedList] = useState< + const [pickOrderLine, setPickOrderLine] = useState< PickOrderLineWithSuggestedLot[] >([]); - const sugggestedLotColumn = useMemo( + const pickOrderLineColumns = useMemo( () => [ { field: "id", @@ -70,16 +80,21 @@ const PickOrderDetail: React.FC = ({ consoCode }) => { headerName: "uom", flex: 1, }, + { + field: "warehouse", + headerName: "location", + flex: 1, + }, { field: "suggestedLotNo", headerName: "suggestedLotNo", - flex: 1, + flex: 1.2, }, ], [] ); - const [actualList, setActualList] = useState([]); - const actualLotColumn = useMemo( + const [stockOutLine, setStockOutLine] = useState([]); + const stockOutLineColumns = useMemo( () => [ { field: "code", @@ -94,82 +109,43 @@ const PickOrderDetail: React.FC = ({ consoCode }) => { const handleCompletePickOrder = useCallback(async () => {}, []); - const fetchSuggestedLotList = useCallback( - async (consoCode: string) => {}, - [] - ); - useEffect(() => { console.log(selectedRow); }, [selectedRow]); - const buttonData = useMemo(() => { - switch ("purchaseOrder.status".toLowerCase()) { - case "pending": - return { - buttonName: "start", - title: t("Do you want to start?"), - confirmButtonText: t("Start"), - successTitle: t("Start Success"), - errorTitle: t("Start Fail"), - buttonText: t("Start PO"), - buttonIcon: , - buttonColor: "success", - disabled: false, - onClick: handleStartPickOrder, - }; - case "receiving": - return { - buttonName: "complete", - title: t("Do you want to complete?"), - confirmButtonText: t("Complete"), - successTitle: t("Complete Success"), - errorTitle: t("Complete Fail"), - buttonText: t("Complete PO"), - buttonIcon: , - buttonColor: "info", - disabled: false, - onClick: handleCompletePickOrder, - }; - default: - return { - buttonName: "complete", - title: t("Do you want to complete?"), - confirmButtonText: t("Complete"), - successTitle: t("Complete Success"), - errorTitle: t("Complete Fail"), - buttonText: t("Complete PO"), - buttonIcon: , - buttonColor: "info", - disabled: true, - }; - // break; - } - }, [handleStartPickOrder, handleCompletePickOrder]); + const buttonData = useMemo( + () => ({ + buttonName: "complete", + title: t("Do you want to complete?"), + confirmButtonText: t("Complete"), + successTitle: t("Complete Success"), + errorTitle: t("Complete Fail"), + buttonText: t("Complete PO"), + buttonIcon: , + buttonColor: "info", + disabled: true, + }), + [] + ); const [isOpenScanner, setOpenScanner] = useState(false); const onOpenScanner = useCallback(() => { - setOpenScanner(true); - }, []); - - const onCloseScanner = useCallback(() => { - setOpenScanner(false); + setOpenScanner((prev) => !prev); }, []); - const fetchConsoPickOrderLine = useCallback( + const fetchPickOrderLine = useCallback( async (params: Record) => { setIsLoadingModel((prev) => ({ ...prev, pickOrderLineTable: true, })); - - const res = await fetchConsoPickOrderLineClient({ + const res = await fetchPickOrderLineClient({ ...params, consoCode: consoCode, }); if (res) { console.log(res); - setSuggestedList(res.records); + setPickOrderLine(res.records); setPolTotalCount(res.total); } else { console.log("error"); @@ -180,12 +156,28 @@ const PickOrderDetail: React.FC = ({ consoCode }) => { pickOrderLineTable: false, })); }, - [fetchConsoPickOrderLineClient, consoCode] + [fetchPickOrderLineClient, consoCode] + ); + const fetchStockOutLine = useCallback( + async (params: Record) => {}, + [] ); useEffect(() => { - fetchConsoPickOrderLine(criteriaArgs); - }, [criteriaArgs]); + fetchPickOrderLine(polCriteriaArgs); + }, [polCriteriaArgs]); + + useEffect(() => { + fetchStockOutLine(solCriteriaArgs); + }, [solCriteriaArgs]); + + const getLotDetail = useCallback( + async (stockInLineId: number): Promise => { + const res = await fetchLotDetail(stockInLineId); + return res; + }, + [fetchLotDetail] + ); const scanner = useQcCodeScanner(); useEffect(() => { @@ -196,19 +188,24 @@ const PickOrderDetail: React.FC = ({ consoCode }) => { } }, [isOpenScanner]); - // useEffect(() => { - // if (scanner.values.length > 0 && !Boolean(itemDetail)) { - // console.log(scanner.values[0]); - // const data: QrCodeInfo = JSON.parse(scanner.values[0]); - // console.log(data); - // if (data.stockInLineId) { - // console.log("still got in"); - // console.log(data.stockInLineId); - // setStockInLineId(data.stockInLineId); - // } - // scanner.resetScan(); - // } - // }, [scanner.values]); + useEffect(() => { + if (scanner.values.length > 0) { + console.log(scanner.values[0]); + const data: QrCodeInfo = JSON.parse(scanner.values[0]); + console.log(data); + if (data.stockInLineId) { + console.log("still got in"); + console.log(data.stockInLineId); + // fetch + getLotDetail(data.stockInLineId).then((value) => {}); + } + scanner.resetScan(); + } + }, [scanner.values]); + + const homemade_Qrcode = { + stockInLineId: 156, + }; return ( <> @@ -221,7 +218,7 @@ const PickOrderDetail: React.FC = ({ consoCode }) => { + + {/* homemade qrcode for testing purpose */} + {/* + + */} {/* - + */} {isLoadingModel.pickOrderLineTable ? ( ) : ( { setSelectRow(newRowSelectionModel); }} - pageSizeOptions={[2, 10, 25, 50, 100]} + initialState={{ + pagination: { + paginationModel: { pageSize: 10, page: 0 }, + }, + }} + pageSizeOptions={[10, 25, 50, 100]} onPaginationModelChange={async (model, details) => { - setCriteriaArgs({ + setPolCriteriaArgs({ pageNum: model.page + 1, pageSize: model.pageSize, }); @@ -266,7 +281,30 @@ const PickOrderDetail: React.FC = ({ consoCode }) => { )} - + { + setSolCriteriaArgs({ + pageNum: model.page + 1, + pageSize: model.pageSize, + }); + }} + rowCount={solTotalCount} + /> diff --git a/src/components/PickOrderSearch/ConsolidatedPickOrders.tsx b/src/components/PickOrderSearch/ConsolidatedPickOrders.tsx index 1dd6445..304c9fc 100644 --- a/src/components/PickOrderSearch/ConsolidatedPickOrders.tsx +++ b/src/components/PickOrderSearch/ConsolidatedPickOrders.tsx @@ -1,10 +1,13 @@ import { + Autocomplete, Box, Button, CircularProgress, + FormControl, Grid, Modal, ModalProps, + TextField, Typography, } from "@mui/material"; import { GridToolbarContainer } from "@mui/x-data-grid"; @@ -21,7 +24,12 @@ import SearchResults, { Column, defaultPagingController, } from "../SearchResults/SearchResults"; -import { ByItemsSummary, ConsoPickOrderResult, PickOrderLine, PickOrderResult } from "@/app/api/pickorder"; +import { + ByItemsSummary, + ConsoPickOrderResult, + PickOrderLine, + PickOrderResult, +} from "@/app/api/pickorder"; import { useRouter, useSearchParams } from "next/navigation"; import ConsolidatePickOrderItemSum from "./ConsolidatePickOrderItemSum"; import ConsolidatePickOrderSum from "./ConsolidatePickOrderSum"; @@ -29,8 +37,19 @@ import { GridInputRowSelectionModel } from "@mui/x-data-grid"; import { fetchConsoDetail, fetchConsoPickOrderClient, + releasePickOrder, + ReleasePickOrderInputs, } from "@/app/api/pickorder/actions"; import { EditNote } from "@mui/icons-material"; +import { fetchNameList, NameList } from "@/app/api/user/actions"; +import { useField } from "@mui/x-date-pickers/internals"; +import { + FormProvider, + SubmitErrorHandler, + SubmitHandler, + useForm, +} from "react-hook-form"; +import { pickOrderStatusMap } from "@/app/utils/formatUtil"; interface Props { filterArgs: Record; @@ -47,6 +66,10 @@ const style = { pb: 10, width: 1500, }; +interface DisableButton { + releaseBtn: boolean; + removeBtn: boolean; +} const ConsolidatedPickOrders: React.FC = ({ filterArgs }) => { const { t } = useTranslation("pickOrder"); @@ -60,9 +83,18 @@ const ConsolidatedPickOrders: React.FC = ({ filterArgs }) => { const [consoCode, setConsoCode] = useState(); ///change back to undefined const [revertIds, setRevertIds] = useState([]); const [totalCount, setTotalCount] = useState(); + const [usernameList, setUsernameList] = useState([]); + + const [byPickOrderRows, setByPickOrderRows] = useState< + Omit[] | undefined + >(undefined); + const [byItemsRows, setByItemsRows] = useState( + undefined + ); + const [disableRelease, setDisableRelease] = useState(true); - const [byPickOrderRows, setByPickOrderRows] = useState[] | undefined>(undefined); - const [byItemsRows, setByItemsRows] = useState(undefined); + const formProps = useForm(); + const errors = formProps.formState.errors; const openDetailModal = useCallback((consoCode: string) => { setConsoCode(consoCode); @@ -77,9 +109,14 @@ const ConsolidatedPickOrders: React.FC = ({ filterArgs }) => { const onDetailClick = useCallback( (pickOrder: any) => { console.log(pickOrder); - openDetailModal(pickOrder.consoCode); + const status = pickOrder.status + if (pickOrderStatusMap[status] >= 2) { + router.push(`/pickorder/detail?consoCode=${pickOrder.consoCode}`); + } else { + openDetailModal(pickOrder.consoCode); + } }, - [openDetailModal] + [router, openDetailModal] ); const columns = useMemo[]>( () => [ @@ -93,6 +130,10 @@ const ConsolidatedPickOrders: React.FC = ({ filterArgs }) => { name: "consoCode", label: t("consoCode"), }, + { + name: "status", + label: t("status"), + }, ], [] ); @@ -127,6 +168,40 @@ const ConsolidatedPickOrders: React.FC = ({ filterArgs }) => { fetchNewPageConsoPickOrder(pagingController, filterArgs); }, [fetchNewPageConsoPickOrder, pagingController, filterArgs]); + const isReleasable = useCallback((itemList: ByItemsSummary[]): boolean => { + var isReleasable = true; + for (const item of itemList) { + isReleasable = item.requiredQty >= item.availableQty; + if (!isReleasable) return isReleasable; + } + return isReleasable; + }, []); + + const fetchConso = useCallback( + async (consoCode: string) => { + const res = await fetchConsoDetail(consoCode); + const nameListRes = await fetchNameList(); + if (res) { + console.log(res); + setByPickOrderRows(res.pickOrders); + // for testing + // for (const item of res.items) { + // item.availableQty = 1000; + // } + setByItemsRows(res.items); + setDisableRelease(isReleasable(res.items)); + } else { + console.log("error"); + console.log(res); + } + if (nameListRes) { + console.log(nameListRes); + setUsernameList(nameListRes); + } + }, + [isReleasable] + ); + const closeHandler = useCallback>( (...args) => { closeDetailModal(); @@ -135,32 +210,51 @@ const ConsolidatedPickOrders: React.FC = ({ filterArgs }) => { [closeDetailModal] ); - const handleRelease = useCallback(() => { - console.log("release"); - router.push(`/pickorder/detail?consoCode=${consoCode}`); - }, [router, consoCode]); + + const onChange = useCallback( + ( + event: React.SyntheticEvent, + newValue: NameList + ) => { + console.log(newValue); + formProps.setValue("assignTo", newValue.id); + }, + [] + ); + + const onSubmit = useCallback>( + async (data, event) => { + console.log(data); + try { + const res = await releasePickOrder(data) + console.log(res) + if (res.status = 200) { + router.push(`/pickorder/detail?consoCode=${data.consoCode}`); + } else { + throw Error("hv error") + } + } catch (error) { + console.log(error) + } + }, + [releasePickOrder] + ); + const onSubmitError = useCallback>( + (errors) => {}, + [] + ); const handleConsolidate_revert = useCallback(() => { console.log(revertIds); }, [revertIds]); - const fetchConso = useCallback(async (consoCode: string) => { - const res = await fetchConsoDetail(consoCode); - if (res) { - console.log(res); - setByPickOrderRows(res.pickOrders) - setByItemsRows(res.items) - } else { - console.log("error"); - console.log(res); - } - }, []); - useEffect(() => { if (consoCode) { fetchConso(consoCode); + formProps.setValue("consoCode", consoCode) } }, [consoCode]); + return ( <> = ({ filterArgs }) => { {consoCode != undefined ? ( - - - {consoCode} - - + + - - + + + {consoCode} + - - + + + option.name} + options={usernameList} + onChange={onChange} + renderInput={(params) => } + /> + - - - - - + + + - - + + ) : undefined}