| @@ -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<LotLineInfo>(`${BASE_API_URL}/inventoryLotLine/lot-detail/${stockInLineId}`, { | |||||
| method: 'GET', | |||||
| next: { tags: ["inventory"] }, | |||||
| }); | |||||
| }); | |||||
| @@ -9,6 +9,10 @@ import { RecordsRes } from "../utils"; | |||||
| import { ConsoPickOrderResult, PickOrderLineWithSuggestedLot, PickOrderResult, PreReleasePickOrderSummary } from "."; | import { ConsoPickOrderResult, PickOrderLineWithSuggestedLot, PickOrderResult, PreReleasePickOrderSummary } from "."; | ||||
| // import { BASE_API_URL } from "@/config/api"; | // import { BASE_API_URL } from "@/config/api"; | ||||
| export interface ReleasePickOrderInputs { | |||||
| consoCode: string | |||||
| assignTo: number, | |||||
| } | |||||
| export const consolidatePickOrder = async (ids: number[]) => { | export const consolidatePickOrder = async (ids: number[]) => { | ||||
| const pickOrder = await serverFetchJson<any>(`${BASE_API_URL}/pickOrder/conso`, { | const pickOrder = await serverFetchJson<any>(`${BASE_API_URL}/pickOrder/conso`, { | ||||
| @@ -61,7 +65,7 @@ export const consolidatePickOrder_revert = async (ids: number[]) => { | |||||
| } | } | ||||
| }); | }); | ||||
| export const fetchConsoPickOrderLineClient = cache(async (queryParams?: Record<string, any>) => { | |||||
| export const fetchPickOrderLineClient = cache(async (queryParams?: Record<string, any>) => { | |||||
| if (queryParams) { | if (queryParams) { | ||||
| const queryString = new URLSearchParams(queryParams).toString(); | const queryString = new URLSearchParams(queryParams).toString(); | ||||
| return serverFetchJson<RecordsRes<PickOrderLineWithSuggestedLot[]>>(`${BASE_API_URL}/pickOrder/get-pickorder-line-byPage?${queryString}`, { | return serverFetchJson<RecordsRes<PickOrderLineWithSuggestedLot[]>>(`${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) => { | export const fetchConsoDetail = cache(async (consoCode: string) => { | ||||
| return serverFetchJson<PreReleasePickOrderSummary>(`${BASE_API_URL}/pickOrder/releaseConso/${consoCode}`, { | |||||
| return serverFetchJson<PreReleasePickOrderSummary>(`${BASE_API_URL}/pickOrder/pre-release-info/${consoCode}`, { | |||||
| method: 'GET', | method: 'GET', | ||||
| next: { tags: ["pickorder"] }, | next: { tags: ["pickorder"] }, | ||||
| }); | }); | ||||
| }); | |||||
| }); | |||||
| export const releasePickOrder = async (data: ReleasePickOrderInputs) => { | |||||
| console.log(data) | |||||
| console.log(JSON.stringify(data)) | |||||
| const po = await serverFetchJson<any>(`${BASE_API_URL}/pickOrder/releaseConso`, { | |||||
| method: "POST", | |||||
| body: JSON.stringify(data), | |||||
| headers: { "Content-Type": "application/json" }, | |||||
| }); | |||||
| revalidateTag("pickorder"); | |||||
| return po | |||||
| } | |||||
| @@ -66,9 +66,12 @@ export interface PreReleasePickOrderSummary { | |||||
| } | } | ||||
| export interface PickOrderLineWithSuggestedLot { | export interface PickOrderLineWithSuggestedLot { | ||||
| id: number, | |||||
| itemName: string, | itemName: string, | ||||
| qty: number, | qty: number, | ||||
| uom: string | |||||
| status: string | status: string | ||||
| warehouse: string | |||||
| suggestedLotNo: string | suggestedLotNo: string | ||||
| } | } | ||||
| @@ -20,12 +20,23 @@ export interface PasswordInputs { | |||||
| newPasswordCheck: string; | newPasswordCheck: string; | ||||
| } | } | ||||
| export interface NameList { | |||||
| id: number | |||||
| name: string | |||||
| } | |||||
| export const fetchUserDetails = cache(async (id: number) => { | export const fetchUserDetails = cache(async (id: number) => { | ||||
| return serverFetchJson<UserDetail>(`${BASE_API_URL}/user/${id}`, { | return serverFetchJson<UserDetail>(`${BASE_API_URL}/user/${id}`, { | ||||
| next: { tags: ["user"] }, | next: { tags: ["user"] }, | ||||
| }); | }); | ||||
| }); | }); | ||||
| export const fetchNameList = cache(async () => { | |||||
| return serverFetchJson<NameList[]>(`${BASE_API_URL}/user/name-list`, { | |||||
| next: { tags: ["user"] }, | |||||
| }); | |||||
| }); | |||||
| export const editUser = async (id: number, data: UserInputs) => { | export const editUser = async (id: number, data: UserInputs) => { | ||||
| const newUser = serverFetchWithNoContent(`${BASE_API_URL}/user/${id}`, { | const newUser = serverFetchWithNoContent(`${BASE_API_URL}/user/${id}`, { | ||||
| method: "PUT", | method: "PUT", | ||||
| @@ -67,6 +67,13 @@ export const stockInLineStatusMap: { [status: string]: number } = { | |||||
| "rejected": 9, | "rejected": 9, | ||||
| }; | }; | ||||
| export const pickOrderStatusMap: { [status: string]: number } = { | |||||
| "pending": 1, | |||||
| "consolidated": 2, | |||||
| "released": 3, | |||||
| "completed": 4, | |||||
| }; | |||||
| export const calculateWeight = (qty: number, uom: Uom) => { | export const calculateWeight = (qty: number, uom: Uom) => { | ||||
| return qty * (uom.unit2Qty || 1) * (uom.unit3Qty || 1) * (uom.unit4Qty || 1); | return qty * (uom.unit2Qty || 1) * (uom.unit3Qty || 1) * (uom.unit4Qty || 1); | ||||
| } | } | ||||
| @@ -19,9 +19,13 @@ import { PlayArrow } from "@mui/icons-material"; | |||||
| import DoneIcon from "@mui/icons-material/Done"; | import DoneIcon from "@mui/icons-material/Done"; | ||||
| import { GridRowSelectionModel } from "@mui/x-data-grid"; | import { GridRowSelectionModel } from "@mui/x-data-grid"; | ||||
| import { useQcCodeScanner } from "../QrCodeScannerProvider/QrCodeScannerProvider"; | 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 { PickOrderLineWithSuggestedLot } from "@/app/api/pickorder"; | ||||
| import { Pageable } from "@/app/utils/fetchUtil"; | 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 { | interface Props { | ||||
| consoCode: string; | consoCode: string; | ||||
| @@ -30,6 +34,7 @@ interface IsLoadingModel { | |||||
| pickOrderLineTable: boolean; | pickOrderLineTable: boolean; | ||||
| stockOutLineTable: boolean; | stockOutLineTable: boolean; | ||||
| } | } | ||||
| const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | ||||
| const { t } = useTranslation("pickOrder"); | const { t } = useTranslation("pickOrder"); | ||||
| const [selectedRow, setSelectRow] = useState<GridRowSelectionModel>(); | const [selectedRow, setSelectRow] = useState<GridRowSelectionModel>(); | ||||
| @@ -37,18 +42,23 @@ const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | |||||
| pickOrderLineTable: false, | pickOrderLineTable: false, | ||||
| stockOutLineTable: false, | stockOutLineTable: false, | ||||
| }); | }); | ||||
| const [criteriaArgs, setCriteriaArgs] = useState<Pageable>({ | |||||
| const [polCriteriaArgs, setPolCriteriaArgs] = useState<Pageable>({ | |||||
| pageNum: 1, | |||||
| pageSize: 10, | |||||
| }); | |||||
| const [solCriteriaArgs, setSolCriteriaArgs] = useState<Pageable>({ | |||||
| pageNum: 1, | pageNum: 1, | ||||
| pageSize: 10, | pageSize: 10, | ||||
| }); | }); | ||||
| const [polTotalCount, setPolTotalCount] = useState(0); | const [polTotalCount, setPolTotalCount] = useState(0); | ||||
| const [solTotalCount, setSolTotalCount] = useState(0); | const [solTotalCount, setSolTotalCount] = useState(0); | ||||
| const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({}); | |||||
| const [suggestedList, setSuggestedList] = useState< | |||||
| const [pickOrderLine, setPickOrderLine] = useState< | |||||
| PickOrderLineWithSuggestedLot[] | PickOrderLineWithSuggestedLot[] | ||||
| >([]); | >([]); | ||||
| const sugggestedLotColumn = useMemo<GridColDef[]>( | |||||
| const pickOrderLineColumns = useMemo<GridColDef[]>( | |||||
| () => [ | () => [ | ||||
| { | { | ||||
| field: "id", | field: "id", | ||||
| @@ -70,16 +80,21 @@ const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | |||||
| headerName: "uom", | headerName: "uom", | ||||
| flex: 1, | flex: 1, | ||||
| }, | }, | ||||
| { | |||||
| field: "warehouse", | |||||
| headerName: "location", | |||||
| flex: 1, | |||||
| }, | |||||
| { | { | ||||
| field: "suggestedLotNo", | field: "suggestedLotNo", | ||||
| headerName: "suggestedLotNo", | headerName: "suggestedLotNo", | ||||
| flex: 1, | |||||
| flex: 1.2, | |||||
| }, | }, | ||||
| ], | ], | ||||
| [] | [] | ||||
| ); | ); | ||||
| const [actualList, setActualList] = useState([]); | |||||
| const actualLotColumn = useMemo<GridColDef[]>( | |||||
| const [stockOutLine, setStockOutLine] = useState([]); | |||||
| const stockOutLineColumns = useMemo<GridColDef[]>( | |||||
| () => [ | () => [ | ||||
| { | { | ||||
| field: "code", | field: "code", | ||||
| @@ -94,82 +109,43 @@ const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | |||||
| const handleCompletePickOrder = useCallback(async () => {}, []); | const handleCompletePickOrder = useCallback(async () => {}, []); | ||||
| const fetchSuggestedLotList = useCallback( | |||||
| async (consoCode: string) => {}, | |||||
| [] | |||||
| ); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| console.log(selectedRow); | console.log(selectedRow); | ||||
| }, [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: <PlayArrow />, | |||||
| 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: <DoneIcon />, | |||||
| 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: <DoneIcon />, | |||||
| 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: <DoneIcon />, | |||||
| buttonColor: "info", | |||||
| disabled: true, | |||||
| }), | |||||
| [] | |||||
| ); | |||||
| const [isOpenScanner, setOpenScanner] = useState(false); | const [isOpenScanner, setOpenScanner] = useState(false); | ||||
| const onOpenScanner = useCallback(() => { | const onOpenScanner = useCallback(() => { | ||||
| setOpenScanner(true); | |||||
| }, []); | |||||
| const onCloseScanner = useCallback(() => { | |||||
| setOpenScanner(false); | |||||
| setOpenScanner((prev) => !prev); | |||||
| }, []); | }, []); | ||||
| const fetchConsoPickOrderLine = useCallback( | |||||
| const fetchPickOrderLine = useCallback( | |||||
| async (params: Record<string, any>) => { | async (params: Record<string, any>) => { | ||||
| setIsLoadingModel((prev) => ({ | setIsLoadingModel((prev) => ({ | ||||
| ...prev, | ...prev, | ||||
| pickOrderLineTable: true, | pickOrderLineTable: true, | ||||
| })); | })); | ||||
| const res = await fetchConsoPickOrderLineClient({ | |||||
| const res = await fetchPickOrderLineClient({ | |||||
| ...params, | ...params, | ||||
| consoCode: consoCode, | consoCode: consoCode, | ||||
| }); | }); | ||||
| if (res) { | if (res) { | ||||
| console.log(res); | console.log(res); | ||||
| setSuggestedList(res.records); | |||||
| setPickOrderLine(res.records); | |||||
| setPolTotalCount(res.total); | setPolTotalCount(res.total); | ||||
| } else { | } else { | ||||
| console.log("error"); | console.log("error"); | ||||
| @@ -180,12 +156,28 @@ const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | |||||
| pickOrderLineTable: false, | pickOrderLineTable: false, | ||||
| })); | })); | ||||
| }, | }, | ||||
| [fetchConsoPickOrderLineClient, consoCode] | |||||
| [fetchPickOrderLineClient, consoCode] | |||||
| ); | |||||
| const fetchStockOutLine = useCallback( | |||||
| async (params: Record<string, any>) => {}, | |||||
| [] | |||||
| ); | ); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| fetchConsoPickOrderLine(criteriaArgs); | |||||
| }, [criteriaArgs]); | |||||
| fetchPickOrderLine(polCriteriaArgs); | |||||
| }, [polCriteriaArgs]); | |||||
| useEffect(() => { | |||||
| fetchStockOutLine(solCriteriaArgs); | |||||
| }, [solCriteriaArgs]); | |||||
| const getLotDetail = useCallback( | |||||
| async (stockInLineId: number): Promise<LotLineInfo> => { | |||||
| const res = await fetchLotDetail(stockInLineId); | |||||
| return res; | |||||
| }, | |||||
| [fetchLotDetail] | |||||
| ); | |||||
| const scanner = useQcCodeScanner(); | const scanner = useQcCodeScanner(); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| @@ -196,19 +188,24 @@ const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | |||||
| } | } | ||||
| }, [isOpenScanner]); | }, [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 ( | return ( | ||||
| <> | <> | ||||
| @@ -221,7 +218,7 @@ const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={8}> | <Grid item xs={8}> | ||||
| <Button | <Button | ||||
| onClick={buttonData.onClick} | |||||
| // onClick={buttonData.onClick} | |||||
| disabled={buttonData.disabled} | disabled={buttonData.disabled} | ||||
| color={buttonData.buttonColor as ButtonProps["color"]} | color={buttonData.buttonColor as ButtonProps["color"]} | ||||
| startIcon={buttonData.buttonIcon} | startIcon={buttonData.buttonIcon} | ||||
| @@ -236,27 +233,45 @@ const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | |||||
| justifyContent="end" | justifyContent="end" | ||||
| alignItems="end" | alignItems="end" | ||||
| > | > | ||||
| <Button onClick={onOpenScanner}>{t("bind")}</Button> | |||||
| <Button onClick={onOpenScanner}> | |||||
| {isOpenScanner ? t("binding") : t("bind")} | |||||
| </Button> | |||||
| </Grid> | </Grid> | ||||
| {/* homemade qrcode for testing purpose */} | |||||
| {/* <Grid | |||||
| item | |||||
| xs={12} | |||||
| style={{ display: "flex", justifyContent: "center" }} | |||||
| > | |||||
| <QrCode | |||||
| content={homemade_Qrcode} | |||||
| sx={{ width: 200, height: 200 }} | |||||
| /> | |||||
| </Grid> */} | |||||
| </Grid> | </Grid> | ||||
| <Grid container xs={12} justifyContent="space-between"> | <Grid container xs={12} justifyContent="space-between"> | ||||
| {/* <Grid item xs={12} sx={{ height: 400 }}> | {/* <Grid item xs={12} sx={{ height: 400 }}> | ||||
| <StyledDataGrid rows={suggestedList} columns={columns} /> | |||||
| <StyledDataGrid rows={pickOrderLine} columns={columns} /> | |||||
| </Grid> */} | </Grid> */} | ||||
| <Grid item xs={12} sx={{ height: 400 }}> | <Grid item xs={12} sx={{ height: 400 }}> | ||||
| {isLoadingModel.pickOrderLineTable ? ( | {isLoadingModel.pickOrderLineTable ? ( | ||||
| <CircularProgress size={40} /> | <CircularProgress size={40} /> | ||||
| ) : ( | ) : ( | ||||
| <StyledDataGrid | <StyledDataGrid | ||||
| rows={suggestedList} | |||||
| columns={sugggestedLotColumn} | |||||
| rows={pickOrderLine} | |||||
| columns={pickOrderLineColumns} | |||||
| rowSelectionModel={selectedRow} | rowSelectionModel={selectedRow} | ||||
| onRowSelectionModelChange={(newRowSelectionModel) => { | onRowSelectionModelChange={(newRowSelectionModel) => { | ||||
| setSelectRow(newRowSelectionModel); | setSelectRow(newRowSelectionModel); | ||||
| }} | }} | ||||
| pageSizeOptions={[2, 10, 25, 50, 100]} | |||||
| initialState={{ | |||||
| pagination: { | |||||
| paginationModel: { pageSize: 10, page: 0 }, | |||||
| }, | |||||
| }} | |||||
| pageSizeOptions={[10, 25, 50, 100]} | |||||
| onPaginationModelChange={async (model, details) => { | onPaginationModelChange={async (model, details) => { | ||||
| setCriteriaArgs({ | |||||
| setPolCriteriaArgs({ | |||||
| pageNum: model.page + 1, | pageNum: model.page + 1, | ||||
| pageSize: model.pageSize, | pageSize: model.pageSize, | ||||
| }); | }); | ||||
| @@ -266,7 +281,30 @@ const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | |||||
| )} | )} | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} sx={{ height: 400 }}> | <Grid item xs={12} sx={{ height: 400 }}> | ||||
| <StyledDataGrid rows={actualList} columns={actualLotColumn} /> | |||||
| <StyledDataGrid | |||||
| rows={stockOutLine} | |||||
| columns={stockOutLineColumns} | |||||
| rowModesModel={rowModesModel} | |||||
| onRowModesModelChange={setRowModesModel} | |||||
| disableColumnMenu | |||||
| editMode="row" | |||||
| // processRowUpdate={processRowUpdate} | |||||
| // onProcessRowUpdateError={onProcessRowUpdateError} | |||||
| initialState={{ | |||||
| pagination: { | |||||
| paginationModel: { pageSize: 10, page: 0 }, | |||||
| }, | |||||
| }} | |||||
| pageSizeOptions={[10, 25, 50, 100]} | |||||
| onPaginationModelChange={async (model, details) => { | |||||
| setSolCriteriaArgs({ | |||||
| pageNum: model.page + 1, | |||||
| pageSize: model.pageSize, | |||||
| }); | |||||
| }} | |||||
| rowCount={solTotalCount} | |||||
| /> | |||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| </Stack> | </Stack> | ||||
| @@ -1,10 +1,13 @@ | |||||
| import { | import { | ||||
| Autocomplete, | |||||
| Box, | Box, | ||||
| Button, | Button, | ||||
| CircularProgress, | CircularProgress, | ||||
| FormControl, | |||||
| Grid, | Grid, | ||||
| Modal, | Modal, | ||||
| ModalProps, | ModalProps, | ||||
| TextField, | |||||
| Typography, | Typography, | ||||
| } from "@mui/material"; | } from "@mui/material"; | ||||
| import { GridToolbarContainer } from "@mui/x-data-grid"; | import { GridToolbarContainer } from "@mui/x-data-grid"; | ||||
| @@ -21,7 +24,12 @@ import SearchResults, { | |||||
| Column, | Column, | ||||
| defaultPagingController, | defaultPagingController, | ||||
| } from "../SearchResults/SearchResults"; | } 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 { useRouter, useSearchParams } from "next/navigation"; | ||||
| import ConsolidatePickOrderItemSum from "./ConsolidatePickOrderItemSum"; | import ConsolidatePickOrderItemSum from "./ConsolidatePickOrderItemSum"; | ||||
| import ConsolidatePickOrderSum from "./ConsolidatePickOrderSum"; | import ConsolidatePickOrderSum from "./ConsolidatePickOrderSum"; | ||||
| @@ -29,8 +37,19 @@ import { GridInputRowSelectionModel } from "@mui/x-data-grid"; | |||||
| import { | import { | ||||
| fetchConsoDetail, | fetchConsoDetail, | ||||
| fetchConsoPickOrderClient, | fetchConsoPickOrderClient, | ||||
| releasePickOrder, | |||||
| ReleasePickOrderInputs, | |||||
| } from "@/app/api/pickorder/actions"; | } from "@/app/api/pickorder/actions"; | ||||
| import { EditNote } from "@mui/icons-material"; | 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 { | interface Props { | ||||
| filterArgs: Record<string, any>; | filterArgs: Record<string, any>; | ||||
| @@ -47,6 +66,10 @@ const style = { | |||||
| pb: 10, | pb: 10, | ||||
| width: 1500, | width: 1500, | ||||
| }; | }; | ||||
| interface DisableButton { | |||||
| releaseBtn: boolean; | |||||
| removeBtn: boolean; | |||||
| } | |||||
| const ConsolidatedPickOrders: React.FC<Props> = ({ filterArgs }) => { | const ConsolidatedPickOrders: React.FC<Props> = ({ filterArgs }) => { | ||||
| const { t } = useTranslation("pickOrder"); | const { t } = useTranslation("pickOrder"); | ||||
| @@ -60,9 +83,18 @@ const ConsolidatedPickOrders: React.FC<Props> = ({ filterArgs }) => { | |||||
| const [consoCode, setConsoCode] = useState<string | undefined>(); ///change back to undefined | const [consoCode, setConsoCode] = useState<string | undefined>(); ///change back to undefined | ||||
| const [revertIds, setRevertIds] = useState<GridInputRowSelectionModel>([]); | const [revertIds, setRevertIds] = useState<GridInputRowSelectionModel>([]); | ||||
| const [totalCount, setTotalCount] = useState<number>(); | const [totalCount, setTotalCount] = useState<number>(); | ||||
| const [usernameList, setUsernameList] = useState<NameList[]>([]); | |||||
| const [byPickOrderRows, setByPickOrderRows] = useState< | |||||
| Omit<PickOrderResult, "items">[] | undefined | |||||
| >(undefined); | |||||
| const [byItemsRows, setByItemsRows] = useState<ByItemsSummary[] | undefined>( | |||||
| undefined | |||||
| ); | |||||
| const [disableRelease, setDisableRelease] = useState<boolean>(true); | |||||
| const [byPickOrderRows, setByPickOrderRows] = useState<Omit<PickOrderResult, "items">[] | undefined>(undefined); | |||||
| const [byItemsRows, setByItemsRows] = useState<ByItemsSummary[] | undefined>(undefined); | |||||
| const formProps = useForm<ReleasePickOrderInputs>(); | |||||
| const errors = formProps.formState.errors; | |||||
| const openDetailModal = useCallback((consoCode: string) => { | const openDetailModal = useCallback((consoCode: string) => { | ||||
| setConsoCode(consoCode); | setConsoCode(consoCode); | ||||
| @@ -77,9 +109,14 @@ const ConsolidatedPickOrders: React.FC<Props> = ({ filterArgs }) => { | |||||
| const onDetailClick = useCallback( | const onDetailClick = useCallback( | ||||
| (pickOrder: any) => { | (pickOrder: any) => { | ||||
| console.log(pickOrder); | 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<Column<ConsoPickOrderResult>[]>( | const columns = useMemo<Column<ConsoPickOrderResult>[]>( | ||||
| () => [ | () => [ | ||||
| @@ -93,6 +130,10 @@ const ConsolidatedPickOrders: React.FC<Props> = ({ filterArgs }) => { | |||||
| name: "consoCode", | name: "consoCode", | ||||
| label: t("consoCode"), | label: t("consoCode"), | ||||
| }, | }, | ||||
| { | |||||
| name: "status", | |||||
| label: t("status"), | |||||
| }, | |||||
| ], | ], | ||||
| [] | [] | ||||
| ); | ); | ||||
| @@ -127,6 +168,40 @@ const ConsolidatedPickOrders: React.FC<Props> = ({ filterArgs }) => { | |||||
| fetchNewPageConsoPickOrder(pagingController, filterArgs); | fetchNewPageConsoPickOrder(pagingController, 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<NonNullable<ModalProps["onClose"]>>( | const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>( | ||||
| (...args) => { | (...args) => { | ||||
| closeDetailModal(); | closeDetailModal(); | ||||
| @@ -135,32 +210,51 @@ const ConsolidatedPickOrders: React.FC<Props> = ({ filterArgs }) => { | |||||
| [closeDetailModal] | [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<SubmitHandler<ReleasePickOrderInputs & {}>>( | |||||
| 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<SubmitErrorHandler<ReleasePickOrderInputs>>( | |||||
| (errors) => {}, | |||||
| [] | |||||
| ); | |||||
| const handleConsolidate_revert = useCallback(() => { | const handleConsolidate_revert = useCallback(() => { | ||||
| console.log(revertIds); | console.log(revertIds); | ||||
| }, [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(() => { | useEffect(() => { | ||||
| if (consoCode) { | if (consoCode) { | ||||
| fetchConso(consoCode); | fetchConso(consoCode); | ||||
| formProps.setValue("consoCode", consoCode) | |||||
| } | } | ||||
| }, [consoCode]); | }, [consoCode]); | ||||
| return ( | return ( | ||||
| <> | <> | ||||
| <Grid | <Grid | ||||
| @@ -186,58 +280,89 @@ const ConsolidatedPickOrders: React.FC<Props> = ({ filterArgs }) => { | |||||
| </Grid> | </Grid> | ||||
| {consoCode != undefined ? ( | {consoCode != undefined ? ( | ||||
| <Modal open={modalOpen} onClose={closeHandler}> | <Modal open={modalOpen} onClose={closeHandler}> | ||||
| <Box sx={{ ...style, maxHeight: 800 }}> | |||||
| <Typography mb={2} variant="h4"> | |||||
| {consoCode} | |||||
| </Typography> | |||||
| <Box sx={{ | |||||
| height: 400, | |||||
| overflowY: "auto" | |||||
| }}> | |||||
| <FormProvider {...formProps}> | |||||
| <Box | |||||
| sx={{ ...style, maxHeight: 800 }} | |||||
| component="form" | |||||
| onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)} | |||||
| > | |||||
| <Grid container> | <Grid container> | ||||
| <Grid item xs={12} sx={{ mt: 2 }}> | |||||
| <ConsolidatePickOrderSum | |||||
| rows={byPickOrderRows} | |||||
| setRows={setByPickOrderRows} | |||||
| consoCode={consoCode} | |||||
| revertIds={revertIds} | |||||
| setRevertIds={setRevertIds} | |||||
| /> | |||||
| <Grid item xs={8}> | |||||
| <Typography mb={2} variant="h4"> | |||||
| {consoCode} | |||||
| </Typography> | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12}> | |||||
| <ConsolidatePickOrderItemSum | |||||
| rows={byItemsRows} | |||||
| setRows={setByItemsRows} | |||||
| /> | |||||
| <Grid | |||||
| item | |||||
| xs={4} | |||||
| display="flex" | |||||
| justifyContent="end" | |||||
| alignItems="end" | |||||
| > | |||||
| <FormControl fullWidth> | |||||
| <Autocomplete | |||||
| disableClearable | |||||
| fullWidth | |||||
| getOptionLabel={(option) => option.name} | |||||
| options={usernameList} | |||||
| onChange={onChange} | |||||
| renderInput={(params) => <TextField {...params} />} | |||||
| /> | |||||
| </FormControl> | |||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| </Box> | |||||
| <Grid container> | |||||
| <Grid | |||||
| item | |||||
| xs={12} | |||||
| display="flex" | |||||
| justifyContent="end" | |||||
| alignItems="end" | |||||
| <Box | |||||
| sx={{ | |||||
| height: 400, | |||||
| overflowY: "auto", | |||||
| }} | |||||
| > | > | ||||
| <Button | |||||
| disabled={(revertIds as number[]).length < 1} | |||||
| variant="outlined" | |||||
| onClick={handleConsolidate_revert} | |||||
| sx={{ mr: 1 }} | |||||
| > | |||||
| {t("remove")} | |||||
| </Button> | |||||
| <Button | |||||
| // disabled={selectedRows.length < 1} | |||||
| variant="outlined" | |||||
| onClick={handleRelease} | |||||
| <Grid container> | |||||
| <Grid item xs={12} sx={{ mt: 2 }}> | |||||
| <ConsolidatePickOrderSum | |||||
| rows={byPickOrderRows} | |||||
| setRows={setByPickOrderRows} | |||||
| consoCode={consoCode} | |||||
| revertIds={revertIds} | |||||
| setRevertIds={setRevertIds} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={12}> | |||||
| <ConsolidatePickOrderItemSum | |||||
| rows={byItemsRows} | |||||
| setRows={setByItemsRows} | |||||
| /> | |||||
| </Grid> | |||||
| </Grid> | |||||
| </Box> | |||||
| <Grid container> | |||||
| <Grid | |||||
| item | |||||
| xs={12} | |||||
| display="flex" | |||||
| justifyContent="end" | |||||
| alignItems="end" | |||||
| > | > | ||||
| {t("release")} | |||||
| </Button> | |||||
| <Button | |||||
| disabled={(revertIds as number[]).length < 1} | |||||
| variant="outlined" | |||||
| onClick={handleConsolidate_revert} | |||||
| sx={{ mr: 1 }} | |||||
| > | |||||
| {t("remove")} | |||||
| </Button> | |||||
| <Button | |||||
| disabled={disableRelease} | |||||
| variant="outlined" | |||||
| // onClick={handleRelease} | |||||
| type="submit" | |||||
| > | |||||
| {t("release")} | |||||
| </Button> | |||||
| </Grid> | |||||
| </Grid> | </Grid> | ||||
| </Grid> | |||||
| </Box> | |||||
| </Box> | |||||
| </FormProvider> | |||||
| </Modal> | </Modal> | ||||
| ) : undefined} | ) : undefined} | ||||
| </> | </> | ||||