| @@ -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": "已來貨總數" | |||||
| } | } | ||||