| @@ -6,14 +6,55 @@ import { cache } from "react"; | |||||
| import { serverFetchJson } from "@/app/utils/fetchUtil"; | import { serverFetchJson } from "@/app/utils/fetchUtil"; | ||||
| import { QcItemResult } from "../settings/qcItem"; | import { QcItemResult } from "../settings/qcItem"; | ||||
| import { RecordsRes } from "../utils"; | import { RecordsRes } from "../utils"; | ||||
| import { ConsoPickOrderResult, PickOrderLineWithSuggestedLot, PickOrderResult, PreReleasePickOrderSummary } from "."; | |||||
| import { ConsoPickOrderResult, PickOrderLineWithSuggestedLot, PickOrderResult, PreReleasePickOrderSummary, StockOutLine } from "."; | |||||
| import { PurchaseQcResult } from "../po/actions"; | |||||
| // import { BASE_API_URL } from "@/config/api"; | // import { BASE_API_URL } from "@/config/api"; | ||||
| export interface PostStockOutLiineResponse<T> { | |||||
| id: number | null; | |||||
| name: string; | |||||
| code: string; | |||||
| type?: string | |||||
| message: string | null; | |||||
| errorPosition: string | keyof T; | |||||
| entity: T | T[] | |||||
| } | |||||
| export interface ReleasePickOrderInputs { | export interface ReleasePickOrderInputs { | ||||
| consoCode: string | consoCode: string | ||||
| assignTo: number, | assignTo: number, | ||||
| } | } | ||||
| export interface CreateStockOutLine { | |||||
| consoCode: string, | |||||
| pickOrderLineId: number, | |||||
| inventoryLotLineId: number, | |||||
| qty: number, | |||||
| } | |||||
| export interface UpdateStockOutLine { | |||||
| id: number, | |||||
| // consoCode: String, | |||||
| itemId: number, | |||||
| qty: number, | |||||
| pickOrderLineId: number, | |||||
| inventoryLotLineId?: number, | |||||
| status: string, | |||||
| pickTime?: string, | |||||
| // pickerId: number? | |||||
| } | |||||
| export interface PickOrderQcInput { | |||||
| qty: number | |||||
| status: string | |||||
| qcResult: PurchaseQcResult[]; | |||||
| } | |||||
| export interface PickOrderApprovalInput { | |||||
| allowQty: number | |||||
| rejectQty: number | |||||
| status: string | |||||
| } | |||||
| 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`, { | ||||
| method: "POST", | method: "POST", | ||||
| @@ -80,6 +121,13 @@ export const consolidatePickOrder_revert = async (ids: number[]) => { | |||||
| } | } | ||||
| }); | }); | ||||
| export const fetchStockOutLineClient = cache(async (pickOrderLineId: number) => { | |||||
| return serverFetchJson<StockOutLine[]>(`${BASE_API_URL}/stockOutLine/getByPickOrderLineId/${pickOrderLineId}`, { | |||||
| method: 'GET', | |||||
| next: { tags: ["pickorder"] }, | |||||
| }); | |||||
| }); | |||||
| export const fetchConsoDetail = cache(async (consoCode: string) => { | export const fetchConsoDetail = cache(async (consoCode: string) => { | ||||
| return serverFetchJson<PreReleasePickOrderSummary>(`${BASE_API_URL}/pickOrder/pre-release-info/${consoCode}`, { | return serverFetchJson<PreReleasePickOrderSummary>(`${BASE_API_URL}/pickOrder/pre-release-info/${consoCode}`, { | ||||
| method: 'GET', | method: 'GET', | ||||
| @@ -98,4 +146,27 @@ export const consolidatePickOrder_revert = async (ids: number[]) => { | |||||
| }); | }); | ||||
| revalidateTag("pickorder"); | revalidateTag("pickorder"); | ||||
| return po | return po | ||||
| } | |||||
| export const createStockOutLine = async (data: CreateStockOutLine) => { | |||||
| console.log("triggering") | |||||
| const po = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(`${BASE_API_URL}/stockOutLine/create`, { | |||||
| method: "POST", | |||||
| body: JSON.stringify(data), | |||||
| headers: { "Content-Type": "application/json" }, | |||||
| }); | |||||
| revalidateTag("pickorder"); | |||||
| return po | |||||
| } | |||||
| export const updateStockOutLine = async (data: UpdateStockOutLine) => { | |||||
| console.log(data) | |||||
| const po = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>> | |||||
| (`${BASE_API_URL}/stockOutLine/update`, { | |||||
| method: "POST", | |||||
| body: JSON.stringify(data), | |||||
| headers: { "Content-Type": "application/json" }, | |||||
| }); | |||||
| revalidateTag("pickorder"); | |||||
| return po | |||||
| } | } | ||||
| @@ -8,7 +8,13 @@ import { serverFetchJson } from "../../utils/fetchUtil"; | |||||
| import { QcItemWithChecks } from "."; | import { QcItemWithChecks } from "."; | ||||
| export interface QcResult { | export interface QcResult { | ||||
| id: number | |||||
| qcItemId: number | |||||
| name: string | |||||
| code: string | |||||
| stockInLineId?: number | |||||
| stockOutLineId?: number | |||||
| failQty: number | |||||
| } | } | ||||
| export const fetchQcItemCheck = cache(async (itemId?: number) => { | export const fetchQcItemCheck = cache(async (itemId?: number) => { | ||||
| @@ -21,7 +27,13 @@ export const fetchQcItemCheck = cache(async (itemId?: number) => { | |||||
| export const fetchQcResult = cache(async (id: number) => { | export const fetchQcResult = cache(async (id: number) => { | ||||
| return serverFetchJson<any[]>(`${BASE_API_URL}/qcResult/${id}`, { | |||||
| return serverFetchJson<QcResult[]>(`${BASE_API_URL}/qcResult/${id}`, { | |||||
| next: { tags: ["qc"] }, | |||||
| }); | |||||
| }); | |||||
| export const fetchPickOrderQcResult = cache(async (id: number) => { | |||||
| return serverFetchJson<QcResult[]>(`${BASE_API_URL}/qcResult/pick-order/${id}`, { | |||||
| next: { tags: ["qc"] }, | next: { tags: ["qc"] }, | ||||
| }); | }); | ||||
| }); | }); | ||||
| @@ -1,6 +1,7 @@ | |||||
| import dayjs, { ConfigType, Dayjs } from "dayjs"; | import dayjs, { ConfigType, Dayjs } from "dayjs"; | ||||
| import { Uom } from "../api/settings/uom"; | import { Uom } from "../api/settings/uom"; | ||||
| import { ListIterateeCustom, every, isArray, isNaN, isNull, isUndefined, take } from "lodash"; | import { ListIterateeCustom, every, isArray, isNaN, isNull, isUndefined, take } from "lodash"; | ||||
| import { Box, BoxProps, } from "@mui/material"; | |||||
| export const manhourFormatter = new Intl.NumberFormat("en-HK", { | export const manhourFormatter = new Intl.NumberFormat("en-HK", { | ||||
| minimumFractionDigits: 2, | minimumFractionDigits: 2, | ||||
| @@ -74,8 +75,11 @@ export const stockInLineStatusMap: { [status: string]: number } = { | |||||
| export const stockOutLineStatusMap: { [status: string]: number } = { | export const stockOutLineStatusMap: { [status: string]: number } = { | ||||
| "draft": 0, | "draft": 0, | ||||
| "pending": 1, // waiting for qc | "pending": 1, // waiting for qc | ||||
| "determine1": 2, // waiting for qc | |||||
| "lot-change": 3, // waiting for qc | |||||
| // after qc = completed | // after qc = completed | ||||
| "completed": 2, | |||||
| "completed": 4, | |||||
| "rejected": 5, | |||||
| }; | }; | ||||
| export const pickOrderStatusMap: { [status: string]: number } = { | export const pickOrderStatusMap: { [status: string]: number } = { | ||||
| @@ -92,4 +96,3 @@ export const calculateWeight = (qty: number, uom: Uom) => { | |||||
| export const returnWeightUnit = (uom: Uom) => { | export const returnWeightUnit = (uom: Uom) => { | ||||
| return uom.unit4 || uom.unit3 || uom.unit2 || uom.unit1; | return uom.unit4 || uom.unit3 || uom.unit2 || uom.unit1; | ||||
| } | } | ||||
| @@ -0,0 +1,26 @@ | |||||
| "use client"; | |||||
| import { Box, Grid } from "@mui/material"; | |||||
| export function FitAllCell(list: (string | number)[]) { | |||||
| return ( | |||||
| <Box | |||||
| sx={{ | |||||
| display: "flex", | |||||
| flexDirection: "column", | |||||
| maxHeight: 100, | |||||
| overflowY: "scroll", | |||||
| scrollbarWidth: "none", // For Firefox | |||||
| "&::-webkit-scrollbar": { | |||||
| display: "none", // For Chrome, Safari, and Opera | |||||
| }, | |||||
| }} | |||||
| > | |||||
| {list.map((item, index) => ( | |||||
| <Grid sx={{ mt: 1 }} key={index}> | |||||
| {`${item}`} | |||||
| </Grid> | |||||
| ))} | |||||
| </Box> | |||||
| ); | |||||
| } | |||||
| @@ -160,7 +160,9 @@ const CreateItem: React.FC<Props> = ({ | |||||
| component="form" | component="form" | ||||
| onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)} | onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)} | ||||
| > | > | ||||
| <Grid> | |||||
| <Grid | |||||
| > | |||||
| <Typography mb={2} variant="h4"> | <Typography mb={2} variant="h4"> | ||||
| {t(`${mode} ${title}`)} | {t(`${mode} ${title}`)} | ||||
| </Typography> | </Typography> | ||||
| @@ -185,7 +187,6 @@ const CreateItem: React.FC<Props> = ({ | |||||
| variant="contained" | variant="contained" | ||||
| startIcon={<Check />} | startIcon={<Check />} | ||||
| type="submit" | type="submit" | ||||
| // disabled={submitDisabled} | |||||
| > | > | ||||
| {isEditMode ? t("Save") : t("Confirm")} | {isEditMode ? t("Save") : t("Confirm")} | ||||
| </Button> | </Button> | ||||
| @@ -0,0 +1,132 @@ | |||||
| "use client"; | |||||
| import { | |||||
| Box, | |||||
| Card, | |||||
| CardContent, | |||||
| Grid, | |||||
| Stack, | |||||
| TextField, | |||||
| Tooltip, | |||||
| Typography, | |||||
| } from "@mui/material"; | |||||
| import { useFormContext } from "react-hook-form"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import StyledDataGrid from "../StyledDataGrid"; | |||||
| import { useCallback, useEffect, useMemo } from "react"; | |||||
| import { | |||||
| GridColDef, | |||||
| GridRowIdGetter, | |||||
| GridRowModel, | |||||
| useGridApiContext, | |||||
| GridRenderCellParams, | |||||
| GridRenderEditCellParams, | |||||
| useGridApiRef, | |||||
| } from "@mui/x-data-grid"; | |||||
| import InputDataGrid from "../InputDataGrid"; | |||||
| import { TableRow } from "../InputDataGrid/InputDataGrid"; | |||||
| 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"; | |||||
| import { PickOrderApprovalInput } from "@/app/api/pickorder/actions"; | |||||
| import { StockOutLine } from "@/app/api/pickorder"; | |||||
| interface Props { | |||||
| // approvalDefaultValues: StockInLine; | |||||
| // qc: QcItemWithChecks[]; | |||||
| approvalDefaultValues: StockOutLine & PickOrderApprovalInput; | |||||
| disabled: boolean | |||||
| } | |||||
| const ApprovalContent: React.FC<Props> = ({ | |||||
| // qc, | |||||
| approvalDefaultValues, | |||||
| disabled | |||||
| }) => { | |||||
| const { t } = useTranslation("purchaseOrder"); | |||||
| const apiRef = useGridApiRef(); | |||||
| const { | |||||
| register, | |||||
| formState: { errors, defaultValues, touchedFields }, | |||||
| watch, | |||||
| control, | |||||
| setValue, | |||||
| getValues, | |||||
| reset, | |||||
| resetField, | |||||
| setError, | |||||
| clearErrors, | |||||
| } = useFormContext<PickOrderApprovalInput>(); | |||||
| console.log(approvalDefaultValues) | |||||
| // const status = "rejected" | |||||
| const totalQty = approvalDefaultValues.qty | |||||
| const allowQty = watch("allowQty"); | |||||
| const rejectQty = watch("rejectQty"); | |||||
| return ( | |||||
| <Grid container justifyContent="flex-start" alignItems="flex-start"> | |||||
| <Grid item xs={12}> | |||||
| <Typography variant="h6" display="block" marginBlockEnd={1}> | |||||
| {t(`Lot Change Approval`)} | |||||
| </Typography> | |||||
| </Grid> | |||||
| {/* <Grid item xs={12}> | |||||
| <Typography variant="h6" display="block" marginBlockEnd={1}> | |||||
| {t(`to be processed`)}: {approvalDefaultValues.rejectQty - rejectQty} | |||||
| </Typography> | |||||
| </Grid> */} | |||||
| <Grid | |||||
| container | |||||
| justifyContent="flex-start" | |||||
| alignItems="flex-start" | |||||
| spacing={2} | |||||
| sx={{ mt: 0.5 }} | |||||
| > | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("allowQty")} | |||||
| fullWidth | |||||
| {...register("allowQty", { | |||||
| required: "allowQty required!", | |||||
| min: 0, | |||||
| valueAsNumber: true, | |||||
| max: approvalDefaultValues.allowQty | |||||
| })} | |||||
| disabled={disabled} | |||||
| defaultValue={approvalDefaultValues.allowQty} | |||||
| error={Boolean(errors.allowQty)} | |||||
| helperText={errors.allowQty?.message} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("rejectQty")} | |||||
| fullWidth | |||||
| {...register("rejectQty", { | |||||
| required: "rejectQty required!", | |||||
| min: 0, | |||||
| valueAsNumber: true, | |||||
| max: approvalDefaultValues.rejectQty | |||||
| })} | |||||
| disabled={disabled} | |||||
| defaultValue={approvalDefaultValues.rejectQty} | |||||
| error={Boolean(errors.rejectQty)} | |||||
| helperText={errors.rejectQty?.message} | |||||
| /> | |||||
| </Grid> | |||||
| </Grid> | |||||
| <Grid | |||||
| container | |||||
| justifyContent="flex-start" | |||||
| alignItems="flex-start" | |||||
| spacing={2} | |||||
| sx={{ mt: 0.5 }} | |||||
| > | |||||
| </Grid> | |||||
| </Grid> | |||||
| ); | |||||
| }; | |||||
| export default ApprovalContent; | |||||
| @@ -0,0 +1,137 @@ | |||||
| import { QcItemWithChecks } from "@/app/api/qc"; | |||||
| import useUploadContext from "../UploadProvider/useUploadContext"; | |||||
| import { | |||||
| PickOrderApprovalInput, | |||||
| PickOrderQcInput, | |||||
| updateStockOutLine, | |||||
| UpdateStockOutLine, | |||||
| } from "@/app/api/pickorder/actions"; | |||||
| import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; | |||||
| import QcContent from "./QcContent"; | |||||
| import { Box, Button, Modal, ModalProps, Stack } from "@mui/material"; | |||||
| import { useCallback } from "react"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import { Check } from "@mui/icons-material"; | |||||
| import { StockOutLine } from "@/app/api/pickorder"; | |||||
| import dayjs from "dayjs"; | |||||
| import { INPUT_DATE_FORMAT, OUTPUT_TIME_FORMAT } from "@/app/utils/formatUtil"; | |||||
| import ApprovalContent from "./ApprovalContent"; | |||||
| const style = { | |||||
| position: "absolute", | |||||
| top: "50%", | |||||
| left: "50%", | |||||
| transform: "translate(-50%, -50%)", | |||||
| // overflow: "scroll", | |||||
| bgcolor: "background.paper", | |||||
| pt: 5, | |||||
| px: 5, | |||||
| pb: 10, | |||||
| display: "block", | |||||
| width: { xs: "60%", sm: "60%", md: "60%" }, | |||||
| }; | |||||
| interface Props extends Omit<ModalProps, "children"> { | |||||
| // qc: QcItemWithChecks[]; | |||||
| approvalDefaultValues: StockOutLine & PickOrderApprovalInput; | |||||
| disabled: boolean; | |||||
| } | |||||
| const ApprovalForm: React.FC<Props> = ({ | |||||
| // qc, | |||||
| approvalDefaultValues, | |||||
| disabled, | |||||
| open, | |||||
| onClose, | |||||
| }) => { | |||||
| const { setIsUploading } = useUploadContext(); | |||||
| const { t } = useTranslation("pickOrder"); | |||||
| const formProps = useForm<PickOrderApprovalInput>({ | |||||
| defaultValues: { | |||||
| allowQty: approvalDefaultValues.qty, | |||||
| rejectQty: 0, | |||||
| status: approvalDefaultValues.status, | |||||
| }, | |||||
| }); | |||||
| const errors = formProps.formState.errors; | |||||
| const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>( | |||||
| (...args) => { | |||||
| onClose?.(...args); | |||||
| // reset(); | |||||
| }, | |||||
| [onClose] | |||||
| ); | |||||
| const onSubmit = useCallback<SubmitHandler<PickOrderApprovalInput & {}>>( | |||||
| async (data, event) => { | |||||
| console.log(data); | |||||
| // checking later | |||||
| // post | |||||
| let hasError = false; | |||||
| if (data.allowQty + data.rejectQty != approvalDefaultValues.qty) { | |||||
| formProps.setError("allowQty", { | |||||
| message: "illegal qty", | |||||
| type: "required", | |||||
| }); | |||||
| formProps.setError("rejectQty", { | |||||
| message: "illegal qty", | |||||
| type: "required", | |||||
| }); | |||||
| } | |||||
| if (hasError) { | |||||
| return | |||||
| } | |||||
| const postData: UpdateStockOutLine = { | |||||
| id: approvalDefaultValues.id, | |||||
| itemId: approvalDefaultValues.itemId, | |||||
| pickOrderLineId: approvalDefaultValues.pickOrderLineId, | |||||
| qty: data.allowQty, //allow qty | |||||
| status: data.status, | |||||
| // pickTime: dayjs().format(`${INPUT_DATE_FORMAT} ${OUTPUT_TIME_FORMAT}`), | |||||
| }; | |||||
| console.log(postData); | |||||
| // return; | |||||
| const res = await updateStockOutLine(postData); | |||||
| if (res) { | |||||
| console.log(res); | |||||
| closeHandler({}, "backdropClick"); | |||||
| } else { | |||||
| console.log(res); | |||||
| console.log("bug la"); | |||||
| } | |||||
| }, | |||||
| [t] | |||||
| ); | |||||
| return ( | |||||
| <> | |||||
| <FormProvider {...formProps}> | |||||
| <Modal open={open} onClose={closeHandler}> | |||||
| <Box | |||||
| sx={style} | |||||
| component="form" | |||||
| onSubmit={formProps.handleSubmit(onSubmit)} | |||||
| > | |||||
| <ApprovalContent | |||||
| approvalDefaultValues={approvalDefaultValues} | |||||
| disabled={disabled} | |||||
| /> | |||||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | |||||
| {!disabled ? ( | |||||
| <Button | |||||
| name="submit" | |||||
| variant="contained" | |||||
| startIcon={<Check />} | |||||
| type="submit" | |||||
| > | |||||
| {t("submit")} | |||||
| </Button> | |||||
| ) : undefined} | |||||
| </Stack> | |||||
| </Box> | |||||
| </Modal> | |||||
| </FormProvider> | |||||
| </> | |||||
| ); | |||||
| }; | |||||
| export default ApprovalForm; | |||||
| @@ -1,6 +1,7 @@ | |||||
| "use client"; | "use client"; | ||||
| import { | import { | ||||
| Box, | |||||
| Button, | Button, | ||||
| ButtonProps, | ButtonProps, | ||||
| Card, | Card, | ||||
| @@ -9,25 +10,61 @@ import { | |||||
| CircularProgress, | CircularProgress, | ||||
| Grid, | Grid, | ||||
| Stack, | Stack, | ||||
| Tooltip, | |||||
| Typography, | Typography, | ||||
| } from "@mui/material"; | } from "@mui/material"; | ||||
| import PlayArrowIcon from "@mui/icons-material/PlayArrow"; | |||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
| import StyledDataGrid from "../StyledDataGrid"; | import StyledDataGrid from "../StyledDataGrid"; | ||||
| import { useCallback, useEffect, useMemo, useState } from "react"; | import { useCallback, useEffect, useMemo, useState } from "react"; | ||||
| import { GridColDef } from "@mui/x-data-grid"; | |||||
| import { | |||||
| GridColDef, | |||||
| GridRowId, | |||||
| GridRowIdGetter, | |||||
| GridRowModel, | |||||
| GridRowModes, | |||||
| useGridApiRef, | |||||
| GridRenderEditCellParams, | |||||
| GridEditInputCell, | |||||
| GridRowParams, | |||||
| } from "@mui/x-data-grid"; | |||||
| import { PlayArrow } from "@mui/icons-material"; | 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 { fetchPickOrderLineClient } from "@/app/api/pickorder/actions"; | |||||
| import { PickOrderLineWithSuggestedLot } from "@/app/api/pickorder"; | |||||
| import { | |||||
| CreateStockOutLine, | |||||
| createStockOutLine, | |||||
| fetchPickOrderLineClient, | |||||
| fetchStockOutLineClient, | |||||
| PickOrderApprovalInput, | |||||
| PickOrderQcInput, | |||||
| } from "@/app/api/pickorder/actions"; | |||||
| import { | |||||
| PickOrderLineWithSuggestedLot, | |||||
| StockOutLine, | |||||
| } from "@/app/api/pickorder"; | |||||
| import { Pageable } from "@/app/utils/fetchUtil"; | import { Pageable } from "@/app/utils/fetchUtil"; | ||||
| import { QrCodeInfo } from "@/app/api/qrcode"; | import { QrCodeInfo } from "@/app/api/qrcode"; | ||||
| import { QrCode } from "../QrCode"; | import { QrCode } from "../QrCode"; | ||||
| import { fetchLotDetail, LotLineInfo } from "@/app/api/inventory/actions"; | import { fetchLotDetail, LotLineInfo } from "@/app/api/inventory/actions"; | ||||
| import { GridRowModesModel } from "@mui/x-data-grid"; | import { GridRowModesModel } from "@mui/x-data-grid"; | ||||
| import { stockOutLineStatusMap } from "@/app/utils/formatUtil"; | |||||
| import { GridActionsCellItem } from "@mui/x-data-grid"; | |||||
| import DoDisturbIcon from "@mui/icons-material/DoDisturb"; | |||||
| import useUploadContext from "../UploadProvider/useUploadContext"; | |||||
| import { FitAllCell } from "@/app/utils/gridUtil"; | |||||
| import { QcItemWithChecks } from "@/app/api/qc"; | |||||
| import QcForm from "./QcForm"; | |||||
| import { fetchPickOrderQcResult, QcResult } from "@/app/api/qc/actions"; | |||||
| import ShoppingCartIcon from "@mui/icons-material/ShoppingCart"; | |||||
| import AutoFixNormalIcon from "@mui/icons-material/AutoFixNormal"; | |||||
| import ApprovalForm from "./ApprovalForm"; | |||||
| import InfoIcon from "@mui/icons-material/Info"; | |||||
| import VerifiedIcon from "@mui/icons-material/Verified"; | |||||
| interface Props { | interface Props { | ||||
| qc: QcItemWithChecks[]; | |||||
| consoCode: string; | consoCode: string; | ||||
| } | } | ||||
| interface IsLoadingModel { | interface IsLoadingModel { | ||||
| @@ -35,9 +72,50 @@ interface IsLoadingModel { | |||||
| stockOutLineTable: boolean; | stockOutLineTable: boolean; | ||||
| } | } | ||||
| const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | |||||
| export type StockOutLineEntryError = { | |||||
| [field in keyof StockOutLine]?: string; | |||||
| }; | |||||
| export type StockOutLineRow = Partial< | |||||
| StockOutLine & { | |||||
| id: number; | |||||
| isActive: boolean | undefined; | |||||
| _isNew: boolean; | |||||
| _error: StockOutLineEntryError; | |||||
| } | |||||
| >; | |||||
| class ProcessRowUpdateError extends Error { | |||||
| public readonly row: StockOutLineRow; | |||||
| public readonly errors: StockOutLineEntryError | undefined; | |||||
| constructor( | |||||
| row: StockOutLineRow, | |||||
| message?: string, | |||||
| errors?: StockOutLineEntryError | |||||
| ) { | |||||
| super(message); | |||||
| this.row = row; | |||||
| this.errors = errors; | |||||
| Object.setPrototypeOf(this, ProcessRowUpdateError.prototype); | |||||
| } | |||||
| } | |||||
| export type formDefaultValues = StockOutLine & | |||||
| (PickOrderQcInput | PickOrderApprovalInput); | |||||
| const PickOrderDetail: React.FC<Props> = ({ consoCode, qc }) => { | |||||
| const { t } = useTranslation("pickOrder"); | const { t } = useTranslation("pickOrder"); | ||||
| const [selectedRow, setSelectRow] = useState<GridRowSelectionModel>(); | |||||
| const apiRef = useGridApiRef(); | |||||
| const [qcResult, setQcResult] = useState([] as QcResult[]); | |||||
| const [selectedRow, setSelectedRow] = useState<GridRowSelectionModel>([]); | |||||
| const [currPol, setCurrPol] = useState<PickOrderLineWithSuggestedLot>(); | |||||
| const [isChangeLotSolId, setIsChangeLotSolId] = useState<number | undefined>( | |||||
| undefined | |||||
| ); | |||||
| const [formDefaultValues, setFormDefaultValues] = | |||||
| useState<formDefaultValues>(); | |||||
| const [isLoadingModel, setIsLoadingModel] = useState<IsLoadingModel>({ | const [isLoadingModel, setIsLoadingModel] = useState<IsLoadingModel>({ | ||||
| pickOrderLineTable: false, | pickOrderLineTable: false, | ||||
| stockOutLineTable: false, | stockOutLineTable: false, | ||||
| @@ -53,6 +131,8 @@ const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | |||||
| 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 [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({}); | ||||
| const [btnIsLoading, setBtnIsLoading] = useState(false); | |||||
| const { setIsUploading } = useUploadContext(); | |||||
| const [pickOrderLine, setPickOrderLine] = useState< | const [pickOrderLine, setPickOrderLine] = useState< | ||||
| PickOrderLineWithSuggestedLot[] | PickOrderLineWithSuggestedLot[] | ||||
| @@ -80,58 +160,36 @@ const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | |||||
| headerName: "uom", | headerName: "uom", | ||||
| flex: 1, | flex: 1, | ||||
| }, | }, | ||||
| { | |||||
| field: "lotLineId", | |||||
| headerName: "lotLineId", | |||||
| flex: 1, | |||||
| }, | |||||
| { | { | ||||
| field: "warehouse", | field: "warehouse", | ||||
| headerName: "location", | headerName: "location", | ||||
| flex: 1, | flex: 1, | ||||
| renderCell: (params) => { | |||||
| if (!params.row.warehouse) return <></>; | |||||
| const warehouseList = JSON.parse(params.row.warehouse) as string[]; | |||||
| return FitAllCell(warehouseList); | |||||
| }, | |||||
| }, | }, | ||||
| { | { | ||||
| field: "suggestedLotNo", | field: "suggestedLotNo", | ||||
| headerName: "suggestedLotNo", | headerName: "suggestedLotNo", | ||||
| flex: 1.2, | flex: 1.2, | ||||
| renderCell: (params) => { | |||||
| if (!params.row.suggestedLotNo) return <></>; | |||||
| const suggestedLotNoList = JSON.parse( | |||||
| params.row.suggestedLotNo | |||||
| ) as string[]; | |||||
| return FitAllCell(suggestedLotNoList); | |||||
| }, | |||||
| }, | }, | ||||
| ], | ], | ||||
| [] | [] | ||||
| ); | ); | ||||
| const [stockOutLine, setStockOutLine] = useState([]); | |||||
| const stockOutLineColumns = useMemo<GridColDef[]>( | |||||
| () => [ | |||||
| { | |||||
| field: "code", | |||||
| headerName: "actual lot (out line", | |||||
| flex: 1, | |||||
| }, | |||||
| ], | |||||
| [] | |||||
| ); | |||||
| const handleStartPickOrder = useCallback(async () => {}, []); | |||||
| const handleCompletePickOrder = useCallback(async () => {}, []); | |||||
| useEffect(() => { | |||||
| console.log(selectedRow); | |||||
| }, [selectedRow]); | |||||
| 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 onOpenScanner = useCallback(() => { | |||||
| setOpenScanner((prev) => !prev); | |||||
| }, []); | |||||
| const fetchPickOrderLine = useCallback( | const fetchPickOrderLine = useCallback( | ||||
| async (params: Record<string, any>) => { | async (params: Record<string, any>) => { | ||||
| @@ -158,27 +216,397 @@ const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | |||||
| }, | }, | ||||
| [fetchPickOrderLineClient, consoCode] | [fetchPickOrderLineClient, consoCode] | ||||
| ); | ); | ||||
| 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 [stockOutLine, setStockOutLine] = useState<StockOutLine[]>([]); | |||||
| const getRowId = useCallback<GridRowIdGetter<StockOutLineRow>>( | |||||
| (row) => row.id as number, | |||||
| [] | |||||
| ); | |||||
| const [qcOpen, setQcOpen] = useState(false); | |||||
| const [approvalOpen, setApprovalOpen] = useState(false); | |||||
| const closeQcModal = useCallback(() => { | |||||
| setQcOpen(false); | |||||
| }, []); | |||||
| const openQcModal = useCallback(() => { | |||||
| setQcOpen(true); | |||||
| }, []); | |||||
| const closeApprovalModal = useCallback(() => { | |||||
| setApprovalOpen(false); | |||||
| }, []); | |||||
| const openApprovalModal = useCallback(() => { | |||||
| setApprovalOpen(true); | |||||
| }, []); | |||||
| const triggerRefetch = useCallback(() => { | |||||
| setSelectedRow((prev) => prev); | |||||
| }, []); | |||||
| const handleDelete = useCallback( | |||||
| (id: GridRowId) => () => { | |||||
| setStockOutLine((prev) => prev.filter((e) => getRowId(e) !== id)); | |||||
| }, | |||||
| [getRowId] | |||||
| ); | |||||
| const handleStart = useCallback( | |||||
| (id: GridRowId, params: any) => () => { | |||||
| setBtnIsLoading(true); | |||||
| setRowModesModel((prev) => ({ | |||||
| ...prev, | |||||
| [id]: { mode: GridRowModes.View }, | |||||
| })); | |||||
| setTimeout(async () => { | |||||
| // post stock in line | |||||
| const oldId = params.row.id; | |||||
| console.log(params.row); | |||||
| // console.log(currPol); | |||||
| const postData = { | |||||
| consoCode: consoCode, | |||||
| pickOrderLineId: params.row.pickOrderLineId, | |||||
| inventoryLotLineId: params.row.inventoryLotLineId, | |||||
| qty: params.row.qty, | |||||
| } as CreateStockOutLine; | |||||
| console.log(postData); | |||||
| // return | |||||
| console.log("triggering"); | |||||
| const res = await createStockOutLine(postData); | |||||
| if (res) { | |||||
| console.log(res); | |||||
| setStockOutLine((prev) => | |||||
| prev.map((p) => (p.id === oldId ? (res.entity as StockOutLine) : p)) | |||||
| ); | |||||
| } | |||||
| setBtnIsLoading(false); | |||||
| // do post directly to test | |||||
| // openStartModal(); | |||||
| }, 500); | |||||
| }, | |||||
| [createStockOutLine] | |||||
| ); | |||||
| useEffect(() => { | |||||
| console.log(stockOutLine); | |||||
| }, [stockOutLine]); | |||||
| const handleApproval = useCallback( | |||||
| (id: GridRowId, params: any) => async () => { | |||||
| setBtnIsLoading(true); | |||||
| console.log(params.row.qty); | |||||
| console.log(params.row); | |||||
| setFormDefaultValues({ | |||||
| ...(params.row as StockOutLine), | |||||
| status: "lot-change", | |||||
| } as StockOutLine & PickOrderApprovalInput); | |||||
| setTimeout(() => { | |||||
| // open qc modal | |||||
| console.log("delayed"); | |||||
| openApprovalModal(); | |||||
| setBtnIsLoading(false); | |||||
| }, 200); | |||||
| }, | |||||
| [] | |||||
| ); | |||||
| const handleLotChange = useCallback( | |||||
| (id: GridRowId, params: GridRowParams<any>) => async () => { | |||||
| setOpenScanner((prev) => !prev); | |||||
| console.log(id); | |||||
| setIsChangeLotSolId((prev) => { | |||||
| if (prev != undefined) return undefined | |||||
| return id as number | |||||
| }); | |||||
| }, | |||||
| [] | |||||
| ); | |||||
| useEffect(() => { | |||||
| console.log(isChangeLotSolId) | |||||
| }, [isChangeLotSolId]) | |||||
| const handleComplete = useCallback( | |||||
| (id: GridRowId, params: any) => async () => { | |||||
| setBtnIsLoading(true); | |||||
| setRowModesModel((prev) => ({ | |||||
| ...prev, | |||||
| [id]: { mode: GridRowModes.View }, | |||||
| })); | |||||
| getQcResult(id as number).then((qcResult) => { | |||||
| setQcResult(qcResult); | |||||
| }); | |||||
| console.log(params.row.qty); | |||||
| console.log(params.row); | |||||
| setFormDefaultValues({ | |||||
| ...(params.row as StockOutLine), | |||||
| qty: params.row.qty, | |||||
| status: "completed", | |||||
| } as StockOutLine & PickOrderQcInput); | |||||
| setTimeout(() => { | |||||
| // open qc modal | |||||
| console.log("delayed"); | |||||
| openQcModal(); | |||||
| setBtnIsLoading(false); | |||||
| }, 200); | |||||
| }, | |||||
| [] | |||||
| ); | |||||
| const stockOutLineColumns = useMemo<GridColDef[]>( | |||||
| () => [ | |||||
| { | |||||
| field: "itemName", | |||||
| headerName: "item name", | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| field: "qty", | |||||
| headerName: "qty", | |||||
| editable: true, | |||||
| flex: 1, | |||||
| type: "number", | |||||
| // renderEditCell(params: GridRenderEditCellParams<StockOutLineRow>) { | |||||
| // const errorMessage = | |||||
| // params.row._error?.[params.field as keyof StockOutLineEntryError]; | |||||
| // const content = ( | |||||
| // <GridEditInputCell | |||||
| // {...params} | |||||
| // inputProps={{ min: 0 }} | |||||
| // /> | |||||
| // ); | |||||
| // return errorMessage ? ( | |||||
| // <Tooltip title={t(errorMessage)}> | |||||
| // <Box width="100%">{content}</Box> | |||||
| // </Tooltip> | |||||
| // ) : ( | |||||
| // content | |||||
| // ); | |||||
| // }, | |||||
| }, | |||||
| { | |||||
| field: "lotNo", | |||||
| headerName: "lotNo", | |||||
| flex: 1, | |||||
| }, | |||||
| { | |||||
| field: "status", | |||||
| headerName: t("status"), | |||||
| flex: 0.5, | |||||
| renderCell: (params) => { | |||||
| return t(`${params.row.status}`); | |||||
| }, | |||||
| }, | |||||
| { | |||||
| field: "actions", | |||||
| type: "actions", | |||||
| headerName: `${t("start")} | |||||
| | ${t("approval")} | |||||
| | ${t("lot change")} | |||||
| | ${t("checkout")} | |||||
| | ${t("delete")}`, | |||||
| flex: 1.5, | |||||
| cellClassName: "actions", | |||||
| getActions: (params) => { | |||||
| const status = params.row.status.toLowerCase(); | |||||
| return [ | |||||
| <GridActionsCellItem | |||||
| icon={<PlayArrowIcon />} | |||||
| label="start" | |||||
| sx={{ | |||||
| color: "primary.main", | |||||
| // marginRight: 1, | |||||
| }} | |||||
| disabled={!(stockOutLineStatusMap[status] === 0)} | |||||
| // set _isNew to false after posting | |||||
| // or check status | |||||
| onClick={handleStart(params.row.id, params)} | |||||
| color="inherit" | |||||
| key="edit" | |||||
| />, | |||||
| <GridActionsCellItem | |||||
| icon={<VerifiedIcon />} | |||||
| label="approval" | |||||
| sx={{ | |||||
| color: "primary.main", | |||||
| // marginRight: 1, | |||||
| }} | |||||
| disabled={stockOutLineStatusMap[status] !== 2} | |||||
| // set _isNew to false after posting | |||||
| // or check status | |||||
| onClick={handleApproval(params.row.id, params)} // start scanning for that row | |||||
| color="inherit" | |||||
| key="edit" | |||||
| />, | |||||
| <GridActionsCellItem | |||||
| icon={<AutoFixNormalIcon />} | |||||
| label="lot change" /// | |||||
| sx={{ | |||||
| color: "primary.main", | |||||
| // marginRight: 1, | |||||
| }} | |||||
| disabled={stockOutLineStatusMap[status] !== 3} | |||||
| // set _isNew to false after posting | |||||
| // or check status | |||||
| onClick={handleLotChange(params.row.id, params)} // start scanning for that row | |||||
| color="inherit" | |||||
| key="edit" | |||||
| />, | |||||
| <GridActionsCellItem | |||||
| icon={<ShoppingCartIcon />} | |||||
| label="qcAndPick" | |||||
| sx={{ | |||||
| color: "primary.main", | |||||
| // marginRight: 1, | |||||
| }} | |||||
| disabled={ | |||||
| !params.row.inventoryLotLineId || | |||||
| stockOutLineStatusMap[status] === 2 || | |||||
| stockOutLineStatusMap[status] === 5 | |||||
| } | |||||
| // set _isNew to false after posting | |||||
| // or check status | |||||
| onClick={handleComplete(params.row.id, params)} | |||||
| color="inherit" | |||||
| key="edit" | |||||
| />, | |||||
| <GridActionsCellItem | |||||
| icon={<DoDisturbIcon />} | |||||
| label="Delete" | |||||
| sx={{ | |||||
| color: "error.main", | |||||
| }} | |||||
| disabled={stockOutLineStatusMap[status] > 0} | |||||
| onClick={handleDelete(params.row.id)} | |||||
| />, | |||||
| <GridActionsCellItem | |||||
| icon={<InfoIcon />} | |||||
| label="debug button" | |||||
| sx={{ | |||||
| color: "error.main", | |||||
| }} | |||||
| onClick={() => console.log(params.row)} | |||||
| />, | |||||
| ]; | |||||
| }, | |||||
| }, | |||||
| ], | |||||
| [stockOutLineStatusMap, handleStart, handleDelete] | |||||
| ); | |||||
| const fetchStockOutLine = useCallback( | const fetchStockOutLine = useCallback( | ||||
| async (params: Record<string, any>) => {}, | |||||
| async (params: Record<string, any>, selectedRow: GridRowSelectionModel) => { | |||||
| const _selectedRow = selectedRow as number[]; | |||||
| console.log(params); | |||||
| console.log(_selectedRow); | |||||
| // fetch | |||||
| const res = await fetchStockOutLineClient(_selectedRow[0]); | |||||
| console.log(res); | |||||
| // set state | |||||
| setStockOutLine(res); | |||||
| }, | |||||
| [] | [] | ||||
| ); | ); | ||||
| const addRow = useCallback( | |||||
| (qrcode: LotLineInfo) => { | |||||
| const newEntry = { | |||||
| id: Date.now(), | |||||
| _isNew: true, | |||||
| itemId: qrcode.itemId, | |||||
| itemName: qrcode.itemName, | |||||
| itemNo: qrcode.itemNo, | |||||
| lotNo: qrcode.lotNo, | |||||
| inventoryLotLineId: qrcode.inventoryLotLineId, | |||||
| qty: 0, | |||||
| pickOrderLineId: selectedRow[0] as number, | |||||
| status: "draft", | |||||
| }; | |||||
| setStockOutLine((prev) => [...prev, newEntry]); | |||||
| setRowModesModel((model) => ({ | |||||
| ...model, | |||||
| [getRowId(newEntry)]: { | |||||
| mode: GridRowModes.Edit, | |||||
| }, | |||||
| })); | |||||
| }, | |||||
| [getRowId, selectedRow] | |||||
| ); | |||||
| // need modify this later | |||||
| const changeRow = useCallback( | |||||
| (id: number, qrcode: LotLineInfo) => { | |||||
| console.log(stockOutLine); | |||||
| console.log(stockOutLine.find((line) => line.id === id)); | |||||
| const rowToSave = { | |||||
| ...stockOutLine.find((line) => line.id === id), | |||||
| itemId: qrcode.itemId, | |||||
| itemName: qrcode.itemName, | |||||
| itemNo: qrcode.itemNo, | |||||
| lotNo: qrcode.lotNo, | |||||
| inventoryLotLineId: qrcode.inventoryLotLineId, | |||||
| }; | |||||
| console.log(rowToSave); | |||||
| const newEntries = stockOutLine.map((e) => | |||||
| getRowId(e) === id ? rowToSave : e | |||||
| ); | |||||
| setStockOutLine(newEntries as StockOutLine[]); | |||||
| }, | |||||
| [stockOutLine, getRowId] | |||||
| ); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| fetchPickOrderLine(polCriteriaArgs); | fetchPickOrderLine(polCriteriaArgs); | ||||
| }, [polCriteriaArgs]); | }, [polCriteriaArgs]); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| fetchStockOutLine(solCriteriaArgs); | |||||
| }, [solCriteriaArgs]); | |||||
| if (!qcOpen || !approvalOpen) { | |||||
| console.log("triggering") | |||||
| triggerRefetch(); | |||||
| } | |||||
| if (selectedRow.length > 0) fetchStockOutLine(solCriteriaArgs, selectedRow); | |||||
| }, [qcOpen, approvalOpen, solCriteriaArgs, selectedRow, triggerRefetch]); | |||||
| const getLotDetail = useCallback( | const getLotDetail = useCallback( | ||||
| async (stockInLineId: number): Promise<LotLineInfo> => { | async (stockInLineId: number): Promise<LotLineInfo> => { | ||||
| const res = await fetchLotDetail(stockInLineId); | const res = await fetchLotDetail(stockInLineId); | ||||
| console.log("res"); | |||||
| console.log(res); | |||||
| return res; | return res; | ||||
| }, | }, | ||||
| [fetchLotDetail] | [fetchLotDetail] | ||||
| ); | ); | ||||
| const getQcResult = useCallback( | |||||
| async (stockOutLineId: number): Promise<QcResult[]> => { | |||||
| const res = await fetchPickOrderQcResult(stockOutLineId); | |||||
| console.log("res"); | |||||
| console.log(res); | |||||
| return res; | |||||
| }, | |||||
| [fetchPickOrderQcResult] | |||||
| ); | |||||
| const [isOpenScanner, setOpenScanner] = useState(false); | |||||
| const onOpenScanner = useCallback(() => { | |||||
| setOpenScanner((prev) => !prev); | |||||
| }, []); | |||||
| const scanner = useQcCodeScanner(); | const scanner = useQcCodeScanner(); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| if (isOpenScanner && !scanner.isScanning) { | if (isOpenScanner && !scanner.isScanning) { | ||||
| @@ -188,24 +616,102 @@ const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | |||||
| } | } | ||||
| }, [isOpenScanner]); | }, [isOpenScanner]); | ||||
| const homemade_Qrcode = { | |||||
| stockInLineId: 156, // eggs | |||||
| // stockInLineId: 162, // chicken wings | |||||
| }; | |||||
| useEffect(() => { | useEffect(() => { | ||||
| if (scanner.values.length > 0) { | if (scanner.values.length > 0) { | ||||
| console.log(scanner.values[0]); | console.log(scanner.values[0]); | ||||
| const data: QrCodeInfo = JSON.parse(scanner.values[0]); | const data: QrCodeInfo = JSON.parse(scanner.values[0]); | ||||
| console.log(data); | console.log(data); | ||||
| if (data.stockInLineId) { | if (data.stockInLineId) { | ||||
| console.log("still got in"); | |||||
| console.log(data.stockInLineId); | |||||
| setIsUploading(true); | |||||
| // fetch | // fetch | ||||
| getLotDetail(data.stockInLineId).then((value) => {}); | |||||
| getLotDetail(data.stockInLineId).then((qrcode) => { | |||||
| // add row | |||||
| if (isChangeLotSolId) { | |||||
| changeRow(isChangeLotSolId, qrcode); | |||||
| } else { | |||||
| addRow(qrcode); | |||||
| } | |||||
| }); | |||||
| setIsUploading(false); | |||||
| } | } | ||||
| scanner.resetScan(); | scanner.resetScan(); | ||||
| } | } | ||||
| }, [scanner.values]); | |||||
| }, [ | |||||
| isChangeLotSolId, | |||||
| scanner.values, | |||||
| selectedRow, | |||||
| changeRow, | |||||
| addRow, | |||||
| getLotDetail, | |||||
| ]); | |||||
| const homemade_Qrcode = { | |||||
| stockInLineId: 156, | |||||
| }; | |||||
| const mannuallyAddRow = useCallback(() => { | |||||
| getLotDetail(homemade_Qrcode.stockInLineId).then((qrcode) => { | |||||
| addRow(qrcode); | |||||
| // scanner.resetScan(); | |||||
| }); | |||||
| }, [addRow, homemade_Qrcode]); | |||||
| const validation = useCallback( | |||||
| ( | |||||
| newRow: GridRowModel<StockOutLineRow> | |||||
| // rowModel: GridRowSelectionModel | |||||
| ): StockOutLineEntryError | undefined => { | |||||
| const error: StockOutLineEntryError = {}; | |||||
| const checkQty = currPol?.qty; | |||||
| console.log(newRow); | |||||
| if (!newRow.qty || newRow.qty <= 0) { | |||||
| error["qty"] = t("illegal qty"); | |||||
| } | |||||
| return Object.keys(error).length > 0 ? error : undefined; | |||||
| }, | |||||
| [currPol] | |||||
| ); | |||||
| const processRowUpdate = useCallback( | |||||
| ( | |||||
| newRow: GridRowModel<StockOutLineRow>, | |||||
| originalRow: GridRowModel<StockOutLineRow> | |||||
| ) => { | |||||
| const errors = validation(newRow); // change to validation | |||||
| console.log(newRow); | |||||
| if (errors) { | |||||
| throw new ProcessRowUpdateError( | |||||
| originalRow, | |||||
| "validation error", | |||||
| errors | |||||
| ); | |||||
| } | |||||
| const { _isNew, _error, ...updatedRow } = newRow; | |||||
| const rowToSave = { | |||||
| ...updatedRow, | |||||
| } satisfies StockOutLineRow; | |||||
| console.log(rowToSave); | |||||
| const newEntries = stockOutLine.map((e) => | |||||
| getRowId(e) === getRowId(originalRow) ? rowToSave : e | |||||
| ); | |||||
| console.log(newEntries); | |||||
| setStockOutLine(newEntries as StockOutLine[]); | |||||
| return rowToSave; | |||||
| }, | |||||
| [stockOutLine, validation] | |||||
| ); | |||||
| const onProcessRowUpdateError = useCallback( | |||||
| (updateError: ProcessRowUpdateError) => { | |||||
| const errors = updateError.errors; | |||||
| const oldRow = updateError.row; | |||||
| apiRef.current.updateRows([{ ...oldRow, _error: errors }]); | |||||
| }, | |||||
| [apiRef] | |||||
| ); | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| @@ -233,7 +739,13 @@ const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | |||||
| justifyContent="end" | justifyContent="end" | ||||
| alignItems="end" | alignItems="end" | ||||
| > | > | ||||
| <Button onClick={onOpenScanner}> | |||||
| <Button | |||||
| onClick={mannuallyAddRow} | |||||
| disabled={selectedRow.length === 0} | |||||
| > | |||||
| {isOpenScanner ? t("manual scanning") : t("manual scan")} | |||||
| </Button> | |||||
| <Button onClick={onOpenScanner} disabled={selectedRow.length === 0}> | |||||
| {isOpenScanner ? t("binding") : t("bind")} | {isOpenScanner ? t("binding") : t("bind")} | ||||
| </Button> | </Button> | ||||
| </Grid> | </Grid> | ||||
| @@ -254,7 +766,7 @@ const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | |||||
| <StyledDataGrid rows={pickOrderLine} 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 && pickOrderLine == undefined ? ( | |||||
| <CircularProgress size={40} /> | <CircularProgress size={40} /> | ||||
| ) : ( | ) : ( | ||||
| <StyledDataGrid | <StyledDataGrid | ||||
| @@ -262,7 +774,14 @@ const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | |||||
| columns={pickOrderLineColumns} | columns={pickOrderLineColumns} | ||||
| rowSelectionModel={selectedRow} | rowSelectionModel={selectedRow} | ||||
| onRowSelectionModelChange={(newRowSelectionModel) => { | onRowSelectionModelChange={(newRowSelectionModel) => { | ||||
| setSelectRow(newRowSelectionModel); | |||||
| setSelectedRow(newRowSelectionModel); | |||||
| if (newRowSelectionModel && newRowSelectionModel.length > 0) { | |||||
| const pol = pickOrderLine.find( | |||||
| (item) => item.id === newRowSelectionModel[0] | |||||
| ); | |||||
| console.log(pol); | |||||
| setCurrPol(pol); | |||||
| } | |||||
| }} | }} | ||||
| initialState={{ | initialState={{ | ||||
| pagination: { | pagination: { | ||||
| @@ -282,15 +801,22 @@ const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={12} sx={{ height: 400 }}> | <Grid item xs={12} sx={{ height: 400 }}> | ||||
| <StyledDataGrid | <StyledDataGrid | ||||
| apiRef={apiRef} | |||||
| rows={stockOutLine} | rows={stockOutLine} | ||||
| columns={stockOutLineColumns} | columns={stockOutLineColumns} | ||||
| rowModesModel={rowModesModel} | rowModesModel={rowModesModel} | ||||
| onRowModesModelChange={setRowModesModel} | onRowModesModelChange={setRowModesModel} | ||||
| disableColumnMenu | disableColumnMenu | ||||
| editMode="row" | editMode="row" | ||||
| // processRowUpdate={processRowUpdate} | |||||
| // onProcessRowUpdateError={onProcessRowUpdateError} | |||||
| processRowUpdate={processRowUpdate} | |||||
| onProcessRowUpdateError={onProcessRowUpdateError} | |||||
| isCellEditable={(params) => { | |||||
| const status = params.row.status.toLowerCase(); | |||||
| return ( | |||||
| stockOutLineStatusMap[status] === 0 || | |||||
| stockOutLineStatusMap[status] === 3 | |||||
| ); | |||||
| }} | |||||
| initialState={{ | initialState={{ | ||||
| pagination: { | pagination: { | ||||
| paginationModel: { pageSize: 10, page: 0 }, | paginationModel: { pageSize: 10, page: 0 }, | ||||
| @@ -308,6 +834,26 @@ const PickOrderDetail: React.FC<Props> = ({ consoCode }) => { | |||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| </Stack> | </Stack> | ||||
| {/* modals */} | |||||
| {qcOpen && formDefaultValues != undefined && ( | |||||
| <QcForm | |||||
| qcDefaultValues={formDefaultValues as StockOutLine & PickOrderQcInput} | |||||
| qc={qc} | |||||
| disabled={false} | |||||
| open={qcOpen} | |||||
| onClose={closeQcModal} | |||||
| /> | |||||
| )} | |||||
| {approvalOpen && formDefaultValues != undefined && ( | |||||
| <ApprovalForm | |||||
| approvalDefaultValues={ | |||||
| formDefaultValues as StockOutLine & PickOrderApprovalInput | |||||
| } | |||||
| disabled={false} | |||||
| open={approvalOpen} | |||||
| onClose={closeApprovalModal} | |||||
| /> | |||||
| )} | |||||
| </> | </> | ||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -20,14 +20,14 @@ type Props = { | |||||
| }; | }; | ||||
| const PoDetailWrapper: React.FC<Props> & SubComponents = async ({ consoCode }) => { | const PoDetailWrapper: React.FC<Props> & SubComponents = async ({ consoCode }) => { | ||||
| // const [poWithStockInLine, warehouse, qc] = await Promise.all([ | |||||
| // fetchPoWithStockInLines(id), | |||||
| // fetchWarehouseList(), | |||||
| // fetchQcItemCheck(), | |||||
| // ]); | |||||
| const [ | |||||
| qc | |||||
| ] = await Promise.all([ | |||||
| fetchQcItemCheck(), | |||||
| ]); | |||||
| // const poWithStockInLine = await fetchPoWithStockInLines(id) | // const poWithStockInLine = await fetchPoWithStockInLines(id) | ||||
| return <PickOrderDetail consoCode={consoCode}/>; | |||||
| return <PickOrderDetail consoCode={consoCode} qc={qc}/>; | |||||
| }; | }; | ||||
| PoDetailWrapper.Loading = PickOrderDetailLoading; | PoDetailWrapper.Loading = PickOrderDetailLoading; | ||||
| @@ -0,0 +1,247 @@ | |||||
| "use client"; | |||||
| import { PurchaseQcResult, PurchaseQCInput } from "@/app/api/po/actions"; | |||||
| import { | |||||
| Box, | |||||
| Card, | |||||
| CardContent, | |||||
| Grid, | |||||
| Stack, | |||||
| TextField, | |||||
| Tooltip, | |||||
| Typography, | |||||
| } from "@mui/material"; | |||||
| import { useFormContext } from "react-hook-form"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import StyledDataGrid from "../StyledDataGrid"; | |||||
| import { useCallback, useEffect, useMemo, useState } from "react"; | |||||
| import { | |||||
| GridColDef, | |||||
| GridRowIdGetter, | |||||
| GridRowModel, | |||||
| useGridApiContext, | |||||
| GridRenderCellParams, | |||||
| GridRenderEditCellParams, | |||||
| useGridApiRef, | |||||
| } from "@mui/x-data-grid"; | |||||
| import InputDataGrid from "../InputDataGrid"; | |||||
| import { TableRow } from "../InputDataGrid/InputDataGrid"; | |||||
| import { GridEditInputCell } from "@mui/x-data-grid"; | |||||
| import { StockInLine } from "@/app/api/po"; | |||||
| import { stockInLineStatusMap } from "@/app/utils/formatUtil"; | |||||
| import { fetchQcItemCheck, fetchQcResult } from "@/app/api/qc/actions"; | |||||
| import { QcItemWithChecks } from "@/app/api/qc"; | |||||
| import axios from "@/app/(main)/axios/axiosInstance"; | |||||
| import { NEXT_PUBLIC_API_URL } from "@/config/api"; | |||||
| import axiosInstance from "@/app/(main)/axios/axiosInstance"; | |||||
| import TwoLineCell from "../PoDetail/TwoLineCell"; | |||||
| import QcSelect from "../PoDetail/QcSelect"; | |||||
| import { PickOrderQcInput } from "@/app/api/pickorder/actions"; | |||||
| interface Props { | |||||
| qcDefaultValues: PickOrderQcInput | |||||
| qc: QcItemWithChecks[]; | |||||
| disabled: boolean | |||||
| } | |||||
| type EntryError = | |||||
| | { | |||||
| [field in keyof PurchaseQcResult]?: string; | |||||
| } | |||||
| | undefined; | |||||
| type PoQcRow = TableRow<Partial<PurchaseQcResult>, EntryError>; | |||||
| // fetchQcItemCheck | |||||
| const QcContent: React.FC<Props> = ({ qc, qcDefaultValues, disabled }) => { | |||||
| const { t } = useTranslation("purchaseOrder"); | |||||
| const apiRef = useGridApiRef(); | |||||
| const { | |||||
| register, | |||||
| formState: { errors, defaultValues, touchedFields }, | |||||
| watch, | |||||
| control, | |||||
| setValue, | |||||
| getValues, | |||||
| reset, | |||||
| resetField, | |||||
| setError, | |||||
| clearErrors, | |||||
| } = useFormContext<PickOrderQcInput>(); | |||||
| console.log(qcDefaultValues); | |||||
| console.log(defaultValues); | |||||
| //// validate form | |||||
| const accQty = watch("qty"); | |||||
| const validateForm = useCallback(() => { | |||||
| console.log(accQty); | |||||
| if (accQty > qcDefaultValues.qty) { | |||||
| setError("qty", { | |||||
| message: `${t("qty must not greater than")} ${qcDefaultValues.qty}`, | |||||
| type: "required", | |||||
| }); | |||||
| } | |||||
| if (accQty < 1) { | |||||
| setError("qty", { | |||||
| message: t("minimal value is 1"), | |||||
| type: "required", | |||||
| }); | |||||
| } | |||||
| if (isNaN(accQty)) { | |||||
| setError("qty", { | |||||
| message: t("value must be a number"), | |||||
| type: "required", | |||||
| }); | |||||
| } | |||||
| }, [accQty]); | |||||
| useEffect(() => { | |||||
| clearErrors(); | |||||
| validateForm(); | |||||
| }, [validateForm]); | |||||
| // const [recordQty, setRecordQty] = useState(0); | |||||
| const columns = useMemo<GridColDef[]>( | |||||
| () => [ | |||||
| { | |||||
| field: "qcItemId", | |||||
| headerName: t("qc Check"), | |||||
| flex: 1, | |||||
| editable: !disabled, | |||||
| valueFormatter(params) { | |||||
| const row = params.id ? params.api.getRow<PoQcRow>(params.id) : null; | |||||
| if (!row) { | |||||
| return null; | |||||
| } | |||||
| const Qc = qc.find((q) => q.id === row.qcItemId); | |||||
| return Qc ? `${Qc.code} - ${Qc.name}` : t("Please select QC"); | |||||
| }, | |||||
| renderCell(params: GridRenderCellParams<PoQcRow, number>) { | |||||
| console.log(params.value); | |||||
| return <TwoLineCell>{params.formattedValue}</TwoLineCell>; | |||||
| }, | |||||
| renderEditCell(params: GridRenderEditCellParams<PoQcRow, number>) { | |||||
| const errorMessage = | |||||
| params.row._error?.[params.field as keyof PurchaseQcResult]; | |||||
| console.log(errorMessage); | |||||
| const content = ( | |||||
| <QcSelect | |||||
| allQcs={qc} | |||||
| value={params.row.qcItemId} | |||||
| onQcSelect={async (qcItemId) => { | |||||
| await params.api.setEditCellValue({ | |||||
| id: params.id, | |||||
| field: "qcItemId", | |||||
| value: qcItemId, | |||||
| }); | |||||
| // await params.api.setEditCellValue({ | |||||
| // id: params.id, | |||||
| // field: "type", | |||||
| // value: "determine1", | |||||
| // }); | |||||
| }} | |||||
| /> | |||||
| ); | |||||
| return errorMessage ? ( | |||||
| <Tooltip title={errorMessage}> | |||||
| <Box width="100%">{content}</Box> | |||||
| </Tooltip> | |||||
| ) : ( | |||||
| content | |||||
| ); | |||||
| }, | |||||
| }, | |||||
| { | |||||
| field: "failQty", | |||||
| headerName: t("failQty"), | |||||
| flex: 1, | |||||
| editable: !disabled, | |||||
| type: "number", | |||||
| renderEditCell(params: GridRenderEditCellParams<PoQcRow>) { | |||||
| // const recordQty = params.row.qty | |||||
| // if (recordQty !== undefined) { | |||||
| // setUnrecordQty((prev) => prev - recordQty) | |||||
| // } | |||||
| const errorMessage = | |||||
| params.row._error?.[params.field as keyof PurchaseQcResult]; | |||||
| const content = <GridEditInputCell {...params} />; | |||||
| return errorMessage ? ( | |||||
| <Tooltip title={t(errorMessage)}> | |||||
| <Box width="100%">{content}</Box> | |||||
| </Tooltip> | |||||
| ) : ( | |||||
| content | |||||
| ); | |||||
| }, | |||||
| }, | |||||
| ], | |||||
| [qc] | |||||
| ); | |||||
| /// validate datagrid | |||||
| const validation = useCallback( | |||||
| (newRow: GridRowModel<PoQcRow>): EntryError => { | |||||
| const error: EntryError = {}; | |||||
| const { qcItemId, failQty } = newRow; | |||||
| if (!qcItemId || qcItemId <= 0) { | |||||
| error["qcItemId"] = t("select qc"); | |||||
| } | |||||
| if (!failQty || failQty <= 0) { | |||||
| error["failQty"] = t("enter a failQty"); | |||||
| } | |||||
| if (failQty && failQty > qcDefaultValues.qty) { | |||||
| error["failQty"] = t("qty too big"); | |||||
| } | |||||
| return Object.keys(error).length > 0 ? error : undefined; | |||||
| }, | |||||
| [] | |||||
| ); | |||||
| return ( | |||||
| <Grid container justifyContent="flex-start" alignItems="flex-start"> | |||||
| <Grid item xs={12}> | |||||
| <Typography variant="h6" display="block" marginBlockEnd={1}> | |||||
| {t("Qc Detail")} | |||||
| </Typography> | |||||
| </Grid> | |||||
| <Grid | |||||
| container | |||||
| justifyContent="flex-start" | |||||
| alignItems="flex-start" | |||||
| spacing={2} | |||||
| sx={{ mt: 0.5 }} | |||||
| > | |||||
| <Grid item xs={12} lg={12}> | |||||
| <TextField | |||||
| label={t("accepted Qty")} | |||||
| fullWidth | |||||
| // value={qcDefaultValues.qty} | |||||
| {...register("qty", { | |||||
| required: "qty required!", | |||||
| valueAsNumber: true, | |||||
| max: qcDefaultValues.qty, | |||||
| })} | |||||
| disabled={disabled} | |||||
| error={Boolean(errors.qty)} | |||||
| helperText={errors.qty?.message} | |||||
| /> | |||||
| </Grid> | |||||
| </Grid> | |||||
| <Grid | |||||
| container | |||||
| justifyContent="flex-start" | |||||
| alignItems="flex-start" | |||||
| spacing={2} | |||||
| sx={{ mt: 0.5 }} | |||||
| > | |||||
| <Grid item xs={12}> | |||||
| <InputDataGrid<PickOrderQcInput, PurchaseQcResult, EntryError> | |||||
| apiRef={apiRef} | |||||
| checkboxSelection={false} | |||||
| _formKey={"qcResult"} | |||||
| columns={columns} | |||||
| validateRow={validation} | |||||
| needAdd={!disabled} | |||||
| /> | |||||
| </Grid> | |||||
| </Grid> | |||||
| </Grid> | |||||
| ); | |||||
| }; | |||||
| export default QcContent; | |||||
| @@ -0,0 +1,119 @@ | |||||
| import { QcItemWithChecks } from "@/app/api/qc"; | |||||
| import useUploadContext from "../UploadProvider/useUploadContext"; | |||||
| import { PickOrderQcInput, updateStockOutLine, UpdateStockOutLine } from "@/app/api/pickorder/actions"; | |||||
| import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; | |||||
| import QcContent from "./QcContent"; | |||||
| import { Box, Button, Modal, ModalProps, Stack } from "@mui/material"; | |||||
| import { useCallback } from "react"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import { Check } from "@mui/icons-material"; | |||||
| import { StockOutLine } from "@/app/api/pickorder"; | |||||
| import dayjs from "dayjs"; | |||||
| import { INPUT_DATE_FORMAT, OUTPUT_TIME_FORMAT } from "@/app/utils/formatUtil"; | |||||
| const style = { | |||||
| position: "absolute", | |||||
| top: "50%", | |||||
| left: "50%", | |||||
| transform: "translate(-50%, -50%)", | |||||
| // overflow: "scroll", | |||||
| bgcolor: "background.paper", | |||||
| pt: 5, | |||||
| px: 5, | |||||
| pb: 10, | |||||
| display: "block", | |||||
| width: { xs: "60%", sm: "60%", md: "60%" }, | |||||
| }; | |||||
| interface Props extends Omit<ModalProps, "children"> { | |||||
| qc: QcItemWithChecks[]; | |||||
| qcDefaultValues: StockOutLine & PickOrderQcInput; | |||||
| disabled: boolean; | |||||
| } | |||||
| const QcForm: React.FC<Props> = ({ | |||||
| qc, | |||||
| qcDefaultValues, | |||||
| disabled, | |||||
| open, | |||||
| onClose, | |||||
| }) => { | |||||
| const { setIsUploading } = useUploadContext(); | |||||
| const { t } = useTranslation("pickOrder"); | |||||
| const formProps = useForm<PickOrderQcInput>({ | |||||
| defaultValues: { | |||||
| qty: qcDefaultValues.qty, | |||||
| status: qcDefaultValues.status | |||||
| }, | |||||
| }); | |||||
| const errors = formProps.formState.errors; | |||||
| const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>( | |||||
| (...args) => { | |||||
| onClose?.(...args); | |||||
| // reset(); | |||||
| }, | |||||
| [onClose] | |||||
| ); | |||||
| const onSubmit = useCallback<SubmitHandler<PickOrderQcInput & {}>>( | |||||
| async (data, event) => { | |||||
| console.log(data); | |||||
| console.log(qcDefaultValues); | |||||
| // checking later | |||||
| // post | |||||
| const postData: UpdateStockOutLine = { | |||||
| id: qcDefaultValues.id, | |||||
| itemId: qcDefaultValues.itemId, | |||||
| pickOrderLineId: qcDefaultValues.pickOrderLineId, | |||||
| inventoryLotLineId: qcDefaultValues.inventoryLotLineId, | |||||
| qty: data.qty, | |||||
| status: data.status, | |||||
| // pickTime: dayjs().format(`${INPUT_DATE_FORMAT} ${OUTPUT_TIME_FORMAT}`), | |||||
| } | |||||
| console.log(postData) | |||||
| // return | |||||
| const res = await updateStockOutLine(postData) | |||||
| if (res) { | |||||
| console.log(res) | |||||
| closeHandler({}, "backdropClick"); | |||||
| } else { | |||||
| console.log(res) | |||||
| console.log("bug la") | |||||
| } | |||||
| }, | |||||
| [updateStockOutLine] | |||||
| ); | |||||
| return ( | |||||
| <> | |||||
| <FormProvider {...formProps}> | |||||
| <Modal open={open} onClose={closeHandler}> | |||||
| <Box | |||||
| sx={style} | |||||
| component="form" | |||||
| onSubmit={formProps.handleSubmit(onSubmit)} | |||||
| > | |||||
| <QcContent | |||||
| qc={qc} | |||||
| qcDefaultValues={qcDefaultValues} | |||||
| disabled={disabled} | |||||
| /> | |||||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | |||||
| {!disabled ? ( | |||||
| <Button | |||||
| name="submit" | |||||
| variant="contained" | |||||
| startIcon={<Check />} | |||||
| type="submit" | |||||
| > | |||||
| {t("submit")} | |||||
| </Button> | |||||
| ) : undefined} | |||||
| </Stack> | |||||
| </Box> | |||||
| </Modal> | |||||
| </FormProvider> | |||||
| </> | |||||
| ); | |||||
| }; | |||||
| export default QcForm; | |||||
| @@ -109,8 +109,8 @@ const ConsolidatedPickOrders: React.FC<Props> = ({ filterArgs }) => { | |||||
| const onDetailClick = useCallback( | const onDetailClick = useCallback( | ||||
| (pickOrder: any) => { | (pickOrder: any) => { | ||||
| console.log(pickOrder); | console.log(pickOrder); | ||||
| const status = pickOrder.status | |||||
| if (pickOrderStatusMap[status] >= 2) { | |||||
| const status = pickOrder.status; | |||||
| if (pickOrderStatusMap[status] >= 3) { | |||||
| router.push(`/pickorder/detail?consoCode=${pickOrder.consoCode}`); | router.push(`/pickorder/detail?consoCode=${pickOrder.consoCode}`); | ||||
| } else { | } else { | ||||
| openDetailModal(pickOrder.consoCode); | openDetailModal(pickOrder.consoCode); | ||||
| @@ -210,31 +210,28 @@ const ConsolidatedPickOrders: React.FC<Props> = ({ filterArgs }) => { | |||||
| [closeDetailModal] | [closeDetailModal] | ||||
| ); | ); | ||||
| const onChange = useCallback( | |||||
| ( | |||||
| event: React.SyntheticEvent, | |||||
| newValue: NameList | |||||
| ) => { | |||||
| console.log(newValue); | |||||
| formProps.setValue("assignTo", newValue.id); | |||||
| }, | |||||
| [] | |||||
| ); | |||||
| const onChange = useCallback( | |||||
| (event: React.SyntheticEvent, newValue: NameList) => { | |||||
| console.log(newValue); | |||||
| formProps.setValue("assignTo", newValue.id); | |||||
| }, | |||||
| [] | |||||
| ); | |||||
| const onSubmit = useCallback<SubmitHandler<ReleasePickOrderInputs & {}>>( | const onSubmit = useCallback<SubmitHandler<ReleasePickOrderInputs & {}>>( | ||||
| async (data, event) => { | async (data, event) => { | ||||
| console.log(data); | console.log(data); | ||||
| try { | 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") | |||||
| const res = await releasePickOrder(data); | |||||
| console.log(res); | |||||
| console.log(res.status); | |||||
| if ((res.status === 200)) { | |||||
| router.push(`/pickorder/detail?consoCode=${data.consoCode}`); | |||||
| } else { | |||||
| console.log(res); | |||||
| } | } | ||||
| } catch (error) { | } catch (error) { | ||||
| console.log(error) | |||||
| console.log(error); | |||||
| } | } | ||||
| }, | }, | ||||
| [releasePickOrder] | [releasePickOrder] | ||||
| @@ -251,7 +248,7 @@ const ConsolidatedPickOrders: React.FC<Props> = ({ filterArgs }) => { | |||||
| useEffect(() => { | useEffect(() => { | ||||
| if (consoCode) { | if (consoCode) { | ||||
| fetchConso(consoCode); | fetchConso(consoCode); | ||||
| formProps.setValue("consoCode", consoCode) | |||||
| formProps.setValue("consoCode", consoCode); | |||||
| } | } | ||||
| }, [consoCode]); | }, [consoCode]); | ||||
| @@ -75,6 +75,7 @@ import ReactQrCodeScannerModal, { | |||||
| import QrModal from "./QrModal"; | import QrModal from "./QrModal"; | ||||
| import { PlayArrow } from "@mui/icons-material"; | import { PlayArrow } from "@mui/icons-material"; | ||||
| import DoneIcon from "@mui/icons-material/Done"; | import DoneIcon from "@mui/icons-material/Done"; | ||||
| import { QrCode } from "../QrCode"; | |||||
| type Props = { | type Props = { | ||||
| po: PoResult; | po: PoResult; | ||||
| @@ -272,6 +273,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| // break; | // break; | ||||
| } | } | ||||
| }, [purchaseOrder, handleStartPo, handleCompletePo]); | }, [purchaseOrder, handleStartPo, handleCompletePo]); | ||||
| return ( | return ( | ||||
| <> | <> | ||||
| <Stack | <Stack | ||||
| @@ -283,7 +285,8 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| <Grid item> | <Grid item> | ||||
| <Typography mb={2} variant="h4"> | <Typography mb={2} variant="h4"> | ||||
| {/* {purchaseOrder.code} - {currPoStatus} */} | {/* {purchaseOrder.code} - {currPoStatus} */} | ||||
| {purchaseOrder.code} - {t(`${purchaseOrder.status.toLowerCase()}`)} | |||||
| {purchaseOrder.code} -{" "} | |||||
| {t(`${purchaseOrder.status.toLowerCase()}`)} | |||||
| </Typography> | </Typography> | ||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| @@ -413,7 +413,6 @@ function PoInputGrid({ | |||||
| renderCell: (params) => { | renderCell: (params) => { | ||||
| return t(`${params.row.status}`) | return t(`${params.row.status}`) | ||||
| } | } | ||||
| // editable: true, | |||||
| }, | }, | ||||
| { | { | ||||
| field: "actions", | field: "actions", | ||||
| @@ -423,10 +422,10 @@ function PoInputGrid({ | |||||
| flex: 1.5, | flex: 1.5, | ||||
| cellClassName: "actions", | cellClassName: "actions", | ||||
| getActions: (params) => { | getActions: (params) => { | ||||
| console.log(params.row.status); | |||||
| // console.log(params.row.status); | |||||
| const status = params.row.status.toLowerCase(); | const status = params.row.status.toLowerCase(); | ||||
| console.log(stockInLineStatusMap[status]); | |||||
| console.log(session?.user?.abilities?.includes("APPROVAL")); | |||||
| // console.log(stockInLineStatusMap[status]); | |||||
| // console.log(session?.user?.abilities?.includes("APPROVAL")); | |||||
| return [ | return [ | ||||
| <GridActionsCellItem | <GridActionsCellItem | ||||
| icon={<PlayArrowIcon />} | icon={<PlayArrowIcon />} | ||||
| @@ -96,7 +96,7 @@ const QcForm: React.FC<Props> = ({ qc, itemDetail, disabled }) => { | |||||
| clearErrors(); | clearErrors(); | ||||
| validateForm(); | validateForm(); | ||||
| }, [validateForm]); | }, [validateForm]); | ||||
| // const [recordQty, setRecordQty] = useState(0); | |||||
| const columns = useMemo<GridColDef[]>( | const columns = useMemo<GridColDef[]>( | ||||
| () => [ | () => [ | ||||
| { | { | ||||