Bläddra i källkod

remove hardcoded date, import download fg stock in label api, handling fallbacks

master^2
kelvin.yau 20 timmar sedan
förälder
incheckning
f9e2ea1134
5 ändrade filer med 175 tillägg och 53 borttagningar
  1. +20
    -2
      src/app/api/jo/actions.ts
  2. +2
    -1
      src/components/ProductionProcess/ProductionProcessList.tsx
  3. +3
    -2
      src/components/Qc/QcComponent.tsx
  4. +37
    -23
      src/components/Qc/QcStockInModal.tsx
  5. +113
    -25
      src/components/StockIn/FgStockInForm.tsx

+ 20
- 2
src/app/api/jo/actions.ts Visa fil

@@ -1,11 +1,12 @@
"use server";

import { cache } from 'react';
import { Pageable, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil";
import { Pageable, serverFetchBlob, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil";
import { JobOrder, JoStatus, Machine, Operator } from ".";
import { BASE_API_URL } from "@/config/api";
import { revalidateTag } from "next/cache";
import { convertObjToURLSearchParams } from "@/app/utils/commonUtil";
import { FileResponse } from "@/app/api/pdf/actions";

export interface SaveJo {
bomId: number;
@@ -155,7 +156,7 @@ export const printFGStockInLabel = cache(async(data: PrintFGStockInLabelRequest)
}
return serverFetchWithNoContent(
`${BASE_API_URL}/jo/print-FGPickRecordLabel?${params.toString()}`,
`${BASE_API_URL}/jo/print-FGStockInLabel?${params.toString()}`,
{
method: "GET",
next: {
@@ -1027,3 +1028,20 @@ export async function PrintPickRecord(request: PrintPickRecordRequest){

return { success: true, message: "Print job sent successfully (Pick Record)" } as PrintPickRecordResponse;
}

export interface ExportFGStockInLabelRequest {
stockInLineId: number;
}

export const fetchFGStockInLabel = async (data: ExportFGStockInLabelRequest): Promise<FileResponse> => {
const reportBlob = await serverFetchBlob<FileResponse>(
`${BASE_API_URL}/jo/FGStockInLabel`,
{
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
},
);

return reportBlob;
};

+ 2
- 1
src/components/ProductionProcess/ProductionProcessList.tsx Visa fil

@@ -94,7 +94,8 @@ const ProductProcessList: React.FC<ProductProcessListProps> = ({ onSelectProcess

setModalInfo({
id: process.stockInLineId,
expiryDate: dayjs().add(1, "month").format(OUTPUT_DATE_FORMAT),
//expiryDate: dayjs().add(1, "month").format(OUTPUT_DATE_FORMAT),
// 视需要补 itemId、jobOrderId 等
});
setOpenModal(true);


+ 3
- 2
src/components/Qc/QcComponent.tsx Visa fil

@@ -413,10 +413,11 @@ useEffect(() => {
} else { return 60}
};

const formattedDesc = (content: string = "") => {
const formattedDesc = (content: string | null | undefined = "") => {
const safeContent = content || "";
return (
<>
{content.split("\\n").map((line, index) => (
{safeContent.split("\\n").map((line, index) => (
<span key={index}> {line} <br/></span>
))}
</>


+ 37
- 23
src/components/Qc/QcStockInModal.tsx Visa fil

@@ -40,7 +40,7 @@ import { StockInLineEntry, updateStockInLine, printQrCodeForSil, PrintQrCodeForS
import { fetchStockInLineInfo } from "@/app/api/stockIn/actions";
import FgStockInForm from "../StockIn/FgStockInForm";
import LoadingComponent from "../General/LoadingComponent";
import { printFGStockInLabel, PrintFGStockInLabelRequest } from "@/app/api/jo/actions";
import { printFGStockInLabel, PrintFGStockInLabelRequest, fetchFGStockInLabel } from "@/app/api/jo/actions";

const style = {
position: "absolute",
@@ -119,7 +119,7 @@ const QcStockInModal: React.FC<Props> = ({
const res = await fetchStockInLineInfo(stockInLineId);
if (res) {
console.log("%c Fetched Stock In Line: ", "color:orange", res);
setStockInLineInfo({...inputDetail, ...res, expiryDate: inputDetail?.expiryDate}); // TODO review to overwrite res with inputDetail instead (revise PO fetching data)
setStockInLineInfo({...inputDetail, ...res, expiryDate: res.expiryDate});
// fetchQcResultData(stockInLineId);
} else throw("Result is undefined");
@@ -168,8 +168,8 @@ const QcStockInModal: React.FC<Props> = ({
{
...d,
// status: d.status ?? "pending",
productionDate: d.productionDate ? arrayToDateString(d.productionDate, "input") : undefined,
expiryDate: d.expiryDate ? arrayToDateString(d.expiryDate, "input") : undefined,
productionDate: d.productionDate ? arrayToDateString(d.productionDate, "input") : dayjs().format(INPUT_DATE_FORMAT),
expiryDate: d.expiryDate ? (Array.isArray(d.expiryDate) ? arrayToDateString(d.expiryDate, "input") : d.expiryDate) : undefined,
receiptDate: d.receiptDate ? arrayToDateString(d.receiptDate, "input")
: dayjs().add(0, "month").format(INPUT_DATE_FORMAT),
acceptQty: d.status != StockInStatus.REJECTED ? (d.demandQty?? d.acceptedQty) : 0,
@@ -350,9 +350,9 @@ const QcStockInModal: React.FC<Props> = ({
const qcData = {
dnNo : data.dnNo? data.dnNo : "DN00000",
// dnDate : data.dnDate? arrayToDateString(data.dnDate, "input") : dayjsToInputDateString(dayjs()),
productionDate : arrayToDateString(data.productionDate, "input"),
expiryDate : arrayToDateString(data.expiryDate, "input"),
receiptDate : arrayToDateString(data.receiptDate, "input"),
productionDate : data.productionDate ? (Array.isArray(data.productionDate) ? arrayToDateString(data.productionDate, "input") : data.productionDate) : undefined,
expiryDate : data.expiryDate ? (Array.isArray(data.expiryDate) ? arrayToDateString(data.expiryDate, "input") : data.expiryDate) : undefined,
receiptDate : data.receiptDate ? (Array.isArray(data.receiptDate) ? arrayToDateString(data.receiptDate, "input") : data.receiptDate) : undefined,
qcAccept: qcAccept? qcAccept : false,
acceptQty: acceptQty? acceptQty : 0,
@@ -540,24 +540,38 @@ const QcStockInModal: React.FC<Props> = ({
// return isPassed
// }, [acceptQty, formProps])

const printQrcode = useCallback(
async () => {
setIsPrinting(true);
try {
const postData = { stockInLineIds: [stockInLineInfo?.id] };
const response = await fetchPoQrcode(postData);
if (response) {
console.log(response);
downloadFile(new Uint8Array(response.blobValue), response.filename!);
}
} catch (e) {
console.log("%c Error downloading QR Code", "color:red", e);
} finally {
const printQrcode = useCallback(
async () => {
setIsPrinting(true);
try {
let response;
if (printSource === "productionProcess") {
// Use FG Stock In Label download API for production process
if (!stockInLineInfo?.id) {
console.error("Stock In Line ID is required for download");
setIsPrinting(false);
return;
}
},
[stockInLineInfo],
);
const postData = { stockInLineId: stockInLineInfo.id };
response = await fetchFGStockInLabel(postData);
} else {
const postData = { stockInLineIds: [stockInLineInfo?.id] };
response = await fetchPoQrcode(postData);
}
if (response) {
console.log(response);
downloadFile(new Uint8Array(response.blobValue), response.filename!);
}
} catch (e) {
console.log("%c Error downloading QR Code", "color:red", e);
} finally {
setIsPrinting(false);
}
},
[stockInLineInfo, printSource],
);

return (
<>


+ 113
- 25
src/components/StockIn/FgStockInForm.tsx Visa fil

@@ -12,11 +12,12 @@ import {
TextField,
Tooltip,
Typography,
Button,
} from "@mui/material";
import { Controller, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import StyledDataGrid from "../StyledDataGrid";
import { useCallback, useEffect, useMemo } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import {
GridColDef,
GridRowIdGetter,
@@ -35,6 +36,11 @@ import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import { INPUT_DATE_FORMAT, OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
import dayjs from "dayjs";
import CalculateExpiryDateModal from "./CalculateExpiryDateModal";
import { InputAdornment } from "@mui/material";
import { dayjsToDateString } from "@/app/utils/formatUtil";


// change PurchaseQcResult to stock in entry props
interface Props {
itemDetail: StockInLine;
@@ -115,6 +121,8 @@ const FgStockInForm: React.FC<Props> = ({
console.log(errors);
}, [errors]);
const [openModal, setOpenModal] = useState<boolean>(false);
const [openExpDatePicker, setOpenExpDatePicker] = useState<boolean>(false);
const productionDate = watch("productionDate");
const expiryDate = watch("expiryDate");
const uom = watch("uom");
@@ -140,7 +148,23 @@ const FgStockInForm: React.FC<Props> = ({
console.log("%c StockInForm itemDetail update: ", "color: brown", itemDetail);
}, [itemDetail]);

return (
const handleOpenModal = useCallback(() => {
setOpenModal(true);
}, []);

const handleOnModalClose = useCallback(() => {
setOpenExpDatePicker(false);
setOpenModal(false);
}, []);

const handleReturnExpiryDate = useCallback((result: dayjs.Dayjs) => {
if (result) {
setValue("expiryDate", dayjsToDateString(result));
}
}, [setValue]);

return (
<>
<Grid container justifyContent="flex-start" alignItems="flex-start">
{/* <Grid item xs={12}>
<Typography variant="h6" display="block" marginBlockEnd={1}>
@@ -250,6 +274,44 @@ const FgStockInForm: React.FC<Props> = ({
/>)
}
</Grid>
<Grid item xs={6}>
<Controller
control={control}
name="productionDate"
render={({ field }) => {
return (
<LocalizationProvider
dateAdapter={AdapterDayjs}
adapterLocale={`${language}-hk`}
>
<DatePicker
{...field}
sx={textfieldSx}
label={t("productionDate")}
value={productionDate ? dayjs(productionDate) : undefined}
format={OUTPUT_DATE_FORMAT}
disabled={disabled}
onChange={(date) => {
if (!date) return;
setValue(
"productionDate",
date.format(INPUT_DATE_FORMAT),
);
}}
inputRef={field.ref}
slotProps={{
textField: {
error: Boolean(errors.productionDate?.message),
helperText: errors.productionDate?.message,
},
}}
/>
</LocalizationProvider>
);
}}
/>
</Grid>
{/* {putawayMode || (<>
{/* {putawayMode || (<>
<Grid item xs={6}>
<Controller
@@ -313,28 +375,47 @@ const FgStockInForm: React.FC<Props> = ({
dateAdapter={AdapterDayjs}
adapterLocale={`${language}-hk`}
>
<DatePicker
{...field}
sx={textfieldSx}
label={t("expiryDate")}
value={expiryDate ? dayjs(expiryDate) : undefined}
format={OUTPUT_DATE_FORMAT}
disabled={disabled}
onChange={(date) => {
if (!date) return;
console.log(date.format(INPUT_DATE_FORMAT));
setValue("expiryDate", date.format(INPUT_DATE_FORMAT));
// field.onChange(date);
}}
inputRef={field.ref}
slotProps={{
textField: {
// required: true,
error: Boolean(errors.expiryDate?.message),
helperText: errors.expiryDate?.message,
},
}}
/>
<DatePicker
{...field}
sx={textfieldSx}
label={t("expiryDate")}
value={expiryDate ? dayjs(expiryDate) : undefined}
format={OUTPUT_DATE_FORMAT}
disabled={disabled}
onChange={(date) => {
if (!date) return;
console.log(date.format(INPUT_DATE_FORMAT));
setValue("expiryDate", date.format(INPUT_DATE_FORMAT));
}}
inputRef={field.ref}
open={openExpDatePicker && !openModal}
onOpen={() => setOpenExpDatePicker(true)}
onClose={() => setOpenExpDatePicker(false)}
slotProps={{
textField: {
InputProps: {
...(!disabled && {
endAdornment: (
<InputAdornment position='end'>
<Button
type="button"
variant="contained"
color="primary"
sx={{ fontSize: '24px' }}
onClick={handleOpenModal}
>
{t("Calculate Expiry Date")}
</Button>
</InputAdornment>
),
})
},
error: Boolean(errors.expiryDate?.message),
helperText: errors.expiryDate?.message,
onClick: () => setOpenExpDatePicker(true),
},
}}
/>
</LocalizationProvider>
);
}}
@@ -442,6 +523,13 @@ const FgStockInForm: React.FC<Props> = ({
</Grid> */}
</Grid>
</Grid>
);
<CalculateExpiryDateModal
open={openModal}
onClose={handleOnModalClose}
onSubmit={handleReturnExpiryDate}
textfieldSx={textfieldSx}
/>
</>
);
};
export default FgStockInForm;

Laddar…
Avbryt
Spara