Bläddra i källkod

update stock in modal

master
kelvinsuen 1 vecka sedan
förälder
incheckning
f7b35c13a1
16 ändrade filer med 244 tillägg och 105 borttagningar
  1. +12
    -10
      src/app/api/dashboard/actions.ts
  2. +8
    -5
      src/app/api/po/actions.ts
  3. +4
    -0
      src/app/utils/formatUtil.ts
  4. +4
    -4
      src/components/DashboardPage/QC/SupervisorQcApproval.tsx
  5. +1
    -0
      src/components/PoDetail/PoDetail.tsx
  6. +35
    -18
      src/components/PoDetail/PoInputGrid.tsx
  7. +3
    -3
      src/components/PoDetail/PoQcStockInModal.tsx
  8. +3
    -2
      src/components/PoDetail/PutawayForm.tsx
  9. +26
    -15
      src/components/PoDetail/QcFormVer2.tsx
  10. +120
    -31
      src/components/PoDetail/QcStockInModalVer2.tsx
  11. +6
    -5
      src/components/PoDetail/StockInFormVer2.tsx
  12. +6
    -6
      src/components/PoDetail/dummyQcTemplate.tsx
  13. +1
    -0
      src/components/Swal/CustomAlerts.tsx
  14. +2
    -2
      src/i18n/index.tsx
  15. +4
    -0
      src/i18n/zh/dashboard.json
  16. +9
    -4
      src/i18n/zh/purchaseOrder.json

+ 12
- 10
src/app/api/dashboard/actions.ts Visa fil

@@ -11,7 +11,7 @@ import { QcItemResult } from "../settings/qcItem";
import { RecordsRes } from "../utils";
// import { BASE_API_URL } from "@/config/api";

export interface PostStockInLiineResponse<T> {
export interface PostStockInLineResponse<T> {
id: number | null;
name: string;
code: string;
@@ -38,6 +38,7 @@ export interface StockInLineEntry {
export interface PurchaseQcResult {
qcItemId: number;
failQty: number;
isPassed: boolean;
}
export interface StockInInput {
status: string;
@@ -49,11 +50,12 @@ export interface StockInInput {
expiryDate: string;
}
export interface PurchaseQCInput {
status: string;
acceptedQty: number;
sampleRate: number;
sampleWeight: number;
totalWeight: number;
status?: string;
qcAccept: boolean;
acceptQty: number;
sampleRate?: number;
sampleWeight?: number;
totalWeight?: number;
qcResult: PurchaseQcResult[];
}
export interface EscalationInput {
@@ -92,7 +94,7 @@ export const fetchStockInLineInfo = cache(async (stockInLineId: number) => {
export const createStockInLine = async (data: StockInLineEntry) => {
console.log(data)
const stockInLine = await serverFetchJson<
PostStockInLiineResponse<StockInLineEntry>
PostStockInLineResponse<StockInLineEntry>
>(`${BASE_API_URL}/stockInLine/create`, {
method: "POST",
body: JSON.stringify(data),
@@ -106,7 +108,7 @@ export const updateStockInLine = async (
data: StockInLineEntry & ModalFormInput,
) => {
const stockInLine = await serverFetchJson<
PostStockInLiineResponse<StockInLineEntry & ModalFormInput>
PostStockInLineResponse<StockInLineEntry & ModalFormInput>
>(`${BASE_API_URL}/stockInLine/update`, {
method: "POST",
body: JSON.stringify(data),
@@ -117,7 +119,7 @@ export const updateStockInLine = async (
};

export const startPo = async (poId: number) => {
const po = await serverFetchJson<PostStockInLiineResponse<PoResult>>(
const po = await serverFetchJson<PostStockInLineResponse<PoResult>>(
`${BASE_API_URL}/po/start/${poId}`,
{
method: "POST",
@@ -130,7 +132,7 @@ export const startPo = async (poId: number) => {
};

export const checkPolAndCompletePo = async (poId: number) => {
const po = await serverFetchJson<PostStockInLiineResponse<PoResult>>(
const po = await serverFetchJson<PostStockInLineResponse<PoResult>>(
`${BASE_API_URL}/po/check/${poId}`,
{
method: "POST",


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

@@ -12,7 +12,7 @@ import { RecordsRes } from "../utils";
import { Uom } from "../settings/uom";
// import { BASE_API_URL } from "@/config/api";

export interface PostStockInLiineResponse<T> {
export interface PostStockInLineResponse<T> {
id: number | null;
name: string;
code: string;
@@ -35,13 +35,16 @@ export interface StockInLineEntry {

export interface PurchaseQcResult {
qcItemId: number;
isPassed: boolean,
failQty: number;
remarks?: string
}
export interface StockInInput {
status: string;
poCode: string;
productLotNo?: string;
dnNo?: string;
dnDate?: string;
itemName: string;
invoiceNo?: string;
receiptDate: string;
@@ -107,7 +110,7 @@ export const fetchStockInLineInfo = cache(async (stockInLineId: number) => {

export const createStockInLine = async (data: StockInLineEntry) => {
const stockInLine = await serverFetchJson<
PostStockInLiineResponse<StockInLineEntry>
PostStockInLineResponse<StockInLineEntry>
>(`${BASE_API_URL}/stockInLine/create`, {
method: "POST",
body: JSON.stringify(data),
@@ -121,7 +124,7 @@ export const updateStockInLine = async (
data: StockInLineEntry & ModalFormInput,
) => {
const stockInLine = await serverFetchJson<
PostStockInLiineResponse<StockInLineEntry & ModalFormInput>
PostStockInLineResponse<StockInLineEntry & ModalFormInput>
>(`${BASE_API_URL}/stockInLine/update`, {
method: "POST",
body: JSON.stringify(data),
@@ -132,7 +135,7 @@ export const updateStockInLine = async (
};

export const startPo = async (poId: number) => {
const po = await serverFetchJson<PostStockInLiineResponse<PoResult>>(
const po = await serverFetchJson<PostStockInLineResponse<PoResult>>(
`${BASE_API_URL}/po/start/${poId}`,
{
method: "POST",
@@ -145,7 +148,7 @@ export const startPo = async (poId: number) => {
};

export const checkPolAndCompletePo = async (poId: number) => {
const po = await serverFetchJson<PostStockInLiineResponse<PoResult>>(
const po = await serverFetchJson<PostStockInLineResponse<PoResult>>(
`${BASE_API_URL}/po/check/${poId}`,
{
method: "POST",


+ 4
- 0
src/app/utils/formatUtil.ts Visa fil

@@ -54,6 +54,10 @@ export const arrayToDateString = (arr: ConfigType | (number | undefined)[]) => {
return arrayToDayjs(arr).format(OUTPUT_DATE_FORMAT);
};

export const arrayToInputDateString = (arr: ConfigType | (number | undefined)[]) => {
return arrayToDayjs(arr).format(INPUT_DATE_FORMAT);
};

export const dateStringToDayjs = (date: string) => {
// Format: YYYY/MM/DD
return dayjs(date, OUTPUT_DATE_FORMAT);


+ 4
- 4
src/components/DashboardPage/QC/SupervisorQcApproval.tsx Visa fil

@@ -55,10 +55,10 @@ const navigateTo = useCallback(
<Table aria-label="Two column navigable table" size="small">
<TableHead>
<TableRow>
<TableCell>{t("purchase order code")}</TableCell>
<TableCell>{t("item name")}</TableCell>
<TableCell>{t("escalation level")}</TableCell>
<TableCell>{t("reason")}</TableCell>
<TableCell>{t("Purchase Order Code")}</TableCell>
<TableCell>{t("Item Name")}</TableCell>
<TableCell>{t("Escalation Level")}</TableCell>
<TableCell>{t("Reason")}</TableCell>
</TableRow>
</TableHead>
<TableBody>


+ 1
- 0
src/components/PoDetail/PoDetail.tsx Visa fil

@@ -823,6 +823,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
setProcessedQty={setProcessedQty}
itemDetail={selectedRow}
warehouse={warehouse}
fetchPoDetail={fetchPoDetail}
/>
</Box>
</TableCell>


+ 35
- 18
src/components/PoDetail/PoInputGrid.tsx Visa fil

@@ -73,6 +73,7 @@ interface Props {
itemDetail: PurchaseOrderLine;
stockInLine: StockInLine[];
warehouse: WarehouseResult[];
fetchPoDetail: (poId: string) => void;
}

export type StockInLineEntryError = {
@@ -111,6 +112,7 @@ function PoInputGrid({
itemDetail,
stockInLine,
warehouse,
fetchPoDetail
}: Props) {
console.log(itemDetail);
const { t } = useTranslation("purchaseOrder");
@@ -272,6 +274,7 @@ const closeNewModal = useCallback(() => {
const newParams = new URLSearchParams(searchParams.toString());
newParams.delete("stockInLineId"); // Remove the parameter
router.replace(`${pathname}?${newParams.toString()}`);
fetchPoDetail(itemDetail.purchaseOrderId.toString());
setTimeout(() => {
setNewOpen(false); // Close the modal first
}, 300); // Add a delay to avoid immediate re-trigger of useEffect
@@ -413,6 +416,17 @@ const closeNewModal = useCallback(() => {
[],
);

const getButtonSx = (status : string) => {
let btnSx = {label:"", color:""};
switch (status) {
case "received": btnSx = {label: t("putaway processing"), color:"secondary.main"}; break;
case "rejected":
case "completed": btnSx = {label: t("view stockin"), color:"info.main"}; break;
default: btnSx = {label: t("qc processing"), color:"success.main"};
}
return btnSx
};

// const handleQrCode = useCallback(
// (id: GridRowId, params: any) => () => {
// setRowModesModel((prev) => ({
@@ -532,7 +546,7 @@ const closeNewModal = useCallback(() => {
{
field: "status",
headerName: t("Status"),
width: 70,
width: 140,
// flex: 0.5,
renderCell: (params) => {
return t(`${params.row.status}`);
@@ -546,20 +560,22 @@ const closeNewModal = useCallback(() => {
// )} | ${t("putaway")} | ${t("delete")}`,
headerName: "動作",
// headerName: "start | qc | escalation | stock in | putaway | delete",
width: 300,
width: 200,
// flex: 2,
cellClassName: "actions",
getActions: (params) => {
// console.log(params.row.status);
const status = params.row.status.toLowerCase();
const btnSx = getButtonSx(status);
// console.log(stockInLineStatusMap[status]);
// console.log(session?.user?.abilities?.includes("APPROVAL"));
return [
<GridActionsCellItem
icon={<Button variant="contained">{t("qc processing")}</Button>}
icon={<Button variant="contained" sx={{ width: '150px', backgroundColor: btnSx.color }}>
{btnSx.label}</Button>}
label="start"
sx={{
color: "primary.main",
// color: "primary.main",
// marginRight: 1,
}}
// disabled={!(stockInLineStatusMap[status] === 0)}
@@ -569,20 +585,21 @@ const closeNewModal = useCallback(() => {
color="inherit"
key="edit"
/>,
<GridActionsCellItem
icon={<Button variant="contained">{t("putawayBtn")}</Button>}
label="start"
sx={{
color: "primary.main",
// marginRight: 1,
}}
// disabled={!(stockInLineStatusMap[status] === 0)}
// set _isNew to false after posting
// or check status
onClick={handleStart(params.row.id, params)}
color="inherit"
key="edit"
/>,
// <GridActionsCellItem
// icon={<Button variant="contained">{t("putawayBtn")}</Button>}
// label="start"
// sx={{
// color: "primary.main",
// // marginRight: 1,
// }}
// // disabled={!(stockInLineStatusMap[status] === 0)}
// // set _isNew to false after posting
// // or check status
// onClick={handleStart(params.row.id, params)}
// color="inherit"
// key="edit"
// />,

// <GridActionsCellItem
// icon={<Button variant="contained">{t("qc processing")}</Button>}
// label="start"


+ 3
- 3
src/components/PoDetail/PoQcStockInModal.tsx Visa fil

@@ -121,9 +121,9 @@ const PoQcStockInModal: React.FC<Props> = ({
const params = useSearchParams();
const [btnIsLoading, setBtnIsLoading] = useState(false);

console.log(params.get("id"));
console.log(itemDetail);
console.log(itemDetail.qcResult);
// console.log(params.get("id"));
// console.log(itemDetail);
// console.log(itemDetail.qcResult);
const formProps = useForm<ModalFormInput>({
defaultValues: {
...itemDetail,


+ 3
- 2
src/components/PoDetail/PutawayForm.tsx Visa fil

@@ -245,8 +245,9 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {
}, [scanner.values]);

useEffect(() => {
setValue("status", "completed");
setValue("warehouseId", options[0].value);
setValue("status", "received");
// setValue("status", "completed");
setValue("warehouseId", options[0].value); //TODO: save all warehouse entry?
}, []);

useEffect(() => {


+ 26
- 15
src/components/PoDetail/QcFormVer2.tsx Visa fil

@@ -51,6 +51,7 @@ import StockInFormVer2 from "./StockInFormVer2";
import { dummyEscalationHistory, dummyQCData, QcData } from "./dummyQcTemplate";
import { ModalFormInput } from "@/app/api/dashboard/actions";
import { escape } from "lodash";
import { PanoramaSharp } from "@mui/icons-material";

interface Props {
itemDetail: StockInLine;
@@ -216,7 +217,8 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
<FormControlLabel
value="true"
control={<Radio />}
label="合格"
label="合格"
disabled={itemDetail.status.toLowerCase() == "completed"}
sx={{
color: currentValue === true ? "green" : "inherit",
"& .Mui-checked": {color: "green"}
@@ -226,6 +228,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
value="false"
control={<Radio />}
label="不合格"
disabled={itemDetail.status.toLowerCase() == "completed"}
sx={{
color: currentValue === false ? "red" : "inherit",
"& .Mui-checked": {color: "red"}
@@ -237,7 +240,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
},
},
{
field: "failedQty",
field: "failQty",
headerName: t("failedQty"),
flex: 1,
// editable: true,
@@ -246,13 +249,13 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
type="number"
size="small"
value={!params.row.isPassed? (params.value ?? '') : '0'}
disabled={params.row.isPassed}
disabled={params.row.isPassed || itemDetail.status.toLowerCase() == "completed"}
onChange={(e) => {
const v = e.target.value;
const next = v === '' ? undefined : Number(v);
if (Number.isNaN(next)) return;
setQcItems((prev) =>
prev.map((r) => (r.id === params.id ? { ...r, failedQty: next } : r))
prev.map((r) => (r.id === params.id ? { ...r, failQty: next } : r))
);
}}
onClick={(e) => e.stopPropagation()}
@@ -271,6 +274,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
<TextField
size="small"
value={params.value ?? ''}
disabled={itemDetail.status.toLowerCase() == "completed"}
onChange={(e) => {
const remarks = e.target.value;
// const next = v === '' ? undefined : Number(v);
@@ -296,10 +300,12 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI

// Set initial value for acceptQty
useEffect(() => {
if (itemDetail?.acceptedQty !== undefined) {
setValue("acceptQty", itemDetail.acceptedQty);
if (itemDetail?.demandQty > 0) { //!== undefined) {
setValue("acceptQty", itemDetail.demandQty); // THIS NEED TO UPDATE TO NOT USE DEMAND QTY
} else {
setValue("acceptQty", itemDetail?.acceptedQty);
}
}, [itemDetail?.acceptedQty, setValue]);
}, [itemDetail?.demandQty, itemDetail?.acceptedQty, setValue]);

// const [openCollapse, setOpenCollapse] = useState(false)
const [isCollapsed, setIsCollapsed] = useState<boolean>(false);
@@ -365,14 +371,14 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
/>
</Grid>
{/* <Grid item xs={12}>
{!qcAccept && (
<Grid item xs={12}>
<EscalationComponent
forSupervisor={false}
isCollapsed={isCollapsed}
setIsCollapsed={setIsCollapsed}
/>
</Grid> */}
</Grid>)}
</>
)}
{tabIndex == 1 && (
@@ -419,14 +425,16 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
field.onChange(value);
}}
>
<FormControlLabel value="true" control={<Radio />} label="接受" />
<FormControlLabel disabled={itemDetail.status.toLowerCase() == "completed"}
value="true" control={<Radio />} label="接受" />
<Box sx={{mr:2}}>
<TextField
type="number"
label={t("acceptQty")}
sx={{ width: '150px' }}
value={qcAccept? accQty : 0 }
defaultValue={accQty}
disabled={!qcAccept}
disabled={!qcAccept || itemDetail.status.toLowerCase() == "completed"}
{...register("acceptQty", {
required: "acceptQty required!",
})}
@@ -434,13 +442,16 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
helperText={errors.acceptQty?.message}
/>
</Box>
<FormControlLabel value="false" control={<Radio />} label="不接受及上報" />
<FormControlLabel disabled={itemDetail.status.toLowerCase() == "completed"}
value="false" control={<Radio />}
sx={{"& .Mui-checked": {color: "red"}}}
label="不接受及上報" />
</RadioGroup>
)}
/>
</FormControl>
</Grid>
{/* <Grid item xs={12}>
{/* {qcAccept && <Grid item xs={12}>
<Typography variant="h6" display="block" marginBlockEnd={1}>
{t("Escalation Result")}
</Typography>
@@ -451,7 +462,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
isCollapsed={isCollapsed}
setIsCollapsed={setIsCollapsed}
/>
</Grid> */}
</Grid>} */}
</Grid>
</Grid>
</>


+ 120
- 31
src/components/PoDetail/QcStockInModalVer2.tsx Visa fil

@@ -1,6 +1,6 @@
"use client";
import { StockInLine } from "@/app/api/po";
import { ModalFormInput, PurchaseQcResult } from "@/app/api/po/actions";
import { ModalFormInput, PurchaseQcResult, StockInLineEntry, updateStockInLine } from "@/app/api/po/actions";
import { QcItemWithChecks } from "@/app/api/qc";
import {
Box,
@@ -22,6 +22,9 @@ import PutawayForm from "./PutawayForm";
import { dummyPutawayLine, dummyQCData, QcData } from "./dummyQcTemplate";
import { useGridApiRef } from "@mui/x-data-grid";
import {submitDialogWithWarning} from "../Swal/CustomAlerts";
import { PurchaseQCInput, PutawayInput } from "@/app/api/dashboard/actions";
import { arrayToDateString, arrayToInputDateString, dayjsToInputDateString } from "@/app/utils/formatUtil";
import dayjs from "dayjs";

const style = {
position: "absolute",
@@ -73,15 +76,18 @@ const PoQcStockInModalVer2: React.FC<Props> = ({
t,
i18n: { language },
} = useTranslation("purchaseOrder");

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

const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>(
(...args) => {
onClose?.(...args);
@@ -89,13 +95,34 @@ const [qcItems, setQcItems] = useState(dummyQCData)
},
[onClose],
);
const isPutaway = () => {
if (itemDetail) {
const status = itemDetail.status;
return status == "received";

} else return false;
};

useEffect(() => {
formProps.reset({
...itemDetail,
dnDate: dayjsToInputDateString(dayjs()),
putawayLine: dummyPutawayLine,
})
setOpenPutaway(isPutaway);
}, [open])

const [openPutaway, setOpenPutaway] = useState(false);
const onOpenPutaway = useCallback(() => {
setOpenPutaway(true);
}, []);
const onClosePutaway = useCallback(() => {
setOpenPutaway(false);
}, []);

// Stock In submission handler
const onSubmitStockIn = useCallback<SubmitHandler<ModalFormInput>>(
async (data, event) => {
@@ -118,14 +145,16 @@ const [qcItems, setQcItems] = useState(dummyQCData)
},
[],
);

// QC submission handler
const onSubmitQc = useCallback<SubmitHandler<ModalFormInput>>(
async (data, event) => {
console.log("QC Submission:", event!.nativeEvent);
// TODO: Move validation into QC page
// Get QC data from the shared form context
const qcAccept = data.qcAccept;
const acceptQty = data.acceptQty;
const acceptQty = data.acceptQty as number;
// Validate QC data
const validationErrors : string[] = [];
@@ -137,7 +166,7 @@ const [qcItems, setQcItems] = useState(dummyQCData)

// Check if failed items have failed quantity
const failedItemsWithoutQty = qcItems.filter(item =>
item.isPassed === false && (!item.failedQty || item.failedQty <= 0)
item.isPassed === false && (!item.failQty || item.failQty <= 0)
);
if (failedItemsWithoutQty.length > 0) {
validationErrors.push(`${t("Failed items must have failed quantity")}: ${failedItemsWithoutQty.map(item => item.qcItem).join(', ')}`);
@@ -153,6 +182,14 @@ const [qcItems, setQcItems] = useState(dummyQCData)
validationErrors.push("Accept quantity must be greater than 0");
}

// Check if dates are input
if (data.productionDate === undefined || data.productionDate == null) {
validationErrors.push("Production Date cannot be null!");
}
if (data.expiryDate === undefined || data.expiryDate == null) {
validationErrors.push("Expiry Date cannot be null!");
}

if (validationErrors.length > 0) {
console.error("QC Validation failed:", validationErrors);
alert(`未完成品檢: ${validationErrors}`);
@@ -160,35 +197,75 @@ const [qcItems, setQcItems] = useState(dummyQCData)
}

const qcData = {
qcAccept: qcAccept,
acceptQty: acceptQty,
qcItems: qcItems.map(item => ({
id: item.id,
qcItem: item.qcItem,
qcDescription: item.qcDescription,
isPassed: item.isPassed,
failedQty: (item.failedQty && !item.isPassed) || 0,
dnNo : data.dnNo? data.dnNo : "DN00000",
dnDate : data.dnDate? arrayToInputDateString(data.dnDate) : dayjsToInputDateString(dayjs()),
productionDate : arrayToInputDateString(data.productionDate),
expiryDate : arrayToInputDateString(data.expiryDate),
receiptDate : arrayToInputDateString(data.receiptDate),
qcAccept: qcAccept? qcAccept : false,
acceptQty: acceptQty? acceptQty : 0,
qcResult: qcItems.map(item => ({
qcItemId: item.id,
// qcItem: item.qcItem,
// qcDescription: item.qcDescription,
isPassed: item.isPassed? item.isPassed : false,
failQty: (item.failQty && !item.isPassed) ? item.failQty : 0,
// failedQty: (typeof item.failedQty === "number" && !item.isPassed) ? item.failedQty : 0,
remarks: item.remarks || ''
}))
};
// const qcData = data;

console.log("QC Data for submission:", qcData);
// await submitQcData(qcData);
if (!qcData.qcItems.every((qc) => qc.isPassed) && qcData.qcAccept) {
submitDialogWithWarning(onOpenPutaway, t, {title:"有不合格檢查項目,確認接受收貨?", confirmButtonText: "Confirm", html: ""});
if (!qcData.qcResult.every((qc) => qc.isPassed) && qcData.qcAccept) {
submitDialogWithWarning(() => postStockInLineWithQc(qcData), t, {title:"有不合格檢查項目,確認接受收貨?",
confirmButtonText: t("confirm putaway"), html: ""});
return;
}

if (qcData.qcAccept) {
onOpenPutaway();
} else {
onClose();
}
await postStockInLineWithQc(qcData);
// return;
},
[onOpenPutaway, qcItems],
);
const postStockInLineWithQc = useCallback(async (qcData: PurchaseQCInput) => {
const args = {
...qcData
// id: itemDetail.id,
// purchaseOrderId: itemDetail.purchaseOrderId,
// purchaseOrderLineId: itemDetail.purchaseOrderLineId,
// itemId: itemDetail.itemId,
// ...data,
// productionDate: productionDate,
// expiryDate: expiryDate,
// receiptDate: receiptDate,
} as ModalFormInput;

await postStockInLine(args);

if (qcData.qcAccept) {
// submitDialogWithWarning(onOpenPutaway, t, {title:"Save success, confirm to proceed?",
// confirmButtonText: t("confirm putaway"), html: ""});
onOpenPutaway();
} else {
closeHandler({}, "backdropClick");
}
return ;
},[onOpenPutaway,closeHandler]);

const postStockInLine = useCallback(async (args: ModalFormInput) => {
const submitData = {
...itemDetail, ...args
} as StockInLineEntry & ModalFormInput;
console.log(submitData);

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

// Email supplier handler
const onSubmitEmailSupplier = useCallback<SubmitHandler<ModalFormInput>>(
async (data, event) => {
@@ -222,12 +299,22 @@ const [qcItems, setQcItems] = useState(dummyQCData)
// binLocation: data.binLocation,
// putawayQuantity: data.putawayQuantity,
// putawayNotes: data.putawayNotes,
data: data,
acceptQty: itemDetail.demandQty? itemDetail.demandQty : itemDetail.acceptedQty,
...data,
dnDate : data.dnDate? arrayToInputDateString(data.dnDate) : dayjsToInputDateString(dayjs()),
productionDate : arrayToInputDateString(data.productionDate),
expiryDate : arrayToInputDateString(data.expiryDate),
receiptDate : arrayToInputDateString(data.receiptDate),

// Add other putaway specific fields
};
} as ModalFormInput;
console.log("Putaway Data:", putawayData);

// Handle putaway submission logic here
const res = await postStockInLine(putawayData);
console.log("result ", res);
// Close modal after successful putaway
closeHandler({}, "backdropClick");
},
@@ -239,6 +326,7 @@ const [qcItems, setQcItems] = useState(dummyQCData)
// Handle print logic here
window.print();
}, []);
const acceptQty = formProps.watch("acceptedQty")

const checkQcIsPassed = useCallback((qcItems: QcData[]) => {
@@ -272,7 +360,7 @@ const [qcItems, setQcItems] = useState(dummyQCData)
marginRight: 3,
}}
>
{openPutaway ? (
{openPutaway ? (
<Box
component="form"
onSubmit={formProps.handleSubmit(onSubmitPutaway)}
@@ -299,6 +387,7 @@ const [qcItems, setQcItems] = useState(dummyQCData)
variant="contained"
color="primary"
sx={{ mt: 1 }}
onClick={formProps.handleSubmit(onSubmitPutaway)}
>
{t("confirm putaway")}
</Button>
@@ -320,7 +409,7 @@ const [qcItems, setQcItems] = useState(dummyQCData)
<StockInFormVer2 itemDetail={itemDetail} disabled={false} />
</Grid>
</Grid>
<Stack direction="row" justifyContent="flex-end" gap={1}>
{/* <Stack direction="row" justifyContent="flex-end" gap={1}>
<Button
id="stockInSubmit"
type="button"
@@ -330,7 +419,7 @@ const [qcItems, setQcItems] = useState(dummyQCData)
>
{t("submitStockIn")}
</Button>
</Stack>
</Stack> */}
<Grid
container
justifyContent="flex-start"
@@ -345,7 +434,7 @@ const [qcItems, setQcItems] = useState(dummyQCData)
/>
</Grid>
<Stack direction="row" justifyContent="flex-end" gap={1}>
<Button
{itemDetail.status.toLowerCase() == "rejected" && (<Button
id="emailSupplier"
type="button"
variant="contained"
@@ -354,8 +443,8 @@ const [qcItems, setQcItems] = useState(dummyQCData)
onClick={formProps.handleSubmit(onSubmitEmailSupplier)}
>
{t("email supplier")}
</Button>
<Button
</Button>)}
{itemDetail.status.toLowerCase() != "completed" && (<Button
id="qcSubmit"
type="button"
variant="contained"
@@ -363,8 +452,8 @@ const [qcItems, setQcItems] = useState(dummyQCData)
sx={{ mt: 1 }}
onClick={formProps.handleSubmit(onSubmitQc)}
>
{t("confirm putaway")}
</Button>
{t("confirm qc result")}
</Button>)}
</Stack>
</>
)}


+ 6
- 5
src/components/PoDetail/StockInFormVer2.tsx Visa fil

@@ -123,7 +123,7 @@ const StockInFormVer2: React.FC<Props> = ({
{...register("dnNo", {
// required: "productLotNo required!",
})}
disabled={true}
disabled={itemDetail.status.toLowerCase() == "completed"}
// error={Boolean(errors.productLotNo)}
// helperText={errors.productLotNo?.message}
/>
@@ -205,7 +205,7 @@ const StockInFormVer2: React.FC<Props> = ({
{...register("productLotNo", {
// required: "productLotNo required!",
})}
disabled={disabled}
disabled={itemDetail.status.toLowerCase() == "completed"}
error={Boolean(errors.productLotNo)}
helperText={errors.productLotNo?.message}
/>
@@ -226,7 +226,7 @@ const StockInFormVer2: React.FC<Props> = ({
sx={{ width: "100%" }}
label={t("productionDate")}
value={productionDate ? dayjs(productionDate) : undefined}
disabled={disabled}
disabled={itemDetail.status.toLowerCase() == "completed"}
onChange={(date) => {
if (!date) return;
setValue(
@@ -275,7 +275,7 @@ const StockInFormVer2: React.FC<Props> = ({
sx={{ width: "100%" }}
label={t("expiryDate")}
value={expiryDate ? dayjs(expiryDate) : undefined}
disabled={disabled}
disabled={itemDetail.status.toLowerCase() == "completed"}
onChange={(date) => {
console.log(date);
if (!date) return;
@@ -322,10 +322,11 @@ const StockInFormVer2: React.FC<Props> = ({
<TextField
label={t("acceptedQty")}
fullWidth
disabled={itemDetail.status.toLowerCase() == "completed"}
{...register("acceptedQty", {
required: "acceptedQty required!",
})}
disabled={true}
// disabled={true}
// disabled={disabled}
// error={Boolean(errors.acceptedQty)}
// helperText={errors.acceptedQty?.message}


+ 6
- 6
src/components/PoDetail/dummyQcTemplate.tsx Visa fil

@@ -5,7 +5,7 @@ export interface QcData {
qcItem: string,
qcDescription: string,
isPassed: boolean | undefined
failedQty: number | undefined
failQty: number | undefined
remarks: string | undefined
}

@@ -15,7 +15,7 @@ export const dummyQCData: QcData[] = [
qcItem: "包裝",
qcDescription: "有破爛、污糟、脹袋、積水、與實物不符等任何一種情況,則不合格",
isPassed: undefined,
failedQty: undefined,
failQty: undefined,
remarks: undefined,
},
{
@@ -23,7 +23,7 @@ export const dummyQCData: QcData[] = [
qcItem: "肉質",
qcDescription: "肉質鬆散,則不合格",
isPassed: undefined,
failedQty: undefined,
failQty: undefined,
remarks: undefined,
},
{
@@ -31,7 +31,7 @@ export const dummyQCData: QcData[] = [
qcItem: "顔色",
qcDescription: "不是食材應有的顔色、顔色不均匀、出現其他顔色、腌料/醬顔色不均匀,油脂部分變綠色、黃色,",
isPassed: undefined,
failedQty: undefined,
failQty: undefined,
remarks: undefined,
},
{
@@ -39,7 +39,7 @@ export const dummyQCData: QcData[] = [
qcItem: "狀態",
qcDescription: "有結晶、結霜、解凍跡象、發霉、散發異味等任何一種情況,則不合格",
isPassed: undefined,
failedQty: undefined,
failQty: undefined,
remarks: undefined,
},
{
@@ -47,7 +47,7 @@ export const dummyQCData: QcData[] = [
qcItem: "異物",
qcDescription: "有不屬於本食材的雜質,則不合格",
isPassed: undefined,
failedQty: undefined,
failQty: undefined,
remarks: undefined,
},
]


+ 1
- 0
src/components/Swal/CustomAlerts.tsx Visa fil

@@ -126,6 +126,7 @@ export const submitDialogWithWarning = async (
title: t("Do you want to submit?") as SweetAlertTitle,
html: t("Warning!") as SweetAlertHtml,
confirmButtonText: t("Submit") as SweetAlertConfirmButtonText,
// cancelButtonText: t("Cancel") as SweetAlertConfirmButtonText,
},
) => {
// console.log(props)


+ 2
- 2
src/i18n/index.tsx Visa fil

@@ -6,8 +6,8 @@ import { authOptions } from "@/config/authConfig";
import I18nClientProvider from "./I18nClientProvider";
import universalLanguageDetect from "@unly/universal-language-detector";

const FALLBACK_LANG = "en";
const SUPPORTED_LANGUAGES = ["en", "zh"];
const FALLBACK_LANG = "zh";
const SUPPORTED_LANGUAGES = ["zh"];

export const detectLanguage = async (): Promise<string> => {
// Logic to get language preference from cookies/headers/session


+ 4
- 0
src/i18n/zh/dashboard.json Visa fil

@@ -14,6 +14,10 @@
"Humidity status": "濕度狀態",
"Warehouse status": "倉庫狀態",
"Progress chart": "進度圖表",
"Purchase Order Code": "採購單號",
"Item Name": "貨品名稱",
"Escalation Level": "上報等級",
"Reason": "原因",
"Order completion": "訂單完成度",
"Raw material": "原料",
"Consumable": "消耗品",


+ 9
- 4
src/i18n/zh/purchaseOrder.json Visa fil

@@ -19,6 +19,7 @@
"Start Fail": "開始失敗",
"Start PO": "開始採購訂單",
"Do you want to complete?": "確定完成嗎?",
"Cancel": "取消",
"Complete": "完成",
"Complete Success": "完成成功",
"Complete Fail": "完成失敗",
@@ -64,9 +65,9 @@
"determine2": "上報2",
"determine3": "上報3",
"receiving": "收貨中",
"received": "已收",
"completed": "已完成",
"rejected": "已拒絕",
"received": "已收",
"completed": "已上架",
"rejected": "已拒絕及上報",
"status": "狀態",

"acceptedQty must not greater than": "接受數量不得大於",
@@ -107,6 +108,9 @@

"Accept submit": "接受來貨",
"qc processing": "處理來貨及品檢",
"putaway processing": "處理來貨及上架",
"view stockin": "查看收貨及品檢",
"putaway processing": "處理來貨及上架",
"putawayBtn": "上架",
"dnNo": "送貨單編號",
"dnDate": "送貨單日期",
@@ -120,9 +124,10 @@
"update qc info": "更新品檢資料",
"email supplier": "電郵供應商",
"confirm putaway": "確定及上架",
"confirm qc result": "確定品檢結果",
"warehouse": "倉庫",
"qcItem": "檢項目",
"qcItem": "檢項目",
"passed": "接受",
"failed": "不接受",
"failedQty": "不合格數",


Laddar…
Avbryt
Spara