@@ -16,7 +16,6 @@ export interface PostStockInLiineResponse<T> { | |||||
entity: StockInLine | StockInLine[] | entity: StockInLine | StockInLine[] | ||||
} | } | ||||
export interface StockInLineEntry { | export interface StockInLineEntry { | ||||
id?: number | id?: number | ||||
itemId: number | itemId: number | ||||
@@ -24,19 +23,37 @@ export interface StockInLineEntry { | |||||
purchaseOrderLineId: number | purchaseOrderLineId: number | ||||
acceptedQty: number | acceptedQty: number | ||||
status?: string | status?: string | ||||
expiryDate?: string | |||||
} | } | ||||
export interface PurchaseQcCheck { | export interface PurchaseQcCheck { | ||||
qcCheckId: number; | qcCheckId: number; | ||||
qty: number; | qty: number; | ||||
} | } | ||||
export interface StockInInput { | |||||
productLotNo?: string, | |||||
receiptDate: string | |||||
acceptedQty: number | |||||
acceptedWeight?: number | |||||
productionDate?: string | |||||
expiryDate: string | |||||
} | |||||
export interface PurchaseQCInput { | export interface PurchaseQCInput { | ||||
sampleRate: number; | sampleRate: number; | ||||
sampleWeight: number; | sampleWeight: number; | ||||
totalWeight: number; | totalWeight: number; | ||||
qcCheck: PurchaseQcCheck[]; | qcCheck: PurchaseQcCheck[]; | ||||
} | } | ||||
export interface EscalationInput { | |||||
handler: string | |||||
stockInLine: StockInLineEntry[] | |||||
} | |||||
export interface PutawayInput { | |||||
handler: string | |||||
stockInLine: StockInLineEntry[] | |||||
} | |||||
export type ModalFormInput = Partial<PurchaseQCInput & StockInInput & EscalationInput & PutawayInput> | |||||
export const testFetch = cache(async (id: number) => { | export const testFetch = cache(async (id: number) => { | ||||
return serverFetchJson<PoResult>(`${BASE_API_URL}/po/detail/${id}`, { | return serverFetchJson<PoResult>(`${BASE_API_URL}/po/detail/${id}`, { | ||||
@@ -53,8 +70,8 @@ export const createStockInLine = async (data: StockInLineEntry) => { | |||||
return stockInLine | return stockInLine | ||||
} | } | ||||
export const updateStockInLine = async (data: StockInLineEntry) => { | |||||
const stockInLine = await serverFetchJson<PostStockInLiineResponse<StockInLineEntry>>(`${BASE_API_URL}/stockInLine/update`, { | |||||
export const updateStockInLine = async (data: StockInLineEntry & ModalFormInput) => { | |||||
const stockInLine = await serverFetchJson<PostStockInLiineResponse<StockInLineEntry & ModalFormInput>>(`${BASE_API_URL}/stockInLine/update`, { | |||||
method: "POST", | method: "POST", | ||||
body: JSON.stringify(data), | body: JSON.stringify(data), | ||||
headers: { "Content-Type": "application/json" }, | headers: { "Content-Type": "application/json" }, | ||||
@@ -20,6 +20,7 @@ export interface PurchaseOrderLine { | |||||
itemNo: string | itemNo: string | ||||
itemName: string | itemName: string | ||||
qty: number | qty: number | ||||
uom?: string | |||||
price: number | price: number | ||||
status: string | status: string | ||||
stockInLine: StockInLine[] | stockInLine: StockInLine[] | ||||
@@ -45,7 +45,8 @@ export const serverFetch: typeof fetch = async (input, init) => { | |||||
...(accessToken | ...(accessToken | ||||
? { | ? { | ||||
Authorization: `Bearer ${accessToken}`, | Authorization: `Bearer ${accessToken}`, | ||||
Accept: | |||||
"application/json, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, multipart/form-data", | |||||
} | } | ||||
: {}), | : {}), | ||||
}, | }, | ||||
@@ -8,13 +8,14 @@ export const moneyFormatter = new Intl.NumberFormat("en-HK", { | |||||
currency: "HKD", | currency: "HKD", | ||||
}); | }); | ||||
export const stockInLineStatusMap: { [status: string]: number } = { | |||||
draft: 0, | |||||
pending: 1, | |||||
qc: 2, | |||||
determine1: 3, | |||||
determine2: 4, | |||||
determine3: 5, | |||||
receiving: 6, | |||||
completed: 7, | |||||
export const stockInLineStatusMap: { [status: string]: {key: string, value: number} } = { | |||||
draft: { key: "draft", value: 0 }, | |||||
pending: { key: "pending", value: 1 }, | |||||
qc: { key: "qc", value: 2 }, | |||||
determine1: { key: "determine1", value: 3 }, | |||||
determine2: { key: "determine2", value: 4 }, | |||||
determine3: { key: "determine3", value: 5 }, | |||||
receiving: { key: "receiving", value: 6 }, | |||||
received: { key: "received", value: 7 }, | |||||
completed: { key: "completed", value: 8 }, | |||||
}; | }; |
@@ -0,0 +1,206 @@ | |||||
"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, 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"; | |||||
interface Props { | |||||
itemDetail: StockInLine; | |||||
// qc: QcItemWithChecks[]; | |||||
} | |||||
type EntryError = | |||||
| { | |||||
[field in keyof StockInLineEntry]?: string; | |||||
} | |||||
| undefined; | |||||
type PoEscalationRow = TableRow<Partial<StockInLineEntry>, EntryError>; | |||||
const EscalationForm: React.FC<Props> = ({ | |||||
// qc, | |||||
itemDetail, | |||||
}) => { | |||||
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 columns = useMemo<GridColDef[]>( | |||||
() => [ | |||||
// { | |||||
// field: "qcCheckId", | |||||
// headerName: "qc Check", | |||||
// flex: 1, | |||||
// editable: true, | |||||
// valueFormatter(params) { | |||||
// const row = params.id ? params.api.getRow<PoEscalationRow>(params.id) : null; | |||||
// if (!row) { | |||||
// return null; | |||||
// } | |||||
// const Qc = qc.find((q) => q.id === row.qcCheckId); | |||||
// return Qc ? `${Qc.code} - ${Qc.name}` : t("Please select QC"); | |||||
// }, | |||||
// renderCell(params: GridRenderCellParams<PoEscalationRow, number>) { | |||||
// console.log(params.value); | |||||
// return <TwoLineCell>{params.formattedValue}</TwoLineCell>; | |||||
// }, | |||||
// renderEditCell(params: GridRenderEditCellParams<PoEscalationRow, number>) { | |||||
// const errorMessage = | |||||
// params.row._error?.[params.field as keyof StockInLineEntry]; | |||||
// console.log(errorMessage); | |||||
// const content = ( | |||||
// <QcSelect | |||||
// allQcs={qc} | |||||
// value={params.row.qcCheckId} | |||||
// onQcSelect={async (qcCheckId) => { | |||||
// await params.api.setEditCellValue({ | |||||
// id: params.id, | |||||
// field: "qcCheckId", | |||||
// value: qcCheckId, | |||||
// }); | |||||
// }} | |||||
// /> | |||||
// ); | |||||
// return errorMessage ? ( | |||||
// <Tooltip title={t(errorMessage)}> | |||||
// <Box width="100%">{content}</Box> | |||||
// </Tooltip> | |||||
// ) : ( | |||||
// content | |||||
// ); | |||||
// }, | |||||
// }, | |||||
{ | |||||
field: "qty", | |||||
headerName: "qty", | |||||
flex: 1, | |||||
editable: true, | |||||
type: "number", | |||||
renderEditCell(params: GridRenderEditCellParams<PoEscalationRow>) { | |||||
const errorMessage = | |||||
params.row._error?.[params.field as keyof StockInLineEntry]; | |||||
const content = <GridEditInputCell {...params} />; | |||||
return errorMessage ? ( | |||||
<Tooltip title={t(errorMessage)}> | |||||
<Box width="100%">{content}</Box> | |||||
</Tooltip> | |||||
) : ( | |||||
content | |||||
); | |||||
}, | |||||
}, | |||||
], | |||||
[] | |||||
); | |||||
const validationTest = useCallback( | |||||
(newRow: GridRowModel<PoEscalationRow>): EntryError => { | |||||
const error: EntryError = {}; | |||||
// const { qcCheckId, qty } = newRow; | |||||
// if (!qcCheckId || qcCheckId <= 0) { | |||||
// error["qcCheckId"] = "select qc"; | |||||
// } | |||||
// if (!qty || qty <= 0) { | |||||
// error["qty"] = "enter a qty"; | |||||
// } | |||||
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={4}> | |||||
<TextField | |||||
label={t("handler")} | |||||
fullWidth | |||||
{...register("handler", { | |||||
required: "handler required!", | |||||
})} | |||||
error={Boolean(errors.handler)} | |||||
helperText={errors.handler?.message} | |||||
/> | |||||
</Grid> | |||||
<Grid item xs={4}> | |||||
<TextField | |||||
label={t("total")} | |||||
fullWidth | |||||
// {...register("handler", { | |||||
// required: "handler required!", | |||||
// })} | |||||
value={itemDetail.acceptedQty} | |||||
disabled | |||||
// error={Boolean(errors.handler)} | |||||
// helperText={errors.handler?.message} | |||||
/> | |||||
</Grid> | |||||
</Grid> | |||||
<Grid | |||||
container | |||||
justifyContent="flex-start" | |||||
alignItems="flex-start" | |||||
spacing={2} | |||||
sx={{ mt: 0.5 }} | |||||
> | |||||
<Grid item xs={12}> | |||||
<InputDataGrid<EscalationInput, StockInLineEntry, EntryError> | |||||
apiRef={apiRef} | |||||
checkboxSelection={false} | |||||
_formKey={"stockInLine"} | |||||
columns={columns} | |||||
validateRow={validationTest} | |||||
/> | |||||
</Grid> | |||||
</Grid> | |||||
</Grid> | |||||
); | |||||
}; | |||||
export default EscalationForm; |
@@ -77,7 +77,7 @@ const PoDetail: React.FC<Props> = ({ | |||||
<TableCell align="left">{row.itemNo}</TableCell> | <TableCell align="left">{row.itemNo}</TableCell> | ||||
<TableCell align="left">{row.itemName}</TableCell> | <TableCell align="left">{row.itemName}</TableCell> | ||||
<TableCell align="left">{row.qty}</TableCell> | <TableCell align="left">{row.qty}</TableCell> | ||||
{/* <TableCell align="left">{row.uom}</TableCell> */} | |||||
<TableCell align="left">{row.uom}</TableCell> | |||||
<TableCell align="left">{row.price}</TableCell> | <TableCell align="left">{row.price}</TableCell> | ||||
{/* <TableCell align="left">{row.expiryDate}</TableCell> */} | {/* <TableCell align="left">{row.expiryDate}</TableCell> */} | ||||
<TableCell align="left">{row.status}</TableCell> | <TableCell align="left">{row.status}</TableCell> | ||||
@@ -133,6 +133,7 @@ const PoDetail: React.FC<Props> = ({ | |||||
<TableCell>{t("itemNo")}</TableCell> | <TableCell>{t("itemNo")}</TableCell> | ||||
<TableCell align="left">{t("itemName")}</TableCell> | <TableCell align="left">{t("itemName")}</TableCell> | ||||
<TableCell align="left">{t("qty")}</TableCell> | <TableCell align="left">{t("qty")}</TableCell> | ||||
<TableCell align="left">{t("uom")}</TableCell> | |||||
<TableCell align="left">{t("price")}</TableCell> | <TableCell align="left">{t("price")}</TableCell> | ||||
{/* <TableCell align="left">{t("expiryDate")}</TableCell> */} | {/* <TableCell align="left">{t("expiryDate")}</TableCell> */} | ||||
<TableCell align="left">{t("status")}</TableCell> | <TableCell align="left">{t("status")}</TableCell> | ||||
@@ -30,14 +30,14 @@ import DeleteIcon from "@mui/icons-material/Delete"; | |||||
import CancelIcon from "@mui/icons-material/Cancel"; | import CancelIcon from "@mui/icons-material/Cancel"; | ||||
import FactCheckIcon from "@mui/icons-material/FactCheck"; | import FactCheckIcon from "@mui/icons-material/FactCheck"; | ||||
import ShoppingCartIcon from "@mui/icons-material/ShoppingCart"; | import ShoppingCartIcon from "@mui/icons-material/ShoppingCart"; | ||||
import PoQcModal from "./PoQcModal"; | |||||
import { QcItemWithChecks } from "src/app/api/qc"; | import { QcItemWithChecks } from "src/app/api/qc"; | ||||
import PlayArrowIcon from "@mui/icons-material/PlayArrow"; | import PlayArrowIcon from "@mui/icons-material/PlayArrow"; | ||||
import { PurchaseOrderLine, StockInLine } from "@/app/api/po"; | import { PurchaseOrderLine, StockInLine } from "@/app/api/po"; | ||||
import { createStockInLine, testFetch } from "@/app/api/po/actions"; | import { createStockInLine, testFetch } from "@/app/api/po/actions"; | ||||
import { useSearchParams } from "next/navigation"; | import { useSearchParams } from "next/navigation"; | ||||
import { stockInLineStatusMap } from "@/app/utils/formatUtil"; | import { stockInLineStatusMap } from "@/app/utils/formatUtil"; | ||||
import PoQcStockInModal from "./PoQcStockInModal"; | |||||
import NotificationImportantIcon from '@mui/icons-material/NotificationImportant'; | |||||
interface ResultWithId { | interface ResultWithId { | ||||
id: number; | id: number; | ||||
} | } | ||||
@@ -85,19 +85,23 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { | |||||
const [entries, setEntries] = useState<StockInLineRow[]>(stockInLine || []); | const [entries, setEntries] = useState<StockInLineRow[]>(stockInLine || []); | ||||
const [modalInfo, setModalInfo] = useState<StockInLine>() | const [modalInfo, setModalInfo] = useState<StockInLine>() | ||||
const [qcOpen, setQcOpen] = useState(false); | const [qcOpen, setQcOpen] = useState(false); | ||||
const [escalOpen, setEscalOpen] = useState(false); | |||||
const [stockInOpen, setStockInOpen] = useState(false); | |||||
const [putAwayOpen, setPutAwayOpen] = useState(false); | |||||
const [type, setType] = useState<"qc" | "stockIn">("qc"); | |||||
const [defaultQty, setDefaultQty] = useState(() => { | const [defaultQty, setDefaultQty] = useState(() => { | ||||
const total = entries.reduce((acc, curr) => acc + (curr.acceptedQty || 0), 0); | const total = entries.reduce((acc, curr) => acc + (curr.acceptedQty || 0), 0); | ||||
return itemDetail.qty - total; | return itemDetail.qty - total; | ||||
}); | }); | ||||
const params = useSearchParams() | const params = useSearchParams() | ||||
const refetchData = useCallback(async () => { | |||||
const id = parseInt(params.get("id")!!) | |||||
const res = await testFetch(id) | |||||
const pol = res.pol!! | |||||
console.log(pol) | |||||
setRows(pol); | |||||
}, [params]) | |||||
// const refetchData = useCallback(async () => { | |||||
// const id = parseInt(params.get("id")!!) | |||||
// const res = await testFetch(id) | |||||
// const pol = res.pol!! | |||||
// console.log(pol) | |||||
// setRows(pol); | |||||
// }, [params]) | |||||
const handleDelete = useCallback( | const handleDelete = useCallback( | ||||
(id: GridRowId) => () => { | (id: GridRowId) => () => { | ||||
@@ -127,7 +131,7 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { | |||||
} | } | ||||
const res = await createStockInLine(postData) | const res = await createStockInLine(postData) | ||||
console.log(res) | console.log(res) | ||||
// setEntries((prev) => prev.map((p) => p.id === oldId ? res.entity : p)) | |||||
setEntries((prev) => prev.map((p) => p.id === oldId ? res.entity as StockInLine : p)) | |||||
// do post directly to test | // do post directly to test | ||||
// openStartModal(); | // openStartModal(); | ||||
}, 200); | }, 200); | ||||
@@ -149,14 +153,49 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { | |||||
}, | }, | ||||
[] | [] | ||||
); | ); | ||||
const handleEscalation = useCallback( | |||||
(id: GridRowId, params: any) => () => { | |||||
setRowModesModel((prev) => ({ | |||||
...prev, | |||||
[id]: { mode: GridRowModes.View }, | |||||
})); | |||||
setModalInfo(params.row) | |||||
setTimeout(() => { | |||||
// open qc modal | |||||
console.log("delayed"); | |||||
openEscalationModal() | |||||
}, 200); | |||||
}, | |||||
[] | |||||
); | |||||
const handleStockIn = useCallback( | const handleStockIn = useCallback( | ||||
(id: GridRowId) => () => { | |||||
(id: GridRowId, params: any) => () => { | |||||
setRowModesModel((prev) => ({ | setRowModesModel((prev) => ({ | ||||
...prev, | ...prev, | ||||
[id]: { mode: GridRowModes.View }, | [id]: { mode: GridRowModes.View }, | ||||
})); | })); | ||||
setModalInfo(params.row) | |||||
setTimeout(() => { | setTimeout(() => { | ||||
// open stock in modal | // open stock in modal | ||||
openStockInModal() | |||||
// return the record with its status as pending | |||||
// update layout | |||||
console.log("delayed"); | |||||
}, 200); | |||||
}, | |||||
[] | |||||
); | |||||
const handlePutAway = 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 | // return the record with its status as pending | ||||
// update layout | // update layout | ||||
console.log("delayed"); | console.log("delayed"); | ||||
@@ -172,11 +211,32 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { | |||||
setQcOpen(true); | setQcOpen(true); | ||||
}, []); | }, []); | ||||
const closeStockInModal = useCallback(() => { | |||||
setStockInOpen(false); | |||||
}, []); | |||||
const openStockInModal = useCallback(() => { | |||||
setStockInOpen(true); | |||||
}, []); | |||||
const closePutAwayModal = useCallback(() => { | |||||
setPutAwayOpen(false); | |||||
}, []); | |||||
const openPutAwayModal = useCallback(() => { | |||||
setPutAwayOpen(true); | |||||
}, []); | |||||
const closeEscalationModal = useCallback(() => { | |||||
setEscalOpen(false); | |||||
}, []); | |||||
const openEscalationModal = useCallback(() => { | |||||
setEscalOpen(true); | |||||
}, []); | |||||
const columns = useMemo<GridColDef[]>( | const columns = useMemo<GridColDef[]>( | ||||
() => [ | () => [ | ||||
{ | { | ||||
field: "itemNo", | field: "itemNo", | ||||
flex: 1, | |||||
flex: .8, | |||||
}, | }, | ||||
{ | { | ||||
field: "itemName", | field: "itemName", | ||||
@@ -198,20 +258,10 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { | |||||
{ | { | ||||
field: "actions", | field: "actions", | ||||
type: "actions", | type: "actions", | ||||
headerName: "start | qc | stock in | delete", | |||||
flex: 1, | |||||
headerName: "start | qc | escalate | stock in | putaway | delete", | |||||
flex: 1.5, | |||||
cellClassName: "actions", | cellClassName: "actions", | ||||
getActions: (params) => { | getActions: (params) => { | ||||
// const stockInLineStatusMap: { [status: string]: number } = { | |||||
// draft: 0, | |||||
// pending: 1, | |||||
// qc: 2, | |||||
// determine1: 3, | |||||
// determine2: 4, | |||||
// determine3: 5, | |||||
// receiving: 6, | |||||
// completed: 7, | |||||
// }; | |||||
console.log(params.row.status); | console.log(params.row.status); | ||||
const status = params.row.status.toLowerCase() | const status = params.row.status.toLowerCase() | ||||
return [ | return [ | ||||
@@ -220,8 +270,9 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { | |||||
label="start" | label="start" | ||||
sx={{ | sx={{ | ||||
color: "primary.main", | color: "primary.main", | ||||
marginRight: 2 | |||||
}} | }} | ||||
disabled={!(stockInLineStatusMap[status] === 0)} | |||||
disabled={!(stockInLineStatusMap[status].value === 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)} | ||||
@@ -233,24 +284,54 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { | |||||
label="qc" | label="qc" | ||||
sx={{ | sx={{ | ||||
color: "primary.main", | color: "primary.main", | ||||
marginRight: 2 | |||||
}} | }} | ||||
disabled={stockInLineStatusMap[status] <= 0 || stockInLineStatusMap[status] >= 6} | |||||
disabled={stockInLineStatusMap[status].value <= 0 || stockInLineStatusMap[status].value >= 5} | |||||
// 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)} | ||||
color="inherit" | color="inherit" | ||||
key="edit" | key="edit" | ||||
/>, | />, | ||||
<GridActionsCellItem | |||||
icon={<NotificationImportantIcon />} | |||||
label="escalation" | |||||
sx={{ | |||||
color: "primary.main", | |||||
marginRight: 2 | |||||
}} | |||||
disabled={stockInLineStatusMap[status].value <= 0 || stockInLineStatusMap[status].value >= 5} | |||||
// set _isNew to false after posting | |||||
// or check status | |||||
onClick={handleEscalation(params.row.id, params)} | |||||
color="inherit" | |||||
key="edit" | |||||
/>, | |||||
<GridActionsCellItem | <GridActionsCellItem | ||||
icon={<ShoppingCartIcon />} | icon={<ShoppingCartIcon />} | ||||
label="stockin" | label="stockin" | ||||
sx={{ | sx={{ | ||||
color: "primary.main", | color: "primary.main", | ||||
marginRight: 2 | |||||
}} | |||||
disabled={stockInLineStatusMap[status].value !== 6} | |||||
// set _isNew to false after posting | |||||
// or check status | |||||
onClick={handleStockIn(params.row.id, params)} | |||||
color="inherit" | |||||
key="edit" | |||||
/>, | |||||
<GridActionsCellItem | |||||
icon={<ShoppingCartIcon />} | |||||
label="putaway" | |||||
sx={{ | |||||
color: "primary.main", | |||||
marginRight: 2 | |||||
}} | }} | ||||
disabled={stockInLineStatusMap[status] !== 6} | |||||
disabled={stockInLineStatusMap[status].value !== 7} | |||||
// set _isNew to false after posting | // set _isNew to false after posting | ||||
// or check status | // or check status | ||||
onClick={handleStockIn(params.row.id)} | |||||
onClick={handlePutAway(params.row.id, params)} | |||||
color="inherit" | color="inherit" | ||||
key="edit" | key="edit" | ||||
/>, | />, | ||||
@@ -260,7 +341,7 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { | |||||
sx={{ | sx={{ | ||||
color: "error.main", | color: "error.main", | ||||
}} | }} | ||||
disabled={stockInLineStatusMap[status] !== 0} | |||||
disabled={stockInLineStatusMap[status].value !== 0} | |||||
// disabled={Boolean(params.row.status)} | // disabled={Boolean(params.row.status)} | ||||
onClick={handleDelete(params.row.id)} | onClick={handleDelete(params.row.id)} | ||||
color="inherit" | color="inherit" | ||||
@@ -404,7 +485,8 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { | |||||
}} | }} | ||||
/> | /> | ||||
<> | <> | ||||
<PoQcModal | |||||
<PoQcStockInModal | |||||
type={"qc"} | |||||
setEntries={setEntries} | setEntries={setEntries} | ||||
qc={qc} | qc={qc} | ||||
open={qcOpen} | open={qcOpen} | ||||
@@ -412,6 +494,36 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) { | |||||
itemDetail={modalInfo!!} | itemDetail={modalInfo!!} | ||||
/> | /> | ||||
</> | </> | ||||
<> | |||||
<PoQcStockInModal | |||||
type={"escalation"} | |||||
setEntries={setEntries} | |||||
// qc={qc} | |||||
open={escalOpen} | |||||
onClose={closeEscalationModal} | |||||
itemDetail={modalInfo!!} | |||||
/> | |||||
</> | |||||
<> | |||||
<PoQcStockInModal | |||||
type={"stockIn"} | |||||
setEntries={setEntries} | |||||
// qc={qc} | |||||
open={stockInOpen} | |||||
onClose={closeStockInModal} | |||||
itemDetail={modalInfo!!} | |||||
/> | |||||
</> | |||||
<> | |||||
<PoQcStockInModal | |||||
type={"putaway"} | |||||
setEntries={setEntries} | |||||
open={putAwayOpen} | |||||
warehouse={[]} | |||||
onClose={closePutAwayModal} | |||||
itemDetail={modalInfo!!} | |||||
/> | |||||
</> | |||||
</> | </> | ||||
); | ); | ||||
} | } | ||||
@@ -1,8 +1,8 @@ | |||||
"use client"; | "use client"; | ||||
import { PurchaseQCInput, StockInLineEntry, updateStockInLine } from "@/app/api/po/actions"; | |||||
import { ModalFormInput, PurchaseQCInput, StockInInput, StockInLineEntry, updateStockInLine } from "@/app/api/po/actions"; | |||||
import { Box, Button, Modal, ModalProps, Stack } from "@mui/material"; | import { Box, Button, Modal, ModalProps, Stack } from "@mui/material"; | ||||
import { Dispatch, SetStateAction, useCallback, useEffect, useState } from "react"; | |||||
import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from "react"; | |||||
import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; | import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; | ||||
import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
import QcForm from "./QcForm"; | import QcForm from "./QcForm"; | ||||
@@ -12,24 +12,36 @@ import { StockInLine } from "@/app/api/po"; | |||||
import { useSearchParams } from "next/navigation"; | import { useSearchParams } from "next/navigation"; | ||||
import { stockInLineStatusMap } from "@/app/utils/formatUtil"; | import { stockInLineStatusMap } from "@/app/utils/formatUtil"; | ||||
import { StockInLineRow } from "./PoInputGrid"; | import { StockInLineRow } from "./PoInputGrid"; | ||||
// type: | |||||
import EscalationForm from "./EscalationForm"; | |||||
import StockInForm from "./StockInForm"; | |||||
interface CommonProps extends Omit<ModalProps, "children"> { | interface CommonProps extends Omit<ModalProps, "children"> { | ||||
setEntries: Dispatch<SetStateAction<StockInLineRow[]>> | setEntries: Dispatch<SetStateAction<StockInLineRow[]>> | ||||
itemDetail: StockInLine; | itemDetail: StockInLine; | ||||
qc?: QcItemWithChecks[]; | qc?: QcItemWithChecks[]; | ||||
warehouse?: any[]; | warehouse?: any[]; | ||||
type: "qc" | "stockIn" | "escalation" | "putaway" | |||||
} | } | ||||
interface QcProps extends CommonProps { | interface QcProps extends CommonProps { | ||||
qc: QcItemWithChecks[]; | qc: QcItemWithChecks[]; | ||||
type: "qc" | |||||
} | } | ||||
interface StockInProps extends CommonProps { | interface StockInProps extends CommonProps { | ||||
// naming | // naming | ||||
type: "stockIn" | |||||
} | |||||
interface PutawayProps extends CommonProps { | |||||
// naming | |||||
// warehouse: any[]; | |||||
warehouse: any[]; | warehouse: any[]; | ||||
type: "putaway" | |||||
} | |||||
interface EscalationProps extends CommonProps { | |||||
// naming | |||||
type: "escalation" | |||||
} | } | ||||
type Props = QcProps | StockInProps; | |||||
type Props = QcProps | StockInProps | EscalationProps | PutawayProps; | |||||
const style = { | const style = { | ||||
position: "absolute", | position: "absolute", | ||||
@@ -42,7 +54,8 @@ const style = { | |||||
pb: 10, | pb: 10, | ||||
width: { xs: "80%", sm: "80%", md: "80%" }, | width: { xs: "80%", sm: "80%", md: "80%" }, | ||||
}; | }; | ||||
const PoQcModal: React.FC<Props> = ({ | |||||
const PoQcStockInModal: React.FC<Props> = ({ | |||||
type, | |||||
setEntries, | setEntries, | ||||
open, | open, | ||||
onClose, | onClose, | ||||
@@ -56,7 +69,7 @@ const PoQcModal: React.FC<Props> = ({ | |||||
const params = useSearchParams() | const params = useSearchParams() | ||||
console.log(params.get("id")) | console.log(params.get("id")) | ||||
const [defaultValues, setDefaultValues] = useState({}); | const [defaultValues, setDefaultValues] = useState({}); | ||||
const formProps = useForm<PurchaseQCInput>({ | |||||
const formProps = useForm<ModalFormInput>({ | |||||
defaultValues: defaultValues ? defaultValues : {}, | defaultValues: defaultValues ? defaultValues : {}, | ||||
}); | }); | ||||
const errors = formProps.formState.errors; | const errors = formProps.formState.errors; | ||||
@@ -72,32 +85,84 @@ const PoQcModal: React.FC<Props> = ({ | |||||
setDefaultValues({}); | setDefaultValues({}); | ||||
}, []); | }, []); | ||||
const onSubmit = useCallback<SubmitHandler<PurchaseQCInput & {}>>( | |||||
// status to be posted | |||||
const getPostingStatus = useCallback( | |||||
(type: string) => { | |||||
switch (type) { | |||||
case "qc": | |||||
return stockInLineStatusMap.receiving.key; | |||||
case "stockIn": | |||||
return stockInLineStatusMap.received.key; | |||||
case "putaway": | |||||
return stockInLineStatusMap.completed.key; | |||||
default: | |||||
return stockInLineStatusMap.pending.key; | |||||
} | |||||
}, [] | |||||
) | |||||
const onSubmit = useCallback<SubmitHandler<ModalFormInput & {}>>( | |||||
async (data, event) => { | async (data, event) => { | ||||
let hasErrors = false; | let hasErrors = false; | ||||
console.log(errors); | console.log(errors); | ||||
console.log(data); | console.log(data); | ||||
console.log(itemDetail); | console.log(itemDetail); | ||||
try { | try { | ||||
if (hasErrors) { | |||||
setServerError(t("An error has occurred. Please try again later.")); | |||||
return false; | |||||
console.log(type) | |||||
var status = getPostingStatus(type) | |||||
// if escalation, take data.status as status | |||||
console.log(status) | |||||
// add checking | |||||
// const qty = data.sampleRate | |||||
//////////////////////// modify this mess later ////////////////////// | |||||
var productionDate = null | |||||
var acceptedQty = null | |||||
if (data.productionDate && data.productionDate.length > 0) { | |||||
productionDate = data.productionDate | |||||
} | |||||
if (data.acceptedQty) { | |||||
acceptedQty = parseInt(data.acceptedQty.toString()) | |||||
} else { | |||||
acceptedQty = data.sampleRate | |||||
} | } | ||||
// do post update stock in line | |||||
// const reqStatus = stockInLineStatusMap | |||||
const args: StockInLineEntry = { | |||||
const args = { | |||||
id: itemDetail.id, | id: itemDetail.id, | ||||
purchaseOrderId: parseInt(params.get("id")!!), | purchaseOrderId: parseInt(params.get("id")!!), | ||||
purchaseOrderLineId: itemDetail.purchaseOrderLineId, | purchaseOrderLineId: itemDetail.purchaseOrderLineId, | ||||
itemId: itemDetail.itemId, | itemId: itemDetail.itemId, | ||||
acceptedQty: itemDetail.acceptedQty, | |||||
status: "receiving", | |||||
} | |||||
...data, | |||||
acceptedQty: acceptedQty, | |||||
productionDate: productionDate, | |||||
status: status, | |||||
} as StockInLineEntry & ModalFormInput; | |||||
////////////////////////////////////////////////////////////////////// | |||||
console.log(args) | console.log(args) | ||||
// return | |||||
if (hasErrors) { | |||||
setServerError(t("An error has occurred. Please try again later.")); | |||||
return false; | |||||
} | |||||
const res = await updateStockInLine(args) | const res = await updateStockInLine(args) | ||||
// this.res.entity = list of entity | |||||
for (const inLine in res.entity as StockInLine[]) { | |||||
if (Boolean(res.id)) { | |||||
// set entries | |||||
const newEntries = res.entity as StockInLine[] | |||||
setEntries((prev) => { | |||||
const updatedEntries = [...prev]; // Create a new array | |||||
newEntries.forEach((item) => { | |||||
const index = updatedEntries.findIndex(p => p.id === item.id); | |||||
if (index !== -1) { | |||||
// Update existing item | |||||
updatedEntries[index] = item; | |||||
} else { | |||||
// Add new item | |||||
updatedEntries.push(item); | |||||
} | |||||
}); | |||||
return updatedEntries; // Return the new array | |||||
}) | |||||
// add loading | |||||
closeHandler({}, "backdropClick") | |||||
} | } | ||||
console.log(res) | console.log(res) | ||||
@@ -110,7 +175,7 @@ const PoQcModal: React.FC<Props> = ({ | |||||
}, | }, | ||||
[t, itemDetail] | [t, itemDetail] | ||||
); | ); | ||||
return ( | return ( | ||||
<> | <> | ||||
<Modal open={open} onClose={closeHandler}> | <Modal open={open} onClose={closeHandler}> | ||||
@@ -120,7 +185,9 @@ const PoQcModal: React.FC<Props> = ({ | |||||
component="form" | component="form" | ||||
onSubmit={formProps.handleSubmit(onSubmit)} | onSubmit={formProps.handleSubmit(onSubmit)} | ||||
> | > | ||||
{qc && <QcForm qc={qc} itemDetail={itemDetail} />} | |||||
{type === "qc" && <QcForm qc={qc} itemDetail={itemDetail} />} | |||||
{type === "stockIn" && <StockInForm itemDetail={itemDetail} />} | |||||
{type === "escalation" && <EscalationForm itemDetail={itemDetail} />} | |||||
<Stack direction="row" justifyContent="flex-end" gap={1}> | <Stack direction="row" justifyContent="flex-end" gap={1}> | ||||
<Button | <Button | ||||
name="submit" | name="submit" | ||||
@@ -138,4 +205,4 @@ const PoQcModal: React.FC<Props> = ({ | |||||
</> | </> | ||||
); | ); | ||||
}; | }; | ||||
export default PoQcModal; | |||||
export default PoQcStockInModal; |
@@ -0,0 +1,249 @@ | |||||
"use client"; | |||||
import { PurchaseQcCheck, 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, 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 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"; | |||||
interface Props { | |||||
itemDetail: StockInLine; | |||||
qc: QcItemWithChecks[]; | |||||
} | |||||
type EntryError = | |||||
| { | |||||
[field in keyof PurchaseQcCheck]?: string; | |||||
} | |||||
| undefined; | |||||
type PoQcRow = TableRow<Partial<PurchaseQcCheck>, EntryError>; | |||||
const PutawayForm: React.FC<Props> = ({ | |||||
qc, | |||||
itemDetail, | |||||
}) => { | |||||
const { t } = useTranslation(); | |||||
const apiRef = useGridApiRef(); | |||||
const { | |||||
register, | |||||
formState: { errors, defaultValues, touchedFields }, | |||||
watch, | |||||
control, | |||||
setValue, | |||||
getValues, | |||||
reset, | |||||
resetField, | |||||
setError, | |||||
clearErrors, | |||||
} = useFormContext<PurchaseQCInput>(); | |||||
console.log(itemDetail) | |||||
const [recordQty, setRecordQty] = useState(0) | |||||
const columns = useMemo<GridColDef[]>( | |||||
() => [ | |||||
{ | |||||
field: "qcCheckId", | |||||
headerName: "qc Check", | |||||
flex: 1, | |||||
editable: true, | |||||
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.qcCheckId); | |||||
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 PurchaseQcCheck]; | |||||
console.log(errorMessage); | |||||
const content = ( | |||||
<QcSelect | |||||
allQcs={qc} | |||||
value={params.row.qcCheckId} | |||||
onQcSelect={async (qcCheckId) => { | |||||
await params.api.setEditCellValue({ | |||||
id: params.id, | |||||
field: "qcCheckId", | |||||
value: qcCheckId, | |||||
}); | |||||
}} | |||||
/> | |||||
); | |||||
return errorMessage ? ( | |||||
<Tooltip title={t(errorMessage)}> | |||||
<Box width="100%">{content}</Box> | |||||
</Tooltip> | |||||
) : ( | |||||
content | |||||
); | |||||
}, | |||||
}, | |||||
{ | |||||
field: "qty", | |||||
headerName: "qty", | |||||
flex: 1, | |||||
editable: true, | |||||
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 PurchaseQcCheck]; | |||||
const content = <GridEditInputCell {...params} />; | |||||
return errorMessage ? ( | |||||
<Tooltip title={t(errorMessage)}> | |||||
<Box width="100%">{content}</Box> | |||||
</Tooltip> | |||||
) : ( | |||||
content | |||||
); | |||||
}, | |||||
}, | |||||
], | |||||
[] | |||||
); | |||||
const validation = useCallback( | |||||
(newRow: GridRowModel<PoQcRow>): EntryError => { | |||||
const error: EntryError = {}; | |||||
const { qcCheckId, qty } = newRow; | |||||
if (!qcCheckId || qcCheckId <= 0) { | |||||
error["qcCheckId"] = "select qc"; | |||||
} | |||||
if (!qty || qty <= 0) { | |||||
error["qty"] = "enter a qty"; | |||||
} | |||||
if (qty && qty > itemDetail.acceptedQty) { | |||||
error["qty"] = "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={6}> | |||||
<TextField | |||||
label={t("Total qty")} | |||||
fullWidth | |||||
value={itemDetail.acceptedQty} | |||||
disabled | |||||
// {...register("sampleRate", { | |||||
// required: "sampleRate required!", | |||||
// })} | |||||
// error={Boolean(errors.sampleRate)} | |||||
// helperText={errors.sampleRate?.message} | |||||
/> | |||||
</Grid> | |||||
<Grid item xs={6}> | |||||
<TextField | |||||
label={t("Total record qty")} | |||||
fullWidth | |||||
value={recordQty} | |||||
disabled | |||||
// {...register("sampleRate", { | |||||
// required: "sampleRate required!", | |||||
// })} | |||||
// error={Boolean(errors.sampleRate)} | |||||
// helperText={errors.sampleRate?.message} | |||||
/> | |||||
</Grid> | |||||
<Grid item xs={4}> | |||||
<TextField | |||||
label={t("sampleRate")} | |||||
fullWidth | |||||
{...register("sampleRate", { | |||||
required: "sampleRate required!", | |||||
})} | |||||
error={Boolean(errors.sampleRate)} | |||||
helperText={errors.sampleRate?.message} | |||||
/> | |||||
</Grid> | |||||
<Grid item xs={4}> | |||||
<TextField | |||||
label={t("sampleWeight")} | |||||
fullWidth | |||||
{...register("sampleWeight", { | |||||
required: "sampleWeight required!", | |||||
})} | |||||
error={Boolean(errors.sampleWeight)} | |||||
helperText={errors.sampleWeight?.message} | |||||
/> | |||||
</Grid> | |||||
<Grid item xs={4}> | |||||
<TextField | |||||
label={t("totalWeight")} | |||||
fullWidth | |||||
{...register("totalWeight", { | |||||
required: "totalWeight required!", | |||||
})} | |||||
error={Boolean(errors.totalWeight)} | |||||
helperText={errors.totalWeight?.message} | |||||
/> | |||||
</Grid> | |||||
</Grid> | |||||
<Grid | |||||
container | |||||
justifyContent="flex-start" | |||||
alignItems="flex-start" | |||||
spacing={2} | |||||
sx={{ mt: 0.5 }} | |||||
> | |||||
<Grid item xs={12}> | |||||
<InputDataGrid<PurchaseQCInput, PurchaseQcCheck, EntryError> | |||||
apiRef={apiRef} | |||||
checkboxSelection={false} | |||||
_formKey={"qcCheck"} | |||||
columns={columns} | |||||
validateRow={validation} | |||||
/> | |||||
</Grid> | |||||
</Grid> | |||||
</Grid> | |||||
); | |||||
}; | |||||
export default PutawayForm; |
@@ -14,7 +14,7 @@ import { | |||||
import { useFormContext } from "react-hook-form"; | import { useFormContext } from "react-hook-form"; | ||||
import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
import StyledDataGrid from "../StyledDataGrid"; | import StyledDataGrid from "../StyledDataGrid"; | ||||
import { useCallback, useMemo } from "react"; | |||||
import { useCallback, useMemo, useState } from "react"; | |||||
import { | import { | ||||
GridColDef, | GridColDef, | ||||
GridRowIdGetter, | GridRowIdGetter, | ||||
@@ -63,6 +63,7 @@ const QcForm: React.FC<Props> = ({ | |||||
clearErrors, | clearErrors, | ||||
} = useFormContext<PurchaseQCInput>(); | } = useFormContext<PurchaseQCInput>(); | ||||
console.log(itemDetail) | console.log(itemDetail) | ||||
const [recordQty, setRecordQty] = useState(0) | |||||
const columns = useMemo<GridColDef[]>( | const columns = useMemo<GridColDef[]>( | ||||
() => [ | () => [ | ||||
{ | { | ||||
@@ -115,6 +116,10 @@ const QcForm: React.FC<Props> = ({ | |||||
editable: true, | editable: true, | ||||
type: "number", | type: "number", | ||||
renderEditCell(params: GridRenderEditCellParams<PoQcRow>) { | renderEditCell(params: GridRenderEditCellParams<PoQcRow>) { | ||||
// const recordQty = params.row.qty | |||||
// if (recordQty !== undefined) { | |||||
// setUnrecordQty((prev) => prev - recordQty) | |||||
// } | |||||
const errorMessage = | const errorMessage = | ||||
params.row._error?.[params.field as keyof PurchaseQcCheck]; | params.row._error?.[params.field as keyof PurchaseQcCheck]; | ||||
const content = <GridEditInputCell {...params} />; | const content = <GridEditInputCell {...params} />; | ||||
@@ -130,7 +135,7 @@ const QcForm: React.FC<Props> = ({ | |||||
], | ], | ||||
[] | [] | ||||
); | ); | ||||
const validationTest = useCallback( | |||||
const validation = useCallback( | |||||
(newRow: GridRowModel<PoQcRow>): EntryError => { | (newRow: GridRowModel<PoQcRow>): EntryError => { | ||||
const error: EntryError = {}; | const error: EntryError = {}; | ||||
const { qcCheckId, qty } = newRow; | const { qcCheckId, qty } = newRow; | ||||
@@ -140,6 +145,9 @@ const QcForm: React.FC<Props> = ({ | |||||
if (!qty || qty <= 0) { | if (!qty || qty <= 0) { | ||||
error["qty"] = "enter a qty"; | error["qty"] = "enter a qty"; | ||||
} | } | ||||
if (qty && qty > itemDetail.acceptedQty) { | |||||
error["qty"] = "qty too big"; | |||||
} | |||||
return Object.keys(error).length > 0 ? error : undefined; | return Object.keys(error).length > 0 ? error : undefined; | ||||
}, | }, | ||||
[] | [] | ||||
@@ -158,6 +166,32 @@ const QcForm: React.FC<Props> = ({ | |||||
spacing={2} | spacing={2} | ||||
sx={{ mt: 0.5 }} | sx={{ mt: 0.5 }} | ||||
> | > | ||||
<Grid item xs={6}> | |||||
<TextField | |||||
label={t("Total qty")} | |||||
fullWidth | |||||
value={itemDetail.acceptedQty} | |||||
disabled | |||||
// {...register("sampleRate", { | |||||
// required: "sampleRate required!", | |||||
// })} | |||||
// error={Boolean(errors.sampleRate)} | |||||
// helperText={errors.sampleRate?.message} | |||||
/> | |||||
</Grid> | |||||
<Grid item xs={6}> | |||||
<TextField | |||||
label={t("Total record qty")} | |||||
fullWidth | |||||
value={recordQty} | |||||
disabled | |||||
// {...register("sampleRate", { | |||||
// required: "sampleRate required!", | |||||
// })} | |||||
// error={Boolean(errors.sampleRate)} | |||||
// helperText={errors.sampleRate?.message} | |||||
/> | |||||
</Grid> | |||||
<Grid item xs={4}> | <Grid item xs={4}> | ||||
<TextField | <TextField | ||||
label={t("sampleRate")} | label={t("sampleRate")} | ||||
@@ -205,7 +239,7 @@ const QcForm: React.FC<Props> = ({ | |||||
checkboxSelection={false} | checkboxSelection={false} | ||||
_formKey={"qcCheck"} | _formKey={"qcCheck"} | ||||
columns={columns} | columns={columns} | ||||
validateRow={validationTest} | |||||
validateRow={validation} | |||||
/> | /> | ||||
</Grid> | </Grid> | ||||
</Grid> | </Grid> | ||||
@@ -0,0 +1,168 @@ | |||||
"use client"; | |||||
import { PurchaseQcCheck, PurchaseQCInput, StockInInput } 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, 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"; | |||||
// change PurchaseQcCheck to stock in entry props | |||||
interface Props { | |||||
itemDetail: StockInLine; | |||||
// qc: QcItemWithChecks[]; | |||||
} | |||||
type EntryError = | |||||
| { | |||||
[field in keyof StockInInput]?: string; | |||||
} | |||||
| undefined; | |||||
// type PoQcRow = TableRow<Partial<PurchaseQcCheck>, EntryError>; | |||||
const StockInForm: React.FC<Props> = ({ | |||||
// qc, | |||||
itemDetail, | |||||
}) => { | |||||
const { t } = useTranslation(); | |||||
const apiRef = useGridApiRef(); | |||||
const { | |||||
register, | |||||
formState: { errors, defaultValues, touchedFields }, | |||||
watch, | |||||
control, | |||||
setValue, | |||||
getValues, | |||||
reset, | |||||
resetField, | |||||
setError, | |||||
clearErrors, | |||||
} = useFormContext<StockInInput>(); | |||||
console.log(itemDetail) | |||||
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={4}> | |||||
<TextField | |||||
label={t("productLotNo")} | |||||
fullWidth | |||||
{...register("productLotNo", { | |||||
// required: "productLotNo required!", | |||||
})} | |||||
// error={Boolean(errors.productLotNo)} | |||||
// helperText={errors.productLotNo?.message} | |||||
/> | |||||
</Grid> | |||||
<Grid item xs={4}> | |||||
<TextField | |||||
label={t("receiptDate")} | |||||
fullWidth | |||||
{...register("receiptDate", { | |||||
required: "receiptDate required!", | |||||
})} | |||||
error={Boolean(errors.receiptDate)} | |||||
helperText={errors.receiptDate?.message} | |||||
/> | |||||
</Grid> | |||||
<Grid item xs={4}> | |||||
<TextField | |||||
label={t("acceptedQty")} | |||||
fullWidth | |||||
{...register("acceptedQty", { | |||||
required: "acceptedQty required!", | |||||
})} | |||||
error={Boolean(errors.acceptedQty)} | |||||
helperText={errors.acceptedQty?.message} | |||||
/> | |||||
</Grid> | |||||
<Grid item xs={4}> | |||||
<TextField | |||||
label={t("acceptedWeight")} | |||||
fullWidth | |||||
// {...register("acceptedWeight", { | |||||
// required: "acceptedWeight required!", | |||||
// })} | |||||
error={Boolean(errors.acceptedWeight)} | |||||
helperText={errors.acceptedWeight?.message} | |||||
/> | |||||
</Grid> | |||||
<Grid item xs={4}> | |||||
<TextField | |||||
label={t("productionDate")} | |||||
fullWidth | |||||
{...register("productionDate", { | |||||
// required: "productionDate required!", | |||||
})} | |||||
// error={Boolean(errors.productionDate)} | |||||
// helperText={errors.productionDate?.message} | |||||
/> | |||||
</Grid> | |||||
<Grid item xs={4}> | |||||
<TextField | |||||
label={t("expiryDate")} | |||||
fullWidth | |||||
{...register("expiryDate", { | |||||
required: "expiryDate required!", | |||||
})} | |||||
error={Boolean(errors.expiryDate)} | |||||
helperText={errors.expiryDate?.message} | |||||
/> | |||||
</Grid> | |||||
</Grid> | |||||
<Grid | |||||
container | |||||
justifyContent="flex-start" | |||||
alignItems="flex-start" | |||||
spacing={2} | |||||
sx={{ mt: 0.5 }} | |||||
> | |||||
{/* <Grid item xs={12}> | |||||
<InputDataGrid<PurchaseQCInput, PurchaseQcCheck, EntryError> | |||||
apiRef={apiRef} | |||||
checkboxSelection={false} | |||||
_formKey={"qcCheck"} | |||||
columns={columns} | |||||
validateRow={validationTest} | |||||
/> | |||||
</Grid> */} | |||||
</Grid> | |||||
</Grid> | |||||
); | |||||
}; | |||||
export default StockInForm; |