@@ -55,6 +55,8 @@ import { downloadFile } from "@/app/utils/commonUtil"; | |||||
import { fetchPoQrcode } from "@/app/api/pdf/actions"; | import { fetchPoQrcode } from "@/app/api/pdf/actions"; | ||||
import { fetchQcResult } from "@/app/api/qc/actions"; | import { fetchQcResult } from "@/app/api/qc/actions"; | ||||
import PoQcStockInModal from "./PoQcStockInModal"; | import PoQcStockInModal from "./PoQcStockInModal"; | ||||
import { notifyActionSuccess } from "../Toast/Toast"; | |||||
import DoDisturbIcon from "@mui/icons-material/DoDisturb"; | |||||
interface ResultWithId { | interface ResultWithId { | ||||
id: number; | id: number; | ||||
@@ -124,6 +126,7 @@ function PoInputGrid({ | |||||
const [escalOpen, setEscalOpen] = useState(false); | const [escalOpen, setEscalOpen] = useState(false); | ||||
const [stockInOpen, setStockInOpen] = useState(false); | const [stockInOpen, setStockInOpen] = useState(false); | ||||
const [putAwayOpen, setPutAwayOpen] = useState(false); | const [putAwayOpen, setPutAwayOpen] = useState(false); | ||||
const [rejectOpen, setRejectOpen] = useState(false); | |||||
const [btnIsLoading, setBtnIsLoading] = useState(false); | const [btnIsLoading, setBtnIsLoading] = useState(false); | ||||
const [currQty, setCurrQty] = useState(() => { | const [currQty, setCurrQty] = useState(() => { | ||||
const total = entries.reduce( | const total = entries.reduce( | ||||
@@ -178,6 +181,7 @@ function PoInputGrid({ | |||||
) as StockInLine[] | ) as StockInLine[] | ||||
); | ); | ||||
setBtnIsLoading(false); | setBtnIsLoading(false); | ||||
notifyActionSuccess(); | |||||
// do post directly to test | // do post directly to test | ||||
// openStartModal(); | // openStartModal(); | ||||
}, 200); | }, 200); | ||||
@@ -229,6 +233,27 @@ function PoInputGrid({ | |||||
}, | }, | ||||
[] | [] | ||||
); | ); | ||||
const handleReject = useCallback( | |||||
(id: GridRowId, params: any) => () => { | |||||
setRowModesModel((prev) => ({ | |||||
...prev, | |||||
[id]: { mode: GridRowModes.View }, | |||||
})); | |||||
setModalInfo(params.row); | |||||
setTimeout(() => { | |||||
// open stock in modal | |||||
// openPutAwayModal(); | |||||
// return the record with its status as pending | |||||
// update layout | |||||
console.log("delayed"); | |||||
openRejectModal(); | |||||
// printQrcode(params.row); | |||||
}, 200); | |||||
}, | |||||
[] | |||||
); | |||||
const handleStockIn = useCallback( | const handleStockIn = useCallback( | ||||
(id: GridRowId, params: any) => () => { | (id: GridRowId, params: any) => () => { | ||||
// setBtnIsLoading(true); | // setBtnIsLoading(true); | ||||
@@ -332,6 +357,13 @@ function PoInputGrid({ | |||||
setEscalOpen(true); | setEscalOpen(true); | ||||
}, []); | }, []); | ||||
const closeRejectModal = useCallback(() => { | |||||
setRejectOpen(false); | |||||
}, []); | |||||
const openRejectModal = useCallback(() => { | |||||
setRejectOpen(true); | |||||
}, []); | |||||
const columns = useMemo<GridColDef[]>( | const columns = useMemo<GridColDef[]>( | ||||
() => [ | () => [ | ||||
{ | { | ||||
@@ -393,7 +425,7 @@ function PoInputGrid({ | |||||
color: "primary.main", | color: "primary.main", | ||||
// marginRight: 1, | // marginRight: 1, | ||||
}} | }} | ||||
disabled={btnIsLoading || !(stockInLineStatusMap[status] === 0)} | |||||
disabled={!(stockInLineStatusMap[status] === 0)} | |||||
// set _isNew to false after posting | // set _isNew to false after posting | ||||
// or check status | // or check status | ||||
onClick={handleStart(params.row.id, params)} | onClick={handleStart(params.row.id, params)} | ||||
@@ -407,7 +439,10 @@ function PoInputGrid({ | |||||
color: "primary.main", | color: "primary.main", | ||||
// marginRight: 1, | // marginRight: 1, | ||||
}} | }} | ||||
disabled={btnIsLoading || stockInLineStatusMap[status] < 1} | |||||
disabled={ | |||||
// stockInLineStatusMap[status] === 9 || | |||||
stockInLineStatusMap[status] < 1 | |||||
} | |||||
// set _isNew to false after posting | // set _isNew to false after posting | ||||
// or check status | // or check status | ||||
onClick={handleQC(params.row.id, params)} | onClick={handleQC(params.row.id, params)} | ||||
@@ -422,7 +457,7 @@ function PoInputGrid({ | |||||
// marginRight: 1, | // marginRight: 1, | ||||
}} | }} | ||||
disabled={ | disabled={ | ||||
btnIsLoading || | |||||
stockInLineStatusMap[status] === 9 || | |||||
stockInLineStatusMap[status] <= 0 || | stockInLineStatusMap[status] <= 0 || | ||||
stockInLineStatusMap[status] >= 5 | stockInLineStatusMap[status] >= 5 | ||||
} | } | ||||
@@ -440,7 +475,7 @@ function PoInputGrid({ | |||||
// marginRight: 1, | // marginRight: 1, | ||||
}} | }} | ||||
disabled={ | disabled={ | ||||
btnIsLoading || | |||||
stockInLineStatusMap[status] === 9 || | |||||
stockInLineStatusMap[status] <= 2 || | stockInLineStatusMap[status] <= 2 || | ||||
stockInLineStatusMap[status] >= 7 | stockInLineStatusMap[status] >= 7 | ||||
} | } | ||||
@@ -457,7 +492,7 @@ function PoInputGrid({ | |||||
color: "primary.main", | color: "primary.main", | ||||
// marginRight: 1, | // marginRight: 1, | ||||
}} | }} | ||||
disabled={btnIsLoading || stockInLineStatusMap[status] < 7} | |||||
disabled={stockInLineStatusMap[status] === 9 || stockInLineStatusMap[status] < 7} | |||||
// set _isNew to false after posting | // set _isNew to false after posting | ||||
// or check status | // or check status | ||||
onClick={handlePutAway(params.row.id, params)} | onClick={handlePutAway(params.row.id, params)} | ||||
@@ -471,7 +506,7 @@ function PoInputGrid({ | |||||
color: "primary.main", | color: "primary.main", | ||||
// marginRight: 1, | // marginRight: 1, | ||||
}} | }} | ||||
disabled={btnIsLoading || stockInLineStatusMap[status] !== 8} | |||||
disabled={stockInLineStatusMap[status] === 9 || stockInLineStatusMap[status] !== 8} | |||||
// set _isNew to false after posting | // set _isNew to false after posting | ||||
// or check status | // or check status | ||||
onClick={handleQrCode(params.row.id, params)} | onClick={handleQrCode(params.row.id, params)} | ||||
@@ -479,14 +514,26 @@ function PoInputGrid({ | |||||
key="edit" | key="edit" | ||||
/>, | />, | ||||
<GridActionsCellItem | <GridActionsCellItem | ||||
icon={<DeleteIcon />} | |||||
icon={ | |||||
stockInLineStatusMap[status] >= 1 ? ( | |||||
<DoDisturbIcon /> | |||||
) : ( | |||||
<DeleteIcon /> | |||||
) | |||||
} | |||||
label="Delete" | label="Delete" | ||||
sx={{ | sx={{ | ||||
color: "error.main", | color: "error.main", | ||||
}} | }} | ||||
disabled={btnIsLoading || stockInLineStatusMap[status] !== 0} | |||||
// disabled={Boolean(params.row.status)} | |||||
onClick={handleDelete(params.row.id)} | |||||
disabled={ | |||||
stockInLineStatusMap[status] >= 7 && | |||||
stockInLineStatusMap[status] <= 9 | |||||
} | |||||
onClick={ | |||||
stockInLineStatusMap[status] === 0 | |||||
? handleDelete(params.row.id) | |||||
: handleReject(params.row.id, params) | |||||
} | |||||
color="inherit" | color="inherit" | ||||
key="edit" | key="edit" | ||||
/>, | />, | ||||
@@ -494,7 +541,7 @@ function PoInputGrid({ | |||||
}, | }, | ||||
}, | }, | ||||
], | ], | ||||
[btnIsLoading] | |||||
[stockInLineStatusMap, btnIsLoading, handleQrCode, handleReject] | |||||
); | ); | ||||
const addRow = useCallback(() => { | const addRow = useCallback(() => { | ||||
@@ -671,6 +718,21 @@ function PoInputGrid({ | |||||
/> | /> | ||||
</> | </> | ||||
)} | )} | ||||
{modalInfo !== undefined && ( | |||||
<> | |||||
<PoQcStockInModal | |||||
type={"reject"} | |||||
// setRows={setRows} | |||||
setEntries={setEntries} | |||||
setStockInLine={setStockInLine} | |||||
setItemDetail={setModalInfo} | |||||
// qc={qc} | |||||
open={rejectOpen} | |||||
onClose={closeRejectModal} | |||||
itemDetail={modalInfo} | |||||
/> | |||||
</> | |||||
)} | |||||
{modalInfo !== undefined && ( | {modalInfo !== undefined && ( | ||||
<> | <> | ||||
<PoQcStockInModal | <PoQcStockInModal | ||||
@@ -39,6 +39,7 @@ import { downloadFile } from "@/app/utils/commonUtil"; | |||||
import { fetchPoQrcode } from "@/app/api/pdf/actions"; | import { fetchPoQrcode } from "@/app/api/pdf/actions"; | ||||
import UploadContext from "../UploadProvider/UploadProvider"; | import UploadContext from "../UploadProvider/UploadProvider"; | ||||
import useUploadContext from "../UploadProvider/useUploadContext"; | import useUploadContext from "../UploadProvider/useUploadContext"; | ||||
import RejectForm from "./RejectForm"; | |||||
dayjs.extend(arraySupport); | dayjs.extend(arraySupport); | ||||
interface CommonProps extends Omit<ModalProps, "children"> { | interface CommonProps extends Omit<ModalProps, "children"> { | ||||
@@ -56,7 +57,7 @@ interface CommonProps extends Omit<ModalProps, "children"> { | |||||
>; | >; | ||||
qc?: QcItemWithChecks[]; | qc?: QcItemWithChecks[]; | ||||
warehouse?: any[]; | warehouse?: any[]; | ||||
type: "qc" | "stockIn" | "escalation" | "putaway"; | |||||
type: "qc" | "stockIn" | "escalation" | "putaway" | "reject"; | |||||
} | } | ||||
interface QcProps extends CommonProps { | interface QcProps extends CommonProps { | ||||
qc: QcItemWithChecks[]; | qc: QcItemWithChecks[]; | ||||
@@ -74,8 +75,17 @@ interface EscalationProps extends CommonProps { | |||||
// naming | // naming | ||||
type: "escalation"; | type: "escalation"; | ||||
} | } | ||||
interface RejectProps extends CommonProps { | |||||
// naming | |||||
type: "reject"; | |||||
} | |||||
type Props = QcProps | StockInProps | PutawayProps | EscalationProps; | |||||
type Props = | |||||
| QcProps | |||||
| StockInProps | |||||
| PutawayProps | |||||
| EscalationProps | |||||
| RejectProps; | |||||
const style = { | const style = { | ||||
position: "absolute", | position: "absolute", | ||||
top: "50%", | top: "50%", | ||||
@@ -194,7 +204,7 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||||
const onSubmit = useCallback<SubmitHandler<ModalFormInput & {}>>( | const onSubmit = useCallback<SubmitHandler<ModalFormInput & {}>>( | ||||
async (data, event) => { | async (data, event) => { | ||||
setBtnIsLoading(true); | setBtnIsLoading(true); | ||||
setIsUploading(true) | |||||
setIsUploading(true); | |||||
formProps.clearErrors(); | formProps.clearErrors(); | ||||
let hasErrors = false; | let hasErrors = false; | ||||
console.log(errors); | console.log(errors); | ||||
@@ -204,12 +214,12 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||||
try { | try { | ||||
// add checking | // add checking | ||||
if (type === "stockIn") { | if (type === "stockIn") { | ||||
hasErrors = checkStockIn(data) | |||||
console.log(hasErrors) | |||||
hasErrors = checkStockIn(data); | |||||
console.log(hasErrors); | |||||
} | } | ||||
if (type === "putaway") { | if (type === "putaway") { | ||||
hasErrors = checkPutaway(data); | hasErrors = checkPutaway(data); | ||||
console.log(hasErrors) | |||||
console.log(hasErrors); | |||||
} | } | ||||
//////////////////////// modify this mess later ////////////////////// | //////////////////////// modify this mess later ////////////////////// | ||||
var productionDate = null; | var productionDate = null; | ||||
@@ -246,7 +256,7 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||||
console.log(args); | console.log(args); | ||||
setServerError(t("An error has occurred. Please try again later.")); | setServerError(t("An error has occurred. Please try again later.")); | ||||
setBtnIsLoading(false); | setBtnIsLoading(false); | ||||
setIsUploading(false) | |||||
setIsUploading(false); | |||||
return; | return; | ||||
} | } | ||||
console.log(args); | console.log(args); | ||||
@@ -294,7 +304,7 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||||
} | } | ||||
// add loading | // add loading | ||||
setBtnIsLoading(false); | setBtnIsLoading(false); | ||||
setIsUploading(false) | |||||
setIsUploading(false); | |||||
setItemDetail(undefined); | setItemDetail(undefined); | ||||
closeHandler({}, "backdropClick"); | closeHandler({}, "backdropClick"); | ||||
} | } | ||||
@@ -303,7 +313,7 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||||
} catch (e) { | } catch (e) { | ||||
// server error | // server error | ||||
setBtnIsLoading(false); | setBtnIsLoading(false); | ||||
setIsUploading(false) | |||||
setIsUploading(false); | |||||
setServerError(t("An error has occurred. Please try again later.")); | setServerError(t("An error has occurred. Please try again later.")); | ||||
console.log(e); | console.log(e); | ||||
} | } | ||||
@@ -313,7 +323,7 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||||
const printQrcode = useCallback(async () => { | const printQrcode = useCallback(async () => { | ||||
setBtnIsLoading(true); | setBtnIsLoading(true); | ||||
setIsUploading(true) | |||||
setIsUploading(true); | |||||
const postData = { stockInLineIds: [itemDetail.id] }; | const postData = { stockInLineIds: [itemDetail.id] }; | ||||
// const postData = { stockInLineIds: [42,43,44] }; | // const postData = { stockInLineIds: [42,43,44] }; | ||||
const response = await fetchPoQrcode(postData); | const response = await fetchPoQrcode(postData); | ||||
@@ -322,7 +332,7 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||||
downloadFile(new Uint8Array(response.blobValue), response.filename!!); | downloadFile(new Uint8Array(response.blobValue), response.filename!!); | ||||
} | } | ||||
setBtnIsLoading(false); | setBtnIsLoading(false); | ||||
setIsUploading(false) | |||||
setIsUploading(false); | |||||
}, [itemDetail, fetchPoQrcode, downloadFile]); | }, [itemDetail, fetchPoQrcode, downloadFile]); | ||||
const renderSubmitButton = useMemo((): boolean => { | const renderSubmitButton = useMemo((): boolean => { | ||||
@@ -348,6 +358,11 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||||
); | ); | ||||
case "putaway": | case "putaway": | ||||
return stockInLineStatusMap[status] === 7; | return stockInLineStatusMap[status] === 7; | ||||
case "reject": | |||||
return ( | |||||
stockInLineStatusMap[status] >= 1 && | |||||
stockInLineStatusMap[status] <= 6 | |||||
); | |||||
default: | default: | ||||
return false; // Handle unexpected type | return false; // Handle unexpected type | ||||
} | } | ||||
@@ -366,16 +381,36 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||||
onSubmit={formProps.handleSubmit(onSubmit)} | onSubmit={formProps.handleSubmit(onSubmit)} | ||||
> | > | ||||
{itemDetail !== undefined && type === "qc" && ( | {itemDetail !== undefined && type === "qc" && ( | ||||
<QcForm qc={qc!!} itemDetail={itemDetail} disabled={renderSubmitButton}/> | |||||
<QcForm | |||||
qc={qc!!} | |||||
itemDetail={itemDetail} | |||||
disabled={!renderSubmitButton} | |||||
/> | |||||
)} | )} | ||||
{itemDetail !== undefined && type === "stockIn" && ( | {itemDetail !== undefined && type === "stockIn" && ( | ||||
<StockInForm itemDetail={itemDetail} disabled={renderSubmitButton}/> | |||||
<StockInForm | |||||
itemDetail={itemDetail} | |||||
disabled={!renderSubmitButton} | |||||
/> | |||||
)} | )} | ||||
{itemDetail !== undefined && type === "escalation" && ( | {itemDetail !== undefined && type === "escalation" && ( | ||||
<EscalationForm itemDetail={itemDetail} disabled={renderSubmitButton}/> | |||||
<EscalationForm | |||||
itemDetail={itemDetail} | |||||
disabled={!renderSubmitButton} | |||||
/> | |||||
)} | )} | ||||
{itemDetail !== undefined && type === "putaway" && ( | {itemDetail !== undefined && type === "putaway" && ( | ||||
<PutawayForm itemDetail={itemDetail} warehouse={warehouse!!} disabled={renderSubmitButton}/> | |||||
<PutawayForm | |||||
itemDetail={itemDetail} | |||||
warehouse={warehouse!!} | |||||
disabled={!renderSubmitButton} | |||||
/> | |||||
)} | |||||
{itemDetail !== undefined && type === "reject" && ( | |||||
<RejectForm | |||||
itemDetail={itemDetail} | |||||
disabled={!renderSubmitButton} | |||||
/> | |||||
)} | )} | ||||
<Stack direction="row" justifyContent="flex-end" gap={1}> | <Stack direction="row" justifyContent="flex-end" gap={1}> | ||||
{renderSubmitButton ? ( | {renderSubmitButton ? ( | ||||
@@ -249,7 +249,7 @@ const QcForm: React.FC<Props> = ({ qc, itemDetail, disabled }) => { | |||||
<TextField | <TextField | ||||
label={t("sampleRate")} | label={t("sampleRate")} | ||||
fullWidth | fullWidth | ||||
defaultValue={1} | |||||
// defaultValue={1} | |||||
{...register("sampleRate", { | {...register("sampleRate", { | ||||
required: "sampleRate required!", | required: "sampleRate required!", | ||||
valueAsNumber: true, | valueAsNumber: true, | ||||
@@ -263,7 +263,7 @@ const QcForm: React.FC<Props> = ({ qc, itemDetail, disabled }) => { | |||||
<TextField | <TextField | ||||
label={t("sampleWeight")} | label={t("sampleWeight")} | ||||
fullWidth | fullWidth | ||||
defaultValue={1} | |||||
// defaultValue={1} | |||||
{...register("sampleWeight", { | {...register("sampleWeight", { | ||||
required: "sampleWeight required!", | required: "sampleWeight required!", | ||||
valueAsNumber: true, | valueAsNumber: true, | ||||
@@ -277,7 +277,7 @@ const QcForm: React.FC<Props> = ({ qc, itemDetail, disabled }) => { | |||||
<TextField | <TextField | ||||
label={t("totalWeight")} | label={t("totalWeight")} | ||||
fullWidth | fullWidth | ||||
defaultValue={1} | |||||
// defaultValue={1} | |||||
{...register("totalWeight", { | {...register("totalWeight", { | ||||
required: "totalWeight required!", | required: "totalWeight required!", | ||||
valueAsNumber: true, | valueAsNumber: true, | ||||
@@ -0,0 +1,125 @@ | |||||
"use client"; | |||||
import { StockInLineEntry, EscalationInput } 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 } 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 TwoLineCell from "./TwoLineCell"; | |||||
import QcSelect from "./QcSelect"; | |||||
import { QcItemWithChecks } from "@/app/api/qc"; | |||||
import { GridEditInputCell } from "@mui/x-data-grid"; | |||||
import { StockInLine } from "@/app/api/po"; | |||||
import { stockInLineStatusMap } from "@/app/utils/formatUtil"; | |||||
interface Props { | |||||
itemDetail: StockInLine; | |||||
// qc: QcItemWithChecks[]; | |||||
disabled: boolean | |||||
} | |||||
type EntryError = | |||||
| { | |||||
[field in keyof StockInLineEntry]?: string; | |||||
} | |||||
| undefined; | |||||
const RejectForm: React.FC<Props> = ({ | |||||
// qc, | |||||
itemDetail, | |||||
disabled | |||||
}) => { | |||||
const { t } = useTranslation(); | |||||
const apiRef = useGridApiRef(); | |||||
const { | |||||
register, | |||||
formState: { errors, defaultValues, touchedFields }, | |||||
watch, | |||||
control, | |||||
setValue, | |||||
getValues, | |||||
reset, | |||||
resetField, | |||||
setError, | |||||
clearErrors, | |||||
} = useFormContext<EscalationInput>(); | |||||
console.log(itemDetail) | |||||
// const status = "rejected" | |||||
const acceptedQty = watch("acceptedQty") || 0 | |||||
console.log(disabled) | |||||
useEffect(() => { | |||||
console.log("triggered") | |||||
setValue("status", "rejected") | |||||
}, []) | |||||
return ( | |||||
<Grid container justifyContent="flex-start" alignItems="flex-start"> | |||||
<Grid item xs={12}> | |||||
<Typography variant="h6" display="block" marginBlockEnd={1}> | |||||
{t(`Reject`)} | |||||
</Typography> | |||||
</Grid> | |||||
<Grid item xs={12}> | |||||
<Typography variant="h6" display="block" marginBlockEnd={1}> | |||||
{t(`to be processed`)}: {itemDetail.acceptedQty - acceptedQty} | |||||
</Typography> | |||||
</Grid> | |||||
<Grid | |||||
container | |||||
justifyContent="flex-start" | |||||
alignItems="flex-start" | |||||
spacing={2} | |||||
sx={{ mt: 0.5 }} | |||||
> | |||||
<Grid item xs={6}> | |||||
<TextField | |||||
label={t("acceptedQty")} | |||||
fullWidth | |||||
{...register("acceptedQty", { | |||||
required: "acceptedQty required!", | |||||
min: 0, | |||||
valueAsNumber: true, | |||||
max: itemDetail.acceptedQty | |||||
})} | |||||
disabled={disabled} | |||||
defaultValue={itemDetail.acceptedQty} | |||||
error={Boolean(errors.acceptedQty)} | |||||
helperText={errors.acceptedQty?.message} | |||||
/> | |||||
</Grid> | |||||
</Grid> | |||||
<Grid | |||||
container | |||||
justifyContent="flex-start" | |||||
alignItems="flex-start" | |||||
spacing={2} | |||||
sx={{ mt: 0.5 }} | |||||
> | |||||
</Grid> | |||||
</Grid> | |||||
); | |||||
}; | |||||
export default RejectForm; |