Browse Source

update escalation

master
kelvinsuen 1 day ago
parent
commit
cd3cb79061
15 changed files with 327 additions and 150 deletions
  1. +1
    -1
      src/app/(main)/po/edit/page.tsx
  2. +21
    -0
      src/app/api/escalation/actions.ts
  3. +0
    -13
      src/app/api/escalation/index.ts
  4. +11
    -7
      src/app/api/po/actions.ts
  5. +1
    -0
      src/app/api/po/index.ts
  6. +6
    -0
      src/app/utils/formatUtil.ts
  7. +27
    -10
      src/components/DashboardPage/escalation/EscalationLogTable.tsx
  8. +29
    -7
      src/components/PoDetail/EscalationComponent.tsx
  9. +33
    -11
      src/components/PoDetail/PoInputGrid.tsx
  10. +90
    -47
      src/components/PoDetail/QcFormVer2.tsx
  11. +94
    -43
      src/components/PoDetail/QcStockInModalVer2.tsx
  12. +7
    -7
      src/components/PoDetail/StockInFormVer2.tsx
  13. +1
    -1
      src/components/PoSearch/PoSearch.tsx
  14. +2
    -1
      src/i18n/zh/dashboard.json
  15. +4
    -2
      src/i18n/zh/purchaseOrder.json

+ 1
- 1
src/app/(main)/po/edit/page.tsx View File

@@ -29,7 +29,7 @@ const PoEdit: React.FC<Props> = async ({ searchParams }) => {
return ( return (
<> <>
{/* <Typography variant="h4">{t("Create Material")}</Typography> */} {/* <Typography variant="h4">{t("Create Material")}</Typography> */}
<I18nProvider namespaces={[type]}>
<I18nProvider namespaces={[type, "dashboard"]}>
<Suspense fallback={<PoDetail.Loading />}> <Suspense fallback={<PoDetail.Loading />}>
<PoDetail id={id} /> <PoDetail id={id} />
</Suspense> </Suspense>


+ 21
- 0
src/app/api/escalation/actions.ts View File

@@ -0,0 +1,21 @@
"use server"

import { convertObjToURLSearchParams } from "@/app/utils/commonUtil";
import { serverFetchJson } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";
import { cache } from "react";
import { EscalationResult } from ".";


export const fetchEscalationLogsByStockInLines = cache(async(stockInLineIds: number[]) => {
const searchParams = convertObjToURLSearchParams({stockInLineIds: stockInLineIds})
return serverFetchJson<EscalationResult[]>(`${BASE_API_URL}/escalationLog/stockInLines?${searchParams}`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
next: {
tags: ["escalationLogs"],
},
},
);
});

+ 0
- 13
src/app/api/escalation/index.ts View File

@@ -32,19 +32,6 @@ export interface EscalationResult {
dnNo?: string; dnNo?: string;
} }


export const fetchEscalationLogsByStockInLines = cache(async(stockInLineIds: number[]) => {
const searchParams = convertObjToURLSearchParams({stockInLineIds: stockInLineIds})
return serverFetchJson<EscalationResult[]>(`${BASE_API_URL}/escalationLog/stockInLines?${searchParams}`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
next: {
tags: ["escalationLogs"],
},
},
);
});

export const fetchEscalationLogsByUser = cache(async() => { export const fetchEscalationLogsByUser = cache(async() => {
return serverFetchJson<EscalationResult[]>(`${BASE_API_URL}/escalationLog/user`, return serverFetchJson<EscalationResult[]>(`${BASE_API_URL}/escalationLog/user`,
{ {


+ 11
- 7
src/app/api/po/actions.ts View File

@@ -38,8 +38,8 @@ export interface StockInLineEntry {


export interface PurchaseQcResult{ export interface PurchaseQcResult{
qcItemId: number; qcItemId: number;
qcPassed: boolean;
failQty: number;
qcPassed?: boolean;
failQty?: number;
remarks?: string; remarks?: string;
} }
@@ -69,14 +69,16 @@ export interface PurchaseQCInput {
sampleWeight: number; sampleWeight: number;
totalWeight: number; totalWeight: number;
qcAccept: boolean; qcAccept: boolean;
qcDecision?: number;
qcResult: PurchaseQcResult[]; qcResult: PurchaseQcResult[];
} }
export interface EscalationInput { export interface EscalationInput {
status: string; status: string;
remarks?: string; remarks?: string;
handler: string;
productLotNo: string;
acceptedQty: number; // this is the qty to be escalated
reason?: string;
handlerId: number;
productLotNo?: string;
acceptedQty?: number; // this is the qty to be escalated
// escalationQty: number // escalationQty: number
} }
export interface PutawayLine { export interface PutawayLine {
@@ -94,8 +96,10 @@ export interface PutawayInput {
} }


export type ModalFormInput = Partial< export type ModalFormInput = Partial<
PurchaseQCInput & StockInInput & EscalationInput & PutawayInput
>;
PurchaseQCInput & StockInInput & PutawayInput
> & {
escalationLog? : Partial<EscalationInput>
};


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}`, {


+ 1
- 0
src/app/api/po/index.ts View File

@@ -81,6 +81,7 @@ export interface StockInLine {
dnNo?: string; dnNo?: string;
dnDate?: number[]; dnDate?: number[];
stockQty?: number; stockQty?: number;
handlerId?: number;
} }


export const fetchPoList = cache(async (queryParams?: Record<string, any>) => { export const fetchPoList = cache(async (queryParams?: Record<string, any>) => {


+ 6
- 0
src/app/utils/formatUtil.ts View File

@@ -32,6 +32,8 @@ export const INPUT_DATE_FORMAT = "YYYY-MM-DD";


export const OUTPUT_DATE_FORMAT = "YYYY/MM/DD"; export const OUTPUT_DATE_FORMAT = "YYYY/MM/DD";


export const INPUT_TIME_FORMAT = "HH:mm:ss";

export const OUTPUT_TIME_FORMAT = "HH:mm:ss"; export const OUTPUT_TIME_FORMAT = "HH:mm:ss";


export const arrayToDayjs = (arr: ConfigType | (number | undefined)[]) => { export const arrayToDayjs = (arr: ConfigType | (number | undefined)[]) => {
@@ -73,6 +75,10 @@ export const dayjsToDateString = (date: Dayjs) => {
}; };


export const dayjsToInputDateString = (date: Dayjs) => { export const dayjsToInputDateString = (date: Dayjs) => {
return date.format(INPUT_DATE_FORMAT + "T" + INPUT_TIME_FORMAT);
};

export const dayjsToInputDatetimeString = (date: Dayjs) => {
return date.format(INPUT_DATE_FORMAT); return date.format(INPUT_DATE_FORMAT);
}; };




+ 27
- 10
src/components/DashboardPage/escalation/EscalationLogTable.tsx View File

@@ -62,40 +62,57 @@ const EscalationLogTable: React.FC<Props> = ({
() => [ () => [
{ {
name: "handler", name: "handler",
label: t("Responsible for handling colleagues")
label: t("Responsible for handling colleagues"),
sx: { width: "20%", minWidth: 200, maxWidth: 500 },
}, },
{ {
name: "acceptedQty", name: "acceptedQty",
label: t("Received Qty"), label: t("Received Qty"),
align: "right", align: "right",
headerAlign: "right"
headerAlign: "right",
sx: { width: "10%", minWidth: 100 },
}, },
{ {
name: "purchaseUomDesc", name: "purchaseUomDesc",
label: t("Purchase UoM")
label: t("Purchase UoM"),
sx: { width: "15%", minWidth: 120 },
}, },
{ {
name: "dnDate", name: "dnDate",
label: t("DN Date"), label: t("DN Date"),
sx: { width: "10%", minWidth: 120 },
renderCell: (params) => { renderCell: (params) => {
return params.dnDate ? arrayToDateString(params.dnDate) : "N/A" return params.dnDate ? arrayToDateString(params.dnDate) : "N/A"
} }
}, },
{
name: "qcTotalCount",
label: t("QC Completed Count"),
align: "right",
headerAlign: "right"
},
{ {
name: "qcFailCount", name: "qcFailCount",
label: t("QC Fail Count"), label: t("QC Fail Count"),
align: "right", align: "right",
headerAlign: "right"
headerAlign: "right",
sx: { width: "15%", minWidth: 120 },
renderCell: (params) => {
return `${params.qcFailCount} / ${params.qcTotalCount}`
}
}, },
// {
// name: "qcTotalCount",
// label: t("QC Completed Count"),
// align: "right",
// headerAlign: "right"
// flex: 1,
// },
// {
// name: "qcFailCount",
// label: t("QC Fail Count"),
// align: "right",
// headerAlign: "right"
// flex: 1,
// },
{ {
name: "reason", name: "reason",
label: t("Reason"), label: t("Reason"),
sx: { width: "30%", minWidth: 150 },
}, },
], []) ], [])




+ 29
- 7
src/components/PoDetail/EscalationComponent.tsx View File

@@ -21,6 +21,8 @@ import { SelectChangeEvent } from '@mui/material/Select';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ExpandLessIcon from '@mui/icons-material/ExpandLess'; import ExpandLessIcon from '@mui/icons-material/ExpandLess';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useFormContext } from 'react-hook-form';
import { EscalationInput, ModalFormInput } from '@/app/api/po/actions';


interface NameOption { interface NameOption {
value: string; value: string;
@@ -38,6 +40,7 @@ interface Props {
isCollapsed: boolean isCollapsed: boolean
setIsCollapsed: Dispatch<React.SetStateAction<boolean>> setIsCollapsed: Dispatch<React.SetStateAction<boolean>>
} }

const EscalationComponent: React.FC<Props> = ({ const EscalationComponent: React.FC<Props> = ({
forSupervisor, forSupervisor,
isCollapsed, isCollapsed,
@@ -60,6 +63,19 @@ const EscalationComponent: React.FC<Props> = ({
{ value: 'david', label: '林建國' }, { value: 'david', label: '林建國' },
]; ];


const {
register,
formState: { errors, defaultValues, touchedFields },
watch,
control,
setValue,
getValues,
reset,
resetField,
setError,
clearErrors,
} = useFormContext<ModalFormInput>();

const handleInputChange = ( const handleInputChange = (
event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> | SelectChangeEvent<string> event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> | SelectChangeEvent<string>
): void => { ): void => {
@@ -70,7 +86,7 @@ const EscalationComponent: React.FC<Props> = ({
})); }));
}; };


const handleSubmit = (e: FormEvent<HTMLFormElement>): void => {
const handleSubmit = (e: FormEvent<HTMLFormElement>): void => {console.log("called this?");
e.preventDefault(); e.preventDefault();
console.log('表單已提交:', formData); console.log('表單已提交:', formData);
// 處理表單提交 // 處理表單提交
@@ -118,9 +134,12 @@ const EscalationComponent: React.FC<Props> = ({
<Box component="form" onSubmit={handleSubmit} sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}> <Box component="form" onSubmit={handleSubmit} sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<FormControl fullWidth> <FormControl fullWidth>
<select <select
id="name"
name="name"
value={formData.name}
id="handlerId"
// name="name"
// value={formData.name}
{...register("escalationLog.handlerId", {
required: "handler required!",
})}
onChange={handleInputChange} onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white" className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white"
> >
@@ -169,12 +188,15 @@ const EscalationComponent: React.FC<Props> = ({


<TextField <TextField
fullWidth fullWidth
id="message"
name="message"
id="reason"
// name="reason"
{...register("escalationLog.reason", {
required: "reason required!",
})}
label="上報原因" label="上報原因"
multiline multiline
rows={4} rows={4}
value={formData.message}
// value={formData.reason}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="請輸入上報原因" placeholder="請輸入上報原因"
/> />


+ 33
- 11
src/components/PoDetail/PoInputGrid.tsx View File

@@ -9,6 +9,7 @@ import {
GridRowModes, GridRowModes,
GridRowModesModel, GridRowModesModel,
GridToolbarContainer, GridToolbarContainer,
GridValidRowModel,
useGridApiRef, useGridApiRef,
} from "@mui/x-data-grid"; } from "@mui/x-data-grid";
import { import {
@@ -58,8 +59,12 @@ import { fetchQcResult } from "@/app/api/qc/actions";
import PoQcStockInModal from "./PoQcStockInModal"; import PoQcStockInModal from "./PoQcStockInModal";
import DoDisturbIcon from "@mui/icons-material/DoDisturb"; import DoDisturbIcon from "@mui/icons-material/DoDisturb";
import { useSession } from "next-auth/react"; import { useSession } from "next-auth/react";
// import { SessionWithTokens } from "src/config/authConfig";
import PoQcStockInModalVer2 from "./QcStockInModalVer2"; import PoQcStockInModalVer2 from "./QcStockInModalVer2";
import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil"; import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil";
import { EscalationResult } from "@/app/api/escalation";
import { fetchEscalationLogsByStockInLines } from "@/app/api/escalation/actions";
import { SessionWithTokens } from "@/config/authConfig";


interface ResultWithId { interface ResultWithId {
id: number; id: number;
@@ -130,7 +135,7 @@ function PoInputGrid({
setEntries(stockInLine) setEntries(stockInLine)
}, [stockInLine]) }, [stockInLine])
const [modalInfo, setModalInfo] = useState< const [modalInfo, setModalInfo] = useState<
StockInLine & { qcResult?: PurchaseQcResult[] }
StockInLine & { qcResult?: PurchaseQcResult[] } & { escalationResult?: EscalationResult[] }
>(); >();
const pathname = usePathname() const pathname = usePathname()
const router = useRouter(); const router = useRouter();
@@ -151,6 +156,7 @@ function PoInputGrid({
}); });


const { data: session } = useSession(); const { data: session } = useSession();
const sessionToken = session as SessionWithTokens | null;


useEffect(() => { useEffect(() => {
const completedList = entries.filter( const completedList = entries.filter(
@@ -245,7 +251,7 @@ function PoInputGrid({
return await fetchQcResult(stockInLineId as number); return await fetchQcResult(stockInLineId as number);
}, []); }, []);


const handleQC = useCallback(
const handleQC = useCallback( // UNUSED NOW!
(id: GridRowId, params: any) => async () => { (id: GridRowId, params: any) => async () => {
setBtnIsLoading(true); setBtnIsLoading(true);
setRowModesModel((prev) => ({ setRowModesModel((prev) => ({
@@ -253,8 +259,10 @@ function PoInputGrid({
[id]: { mode: GridRowModes.View }, [id]: { mode: GridRowModes.View },
})); }));
const qcResult = await fetchQcDefaultValue(id); const qcResult = await fetchQcDefaultValue(id);
const escResult = await fetchEscalationLogsByStockInLines([Number(id)]);
console.log(params.row); console.log(params.row);
console.log(qcResult); console.log(qcResult);
setModalInfo({ setModalInfo({
...params.row, ...params.row,
qcResult: qcResult, qcResult: qcResult,
@@ -273,6 +281,7 @@ function PoInputGrid({
const [newOpen, setNewOpen] = useState(false); const [newOpen, setNewOpen] = useState(false);
const stockInLineId = searchParams.get("stockInLineId"); const stockInLineId = searchParams.get("stockInLineId");
const poLineId = searchParams.get("poLineId"); const poLineId = searchParams.get("poLineId");

const closeNewModal = useCallback(() => { const closeNewModal = useCallback(() => {
const newParams = new URLSearchParams(searchParams.toString()); const newParams = new URLSearchParams(searchParams.toString());
newParams.delete("stockInLineId"); // Remove the parameter newParams.delete("stockInLineId"); // Remove the parameter
@@ -300,9 +309,12 @@ const closeNewModal = useCallback(() => {
})); }));


const qcResult = await fetchQcDefaultValue(id); const qcResult = await fetchQcDefaultValue(id);
const escResult = await fetchEscalationLogsByStockInLines([Number(id)]);

setModalInfo(() => ({ setModalInfo(() => ({
...params.row, ...params.row,
qcResult: qcResult, qcResult: qcResult,
escResult: escResult,
receivedQty: itemDetail.receivedQty, receivedQty: itemDetail.receivedQty,
})); }));


@@ -424,10 +436,14 @@ const closeNewModal = useCallback(() => {
[], [],
); );


const getButtonSx = (status : string) => {
const getButtonSx = (sil : StockInLine) => {
const status = sil?.status?.toLowerCase();
let btnSx = {label:"", color:""}; let btnSx = {label:"", color:""};
switch (status) { switch (status) {
case "received": btnSx = {label: t("putaway processing"), color:"secondary.main"}; break; case "received": btnSx = {label: t("putaway processing"), color:"secondary.main"}; break;
case "escalated": if (sessionToken?.id == sil?.handlerId) {
btnSx = {label: t("escalation processing"), color:"warning.main"};
break;}
case "rejected": case "rejected":
case "completed": btnSx = {label: t("view stockin"), color:"info.main"}; break; case "completed": btnSx = {label: t("view stockin"), color:"info.main"}; break;
default: btnSx = {label: t("qc processing"), color:"success.main"}; default: btnSx = {label: t("qc processing"), color:"success.main"};
@@ -476,7 +492,7 @@ const closeNewModal = useCallback(() => {
headerName: t("dnDate"), headerName: t("dnDate"),
width: 125, width: 125,
renderCell: (params) => { renderCell: (params) => {
console.log(params.row)
// console.log(params.row)
// return <>07/08/2025</> // return <>07/08/2025</>
return arrayToDateString(params.value) return arrayToDateString(params.value)
} }
@@ -511,7 +527,7 @@ const closeNewModal = useCallback(() => {
width: 120, width: 120,
// flex: 0.5, // flex: 0.5,
renderCell: (params) => { renderCell: (params) => {
return params.row.uom.code;
return params.row.uom?.code;
}, },
}, },
{ {
@@ -557,7 +573,12 @@ const closeNewModal = useCallback(() => {
width: 140, width: 140,
// flex: 0.5, // flex: 0.5,
renderCell: (params) => { renderCell: (params) => {
return t(`${params.row.status}`);
const handlerId = params.row.handlerId
const status = params.row.status
return (<span style={{
color: (status == "escalated")? "red":"inherit"}}>
{t(`${params.row.status}`)}
</span>);
}, },
}, },
{ {
@@ -572,9 +593,9 @@ const closeNewModal = useCallback(() => {
// flex: 2, // flex: 2,
cellClassName: "actions", cellClassName: "actions",
getActions: (params) => { getActions: (params) => {
const data = params.row;
// console.log(params.row.status); // console.log(params.row.status);
const status = params.row.status.toLowerCase();
const btnSx = getButtonSx(status);
const btnSx = getButtonSx(data);
// console.log(stockInLineStatusMap[status]); // console.log(stockInLineStatusMap[status]);
// console.log(session?.user?.abilities?.includes("APPROVAL")); // console.log(session?.user?.abilities?.includes("APPROVAL"));
return [ return [
@@ -754,7 +775,7 @@ const closeNewModal = useCallback(() => {
}, },
}, },
], ],
[t, handleStart, handleQC, handleEscalation, session?.user?.abilities, handleStockIn, handlePutAway, handleDelete, handleReject, itemDetail],
[t, handleStart, handleQC, handleEscalation, handleStockIn, handlePutAway, handleDelete, handleReject, itemDetail],
); );


const addRow = useCallback(() => { const addRow = useCallback(() => {
@@ -911,6 +932,7 @@ const closeNewModal = useCallback(() => {
setEntries={setEntries} setEntries={setEntries}
setStockInLine={setStockInLine} setStockInLine={setStockInLine}
setItemDetail={setModalInfo} setItemDetail={setModalInfo}
session={sessionToken}
qc={qc} qc={qc}
warehouse={warehouse} warehouse={warehouse}
open={newOpen} open={newOpen}
@@ -921,7 +943,7 @@ const closeNewModal = useCallback(() => {
</> </>
) )
} }
{modalInfo !== undefined && (
{/* {modalInfo !== undefined && (
<> <>
<PoQcStockInModal <PoQcStockInModal
type={"qc"} type={"qc"}
@@ -995,7 +1017,7 @@ const closeNewModal = useCallback(() => {
itemDetail={modalInfo} itemDetail={modalInfo}
/> />
</> </>
)}
)} */}
</> </>
); );
} }


+ 90
- 47
src/components/PoDetail/QcFormVer2.tsx View File

@@ -52,9 +52,11 @@ import { dummyEscalationHistory, dummyQCData } from "./dummyQcTemplate";
import { ModalFormInput } from "@/app/api/po/actions"; import { ModalFormInput } from "@/app/api/po/actions";
import { escape } from "lodash"; import { escape } from "lodash";
import { PanoramaSharp } from "@mui/icons-material"; import { PanoramaSharp } from "@mui/icons-material";
import EscalationLogTable from "../DashboardPage/escalation/EscalationLogTable";
import { EscalationResult } from "@/app/api/escalation";


interface Props { interface Props {
itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] };
itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] };
qc: QcItemWithChecks[]; qc: QcItemWithChecks[];
disabled: boolean; disabled: boolean;
qcItems: QcData[] qcItems: QcData[]
@@ -90,6 +92,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
const [escalationHistory, setEscalationHistory] = useState(dummyEscalationHistory); const [escalationHistory, setEscalationHistory] = useState(dummyEscalationHistory);
// const [qcResult, setQcResult] = useState(); // const [qcResult, setQcResult] = useState();
const qcAccept = watch("qcAccept"); const qcAccept = watch("qcAccept");
const qcDecision = watch("qcDecision"); //WIP
const qcResult = watch("qcResult"); const qcResult = watch("qcResult");
console.log(qcResult); console.log(qcResult);
// const [qcAccept, setQcAccept] = useState(true); // const [qcAccept, setQcAccept] = useState(true);
@@ -119,28 +122,29 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
//// validate form //// validate form
const accQty = watch("acceptQty"); const accQty = watch("acceptQty");
const validateForm = useCallback(() => { const validateForm = useCallback(() => {
console.log(accQty);
if (accQty > itemDetail.acceptedQty) {
setError("acceptQty", {
message: `${t("acceptQty must not greater than")} ${
itemDetail.acceptedQty
}`,
type: "required",
});
if (qcDecision == 1) {
if (accQty > itemDetail.acceptedQty) {
setError("acceptQty", {
message: `${t("acceptQty must not greater than")} ${
itemDetail.acceptedQty
}`,
type: "required",
});
}
if (accQty < 1) {
setError("acceptQty", {
message: t("minimal value is 1"),
type: "required",
});
}
if (isNaN(accQty)) {
setError("acceptQty", {
message: t("value must be a number"),
type: "required",
});
}
} }
if (accQty < 1) {
setError("acceptQty", {
message: t("minimal value is 1"),
type: "required",
});
}
if (isNaN(accQty)) {
setError("acceptQty", {
message: t("value must be a number"),
type: "required",
});
}
}, [accQty]);
}, [accQty, qcDecision]);


useEffect(() => { useEffect(() => {
clearErrors(); clearErrors();
@@ -191,7 +195,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
flex: 2, flex: 2,
renderCell: (params) => ( renderCell: (params) => (
<Box> <Box>
<b>{params.value}</b><br/>
<b>{`${params.api.getRowIndexRelativeToVisibleRows(params.id) + 1}. ${params.value}`}</b><br/>
{params.row.name}<br/> {params.row.name}<br/>
</Box> </Box>
), ),
@@ -202,7 +206,8 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
flex: 1.5, flex: 1.5,
renderCell: (params) => { renderCell: (params) => {
const currentValue = params.row; const currentValue = params.row;
console.log(currentValue.row);
const index = params.api.getRowIndexRelativeToVisibleRows(params.id);
// console.log(currentValue.row);
return ( return (
<FormControl> <FormControl>
<RadioGroup <RadioGroup
@@ -215,6 +220,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
setQcItems((prev) => setQcItems((prev) =>
prev.map((r): QcData => (r.id === params.id ? { ...r, qcPassed: value === "true" } : r)) prev.map((r): QcData => (r.id === params.id ? { ...r, qcPassed: value === "true" } : r))
); );
// setValue(`qcResult.${index}.qcPassed`, value == "true");
}} }}
name={`qcPassed-${params.id}`} name={`qcPassed-${params.id}`}
> >
@@ -222,7 +228,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
value="true" value="true"
control={<Radio />} control={<Radio />}
label="合格" label="合格"
disabled={disabled}
disabled={disabled || itemDetail.status == "escalated"}
sx={{ sx={{
color: currentValue.qcPassed === true ? "green" : "inherit", color: currentValue.qcPassed === true ? "green" : "inherit",
"& .Mui-checked": {color: "green"} "& .Mui-checked": {color: "green"}
@@ -232,7 +238,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
value="false" value="false"
control={<Radio />} control={<Radio />}
label="不合格" label="不合格"
disabled={disabled}
disabled={disabled || itemDetail.status == "escalated"}
sx={{ sx={{
color: currentValue.qcPassed === false ? "red" : "inherit", color: currentValue.qcPassed === false ? "red" : "inherit",
"& .Mui-checked": {color: "red"} "& .Mui-checked": {color: "red"}
@@ -253,7 +259,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
type="number" type="number"
size="small" size="small"
value={!params.row.qcPassed? (params.value ?? '') : '0'} value={!params.row.qcPassed? (params.value ?? '') : '0'}
disabled={params.row.qcPassed || disabled}
disabled={params.row.qcPassed || disabled || itemDetail.status == "escalated"}
onChange={(e) => { onChange={(e) => {
const v = e.target.value; const v = e.target.value;
const next = v === '' ? undefined : Number(v); const next = v === '' ? undefined : Number(v);
@@ -279,7 +285,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
<TextField <TextField
size="small" size="small"
value={params.value ?? ''} value={params.value ?? ''}
disabled={disabled}
disabled={disabled || itemDetail.status == "escalated"}
onChange={(e) => { onChange={(e) => {
const remarks = e.target.value; const remarks = e.target.value;
// const next = v === '' ? undefined : Number(v); // const next = v === '' ? undefined : Number(v);
@@ -331,13 +337,29 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI


useEffect(() => { useEffect(() => {
console.log("ItemDetail in QC:", itemDetail); console.log("ItemDetail in QC:", itemDetail);
console.log("Qc Result in QC:", qcResult);
}, [itemDetail]); }, [itemDetail]);


const setQcDecision = (status : string | undefined) => {
const param = status?.toLowerCase();
if (param !== undefined && param !== null) {
if (param == "completed") {
return 1;
} else if (param == "rejected") {
return 2;
} else if (param == "escalated") {
return 3;
} else { return undefined; }
} else {
return undefined;
}
}



useEffect(() => { useEffect(() => {
// onFailedOpenCollapse(qcItems) // This function is no longer needed // onFailedOpenCollapse(qcItems) // This function is no longer needed
}, [qcItems]); // Removed onFailedOpenCollapse from dependency array
}, [qcItems]);


return ( return (
<> <>
@@ -362,6 +384,19 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
{tabIndex == 0 && ( {tabIndex == 0 && (
<> <>
<Grid item xs={12}> <Grid item xs={12}>
<Box sx={{ mb: 2, p: 2, backgroundColor: '#f5f5f5', borderRadius: 1 }}>
<Typography variant="h5" component="h2" sx={{ fontWeight: 'bold', color: '#333' }}>
Group A - 急凍貨類 (QCA1-MEAT01)
</Typography>
<Typography variant="subtitle1" sx={{ color: '#666' }}>
<b>品檢類型</b>:IQC
</Typography>
<Typography variant="subtitle2" sx={{ color: '#666' }}>
記錄探測溫度的時間,請在1小時内完成卸貨盤點入庫,以保障食品安全<br/>
監察方法:目視檢查、嗅覺檢查和使用適當的食物溫度計,檢查食物溫度是否符合指標
</Typography>
</Box>
{/* <QcDataGrid<ModalFormInput, QcData, EntryError> {/* <QcDataGrid<ModalFormInput, QcData, EntryError>
apiRef={apiRef} apiRef={apiRef}
columns={qcColumns} columns={qcColumns}
@@ -370,7 +405,8 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
/> */} /> */}
<StyledDataGrid <StyledDataGrid
columns={qcColumns} columns={qcColumns}
rows={disabled? qcResult:qcItems}
rows={qcResult && qcResult.length > 0 ? qcResult : qcItems}
// rows={disabled? qcResult:qcItems}
autoHeight autoHeight
/> />
</Grid> </Grid>
@@ -384,52 +420,58 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
disabled={false} disabled={false}
/> />
</Grid> */} </Grid> */}
<Grid item xs={12}>
{/* <Grid item xs={12}>
<Typography variant="h6" display="block" marginBlockEnd={1}> <Typography variant="h6" display="block" marginBlockEnd={1}>
{t("Escalation Info")} {t("Escalation Info")}
</Typography> </Typography>
</Grid>
</Grid> */}
<Grid item xs={12}> <Grid item xs={12}>
<StyledDataGrid
<EscalationLogTable items={itemDetail.escResult || []}/>
{/* <StyledDataGrid
rows={escalationHistory} rows={escalationHistory}
columns={columns} columns={columns}
onRowSelectionModelChange={(newRowSelectionModel) => { onRowSelectionModelChange={(newRowSelectionModel) => {
setRowSelectionModel(newRowSelectionModel); setRowSelectionModel(newRowSelectionModel);
}} }}
/>
/> */}
</Grid> </Grid>
</> </>
)} )}
<Grid item xs={12}> <Grid item xs={12}>
<FormControl> <FormControl>
<Controller <Controller
name="qcAccept"
name="qcDecision"
// name="qcAccept"
control={control} control={control}
defaultValue={true}
defaultValue={setQcDecision(itemDetail?.status)}
// defaultValue={true}
render={({ field }) => ( render={({ field }) => (
<RadioGroup <RadioGroup
row row
aria-labelledby="demo-radio-buttons-group-label" aria-labelledby="demo-radio-buttons-group-label"
{...field} {...field}
value={field.value?.toString() || "true"}
value={field.value}
// value={field.value?.toString() || "true"}
onChange={(e) => { onChange={(e) => {
const value = e.target.value === 'true';
if (!value && Boolean(errors.acceptQty)) {
setValue("acceptQty", itemDetail.acceptedQty);
const value = e.target.value.toString();// === 'true';
if (value != "1" && Boolean(errors.acceptQty)) {
// if (!value && Boolean(errors.acceptQty)) {
setValue("acceptQty", itemDetail.acceptedQty ?? 0);
} }
field.onChange(value); field.onChange(value);
}} }}
> >
<FormControlLabel disabled={disabled} <FormControlLabel disabled={disabled}
value="true" control={<Radio />} label="接受" />
value="1" control={<Radio />} label="接受" />
<Box sx={{mr:2}}> <Box sx={{mr:2}}>
<TextField <TextField
type="number" type="number"
label={t("acceptQty")} label={t("acceptQty")}
sx={{ width: '150px' }} sx={{ width: '150px' }}
value={qcAccept? accQty : 0 }
defaultValue={accQty}
disabled={!qcAccept || disabled}
value={(qcDecision == 1)? accQty : 0 }
// value={qcAccept? accQty : 0 }
disabled={qcDecision != 1 || disabled}
// disabled={!qcAccept || disabled}
{...register("acceptQty", { {...register("acceptQty", {
required: "acceptQty required!", required: "acceptQty required!",
})} })}
@@ -438,11 +480,11 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
/> />
</Box> </Box>
<FormControlLabel disabled={disabled} <FormControlLabel disabled={disabled}
value="false" control={<Radio />}
value="2" control={<Radio />}
sx={{"& .Mui-checked": {color: "red"}}} sx={{"& .Mui-checked": {color: "red"}}}
label="不接受" /> label="不接受" />
<FormControlLabel disabled={disabled} <FormControlLabel disabled={disabled}
value="false" control={<Radio />}
value="3" control={<Radio />}
sx={{"& .Mui-checked": {color: "blue"}}} sx={{"& .Mui-checked": {color: "blue"}}}
label="上報品檢結果" /> label="上報品檢結果" />
</RadioGroup> </RadioGroup>
@@ -450,7 +492,8 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
/> />
</FormControl> </FormControl>
</Grid> </Grid>
{!qcAccept && (
{qcDecision == 3 && (
// {!qcAccept && (
<Grid item xs={12}> <Grid item xs={12}>
<EscalationComponent <EscalationComponent
forSupervisor={false} forSupervisor={false}


+ 94
- 43
src/components/PoDetail/QcStockInModalVer2.tsx View File

@@ -11,8 +11,8 @@ import {
Stack, Stack,
Typography, Typography,
} from "@mui/material"; } from "@mui/material";
import { Dispatch, SetStateAction, useCallback, useEffect, useState } from "react";
import { FormProvider, SubmitHandler, useForm } from "react-hook-form";
import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from "react";
import { FormProvider, SubmitErrorHandler, SubmitHandler, useForm } from "react-hook-form";
import { StockInLineRow } from "./PoInputGrid"; import { StockInLineRow } from "./PoInputGrid";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import StockInForm from "./StockInForm"; import StockInForm from "./StockInForm";
@@ -22,8 +22,11 @@ import PutawayForm from "./PutawayForm";
import { dummyPutawayLine, dummyQCData } from "./dummyQcTemplate"; import { dummyPutawayLine, dummyQCData } from "./dummyQcTemplate";
import { useGridApiRef } from "@mui/x-data-grid"; import { useGridApiRef } from "@mui/x-data-grid";
import {submitDialogWithWarning} from "../Swal/CustomAlerts"; import {submitDialogWithWarning} from "../Swal/CustomAlerts";
import { arrayToDateString, arrayToInputDateString, dayjsToInputDateString } from "@/app/utils/formatUtil";
import { arrayToDateString, arrayToInputDateString, dayjsToInputDateString, INPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { watch } from "fs";
import { EscalationResult } from "@/app/api/escalation";
import { SessionWithTokens } from "@/config/authConfig";


const style = { const style = {
position: "absolute", position: "absolute",
@@ -42,7 +45,7 @@ interface CommonProps extends Omit<ModalProps, "children"> {
// setRows: Dispatch<SetStateAction<PurchaseOrderLine[]>>; // setRows: Dispatch<SetStateAction<PurchaseOrderLine[]>>;
setEntries?: Dispatch<SetStateAction<StockInLineRow[]>>; setEntries?: Dispatch<SetStateAction<StockInLineRow[]>>;
setStockInLine?: Dispatch<SetStateAction<StockInLine[]>>; setStockInLine?: Dispatch<SetStateAction<StockInLine[]>>;
itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] };
itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] };
setItemDetail: Dispatch< setItemDetail: Dispatch<
SetStateAction< SetStateAction<
| (StockInLine & { | (StockInLine & {
@@ -51,13 +54,15 @@ interface CommonProps extends Omit<ModalProps, "children"> {
| undefined | undefined
> >
>; >;
session: SessionWithTokens | null;
qc?: QcItemWithChecks[]; qc?: QcItemWithChecks[];
warehouse?: any[]; warehouse?: any[];
// type: "qc" | "stockIn" | "escalation" | "putaway" | "reject"; // type: "qc" | "stockIn" | "escalation" | "putaway" | "reject";
handleMailTemplateForStockInLine: (stockInLineId: number) => void; handleMailTemplateForStockInLine: (stockInLineId: number) => void;
onClose: () => void;
} }
interface Props extends CommonProps { interface Props extends CommonProps {
itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] };
itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] };
} }
const PoQcStockInModalVer2: React.FC<Props> = ({ const PoQcStockInModalVer2: React.FC<Props> = ({
// type, // type,
@@ -68,6 +73,7 @@ const PoQcStockInModalVer2: React.FC<Props> = ({
onClose, onClose,
itemDetail, itemDetail,
setItemDetail, setItemDetail,
session,
qc, qc,
warehouse, warehouse,
handleMailTemplateForStockInLine, handleMailTemplateForStockInLine,
@@ -77,20 +83,32 @@ const PoQcStockInModalVer2: React.FC<Props> = ({
i18n: { language }, i18n: { language },
} = useTranslation("purchaseOrder"); } = useTranslation("purchaseOrder");


const defaultNewValue = useMemo(() => {
return (
{
...itemDetail,
status: itemDetail.status ?? "pending",
dnDate: arrayToInputDateString(itemDetail.dnDate)?? dayjsToInputDateString(dayjs()),
putawayLine: dummyPutawayLine,
qcResult: (itemDetail.qcResult && itemDetail.qcResult?.length > 0) ? itemDetail.qcResult : [],//[...dummyQCData],
escResult: (itemDetail.escResult && itemDetail.escResult?.length > 0) ? itemDetail.escResult : [],
receiptDate: itemDetail.receiptDate ?? dayjs().add(0, "month").format(INPUT_DATE_FORMAT),
acceptQty: itemDetail.demandQty?? itemDetail.acceptedQty,
warehouseId: itemDetail.defaultWarehouseId ?? 1
}
)
},[itemDetail])

const [qcItems, setQcItems] = useState(dummyQCData) const [qcItems, setQcItems] = useState(dummyQCData)
const formProps = useForm<ModalFormInput>({ const formProps = useForm<ModalFormInput>({
defaultValues: { defaultValues: {
...itemDetail,
dnDate: dayjsToInputDateString(dayjs()),
putawayLine: dummyPutawayLine,
// receiptDate: itemDetail.receiptDate || dayjs().add(-1, "month").format(INPUT_DATE_FORMAT),
// warehouseId: itemDetail.defaultWarehouseId || 0
...defaultNewValue,
}, },
}); });

const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>( const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>(
(...args) => {
onClose?.(...args);
() => {
onClose?.();
// reset(); // reset();
}, },
[onClose], [onClose],
@@ -104,25 +122,31 @@ const [qcItems, setQcItems] = useState(dummyQCData)
} else return false; } else return false;
}; };


useEffect(() => {
formProps.reset({
...itemDetail,
dnDate: dayjsToInputDateString(dayjs()),
putawayLine: dummyPutawayLine,
})
setOpenPutaway(isPutaway);
}, [open])
const [viewOnly, setViewOnly] = useState(false); const [viewOnly, setViewOnly] = useState(false);
useEffect(() => { useEffect(() => {
if (itemDetail && itemDetail.status) { if (itemDetail && itemDetail.status) {
const isViewOnly = itemDetail.status.toLowerCase() == "completed" || itemDetail.status.toLowerCase() == "rejected"
const isViewOnly = itemDetail.status.toLowerCase() == "completed"
|| itemDetail.status.toLowerCase() == "rejected"
|| (itemDetail.status.toLowerCase() == "escalated" && session?.id != itemDetail.handlerId)
setViewOnly(isViewOnly) setViewOnly(isViewOnly)
} }
console.log("ITEM", itemDetail);
}, [itemDetail]); }, [itemDetail]);


useEffect(() => {
const qcRes = itemDetail?.qcResult;
// if (!qcRes || qcRes?.length <= 0) {
// itemDetail.qcResult = dummyQCData;
// }
formProps.reset({
...defaultNewValue
})

setQcItems(dummyQCData);
setOpenPutaway(isPutaway);
}, [open, defaultNewValue])

const [openPutaway, setOpenPutaway] = useState(false); const [openPutaway, setOpenPutaway] = useState(false);
const onOpenPutaway = useCallback(() => { const onOpenPutaway = useCallback(() => {
@@ -155,6 +179,13 @@ const [qcItems, setQcItems] = useState(dummyQCData)
[], [],
); );


// QC submission handler
const onSubmitErrorQc = useCallback<SubmitErrorHandler<ModalFormInput>>(
async (data, event) => {
console.log("Error", data);
}, []
);

// QC submission handler // QC submission handler
const onSubmitQc = useCallback<SubmitHandler<ModalFormInput>>( const onSubmitQc = useCallback<SubmitHandler<ModalFormInput>>(
async (data, event) => { async (data, event) => {
@@ -162,19 +193,16 @@ const [qcItems, setQcItems] = useState(dummyQCData)
// TODO: Move validation into QC page // TODO: Move validation into QC page
// Get QC data from the shared form context // Get QC data from the shared form context
const qcAccept = data.qcAccept;
const qcAccept = data.qcDecision == 1;
// const qcAccept = data.qcAccept;
const acceptQty = data.acceptQty as number; const acceptQty = data.acceptQty as number;
const qcResults = qcItems;
const qcResults = qcItems; // qcItems;
// const qcResults = data.qcResult as PurchaseQcResult[]; // qcItems;
// const qcResults = viewOnly? data.qcResult as PurchaseQcResult[] : qcItems; // const qcResults = viewOnly? data.qcResult as PurchaseQcResult[] : qcItems;
// Validate QC data // Validate QC data
const validationErrors : string[] = []; const validationErrors : string[] = [];
// Check if all QC items have results
const itemsWithoutResult = qcResults.filter(item => item.qcPassed === undefined);
if (itemsWithoutResult.length > 0) {
validationErrors.push(`${t("QC items without result")}`);
// validationErrors.push(`${t("QC items without result")}: ${itemsWithoutResult.map(item => item.code).join(', ')}`);
}


// Check if failed items have failed quantity // Check if failed items have failed quantity
const failedItemsWithoutQty = qcResults.filter(item => const failedItemsWithoutQty = qcResults.filter(item =>
@@ -202,13 +230,21 @@ const [qcItems, setQcItems] = useState(dummyQCData)
if (data.expiryDate === undefined || data.expiryDate == null) { if (data.expiryDate === undefined || data.expiryDate == null) {
validationErrors.push("請輸入到期日!"); validationErrors.push("請輸入到期日!");
} }
if (!qcResults.every((qc) => qc.qcPassed) && qcAccept) {
if (!qcResults.every((qc) => qc.qcPassed) && qcAccept && itemDetail.status != "escalated") { //TODO: fix it please!
validationErrors.push("有不合格檢查項目,無法收貨!"); validationErrors.push("有不合格檢查項目,無法收貨!");
// submitDialogWithWarning(() => postStockInLineWithQc(qcData), t, {title:"有不合格檢查項目,確認接受收貨?", // submitDialogWithWarning(() => postStockInLineWithQc(qcData), t, {title:"有不合格檢查項目,確認接受收貨?",
// confirmButtonText: t("confirm putaway"), html: ""}); // confirmButtonText: t("confirm putaway"), html: ""});
// return; // return;
} }


// Check if all QC items have results
const itemsWithoutResult = qcResults.filter(item => item.qcPassed === undefined);
// if (itemsWithoutResult.length > 0 && itemDetail.status != "escalated") { //TODO: fix it please!
// validationErrors.push(`${t("QC items without result")}`);
// // validationErrors.push(`${t("QC items without result")}: ${itemsWithoutResult.map(item => item.code).join(', ')}`);
// }

if (validationErrors.length > 0) { if (validationErrors.length > 0) {
console.error("QC Validation failed:", validationErrors); console.error("QC Validation failed:", validationErrors);
alert(`未完成品檢: ${validationErrors}`); alert(`未完成品檢: ${validationErrors}`);
@@ -224,7 +260,8 @@ const [qcItems, setQcItems] = useState(dummyQCData)
qcAccept: qcAccept? qcAccept : false, qcAccept: qcAccept? qcAccept : false,
acceptQty: acceptQty? acceptQty : 0, acceptQty: acceptQty? acceptQty : 0,
qcResult: qcResults.map(item => ({
qcResult: itemDetail.status != "escalated" ? qcResults.map(item => ({
// id: item.id,
qcItemId: item.id, qcItemId: item.id,
// code: item.code, // code: item.code,
// qcDescription: item.qcDescription, // qcDescription: item.qcDescription,
@@ -232,13 +269,27 @@ const [qcItems, setQcItems] = useState(dummyQCData)
failQty: (item.failQty && !item.qcPassed) ? item.failQty : 0, failQty: (item.failQty && !item.qcPassed) ? item.failQty : 0,
// failedQty: (typeof item.failedQty === "number" && !item.isPassed) ? item.failedQty : 0, // failedQty: (typeof item.failedQty === "number" && !item.isPassed) ? item.failedQty : 0,
remarks: item.remarks || '' remarks: item.remarks || ''
}))
})) : [],
}; };
// const qcData = data; // const qcData = data;


console.log("QC Data for submission:", qcData); console.log("QC Data for submission:", qcData);


await postStockInLine(qcData);
if (data.qcDecision == 3) { // Escalate
const escalationLog = {
type : "qc",
status : "pending", // TODO: update with supervisor decision
reason : data.escalationLog?.reason,
recordDate : dayjsToInputDateString(dayjs()),
handlerId : Number(session?.id),
}
console.log("ESCALATION RESULT", escalationLog);
await postStockInLine({...qcData, escalationLog});

} else {
await postStockInLine(qcData);
}

if (qcData.qcAccept) { if (qcData.qcAccept) {
// submitDialogWithWarning(onOpenPutaway, t, {title:"Save success, confirm to proceed?", // submitDialogWithWarning(onOpenPutaway, t, {title:"Save success, confirm to proceed?",
@@ -257,12 +308,12 @@ const [qcItems, setQcItems] = useState(dummyQCData)
const submitData = { const submitData = {
...itemDetail, ...args ...itemDetail, ...args
} as StockInLineEntry & ModalFormInput; } as StockInLineEntry & ModalFormInput;
console.log(submitData);
console.log("Submitting", submitData);


const res = await updateStockInLine(submitData); const res = await updateStockInLine(submitData);
console.log("result ", res);
console.log("Result ", res);
return res; return res;
},[])
},[itemDetail])


// Email supplier handler // Email supplier handler
const onSubmitEmailSupplier = useCallback<SubmitHandler<ModalFormInput>>( const onSubmitEmailSupplier = useCallback<SubmitHandler<ModalFormInput>>(
@@ -297,7 +348,7 @@ const [qcItems, setQcItems] = useState(dummyQCData)
// binLocation: data.binLocation, // binLocation: data.binLocation,
// putawayQuantity: data.putawayQuantity, // putawayQuantity: data.putawayQuantity,
// putawayNotes: data.putawayNotes, // putawayNotes: data.putawayNotes,
acceptQty: itemDetail.demandQty? itemDetail.demandQty : itemDetail.acceptedQty,
acceptQty: itemDetail.demandQty?? itemDetail.acceptedQty,
...data, ...data,
dnDate : data.dnDate? arrayToInputDateString(data.dnDate) : dayjsToInputDateString(dayjs()), dnDate : data.dnDate? arrayToInputDateString(data.dnDate) : dayjsToInputDateString(dayjs()),
@@ -311,7 +362,7 @@ const [qcItems, setQcItems] = useState(dummyQCData)


// Handle putaway submission logic here // Handle putaway submission logic here
const res = await postStockInLine(putawayData); const res = await postStockInLine(putawayData);
console.log("result ", res);
console.log("Result ", res);
// Close modal after successful putaway // Close modal after successful putaway
closeHandler({}, "backdropClick"); closeHandler({}, "backdropClick");
@@ -438,7 +489,7 @@ const [qcItems, setQcItems] = useState(dummyQCData)
variant="contained" variant="contained"
color="primary" color="primary"
sx={{ mt: 1 }} sx={{ mt: 1 }}
onClick={formProps.handleSubmit(onSubmitQc)}
onClick={formProps.handleSubmit(onSubmitQc, onSubmitErrorQc)}
> >
{t("confirm qc result")} {t("confirm qc result")}
</Button>)} </Button>)}


+ 7
- 7
src/components/PoDetail/StockInFormVer2.tsx View File

@@ -76,18 +76,18 @@ const StockInFormVer2: React.FC<Props> = ({
clearErrors, clearErrors,
} = useFormContext<StockInInput>(); } = useFormContext<StockInInput>();
// console.log(itemDetail); // console.log(itemDetail);
useEffect(() => { useEffect(() => {
console.log("triggered");
// receiptDate default tdy
setValue("receiptDate", dayjs().add(0, "month").format(INPUT_DATE_FORMAT));
setValue("status", "received");
// console.log("triggered");
// // receiptDate default tdy
// setValue("receiptDate", dayjs().add(0, "month").format(INPUT_DATE_FORMAT));
// setValue("status", "received");
}, [setValue]); }, [setValue]);


useEffect(() => { useEffect(() => {
console.log(errors); console.log(errors);
}, [errors]); }, [errors]);
const productionDate = watch("productionDate"); const productionDate = watch("productionDate");
const expiryDate = watch("expiryDate"); const expiryDate = watch("expiryDate");
const uom = watch("uom"); const uom = watch("uom");
@@ -322,7 +322,7 @@ const StockInFormVer2: React.FC<Props> = ({
<TextField <TextField
label={t("acceptedQty")} label={t("acceptedQty")}
fullWidth fullWidth
disabled={disabled}
disabled={true}
{...register("acceptedQty", { {...register("acceptedQty", {
required: "acceptedQty required!", required: "acceptedQty required!",
})} })}


+ 1
- 1
src/components/PoSearch/PoSearch.tsx View File

@@ -215,7 +215,7 @@ const PoSearch: React.FC<Props> = ({
name: "escalated", name: "escalated",
label: t("Escalated"), label: t("Escalated"),
renderCell: (params) => { renderCell: (params) => {
console.log(params.escalated);
// console.log(params.escalated);
return params.escalated ? ( return params.escalated ? (
<NotificationIcon color="warning" /> <NotificationIcon color="warning" />
) : undefined; ) : undefined;


+ 2
- 1
src/i18n/zh/dashboard.json View File

@@ -42,5 +42,6 @@
"Received Qty": "收貨數量", "Received Qty": "收貨數量",
"Escalation List": "上報列表", "Escalation List": "上報列表",
"Purchase UoM": "計量單位", "Purchase UoM": "計量單位",
"QC Completed Count": "品檢完成數量"
"QC Completed Count": "品檢完成數量",
"QC Fail-Total Count": "品檢不合格/總數"
} }

+ 4
- 2
src/i18n/zh/purchaseOrder.json View File

@@ -72,7 +72,8 @@
"receiving": "收貨中", "receiving": "收貨中",
"received": "已檢收", "received": "已檢收",
"completed": "已上架", "completed": "已上架",
"rejected": "已拒絕及上報",
"rejected": "已拒絕",
"escalated": "已上報",
"status": "狀態", "status": "狀態",
"acceptedQty must not greater than": "接受數量不得大於", "acceptedQty must not greater than": "接受數量不得大於",
"minimal value is 1": "最小值為1", "minimal value is 1": "最小值為1",
@@ -131,5 +132,6 @@
"print": "列印", "print": "列印",
"bind": "綁定", "bind": "綁定",
"Search": "搜尋", "Search": "搜尋",
"Found": "已找到"
"Found": "已找到",
"escalation processing": "處理上報記錄"
} }

Loading…
Cancel
Save