Bläddra i källkod

update po flow & warehouse api

create_edit_user
MSI\derek 3 månader sedan
förälder
incheckning
026d135058
14 ändrade filer med 371 tillägg och 300 borttagningar
  1. +8
    -2
      src/app/api/po/actions.ts
  2. +1
    -0
      src/app/api/po/index.ts
  3. +15
    -0
      src/app/api/qc/actions.ts
  4. +21
    -3
      src/app/api/qc/index.ts
  5. +2
    -2
      src/app/api/tasks/index.ts
  6. +17
    -0
      src/app/api/warehouse/index.ts
  7. +8
    -1
      src/components/PoDetail/EscalationForm.tsx
  8. +28
    -23
      src/components/PoDetail/PoDetail.tsx
  9. +12
    -23
      src/components/PoDetail/PoDetailWrapper.tsx
  10. +125
    -80
      src/components/PoDetail/PoInputGrid.tsx
  11. +17
    -20
      src/components/PoDetail/PoQcStockInModal.tsx
  12. +72
    -133
      src/components/PoDetail/PutawayForm.tsx
  13. +38
    -12
      src/components/PoDetail/QcForm.tsx
  14. +7
    -1
      src/components/PoDetail/StockInForm.tsx

+ 8
- 2
src/app/api/po/actions.ts Visa fil

@@ -31,6 +31,7 @@ export interface PurchaseQcCheck {
qty: number;
}
export interface StockInInput {
status: string
productLotNo?: string,
receiptDate: string
acceptedQty: number
@@ -39,18 +40,23 @@ export interface StockInInput {
expiryDate: string
}
export interface PurchaseQCInput {
status: string
sampleRate: number;
sampleWeight: number;
totalWeight: number;
qcCheck: PurchaseQcCheck[];
}
export interface EscalationInput {
status: string
handler: string
stockInLine: StockInLineEntry[]
}
export interface PutawayInput {
handler: string
stockInLine: StockInLineEntry[]
status: string
acceptedQty: number
warehouseId: number
// handler: string
// stockInLine: StockInLineEntry[]
}

export type ModalFormInput = Partial<PurchaseQCInput & StockInInput & EscalationInput & PutawayInput>


+ 1
- 0
src/app/api/po/index.ts Visa fil

@@ -20,6 +20,7 @@ export interface PurchaseOrderLine {
itemNo: string
itemName: string
qty: number
processed: number
uom?: string
price: number
status: string


+ 15
- 0
src/app/api/qc/actions.ts Visa fil

@@ -0,0 +1,15 @@
"use server";
import { BASE_API_URL } from "@/config/api";
import { revalidateTag } from "next/cache";
import { cache } from "react";
import { serverFetchJson } from "@/app/utils/fetchUtil";
import { QcItemWithChecks } from ".";

export const fetchQcItemCheck = cache(async (itemId?: number) => {
var url = `${BASE_API_URL}/qcCheck`
if (itemId) url +=`/${itemId}`
return serverFetchJson<QcItemWithChecks[]>(url, {
next: { tags: ["qc"] },
});
});

+ 21
- 3
src/app/api/qc/index.ts Visa fil

@@ -1,12 +1,30 @@
import { cache } from "react";
import "server-only";
import { serverFetchJson } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";

export interface QcItemWithChecks {
id: number;
code: string;
name: string;
itemId: number;
lowerLimit: number;
upperLimit: number;
description: string;
lowerLimit: number | undefined;
upperLimit: number | undefined;
description: string | undefined;
}

export const fetchQcItemCheckList = cache(async () => {
return serverFetchJson<QcItemWithChecks[]>(`${BASE_API_URL}/qc/list`, {
next: { tags: ["qc"] },
});
});


export const fetchQcItemCheck = cache(async (itemId?: number) => {
var url = `${BASE_API_URL}/qcCheck`
if (itemId) url +=`/${itemId}`
return serverFetchJson<QcItemWithChecks[]>(url, {
next: { tags: ["qc"] },
});
});

+ 2
- 2
src/app/api/tasks/index.ts Visa fil

@@ -1,7 +1,7 @@
import { serverFetchJson } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";
import { cache } from "react";
import "server-only";
import { serverFetchJson } from "src/app/utils/fetchUtil";
import { BASE_API_URL } from "src/config/api";

export interface TaskGroup {
id: number;


+ 17
- 0
src/app/api/warehouse/index.ts Visa fil

@@ -0,0 +1,17 @@
import { cache } from "react";
import "server-only";
import { serverFetchJson } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";

export interface WarehouseResult {
id: number
code: string
name: string
description: string
}

export const fetchWarehouseList = cache(async () => {
return serverFetchJson<WarehouseResult[]>(`${BASE_API_URL}/warehouse`, {
next: { tags: ["warehouse"] },
});
});

+ 8
- 1
src/components/PoDetail/EscalationForm.tsx Visa fil

@@ -14,7 +14,7 @@ import {
import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import StyledDataGrid from "../StyledDataGrid";
import { useCallback, useMemo } from "react";
import { useCallback, useEffect, useMemo } from "react";
import {
GridColDef,
GridRowIdGetter,
@@ -31,6 +31,7 @@ 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;
@@ -144,6 +145,12 @@ const EscalationForm: React.FC<Props> = ({
},
[]
);
useEffect(() => {
console.log("triggered")
// setValue("status", stockInLineStatusMap.determine1.key)
}, [])

return (
<Grid container justifyContent="flex-start" alignItems="flex-start">
<Grid item xs={12}>


+ 28
- 23
src/components/PoDetail/PoDetail.tsx Visa fil

@@ -1,6 +1,11 @@
"use client";

import { fetchPoWithStockInLines, PoResult, PurchaseOrderLine, StockInLine } from "@/app/api/po";
import {
fetchPoWithStockInLines,
PoResult,
PurchaseOrderLine,
StockInLine,
} from "@/app/api/po";
import {
Box,
Button,
@@ -35,10 +40,12 @@ import InputDataGrid, {
import PoInputGrid from "./PoInputGrid";
import { QcItemWithChecks } from "@/app/api/qc";
import { useSearchParams } from "next/navigation";
import { WarehouseResult } from "@/app/api/warehouse";

type Props = {
po: PoResult;
qc: QcItemWithChecks[]
qc: QcItemWithChecks[];
warehouse: WarehouseResult[];
};

type EntryError =
@@ -48,20 +55,17 @@ type EntryError =
| undefined;

// type PolRow = TableRow<Partial<StockInLine>, EntryError>;
const PoDetail: React.FC<Props> = ({
po,
// poLine,
qc
}) => {
const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
const { t } = useTranslation();
const apiRef = useGridApiRef();
const [rows, setRows] = useState<PurchaseOrderLine[]>(po.pol || []);
const params = useSearchParams()
const params = useSearchParams();

function Row(props: { row: PurchaseOrderLine }) {
const { row } = props;
const [open, setOpen] = useState(false);
const [processedQty, setProcessedQty] = useState(row.processed);

return (
<>
<TableRow sx={{ "& > *": { borderBottom: "unset" }, color: "black" }}>
@@ -77,30 +81,30 @@ const PoDetail: React.FC<Props> = ({
<TableCell align="left">{row.itemNo}</TableCell>
<TableCell align="left">{row.itemName}</TableCell>
<TableCell align="left">{row.qty}</TableCell>
<TableCell align="left">{processedQty}</TableCell>
<TableCell align="left">{row.uom}</TableCell>
<TableCell align="left">{row.price}</TableCell>
{/* <TableCell align="left">{row.expiryDate}</TableCell> */}
<TableCell align="left">{row.status}</TableCell>
</TableRow>
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
{/* <TableCell /> */}
<TableCell style={{ paddingBottom: 0, paddingTop: 0}} colSpan={12}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Table>
<TableBody>
<TableRow>
{/* <Button
onClick={()=> {
console.log(row)
console.log(row.stockInLine)
}}
>console log</Button> */}
<TableCell>
<PoInputGrid
qc={qc}
setRows={setRows}
itemDetail={row}
stockInLine={row.stockInLine}
/>
<TableCell align="right">
<Box>
<PoInputGrid
qc={qc}
setRows={setRows}
setProcessedQty={setProcessedQty}
itemDetail={row}
stockInLine={row.stockInLine}
warehouse={warehouse}
/>
</Box>
</TableCell>
</TableRow>
</TableBody>
@@ -133,6 +137,7 @@ const PoDetail: React.FC<Props> = ({
<TableCell>{t("itemNo")}</TableCell>
<TableCell align="left">{t("itemName")}</TableCell>
<TableCell align="left">{t("qty")}</TableCell>
<TableCell align="left">processed</TableCell>
<TableCell align="left">{t("uom")}</TableCell>
<TableCell align="left">{t("price")}</TableCell>
{/* <TableCell align="left">{t("expiryDate")}</TableCell> */}


+ 12
- 23
src/components/PoDetail/PoDetailWrapper.tsx Visa fil

@@ -8,6 +8,8 @@ import { fetchPoWithStockInLines, PoResult } from "@/app/api/po";
import PoDetailLoading from "./PoDetailLoading";
import PoDetail from "./PoDetail";
import { QcItemWithChecks } from "@/app/api/qc";
import { fetchWarehouseList } from "@/app/api/warehouse";
import { fetchQcItemCheck } from "@/app/api/qc/actions";

interface SubComponents {
Loading: typeof PoDetailLoading;
@@ -19,35 +21,22 @@ type Props = {

const PoDetailWrapper: React.FC<Props> & SubComponents = async ({ id }) => {
const [
poWithStockInLine
poWithStockInLine,
warehouse,
qc,
] = await Promise.all([
fetchPoWithStockInLines(id)
fetchPoWithStockInLines(id),
fetchWarehouseList(),
fetchQcItemCheck()
])
// const poWithStockInLine = await fetchPoWithStockInLines(id)
console.log(poWithStockInLine)
const qc: QcItemWithChecks[] = [ // just qc
{
id: 1,
code: "code1",
name: "name1",
itemId: 1,
lowerLimit: 1,
upperLimit: 3,
description: 'desc',
},
{
id: 2,
code: "code2",
name: "name2",
itemId: 1,
lowerLimit: 1,
upperLimit: 3,
description: 'desc',
},
]
console.log(warehouse)
console.log(qc)


return <PoDetail po={poWithStockInLine} qc={qc} />;
return <PoDetail po={poWithStockInLine} qc={qc} warehouse={warehouse}/>;
};

PoDetailWrapper.Loading = PoDetailLoading;


+ 125
- 80
src/components/PoDetail/PoInputGrid.tsx Visa fil

@@ -37,7 +37,8 @@ import { createStockInLine, testFetch } from "@/app/api/po/actions";
import { useSearchParams } from "next/navigation";
import { stockInLineStatusMap } from "@/app/utils/formatUtil";
import PoQcStockInModal from "./PoQcStockInModal";
import NotificationImportantIcon from '@mui/icons-material/NotificationImportant';
import NotificationImportantIcon from "@mui/icons-material/NotificationImportant";
import { WarehouseResult } from "@/app/api/warehouse";
interface ResultWithId {
id: number;
}
@@ -45,8 +46,10 @@ interface ResultWithId {
interface Props {
qc: QcItemWithChecks[];
setRows: Dispatch<SetStateAction<PurchaseOrderLine[]>>;
setProcessedQty: Dispatch<SetStateAction<number>>;
itemDetail: PurchaseOrderLine;
stockInLine: StockInLine[];
warehouse: WarehouseResult[];
}

export type StockInLineEntryError = {
@@ -64,7 +67,11 @@ export type StockInLineRow = Partial<
class ProcessRowUpdateError extends Error {
public readonly row: StockInLineRow;
public readonly errors: StockInLineEntryError | undefined;
constructor(row: StockInLineRow, message?: string, errors?: StockInLineEntryError) {
constructor(
row: StockInLineRow,
message?: string,
errors?: StockInLineEntryError
) {
super(message);
this.row = row;
this.errors = errors;
@@ -73,7 +80,14 @@ class ProcessRowUpdateError extends Error {
}
}

function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) {
function PoInputGrid({
qc,
setRows,
setProcessedQty,
itemDetail,
stockInLine,
warehouse,
}: Props) {
const { t } = useTranslation("home");
const apiRef = useGridApiRef();
const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});
@@ -83,25 +97,32 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) {
);
console.log(stockInLine);
const [entries, setEntries] = useState<StockInLineRow[]>(stockInLine || []);
const [modalInfo, setModalInfo] = useState<StockInLine>()
const [modalInfo, setModalInfo] = useState<StockInLine>();
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 total = entries.reduce((acc, curr) => acc + (curr.acceptedQty || 0), 0);
return itemDetail.qty - total;
const [currQty, setCurrQty] = useState(() => {
const total = entries.reduce(
(acc, curr) => acc + (curr.acceptedQty || 0),
0
);
return total;
});

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])
useEffect(() => {
}, [])
useEffect(() => {
const completedList = entries.filter(
(e) => e.status === stockInLineStatusMap.completed.key
);
const processedQty = completedList.reduce(
(acc, curr) => acc + (curr.acceptedQty || 0),
0
);
setProcessedQty(processedQty);
}, [entries]);

const handleDelete = useCallback(
(id: GridRowId) => () => {
@@ -119,8 +140,8 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) {
// post stock in line
console.log("delayed");
console.log(params);
const oldId = params.row.id
console.log(oldId)
const oldId = params.row.id;
console.log(oldId);
const postData = {
itemId: params.row.itemId,
itemNo: params.row.itemNo,
@@ -128,10 +149,12 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) {
purchaseOrderId: params.row.purchaseOrderId,
purchaseOrderLineId: params.row.purchaseOrderLineId,
acceptedQty: params.row.acceptedQty,
}
const res = await createStockInLine(postData)
console.log(res)
setEntries((prev) => prev.map((p) => p.id === oldId ? res.entity as StockInLine : p))
};
const res = await createStockInLine(postData);
console.log(res);
setEntries((prev) =>
prev.map((p) => (p.id === oldId ? (res.entity as StockInLine) : p))
);
// do post directly to test
// openStartModal();
}, 200);
@@ -144,7 +167,7 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) {
...prev,
[id]: { mode: GridRowModes.View },
}));
setModalInfo(params.row)
setModalInfo(params.row);
setTimeout(() => {
// open qc modal
console.log("delayed");
@@ -153,31 +176,31 @@ 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 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(
(id: GridRowId, params: any) => () => {
setRowModesModel((prev) => ({
...prev,
[id]: { mode: GridRowModes.View },
}));
setModalInfo(params.row)
setModalInfo(params.row);
setTimeout(() => {
// open stock in modal
openStockInModal()
openStockInModal();
// return the record with its status as pending
// update layout
console.log("delayed");
@@ -192,10 +215,10 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) {
...prev,
[id]: { mode: GridRowModes.View },
}));
setModalInfo(params.row)
setModalInfo(params.row);
setTimeout(() => {
// open stock in modal
openPutAwayModal()
openPutAwayModal();
// return the record with its status as pending
// update layout
console.log("delayed");
@@ -236,7 +259,7 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) {
() => [
{
field: "itemNo",
flex: .8,
flex: 0.8,
},
{
field: "itemName",
@@ -253,24 +276,24 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) {
{
field: "status",
flex: 0.5,
editable: true
// editable: true,
},
{
field: "actions",
type: "actions",
headerName: "start | qc | escalate | stock in | putaway | delete",
headerName: "start | qc | stock in | putaway | delete",
flex: 1.5,
cellClassName: "actions",
getActions: (params) => {
console.log(params.row.status);
const status = params.row.status.toLowerCase()
const status = params.row.status.toLowerCase();
return [
<GridActionsCellItem
icon={<PlayArrowIcon />}
label="start"
sx={{
color: "primary.main",
marginRight: 2
marginRight: 2,
}}
disabled={!(stockInLineStatusMap[status].value === 0)}
// set _isNew to false after posting
@@ -284,35 +307,41 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) {
label="qc"
sx={{
color: "primary.main",
marginRight: 2
marginRight: 2,
}}
disabled={stockInLineStatusMap[status].value <= 0 || stockInLineStatusMap[status].value >= 5}
disabled={
stockInLineStatusMap[status].value <= 0 ||
stockInLineStatusMap[status].value >= 5
}
// set _isNew to false after posting
// or check status
onClick={handleQC(params.row.id, params)}
color="inherit"
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
// 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
icon={<ShoppingCartIcon />}
label="stockin"
sx={{
color: "primary.main",
marginRight: 2
marginRight: 2,
}}
disabled={stockInLineStatusMap[status].value !== 6}
// set _isNew to false after posting
@@ -326,7 +355,7 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) {
label="putaway"
sx={{
color: "primary.main",
marginRight: 2
marginRight: 2,
}}
disabled={stockInLineStatusMap[status].value !== 7}
// set _isNew to false after posting
@@ -363,7 +392,7 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) {
purchaseOrderLineId: itemDetail.id,
itemNo: itemDetail.itemNo,
itemName: itemDetail.itemName,
acceptedQty: defaultQty,
acceptedQty: itemDetail.qty - currQty, // this bug
status: "draft",
};
setEntries((e) => [...e, newEntry]);
@@ -374,7 +403,7 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) {
// fieldToFocus: "projectId",
},
}));
}, [getRowId]);
}, [currQty, getRowId]);
const validation = useCallback(
(
newRow: GridRowModel<StockInLineRow>
@@ -382,16 +411,19 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) {
): StockInLineEntryError | undefined => {
const error: StockInLineEntryError = {};
console.log(newRow);
console.log(defaultQty);
if (newRow.acceptedQty && newRow.acceptedQty > defaultQty) {
console.log(currQty);
if (newRow.acceptedQty && newRow.acceptedQty > itemDetail.qty) {
error["acceptedQty"] = "qty cannot be greater than remaining qty";
}
return Object.keys(error).length > 0 ? error : undefined;
},
[defaultQty]
[currQty]
);
const processRowUpdate = useCallback(
(newRow: GridRowModel<StockInLineRow>, originalRow: GridRowModel<StockInLineRow>) => {
(
newRow: GridRowModel<StockInLineRow>,
originalRow: GridRowModel<StockInLineRow>
) => {
const errors = validation(newRow); // change to validation
if (errors) {
throw new ProcessRowUpdateError(
@@ -409,8 +441,11 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) {
);
setEntries(newEntries);
//update remaining qty
const total = newEntries.reduce((acc, curr) => acc + (curr.acceptedQty || 0), 0);
setDefaultQty(itemDetail.qty - total);
const total = newEntries.reduce(
(acc, curr) => acc + (curr.acceptedQty || 0),
0
);
setCurrQty(total);
return rowToSave;
},
[getRowId, entries]
@@ -426,17 +461,20 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) {
[apiRef]
);

useEffect(() => {
const total = entries.reduce((acc, curr) => acc + (curr.acceptedQty || 0), 0);
setDefaultQty(itemDetail.qty - total);
}, [entries]);
// useEffect(() => {
// const total = entries.reduce(
// (acc, curr) => acc + (curr.acceptedQty || 0),
// 0
// );
// setDefaultQty(itemDetail.qty - total);
// }, [entries]);
const footer = (
<Box display="flex" gap={2} alignItems="center">
<Button
disableRipple
variant="outlined"
startIcon={<Add />}
disabled={defaultQty <= 0}
disabled={itemDetail.qty - currQty <= 0}
onClick={addRow}
size="small"
>
@@ -469,6 +507,13 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) {
processRowUpdate={processRowUpdate}
onProcessRowUpdateError={onProcessRowUpdateError}
columns={columns}
isCellEditable={(params) => {
const status = params.row.status.toLowerCase();
return (
stockInLineStatusMap[status].value >= 0 ||
stockInLineStatusMap[status].value <= 1
);
}}
getCellClassName={(params: GridCellParams<StockInLineRow>) => {
let classname = "";
if (params.row._error) {
@@ -512,14 +557,14 @@ function PoInputGrid({ qc, setRows, itemDetail, stockInLine }: Props) {
open={stockInOpen}
onClose={closeStockInModal}
itemDetail={modalInfo!!}
/>
/>
</>
<>
<PoQcStockInModal
type={"putaway"}
setEntries={setEntries}
open={putAwayOpen}
warehouse={[]}
warehouse={warehouse}
onClose={closePutAwayModal}
itemDetail={modalInfo!!}
/>


+ 17
- 20
src/components/PoDetail/PoQcStockInModal.tsx Visa fil

@@ -14,6 +14,7 @@ import { stockInLineStatusMap } from "@/app/utils/formatUtil";
import { StockInLineRow } from "./PoInputGrid";
import EscalationForm from "./EscalationForm";
import StockInForm from "./StockInForm";
import PutawayForm from "./PutawayForm";

interface CommonProps extends Omit<ModalProps, "children"> {
setEntries: Dispatch<SetStateAction<StockInLineRow[]>>
@@ -86,20 +87,20 @@ const PoQcStockInModal: React.FC<Props> = ({
}, []);

// 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 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) => {
@@ -108,10 +109,6 @@ const PoQcStockInModal: React.FC<Props> = ({
console.log(data);
console.log(itemDetail);
try {
console.log(type)
var status = getPostingStatus(type)
// if escalation, take data.status as status
console.log(status)
// add checking
// const qty = data.sampleRate
@@ -134,11 +131,10 @@ const PoQcStockInModal: React.FC<Props> = ({
...data,
acceptedQty: acceptedQty,
productionDate: productionDate,
status: status,
} as StockInLineEntry & ModalFormInput;
//////////////////////////////////////////////////////////////////////
console.log(args)
// return
return
if (hasErrors) {
setServerError(t("An error has occurred. Please try again later."));
return false;
@@ -188,6 +184,7 @@ const PoQcStockInModal: React.FC<Props> = ({
{type === "qc" && <QcForm qc={qc} itemDetail={itemDetail} />}
{type === "stockIn" && <StockInForm itemDetail={itemDetail} />}
{type === "escalation" && <EscalationForm itemDetail={itemDetail} />}
{type === "putaway" && <PutawayForm itemDetail={itemDetail} warehouse={warehouse} />}
<Stack direction="row" justifyContent="flex-end" gap={1}>
<Button
name="submit"


+ 72
- 133
src/components/PoDetail/PutawayForm.tsx Visa fil

@@ -1,10 +1,12 @@
"use client";

import { PurchaseQcCheck, PurchaseQCInput } from "@/app/api/po/actions";
import { PurchaseQcCheck, PutawayInput } from "@/app/api/po/actions";
import {
Autocomplete,
Box,
Card,
CardContent,
FormControl,
Grid,
Stack,
TextField,
@@ -14,7 +16,7 @@ import {
import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import StyledDataGrid from "../StyledDataGrid";
import { useCallback, useMemo, useState } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import {
GridColDef,
GridRowIdGetter,
@@ -31,10 +33,13 @@ import QcSelect from "./QcSelect";
import { QcItemWithChecks } from "@/app/api/qc";
import { GridEditInputCell } from "@mui/x-data-grid";
import { StockInLine } from "@/app/api/po";
import { WarehouseResult } from "@/app/api/warehouse";
import { stockInLineStatusMap } from "@/app/utils/formatUtil";

interface Props {
itemDetail: StockInLine;
qc: QcItemWithChecks[];
warehouse: WarehouseResult[];
// qc: QcItemWithChecks[];
}
type EntryError =
| {
@@ -42,12 +47,9 @@ type EntryError =
}
| undefined;

type PoQcRow = TableRow<Partial<PurchaseQcCheck>, EntryError>;
// type PoQcRow = TableRow<Partial<PurchaseQcCheck>, EntryError>;

const PutawayForm: React.FC<Props> = ({
qc,
itemDetail,
}) => {
const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse }) => {
const { t } = useTranslation();
const apiRef = useGridApiRef();
const {
@@ -61,97 +63,52 @@ const PutawayForm: React.FC<Props> = ({
resetField,
setError,
clearErrors,
} = useFormContext<PurchaseQCInput>();
console.log(itemDetail)
const [recordQty, setRecordQty] = useState(0)
const columns = useMemo<GridColDef[]>(
() => [
} = useFormContext<PutawayInput>();
console.log(itemDetail);
const [recordQty, setRecordQty] = useState(0);
const [warehouseId, setWarehouseId] = useState(0);
const filteredWarehouse = useMemo(() => {
// do filtering here if any
return warehouse;
}, []);
const options = useMemo(() => {
return [
{
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
);
},
value: -1, // think think sin
label: t("Select warehouse"),
group: "default",
},
],
[]
);
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;
...filteredWarehouse.map((w) => ({
value: w.id,
label: `${w.code} - ${w.name}`,
group: "existing",
})),
];
}, [filteredWarehouse]);
const currentValue =
warehouseId > 0
? options.find((o) => o.value === warehouseId)
: options.find((o) => o.value === getValues("warehouseId")) || options[0];

const onChange = useCallback(
(
event: React.SyntheticEvent,
newValue: { value: number; group: string } | { value: number }[]
) => {
const singleNewVal = newValue as {
value: number;
group: string;
};
console.log(singleNewVal);
setValue("warehouseId", singleNewVal.value);
setWarehouseId(singleNewVal.value);
},
[]
);

useEffect(() => {
setValue("status", stockInLineStatusMap.completed.key)
}, [])
return (
<Grid container justifyContent="flex-start" alignItems="flex-start">
<Grid item xs={12}>
@@ -172,58 +129,40 @@ const PutawayForm: React.FC<Props> = ({
fullWidth
value={itemDetail.acceptedQty}
disabled
// {...register("sampleRate", {
// required: "sampleRate required!",
// })}
// error={Boolean(errors.sampleRate)}
// helperText={errors.sampleRate?.message}
/>
</Grid>
</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")}
label={t("acceptedQty")}
fullWidth
{...register("sampleWeight", {
required: "sampleWeight required!",
{...register("acceptedQty", {
required: "acceptedQty required!",
})}
error={Boolean(errors.sampleWeight)}
helperText={errors.sampleWeight?.message}
error={Boolean(errors.acceptedQty)}
helperText={errors.acceptedQty?.message}
/>
</Grid>
<Grid item xs={4}>
<TextField
label={t("totalWeight")}
fullWidth
{...register("totalWeight", {
required: "totalWeight required!",
})}
error={Boolean(errors.totalWeight)}
helperText={errors.totalWeight?.message}
/>
<FormControl fullWidth>
<Autocomplete
noOptionsText={t("No Warehouse")}
disableClearable
fullWidth
value={currentValue}
onChange={onChange}
getOptionLabel={(option) => option.label}
options={options}
renderInput={(params) => <TextField {...params} />}
/>
</FormControl>
</Grid>
</Grid>
<Grid
@@ -233,15 +172,15 @@ const PutawayForm: React.FC<Props> = ({
spacing={2}
sx={{ mt: 0.5 }}
>
<Grid item xs={12}>
<InputDataGrid<PurchaseQCInput, PurchaseQcCheck, EntryError>
{/* <Grid item xs={12}>
<InputDataGrid<PutawayInput, PurchaseQcCheck, EntryError>
apiRef={apiRef}
checkboxSelection={false}
_formKey={"qcCheck"}
columns={columns}
validateRow={validation}
/>
</Grid>
</Grid> */}
</Grid>
</Grid>
);


+ 38
- 12
src/components/PoDetail/QcForm.tsx Visa fil

@@ -14,7 +14,7 @@ import {
import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import StyledDataGrid from "../StyledDataGrid";
import { useCallback, useMemo, useState } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import {
GridColDef,
GridRowIdGetter,
@@ -28,9 +28,13 @@ 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";
import { fetchQcItemCheck } 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";

interface Props {
itemDetail: StockInLine;
@@ -43,11 +47,8 @@ type EntryError =
| undefined;

type PoQcRow = TableRow<Partial<PurchaseQcCheck>, EntryError>;

const QcForm: React.FC<Props> = ({
qc,
itemDetail,
}) => {
// fetchQcItemCheck
const QcForm: React.FC<Props> = ({ qc, itemDetail }) => {
const { t } = useTranslation();
const apiRef = useGridApiRef();
const {
@@ -62,8 +63,21 @@ const QcForm: React.FC<Props> = ({
setError,
clearErrors,
} = useFormContext<PurchaseQCInput>();
console.log(itemDetail)
const [recordQty, setRecordQty] = useState(0)
console.log(itemDetail);

// const [qc, setQc] = useState<QcItemWithChecks[]>([])
// const fetchQcCheck = useCallback(async () => {
// const params = {
// itemId: itemDetail.itemId
// }
// console.log(params)
// const res = await axios.get<QcItemWithChecks[]>(`${NEXT_PUBLIC_API_URL}/qcCheck`, { params })
// console.log(res)
// }, [axios])
// useEffect(() => {
// fetchQcCheck()
// }, [fetchQcCheck])
const [recordQty, setRecordQty] = useState(0);
const columns = useMemo<GridColDef[]>(
() => [
{
@@ -133,7 +147,7 @@ const QcForm: React.FC<Props> = ({
},
},
],
[]
[qc]
);
const validation = useCallback(
(newRow: GridRowModel<PoQcRow>): EntryError => {
@@ -152,6 +166,18 @@ const QcForm: React.FC<Props> = ({
},
[]
);
useEffect(() => {
console.log(itemDetail)
var status = stockInLineStatusMap.receiving.key
// switch (itemDetail.status) {
// case 'pending':
// status = "receiving"
// break;
// }
setValue("status", status)
}, [itemDetail])

return (
<Grid container justifyContent="flex-start" alignItems="flex-start">
<Grid item xs={12}>
@@ -178,7 +204,7 @@ const QcForm: React.FC<Props> = ({
// error={Boolean(errors.sampleRate)}
// helperText={errors.sampleRate?.message}
/>
</Grid>
</Grid>
<Grid item xs={6}>
<TextField
label={t("Total record qty")}
@@ -191,7 +217,7 @@ const QcForm: React.FC<Props> = ({
// error={Boolean(errors.sampleRate)}
// helperText={errors.sampleRate?.message}
/>
</Grid>
</Grid>
<Grid item xs={4}>
<TextField
label={t("sampleRate")}


+ 7
- 1
src/components/PoDetail/StockInForm.tsx Visa fil

@@ -14,7 +14,7 @@ import {
import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import StyledDataGrid from "../StyledDataGrid";
import { useCallback, useMemo } from "react";
import { useCallback, useEffect, useMemo } from "react";
import {
GridColDef,
GridRowIdGetter,
@@ -31,6 +31,7 @@ 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";
// change PurchaseQcCheck to stock in entry props
interface Props {
itemDetail: StockInLine;
@@ -64,6 +65,11 @@ const StockInForm: React.FC<Props> = ({
} = useFormContext<StockInInput>();
console.log(itemDetail)

useEffect(() => {
console.log("triggered")
setValue("status", stockInLineStatusMap.received.key)
}, [])

return (
<Grid container justifyContent="flex-start" alignItems="flex-start">
<Grid item xs={12}>


Laddar…
Avbryt
Spara