@@ -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[]>( | ||||
() => [ | () => [ | ||||
{ | { | ||||