Bladeren bron

Merge branch 'production' into stockTakeUpdate

stockTakeUpdate
CANCERYS\kw093 6 dagen geleden
bovenliggende
commit
be4c90ee89
18 gewijzigde bestanden met toevoegingen van 880 en 368 verwijderingen
  1. +1
    -1
      src/app/api/jo/actions.ts
  2. +2
    -0
      src/app/api/shop/actions.ts
  3. +7
    -7
      src/components/DashboardPage/truckSchedule/TruckScheduleDashboard.tsx
  4. +36
    -19
      src/components/ProductionProcess/ProductionOutputForm.tsx
  5. +35
    -24
      src/components/ProductionProcess/ProductionOutputFormPage.tsx
  6. +10
    -1
      src/components/ProductionProcess/ProductionProcessDetail.tsx
  7. +14
    -7
      src/components/ProductionProcess/ProductionProcessJobOrderDetail.tsx
  8. +1
    -1
      src/components/ProductionProcess/ProductionProcessList.tsx
  9. +68
    -37
      src/components/ProductionProcess/ProductionProcessStepExecution.tsx
  10. +678
    -259
      src/components/Shop/RouteBoard.tsx
  11. +2
    -0
      src/i18n/en/jo.json
  12. +2
    -0
      src/i18n/en/productionProcess.json
  13. +1
    -1
      src/i18n/en/routeboard.json
  14. +9
    -5
      src/i18n/en/shop.json
  15. +2
    -0
      src/i18n/zh/jo.json
  16. +2
    -0
      src/i18n/zh/productionProcess.json
  17. +1
    -1
      src/i18n/zh/routeboard.json
  18. +9
    -5
      src/i18n/zh/shop.json

+ 1
- 1
src/app/api/jo/actions.ts Bestand weergeven

@@ -906,7 +906,7 @@ export const fetchAllJoborderProductProcessInfo = cache(async (type?: string | n
); );
}); });


/** FP-MTMS Version Checklist | Functions Ref. No. 40 | v1.0.7 | 2026-08-06 */
/** FP-MTMS Version Checklist | Functions Ref. No. 40 | v1.0.11 | 2026-10-29 */
export const fetchJoborderProductProcessesPage = cache(async (params: { export const fetchJoborderProductProcessesPage = cache(async (params: {
/** Job order / process date(YYYY-MM-DD) */ /** Job order / process date(YYYY-MM-DD) */
date?: string | null; date?: string | null;


+ 2
- 0
src/app/api/shop/actions.ts Bestand weergeven

@@ -965,6 +965,8 @@ export interface TruckLaneRenameLogResponse {
oldDepartureTime?: string | null; oldDepartureTime?: string | null;
newDepartureTime?: string | null; newDepartureTime?: string | null;
ticketCount: number; ticketCount: number;
/** `truck.id` of shops in the renamed lane, comma-separated. */
truckIds?: string | null;
} }


export const previewTruckLaneRenameAction = async ( export const previewTruckLaneRenameAction = async (


+ 7
- 7
src/components/DashboardPage/truckSchedule/TruckScheduleDashboard.tsx Bestand weergeven

@@ -203,13 +203,13 @@ const TruckScheduleDashboard: React.FC = () => {
return '-'; return '-';
}; };


// Compare a timestamp against the selected date (today/tomorrow/...) and return a short label.
// Used to avoid confusion when picking crosses midnight.
// Compare a timestamp against calendar today. Same-day picks show no chip;
// only midnight-crossing (yesterday / tomorrow / N days) gets a label.
// Does not filter rows — trucklane tickets for the selected delivery date still show.
const relativeDayLabel = useCallback( const relativeDayLabel = useCallback(
(dateTimeData: string | number[] | null, dateOption: string): string | null => {
(dateTimeData: string | number[] | null): string | null => {
if (!dateTimeData) return null; if (!dateTimeData) return null;
const dateOffset = getDateOffset(dateOption);
const baseDate = dayjs().startOf('day').add(dateOffset, 'day');
const baseDate = dayjs().startOf('day');


let dt: dayjs.Dayjs; let dt: dayjs.Dayjs;
if (Array.isArray(dateTimeData)) { if (Array.isArray(dateTimeData)) {
@@ -518,8 +518,8 @@ const TruckScheduleDashboard: React.FC = () => {
filteredData.map((row, index) => { filteredData.map((row, index) => {
const timeRemaining = calculateTimeRemaining(row, selectedDate); const timeRemaining = calculateTimeRemaining(row, selectedDate);
const chipColor = getTimeChipColor(row, selectedDate); const chipColor = getTimeChipColor(row, selectedDate);
const startRel = relativeDayLabel(row.firstTicketStartTime, selectedDate);
const endRel = relativeDayLabel(row.lastTicketEndTime, selectedDate);
const startRel = relativeDayLabel(row.firstTicketStartTime);
const endRel = relativeDayLabel(row.lastTicketEndTime);
return ( return (
<TableRow <TableRow


+ 36
- 19
src/components/ProductionProcess/ProductionOutputForm.tsx Bestand weergeven

@@ -17,25 +17,42 @@ import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { UpdateProductProcessLineQtyRequest } from "@/app/api/jo/actions"; import { UpdateProductProcessLineQtyRequest } from "@/app/api/jo/actions";


type QtyInput = number | "";

export type ProductionOutputFormData = Omit<
UpdateProductProcessLineQtyRequest,
| "outputFromProcessQty"
| "byproductQty"
| "defectQty"
| "defect2Qty"
| "defect3Qty"
| "scrapQty"
> & {
outputFromProcessQty: QtyInput;
byproductQty: QtyInput;
defectQty: QtyInput;
defect2Qty: QtyInput;
defect3Qty: QtyInput;
scrapQty: QtyInput;
};

const parseQtyInput = (raw: string): QtyInput => {
if (raw === "") return "";
const n = parseInt(raw, 10);
return Number.isNaN(n) ? "" : n;
};

export const toSubmitQty = (v: QtyInput | null | undefined): number =>
v === "" || v == null || Number.isNaN(Number(v)) ? 0 : Number(v);

interface ProductionOutputFormProps { interface ProductionOutputFormProps {
outputData: (UpdateProductProcessLineQtyRequest & {
byproductName: string;
byproductQty: number;
byproductUom: string;
});
setOutputData: React.Dispatch<
React.SetStateAction<
UpdateProductProcessLineQtyRequest & {
byproductName: string;
byproductQty: number;
byproductUom: string;
}
>
>;
outputData: ProductionOutputFormData;
setOutputData: React.Dispatch<React.SetStateAction<ProductionOutputFormData>>;
onSubmit: () => Promise<void> | void; onSubmit: () => Promise<void> | void;
onCancel: () => void; onCancel: () => void;
} }


/** FP-MTMS Version Checklist | Functions Ref. No. 59 | v1.0.1 | 2026-10-29 */
const ProductionOutputForm: React.FC<ProductionOutputFormProps> = ({ const ProductionOutputForm: React.FC<ProductionOutputFormProps> = ({
outputData, outputData,
setOutputData, setOutputData,
@@ -68,7 +85,7 @@ const ProductionOutputForm: React.FC<ProductionOutputFormProps> = ({
onChange={(e) => onChange={(e) =>
setOutputData({ setOutputData({
...outputData, ...outputData,
outputFromProcessQty: parseInt(e.target.value) || 0,
outputFromProcessQty: parseQtyInput(e.target.value),
}) })
} }
/> />
@@ -103,7 +120,7 @@ const ProductionOutputForm: React.FC<ProductionOutputFormProps> = ({
onChange={(e) => onChange={(e) =>
setOutputData({ setOutputData({
...outputData, ...outputData,
byproductQty: parseInt(e.target.value) || 0,
byproductQty: parseQtyInput(e.target.value),
}) })
} }
/> />
@@ -138,7 +155,7 @@ const ProductionOutputForm: React.FC<ProductionOutputFormProps> = ({
onChange={(e) => onChange={(e) =>
setOutputData({ setOutputData({
...outputData, ...outputData,
defectQty: parseInt(e.target.value) || 0,
defectQty: parseQtyInput(e.target.value),
}) })
} }
/> />
@@ -173,7 +190,7 @@ const ProductionOutputForm: React.FC<ProductionOutputFormProps> = ({
onChange={(e) => onChange={(e) =>
setOutputData({ setOutputData({
...outputData, ...outputData,
scrapQty: parseInt(e.target.value) || 0,
scrapQty: parseQtyInput(e.target.value),
}) })
} }
/> />
@@ -207,4 +224,4 @@ const ProductionOutputForm: React.FC<ProductionOutputFormProps> = ({
); );
}; };


export default ProductionOutputForm;
export default ProductionOutputForm;

+ 35
- 24
src/components/ProductionProcess/ProductionOutputFormPage.tsx Bestand weergeven

@@ -5,47 +5,47 @@ import {
Button, Button,
Typography, Typography,
} from "@mui/material"; } from "@mui/material";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { import {
fetchProductProcessLineDetail, fetchProductProcessLineDetail,
updateProductProcessLineQty, updateProductProcessLineQty,
UpdateProductProcessLineQtyRequest,
JobOrderProcessLineDetailResponse, JobOrderProcessLineDetailResponse,
} from "@/app/api/jo/actions"; } from "@/app/api/jo/actions";
import ProductionOutputForm from "./ProductionOutputForm";
import ProductionOutputForm, {
ProductionOutputFormData,
toSubmitQty,
} from "./ProductionOutputForm";


interface ProductionOutputFormPageProps { interface ProductionOutputFormPageProps {
lineId: number | null; lineId: number | null;
onBack: () => void; onBack: () => void;
} }


/** FP-MTMS Version Checklist | Functions Ref. No. 59 | v1.0.1 | 2026-10-29 */
const ProductionOutputFormPage: React.FC<ProductionOutputFormPageProps> = ({ const ProductionOutputFormPage: React.FC<ProductionOutputFormPageProps> = ({
lineId, lineId,
onBack, onBack,
}) => { }) => {
const { t } = useTranslation();
const { t } = useTranslation(["common", "jo", "productionProcess"]);
const [lineDetail, setLineDetail] = useState<JobOrderProcessLineDetailResponse | null>(null); const [lineDetail, setLineDetail] = useState<JobOrderProcessLineDetailResponse | null>(null);
const [outputData, setOutputData] = useState<UpdateProductProcessLineQtyRequest & {
byproductName: string;
byproductQty: number;
byproductUom: string;
}>({
const [outputData, setOutputData] = useState<ProductionOutputFormData>({
productProcessLineId: lineId ?? 0, productProcessLineId: lineId ?? 0,
outputFromProcessQty: 0,
outputFromProcessQty: "",
outputFromProcessUom: "", outputFromProcessUom: "",
defectQty: 0,
defectQty: "",
defectUom: "", defectUom: "",
defect2Qty: 0,
defect2Qty: "",
defect2Uom: "", defect2Uom: "",
defect3Qty: 0,
defect3Qty: "",
defect3Uom: "", defect3Uom: "",
defectDescription: "", defectDescription: "",
defectDescription2: "", defectDescription2: "",
defectDescription3: "", defectDescription3: "",
scrapQty: 0,
scrapQty: "",
scrapUom: "", scrapUom: "",
byproductName: "", byproductName: "",
byproductQty: 0,
byproductQty: "",
byproductUom: "", byproductUom: "",
}); });


@@ -60,14 +60,14 @@ const ProductionOutputFormPage: React.FC<ProductionOutputFormPageProps> = ({
setOutputData((prev) => ({ setOutputData((prev) => ({
...prev, ...prev,
productProcessLineId: detail.id, productProcessLineId: detail.id,
outputFromProcessQty: (detail as any).outputFromProcessQty || 0,
outputFromProcessQty: (detail as any).outputFromProcessQty || "",
outputFromProcessUom: (detail as any).outputFromProcessUom || "", outputFromProcessUom: (detail as any).outputFromProcessUom || "",
defectQty: detail.defectQty || 0,
defectQty: detail.defectQty || "",
defectUom: detail.defectUom || "", defectUom: detail.defectUom || "",
scrapQty: detail.scrapQty || 0,
scrapQty: detail.scrapQty || "",
scrapUom: detail.scrapUom || "", scrapUom: detail.scrapUom || "",
byproductName: detail.byproductName || "", byproductName: detail.byproductName || "",
byproductQty: detail.byproductQty || 0,
byproductQty: detail.byproductQty || "",
byproductUom: detail.byproductUom || "", byproductUom: detail.byproductUom || "",
})); }));
}) })
@@ -84,6 +84,12 @@ const ProductionOutputFormPage: React.FC<ProductionOutputFormPageProps> = ({
await updateProductProcessLineQty({ await updateProductProcessLineQty({
...outputData, ...outputData,
productProcessLineId: lineDetail.id || 0, productProcessLineId: lineDetail.id || 0,
outputFromProcessQty: toSubmitQty(outputData.outputFromProcessQty),
byproductQty: toSubmitQty(outputData.byproductQty),
defectQty: toSubmitQty(outputData.defectQty),
defect2Qty: toSubmitQty(outputData.defect2Qty),
defect3Qty: toSubmitQty(outputData.defect3Qty),
scrapQty: toSubmitQty(outputData.scrapQty),
}); });


console.log("Output data submitted successfully"); console.log("Output data submitted successfully");
@@ -94,14 +100,14 @@ const ProductionOutputFormPage: React.FC<ProductionOutputFormPageProps> = ({
setOutputData((prev) => ({ setOutputData((prev) => ({
...prev, ...prev,
productProcessLineId: detail.id, productProcessLineId: detail.id,
outputFromProcessQty: (detail as any).outputFromProcessQty || 0,
outputFromProcessQty: (detail as any).outputFromProcessQty || "",
outputFromProcessUom: (detail as any).outputFromProcessUom || "", outputFromProcessUom: (detail as any).outputFromProcessUom || "",
defectQty: detail.defectQty || 0,
defectQty: detail.defectQty || "",
defectUom: detail.defectUom || "", defectUom: detail.defectUom || "",
scrapQty: detail.scrapQty || 0,
scrapQty: detail.scrapQty || "",
scrapUom: detail.scrapUom || "", scrapUom: detail.scrapUom || "",
byproductName: detail.byproductName || "", byproductName: detail.byproductName || "",
byproductQty: detail.byproductQty || 0,
byproductQty: detail.byproductQty || "",
byproductUom: detail.byproductUom || "", byproductUom: detail.byproductUom || "",
})); }));


@@ -116,8 +122,13 @@ const ProductionOutputFormPage: React.FC<ProductionOutputFormPageProps> = ({
return ( return (
<Box> <Box>
<Box sx={{ mb: 2 }}> <Box sx={{ mb: 2 }}>
<Button variant="outlined" onClick={onBack}>
{t("Back to List")}
<Button
variant="outlined"
size="small"
onClick={onBack}
startIcon={<ArrowBackIcon />}
>
{t("Back to Process Steps")}
</Button> </Button>
</Box> </Box>




+ 10
- 1
src/components/ProductionProcess/ProductionProcessDetail.tsx Bestand weergeven

@@ -64,13 +64,16 @@ interface ProductProcessDetailProps {
jobOrderId: number; jobOrderId: number;
onBack: () => void; onBack: () => void;
fromJosave?: boolean; fromJosave?: boolean;
/** 進入步驟執行頁時通知父層(用來隱藏「返回列表」) */
onStepViewChange?: (inStepView: boolean) => void;
} }


/** FP-MTMS Version Checklist | Functions Ref. No. 50 | v1.0.1 | 2026-08-10 */
/** FP-MTMS Version Checklist | Functions Ref. No. 50 | v1.0.2 | 2026-10-29 */
const ProductionProcessDetail: React.FC<ProductProcessDetailProps> = ({ const ProductionProcessDetail: React.FC<ProductProcessDetailProps> = ({
jobOrderId, jobOrderId,
onBack, onBack,
fromJosave, fromJosave,
onStepViewChange,
}) => { }) => {
// console.log(" ProductionProcessDetail RENDER", { jobOrderId, fromJosave }); // console.log(" ProductionProcessDetail RENDER", { jobOrderId, fromJosave });
@@ -143,6 +146,12 @@ const fetchProcessDetailRef = useRef<() => Promise<void>>();
useEffect(() => { useEffect(() => {
onBackRef.current = onBack; onBackRef.current = onBack;
}, [onBack]); }, [onBack]);

useEffect(() => {
const inStepView = isExecutingLine && selectedLineId != null;
onStepViewChange?.(inStepView);
return () => onStepViewChange?.(false);
}, [isExecutingLine, selectedLineId, onStepViewChange]);
// 获取 process 和 lines 数据 // 获取 process 和 lines 数据
const fetchProcessDetail = useCallback(async () => { const fetchProcessDetail = useCallback(async () => {
// console.log(" fetchProcessDetail CALLED", { jobOrderId, timestamp: new Date().toISOString() }); // console.log(" fetchProcessDetail CALLED", { jobOrderId, timestamp: new Date().toISOString() });


+ 14
- 7
src/components/ProductionProcess/ProductionProcessJobOrderDetail.tsx Bestand weergeven

@@ -54,7 +54,7 @@ interface ProductProcessJobOrderDetailProps {
initialTabIndex?: number; initialTabIndex?: number;
} }


/** FP-MTMS Version Checklist | Functions Ref. No. 40 | v1.0.7 | 2026-08-06 */
/** FP-MTMS Version Checklist | Functions Ref. No. 40 | v1.0.11 | 2026-10-29 */
const ProductionProcessJobOrderDetail: React.FC<ProductProcessJobOrderDetailProps> = ({ const ProductionProcessJobOrderDetail: React.FC<ProductProcessJobOrderDetailProps> = ({
jobOrderId, jobOrderId,
onBack, onBack,
@@ -68,6 +68,7 @@ const ProductionProcessJobOrderDetail: React.FC<ProductProcessJobOrderDetailProp
const [inventoryData, setInventoryData] = useState<InventoryResult[]>([]); const [inventoryData, setInventoryData] = useState<InventoryResult[]>([]);
const [tabIndex, setTabIndex] = useState(initialTabIndex); const [tabIndex, setTabIndex] = useState(initialTabIndex);
const [selectedProcessId, setSelectedProcessId] = useState<number | null>(null); const [selectedProcessId, setSelectedProcessId] = useState<number | null>(null);
const [inStepView, setInStepView] = useState(false);
const [operationPriority, setOperationPriority] = useState<number>(50); const [operationPriority, setOperationPriority] = useState<number>(50);
const [openOperationPriorityDialog, setOpenOperationPriorityDialog] = useState(false); const [openOperationPriorityDialog, setOpenOperationPriorityDialog] = useState(false);
const [openPlanStartDialog, setOpenPlanStartDialog] = useState(false); const [openPlanStartDialog, setOpenPlanStartDialog] = useState(false);
@@ -320,6 +321,9 @@ const handleRelease = useCallback(async (jobOrderId: number) => {
const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>(
(_e, newValue) => { (_e, newValue) => {
setTabIndex(newValue); setTabIndex(newValue);
if (newValue !== 2) {
setInStepView(false);
}
}, },
[], [],
); );
@@ -796,12 +800,14 @@ const handleRelease = useCallback(async (jobOrderId: number) => {


return ( return (
<Box> <Box>
{/* 返回按钮 */}
<Box sx={{ mb: 2 }}>
<Button variant="outlined" onClick={onBack} startIcon={<ArrowBackIcon />}>
{t("Back to List")}
</Button>
</Box>
{/* 步驟執行頁不顯示「返回列表」,避免誤離開工單 */}
{!inStepView && (
<Box sx={{ mb: 2 }}>
<Button variant="outlined" onClick={onBack} startIcon={<ArrowBackIcon />}>
{t("Back to List")}
</Button>
</Box>
)}
{/* 标签页 */} {/* 标签页 */}
<Box sx={{ borderBottom: '1px solid #e0e0e0' }}> <Box sx={{ borderBottom: '1px solid #e0e0e0' }}>
@@ -833,6 +839,7 @@ const handleRelease = useCallback(async (jobOrderId: number) => {


}} }}
fromJosave={Boolean(fromJosave && !isPutAwayed)} fromJosave={Boolean(fromJosave && !isPutAwayed)}
onStepViewChange={setInStepView}
/> />
)} )}
{tabIndex === 3 && <ProductionProcessesLineRemarkTableContent />} {tabIndex === 3 && <ProductionProcessesLineRemarkTableContent />}


+ 1
- 1
src/components/ProductionProcess/ProductionProcessList.tsx Bestand weergeven

@@ -179,7 +179,7 @@ function isWaitingQcPutAway(
return s !== "completed" && s !== "rejected"; return s !== "completed" && s !== "rejected";
} }


/** FP-MTMS Version Checklist | Functions Ref. No. 40 | v1.0.10 | 2026-10-15 */
/** FP-MTMS Version Checklist | Functions Ref. No. 40 | v1.0.11 | 2026-10-29 */
const ProductProcessList: React.FC<ProductProcessListProps> = ({ const ProductProcessList: React.FC<ProductProcessListProps> = ({
onSelectProcess, onSelectProcess,
printerCombo, printerCombo,


+ 68
- 37
src/components/ProductionProcess/ProductionProcessStepExecution.tsx Bestand weergeven

@@ -28,6 +28,7 @@ import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import StopIcon from "@mui/icons-material/Stop"; import StopIcon from "@mui/icons-material/Stop";
import PauseIcon from "@mui/icons-material/Pause"; import PauseIcon from "@mui/icons-material/Pause";
import PlayArrowIcon from "@mui/icons-material/PlayArrow"; import PlayArrowIcon from "@mui/icons-material/PlayArrow";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { import {
JobOrderProcessLineDetailResponse, JobOrderProcessLineDetailResponse,
@@ -47,6 +48,35 @@ import { fetchNameList, NameList } from "@/app/api/user/actions";
import BagConsumptionForm from "./BagConsumptionForm"; // ✅ 添加导入 import BagConsumptionForm from "./BagConsumptionForm"; // ✅ 添加导入
import OverallTimeRemainingCard from "./OverallTimeRemainingCard"; // ✅ 添加导入 import OverallTimeRemainingCard from "./OverallTimeRemainingCard"; // ✅ 添加导入
import dayjs from "dayjs"; import dayjs from "dayjs";

type QtyInput = number | "";

type OutputFormState = Omit<
UpdateProductProcessLineQtyRequest,
| "outputFromProcessQty"
| "byproductQty"
| "defectQty"
| "defect2Qty"
| "defect3Qty"
| "scrapQty"
> & {
outputFromProcessQty: QtyInput;
byproductQty: QtyInput;
defectQty: QtyInput;
defect2Qty: QtyInput;
defect3Qty: QtyInput;
scrapQty: QtyInput;
};

const parseQtyInput = (raw: string): QtyInput => {
if (raw === "") return "";
const n = parseInt(raw, 10);
return Number.isNaN(n) ? "" : n;
};

const toSubmitQty = (v: QtyInput | null | undefined): number =>
v === "" || v == null || Number.isNaN(Number(v)) ? 0 : Number(v);

interface ProductionProcessStepExecutionProps { interface ProductionProcessStepExecutionProps {
lineId: number | null; lineId: number | null;
onBack: () => void; onBack: () => void;
@@ -55,7 +85,7 @@ interface ProductionProcessStepExecutionProps {
jobOrderId?: number; // ✅ 添加 jobOrderId?: number; // ✅ 添加
} }


/** FP-MTMS Version Checklist | Functions Ref. No. 59 | v1.0.0 | 2026-08-10 */
/** FP-MTMS Version Checklist | Functions Ref. No. 59 | v1.0.1 | 2026-10-29 */
const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionProps> = ({ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionProps> = ({
lineId, lineId,
onBack, onBack,
@@ -75,24 +105,20 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro
lineStatusNorm === "autopass"; lineStatusNorm === "autopass";
const isPassStatus = lineStatusNorm === "pass"; const isPassStatus = lineStatusNorm === "pass";
const isAutoPassStatus = lineStatusNorm === "autopass"; const isAutoPassStatus = lineStatusNorm === "autopass";
const [outputData, setOutputData] = useState<UpdateProductProcessLineQtyRequest & {
byproductName: string;
byproductQty: number;
byproductUom: string;
}>({
const [outputData, setOutputData] = useState<OutputFormState>({
productProcessLineId: lineId ?? 0, productProcessLineId: lineId ?? 0,
outputFromProcessQty: 0,
outputFromProcessQty: "",
outputFromProcessUom: "", outputFromProcessUom: "",
defectQty: 0,
defectQty: "",
defectUom: "", defectUom: "",
scrapQty: 0,
scrapQty: "",
scrapUom: "", scrapUom: "",
byproductName: "", byproductName: "",
byproductQty: 0,
byproductQty: "",
byproductUom: "", byproductUom: "",
defect2Qty: 0,
defect2Qty: "",
defect2Uom: "", defect2Uom: "",
defect3Qty: 0,
defect3Qty: "",
defect3Uom: "", defect3Uom: "",
defectDescription: "", defectDescription: "",
defectDescription2: "", defectDescription2: "",
@@ -153,14 +179,14 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro
setOutputData(prev => ({ setOutputData(prev => ({
...prev, ...prev,
productProcessLineId: detail.id, productProcessLineId: detail.id,
outputFromProcessQty: (detail as any).outputFromProcessQty || 0,
outputFromProcessQty: (detail as any).outputFromProcessQty || "",
outputFromProcessUom: (detail as any).outputFromProcessUom || "", outputFromProcessUom: (detail as any).outputFromProcessUom || "",
defectQty: detail.defectQty || 0,
defectQty: detail.defectQty || "",
defectUom: detail.defectUom || "", defectUom: detail.defectUom || "",
scrapQty: detail.scrapQty || 0,
scrapQty: detail.scrapQty || "",
scrapUom: detail.scrapUom || "", scrapUom: detail.scrapUom || "",
byproductName: detail.byproductName || "", byproductName: detail.byproductName || "",
byproductQty: detail.byproductQty || 0,
byproductQty: detail.byproductQty || "",
byproductUom: detail.byproductUom || "" byproductUom: detail.byproductUom || ""
})); }));
}) })
@@ -351,20 +377,20 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro
await updateProductProcessLineQty({ await updateProductProcessLineQty({
productProcessLineId: lineDetail?.id || 0 as number, productProcessLineId: lineDetail?.id || 0 as number,
byproductName: outputData.byproductName, byproductName: outputData.byproductName,
byproductQty: outputData.byproductQty,
byproductQty: toSubmitQty(outputData.byproductQty),
byproductUom: outputData.byproductUom, byproductUom: outputData.byproductUom,
outputFromProcessQty: outputData.outputFromProcessQty,
outputFromProcessQty: toSubmitQty(outputData.outputFromProcessQty),
outputFromProcessUom: outputData.outputFromProcessUom, outputFromProcessUom: outputData.outputFromProcessUom,
defectQty: outputData.defectQty,
defectQty: toSubmitQty(outputData.defectQty),
defectUom: outputData.defectUom, defectUom: outputData.defectUom,
defect2Qty: outputData.defect2Qty,
defect2Qty: toSubmitQty(outputData.defect2Qty),
defect2Uom: outputData.defect2Uom, defect2Uom: outputData.defect2Uom,
defect3Qty: outputData.defect3Qty,
defect3Qty: toSubmitQty(outputData.defect3Qty),
defect3Uom: outputData.defect3Uom, defect3Uom: outputData.defect3Uom,
defectDescription: outputData.defectDescription, defectDescription: outputData.defectDescription,
defectDescription2: outputData.defectDescription2, defectDescription2: outputData.defectDescription2,
defectDescription3: outputData.defectDescription3, defectDescription3: outputData.defectDescription3,
scrapQty: outputData.scrapQty,
scrapQty: toSubmitQty(outputData.scrapQty),
scrapUom: outputData.scrapUom, scrapUom: outputData.scrapUom,
}); });


@@ -383,21 +409,21 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro
setOutputData(prev => ({ setOutputData(prev => ({
...prev, ...prev,
productProcessLineId: detail.id, productProcessLineId: detail.id,
outputFromProcessQty: (detail as any).outputFromProcessQty || 0,
outputFromProcessQty: (detail as any).outputFromProcessQty || "",
outputFromProcessUom: (detail as any).outputFromProcessUom || "", outputFromProcessUom: (detail as any).outputFromProcessUom || "",
defectQty: detail.defectQty || 0,
defectQty: detail.defectQty || "",
defectUom: detail.defectUom || "", defectUom: detail.defectUom || "",
defectDescription: detail.defectDescription || "", defectDescription: detail.defectDescription || "",
defectDescription2: detail.defectDescription2 || "", defectDescription2: detail.defectDescription2 || "",
defectDescription3: detail.defectDescription3 || "", defectDescription3: detail.defectDescription3 || "",
defectQty2: detail.defectQty2 || 0,
defectUom2: detail.defectUom2 || "",
defectQty3: detail.defectQty3 || 0,
defectUom3: detail.defectUom3 || "",
scrapQty: detail.scrapQty || 0,
defect2Qty: (detail as any).defect2Qty || "",
defect2Uom: (detail as any).defect2Uom || "",
defect3Qty: (detail as any).defect3Qty || "",
defect3Uom: (detail as any).defect3Uom || "",
scrapQty: detail.scrapQty || "",
scrapUom: detail.scrapUom || "", scrapUom: detail.scrapUom || "",
byproductName: detail.byproductName || "", byproductName: detail.byproductName || "",
byproductQty: detail.byproductQty || 0,
byproductQty: detail.byproductQty || "",
byproductUom: detail.byproductUom || "" byproductUom: detail.byproductUom || ""
})); }));
}) })
@@ -561,8 +587,13 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro
return ( return (
<Box> <Box>
<Box sx={{ mb: 2 }}> <Box sx={{ mb: 2 }}>
<Button variant="outlined" onClick={onBack}>
{t("Back to List")}
<Button
variant="outlined"
size="small"
onClick={onBack}
startIcon={<ArrowBackIcon />}
>
{t("Back to Process Steps")}
</Button> </Button>
</Box> </Box>
{processData && ( {processData && (
@@ -812,7 +843,7 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro
value={outputData.outputFromProcessQty} value={outputData.outputFromProcessQty}
onChange={(e) => setOutputData({ onChange={(e) => setOutputData({
...outputData, ...outputData,
outputFromProcessQty: parseInt(e.target.value) || 0
outputFromProcessQty: parseQtyInput(e.target.value)
})} })}
/> />
</TableCell> </TableCell>
@@ -854,7 +885,7 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro
value={outputData.defectQty} value={outputData.defectQty}
onChange={(e) => setOutputData({ onChange={(e) => setOutputData({
...outputData, ...outputData,
defectQty: parseInt(e.target.value) || 0
defectQty: parseQtyInput(e.target.value)
})} })}
/> />
</TableCell> </TableCell>
@@ -902,7 +933,7 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro
value={outputData.defect2Qty} value={outputData.defect2Qty}
onChange={(e) => setOutputData({ onChange={(e) => setOutputData({
...outputData, ...outputData,
defect2Qty: parseInt(e.target.value) || 0
defect2Qty: parseQtyInput(e.target.value)
})} })}
/> />
</TableCell> </TableCell>
@@ -950,7 +981,7 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro
value={outputData.defect3Qty} value={outputData.defect3Qty}
onChange={(e) => setOutputData({ onChange={(e) => setOutputData({
...outputData, ...outputData,
defect3Qty: parseInt(e.target.value) || 0
defect3Qty: parseQtyInput(e.target.value)
})} })}
/> />
</TableCell> </TableCell>
@@ -998,7 +1029,7 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro
value={outputData.scrapQty} value={outputData.scrapQty}
onChange={(e) => setOutputData({ onChange={(e) => setOutputData({
...outputData, ...outputData,
scrapQty: parseInt(e.target.value) || 0
scrapQty: parseQtyInput(e.target.value)
})} })}
/> />
</TableCell> </TableCell>


+ 678
- 259
src/components/Shop/RouteBoard.tsx
Diff onderdrukt omdat het te groot bestand
Bestand weergeven


+ 2
- 0
src/i18n/en/jo.json Bestand weergeven

@@ -32,6 +32,8 @@
"BOM Type": "BOM Type", "BOM Type": "BOM Type",
"Back": "Back", "Back": "Back",
"Back to List": "Back to List", "Back to List": "Back to List",
"Back to Process": "Back to Process Steps",
"Back to Process Steps": "Back to Process Steps",
"Bad Item Qty": "Bad Item Qty", "Bad Item Qty": "Bad Item Qty",
"Bad Package Qty": "Bad Package Qty", "Bad Package Qty": "Bad Package Qty",
"Bag": "Bag", "Bag": "Bag",


+ 2
- 0
src/i18n/en/productionProcess.json Bestand weergeven

@@ -10,6 +10,8 @@
"Auto-refresh every 10 minutes": "Auto-refresh every 10 minutes", "Auto-refresh every 10 minutes": "Auto-refresh every 10 minutes",
"Back": "Back", "Back": "Back",
"Back to List": "Back to List", "Back to List": "Back to List",
"Back to Process": "Back to Process Steps",
"Back to Process Steps": "Back to Process Steps",
"Bag": "Bag", "Bag": "Bag",
"Bag Consumption": "Bag Consumption", "Bag Consumption": "Bag Consumption",
"Balance": "Balance", "Balance": "Balance",


+ 1
- 1
src/i18n/en/routeboard.json Bestand weergeven

@@ -106,7 +106,7 @@
"lane_noMatchFilter": "No lanes match (clear search or floor filter)", "lane_noMatchFilter": "No lanes match (clear search or floor filter)",
"floor_label": "Floor", "floor_label": "Floor",
"floor_all": "All", "floor_all": "All",
"filter_clear": "Clear",
"filter_clear": "Clear all",
"filter_apply": "OK", "filter_apply": "OK",
"btn_addLane": "Add lane", "btn_addLane": "Add lane",
"tools_title": "Tools", "tools_title": "Tools",


+ 9
- 5
src/i18n/en/shop.json Bestand weergeven

@@ -132,13 +132,12 @@
"confirm_restoreDiscardsEdits": "Scheduling a version restore will discard other unsaved board changes (drags, deletes, pending shops/lanes, logistics fields, etc.). Continue?", "confirm_restoreDiscardsEdits": "Scheduling a version restore will discard other unsaved board changes (drags, deletes, pending shops/lanes, logistics fields, etc.). Continue?",
"confirm_restoreSaveWillDropStaging": "Save will apply the snapshot restore first; other staged edits in this save will be skipped. Continue?", "confirm_restoreSaveWillDropStaging": "Save will apply the snapshot restore first; other staged edits in this save will be skipped. Continue?",
"confirm_schedule_removeShop": "Remove this shop from the lane? It will be scheduled for deletion when you submit.", "confirm_schedule_removeShop": "Remove this shop from the lane? It will be scheduled for deletion when you submit.",
"departureDialog_hint": "Name and departure apply to every master row on this lane. Press \"Save changes\" to write them. Completed delivery tickets keep the old name.",
"departureDialog_hint": "Name and departure apply to every master row on this lane. Press \"Save changes\" to write them. Existing delivery tickets keep the old name; later releases use the new name.",
"departureDialog_title": "Edit lane", "departureDialog_title": "Edit lane",
"departureDialog_nameLabel": "Lane name", "departureDialog_nameLabel": "Lane name",
"departureDialog_remarkReadonly": "Remark (not edited here)",
"laneEditTooltip": "Edit lane name and departure time (written when you press Save changes)", "laneEditTooltip": "Edit lane name and departure time (written when you press Save changes)",
"confirm_weekdayToken": "The weekday token in the lane name will change or become unreadable. 4F assignment matches Mon–Sun inside the name. Apply anyway?", "confirm_weekdayToken": "The weekday token in the lane name will change or become unreadable. 4F assignment matches Mon–Sun inside the name. Apply anyway?",
"confirm_renameLane": "Rename \"{{from}}\" to \"{{to}}\".\n{{unfinished}} unfinished delivery ticket(s) will follow (handlers: {{handlers}}).\n{{completed}} completed ticket(s) keep the old name.\n{{schedules}} open schedule line(s) will change from/to.\nNothing is written until you press Save changes.",
"confirm_renameLane": "Rename \"{{from}}\" to \"{{to}}\".\n{{unfinished}} unfinished delivery ticket(s) stay on the old name (handlers: {{handlers}}).\n{{completed}} completed ticket(s) stay on the old name.\nLater releases use the new name.\n{{schedules}} open schedule line(s) will change from/to.\nNothing is written until you press Save changes.",
"confirm_restoreKeepsTickets": "Restore puts lane master data, and open schedules that point at the current name, back to this snapshot. Unfinished delivery tickets stay on the current name.", "confirm_restoreKeepsTickets": "Restore puts lane master data, and open schedules that point at the current name, back to this snapshot. Unfinished delivery tickets stay on the current name.",
"rename_noHandlers": "none", "rename_noHandlers": "none",
"rename_none": "none", "rename_none": "none",
@@ -152,7 +151,7 @@
"renameErr_LANE_NOT_FOUND": "No live lane rows were found, so nothing was written", "renameErr_LANE_NOT_FOUND": "No live lane rows were found, so nothing was written",
"renameErr_LANE_TIME_INVALID": "Departure time is not a valid time", "renameErr_LANE_TIME_INVALID": "Departure time is not a valid time",
"renameLog_title": "Lane renames", "renameLog_title": "Lane renames",
"renameLog_line": "{{when}} {{who}}: {{from}} → {{to}} ({{floor}}, tickets {{tickets}})",
"renameLog_line": "{{when}} {{who}}: {{from}} → {{to}} ({{floor}})",
"departureEditAria": "Edit departure time", "departureEditAria": "Edit departure time",
"departureTooltipEditSave": "Edit departure time (saved with \"Save changes\")", "departureTooltipEditSave": "Edit departure time (saved with \"Save changes\")",
"departureTooltipNeedShops": "Add shops before setting departure time", "departureTooltipNeedShops": "Add shops before setting departure time",
@@ -231,9 +230,14 @@
"err_save": "Save failed", "err_save": "Save failed",
"exportRoutes": "Export routes", "exportRoutes": "Export routes",
"filter_apply": "OK", "filter_apply": "OK",
"filter_clear": "Clear",
"filter_clear": "Clear all",
"floor_all": "All", "floor_all": "All",
"floor_label": "Floor", "floor_label": "Floor",
"lane_filter": "Filter",
"laneFilter_special": "Lane code",
"laneFilter_zone": "Zone",
"laneFilter_weekday": "Weekday",
"laneFilter_area": "Area",
"id": "id", "id": "id",
"importRoutes": "Import routes", "importRoutes": "Import routes",
"import_staged_preview": "Import preview loaded: {{file}} ({{sheets}} sheet(s) / {{rows}} rows). Press \"Save changes\" to persist.", "import_staged_preview": "Import preview loaded: {{file}} ({{sheets}} sheet(s) / {{rows}} rows). Press \"Save changes\" to persist.",


+ 2
- 0
src/i18n/zh/jo.json Bestand weergeven

@@ -34,6 +34,8 @@
"BOM Type": "BOM 類型", "BOM Type": "BOM 類型",
"Back": "返回", "Back": "返回",
"Back to List": "返回列表", "Back to List": "返回列表",
"Back to Process": "返回列表",
"Back to Process Steps": "返回列表",
"Bad Item Qty": "不良物品數量", "Bad Item Qty": "不良物品數量",
"Bad Package Qty": "不良包裝數量", "Bad Package Qty": "不良包裝數量",
"Bag": "包裝袋", "Bag": "包裝袋",


+ 2
- 0
src/i18n/zh/productionProcess.json Bestand weergeven

@@ -10,6 +10,8 @@
"Auto-refresh every 10 minutes": "每10分鐘自動刷新", "Auto-refresh every 10 minutes": "每10分鐘自動刷新",
"Back": "返回", "Back": "返回",
"Back to List": "返回列表", "Back to List": "返回列表",
"Back to Process": "返回列表",
"Back to Process Steps": "返回列表",
"Bag": "包裝袋", "Bag": "包裝袋",
"Bag Consumption": "包裝袋消耗", "Bag Consumption": "包裝袋消耗",
"Balance": "可用數量", "Balance": "可用數量",


+ 1
- 1
src/i18n/zh/routeboard.json Bestand weergeven

@@ -106,7 +106,7 @@
"lane_noMatchFilter": "無符合條件的車線(清除搜索或樓層篩選)", "lane_noMatchFilter": "無符合條件的車線(清除搜索或樓層篩選)",
"floor_label": "樓層", "floor_label": "樓層",
"floor_all": "全部", "floor_all": "全部",
"filter_clear": "清除",
"filter_clear": "清除全部",
"filter_apply": "確定", "filter_apply": "確定",
"btn_addLane": "新增車線", "btn_addLane": "新增車線",
"tools_title": "操作工具", "tools_title": "操作工具",


+ 9
- 5
src/i18n/zh/shop.json Bestand weergeven

@@ -106,7 +106,12 @@
"lane_selectAll": "全選", "lane_selectAll": "全選",
"floor_label": "樓層", "floor_label": "樓層",
"floor_all": "全部", "floor_all": "全部",
"filter_clear": "清除",
"lane_filter": "篩選",
"laneFilter_special": "車線",
"laneFilter_zone": "區",
"laneFilter_weekday": "星期",
"laneFilter_area": "地區",
"filter_clear": "清除全部",
"filter_apply": "確定", "filter_apply": "確定",
"btn_addLane": "新增車線", "btn_addLane": "新增車線",
"tools_title": "操作工具", "tools_title": "操作工具",
@@ -202,11 +207,10 @@
"addShop_confirm": "確認", "addShop_confirm": "確認",
"departureDialog_title": "編輯車線", "departureDialog_title": "編輯車線",
"departureDialog_nameLabel": "車線名稱", "departureDialog_nameLabel": "車線名稱",
"departureDialog_remarkReadonly": "備註(這次不改)",
"departureDialog_hint": "名稱與出車時間會套用到此車線全部主檔。按上方「儲存更改」才寫入。已完成的出倉票不會改名。",
"departureDialog_hint": "名稱與出車時間會套用到此車線全部店鋪。\n 既有送貨訂單維持舊名稱;之後放單才用新名稱。\n 按上方「儲存更改」才寫入。",
"laneEditTooltip": "編輯車線名稱與出車時間(按「儲存更改」才寫入)", "laneEditTooltip": "編輯車線名稱與出車時間(按「儲存更改」才寫入)",
"confirm_weekdayToken": "車線名稱裡的星期代碼會改變,或改完後無法辨認。4F 派車是用名稱裡的 Mon–Sun 對日子。仍要套用?", "confirm_weekdayToken": "車線名稱裡的星期代碼會改變,或改完後無法辨認。4F 派車是用名稱裡的 Mon–Sun 對日子。仍要套用?",
"confirm_renameLane": "把「{{from}}」改成「{{to}}」。\n未完成出倉票 {{unfinished}} 張會改成新名稱(負責人:{{handlers}})。\n已完成 {{completed}} 張維持舊名稱。\n未完成排程 {{schedules}} 筆的起迄名稱會改。\n按「儲存更改」後才寫入。",
"confirm_renameLane": "把「{{from}}」改成「{{to}}」。\n未完成出倉票 {{unfinished}} 張維持舊名稱(負責人:{{handlers}})。\n已完成 {{completed}} 張維持舊名稱。\n之後放單才用新名稱。\n未完成排程 {{schedules}} 筆的起迄名稱會改。\n按「儲存更改」後才寫入。",
"confirm_restoreKeepsTickets": "還原會把車線主檔,以及指向目前名稱的未完成排程,改回此版本的名稱。未完成的出倉票會留在現在的名稱,不會跟回去。", "confirm_restoreKeepsTickets": "還原會把車線主檔,以及指向目前名稱的未完成排程,改回此版本的名稱。未完成的出倉票會留在現在的名稱,不會跟回去。",
"rename_noHandlers": "無", "rename_noHandlers": "無",
"rename_none": "無", "rename_none": "無",
@@ -220,7 +224,7 @@
"renameErr_LANE_NOT_FOUND": "找不到這條車線的主檔,沒有寫入", "renameErr_LANE_NOT_FOUND": "找不到這條車線的主檔,沒有寫入",
"renameErr_LANE_TIME_INVALID": "出車時間格式不正確", "renameErr_LANE_TIME_INVALID": "出車時間格式不正確",
"renameLog_title": "車線改名紀錄", "renameLog_title": "車線改名紀錄",
"renameLog_line": "{{when}} {{who}}:{{from}} → {{to}}({{floor}},票 {{tickets}})",
"renameLog_line": "{{when}} {{who}}:{{from}} → {{to}}({{floor}})",
"seqDialog_title": "編輯裝車順序", "seqDialog_title": "編輯裝車順序",
"logistics_colLaneCount": "{{count}} 條車線", "logistics_colLaneCount": "{{count}} 條車線",
"tooltip_openLaneBoard": "在車線看板開此車線", "tooltip_openLaneBoard": "在車線看板開此車線",


Laden…
Annuleren
Opslaan