Parcourir la source

update putaway

master
cyril.tsui il y a 9 heures
Parent
révision
37ed4d31aa
18 fichiers modifiés avec 244 ajouts et 69 suppressions
  1. +2
    -0
      src/app/(main)/po/edit/page.tsx
  2. +28
    -6
      src/app/api/po/actions.ts
  3. +2
    -0
      src/app/api/po/index.ts
  4. +21
    -0
      src/app/api/settings/printer/index.ts
  5. +12
    -0
      src/app/api/warehouse/index.ts
  6. +12
    -11
      src/components/InputDataGrid/InputDataGrid.tsx
  7. +7
    -7
      src/components/PickOrderSearch/PutawayForm.tsx
  8. +2
    -2
      src/components/PickOrderSearch/dummyQcTemplate.tsx
  9. +1
    -1
      src/components/PickOrderSearch/pickorderModelVer2.tsx
  10. +4
    -1
      src/components/PoDetail/PoDetail.tsx
  11. +5
    -2
      src/components/PoDetail/PoDetailWrapper.tsx
  12. +6
    -2
      src/components/PoDetail/PoInputGrid.tsx
  13. +2
    -2
      src/components/PoDetail/PoQcStockInModal.tsx
  14. +50
    -11
      src/components/PoDetail/PutawayForm.tsx
  15. +82
    -18
      src/components/PoDetail/QcStockInModalVer2.tsx
  16. +2
    -2
      src/components/PoDetail/QrModal.tsx
  17. +3
    -3
      src/components/PoDetail/dummyQcTemplate.tsx
  18. +3
    -1
      src/i18n/zh/purchaseOrder.json

+ 2
- 0
src/app/(main)/po/edit/page.tsx Voir le fichier

@@ -1,3 +1,4 @@
import { fetchPrinterCombo } from "@/app/api/settings/printer";
import { fetchEscalationCombo } from "@/app/api/user";
import { SearchParams } from "@/app/utils/fetchUtil";
import { TypeEnum } from "@/app/utils/typeEnum";
@@ -25,6 +26,7 @@ const PoEdit: React.FC<Props> = async ({ searchParams }) => {
}

fetchEscalationCombo()
fetchPrinterCombo()

return (
<>


+ 28
- 6
src/app/api/po/actions.ts Voir le fichier

@@ -6,10 +6,11 @@ import { revalidateTag } from "next/cache";
import { cache } from "react";
import { PoResult, StockInLine } from ".";
//import { serverFetchJson } from "@/app/utils/fetchUtil";
import { serverFetchJson } from "../../utils/fetchUtil";
import { serverFetchJson, serverFetchWithNoContent } from "../../utils/fetchUtil";
import { QcItemResult } from "../settings/qcItem";
import { RecordsRes } from "../utils";
import { Uom } from "../settings/uom";
import { convertObjToURLSearchParams } from "@/app/utils/commonUtil";
// import { BASE_API_URL } from "@/config/api";

export interface PostStockInLineResponse<T> {
@@ -81,26 +82,34 @@ export interface EscalationInput {
acceptedQty?: number; // this is the qty to be escalated
// escalationQty: number
}
export interface PutawayLine {
export interface PutAwayLine {
id?: number
qty: number
warehouseId: number;
warehouse: string;
printQty: number
printQty: number;
_isNew?: boolean;
printQty?: number;
}
export interface PutawayInput {
export interface PutAwayInput {
status: string;
acceptedQty: number;
warehouseId: number;
putawayLine: PutawayLine[]
putAwayLines: PutAwayLine[]
}

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

export interface PrintQrCodeForSilRequest {
stockInLineId: number;
printerId: number;
printQty?: number;
}

export const testFetch = cache(async (id: number) => {
return serverFetchJson<PoResult>(`${BASE_API_URL}/po/detail/${id}`, {
next: { tags: ["po"] },
@@ -217,3 +226,16 @@ export const testing = cache(async (queryParams?: Record<string, any>) => {
);
}
});

export const printQrCodeForSil = cache(async(data: PrintQrCodeForSilRequest) => {
const params = convertObjToURLSearchParams(data)
return serverFetchWithNoContent(`${BASE_API_URL}/stockInLine/printQrCode?${params}`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
next: {
tags: ["printQrCodeForSil"],
},
},
)
})

+ 2
- 0
src/app/api/po/index.ts Voir le fichier

@@ -6,6 +6,7 @@ import { serverFetchJson } from "../../utils/fetchUtil";
import { BASE_API_URL } from "../../../config/api";
import { Uom } from "../settings/uom";
import { RecordsRes } from "../utils";
import { PutAwayLine } from "./actions";

export interface PoResult {
id: number;
@@ -82,6 +83,7 @@ export interface StockInLine {
dnDate?: number[];
stockQty?: number;
handlerId?: number;
putAwayLines?: PutAwayLine[];
}

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


+ 21
- 0
src/app/api/settings/printer/index.ts Voir le fichier

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

export interface PrinterCombo {
id: number;
value: number;
label?: string;
code?: string;
name?: string;
description?: string;
ip?: string;
port?: number;
}

export const fetchPrinterCombo = cache(async () => {
return serverFetchJson<PrinterCombo[]>(`${BASE_API_URL}/printers/combo`, {
next: { tags: ["qcItems"] },
})
})

+ 12
- 0
src/app/api/warehouse/index.ts Voir le fichier

@@ -10,8 +10,20 @@ export interface WarehouseResult {
description: string;
}

export interface WarehouseCombo {
id: number;
value: number;
label: string;
}

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

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

+ 12
- 11
src/components/InputDataGrid/InputDataGrid.tsx Voir le fichier

@@ -74,6 +74,7 @@ export interface InputDataGridProps<T, V, E> {
validateRow: (newRow: GridRowModel<TableRow<V, E>>) => E;
needAdd?: boolean;
showRemoveBtn?: boolean;
addRowDefaultValue?: Partial<V>;
}

export interface SelectionInputDataGridProps<T, V, E> {
@@ -85,6 +86,7 @@ export interface SelectionInputDataGridProps<T, V, E> {
validateRow: (newRow: GridRowModel<TableRow<V, E>>) => E;
needAdd?: boolean;
showRemoveBtn?: boolean;
addRowDefaultValue?: Partial<V>;
}

export type Props<T, V, E> =
@@ -112,6 +114,7 @@ function InputDataGrid<T, V, E>({
validateRow,
needAdd,
showRemoveBtn = true,
addRowDefaultValue = {},
}: Props<T, V, E>) {
const {
t,
@@ -126,13 +129,13 @@ function InputDataGrid<T, V, E>({
[],
);
const list: TableRow<V, E>[] = getValues(formKey);
console.log(list)
// console.log(list)
const [rows, setRows] = useState<TableRow<V, E>[]>(() => {
const list: TableRow<V, E>[] = getValues(formKey);
console.log(list)
// console.log(list)
return list && list.length > 0 ? list : [];
});
console.log(rows)
// console.log(rows)
// const originalRows = list && list.length > 0 ? list : [];
const originalRows = useMemo(() => (
list && list.length > 0 ? list : []
@@ -145,7 +148,7 @@ function InputDataGrid<T, V, E>({
const rowModel: GridRowSelectionModel = getValues(
`${formKey}_active`,
) as GridRowSelectionModel;
console.log(rowModel);
// console.log(rowModel);
return rowModel;
});

@@ -162,7 +165,7 @@ function InputDataGrid<T, V, E>({
(updateError: ProcessRowUpdateError<T, E>) => {
const errors = updateError.errors;
const row = updateError.row;
console.log(errors);
// console.log(errors);
apiRef.current.updateRows([{ ...row, _error: errors }]);
},
[apiRef],
@@ -176,7 +179,7 @@ function InputDataGrid<T, V, E>({
/////////////////
// validation here
const errors = validateRow(newRow);
console.log(newRow);
// console.log(newRow);
if (errors) {
throw new ProcessRowUpdateError(
originalRow,
@@ -189,7 +192,6 @@ function InputDataGrid<T, V, E>({
const rowToSave = {
...updatedRow,
} as TableRow<V, E>; /// test
console.log(rowToSave);
setRows((rw) =>
rw.map((r) => (getRowId(r) === getRowId(originalRow) ? rowToSave : r)),
);
@@ -198,8 +200,8 @@ function InputDataGrid<T, V, E>({
[validateRow, getRowId],
);

const addRow = useCallback(() => {
const newEntry = { id: Date.now(), _isNew: true } as TableRow<V, E>;
const addRow = useCallback((addRowDefaultValue: Partial<V>) => {
const newEntry = { ...addRowDefaultValue, id: Date.now(), _isNew: true } as TableRow<V, E>;
setRows((prev) => [...prev, newEntry]);
setRowModesModel((model) => ({
...model,
@@ -290,7 +292,6 @@ function InputDataGrid<T, V, E>({
// sync useForm
useEffect(() => {
// console.log(formKey)
// console.log(rows)
setValue(formKey, rows);
}, [formKey, rows, setValue]);

@@ -300,7 +301,7 @@ function InputDataGrid<T, V, E>({
disableRipple
variant="outlined"
startIcon={<Add />}
onClick={addRow}
onClick={() => addRow(addRowDefaultValue)}
size="small"
>
新增


+ 7
- 7
src/components/PickOrderSearch/PutawayForm.tsx Voir le fichier

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

import { PurchaseQcResult, PutawayInput, PutawayLine } from "@/app/api/po/actions";
import { PurchaseQcResult, PutAwayInput, PutAwayLine } from "@/app/api/po/actions";
import {
Autocomplete,
Box,
@@ -61,11 +61,11 @@ interface Props {
}
type EntryError =
| {
[field in keyof PutawayLine]?: string;
[field in keyof PutAwayLine]?: string;
}
| undefined;

type PutawayRow = TableRow<Partial<PutawayLine>, EntryError>;
type PutawayRow = TableRow<Partial<PutAwayLine>, EntryError>;

const style = {
position: "absolute",
@@ -93,7 +93,7 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {
resetField,
setError,
clearErrors,
} = useFormContext<PutawayInput>();
} = useFormContext<PutAwayInput>();
console.log(itemDetail);
// const [recordQty, setRecordQty] = useState(0);
const [warehouseId, setWarehouseId] = useState(itemDetail.defaultWarehouseId);
@@ -143,7 +143,7 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {
},
[],
);
console.log(watch("putawayLine"))
console.log(watch("putAwayLines"))
// const accQty = watch("acceptedQty");
// const validateForm = useCallback(() => {
// console.log(accQty);
@@ -492,10 +492,10 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {
style={{ display: "flex", justifyContent: "center" }}
>
{/* <QrCode content={qrContent} sx={{ width: 200, height: 200 }} /> */}
<InputDataGrid<PutawayInput, PutawayLine, EntryError>
<InputDataGrid<PutAwayInput, PutAwayLine, EntryError>
apiRef={apiRef}
checkboxSelection={false}
_formKey={"putawayLine"}
_formKey={"putAwayLines"}
columns={columns}
validateRow={validation}
needAdd={true}


+ 2
- 2
src/components/PickOrderSearch/dummyQcTemplate.tsx Voir le fichier

@@ -1,4 +1,4 @@
import { PutawayLine } from "@/app/api/po/actions"
import { PutAwayLine } from "@/app/api/po/actions"

export interface QcData {
id: number,
@@ -67,7 +67,7 @@ export const dummyEscalationHistory: EscalationData[] = [
},
]

export const dummyPutawayLine: PutawayLine[] = [
export const dummyPutawayLine: PutAwayLine[] = [
{
id: 1,
qty: 100,


+ 1
- 1
src/components/PickOrderSearch/pickorderModelVer2.tsx Voir le fichier

@@ -80,7 +80,7 @@ const [qcItems, setQcItems] = useState(dummyQCData)
const formProps = useForm<any>({
defaultValues: {
...itemDetail,
putawayLine: dummyPutawayLine,
putAwayLine: dummyPutawayLine,
// receiptDate: itemDetail.receiptDate || dayjs().add(-1, "month").format(INPUT_DATE_FORMAT),
// warehouseId: itemDetail.defaultWarehouseId || 0
},


+ 4
- 1
src/components/PoDetail/PoDetail.tsx Voir le fichier

@@ -79,6 +79,7 @@ import { DatePicker, LocalizationProvider, zhHK } from "@mui/x-date-pickers";
import { debounce } from "lodash";
import LoadingComponent from "../General/LoadingComponent";
import { getMailTemplateForStockInLine } from "@/app/api/mailTemplate/actions";
import { PrinterCombo } from "@/app/api/settings/printer";
//import { useRouter } from "next/navigation";


@@ -86,6 +87,7 @@ type Props = {
po: PoResult;
qc: QcItemWithChecks[];
warehouse: WarehouseResult[];
printerCombo: PrinterCombo[];
};

type EntryError =
@@ -188,7 +190,7 @@ interface PolInputResult {
dnQty: number,
}

const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo }) => {
const cameras = useContext(CameraContext);
// console.log(cameras);
const { t } = useTranslation("purchaseOrder");
@@ -858,6 +860,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
warehouse={warehouse}
fetchPoDetail={fetchPoDetail}
handleMailTemplateForStockInLine={handleMailTemplateForStockInLine}
printerCombo={printerCombo}
/>
</Box>
</TableCell>


+ 5
- 2
src/components/PoDetail/PoDetailWrapper.tsx Voir le fichier

@@ -11,6 +11,7 @@ import { QcItemWithChecks } from "@/app/api/qc";
import { fetchWarehouseList } from "@/app/api/warehouse";
import { fetchQcItemCheck } from "@/app/api/qc/actions";
import { fetchEscalationCombo } from "@/app/api/user";
import { fetchPrinterCombo } from "@/app/api/settings/printer";

interface SubComponents {
Loading: typeof PoDetailLoading;
@@ -25,16 +26,18 @@ const PoDetailWrapper: React.FC<Props> & SubComponents = async ({ id }) => {
poWithStockInLine,
warehouse,
qc,
escalationCombo
escalationCombo,
printerCombo,
] = await Promise.all([
fetchPoWithStockInLines(id),
fetchWarehouseList(),
fetchQcItemCheck(),
fetchEscalationCombo(),
fetchPrinterCombo()
]);
// const poWithStockInLine = await fetchPoWithStockInLines(id)

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

PoDetailWrapper.Loading = PoDetailLoading;


+ 6
- 2
src/components/PoDetail/PoInputGrid.tsx Voir le fichier

@@ -62,6 +62,7 @@ import { useSession } from "next-auth/react";
// import { SessionWithTokens } from "src/config/authConfig";
import PoQcStockInModalVer2 from "./QcStockInModalVer2";
import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil";
import { PrinterCombo } from "@/app/api/settings/printer";
import { EscalationResult } from "@/app/api/escalation";
import { fetchEscalationLogsByStockInLines } from "@/app/api/escalation/actions";
import { SessionWithTokens } from "@/config/authConfig";
@@ -80,6 +81,7 @@ interface Props {
warehouse: WarehouseResult[];
fetchPoDetail: (poId: string) => void;
handleMailTemplateForStockInLine: (stockInLineId: number) => void;
printerCombo: PrinterCombo[];
}

export type StockInLineEntryError = {
@@ -119,7 +121,8 @@ function PoInputGrid({
stockInLine,
warehouse,
fetchPoDetail,
handleMailTemplateForStockInLine
handleMailTemplateForStockInLine,
printerCombo
}: Props) {
console.log(itemDetail);
const { t } = useTranslation("purchaseOrder");
@@ -301,7 +304,7 @@ const closeNewModal = useCallback(() => {
const handleNewQC = useCallback(
(id: GridRowId, params: any) => async() => {
// console.log(id)
// console.log(params)
console.log("params", params.row)
// setBtnIsLoading(true);
setRowModesModel((prev) => ({
...prev,
@@ -939,6 +942,7 @@ const closeNewModal = useCallback(() => {
onClose={closeNewModal}
itemDetail={modalInfo}
handleMailTemplateForStockInLine={handleMailTemplateForStockInLine}
printerCombo={printerCombo}
/>
</>
)


+ 2
- 2
src/components/PoDetail/PoQcStockInModal.tsx Voir le fichier

@@ -28,7 +28,7 @@ import { useSearchParams } from "next/navigation";
import { StockInLineRow } from "./PoInputGrid";
import EscalationForm from "./EscalationForm";
import StockInForm from "./StockInForm";
import PutawayForm from "./PutawayForm";
import PutAwayForm from "./PutAwayForm";
import {
INPUT_DATE_FORMAT,
stockInLineStatusMap,
@@ -437,7 +437,7 @@ const PoQcStockInModal: React.FC<Props> = ({
/>
)}
{itemDetail !== undefined && type === "putaway" && (
<PutawayForm
<PutAwayForm
itemDetail={itemDetail}
warehouse={warehouse!}
disabled={!renderSubmitButton}


+ 50
- 11
src/components/PoDetail/PutawayForm.tsx Voir le fichier

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

import { PurchaseQcResult, PutawayInput, PutawayLine } from "@/app/api/po/actions";
import { PurchaseQcResult, PutAwayInput, PutAwayLine } from "@/app/api/po/actions";
import {
Autocomplete,
Box,
@@ -50,7 +50,8 @@ import { QrCodeInfo } from "@/app/api/qrcode";
import { useQrCodeScannerContext } from "../QrCodeScannerProvider/QrCodeScannerProvider";
import dayjs from "dayjs";
import arraySupport from "dayjs/plugin/arraySupport";
import { dummyPutawayLine } from "./dummyQcTemplate";
import { dummyPutAwayLine } from "./dummyQcTemplate";
import { PrinterCombo } from "@/app/api/settings/printer";
dayjs.extend(arraySupport);

interface Props {
@@ -58,14 +59,15 @@ interface Props {
warehouse: WarehouseResult[];
disabled: boolean;
// qc: QcItemWithChecks[];
printerCombo: PrinterCombo[];
}
type EntryError =
| {
[field in keyof PutawayLine]?: string;
[field in keyof PutAwayLine]?: string;
}
| undefined;

type PutawayRow = TableRow<Partial<PutawayLine>, EntryError>;
type PutAwayRow = TableRow<Partial<PutAwayLine>, EntryError>;

const style = {
position: "absolute",
@@ -79,7 +81,7 @@ const style = {
width: "auto",
};

const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {
const PutAwayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled, printerCombo }) => {
const { t } = useTranslation("purchaseOrder");
const apiRef = useGridApiRef();
const {
@@ -93,7 +95,7 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {
resetField,
setError,
clearErrors,
} = useFormContext<PutawayInput>();
} = useFormContext<PutAwayInput>();
console.log(itemDetail);
// const [recordQty, setRecordQty] = useState(0);
const [warehouseId, setWarehouseId] = useState(itemDetail.defaultWarehouseId);
@@ -143,7 +145,7 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {
},
[],
);
console.log(watch("putawayLine"))
console.log(watch("putAwayLines"))
// const accQty = watch("acceptedQty");
// const validateForm = useCallback(() => {
// console.log(accQty);
@@ -274,6 +276,7 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {
field: "qty",
headerName: t("qty"),
flex: 1,
editable: true,
// renderCell(params) {
// return <>100</>
// },
@@ -282,6 +285,33 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {
field: "warehouse",
headerName: t("warehouse"),
flex: 1,
editable: true,
renderEditCell: (params) => {
const index = params.api.getRowIndexRelativeToVisibleRows(params.row.id)
// console.log(index)
// console.log(watch(`putAwayLines.${index}.warehouse`))
return <Autocomplete
fullWidth
disableClearable
options={options}
// defaultValue={options.find((o) => o.value === itemDetail.defaultWarehouseId)}
// value={options.find((o) => o.value === watch(`putAwayLines.${index}.warehouseId`))}
defaultValue={options.find((o) => o.value === watch(`putAwayLines.${index}.warehouseId`))}
onChange={(event, value) => {
params.api.setEditCellValue({ id: params.id, field: params.field, value: options.find((o) => o.value === value.value)?.label ?? ""})
params.api.setEditCellValue({ id: params.id, field: "warehouseId", value: value.value})
// setValue(`putAwayLines.${index}.warehouseId`, value.value)
// setValue(`putAwayLines.${index}.warehouse`, options.find((o) => o.value === value.value)?.label ?? "")
}}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
// label={t("Warehouse")}
/>
)}
/>
}
// renderCell(params) {
// return <>{filteredWarehouse[0].name}</>
// },
@@ -290,6 +320,7 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {
field: "printQty",
headerName: t("printQty"),
flex: 1,
editable: true,
// renderCell(params) {
// return <>100</>
// },
@@ -297,7 +328,7 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {
], [])

const validation = useCallback(
(newRow: GridRowModel<PutawayRow>): EntryError => {
(newRow: GridRowModel<PutAwayRow>): EntryError => {
const error: EntryError = {};
const { qty, warehouseId, printQty } = newRow;

@@ -306,6 +337,13 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {
[],
);

const addRowDefaultValue = useMemo(() => {
const defaultMaxQty = watch("acceptedQty") - watch("putAwayLines").reduce((acc, cur) => acc + cur.qty, 0)
const defaultWarehouseId = itemDetail.defaultWarehouseId ?? 1
const defaultWarehouse = options.find((o) => o.value === defaultWarehouseId)?.label
return {qty: defaultMaxQty, warehouseId: defaultWarehouseId, warehouse: defaultWarehouse, printQty: 1 } as Partial<PutAwayLine>
}, [])

return (
<Grid container justifyContent="flex-start" alignItems="flex-start">
<Grid item xs={12}>
@@ -493,14 +531,15 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {
style={{ display: "flex", justifyContent: "center" }}
>
{/* <QrCode content={qrContent} sx={{ width: 200, height: 200 }} /> */}
<InputDataGrid<PutawayInput, PutawayLine, EntryError>
<InputDataGrid<PutAwayInput, PutAwayLine, EntryError>
apiRef={apiRef}
checkboxSelection={false}
_formKey={"putawayLine"}
_formKey={"putAwayLines"}
columns={columns}
validateRow={validation}
needAdd={true}
showRemoveBtn={false}
addRowDefaultValue={addRowDefaultValue}
/>
</Grid>
</Grid>
@@ -525,4 +564,4 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {
</Grid>
);
};
export default PutawayForm;
export default PutAwayForm;

+ 82
- 18
src/components/PoDetail/QcStockInModalVer2.tsx Voir le fichier

@@ -1,14 +1,16 @@
"use client";
import { StockInLine } from "@/app/api/po";
import { ModalFormInput, PurchaseQcResult, StockInLineEntry, updateStockInLine, PurchaseQCInput } from "@/app/api/po/actions";
import { ModalFormInput, PurchaseQcResult, StockInLineEntry, updateStockInLine, PurchaseQCInput, printQrCodeForSil, PrintQrCodeForSilRequest } from "@/app/api/po/actions";
import { QcItemWithChecks, QcData } from "@/app/api/qc";
import {
Autocomplete,
Box,
Button,
Grid,
Modal,
ModalProps,
Stack,
TextField,
Typography,
} from "@mui/material";
import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from "react";
@@ -20,10 +22,16 @@ import StockInFormVer2 from "./StockInFormVer2";
import PutawayForm from "./PutawayForm";
import QcComponent from "./QcComponent";
import { dummyPutawayLine, dummyQCData } from "./dummyQcTemplate";
import QcFormVer2 from "./QcFormVer2";
import PutAwayForm from "./PutAwayForm";
import { dummyPutAwayLine, dummyQCData } from "./dummyQcTemplate";
import { useGridApiRef } from "@mui/x-data-grid";
import {submitDialogWithWarning} from "../Swal/CustomAlerts";
import { arrayToDateString, arrayToInputDateString, dayjsToInputDateString, INPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
import { INPUT_DATE_FORMAT, arrayToDateString, arrayToInputDateString, dayjsToInputDateString } from "@/app/utils/formatUtil";
import dayjs from "dayjs";
import { fetchPoQrcode } from "@/app/api/pdf/actions";
import { downloadFile } from "@/app/utils/commonUtil";
import { PrinterCombo } from "@/app/api/settings/printer";
import { watch } from "fs";
import { EscalationResult } from "@/app/api/escalation";
import { SessionWithTokens } from "@/config/authConfig";
@@ -60,6 +68,7 @@ interface CommonProps extends Omit<ModalProps, "children"> {
warehouse?: any[];
// type: "qc" | "stockIn" | "escalation" | "putaway" | "reject";
handleMailTemplateForStockInLine: (stockInLineId: number) => void;
printerCombo: PrinterCombo[];
onClose: () => void;
}
interface Props extends CommonProps {
@@ -78,19 +87,25 @@ const PoQcStockInModalVer2: React.FC<Props> = ({
qc,
warehouse,
handleMailTemplateForStockInLine,
printerCombo
}) => {
const {
t,
i18n: { language },
} = useTranslation("purchaseOrder");

// Select Printer
const [selectedPrinter, setSelectedPrinter] = useState(printerCombo[0])

const defaultNewValue = useMemo(() => {
return (
{
...itemDetail,
status: itemDetail.status ?? "pending",
dnDate: arrayToInputDateString(itemDetail.dnDate)?? dayjsToInputDateString(dayjs()),
putawayLine: dummyPutawayLine,
// putAwayLines: dummyPutAwayLine,
// putAwayLines: itemDetail.putAwayLines.map((line) => (return {...line, printQty: 1})) ?? [],
putAwayLines: itemDetail.putAwayLines?.map((line) => ({...line, printQty: 1})) ?? [],
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),
@@ -123,6 +138,17 @@ const [qcItems, setQcItems] = useState(dummyQCData)
} else return false;
};

useEffect(() => {
formProps.reset({
...itemDetail,
dnDate: dayjsToInputDateString(dayjs()),
// putAwayLines: dummyPutAwayLine,
putAwayLines: itemDetail.putAwayLines?.map((line) => ({...line, printQty: 1})) ?? [],
})
setOpenPutaway(isPutaway);
}, [open, itemDetail])
const [viewOnly, setViewOnly] = useState(false);
useEffect(() => {
if (itemDetail && itemDetail.status) {
@@ -248,7 +274,7 @@ const [qcItems, setQcItems] = useState(dummyQCData)

if (validationErrors.length > 0) {
console.error("QC Validation failed:", validationErrors);
alert(`未完成品檢: ${validationErrors}`);
alert(`未完成品檢: ${validationErrors}`);
return;
}

@@ -275,7 +301,6 @@ const [qcItems, setQcItems] = useState(dummyQCData)
// const qcData = data;

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

if (data.qcDecision == 3) { // Escalate
const escalationLog = {
type : "qc",
@@ -286,12 +311,10 @@ const [qcItems, setQcItems] = useState(dummyQCData)
}
console.log("ESCALATION RESULT", escalationLog);
await postStockInLine({...qcData, escalationLog});

} else {
await postStockInLine(qcData);
}

if (qcData.qcAccept) {
// submitDialogWithWarning(onOpenPutaway, t, {title:"Save success, confirm to proceed?",
// confirmButtonText: t("confirm putaway"), html: ""});
@@ -312,7 +335,6 @@ const [qcItems, setQcItems] = useState(dummyQCData)
console.log("Submitting", submitData);

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

@@ -357,13 +379,16 @@ const [qcItems, setQcItems] = useState(dummyQCData)
expiryDate : arrayToInputDateString(data.expiryDate),
receiptDate : arrayToInputDateString(data.receiptDate),

// for putaway data
inventoryLotLines: data.putAwayLines?.filter((line) => Boolean(line._isNew))

// 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);
console.log("result ", res);
// Close modal after successful putaway
closeHandler({}, "backdropClick");
@@ -371,11 +396,32 @@ const [qcItems, setQcItems] = useState(dummyQCData)
[closeHandler],
);
// Print handler
const onPrint = useCallback(() => {
console.log("Print putaway documents");
const [isPrinting, setIsPrinting] = useState(false)
const handlePrint = useCallback(async () => {
// console.log("Print putaway documents");
console.log("%c data", "background: white; color: red", formProps.watch("putAwayLines"));
// Handle print logic here
window.print();
}, []);
// window.print();
// const postData = { stockInLineIds: [itemDetail.id]};
// const response = await fetchPoQrcode(postData);
// if (response) {
// downloadFile(new Uint8Array(response.blobValue), response.filename)
// }
try {
setIsPrinting(() => true)
const data: PrintQrCodeForSilRequest = {
stockInLineId: itemDetail.id,
printerId: selectedPrinter.id,
printQty: formProps.watch("putAwayLines")?.reduce((acc, cur) => acc + cur.printQty, 0)
}
const response = await printQrCodeForSil(data);
if (response) {
console.log(response)
}
} finally {
setIsPrinting(() => false)
}
}, [itemDetail.id]);
const acceptQty = formProps.watch("acceptedQty")

@@ -395,7 +441,7 @@ const [qcItems, setQcItems] = useState(dummyQCData)
console.log(qcItems)
// checkQcIsPassed(qcItems)
}, [qcItems, checkQcIsPassed])
return (
<>
<FormProvider {...formProps}>
@@ -410,26 +456,44 @@ const [qcItems, setQcItems] = useState(dummyQCData)
marginRight: 3,
}}
>
{openPutaway ? (
{openPutaway ? (
<Box
component="form"
onSubmit={formProps.handleSubmit(onSubmitPutaway)}
>
<PutawayForm
<PutAwayForm
printerCombo={printerCombo}
itemDetail={itemDetail}
warehouse={warehouse!}
disabled={viewOnly}
/>
<Stack direction="row" justifyContent="flex-end" gap={1}>
<Autocomplete
disableClearable
options={printerCombo}
defaultValue={selectedPrinter}
onChange={(event, value) => {
setSelectedPrinter(value)
}}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label={t("Printer")}
sx={{ width: 300}}
/>
)}
/>
<Button
id="printButton"
type="button"
variant="contained"
color="primary"
sx={{ mt: 1 }}
onClick={onPrint}
onClick={handlePrint}
disabled={isPrinting}
>
{t("print")}
{isPrinting ? t("Printing") : t("print")}
</Button>
<Button
id="putawaySubmit"


+ 2
- 2
src/components/PoDetail/QrModal.tsx Voir le fichier

@@ -20,7 +20,7 @@ import {
StockInLineEntry,
updateStockInLine,
} from "@/app/api/po/actions";
import PutawayForm from "./PutawayForm";
import PutAwayForm from "./PutAwayForm";
import { StockInLine } from "@/app/api/po";
import { WarehouseResult } from "@/app/api/warehouse";
import { QrCodeInfo } from "@/app/api/qrcode";
@@ -204,7 +204,7 @@ const QrModal: React.FC<Props> = ({ open, onClose, warehouse }) => {
</Typography>
) : (
<>
<PutawayForm
<PutAwayForm
itemDetail={itemDetail}
warehouse={warehouse}
disabled={false}


+ 3
- 3
src/components/PoDetail/dummyQcTemplate.tsx Voir le fichier

@@ -1,4 +1,4 @@
import { PutawayLine } from "@/app/api/po/actions"
import { PutAwayLine } from "@/app/api/po/actions"
import { QcData } from "@/app/api/qc"

// export interface QcData {
@@ -73,12 +73,12 @@ export const dummyEscalationHistory: EscalationData[] = [
},
]

export const dummyPutawayLine: PutawayLine[] = [
export const dummyPutAwayLine: PutAwayLine[] = [
{
id: 1,
qty: 100,
warehouseId: 1,
warehouse: "W001 - 憶兆 3樓A倉",
printQty: 100
printQty: 1
}
]

+ 3
- 1
src/i18n/zh/purchaseOrder.json Voir le fichier

@@ -133,5 +133,7 @@
"bind": "綁定",
"Search": "搜尋",
"Found": "已找到",
"escalation processing": "處理上報記錄"
"escalation processing": "處理上報記錄",
"Printer": "列印機",
"Printing": "列印中"
}

Chargement…
Annuler
Enregistrer