@@ -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 ( | |||
<> | |||
@@ -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"], | |||
}, | |||
}, | |||
) | |||
}) |
@@ -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>) => { | |||
@@ -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"] }, | |||
}) | |||
}) |
@@ -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"] }, | |||
}); | |||
}); |
@@ -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" | |||
> | |||
新增 | |||
@@ -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} | |||
@@ -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, | |||
@@ -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 | |||
}, | |||
@@ -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> | |||
@@ -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; | |||
@@ -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} | |||
/> | |||
</> | |||
) | |||
@@ -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} | |||
@@ -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; |
@@ -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" | |||
@@ -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} | |||
@@ -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 | |||
} | |||
] |
@@ -133,5 +133,7 @@ | |||
"bind": "綁定", | |||
"Search": "搜尋", | |||
"Found": "已找到", | |||
"escalation processing": "處理上報記錄" | |||
"escalation processing": "處理上報記錄", | |||
"Printer": "列印機", | |||
"Printing": "列印中" | |||
} |