@@ -15,6 +15,8 @@ export interface EscalationResult { | |||||
poId?: number; | poId?: number; | ||||
reason?: string; | reason?: string; | ||||
handlerId?: number; | handlerId?: number; | ||||
status: string; | |||||
recordDate: string; | |||||
itemName?: string; | itemName?: string; | ||||
demandQty?: number; | demandQty?: number; | ||||
acceptedQty?: number; | acceptedQty?: number; | ||||
@@ -42,6 +42,7 @@ export interface PurchaseQcResult{ | |||||
qcPassed?: boolean; | qcPassed?: boolean; | ||||
failQty?: number; | failQty?: number; | ||||
remarks?: string; | remarks?: string; | ||||
escalationLogId?: number; | |||||
} | } | ||||
export interface StockInInput { | export interface StockInInput { | ||||
status: string; | status: string; | ||||
@@ -8,7 +8,7 @@ import { | |||||
} from "../../utils/fetchUtil"; | } from "../../utils/fetchUtil"; | ||||
import { BASE_API_URL } from "../../../config/api"; | import { BASE_API_URL } from "../../../config/api"; | ||||
import { revalidateTag } from "next/cache"; | import { revalidateTag } from "next/cache"; | ||||
import { UserDetail, UserResult } from "."; | |||||
import { EscalationCombo, UserDetail, UserResult } from "."; | |||||
import { cache } from "react"; | import { cache } from "react"; | ||||
export interface UserInputs { | export interface UserInputs { | ||||
@@ -102,3 +102,9 @@ export const adminChangePassword = async (data: any) => { | |||||
}, | }, | ||||
); | ); | ||||
}; | }; | ||||
export const fetchEscalationCombo = async () => { | |||||
return serverFetchJson<EscalationCombo>(`${BASE_API_URL}/user/escalation-combo`, { | |||||
next: { tags: ["escalationCombo"]} | |||||
}) | |||||
}; |
@@ -67,7 +67,7 @@ export const fetchPwRules = cache(async () => { | |||||
}); | }); | ||||
export const fetchEscalationCombo = cache(async () => { | export const fetchEscalationCombo = cache(async () => { | ||||
return serverFetchJson<EscalationCombo>(`${BASE_API_URL}/user/escalation-combo`, { | |||||
return serverFetchJson<EscalationCombo[]>(`${BASE_API_URL}/user/escalation-combo`, { | |||||
next: { tags: ["escalationCombo"]} | next: { tags: ["escalationCombo"]} | ||||
}) | }) | ||||
}) | }) |
@@ -36,7 +36,7 @@ export const INPUT_TIME_FORMAT = "HH:mm:ss"; | |||||
export const OUTPUT_TIME_FORMAT = "HH:mm:ss"; | export const OUTPUT_TIME_FORMAT = "HH:mm:ss"; | ||||
export const arrayToDayjs = (arr: ConfigType | (number | undefined)[]) => { | |||||
export const arrayToDayjs = (arr: ConfigType | (number | undefined)[], showTime: boolean = false) => { | |||||
const isValidNumber = ( | const isValidNumber = ( | ||||
item: ListIterateeCustom<number | undefined, boolean>, | item: ListIterateeCustom<number | undefined, boolean>, | ||||
): boolean => typeof item === "number" && !isNaN(item) && isFinite(item); | ): boolean => typeof item === "number" && !isNaN(item) && isFinite(item); | ||||
@@ -47,6 +47,10 @@ export const arrayToDayjs = (arr: ConfigType | (number | undefined)[]) => { | |||||
// [year, month, day] | // [year, month, day] | ||||
// tempArr = take(arr, 3); | // tempArr = take(arr, 3); | ||||
tempArr = `${arr[0]?.toString().padStart(4, "0")}-${arr[1]?.toString().padStart(2, "0")}-${arr[2]?.toString().padStart(2, "0")}`; | tempArr = `${arr[0]?.toString().padStart(4, "0")}-${arr[1]?.toString().padStart(2, "0")}-${arr[2]?.toString().padStart(2, "0")}`; | ||||
if (showTime) { | |||||
// [year, month, day, hour, minute, second] | |||||
tempArr += ` ${arr[3]?.toString().padStart(2, "0")}-${arr[4]?.toString().padStart(2, "0")}-${arr[5]?.toString().padStart(2, "0")}`; | |||||
} | |||||
} | } | ||||
return dayjs(tempArr as ConfigType); | return dayjs(tempArr as ConfigType); | ||||
@@ -56,6 +60,10 @@ export const arrayToDateString = (arr: ConfigType | (number | undefined)[]) => { | |||||
return arrayToDayjs(arr).format(OUTPUT_DATE_FORMAT); | return arrayToDayjs(arr).format(OUTPUT_DATE_FORMAT); | ||||
}; | }; | ||||
export const arrayToDateTimeString = (arr: ConfigType | (number | undefined)[]) => { | |||||
return arrayToDayjs(arr, true).format(`${OUTPUT_DATE_FORMAT} ${OUTPUT_TIME_FORMAT}`); | |||||
}; | |||||
export const arrayToInputDateString = (arr: ConfigType | (number | undefined)[]) => { | export const arrayToInputDateString = (arr: ConfigType | (number | undefined)[]) => { | ||||
return arrayToDayjs(arr).format(INPUT_DATE_FORMAT); | return arrayToDayjs(arr).format(INPUT_DATE_FORMAT); | ||||
}; | }; | ||||
@@ -33,7 +33,7 @@ const DashboardPage: React.FC<Props> = ({ | |||||
<ThemeProvider theme={theme}> | <ThemeProvider theme={theme}> | ||||
<Grid container spacing={2}> | <Grid container spacing={2}> | ||||
<Grid item xs={12}> | <Grid item xs={12}> | ||||
<CollapsibleCard title={t("Escalation List")}> | |||||
<CollapsibleCard title={t("My Escalation List")}> | |||||
<CardContent> | <CardContent> | ||||
<EscalationLogTable items={escalationLogs || []}/> | <EscalationLogTable items={escalationLogs || []}/> | ||||
</CardContent> | </CardContent> | ||||
@@ -8,7 +8,7 @@ import { useTranslation } from "react-i18next"; | |||||
import { EscalationResult } from "@/app/api/escalation"; | import { EscalationResult } from "@/app/api/escalation"; | ||||
import { Column } from "@/components/SearchResults"; | import { Column } from "@/components/SearchResults"; | ||||
import SearchResults from "@/components/SearchResults/SearchResults"; | import SearchResults from "@/components/SearchResults/SearchResults"; | ||||
import { arrayToDateString } from "@/app/utils/formatUtil"; | |||||
import { arrayToDateString, arrayToDateTimeString } from "@/app/utils/formatUtil"; | |||||
export type IQCItems = { | export type IQCItems = { | ||||
id: number; | id: number; | ||||
@@ -22,11 +22,12 @@ export type IQCItems = { | |||||
}; | }; | ||||
type Props = { | type Props = { | ||||
type?: "dashboard" | "qc"; | |||||
items: EscalationResult[]; | items: EscalationResult[]; | ||||
}; | }; | ||||
const EscalationLogTable: React.FC<Props> = ({ | const EscalationLogTable: React.FC<Props> = ({ | ||||
items | |||||
type = "dashboard", items | |||||
}) => { | }) => { | ||||
const { t } = useTranslation("dashboard"); | const { t } = useTranslation("dashboard"); | ||||
const CARD_HEADER = t("stock in escalation list") | const CARD_HEADER = t("stock in escalation list") | ||||
@@ -58,19 +59,66 @@ const EscalationLogTable: React.FC<Props> = ({ | |||||
// [navigateTo] | // [navigateTo] | ||||
// ); | // ); | ||||
const columns = useMemo<Column<EscalationResult>[]>( | |||||
const base_col = useMemo<Column<EscalationResult>[]>( | |||||
() => [ | () => [ | ||||
{ | { | ||||
name: "handler", | |||||
label: t("Responsible for handling colleagues"), | |||||
sx: { width: "20%", minWidth: 200, maxWidth: 500 }, | |||||
name: "qcFailCount", | |||||
label: t("QC Fail Count"), | |||||
align: "right", | |||||
headerAlign: "right", | |||||
sx: { width: "10%", minWidth: 120 }, | |||||
renderCell: (params) => { | |||||
return `${params.qcFailCount} / ${params.qcTotalCount}` | |||||
} | |||||
}, | |||||
{ | |||||
name: "reason", | |||||
label: t("Reason"), | |||||
sx: { width: "30%", minWidth: 150 }, | |||||
}, | |||||
{ | |||||
name: "status", | |||||
label: t("escalationStatus"), | |||||
sx: { width: "10%", minWidth: 150 }, | |||||
renderCell: (params) => { | |||||
return t(params.status); | |||||
// return t(`${params.status}`); | |||||
} | |||||
}, | |||||
], []) | |||||
const columns_dashboard = useMemo<Column<EscalationResult>[]>( | |||||
() => [ | |||||
{ | |||||
name: "poCode", | |||||
label: t("Po Code"), | |||||
align: "left", | |||||
headerAlign: "left", | |||||
sx: { width: "15%", minWidth: 100 }, | |||||
}, | |||||
{ | |||||
name: "recordDate", | |||||
label: t("escalated date"), | |||||
align: "right", | |||||
headerAlign: "right", | |||||
sx: { width: "10%", minWidth: 100 }, | |||||
renderCell: (params) => { | |||||
return arrayToDateString(params.recordDate); | |||||
} | |||||
}, | |||||
{ | |||||
name: "itemName", | |||||
label: t("Item Name"), | |||||
align: "left", | |||||
headerAlign: "left", | |||||
sx: { width: "15%", minWidth: 100 }, | |||||
}, | }, | ||||
{ | { | ||||
name: "acceptedQty", | name: "acceptedQty", | ||||
label: t("Received Qty"), | label: t("Received Qty"), | ||||
align: "right", | align: "right", | ||||
headerAlign: "right", | headerAlign: "right", | ||||
sx: { width: "10%", minWidth: 100 }, | |||||
sx: { width: "5%", minWidth: 100 }, | |||||
}, | }, | ||||
{ | { | ||||
name: "purchaseUomDesc", | name: "purchaseUomDesc", | ||||
@@ -85,37 +133,55 @@ const EscalationLogTable: React.FC<Props> = ({ | |||||
return params.dnDate ? arrayToDateString(params.dnDate) : "N/A" | return params.dnDate ? arrayToDateString(params.dnDate) : "N/A" | ||||
} | } | ||||
}, | }, | ||||
...base_col | |||||
], []) | |||||
const columns_qc = useMemo<Column<EscalationResult>[]>( | |||||
() => [ | |||||
{ | { | ||||
name: "qcFailCount", | |||||
label: t("QC Fail Count"), | |||||
name: "handler", | |||||
label: t("Responsible for handling colleagues"), | |||||
sx: { width: "20%", minWidth: 200, maxWidth: 500 }, | |||||
renderCell: (params) => { | |||||
const department = params.handlerDepartment; | |||||
const name = params.handlerName; | |||||
const title = params.handlerTitle; | |||||
return ( | |||||
(department ? `${department} - ` : "") + name + (title ? ` (${title})` : "") | |||||
); | |||||
} | |||||
}, | |||||
{ | |||||
name: "recordDate", | |||||
label: t("escalated date"), | |||||
align: "right", | align: "right", | ||||
headerAlign: "right", | headerAlign: "right", | ||||
sx: { width: "15%", minWidth: 120 }, | |||||
sx: { width: "10%", minWidth: 100 }, | |||||
renderCell: (params) => { | renderCell: (params) => { | ||||
return `${params.qcFailCount} / ${params.qcTotalCount}` | |||||
return arrayToDateString(params.recordDate); | |||||
} | } | ||||
}, | }, | ||||
// { | |||||
// name: "qcTotalCount", | |||||
// label: t("QC Completed Count"), | |||||
// align: "right", | |||||
// headerAlign: "right" | |||||
// flex: 1, | |||||
// }, | |||||
// { | |||||
// name: "qcFailCount", | |||||
// label: t("QC Fail Count"), | |||||
// align: "right", | |||||
// headerAlign: "right" | |||||
// flex: 1, | |||||
// }, | |||||
{ | { | ||||
name: "reason", | |||||
label: t("Reason"), | |||||
sx: { width: "30%", minWidth: 150 }, | |||||
name: "acceptedQty", | |||||
label: t("Received Qty"), | |||||
align: "right", | |||||
headerAlign: "right", | |||||
sx: { width: "5%", minWidth: 100 }, | |||||
}, | |||||
{ | |||||
name: "purchaseUomDesc", | |||||
label: t("Purchase UoM"), | |||||
sx: { width: "15%", minWidth: 120 }, | |||||
}, | }, | ||||
...base_col | |||||
], []) | ], []) | ||||
const getColumnByType = (type : Props['type']) => { | |||||
switch (type) { | |||||
case "qc": return columns_qc; | |||||
default: return columns_dashboard; | |||||
} | |||||
} | |||||
{/* return ( | {/* return ( | ||||
<TableContainer component={Paper}> | <TableContainer component={Paper}> | ||||
<Table aria-label="Two column navigable table" size="small"> | <Table aria-label="Two column navigable table" size="small"> | ||||
@@ -158,7 +224,7 @@ const EscalationLogTable: React.FC<Props> = ({ | |||||
<SearchResults | <SearchResults | ||||
onRowClick={onRowClick} | onRowClick={onRowClick} | ||||
items={items} | items={items} | ||||
columns={columns} | |||||
columns={getColumnByType(type)} | |||||
isAutoPaging={false} | isAutoPaging={false} | ||||
/> | /> | ||||
) | ) | ||||
@@ -1,4 +1,4 @@ | |||||
import React, { useState, ChangeEvent, FormEvent, Dispatch } from 'react'; | |||||
import React, { useState, ChangeEvent, FormEvent, Dispatch, useEffect } from 'react'; | |||||
import { | import { | ||||
Box, | Box, | ||||
Button, | Button, | ||||
@@ -21,8 +21,10 @@ import { SelectChangeEvent } from '@mui/material/Select'; | |||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; | import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; | ||||
import ExpandLessIcon from '@mui/icons-material/ExpandLess'; | import ExpandLessIcon from '@mui/icons-material/ExpandLess'; | ||||
import { useTranslation } from 'react-i18next'; | import { useTranslation } from 'react-i18next'; | ||||
import { useFormContext } from 'react-hook-form'; | |||||
import { Controller, useFormContext } from 'react-hook-form'; | |||||
import { EscalationInput, ModalFormInput } from '@/app/api/po/actions'; | import { EscalationInput, ModalFormInput } from '@/app/api/po/actions'; | ||||
import { EscalationCombo } from "@/app/api/user"; | |||||
import { FireExtinguisher } from '@mui/icons-material'; | |||||
interface NameOption { | interface NameOption { | ||||
value: string; | value: string; | ||||
@@ -39,15 +41,17 @@ interface Props { | |||||
forSupervisor: boolean | forSupervisor: boolean | ||||
isCollapsed: boolean | isCollapsed: boolean | ||||
setIsCollapsed: Dispatch<React.SetStateAction<boolean>> | setIsCollapsed: Dispatch<React.SetStateAction<boolean>> | ||||
escalationCombo: EscalationCombo[] | |||||
} | } | ||||
const EscalationComponent: React.FC<Props> = ({ | |||||
const EscalationComponent: React.FC<Props> = ({ | |||||
forSupervisor, | forSupervisor, | ||||
isCollapsed, | isCollapsed, | ||||
setIsCollapsed | |||||
}) => { | |||||
setIsCollapsed, | |||||
escalationCombo | |||||
}) => { | |||||
const { t } = useTranslation("purchaseOrder"); | const { t } = useTranslation("purchaseOrder"); | ||||
const [formData, setFormData] = useState<FormData>({ | const [formData, setFormData] = useState<FormData>({ | ||||
name: '', | name: '', | ||||
quantity: '', | quantity: '', | ||||
@@ -74,7 +78,7 @@ const EscalationComponent: React.FC<Props> = ({ | |||||
resetField, | resetField, | ||||
setError, | setError, | ||||
clearErrors, | clearErrors, | ||||
} = useFormContext<ModalFormInput>(); | |||||
} = useFormContext<ModalFormInput>(); | |||||
const handleInputChange = ( | const handleInputChange = ( | ||||
event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> | SelectChangeEvent<string> | event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> | SelectChangeEvent<string> | ||||
@@ -86,7 +90,8 @@ const EscalationComponent: React.FC<Props> = ({ | |||||
})); | })); | ||||
}; | }; | ||||
const handleSubmit = (e: FormEvent<HTMLFormElement>): void => {console.log("called this?"); | |||||
const handleSubmit = (e: FormEvent<HTMLFormElement>): void => { | |||||
console.log("Handled Submit?"); | |||||
e.preventDefault(); | e.preventDefault(); | ||||
console.log('表單已提交:', formData); | console.log('表單已提交:', formData); | ||||
// 處理表單提交 | // 處理表單提交 | ||||
@@ -99,9 +104,9 @@ const EscalationComponent: React.FC<Props> = ({ | |||||
return ( | return ( | ||||
// <Paper elevation={3} sx={{ maxWidth: 400, mx: 'auto', p: 3 }}> | // <Paper elevation={3} sx={{ maxWidth: 400, mx: 'auto', p: 3 }}> | ||||
<> | <> | ||||
<Paper sx={{padding: 2}}> | |||||
{/* <Paper elevation={3} sx={{ mx: 'auto', p: 3 }}> */} | |||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}> | |||||
<Paper sx={{ padding: 2 }}> | |||||
{/* <Paper elevation={3} sx={{ mx: 'auto', p: 3 }}> */} | |||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}> | |||||
<Box sx={{ display: 'flex', alignItems: 'center' }}> | <Box sx={{ display: 'flex', alignItems: 'center' }}> | ||||
<Typography variant="body1">上報結果</Typography> | <Typography variant="body1">上報結果</Typography> | ||||
{/* {isCollapsed ? ( | {/* {isCollapsed ? ( | ||||
@@ -110,7 +115,7 @@ const EscalationComponent: React.FC<Props> = ({ | |||||
<ExpandMoreIcon sx={{ ml: 1 }} /> | <ExpandMoreIcon sx={{ ml: 1 }} /> | ||||
)} */} | )} */} | ||||
</Box> | </Box> | ||||
{/* <FormControlLabel | |||||
{/* <FormControlLabel | |||||
control={ | control={ | ||||
<Checkbox | <Checkbox | ||||
checked={isCollapsed} | checked={isCollapsed} | ||||
@@ -129,11 +134,34 @@ const EscalationComponent: React.FC<Props> = ({ | |||||
</Box> | </Box> | ||||
} | } | ||||
/> */} | /> */} | ||||
</Box> | |||||
<Collapse in={isCollapsed}> | |||||
<Box component="form" onSubmit={handleSubmit} sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}> | |||||
<FormControl fullWidth> | |||||
<select | |||||
</Box> | |||||
<Collapse in={isCollapsed}> | |||||
<Box component="form" onSubmit={handleSubmit} sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}> | |||||
<FormControl fullWidth> | |||||
<Controller | |||||
control={control} | |||||
name="escalationLog.handlerId" | |||||
render={({field, fieldState, formState}) => { | |||||
return (<Autocomplete | |||||
noOptionsText={t("No Option")} | |||||
disableClearable | |||||
fullWidth | |||||
// value={} | |||||
// {...register("escalationLog.handlerId", { | |||||
// required: "handler required!", | |||||
// })} | |||||
// onChange={() => handleInputChange} | |||||
onChange={(event, value) => { | |||||
field.onChange(value.id); | |||||
}} | |||||
// getOptionLabel={(option) => option.label} | |||||
options={escalationCombo} | |||||
renderInput={(params) => <TextField {...params} error={false} label={"負責處理同事"}/>} | |||||
/>) | |||||
}} | |||||
/> | |||||
{/* <select | |||||
id="handlerId" | id="handlerId" | ||||
// name="name" | // name="name" | ||||
// value={formData.name} | // value={formData.name} | ||||
@@ -148,33 +176,33 @@ const EscalationComponent: React.FC<Props> = ({ | |||||
{option.label} | {option.label} | ||||
</option> | </option> | ||||
))} | ))} | ||||
</select> | |||||
</FormControl> | |||||
{forSupervisor ? ( | |||||
<FormControl> | |||||
<RadioGroup | |||||
row | |||||
aria-labelledby="demo-radio-buttons-group-label" | |||||
defaultValue="pass" | |||||
name="radio-buttons-group" | |||||
> | |||||
<FormControlLabel value="pass" control={<Radio />} label="合格" /> | |||||
<FormControlLabel value="fail" control={<Radio />} label="不合格" /> | |||||
</RadioGroup> | |||||
</select> */} | |||||
</FormControl> | </FormControl> | ||||
): undefined} | |||||
{forSupervisor && (<TextField | |||||
fullWidth | |||||
id="decision" | |||||
name="decision" | |||||
label="上報結果" | |||||
type="radio" | |||||
// value={formData.decision} | |||||
onChange={handleInputChange} | |||||
InputProps={{ inputProps: { min: 1 } }} | |||||
placeholder="請決定上報結果" | |||||
/>)} | |||||
{/* <TextField | |||||
{forSupervisor ? ( | |||||
<FormControl> | |||||
<RadioGroup | |||||
row | |||||
aria-labelledby="demo-radio-buttons-group-label" | |||||
defaultValue="pass" | |||||
name="radio-buttons-group" | |||||
> | |||||
<FormControlLabel value="pass" control={<Radio />} label="合格" /> | |||||
<FormControlLabel value="fail" control={<Radio />} label="不合格" /> | |||||
</RadioGroup> | |||||
</FormControl> | |||||
) : undefined} | |||||
{forSupervisor && (<TextField | |||||
fullWidth | |||||
id="decision" | |||||
name="decision" | |||||
label="上報結果" | |||||
type="radio" | |||||
// value={formData.decision} | |||||
onChange={handleInputChange} | |||||
InputProps={{ inputProps: { min: 1 } }} | |||||
placeholder="請決定上報結果" | |||||
/>)} | |||||
{/* <TextField | |||||
fullWidth | fullWidth | ||||
id="quantity" | id="quantity" | ||||
name="quantity" | name="quantity" | ||||
@@ -186,22 +214,22 @@ const EscalationComponent: React.FC<Props> = ({ | |||||
placeholder="請輸入數量" | placeholder="請輸入數量" | ||||
/> */} | /> */} | ||||
<TextField | |||||
fullWidth | |||||
id="reason" | |||||
// name="reason" | |||||
{...register("escalationLog.reason", { | |||||
required: "reason required!", | |||||
})} | |||||
label="上報原因" | |||||
multiline | |||||
rows={4} | |||||
// value={formData.reason} | |||||
onChange={handleInputChange} | |||||
placeholder="請輸入上報原因" | |||||
/> | |||||
<TextField | |||||
fullWidth | |||||
id="reason" | |||||
// name="reason" | |||||
{...register("escalationLog.reason", { | |||||
required: "reason required!", | |||||
})} | |||||
label="上報原因" | |||||
multiline | |||||
rows={4} | |||||
// value={formData.reason} | |||||
onChange={handleInputChange} | |||||
placeholder="請輸入上報原因" | |||||
/> | |||||
{/* <Stack direction="row" justifyContent="flex-end" gap={1}> | |||||
{/* <Stack direction="row" justifyContent="flex-end" gap={1}> | |||||
<Button | <Button | ||||
type="submit" | type="submit" | ||||
variant="contained" | variant="contained" | ||||
@@ -210,9 +238,9 @@ const EscalationComponent: React.FC<Props> = ({ | |||||
{t("update qc info")} | {t("update qc info")} | ||||
</Button> | </Button> | ||||
</Stack> */} | </Stack> */} | ||||
</Box> | |||||
</Collapse> | |||||
</Paper> | |||||
</Box> | |||||
</Collapse> | |||||
</Paper> | |||||
</> | </> | ||||
); | ); | ||||
} | } | ||||
@@ -80,6 +80,7 @@ import { debounce } from "lodash"; | |||||
import LoadingComponent from "../General/LoadingComponent"; | import LoadingComponent from "../General/LoadingComponent"; | ||||
import { getMailTemplateForStockInLine } from "@/app/api/mailTemplate/actions"; | import { getMailTemplateForStockInLine } from "@/app/api/mailTemplate/actions"; | ||||
import { PrinterCombo } from "@/app/api/settings/printer"; | import { PrinterCombo } from "@/app/api/settings/printer"; | ||||
import { EscalationCombo } from "@/app/api/user"; | |||||
//import { useRouter } from "next/navigation"; | //import { useRouter } from "next/navigation"; | ||||
@@ -88,6 +89,7 @@ type Props = { | |||||
qc: QcItemWithChecks[]; | qc: QcItemWithChecks[]; | ||||
warehouse: WarehouseResult[]; | warehouse: WarehouseResult[]; | ||||
printerCombo: PrinterCombo[]; | printerCombo: PrinterCombo[]; | ||||
escalationCombo: EscalationCombo[]; | |||||
}; | }; | ||||
type EntryError = | type EntryError = | ||||
@@ -190,7 +192,7 @@ interface PolInputResult { | |||||
dnQty: number, | dnQty: number, | ||||
} | } | ||||
const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo }) => { | |||||
const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo, escalationCombo }) => { | |||||
const cameras = useContext(CameraContext); | const cameras = useContext(CameraContext); | ||||
// console.log(cameras); | // console.log(cameras); | ||||
const { t } = useTranslation("purchaseOrder"); | const { t } = useTranslation("purchaseOrder"); | ||||
@@ -217,6 +219,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo }) => { | |||||
useEffect(() => { | useEffect(() => { | ||||
if (defaultPolId) { | if (defaultPolId) { | ||||
setSelectedRow(rows.find((r) => r.id.toString() === defaultPolId) ?? null) | setSelectedRow(rows.find((r) => r.id.toString() === defaultPolId) ?? null) | ||||
console.log("%c StockIn:", "color:green", selectedRow); | |||||
} | } | ||||
}, []) | }, []) | ||||
@@ -263,6 +266,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo }) => { | |||||
const result = await fetchPoInClient(parseInt(poId)); | const result = await fetchPoInClient(parseInt(poId)); | ||||
console.log(result) | console.log(result) | ||||
if (result) { | if (result) { | ||||
console.log("%c Fetched PO:", "color:orange", result); | |||||
setPurchaseOrder(result); | setPurchaseOrder(result); | ||||
setRows(result.pol || []); | setRows(result.pol || []); | ||||
if (result.pol && result.pol.length > 0) { | if (result.pol && result.pol.length > 0) { | ||||
@@ -506,7 +510,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo }) => { | |||||
<TableCell align="left">{row.itemNo}</TableCell> | <TableCell align="left">{row.itemNo}</TableCell> | ||||
<TableCell align="left">{row.itemName}</TableCell> | <TableCell align="left">{row.itemName}</TableCell> | ||||
<TableCell align="right">{integerFormatter.format(row.qty)}</TableCell> | <TableCell align="right">{integerFormatter.format(row.qty)}</TableCell> | ||||
<TableCell align="right">{integerFormatter.format(processedQty)}</TableCell> | |||||
<TableCell align="right">{integerFormatter.format(row.processed)}</TableCell> | |||||
<TableCell align="left">{row.uom?.code}</TableCell> | <TableCell align="left">{row.uom?.code}</TableCell> | ||||
{/* <TableCell align="right">{decimalFormatter.format(row.stockUom.stockQty)}</TableCell> */} | {/* <TableCell align="right">{decimalFormatter.format(row.stockUom.stockQty)}</TableCell> */} | ||||
<TableCell align="right">{decimalFormatter.format(row.stockInLine.filter((sil) => sil.purchaseOrderLineId === row.id).reduce((acc, cur) => acc + (cur.acceptedQty ?? 0),0) * purchaseToStockRatio)}</TableCell> | <TableCell align="right">{decimalFormatter.format(row.stockInLine.filter((sil) => sil.purchaseOrderLineId === row.id).reduce((acc, cur) => acc + (cur.acceptedQty ?? 0),0) * purchaseToStockRatio)}</TableCell> | ||||
@@ -814,7 +818,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo }) => { | |||||
<TableCell align="right">{t("qty")}</TableCell> | <TableCell align="right">{t("qty")}</TableCell> | ||||
<TableCell align="right">{t("processed")}</TableCell> | <TableCell align="right">{t("processed")}</TableCell> | ||||
<TableCell align="left">{t("uom")}</TableCell> | <TableCell align="left">{t("uom")}</TableCell> | ||||
<TableCell align="right">{t("Stock In Qty")}</TableCell> | |||||
<TableCell align="right">{t("receivedTotal")}</TableCell> | |||||
<TableCell align="left">{t("Stock UoM")}</TableCell> | <TableCell align="left">{t("Stock UoM")}</TableCell> | ||||
{/* <TableCell align="right">{t("total weight")}</TableCell> */} | {/* <TableCell align="right">{t("total weight")}</TableCell> */} | ||||
{/* <TableCell align="right">{`${t("price")} (HKD)`}</TableCell> */} | {/* <TableCell align="right">{`${t("price")} (HKD)`}</TableCell> */} | ||||
@@ -861,6 +865,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo }) => { | |||||
fetchPoDetail={fetchPoDetail} | fetchPoDetail={fetchPoDetail} | ||||
handleMailTemplateForStockInLine={handleMailTemplateForStockInLine} | handleMailTemplateForStockInLine={handleMailTemplateForStockInLine} | ||||
printerCombo={printerCombo} | printerCombo={printerCombo} | ||||
escalationCombo={escalationCombo} | |||||
/> | /> | ||||
</Box> | </Box> | ||||
</TableCell> | </TableCell> | ||||
@@ -878,7 +883,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo }) => { | |||||
/> */} | /> */} | ||||
</Grid> | </Grid> | ||||
</Stack> | </Stack> | ||||
{itemInfo !== undefined && ( | |||||
{/* {itemInfo !== undefined && ( | |||||
<> | <> | ||||
<PoQcStockInModal | <PoQcStockInModal | ||||
type={"putaway"} | type={"putaway"} | ||||
@@ -889,7 +894,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse, printerCombo }) => { | |||||
itemDetail={itemInfo} | itemDetail={itemInfo} | ||||
/> | /> | ||||
</> | </> | ||||
)} | |||||
)} */} | |||||
</> | </> | ||||
); | ); | ||||
}; | }; |
@@ -26,18 +26,18 @@ const PoDetailWrapper: React.FC<Props> & SubComponents = async ({ id }) => { | |||||
poWithStockInLine, | poWithStockInLine, | ||||
warehouse, | warehouse, | ||||
qc, | qc, | ||||
escalationCombo, | |||||
printerCombo, | printerCombo, | ||||
escalationCombo, | |||||
] = await Promise.all([ | ] = await Promise.all([ | ||||
fetchPoWithStockInLines(id), | fetchPoWithStockInLines(id), | ||||
fetchWarehouseList(), | fetchWarehouseList(), | ||||
fetchQcItemCheck(), | fetchQcItemCheck(), | ||||
fetchEscalationCombo(), | |||||
fetchPrinterCombo() | |||||
fetchPrinterCombo(), | |||||
fetchEscalationCombo() | |||||
]); | ]); | ||||
// const poWithStockInLine = await fetchPoWithStockInLines(id) | // const poWithStockInLine = await fetchPoWithStockInLines(id) | ||||
return <PoDetail po={poWithStockInLine} qc={qc} warehouse={warehouse} printerCombo={printerCombo} />; | |||||
console.log("%c pol:", "color:green", poWithStockInLine); | |||||
return <PoDetail po={poWithStockInLine} qc={qc} warehouse={warehouse} printerCombo={printerCombo} escalationCombo={escalationCombo}/>; | |||||
}; | }; | ||||
PoDetailWrapper.Loading = PoDetailLoading; | PoDetailWrapper.Loading = PoDetailLoading; | ||||
@@ -66,6 +66,7 @@ import { PrinterCombo } from "@/app/api/settings/printer"; | |||||
import { EscalationResult } from "@/app/api/escalation"; | import { EscalationResult } from "@/app/api/escalation"; | ||||
import { fetchEscalationLogsByStockInLines } from "@/app/api/escalation/actions"; | import { fetchEscalationLogsByStockInLines } from "@/app/api/escalation/actions"; | ||||
import { SessionWithTokens } from "@/config/authConfig"; | import { SessionWithTokens } from "@/config/authConfig"; | ||||
import { EscalationCombo } from "@/app/api/user"; | |||||
interface ResultWithId { | interface ResultWithId { | ||||
id: number; | id: number; | ||||
@@ -82,6 +83,7 @@ interface Props { | |||||
fetchPoDetail: (poId: string) => void; | fetchPoDetail: (poId: string) => void; | ||||
handleMailTemplateForStockInLine: (stockInLineId: number) => void; | handleMailTemplateForStockInLine: (stockInLineId: number) => void; | ||||
printerCombo: PrinterCombo[]; | printerCombo: PrinterCombo[]; | ||||
escalationCombo: EscalationCombo[]; | |||||
} | } | ||||
export type StockInLineEntryError = { | export type StockInLineEntryError = { | ||||
@@ -122,7 +124,8 @@ function PoInputGrid({ | |||||
warehouse, | warehouse, | ||||
fetchPoDetail, | fetchPoDetail, | ||||
handleMailTemplateForStockInLine, | handleMailTemplateForStockInLine, | ||||
printerCombo | |||||
printerCombo, | |||||
escalationCombo | |||||
}: Props) { | }: Props) { | ||||
const { t } = useTranslation("purchaseOrder"); | const { t } = useTranslation("purchaseOrder"); | ||||
@@ -263,8 +266,8 @@ function PoInputGrid({ | |||||
})); | })); | ||||
const qcResult = await fetchQcDefaultValue(id); | const qcResult = await fetchQcDefaultValue(id); | ||||
const escResult = await fetchEscalationLogsByStockInLines([Number(id)]); | const escResult = await fetchEscalationLogsByStockInLines([Number(id)]); | ||||
console.log(params.row); | |||||
console.log(qcResult); | |||||
// console.log(params.row); | |||||
console.log("Fetched QC Result:", qcResult); | |||||
setModalInfo({ | setModalInfo({ | ||||
...params.row, | ...params.row, | ||||
@@ -580,8 +583,11 @@ const closeNewModal = useCallback(() => { | |||||
const handlerId = params.row.handlerId | const handlerId = params.row.handlerId | ||||
const status = params.row.status | const status = params.row.status | ||||
return (<span style={{ | return (<span style={{ | ||||
color: (status == "escalated")? "red":"inherit"}}> | |||||
{t(`${params.row.status}`)} | |||||
color: | |||||
(status == "escalated")? "red": | |||||
(status == "rejected" || status == "partially_completed") ? "orange" : "inherit"}} | |||||
> | |||||
{t(`${params.row.status}`)} | |||||
</span>); | </span>); | ||||
}, | }, | ||||
}, | }, | ||||
@@ -622,6 +628,7 @@ const closeNewModal = useCallback(() => { | |||||
variant="contained" | variant="contained" | ||||
color="primary" | color="primary" | ||||
sx={{ width: '150px' }} | sx={{ width: '150px' }} | ||||
disabled={params.row.status != "rejected" && params.row.status != "partially_completed"} | |||||
onClick={() => handleMailTemplateForStockInLine(params.row.id as number)} | onClick={() => handleMailTemplateForStockInLine(params.row.id as number)} | ||||
> | > | ||||
{t("email supplier")} | {t("email supplier")} | ||||
@@ -944,6 +951,7 @@ const closeNewModal = useCallback(() => { | |||||
itemDetail={modalInfo} | itemDetail={modalInfo} | ||||
handleMailTemplateForStockInLine={handleMailTemplateForStockInLine} | handleMailTemplateForStockInLine={handleMailTemplateForStockInLine} | ||||
printerCombo={printerCombo} | printerCombo={printerCombo} | ||||
escalationCombo={escalationCombo} | |||||
/> | /> | ||||
</> | </> | ||||
) | ) | ||||
@@ -6,6 +6,7 @@ import { | |||||
Card, | Card, | ||||
CardContent, | CardContent, | ||||
Checkbox, | Checkbox, | ||||
Collapse, | |||||
FormControl, | FormControl, | ||||
FormControlLabel, | FormControlLabel, | ||||
Grid, | Grid, | ||||
@@ -54,11 +55,13 @@ import { escape } from "lodash"; | |||||
import { PanoramaSharp } from "@mui/icons-material"; | import { PanoramaSharp } from "@mui/icons-material"; | ||||
import EscalationLogTable from "../DashboardPage/escalation/EscalationLogTable"; | import EscalationLogTable from "../DashboardPage/escalation/EscalationLogTable"; | ||||
import { EscalationResult } from "@/app/api/escalation"; | import { EscalationResult } from "@/app/api/escalation"; | ||||
import { EscalationCombo } from "@/app/api/user"; | |||||
interface Props { | interface Props { | ||||
itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] }; | itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] }; | ||||
qc: QcItemWithChecks[]; | qc: QcItemWithChecks[]; | ||||
disabled: boolean; | disabled: boolean; | ||||
escalationCombo: EscalationCombo[]; | |||||
// qcItems: QcData[] | // qcItems: QcData[] | ||||
// setQcItems: Dispatch<SetStateAction<QcData[]>> | // setQcItems: Dispatch<SetStateAction<QcData[]>> | ||||
} | } | ||||
@@ -71,7 +74,7 @@ type EntryError = | |||||
type QcRow = TableRow<Partial<QcData>, EntryError>; | type QcRow = TableRow<Partial<QcData>, EntryError>; | ||||
// fetchQcItemCheck | // fetchQcItemCheck | ||||
const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false }) => { | |||||
const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false, escalationCombo }) => { | |||||
const { t } = useTranslation("purchaseOrder"); | const { t } = useTranslation("purchaseOrder"); | ||||
const apiRef = useGridApiRef(); | const apiRef = useGridApiRef(); | ||||
const { | const { | ||||
@@ -95,6 +98,7 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false }) => { | |||||
const qcDecision = watch("qcDecision"); //WIP | const qcDecision = watch("qcDecision"); //WIP | ||||
// const qcResult = useMemo(() => [...watch("qcResult")], [watch("qcResult")]); | // const qcResult = useMemo(() => [...watch("qcResult")], [watch("qcResult")]); | ||||
const qcResult = [...watch("qcResult")]; | const qcResult = [...watch("qcResult")]; | ||||
const [qcHistory, setQcHistory] = useState<PurchaseQcResult[]>([]); | |||||
// const [qcAccept, setQcAccept] = useState(true); | // const [qcAccept, setQcAccept] = useState(true); | ||||
// const [qcItems, setQcItems] = useState(dummyQCData) | // const [qcItems, setQcItems] = useState(dummyQCData) | ||||
@@ -407,6 +411,12 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false }) => { | |||||
} | } | ||||
} | } | ||||
useEffect(() => { | |||||
if (qcHistory.length < 1) { | |||||
setQcHistory(qcResult.filter((qc) => {qc.escalationLogId != null})); | |||||
console.log("QC History updated:", qcHistory); | |||||
} | |||||
}, [watch("qcResult")]); | |||||
// useEffect(() => { | // useEffect(() => { | ||||
// // onFailedOpenCollapse(qcItems) | // // onFailedOpenCollapse(qcItems) | ||||
// }, [qcItems]); | // }, [qcItems]); | ||||
@@ -428,7 +438,8 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false }) => { | |||||
variant="scrollable" | variant="scrollable" | ||||
> | > | ||||
<Tab label={t("QC Info")} iconPosition="end" /> | <Tab label={t("QC Info")} iconPosition="end" /> | ||||
<Tab label={t("Escalation History")} iconPosition="end" /> | |||||
{(itemDetail.escResult && itemDetail.escResult?.length > 0) && | |||||
(<Tab label={t("Escalation History")} iconPosition="end" />)} | |||||
</Tabs> | </Tabs> | ||||
</Grid> | </Grid> | ||||
{tabIndex == 0 && ( | {tabIndex == 0 && ( | ||||
@@ -478,7 +489,17 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false }) => { | |||||
</Typography> | </Typography> | ||||
</Grid> */} | </Grid> */} | ||||
<Grid item xs={12}> | <Grid item xs={12}> | ||||
<EscalationLogTable items={itemDetail.escResult || []}/> | |||||
<EscalationLogTable type="qc" items={itemDetail.escResult || []}/> | |||||
{/* <Collapse in={true}> */} | |||||
<StyledDataGrid | |||||
columns={qcColumns} | |||||
rows={qcResult} | |||||
// rows={qcResult && qcResult.length > 0 ? qcResult : qcItems} | |||||
// rows={disabled? qcResult:qcItems} | |||||
autoHeight | |||||
sortModel={[]} | |||||
/> | |||||
{/* </Collapse> */} | |||||
{/* <StyledDataGrid | {/* <StyledDataGrid | ||||
rows={escalationHistory} | rows={escalationHistory} | ||||
columns={columns} | columns={columns} | ||||
@@ -569,6 +590,7 @@ const QcComponent: React.FC<Props> = ({ qc, itemDetail, disabled = false }) => { | |||||
forSupervisor={false} | forSupervisor={false} | ||||
isCollapsed={isCollapsed} | isCollapsed={isCollapsed} | ||||
setIsCollapsed={setIsCollapsed} | setIsCollapsed={setIsCollapsed} | ||||
escalationCombo={escalationCombo} | |||||
/> | /> | ||||
</Grid>)} | </Grid>)} | ||||
{/* {qcAccept && <Grid item xs={12}> | {/* {qcAccept && <Grid item xs={12}> | ||||
@@ -21,7 +21,6 @@ import StockInForm from "./StockInForm"; | |||||
import StockInFormVer2 from "./StockInFormVer2"; | import StockInFormVer2 from "./StockInFormVer2"; | ||||
import QcComponent from "./QcComponent"; | import QcComponent from "./QcComponent"; | ||||
import { dummyPutAwayLine, dummyQCData } from "./dummyQcTemplate"; | import { dummyPutAwayLine, dummyQCData } from "./dummyQcTemplate"; | ||||
// import QcFormVer2 from "./QcFormVer2"; | |||||
import PutAwayForm from "./PutAwayForm"; | import PutAwayForm from "./PutAwayForm"; | ||||
import { GridRowModes, useGridApiRef } from "@mui/x-data-grid"; | import { GridRowModes, useGridApiRef } from "@mui/x-data-grid"; | ||||
import {submitDialogWithWarning} from "../Swal/CustomAlerts"; | import {submitDialogWithWarning} from "../Swal/CustomAlerts"; | ||||
@@ -34,6 +33,7 @@ import { EscalationResult } from "@/app/api/escalation"; | |||||
import { SessionWithTokens } from "@/config/authConfig"; | import { SessionWithTokens } from "@/config/authConfig"; | ||||
import { GridRowModesModel } from "@mui/x-data-grid"; | import { GridRowModesModel } from "@mui/x-data-grid"; | ||||
import { isEmpty } from "lodash"; | import { isEmpty } from "lodash"; | ||||
import { EscalationCombo } from "@/app/api/user"; | |||||
const style = { | const style = { | ||||
@@ -68,6 +68,7 @@ interface CommonProps extends Omit<ModalProps, "children"> { | |||||
// type: "qc" | "stockIn" | "escalation" | "putaway" | "reject"; | // type: "qc" | "stockIn" | "escalation" | "putaway" | "reject"; | ||||
handleMailTemplateForStockInLine: (stockInLineId: number) => void; | handleMailTemplateForStockInLine: (stockInLineId: number) => void; | ||||
printerCombo: PrinterCombo[]; | printerCombo: PrinterCombo[]; | ||||
escalationCombo: EscalationCombo[]; | |||||
onClose: () => void; | onClose: () => void; | ||||
} | } | ||||
interface Props extends CommonProps { | interface Props extends CommonProps { | ||||
@@ -86,7 +87,8 @@ const PoQcStockInModalVer2: React.FC<Props> = ({ | |||||
qc, | qc, | ||||
warehouse, | warehouse, | ||||
handleMailTemplateForStockInLine, | handleMailTemplateForStockInLine, | ||||
printerCombo | |||||
printerCombo, | |||||
escalationCombo | |||||
}) => { | }) => { | ||||
const { | const { | ||||
t, | t, | ||||
@@ -486,7 +488,6 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
onSubmit={formProps.handleSubmit(onSubmitPutaway)} | onSubmit={formProps.handleSubmit(onSubmitPutaway)} | ||||
> | > | ||||
<PutAwayForm | <PutAwayForm | ||||
printerCombo={printerCombo} | |||||
itemDetail={itemDetail} | itemDetail={itemDetail} | ||||
warehouse={warehouse!} | warehouse={warehouse!} | ||||
disabled={viewOnly} | disabled={viewOnly} | ||||
@@ -569,6 +570,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
qc={qc!} | qc={qc!} | ||||
itemDetail={itemDetail} | itemDetail={itemDetail} | ||||
disabled={viewOnly} | disabled={viewOnly} | ||||
escalationCombo={escalationCombo} | |||||
// qcItems={qcItems} | // qcItems={qcItems} | ||||
// setQcItems={setQcItems} | // setQcItems={setQcItems} | ||||
/> | /> | ||||
@@ -17,7 +17,8 @@ | |||||
"Purchase Order Code": "採購單號", | "Purchase Order Code": "採購單號", | ||||
"Item Name": "貨品名稱", | "Item Name": "貨品名稱", | ||||
"Escalation Level": "上報等級", | "Escalation Level": "上報等級", | ||||
"Reason": "原因", | |||||
"Reason": "上報原因", | |||||
"escalated date": "上報日期", | |||||
"Order completion": "訂單完成度", | "Order completion": "訂單完成度", | ||||
"Raw material": "原料", | "Raw material": "原料", | ||||
"Consumable": "消耗品", | "Consumable": "消耗品", | ||||
@@ -32,6 +33,9 @@ | |||||
"Processed application": "已處理提料申請", | "Processed application": "已處理提料申請", | ||||
"Pending application": "待處理提料申請", | "Pending application": "待處理提料申請", | ||||
"pending inspection material": "待品檢物料", | "pending inspection material": "待品檢物料", | ||||
"pending": "待處理", | |||||
"rejected": "已拒絕", | |||||
"escalated": "已上報", | |||||
"inspected material": "已品檢物料", | "inspected material": "已品檢物料", | ||||
"total material": "物料總數", | "total material": "物料總數", | ||||
"stock in escalation list": "收貨已上報列表", | "stock in escalation list": "收貨已上報列表", | ||||
@@ -40,8 +44,11 @@ | |||||
"QC Fail Count": "品檢不合格數量", | "QC Fail Count": "品檢不合格數量", | ||||
"DN Date": "送貨日期", | "DN Date": "送貨日期", | ||||
"Received Qty": "收貨數量", | "Received Qty": "收貨數量", | ||||
"Po Code": "採購訂單編號", | |||||
"My Escalation List": "我的上報列表", | |||||
"Escalation List": "上報列表", | "Escalation List": "上報列表", | ||||
"Purchase UoM": "計量單位", | "Purchase UoM": "計量單位", | ||||
"QC Completed Count": "品檢完成數量", | "QC Completed Count": "品檢完成數量", | ||||
"QC Fail-Total Count": "品檢不合格/總數" | |||||
"QC Fail-Total Count": "品檢不合格/總數", | |||||
"escalationStatus": "上報狀態" | |||||
} | } |
@@ -39,7 +39,7 @@ | |||||
"qty": "訂單數量", | "qty": "訂單數量", | ||||
"uom": "計量單位", | "uom": "計量單位", | ||||
"Stock UoM": "庫存單位", | "Stock UoM": "庫存單位", | ||||
"Stock In Qty": "收貨數量", | |||||
"Stock In Qty": "換算庫存數量", | |||||
"total weight": "總重量", | "total weight": "總重量", | ||||
"weight unit": "重量單位", | "weight unit": "重量單位", | ||||
"price": "訂單貨值", | "price": "訂單貨值", | ||||
@@ -105,7 +105,7 @@ | |||||
"No Warehouse": "沒有倉庫", | "No Warehouse": "沒有倉庫", | ||||
"Please scan warehouse qr code.": "請掃描倉庫 QR 碼。", | "Please scan warehouse qr code.": "請掃描倉庫 QR 碼。", | ||||
"receivedQty": "已來貨數量", | "receivedQty": "已來貨數量", | ||||
"dnQty": "送貨單數量", | |||||
"dnQty": "本批來貨數量", | |||||
"Accept submit": "接受來貨", | "Accept submit": "接受來貨", | ||||
"qc processing": "處理來貨及品檢", | "qc processing": "處理來貨及品檢", | ||||
"putaway processing": "處理來貨及上架", | "putaway processing": "處理來貨及上架", | ||||
@@ -138,5 +138,7 @@ | |||||
"Printer": "列印機", | "Printer": "列印機", | ||||
"Printing": "列印中", | "Printing": "列印中", | ||||
"rejectQty": "拒絕數量", | "rejectQty": "拒絕數量", | ||||
"QC decision is required": "請決定品檢結果" | |||||
"QC decision is required": "請決定品檢結果", | |||||
"No Option": "沒有選項", | |||||
"receivedTotal": "已來貨總數" | |||||
} | } |