Pārlūkot izejas kodu

Merge branch 'master' of https://git.2fi-solutions.com/derek/FPSMS-frontend

# Conflicts:
#	src/app/api/pickOrder/actions.ts
master
CANCERYS\kw093 pirms 2 dienas
vecāks
revīzija
2caff3ecc1
33 mainītis faili ar 744 papildinājumiem un 496 dzēšanām
  1. +3
    -0
      src/app/(main)/dashboard/page.tsx
  2. +2
    -2
      src/app/(main)/layout.tsx
  3. +4
    -0
      src/app/(main)/po/edit/page.tsx
  4. +0
    -3
      src/app/api/dashboard/actions.ts
  5. +5
    -4
      src/app/api/dashboard/index.ts
  6. +58
    -0
      src/app/api/escalation/index.ts
  7. +17
    -0
      src/app/api/mailTemplate/actions.ts
  8. +0
    -41
      src/app/api/pickOrder/actions.ts
  9. +9
    -5
      src/app/api/po/actions.ts
  10. +18
    -11
      src/app/api/po/index.ts
  11. +10
    -0
      src/app/api/qc/index.ts
  12. +15
    -0
      src/app/api/user/index.ts
  13. +6
    -1
      src/app/utils/fetchUtil.ts
  14. +9
    -5
      src/components/DashboardPage/DashboardPage.tsx
  15. +10
    -5
      src/components/DashboardPage/DashboardWrapper.tsx
  16. +76
    -19
      src/components/DashboardPage/escalation/EscalationLogTable.tsx
  17. +19
    -0
      src/components/General/LoadingComponent.tsx
  18. +1
    -0
      src/components/InputDataGrid/InputDataGrid.tsx
  19. +1
    -1
      src/components/PickOrderSearch/QcFormVer2.tsx
  20. +41
    -22
      src/components/PoDetail/EscalationComponent.tsx
  21. +77
    -43
      src/components/PoDetail/PoDetail.tsx
  22. +8
    -1
      src/components/PoDetail/PoDetailWrapper.tsx
  23. +1
    -1
      src/components/PoDetail/PoInfoCard.tsx
  24. +25
    -16
      src/components/PoDetail/PoInputGrid.tsx
  25. +49
    -42
      src/components/PoDetail/QcFormVer2.tsx
  26. +53
    -55
      src/components/PoDetail/QcStockInModalVer2.tsx
  27. +7
    -7
      src/components/PoDetail/StockInFormVer2.tsx
  28. +30
    -24
      src/components/PoDetail/dummyQcTemplate.tsx
  29. +1
    -1
      src/i18n/zh/common.json
  30. +45
    -38
      src/i18n/zh/dashboard.json
  31. +2
    -2
      src/i18n/zh/inventory.json
  32. +134
    -146
      src/i18n/zh/purchaseOrder.json
  33. +8
    -1
      src/theme/devias-material-kit/components.ts

+ 3
- 0
src/app/(main)/dashboard/page.tsx Parādīt failu

@@ -4,6 +4,7 @@ import { Suspense } from "react";
import { getServerI18n } from "@/i18n";
import DashboardPage from "@/components/DashboardPage";
import { SearchParams } from "@/app/utils/fetchUtil";
import { fetchEscalationLogsByUser } from "@/app/api/escalation";

export const metadata: Metadata = {
title: "Dashboard",
@@ -14,6 +15,8 @@ type Props = {} & SearchParams;
const Dashboard: React.FC<Props> = async ({ searchParams }) => {
const { t } = await getServerI18n("dashboard");

fetchEscalationLogsByUser()

return (
<I18nProvider namespaces={["dashboard", "common"]}>
<Suspense fallback={<DashboardPage.Loading />}>


+ 2
- 2
src/app/(main)/layout.tsx Parādīt failu

@@ -37,7 +37,7 @@ export default async function MainLayout({
return (
<SessionProviderWrapper session={session}>
<UploadProvider>
<CameraProvider>
{/* <CameraProvider> */}
<AxiosProvider>
<QrCodeScannerProvider>
<>
@@ -62,7 +62,7 @@ export default async function MainLayout({
</>
</QrCodeScannerProvider>
</AxiosProvider>
</CameraProvider>
{/* </CameraProvider> */}
</UploadProvider>
</SessionProviderWrapper>
);


+ 4
- 0
src/app/(main)/po/edit/page.tsx Parādīt failu

@@ -1,3 +1,4 @@
import { fetchEscalationCombo } from "@/app/api/user";
import { SearchParams } from "@/app/utils/fetchUtil";
import { TypeEnum } from "@/app/utils/typeEnum";
import CreateProductMaterial from "@/components/CreateItem";
@@ -22,6 +23,9 @@ const PoEdit: React.FC<Props> = async ({ searchParams }) => {
if (!id) {
notFound();
}

fetchEscalationCombo()

return (
<>
{/* <Typography variant="h4">{t("Create Material")}</Typography> */}


+ 0
- 3
src/app/api/dashboard/actions.ts Parādīt failu

@@ -30,9 +30,6 @@ export interface StockInLineEntry {
acceptedQty: number;
status?: string;
expiryDate?: string;
productLotNo?: string;
receiptDate?: string;
dnDate?: string;
}

export interface PurchaseQcResult {


+ 5
- 4
src/app/api/dashboard/index.ts Parādīt failu

@@ -6,7 +6,7 @@ import { serverFetchJson } from "../../utils/fetchUtil";
import { BASE_API_URL } from "../../../config/api";
import { Uom } from "../settings/uom";
import { RecordsRes } from "../utils";
import { IQCItems } from "@/components/DashboardPage/QC/SupervisorQcApproval";
// import { IQCItems } from "@/components/DashboardPage/QC/SupervisorQcApproval";

export interface PoResult {
id: number;
@@ -84,7 +84,8 @@ export const fetchPoWithStockInLines = cache(async (id: number) => {
});

export const fetchIqcLogByUser = cache(async () => {
return serverFetchJson<IQCItems[]>(`${BASE_API_URL}/supervisionApprovalLog/stock-in`, {
next: { tags: ["qcLog"] },
});
// return serverFetchJson<IQCItems[]>(`${BASE_API_URL}/supervisionApprovalLog/stock-in`, {
// next: { tags: ["qcLog"] },
// });
return undefined;
});

+ 58
- 0
src/app/api/escalation/index.ts Parādīt failu

@@ -0,0 +1,58 @@
"server only"

import { convertObjToURLSearchParams } from "@/app/utils/commonUtil";
import { serverFetchJson } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";
import { cache } from "react";

export interface EscalationResult {
id: number;
handler?: string;
handlerDepartment?: string;
handlerName?: string;
handlerTitle?: string;
polId?: number;
poId?: number;
reason?: string;
handlerId?: number;
itemName?: string;
demandQty?: number;
acceptedQty?: number;
purchaseUomCode?: string;
purchaseUomDesc?: string;
stockUomCode?: string;
stockUomDesc?: string;
stockInLineId?: number;
stockOutLineId?: number;
qcFailCount?: number;
qcTotalCount?: number;
poCode?: string;
itemCode?: string;
dnDate?: number[];
dnNo?: string;
}

export const fetchEscalationLogsByStockInLines = cache(async(stockInLineIds: number[]) => {
const searchParams = convertObjToURLSearchParams({stockInLineIds: stockInLineIds})
return serverFetchJson<EscalationResult[]>(`${BASE_API_URL}/escalationLog/stockInLines?${searchParams}`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
next: {
tags: ["escalationLogs"],
},
},
);
});

export const fetchEscalationLogsByUser = cache(async() => {
return serverFetchJson<EscalationResult[]>(`${BASE_API_URL}/escalationLog/user`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
next: {
tags: ["escalationLogs"],
},
},
);
});

+ 17
- 0
src/app/api/mailTemplate/actions.ts Parādīt failu

@@ -0,0 +1,17 @@
"use server";

import { serverFetchBlob } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";
import { cache } from "react";

export const getMailTemplateForStockInLine = cache(async (stockInLineId: number) => {
console.log("stockInLineId", stockInLineId)
return serverFetchBlob(`${BASE_API_URL}/mailTemplates/getMailTemplateForStockInLine/${stockInLineId}`,
{
method: "GET",
headers: { "Content-Type": "application/json" },
next: {
tags: ["mailTemplateForStockInLine"],
},
})
})

+ 0
- 41
src/app/api/pickOrder/actions.ts Parādīt failu

@@ -144,47 +144,6 @@ export interface PickOrderLotDetailResponse {
}


export interface AssignPickOrderInputs {
pickOrderIds: number[];
assignTo: number;
}
export const newassignPickOrder = async (data: AssignPickOrderInputs) => {
const pickOrder = await serverFetchJson<any>(
`${BASE_API_URL}/pickOrder/assign`,
{
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
},
);
revalidateTag("pickorder");
return pickOrder;
};

export const newreleasePickOrder = async (data: AssignPickOrderInputs) => {
const pickOrder = await serverFetchJson<any>(
`${BASE_API_URL}/pickOrder/release`,
{
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
},
);
revalidateTag("pickorder");
return pickOrder;
};
export const releaseAssignedPickOrders = async (data: AssignPickOrderInputs) => {
const pickOrder = await serverFetchJson<any>(
`${BASE_API_URL}/pickOrder/release-assigned`,
{
method: "POST",
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
},
);
revalidateTag("pickorder");
return pickOrder;
};
export const fetchAllPickOrderDetails = cache(async () => {
return serverFetchJson<GetPickOrderInfoResponse>(
`${BASE_API_URL}/pickOrder/detail`,


+ 9
- 5
src/app/api/po/actions.ts Parādīt failu

@@ -31,13 +31,17 @@ export interface StockInLineEntry {
acceptedQty: number;
status?: string;
expiryDate?: string;
productLotNo?: string;
receiptDate?: string;
dnDate?: string;
}

export interface PurchaseQcResult {
export interface PurchaseQcResult{
qcItemId: number;
isPassed: boolean,
qcPassed: boolean;
failQty: number;
remarks?: string
remarks?: string;
}
export interface StockInInput {
status: string;
@@ -110,7 +114,7 @@ export const fetchStockInLineInfo = cache(async (stockInLineId: number) => {

export const createStockInLine = async (data: StockInLineEntry) => {
const stockInLine = await serverFetchJson<
PostStockInLineResponse<StockInLineEntry>
PostStockInLineResponse<StockInLine>
>(`${BASE_API_URL}/stockInLine/create`, {
method: "POST",
body: JSON.stringify(data),
@@ -124,7 +128,7 @@ export const updateStockInLine = async (
data: StockInLineEntry & ModalFormInput,
) => {
const stockInLine = await serverFetchJson<
PostStockInLineResponse<StockInLineEntry & ModalFormInput>
PostStockInLineResponse<StockInLine & ModalFormInput>
>(`${BASE_API_URL}/stockInLine/update`, {
method: "POST",
body: JSON.stringify(data),


+ 18
- 11
src/app/api/po/index.ts Parādīt failu

@@ -15,6 +15,11 @@ export interface PoResult {
estimatedArrivalDate: string;
completedDate: string;
itemDetail?: string;
itemCode?: string;
itemName?: string;
itemQty?: string;
itemSumAcceptedQty?: string;
itemUom?: string;
escalated: boolean;
status: string;
pol?: PurchaseOrderLine[];
@@ -49,7 +54,7 @@ export interface StockUomForPoLine {

export interface StockInLine {
id: number;
stockInId: number;
stockInId?: number;
purchaseOrderId?: number;
purchaseOrderLineId: number;
itemId: number;
@@ -58,22 +63,24 @@ export interface StockInLine {
itemType: string;
demandQty: number;
acceptedQty: number;
qty: number;
processed: number;
price: number;
priceUnit: string;
qty?: number;
processed?: number;
price?: number;
priceUnit?: string;
shelfLife?: number;
receiptDate?: string;
productionDate?: string;
productLotNo?: string;
expiryDate?: string;
status: string;
supplier: string;
lotNo: string;
poCode: string;
uom: Uom;
supplier?: string;
lotNo?: string;
poCode?: string;
uom?: Uom;
defaultWarehouseId: number; // id for now
dnNo: string;
dnDate: number[];
dnNo?: string;
dnDate?: number[];
stockQty?: number;
}

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


+ 10
- 0
src/app/api/qc/index.ts Parādīt failu

@@ -15,6 +15,16 @@ export interface QcItemWithChecks {
description: string | undefined;
}

export interface QcData {
id: number,
code: string,
name: string,
qcDescription: string,
qcPassed: boolean | undefined
failQty: number | undefined
remarks: string | undefined
}

export const fetchQcItemCheckList = cache(async () => {
return serverFetchJson<QcItemWithChecks[]>(`${BASE_API_URL}/qc/list`, {
next: { tags: ["qc"] },


+ 15
- 0
src/app/api/user/index.ts Parādīt failu

@@ -31,6 +31,15 @@ export type passwordRule = {
specialChar: boolean;
};

export interface EscalationCombo {
id: number;
value: number;
label: string;
name: string;
title: string;
department: string;
}

export const preloadUser = () => {
fetchUser();
};
@@ -56,3 +65,9 @@ export const fetchPwRules = cache(async () => {
next: { tags: ["pwRule"] },
});
});

export const fetchEscalationCombo = cache(async () => {
return serverFetchJson<EscalationCombo>(`${BASE_API_URL}/user/escalation-combo`, {
next: { tags: ["escalationCombo"]}
})
})

+ 6
- 1
src/app/utils/fetchUtil.ts Parādīt failu

@@ -3,6 +3,11 @@ import { getServerSession } from "next-auth";
import { headers } from "next/headers";
import { redirect } from "next/navigation";

export interface BlobResponse {
filename: string;
blobValue: Uint8Array;
}

export interface Pageable {
pageSize?: number;
pageNum?: number;
@@ -86,7 +91,7 @@ export async function serverFetchJson<T>(...args: FetchParams) {
}
}

export async function serverFetchBlob<T>(...args: FetchParams) {
export async function serverFetchBlob<T extends BlobResponse>(...args: FetchParams) {
const response = await serverFetch(...args);

if (response.ok) {


+ 9
- 5
src/components/DashboardPage/DashboardPage.tsx Parādīt failu

@@ -14,13 +14,17 @@ import ApplicationCompletionChart from "./chart/ApplicationCompletionChart";
import OrderCompletionChart from "./chart/OrderCompletionChart";
import DashboardBox from "./Dashboardbox";
import CollapsibleCard from "./CollapsibleCard";
import SupervisorQcApproval, { IQCItems } from "./QC/SupervisorQcApproval";
// import SupervisorQcApproval, { IQCItems } from "./QC/SupervisorQcApproval";
import { EscalationResult } from "@/app/api/escalation";
import EscalationLogTable from "./escalation/EscalationLogTable";
type Props = {
iqc: IQCItems[]
// iqc: IQCItems[] | undefined
escalationLogs: EscalationResult[]
};

const DashboardPage: React.FC<Props> = ({
iqc
// iqc,
escalationLogs
}) => {
const { t } = useTranslation("dashboard");
const router = useRouter();
@@ -29,9 +33,9 @@ const DashboardPage: React.FC<Props> = ({
<ThemeProvider theme={theme}>
<Grid container spacing={2}>
<Grid item xs={12}>
<CollapsibleCard title={t("stock in escalation list")}>
<CollapsibleCard title={t("Escalation List")}>
<CardContent>
<SupervisorQcApproval items={iqc || []}/>
<EscalationLogTable items={escalationLogs || []}/>
</CardContent>
</CollapsibleCard>
</Grid>


+ 10
- 5
src/components/DashboardPage/DashboardWrapper.tsx Parādīt failu

@@ -4,7 +4,8 @@ import DashboardPage from "./DashboardPage";
import { Typography } from "@mui/material";
import { I18nProvider, getServerI18n } from "@/i18n";
import DashboardLoading from "./DashboardLoading";
import { fetchIqcLogByUser } from "@/app/api/dashboard";
import { fetchEscalationLogsByUser } from "@/app/api/escalation";
// import { fetchIqcLogByUser } from "@/app/api/dashboard";

// export type SessionWithAbilities = {
// abilities: string[]
@@ -23,16 +24,20 @@ const DashboardWrapper: React.FC<Props> & SubComponents = async ({
}) => {
const { t } = await getServerI18n("dashboard");
// const session: SessionWithAbilities = await getServerSession(authOptions)
const [iqcLog] = await Promise.all([
fetchIqcLogByUser()
// const [iqcLog] = await Promise.all([
// fetchIqcLogByUser()
// ])

const [escalationLogs] = await Promise.all([
fetchEscalationLogsByUser()
])
console.log(iqcLog)
return (
<>
<Typography variant="h4">{t("Dashboard")}</Typography>
<DashboardPage
iqc={iqcLog}
escalationLogs={escalationLogs}
// iqc={iqcLog}
// abilities={session ? session?.abilities : []}
/>
</>


src/components/DashboardPage/QC/SupervisorQcApproval.tsx → src/components/DashboardPage/escalation/EscalationLogTable.tsx Parādīt failu

@@ -2,9 +2,13 @@

import { Box, Card, CardActionArea, CardContent, CardHeader, Grid, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material";
import { useRouter } from "next/navigation";
import { useCallback, useState } from "react";
import { useCallback, useMemo, useState } from "react";
import { usePathname } from "next/navigation";
import { useTranslation } from "react-i18next";
import { EscalationResult } from "@/app/api/escalation";
import { Column } from "@/components/SearchResults";
import SearchResults from "@/components/SearchResults/SearchResults";
import { arrayToDateString } from "@/app/utils/formatUtil";

export type IQCItems = {
id: number;
@@ -18,11 +22,11 @@ export type IQCItems = {
};

type Props = {
items: IQCItems[];
items: EscalationResult[];
};

const SupervisorQcApproval: React.FC<Props> = ({
items
const EscalationLogTable: React.FC<Props> = ({
items
}) => {
const { t } = useTranslation("dashboard");
const CARD_HEADER = t("stock in escalation list")
@@ -31,8 +35,8 @@ const SupervisorQcApproval: React.FC<Props> = ({
const router = useRouter();
const [selectedId, setSelectedId] = useState<number | null>(null);

const navigateTo = useCallback(
(item: IQCItems) => {
const navigateTo = useCallback(
(item: EscalationResult) => {
setSelectedId(item.id);
console.log(pathname)
router.replace(`/po/edit?id=${item.poId}&polId=${item.polId}&stockInLineId=${item.stockInLineId}`);
@@ -40,17 +44,62 @@ const navigateTo = useCallback(
[router, pathname]
);

const handleKeyDown = useCallback(
(e: React.KeyboardEvent, item: IQCItems) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
navigateTo(item);
}
},
[navigateTo]
);
const onRowClick = useCallback((item: EscalationResult) => {
router.push(`/po/edit?id=${item.poId}&selectedIds=${item.poId}&polId=${item.polId}&stockInLineId=${item.stockInLineId}`);
}, [router])

// const handleKeyDown = useCallback(
// (e: React.KeyboardEvent, item: EscalationResult) => {
// if (e.key === 'Enter' || e.key === ' ') {
// e.preventDefault();
// navigateTo(item);
// }
// },
// [navigateTo]
// );

return (
const columns = useMemo<Column<EscalationResult>[]>(
() => [
{
name: "handler",
label: t("Responsible for handling colleagues")
},
{
name: "acceptedQty",
label: t("Received Qty"),
align: "right",
headerAlign: "right"
},
{
name: "purchaseUomDesc",
label: t("Purchase UoM")
},
{
name: "dnDate",
label: t("DN Date"),
renderCell: (params) => {
return params.dnDate ? arrayToDateString(params.dnDate) : "N/A"
}
},
{
name: "qcTotalCount",
label: t("QC Completed Count"),
align: "right",
headerAlign: "right"
},
{
name: "qcFailCount",
label: t("QC Fail Count"),
align: "right",
headerAlign: "right"
},
{
name: "reason",
label: t("Reason"),
},
], [])

{/* return (
<TableContainer component={Paper}>
<Table aria-label="Two column navigable table" size="small">
<TableHead>
@@ -87,7 +136,15 @@ const navigateTo = useCallback(
</TableBody>
</Table>
</TableContainer>
);
};
);*/}
return (
<SearchResults
onRowClick={onRowClick}
items={items}
columns={columns}
isAutoPaging={false}
/>
)
};

export default SupervisorQcApproval;
export default EscalationLogTable;

+ 19
- 0
src/components/General/LoadingComponent.tsx Parādīt failu

@@ -0,0 +1,19 @@
import {Box, CircularProgress, Grid} from "@mui/material";

export const LoadingComponent: React.FC = () => {
return (
<>
<Grid item xs={12} md={12} lg={12} justifyContent="space-between" alignItems="center" marginTop={10}>
<Box
display="flex"
justifyContent="center"
alignItems="center"
// autoheight="true"
>
<CircularProgress />
</Box>
</Grid>
</>
)
}
export default LoadingComponent;

+ 1
- 0
src/components/InputDataGrid/InputDataGrid.tsx Parādīt failu

@@ -84,6 +84,7 @@ export interface SelectionInputDataGridProps<T, V, E> {
columns: GridColDef[];
validateRow: (newRow: GridRowModel<TableRow<V, E>>) => E;
needAdd?: boolean;
showRemoveBtn?: boolean;
}

export type Props<T, V, E> =


+ 1
- 1
src/components/PickOrderSearch/QcFormVer2.tsx Parādīt failu

@@ -49,7 +49,7 @@ import EscalationComponent from "./EscalationComponent";
import QcDataGrid from "./QCDatagrid";
import StockInFormVer2 from "./StockInFormVer2";
import { dummyEscalationHistory, dummyQCData, QcData } from "./dummyQcTemplate";
import { ModalFormInput } from "@/app/api/dashboard/actions";
import { ModalFormInput } from "@/app/api/po/actions";
import { escape } from "lodash";

interface Props {


+ 41
- 22
src/components/PoDetail/EscalationComponent.tsx Parādīt failu

@@ -83,10 +83,18 @@ const EscalationComponent: React.FC<Props> = ({
return (
// <Paper elevation={3} sx={{ maxWidth: 400, mx: 'auto', p: 3 }}>
<>
<Paper>
<Paper sx={{padding: 2}}>
{/* <Paper elevation={3} sx={{ mx: 'auto', p: 3 }}> */}
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<FormControlLabel
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Typography variant="body1">上報結果</Typography>
{/* {isCollapsed ? (
<ExpandLessIcon sx={{ ml: 1 }} />
) : (
<ExpandMoreIcon sx={{ ml: 1 }} />
)} */}
</Box>
{/* <FormControlLabel
control={
<Checkbox
checked={isCollapsed}
@@ -104,23 +112,10 @@ const EscalationComponent: React.FC<Props> = ({
)}
</Box>
}
/>
/> */}
</Box>
<Collapse in={isCollapsed}>
<Box component="form" onSubmit={handleSubmit} sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
{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}
<FormControl fullWidth>
<select
id="name"
@@ -136,7 +131,31 @@ const EscalationComponent: React.FC<Props> = ({
))}
</select>
</FormControl>
<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
id="quantity"
name="quantity"
@@ -146,21 +165,21 @@ const EscalationComponent: React.FC<Props> = ({
onChange={handleInputChange}
InputProps={{ inputProps: { min: 1 } }}
placeholder="請輸入數量"
/>
/> */}

<TextField
fullWidth
id="message"
name="message"
label="備註"
label="上報原因"
multiline
rows={4}
value={formData.message}
onChange={handleInputChange}
placeholder="請輸入您的備註"
placeholder="請輸入上報原因"
/>

<Stack direction="row" justifyContent="flex-end" gap={1}>
{/* <Stack direction="row" justifyContent="flex-end" gap={1}>
<Button
type="submit"
variant="contained"
@@ -168,7 +187,7 @@ const EscalationComponent: React.FC<Props> = ({
>
{t("update qc info")}
</Button>
</Stack>
</Stack> */}
</Box>
</Collapse>
</Paper>


+ 77
- 43
src/components/PoDetail/PoDetail.tsx Parādīt failu

@@ -43,9 +43,11 @@ import {
import {
checkPolAndCompletePo,
fetchPoInClient,
fetchPoListClient,
fetchStockInLineInfo,
PurchaseQcResult,
startPo,
createStockInLine
} from "@/app/api/po/actions";
import {
useCallback,
@@ -66,21 +68,20 @@ import PoQcStockInModal from "./PoQcStockInModal";
import QrModal from "./QrModal";
import { PlayArrow } from "@mui/icons-material";
import DoneIcon from "@mui/icons-material/Done";
import { getCustomWidth } from "@/app/utils/commonUtil";
import { downloadFile, getCustomWidth } from "@/app/utils/commonUtil";
import PoInfoCard from "./PoInfoCard";
import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil";
import { fetchPoListClient } from "@/app/api/po/actions";
import { List, ListItem, ListItemButton, ListItemText, Divider } from "@mui/material";
import { createStockInLine } from "@/app/api/dashboard/actions";
import { Controller, FormProvider, useForm } from "react-hook-form";
import dayjs, { Dayjs } from "dayjs";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
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 { useRouter } from "next/navigation";



type Props = {
po: PoResult;
qc: QcItemWithChecks[];
@@ -115,7 +116,7 @@ const PoSearchList: React.FC<{
return (
<Paper sx={{ p: 2, maxHeight: "480px", overflow: "auto", minWidth: "300px", height: "480px" }}>
<Typography variant="h6" gutterBottom>
{t("Purchase Orders")}
{t("Purchase Order")}
</Typography>
<TextField
label={t("Search")}
@@ -133,44 +134,49 @@ const PoSearchList: React.FC<{
),
}}
/>
<List dense sx={{ width: '100%' }}>
{filteredPoList.map((poItem, index) => (
<div key={poItem.id}>
<ListItem disablePadding sx={{ width: '100%' }}>
<ListItemButton
selected={selectedPoId === poItem.id}
onClick={() => onSelect(poItem)}
sx={{
width: '100%',
"&.Mui-selected": {
backgroundColor: "primary.light",
"&:hover": {
{(filteredPoList.length > 0)? (
<List dense sx={{ width: '100%' }}>
{filteredPoList.map((poItem, index) => (
<div key={poItem.id}>
<ListItem disablePadding sx={{ width: '100%' }}>
<ListItemButton
selected={selectedPoId === poItem.id}
onClick={() => onSelect(poItem)}
sx={{
width: '100%',
"&.Mui-selected": {
backgroundColor: "primary.light",
"&:hover": {
backgroundColor: "primary.light",
},
},
},
}}
>
<ListItemText
primary={
<Typography variant="body2" sx={{ wordBreak: 'break-all' }}>
{poItem.code}
</Typography>
}
secondary={
<Typography variant="caption" color="text.secondary">
{t(`${poItem.status.toLowerCase()}`)}
</Typography>
}
/>
</ListItemButton>
</ListItem>
{index < filteredPoList.length - 1 && <Divider />}
</div>
))}
</List>
}}
>
<ListItemText
primary={
<Typography variant="body2" sx={{ wordBreak: 'break-all' }}>
{poItem.code}
</Typography>
}
secondary={
<Typography variant="caption" color="text.secondary">
{t(`${poItem.status.toLowerCase()}`)}
</Typography>
}
/>
</ListItemButton>
</ListItem>
{index < filteredPoList.length - 1 && <Divider />}
</div>
))}
</List>) : (
<LoadingComponent/>
)
}
{searchTerm && (
<Typography variant="caption" color="text.secondary" sx={{ mt: 1, display: "block" }}>
{t("Found")} {filteredPoList.length} {t("of")} {poList.length} {t("items")}
{`${t("Found")} ${filteredPoList.length} ${t("Purchase Order")}`}
{/* {`${t("Found")} ${filteredPoList.length} of ${poList.length} ${t("Item")}`} */}
</Typography>
)}
</Paper>
@@ -184,7 +190,7 @@ interface PolInputResult {

const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
const cameras = useContext(CameraContext);
console.log(cameras);
// console.log(cameras);
const { t } = useTranslation("purchaseOrder");
const apiRef = useGridApiRef();
const [purchaseOrder, setPurchaseOrder] = useState({ ...po });
@@ -205,6 +211,12 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
const searchParams = useSearchParams();
const [selectedRow, setSelectedRow] = useState<PurchaseOrderLine | null>(null);
const defaultPolId = searchParams.get("polId")
useEffect(() => {
if (defaultPolId) {
setSelectedRow(rows.find((r) => r.id.toString() === defaultPolId) ?? null)
}
}, [])

const [stockInLine, setStockInLine] = useState<StockInLine[]>([]);
const [processedQty, setProcessedQty] = useState(0);
@@ -212,6 +224,8 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
const router = useRouter();
const [poList, setPoList] = useState<PoResult[]>([]);
const [selectedPoId, setSelectedPoId] = useState(po.id);
const [focusField, setFocusField] = useState<HTMLInputElement>();

const currentPoId = searchParams.get('id');
const selectedIdsParam = searchParams.get('selectedIds');
// const [selectedRowId, setSelectedRowId] = useState<number | null>(null);
@@ -261,6 +275,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
setProcessedQty(result.pol[0].processed);
}
}
// if (focusField) {console.log(focusField);focusField.focus();}
}
} catch (error) {
console.error("Failed to fetch PO detail:", error);
@@ -319,6 +334,13 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
setPurchaseOrder(newPo);
}, [purchaseOrder.id]);

const handleMailTemplateForStockInLine = useCallback(async (stockInLineId: number) => {
const response = await getMailTemplateForStockInLine(stockInLineId)
if (response) {
downloadFile(new Uint8Array(response.blobValue), response.filename);
}
}, [])

useEffect(() => {
setRows(purchaseOrder.pol || []);
}, [purchaseOrder]);
@@ -388,6 +410,9 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
setTimeout(async () => {
// post stock in line
const oldId = row.id;
const acceptedQty = Number(polInputList[rowIndex].dnQty);
if (isNaN(acceptedQty) || acceptedQty <= 0) { alert("來貨數量必須大於0!"); return; } // Temp check, need update

const postData = {
dnNo: dnFormProps.watch("dnNo"),
dnDate: outputDateStringToInputDateString(dnFormProps.watch("dnDate")),
@@ -396,7 +421,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
itemName: row.itemName,
purchaseOrderId: row.purchaseOrderId,
purchaseOrderLineId: row.id,
acceptedQty: polInputList[rowIndex].dnQty || 0,
acceptedQty: acceptedQty,
productLotNo: polInputList[rowIndex].lotNo || '',
// acceptedQty: secondReceiveQty || 0,
// acceptedQty: row.acceptedQty,
@@ -447,6 +472,10 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
}
// setPolInputList(() => temp)
}, 300), [rowIndex]);

// const [focusField, setFocusField] = useState<HTMLInputElement>();

const purchaseToStockRatio = (row.stockUom.purchaseRatioN ?? 1) / (row.stockUom.purchaseRatioD ?? 1) * (row.stockUom.stockRatioD ?? 1) / (row.stockUom.stockRatioN ?? 1)
return (
<>
<TableRow
@@ -477,7 +506,8 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
<TableCell align="right">{integerFormatter.format(row.qty)}</TableCell>
<TableCell align="right">{integerFormatter.format(processedQty)}</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="left">{row.stockUom.stockUomCode}</TableCell>
{/* <TableCell align="right">
{decimalFormatter.format(totalWeight)} {weightUnit}
@@ -495,6 +525,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
variant="outlined"
defaultValue={polInputList[rowIndex]?.lotNo ?? ''}
onChange={handleChange}
// onFocus={(e) => {setFocusField(e.target as HTMLInputElement);}}
/>
</TableCell>
<TableCell align="center">
@@ -520,7 +551,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
handleStart()
}
>
提交
{t("submit")}
</Button>
</TableCell>
</TableRow>
@@ -554,6 +585,8 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
</>
);
}
// ROW END

const [tabIndex, setTabIndex] = useState(0);
const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>(
(_e, newValue) => {
@@ -824,6 +857,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
itemDetail={selectedRow}
warehouse={warehouse}
fetchPoDetail={fetchPoDetail}
handleMailTemplateForStockInLine={handleMailTemplateForStockInLine}
/>
</Box>
</TableCell>


+ 8
- 1
src/components/PoDetail/PoDetailWrapper.tsx Parādīt failu

@@ -10,6 +10,7 @@ import PoDetail from "./PoDetail";
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";

interface SubComponents {
Loading: typeof PoDetailLoading;
@@ -20,10 +21,16 @@ type Props = {
};

const PoDetailWrapper: React.FC<Props> & SubComponents = async ({ id }) => {
const [poWithStockInLine, warehouse, qc] = await Promise.all([
const [
poWithStockInLine,
warehouse,
qc,
escalationCombo
] = await Promise.all([
fetchPoWithStockInLines(id),
fetchWarehouseList(),
fetchQcItemCheck(),
fetchEscalationCombo(),
]);
// const poWithStockInLine = await fetchPoWithStockInLines(id)



+ 1
- 1
src/components/PoDetail/PoInfoCard.tsx Parādīt failu

@@ -19,7 +19,7 @@ type Props = {
po: PoResult;
};

const PoInfoCard: React.FC<Props> = async (
const PoInfoCard: React.FC<Props> = (
{
// id
po


+ 25
- 16
src/components/PoDetail/PoInputGrid.tsx Parādīt failu

@@ -74,6 +74,7 @@ interface Props {
stockInLine: StockInLine[];
warehouse: WarehouseResult[];
fetchPoDetail: (poId: string) => void;
handleMailTemplateForStockInLine: (stockInLineId: number) => void;
}

export type StockInLineEntryError = {
@@ -112,7 +113,8 @@ function PoInputGrid({
itemDetail,
stockInLine,
warehouse,
fetchPoDetail
fetchPoDetail,
handleMailTemplateForStockInLine
}: Props) {
console.log(itemDetail);
const { t } = useTranslation("purchaseOrder");
@@ -270,6 +272,7 @@ function PoInputGrid({

const [newOpen, setNewOpen] = useState(false);
const stockInLineId = searchParams.get("stockInLineId");
const poLineId = searchParams.get("poLineId");
const closeNewModal = useCallback(() => {
const newParams = new URLSearchParams(searchParams.toString());
newParams.delete("stockInLineId"); // Remove the parameter
@@ -282,46 +285,51 @@ const closeNewModal = useCallback(() => {

// Open modal
const openNewModal = useCallback(() => {
setNewOpen(true);
setNewOpen(() => true);
}, []);

// Button handler to update the URL and open the modal
const handleNewQC = useCallback(
(id: GridRowId, params: any) => async () => {
(id: GridRowId, params: any) => async() => {
// console.log(id)
// console.log(params)
setBtnIsLoading(true);
// setBtnIsLoading(true);
setRowModesModel((prev) => ({
...prev,
[id]: { mode: GridRowModes.View },
}));

const qcResult = await fetchQcDefaultValue(id);
setModalInfo({
setModalInfo(() => ({
...params.row,
qcResult: qcResult,
receivedQty: itemDetail.receivedQty,
});
}));

setTimeout(() => {
const newParams = new URLSearchParams(searchParams.toString());
newParams.set("stockInLineId", id.toString()); // Ensure `set` to avoid duplicates
router.replace(`${pathname}?${newParams.toString()}`);
console.log("hello")
// console.log("hello")
openNewModal()
setBtnIsLoading(false);
// setBtnIsLoading(false);
}, 200);
},
[fetchQcDefaultValue, openNewModal, pathname, router, searchParams]
);

// Open modal if `stockInLineId` exists in the URL
const [firstCheckForSil, setFirstCheckForSil] = useState(false)
useEffect(() => {
if (stockInLineId) {
console.log("heeloo")
console.log(stockInLineId)
handleNewQC(stockInLineId, apiRef.current.getRow(stockInLineId));
if (stockInLineId && itemDetail && !firstCheckForSil) {
// console.log("heeloo")
// console.log(stockInLineId)
// console.log(apiRef.current.getRow(stockInLineId))
setFirstCheckForSil(true)
const fn = handleNewQC(stockInLineId, {row: apiRef.current.getRow(stockInLineId)});
fn();
}
}, [stockInLineId, newOpen, handleNewQC, apiRef]);
}, [stockInLineId, poLineId, itemDetail]);
const handleEscalation = useCallback(
(id: GridRowId, params: any) => () => {
// setBtnIsLoading(true);
@@ -580,7 +588,7 @@ const closeNewModal = useCallback(() => {
}}
onClick={handleNewQC(params.row.id, params)}
color="inherit"
key="edit"
key={`edit`}
/>,
<GridActionsCellItem
icon={<Button
@@ -589,7 +597,7 @@ const closeNewModal = useCallback(() => {
variant="contained"
color="primary"
sx={{ width: '150px' }}
// onClick={formProps.handleSubmit(onSubmitEmailSupplier)}
onClick={() => handleMailTemplateForStockInLine(params.row.id as number)}
>
{t("email supplier")}
</Button>}
@@ -598,7 +606,7 @@ const closeNewModal = useCallback(() => {
// color: "primary.main",
// marginRight: 1,
}}
onClick={handleNewQC(params.row.id, params)}
// onClick={handleNewQC(params.row.id, params)}
color="inherit"
key="edit"
/>,
@@ -908,6 +916,7 @@ const closeNewModal = useCallback(() => {
open={newOpen}
onClose={closeNewModal}
itemDetail={modalInfo}
handleMailTemplateForStockInLine={handleMailTemplateForStockInLine}
/>
</>
)


+ 49
- 42
src/components/PoDetail/QcFormVer2.tsx Parādīt failu

@@ -41,20 +41,20 @@ import { GridEditInputCell } from "@mui/x-data-grid";
import { StockInLine } from "@/app/api/po";
import { stockInLineStatusMap } from "@/app/utils/formatUtil";
import { fetchQcItemCheck, fetchQcResult } from "@/app/api/qc/actions";
import { QcItemWithChecks } from "@/app/api/qc";
import { QcItemWithChecks, QcData } from "@/app/api/qc";
import axios from "@/app/(main)/axios/axiosInstance";
import { NEXT_PUBLIC_API_URL } from "@/config/api";
import axiosInstance from "@/app/(main)/axios/axiosInstance";
import EscalationComponent from "./EscalationComponent";
import QcDataGrid from "./QCDatagrid";
import StockInFormVer2 from "./StockInFormVer2";
import { dummyEscalationHistory, dummyQCData, QcData } from "./dummyQcTemplate";
import { ModalFormInput } from "@/app/api/dashboard/actions";
import { dummyEscalationHistory, dummyQCData } from "./dummyQcTemplate";
import { ModalFormInput } from "@/app/api/po/actions";
import { escape } from "lodash";
import { PanoramaSharp } from "@mui/icons-material";

interface Props {
itemDetail: StockInLine;
itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] };
qc: QcItemWithChecks[];
disabled: boolean;
qcItems: QcData[]
@@ -88,8 +88,10 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
const [tabIndex, setTabIndex] = useState(0);
const [rowSelectionModel, setRowSelectionModel] = useState<GridRowSelectionModel>();
const [escalationHistory, setEscalationHistory] = useState(dummyEscalationHistory);
const [qcResult, setQcResult] = useState();
// const [qcResult, setQcResult] = useState();
const qcAccept = watch("qcAccept");
const qcResult = watch("qcResult");
console.log(qcResult);
// const [qcAccept, setQcAccept] = useState(true);
// const [qcItems, setQcItems] = useState(dummyQCData)

@@ -184,43 +186,45 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI

const qcColumns: GridColDef[] = [
{
field: "qcItem",
field: "code",
headerName: t("qcItem"),
flex: 2,
renderCell: (params) => (
<Box>
<b>{params.value}</b><br/>
{params.row.qcDescription}<br/>
{params.row.name}<br/>
</Box>
),
},
{
field: 'isPassed',
field: 'qcPassed',
headerName: t("qcResult"),
flex: 1.5,
renderCell: (params) => {
const currentValue = params.value;
const currentValue = params.row;
console.log(currentValue.row);
return (
<FormControl>
<RadioGroup
row
aria-labelledby="demo-radio-buttons-group-label"
value={currentValue === undefined ? "" : (currentValue ? "true" : "false")}
value={currentValue.qcPassed === undefined ? "" : (currentValue.qcPassed ? "true" : "false")}
// value={currentValue.qcPassed === undefined ? (currentValue.failQty!==undefined?(currentValue.failQty==0?"true":"false"):"") : (currentValue.qcPassed ? "true" : "false")}
onChange={(e) => {
const value = e.target.value;
setQcItems((prev) =>
prev.map((r): QcData => (r.id === params.id ? { ...r, isPassed: value === "true" } : r))
prev.map((r): QcData => (r.id === params.id ? { ...r, qcPassed: value === "true" } : r))
);
}}
name={`isPassed-${params.id}`}
name={`qcPassed-${params.id}`}
>
<FormControlLabel
value="true"
control={<Radio />}
label="合格"
disabled={itemDetail.status.toLowerCase() == "completed"}
disabled={disabled}
sx={{
color: currentValue === true ? "green" : "inherit",
color: currentValue.qcPassed === true ? "green" : "inherit",
"& .Mui-checked": {color: "green"}
}}
/>
@@ -228,9 +232,9 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
value="false"
control={<Radio />}
label="不合格"
disabled={itemDetail.status.toLowerCase() == "completed"}
disabled={disabled}
sx={{
color: currentValue === false ? "red" : "inherit",
color: currentValue.qcPassed === false ? "red" : "inherit",
"& .Mui-checked": {color: "red"}
}}
/>
@@ -248,8 +252,8 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
<TextField
type="number"
size="small"
value={!params.row.isPassed? (params.value ?? '') : '0'}
disabled={params.row.isPassed || itemDetail.status.toLowerCase() == "completed"}
value={!params.row.qcPassed? (params.value ?? '') : '0'}
disabled={params.row.qcPassed || disabled}
onChange={(e) => {
const v = e.target.value;
const next = v === '' ? undefined : Number(v);
@@ -257,6 +261,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
setQcItems((prev) =>
prev.map((r) => (r.id === params.id ? { ...r, failQty: next } : r))
);
// setValue(`failQty`,failQty);
}}
onClick={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()}
@@ -274,7 +279,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
<TextField
size="small"
value={params.value ?? ''}
disabled={itemDetail.status.toLowerCase() == "completed"}
disabled={disabled}
onChange={(e) => {
const remarks = e.target.value;
// const next = v === '' ? undefined : Number(v);
@@ -283,6 +288,9 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
prev.map((r) => (r.id === params.id ? { ...r, remarks: remarks } : r))
);
}}
// {...register(`qcResult.${params.row.rowIndex}.remarks`, {
// required: "remarks required!",
// })}
onClick={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
@@ -293,11 +301,6 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
},
]

useEffect(() => {
console.log(itemDetail);
}, [itemDetail]);

// Set initial value for acceptQty
useEffect(() => {
if (itemDetail?.demandQty > 0) { //!== undefined) {
@@ -308,10 +311,10 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
}, [itemDetail?.demandQty, itemDetail?.acceptedQty, setValue]);

// const [openCollapse, setOpenCollapse] = useState(false)
const [isCollapsed, setIsCollapsed] = useState<boolean>(false);
const [isCollapsed, setIsCollapsed] = useState<boolean>(true);

const onFailedOpenCollapse = useCallback((qcItems: QcData[]) => {
const isFailed = qcItems.some((qc) => !qc.isPassed)
const onFailedOpenCollapse = useCallback((qcItems: PurchaseQcResult[]) => {
const isFailed = qcItems.some((qc) => !qc.qcPassed)
console.log(isFailed)
if (isFailed) {
setIsCollapsed(true)
@@ -327,10 +330,11 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI


useEffect(() => {
console.log(itemDetail);
console.log("ItemDetail in QC:", itemDetail);
}, [itemDetail]);


useEffect(() => {
// onFailedOpenCollapse(qcItems) // This function is no longer needed
}, [qcItems]); // Removed onFailedOpenCollapse from dependency array
@@ -366,19 +370,10 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
/> */}
<StyledDataGrid
columns={qcColumns}
rows={qcItems}
rows={disabled? qcResult:qcItems}
autoHeight
/>
</Grid>
{!qcAccept && (
<Grid item xs={12}>
<EscalationComponent
forSupervisor={false}
isCollapsed={isCollapsed}
setIsCollapsed={setIsCollapsed}
/>
</Grid>)}
</>
)}
{tabIndex == 1 && (
@@ -425,7 +420,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
field.onChange(value);
}}
>
<FormControlLabel disabled={itemDetail.status.toLowerCase() == "completed"}
<FormControlLabel disabled={disabled}
value="true" control={<Radio />} label="接受" />
<Box sx={{mr:2}}>
<TextField
@@ -434,7 +429,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
sx={{ width: '150px' }}
value={qcAccept? accQty : 0 }
defaultValue={accQty}
disabled={!qcAccept || itemDetail.status.toLowerCase() == "completed"}
disabled={!qcAccept || disabled}
{...register("acceptQty", {
required: "acceptQty required!",
})}
@@ -442,15 +437,27 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI
helperText={errors.acceptQty?.message}
/>
</Box>
<FormControlLabel disabled={itemDetail.status.toLowerCase() == "completed"}
<FormControlLabel disabled={disabled}
value="false" control={<Radio />}
sx={{"& .Mui-checked": {color: "red"}}}
label="不接受及上報" />
label="不接受" />
<FormControlLabel disabled={disabled}
value="false" control={<Radio />}
sx={{"& .Mui-checked": {color: "blue"}}}
label="上報品檢結果" />
</RadioGroup>
)}
/>
</FormControl>
</Grid>
{!qcAccept && (
<Grid item xs={12}>
<EscalationComponent
forSupervisor={false}
isCollapsed={isCollapsed}
setIsCollapsed={setIsCollapsed}
/>
</Grid>)}
{/* {qcAccept && <Grid item xs={12}>
<Typography variant="h6" display="block" marginBlockEnd={1}>
{t("Escalation Result")}


+ 53
- 55
src/components/PoDetail/QcStockInModalVer2.tsx Parādīt failu

@@ -1,7 +1,7 @@
"use client";
import { StockInLine } from "@/app/api/po";
import { ModalFormInput, PurchaseQcResult, StockInLineEntry, updateStockInLine } from "@/app/api/po/actions";
import { QcItemWithChecks } from "@/app/api/qc";
import { ModalFormInput, PurchaseQcResult, StockInLineEntry, updateStockInLine, PurchaseQCInput } from "@/app/api/po/actions";
import { QcItemWithChecks, QcData } from "@/app/api/qc";
import {
Box,
Button,
@@ -19,10 +19,9 @@ import StockInForm from "./StockInForm";
import StockInFormVer2 from "./StockInFormVer2";
import QcFormVer2 from "./QcFormVer2";
import PutawayForm from "./PutawayForm";
import { dummyPutawayLine, dummyQCData, QcData } from "./dummyQcTemplate";
import { dummyPutawayLine, dummyQCData } from "./dummyQcTemplate";
import { useGridApiRef } from "@mui/x-data-grid";
import {submitDialogWithWarning} from "../Swal/CustomAlerts";
import { PurchaseQCInput, PutawayInput } from "@/app/api/dashboard/actions";
import { arrayToDateString, arrayToInputDateString, dayjsToInputDateString } from "@/app/utils/formatUtil";
import dayjs from "dayjs";

@@ -36,7 +35,7 @@ const style = {
px: 5,
pb: 10,
display: "block",
width: { xs: "60%", sm: "60%", md: "60%" },
width: { xs: "90%", sm: "90%", md: "90%" },
// height: { xs: "60%", sm: "60%", md: "60%" },
};
interface CommonProps extends Omit<ModalProps, "children"> {
@@ -55,6 +54,7 @@ interface CommonProps extends Omit<ModalProps, "children"> {
qc?: QcItemWithChecks[];
warehouse?: any[];
// type: "qc" | "stockIn" | "escalation" | "putaway" | "reject";
handleMailTemplateForStockInLine: (stockInLineId: number) => void;
}
interface Props extends CommonProps {
itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] };
@@ -70,8 +70,8 @@ const PoQcStockInModalVer2: React.FC<Props> = ({
setItemDetail,
qc,
warehouse,
handleMailTemplateForStockInLine,
}) => {
console.log(warehouse);
const {
t,
i18n: { language },
@@ -113,6 +113,15 @@ const [qcItems, setQcItems] = useState(dummyQCData)
setOpenPutaway(isPutaway);
}, [open])
const [viewOnly, setViewOnly] = useState(false);
useEffect(() => {
if (itemDetail && itemDetail.status) {
const isViewOnly = itemDetail.status.toLowerCase() == "completed" || itemDetail.status.toLowerCase() == "rejected"
setViewOnly(isViewOnly)
}
}, [itemDetail]);

const [openPutaway, setOpenPutaway] = useState(false);
const onOpenPutaway = useCallback(() => {
@@ -155,21 +164,25 @@ const [qcItems, setQcItems] = useState(dummyQCData)
// Get QC data from the shared form context
const qcAccept = data.qcAccept;
const acceptQty = data.acceptQty as number;
const qcResults = qcItems;
// const qcResults = viewOnly? data.qcResult as PurchaseQcResult[] : qcItems;
// Validate QC data
const validationErrors : string[] = [];
// Check if all QC items have results
const itemsWithoutResult = qcItems.filter(item => item.isPassed === undefined);
const itemsWithoutResult = qcResults.filter(item => item.qcPassed === undefined);
if (itemsWithoutResult.length > 0) {
validationErrors.push(`${t("QC items without result")}: ${itemsWithoutResult.map(item => item.qcItem).join(', ')}`);
validationErrors.push(`${t("QC items without result")}`);
// validationErrors.push(`${t("QC items without result")}: ${itemsWithoutResult.map(item => item.code).join(', ')}`);
}

// Check if failed items have failed quantity
const failedItemsWithoutQty = qcItems.filter(item =>
item.isPassed === false && (!item.failQty || item.failQty <= 0)
const failedItemsWithoutQty = qcResults.filter(item =>
item.qcPassed === false && (!item.failQty || item.failQty <= 0)
);
if (failedItemsWithoutQty.length > 0) {
validationErrors.push(`${t("Failed items must have failed quantity")}: ${failedItemsWithoutQty.map(item => item.qcItem).join(', ')}`);
validationErrors.push(`${t("Failed items must have failed quantity")}`);
// validationErrors.push(`${t("Failed items must have failed quantity")}: ${failedItemsWithoutQty.map(item => item.code).join(', ')}`);
}

// Check if QC accept decision is made
@@ -184,10 +197,16 @@ const [qcItems, setQcItems] = useState(dummyQCData)

// Check if dates are input
if (data.productionDate === undefined || data.productionDate == null) {
validationErrors.push("Production Date cannot be null!");
validationErrors.push("請輸入生產日期!");
}
if (data.expiryDate === undefined || data.expiryDate == null) {
validationErrors.push("Expiry Date cannot be null!");
validationErrors.push("請輸入到期日!");
}
if (!qcResults.every((qc) => qc.qcPassed) && qcAccept) {
validationErrors.push("有不合格檢查項目,無法收貨!");
// submitDialogWithWarning(() => postStockInLineWithQc(qcData), t, {title:"有不合格檢查項目,確認接受收貨?",
// confirmButtonText: t("confirm putaway"), html: ""});
// return;
}

if (validationErrors.length > 0) {
@@ -205,12 +224,12 @@ const [qcItems, setQcItems] = useState(dummyQCData)
qcAccept: qcAccept? qcAccept : false,
acceptQty: acceptQty? acceptQty : 0,
qcResult: qcItems.map(item => ({
qcResult: qcResults.map(item => ({
qcItemId: item.id,
// qcItem: item.qcItem,
// code: item.code,
// qcDescription: item.qcDescription,
isPassed: item.isPassed? item.isPassed : false,
failQty: (item.failQty && !item.isPassed) ? item.failQty : 0,
qcPassed: item.qcPassed? item.qcPassed : false,
failQty: (item.failQty && !item.qcPassed) ? item.failQty : 0,
// failedQty: (typeof item.failedQty === "number" && !item.isPassed) ? item.failedQty : 0,
remarks: item.remarks || ''
}))
@@ -218,42 +237,21 @@ const [qcItems, setQcItems] = useState(dummyQCData)
// const qcData = data;

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

await postStockInLine(qcData);
if (!qcData.qcResult.every((qc) => qc.isPassed) && qcData.qcAccept) {
submitDialogWithWarning(() => postStockInLineWithQc(qcData), t, {title:"有不合格檢查項目,確認接受收貨?",
confirmButtonText: t("confirm putaway"), html: ""});
return;
if (qcData.qcAccept) {
// submitDialogWithWarning(onOpenPutaway, t, {title:"Save success, confirm to proceed?",
// confirmButtonText: t("confirm putaway"), html: ""});
onOpenPutaway();
} else {
closeHandler({}, "backdropClick");
}
await postStockInLineWithQc(qcData);
// return;
return ;
},
[onOpenPutaway, qcItems],
);
const postStockInLineWithQc = useCallback(async (qcData: PurchaseQCInput) => {
const args = {
...qcData
// id: itemDetail.id,
// purchaseOrderId: itemDetail.purchaseOrderId,
// purchaseOrderLineId: itemDetail.purchaseOrderLineId,
// itemId: itemDetail.itemId,
// ...data,
// productionDate: productionDate,
// expiryDate: expiryDate,
// receiptDate: receiptDate,
} as ModalFormInput;

await postStockInLine(args);

if (qcData.qcAccept) {
// submitDialogWithWarning(onOpenPutaway, t, {title:"Save success, confirm to proceed?",
// confirmButtonText: t("confirm putaway"), html: ""});
onOpenPutaway();
} else {
closeHandler({}, "backdropClick");
}
return ;
},[onOpenPutaway,closeHandler]);

const postStockInLine = useCallback(async (args: ModalFormInput) => {
const submitData = {
@@ -329,8 +327,8 @@ const [qcItems, setQcItems] = useState(dummyQCData)
const acceptQty = formProps.watch("acceptedQty")

const checkQcIsPassed = useCallback((qcItems: QcData[]) => {
const isPassed = qcItems.every((qc) => qc.isPassed);
const checkQcIsPassed = useCallback((qcItems: PurchaseQcResult[]) => {
const isPassed = qcItems.every((qc) => qc.qcPassed);
console.log(isPassed)
if (isPassed) {
formProps.setValue("passingQty", acceptQty)
@@ -343,7 +341,7 @@ const [qcItems, setQcItems] = useState(dummyQCData)
useEffect(() => {
// maybe check if submitted before
console.log(qcItems)
checkQcIsPassed(qcItems)
// checkQcIsPassed(qcItems)
}, [qcItems, checkQcIsPassed])

return (
@@ -368,7 +366,7 @@ const [qcItems, setQcItems] = useState(dummyQCData)
<PutawayForm
itemDetail={itemDetail}
warehouse={warehouse!}
disabled={false}
disabled={viewOnly}
/>
<Stack direction="row" justifyContent="flex-end" gap={1}>
<Button
@@ -406,7 +404,7 @@ const [qcItems, setQcItems] = useState(dummyQCData)
</Typography>
</Grid>
<Grid item xs={12}>
<StockInFormVer2 itemDetail={itemDetail} disabled={false} />
<StockInFormVer2 itemDetail={itemDetail} disabled={viewOnly} />
</Grid>
</Grid>
{/* <Stack direction="row" justifyContent="flex-end" gap={1}>
@@ -428,13 +426,13 @@ const [qcItems, setQcItems] = useState(dummyQCData)
<QcFormVer2
qc={qc!}
itemDetail={itemDetail}
disabled={false}
disabled={viewOnly}
qcItems={qcItems}
setQcItems={setQcItems}
/>
</Grid>
<Stack direction="row" justifyContent="flex-end" gap={1}>
{itemDetail.status.toLowerCase() != "completed" && (<Button
{!viewOnly && (<Button
id="qcSubmit"
type="button"
variant="contained"


+ 7
- 7
src/components/PoDetail/StockInFormVer2.tsx Parādīt failu

@@ -123,7 +123,7 @@ const StockInFormVer2: React.FC<Props> = ({
{...register("dnNo", {
// required: "productLotNo required!",
})}
disabled={itemDetail.status.toLowerCase() == "completed"}
disabled={disabled}
// error={Boolean(errors.productLotNo)}
// helperText={errors.productLotNo?.message}
/>
@@ -205,7 +205,7 @@ const StockInFormVer2: React.FC<Props> = ({
{...register("productLotNo", {
// required: "productLotNo required!",
})}
disabled={itemDetail.status.toLowerCase() == "completed"}
disabled={disabled}
error={Boolean(errors.productLotNo)}
helperText={errors.productLotNo?.message}
/>
@@ -226,7 +226,7 @@ const StockInFormVer2: React.FC<Props> = ({
sx={{ width: "100%" }}
label={t("productionDate")}
value={productionDate ? dayjs(productionDate) : undefined}
disabled={itemDetail.status.toLowerCase() == "completed"}
disabled={disabled}
onChange={(date) => {
if (!date) return;
setValue(
@@ -275,7 +275,7 @@ const StockInFormVer2: React.FC<Props> = ({
sx={{ width: "100%" }}
label={t("expiryDate")}
value={expiryDate ? dayjs(expiryDate) : undefined}
disabled={itemDetail.status.toLowerCase() == "completed"}
disabled={disabled}
onChange={(date) => {
console.log(date);
if (!date) return;
@@ -311,10 +311,10 @@ const StockInFormVer2: React.FC<Props> = ({
<TextField
label={t("uom")}
fullWidth
{...register("uom", {
{...register("uom.code", {
required: "uom required!",
})}
value={uom.code}
// value={uom?.code}
disabled={true}
/>
</Grid>
@@ -322,7 +322,7 @@ const StockInFormVer2: React.FC<Props> = ({
<TextField
label={t("acceptedQty")}
fullWidth
disabled={itemDetail.status.toLowerCase() == "completed"}
disabled={disabled}
{...register("acceptedQty", {
required: "acceptedQty required!",
})}


+ 30
- 24
src/components/PoDetail/dummyQcTemplate.tsx Parādīt failu

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

export interface QcData {
id: number,
qcItem: string,
qcDescription: string,
isPassed: boolean | undefined
failQty: number | undefined
remarks: string | undefined
}
// export interface QcData {
// qcItemId: number,
// qcItem: string,
// qcDescription: string,
// isPassed: boolean | undefined
// failQty: number | undefined
// remarks: string | undefined
// }

export const dummyQCData: QcData[] = [
{
id: 1,
qcItem: "包裝",
id: 4,
code: "包裝",
qcDescription: "有破爛、污糟、脹袋、積水、與實物不符等任何一種情況,則不合格",
isPassed: undefined,
name: "有破爛、污糟、脹袋、積水、與實物不符等任何一種情況,則不合格",
qcPassed: undefined,
failQty: undefined,
remarks: undefined,
},
{
id: 2,
qcItem: "肉質",
id: 5,
code: "肉質",
qcDescription: "肉質鬆散,則不合格",
isPassed: undefined,
name: "肉質鬆散,則不合格",
qcPassed: undefined,
failQty: undefined,
remarks: undefined,
},
{
id: 3,
qcItem: "顔色",
qcDescription: "不是食材應有的顔色、顔色不均匀、出現其他顔色、腌料/醬顔色不均匀,油脂部分變綠色、黃色,",
isPassed: undefined,
id: 6,
code: "顔色",
qcDescription: "不是食材應有的顔色、顔色不均匀、出現其他顔色、腌料/醬顔色不均匀,油脂部分變綠色、黃色,則不合格",
name: "不是食材應有的顔色、顔色不均匀、出現其他顔色、腌料/醬顔色不均匀,油脂部分變綠色、黃色,則不合格",
qcPassed: undefined,
failQty: undefined,
remarks: undefined,
},
{
id: 4,
qcItem: "狀態",
id: 7,
code: "狀態",
qcDescription: "有結晶、結霜、解凍跡象、發霉、散發異味等任何一種情況,則不合格",
isPassed: undefined,
name: "有結晶、結霜、解凍跡象、發霉、散發異味等任何一種情況,則不合格",
qcPassed: undefined,
failQty: undefined,
remarks: undefined,
},
{
id: 5,
qcItem: "異物",
id: 8,
code: "異物",
qcDescription: "有不屬於本食材的雜質,則不合格",
isPassed: undefined,
name: "有不屬於本食材的雜質,則不合格",
qcPassed: undefined,
failQty: undefined,
remarks: undefined,
},


+ 1
- 1
src/i18n/zh/common.json Parādīt failu

@@ -40,7 +40,7 @@
"non-consumables": "非消耗品",
"fg": "成品",
"sfg": "半成品",
"item": "品",
"item": "品",
"FG":"成品",
"FG & Material Demand Forecast Detail":"成品及材料需求預測詳情",
"View item In-out And inventory Ledger":"查看物料出入庫及庫存日誌",


+ 45
- 38
src/i18n/zh/dashboard.json Parādīt failu

@@ -1,39 +1,46 @@
{
"Dashboard": "資訊展示面板",
"Order status": "訂單狀態",
"pending": "未收貨",
"receiving": "收貨中",
"total": "未完成總數",
"Warehouse temperature record": "倉庫溫度記錄",
"Warehouse type": "倉庫類型",
"Last 6 hours": "過去6小時",
"Last 24 hours": "過去24小時",
"Cold storage": "冷藏倉",
"Normal temperature storage": "常溫倉",
"Temperature status": "溫度狀態",
"Humidity status": "濕度狀態",
"Warehouse status": "倉庫狀態",
"Progress chart": "進度圖表",
"Purchase Order Code": "採購單號",
"Item Name": "貨品名稱",
"Escalation Level": "上報等級",
"Reason": "原因",
"Order completion": "訂單完成度",
"Raw material": "原料",
"Consumable": "消耗品",
"Shipment": "出貨",
"Extracted order": "已提取提料單",
"Pending order": "待提取提料單",
"Temperature": "溫度",
"Humidity": "濕度",
"Pending storage": "待入倉物料",
"Total storage": "已入倉物料",
"Application completion": "提料申請完成度",
"Processed application": "已處理提料申請",
"Pending application": "待處理提料申請",
"pending inspection material": "待品檢物料",
"inspected material": "已品檢物料",
"total material": "物料總數",

"stock in escalation list": "收貨已上報列表"
}
"Dashboard": "資訊展示面板",
"Order status": "訂單狀態",
"pending": "未收貨",
"receiving": "收貨中",
"total": "未完成總數",
"Warehouse temperature record": "倉庫溫度記錄",
"Warehouse type": "倉庫類型",
"Last 6 hours": "過去6小時",
"Last 24 hours": "過去24小時",
"Cold storage": "冷藏倉",
"Normal temperature storage": "常溫倉",
"Temperature status": "溫度狀態",
"Humidity status": "濕度狀態",
"Warehouse status": "倉庫狀態",
"Progress chart": "進度圖表",
"Purchase Order Code": "採購單號",
"Item Name": "貨品名稱",
"Escalation Level": "上報等級",
"Reason": "原因",
"Order completion": "訂單完成度",
"Raw material": "原料",
"Consumable": "消耗品",
"Shipment": "出貨",
"Extracted order": "已提取提料單",
"Pending order": "待提取提料單",
"Temperature": "溫度",
"Humidity": "濕度",
"Pending storage": "待入倉物料",
"Total storage": "已入倉物料",
"Application completion": "提料申請完成度",
"Processed application": "已處理提料申請",
"Pending application": "待處理提料申請",
"pending inspection material": "待品檢物料",
"inspected material": "已品檢物料",
"total material": "物料總數",
"stock in escalation list": "收貨已上報列表",
"Responsible for handling colleagues": "負責處理同事",
"Completed QC Total": "品檢完成數量",
"QC Fail Count": "品檢不合格數量",
"DN Date": "送貨日期",
"Received Qty": "收貨數量",
"Escalation List": "上報列表",
"Purchase UoM": "計量單位",
"QC Completed Count": "品檢完成數量"
}

+ 2
- 2
src/i18n/zh/inventory.json Parādīt failu

@@ -15,6 +15,6 @@
"Base UoM": "基本單位",
"Lot No": "批號",
"Expiry Date": "到期日",
"No items are selected yet.": "未選擇項目",
"Item selected": "已選擇項目"
"No items are selected yet.": "未選擇貨品",
"Item selected": "已選擇貨品"
}

+ 134
- 146
src/i18n/zh/purchaseOrder.json Parādīt failu

@@ -1,147 +1,135 @@
{
"Purchase Order": "採購訂單",
"Purchase Receipt": "處理採購來貨",
"Code": "編號",
"OrderDate": "下單日期",
"Order Date": "下單日期",
"Order Date To": "下單日期至",
"ETA": "預計到貨日期",
"ETA To": "預計到貨日期至",
"Details": "詳情",
"Supplier": "供應商",
"Status": "來貨狀態",
"Escalated": "上報狀態",
"NotEscalated": "無上報",

"Do you want to start?": "確定開始嗎?",
"Start": "開始",
"Start Success": "開始成功",
"Start Fail": "開始失敗",
"Start PO": "開始採購訂單",
"Do you want to complete?": "確定完成嗎?",
"Cancel": "取消",
"Complete": "完成",
"Complete Success": "完成成功",
"Complete Fail": "完成失敗",
"Complete PO": "完成採購訂單",
"General": "一般",
"Bind Storage": "綁定倉位",
"Po No.": "採購訂單編號",
"PO No.": "採購訂單編號",
"itemNo": "貨品編號",
"itemName": "貨品名稱",
"Item Detail": "貨品詳情",
"Item Code": "貨品編號",
"Item Name": "貨品名稱",
"Item Qty": "貨品數量",
"Item Accepted Qty": "貨品已收貨數量",
"Item Purchase UoM": "貨品計量單位",
"qty": "訂單數量",
"uom": "計量單位",
"Stock UoM": "庫存單位",
"Stock In Qty": "收貨數量",
"total weight": "總重量",
"weight unit": "重量單位",
"price": "訂單貨值",
"processed": "已上架數量",
"expiryDate": "到期日",
"acceptedQty": "是次來貨數量",
"putawayQty": "上架數量",
"acceptQty": "揀收數量",
"printQty": "列印數量",
"qcResult": "品檢結果",
"weight": "重量",
"start": "開始",
"qc": "質量控制",
"escalation": "上報",
"stock in": "入庫",
"putaway": "上架",
"delete": "刪除",
"Accept quantity must be greater than 0": "揀收數量不能少於1",
"QC items without result": "請完成品檢結果",
"Failed items must have failed quantity": "請輸入不合格數量",
"qty cannot be greater than remaining qty": "數量不能大於剩餘數量",
"acceptQty must not greater than": "揀收數量不能大於",
"Record pol": "記錄採購訂單",
"Add some entries!": "添加條目!",
"draft": "草稿",
"pending": "待處理",
"determine1": "上報1",
"determine2": "上報2",
"determine3": "上報3",
"receiving": "收貨中",
"received": "已檢收",
"completed": "已上架",
"rejected": "已拒絕及上報",
"status": "狀態",

"acceptedQty must not greater than": "接受數量不得大於",
"minimal value is 1": "最小值為1",
"value must be a number": "值必須是數字",
"qc Check": "質量控制檢查",
"Please select QC": "請選擇質量控制",
"failQty": "失敗數量",
"select qc": "選擇質量控制",
"enter a failQty": "請輸入失敗數量",
"qty too big": "數量過大",
"sampleRate": "抽樣率",
"sampleWeight": "樣本重量",
"totalWeight": "總重量",

"Escalation": "上報",
"to be processed": "待處理",
"supervisor": "管理層",

"Stock In Detail": "入庫詳情",
"productLotNo": "貨品批號",
"receiptDate": "收貨日期",
"acceptedWeight": "接受重量",
"productionDate": "生產日期",

"reportQty": "上報數量",

"Default Warehouse": "預設倉庫",
"Select warehouse": "選擇倉庫",
"Putaway Detail": "上架詳情",
"LotNo": "批號",
"Po Code": "採購訂單編號",
"No Warehouse": "沒有倉庫",
"Please scan warehouse qr code.": "請掃描倉庫 QR 碼。",
"receivedQty": "已來貨數量",
"dnQty": "送貨單數量",

"Accept submit": "接受來貨",
"qc processing": "處理來貨及品檢",
"putaway processing": "處理來貨及上架",
"view stockin": "查看收貨及品檢",
"putaway processing": "處理來貨及上架",
"putawayBtn": "上架",
"dnNo": "送貨單編號",
"dnDate": "送貨單日期",

"submitStockIn": "更新來貨資料",
"QC Info": "品檢資料",
"Escalation History": "上報記錄",
"Escalation Info": "上報資料",
"Escalation Result": "上報結果",
"update qc info": "更新品檢資料",
"email supplier": "電郵供應商",
"confirm putaway": "確定及上架",
"confirm qc result": "確定品檢結果",
"warehouse": "倉庫",
"qcItem": "品檢項目",
"passed": "接受",
"failed": "不接受",
"failedQty": "不合格數",
"remarks": "備註",

"Reject": "拒絕",
"submit": "提交",
"print": "列印",
"bind": "綁定"


}
"Purchase Order": "採購訂單",
"Purchase Receipt": "處理採購來貨",
"Code": "編號",
"OrderDate": "下單日期",
"Order Date": "下單日期",
"Order Date To": "下單日期至",
"ETA": "預計到貨日期",
"ETA To": "預計到貨日期至",
"Details": "詳情",
"Supplier": "供應商",
"Status": "來貨狀態",
"Escalated": "上報狀態",
"NotEscalated": "無上報",
"Do you want to start?": "確定開始嗎?",
"Start": "開始",
"Start Success": "開始成功",
"Start Fail": "開始失敗",
"Start PO": "開始採購訂單",
"Do you want to complete?": "確定完成嗎?",
"Cancel": "取消",
"Complete": "完成",
"Complete Success": "完成成功",
"Complete Fail": "完成失敗",
"Complete PO": "完成採購訂單",
"General": "一般",
"Bind Storage": "綁定倉位",
"Po No.": "採購訂單編號",
"PO No.": "採購訂單編號",
"itemNo": "貨品編號",
"itemName": "貨品名稱",
"Item Detail": "貨品詳情",
"Item Code": "貨品編號",
"Item Name": "貨品名稱",
"Item Qty": "貨品數量",
"Item": "貨品",
"Item Accepted Qty": "貨品已收貨數量",
"Item Purchase UoM": "貨品計量單位",
"qty": "訂單數量",
"uom": "計量單位",
"Stock UoM": "庫存單位",
"Stock In Qty": "收貨數量",
"total weight": "總重量",
"weight unit": "重量單位",
"price": "訂單貨值",
"processed": "已上架數量",
"expiryDate": "到期日",
"acceptedQty": "是次來貨數量",
"putawayQty": "上架數量",
"acceptQty": "揀收數量",
"printQty": "列印數量",
"qcResult": "品檢結果",
"weight": "重量",
"start": "開始",
"qc": "質量控制",
"escalation": "上報",
"stock in": "入庫",
"putaway": "上架",
"delete": "刪除",
"Accept quantity must be greater than 0": "揀收數量不能少於1",
"QC items without result": "請完成品檢結果",
"Failed items must have failed quantity": "請輸入不合格數量",
"qty cannot be greater than remaining qty": "數量不能大於剩餘數量",
"acceptQty must not greater than": "揀收數量不能大於",
"Record pol": "記錄採購訂單",
"Add some entries!": "添加條目!",
"draft": "草稿",
"pending": "待處理",
"determine1": "上報1",
"determine2": "上報2",
"determine3": "上報3",
"receiving": "收貨中",
"received": "已檢收",
"completed": "已上架",
"rejected": "已拒絕及上報",
"status": "狀態",
"acceptedQty must not greater than": "接受數量不得大於",
"minimal value is 1": "最小值為1",
"value must be a number": "值必須是數字",
"qc Check": "質量控制檢查",
"Please select QC": "請選擇質量控制",
"failQty": "失敗數量",
"select qc": "選擇質量控制",
"enter a failQty": "請輸入失敗數量",
"qty too big": "數量過大",
"sampleRate": "抽樣率",
"sampleWeight": "樣本重量",
"totalWeight": "總重量",
"Escalation": "上報",
"to be processed": "待處理",
"supervisor": "管理層",
"Stock In Detail": "入庫詳情",
"productLotNo": "貨品批號",
"receiptDate": "收貨日期",
"acceptedWeight": "接受重量",
"productionDate": "生產日期",
"reportQty": "上報數量",
"Default Warehouse": "預設倉庫",
"Select warehouse": "選擇倉庫",
"Putaway Detail": "上架詳情",
"LotNo": "批號",
"Po Code": "採購訂單編號",
"No Warehouse": "沒有倉庫",
"Please scan warehouse qr code.": "請掃描倉庫 QR 碼。",
"receivedQty": "已來貨數量",
"dnQty": "送貨單數量",
"Accept submit": "接受來貨",
"qc processing": "處理來貨及品檢",
"putaway processing": "處理來貨及上架",
"view stockin": "查看收貨及品檢",
"putawayBtn": "上架",
"dnNo": "送貨單編號",
"dnDate": "送貨單日期",
"submitStockIn": "更新來貨資料",
"QC Info": "品檢資料",
"Escalation History": "上報記錄",
"Escalation Info": "上報資料",
"Escalation Result": "上報結果",
"update qc info": "更新品檢資料",
"email supplier": "電郵供應商",
"confirm putaway": "確定及上架",
"confirm qc result": "確定品檢結果",
"warehouse": "倉庫",
"qcItem": "品檢項目",
"passed": "接受",
"failed": "不接受",
"failedQty": "不合格數",
"remarks": "備註",
"Reject": "拒絕",
"submit": "提交",
"print": "列印",
"bind": "綁定",
"Search": "搜尋",
"Found": "已找到"
}

+ 8
- 1
src/theme/devias-material-kit/components.ts Parādīt failu

@@ -314,7 +314,7 @@ const components: ThemeOptions["components"] = {
styleOverrides: {
root: {
borderBottomColor: palette.divider,
padding: "1px 6px",
padding: "10px 6px",
fontSize: defaultFontSize - 2,
// padding: "15px 16px",
// lineHeight: 1.5,
@@ -342,6 +342,13 @@ const components: ThemeOptions["components"] = {
},
},
},
// MuiTableFooter: {
// styleOverrides: {
// root: {
// padding: "1px 6px",
// },
// },
// },
MuiTextField: {
defaultProps: {
variant: "filled",


Notiek ielāde…
Atcelt
Saglabāt