Ver a proveniência

List 卡片的「完成工單」現在只有 ADMIN 才會顯示,非 ADMIN 完全看不到按鈕。

完成工單/完成 API 改為僅 ADMIN;「工單生產流程」tab 僅 ADMIN 可見;「已提料」改為全部 POL completed;文案「未提料」→「未完成提料」/Pick incomplete。
fix負數倉
CANCERYS\kw093 há 1 mês
ascendente
cometimento
6d0531a412
8 ficheiros alterados com 65 adições e 50 eliminações
  1. +1
    -1
      src/app/api/jo/actions.ts
  2. +2
    -2
      src/authorities.ts
  3. +3
    -4
      src/components/ProductionProcess/JobOrderOpsTable.tsx
  4. +1
    -1
      src/components/ProductionProcess/ProductionProcessJobOrderDetail.tsx
  5. +10
    -13
      src/components/ProductionProcess/ProductionProcessList.tsx
  6. +38
    -19
      src/components/ProductionProcess/ProductionProcessPage.tsx
  7. +5
    -5
      src/i18n/en/productionProcess.json
  8. +5
    -5
      src/i18n/zh/productionProcess.json

+ 1
- 1
src/app/api/jo/actions.ts Ver ficheiro

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


/** FP-MTMS Version Checklist | Functions Ref. No. 40 | v1.0.5 | 2026-08-06 */
/** FP-MTMS Version Checklist | Functions Ref. No. 40 | v1.0.7 | 2026-08-06 */
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
- 2
src/authorities.ts Ver ficheiro

@@ -20,7 +20,7 @@ export const AUTH = {
JOB_MAT: "JOB_MAT", JOB_MAT: "JOB_MAT",
JOB_PROD: "JOB_PROD", JOB_PROD: "JOB_PROD",
/** /**
* FP-MTMS Version Checklist | Functions Ref. No. 51 | v1.0.0 | 2026-08-05
* FP-MTMS Version Checklist | Functions Ref. No. 51 | v1.0.1 | 2026-08-06
* 工單 生產流程 完成工單 * 工單 生產流程 完成工單
*/ */
PRODUCT_PROCESS: "PRODUCT_PROCESS", PRODUCT_PROCESS: "PRODUCT_PROCESS",
@@ -28,7 +28,7 @@ export const AUTH = {
} as const; } as const;


/** /**
* FP-MTMS Version Checklist | Functions Ref. No. 51 | v1.0.0 | 2026-08-05
* FP-MTMS Version Checklist | Functions Ref. No. 51 | v1.0.1 | 2026-08-06
* Match session ability codes (exact, trimmed). * Match session ability codes (exact, trimmed).
*/ */
export function hasAbility( export function hasAbility(


+ 3
- 4
src/components/ProductionProcess/JobOrderOpsTable.tsx Ver ficheiro

@@ -223,7 +223,7 @@ interface JobOrderOpsTableProps {
printerCombo?: PrinterCombo[]; printerCombo?: PrinterCombo[];
} }


/** FP-MTMS Version Checklist | Functions Ref. No. 47 | v1.0.4 | 2026-08-06 */
/** FP-MTMS Version Checklist | Functions Ref. No. 47 | v1.0.5 | 2026-08-06 */
const JobOrderOpsTable: React.FC<JobOrderOpsTableProps> = ({ const JobOrderOpsTable: React.FC<JobOrderOpsTableProps> = ({
onSelectProcess, onSelectProcess,
printerCombo = [], printerCombo = [],
@@ -232,10 +232,9 @@ const JobOrderOpsTable: React.FC<JobOrderOpsTableProps> = ({
const { data: session } = useSession() as { data: SessionWithTokens | null }; const { data: session } = useSession() as { data: SessionWithTokens | null };
const sessionToken = session as SessionWithTokens | null; const sessionToken = session as SessionWithTokens | null;
const abilities = session?.abilities ?? session?.user?.abilities ?? []; const abilities = session?.abilities ?? session?.user?.abilities ?? [];
/** 取消工單:僅 ADMIN */
/** 取消工單 / 完成工單:僅 ADMIN */
const canCancel = hasAbility(abilities, AUTH.ADMIN); const canCancel = hasAbility(abilities, AUTH.ADMIN);
/** 完成工單:僅 PRODUCT_PROCESS(工單 生產流程 完成工單) */
const canComplete = hasAbility(abilities, AUTH.PRODUCT_PROCESS);
const canComplete = hasAbility(abilities, AUTH.ADMIN);


const labelPrinterCombo = useMemo( const labelPrinterCombo = useMemo(
() => (printerCombo || []).filter((p) => p.type === "Label"), () => (printerCombo || []).filter((p) => p.type === "Label"),


+ 1
- 1
src/components/ProductionProcess/ProductionProcessJobOrderDetail.tsx Ver ficheiro

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


/** FP-MTMS Version Checklist | Functions Ref. No. 40 | v1.0.5 | 2026-08-06 */
/** FP-MTMS Version Checklist | Functions Ref. No. 40 | v1.0.7 | 2026-08-06 */
const ProductionProcessJobOrderDetail: React.FC<ProductProcessJobOrderDetailProps> = ({ const ProductionProcessJobOrderDetail: React.FC<ProductProcessJobOrderDetailProps> = ({
jobOrderId, jobOrderId,
onBack, onBack,


+ 10
- 13
src/components/ProductionProcess/ProductionProcessList.tsx Ver ficheiro

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


/** FP-MTMS Version Checklist | Functions Ref. No. 40 | v1.0.5 | 2026-08-06 */
/** FP-MTMS Version Checklist | Functions Ref. No. 40 | v1.0.7 | 2026-08-06 */
const ProductProcessList: React.FC<ProductProcessListProps> = ({ const ProductProcessList: React.FC<ProductProcessListProps> = ({
onSelectProcess, onSelectProcess,
printerCombo, printerCombo,
@@ -209,8 +209,8 @@ const ProductProcessList: React.FC<ProductProcessListProps> = ({
const [modalInfo, setModalInfo] = useState<StockInLineInput>(); const [modalInfo, setModalInfo] = useState<StockInLineInput>();
const currentUserId = session?.id ? parseInt(session.id) : undefined; const currentUserId = session?.id ? parseInt(session.id) : undefined;
const abilities = session?.abilities ?? session?.user?.abilities ?? []; const abilities = session?.abilities ?? session?.user?.abilities ?? [];
/** 完成工單:僅 PRODUCT_PROCESS(工單 生產流程 完成工單);不含 ADMIN 以免群組 ADMIN 繞過勾選 */
const canManageUpdateJo = hasAbility(abilities, AUTH.PRODUCT_PROCESS);
/** 完成工單:僅 ADMIN */
const canManageUpdateJo = hasAbility(abilities, AUTH.ADMIN);
type ProcessFilter = "all" | "drink" | "Powder_Mixture" | "other"; type ProcessFilter = "all" | "drink" | "Powder_Mixture" | "other";


const listTab = normalizeListTab(listPersistedState.pickBucket); const listTab = normalizeListTab(listPersistedState.pickBucket);
@@ -1230,20 +1230,17 @@ const ProductProcessList: React.FC<ProductProcessListProps> = ({
{t("Matching Stock")} {t("Matching Stock")}
</Button> </Button>


{statusLower !== "completed" && (
{canManageUpdateJo && statusLower !== "completed" && (
<Button <Button
variant="contained" variant="contained"
size="small" size="small"
disabled={!canManageUpdateJo}
onClick={() => onClick={() =>
canManageUpdateJo
? openConfirm(
t("Confirm to update this Job Order?"),
async () => {
await handleUpdateJo(process);
},
)
: undefined
openConfirm(
t("Confirm to update this Job Order?"),
async () => {
await handleUpdateJo(process);
},
)
} }
> >
{t("Update Job Order")} {t("Update Job Order")}


+ 38
- 19
src/components/ProductionProcess/ProductionProcessPage.tsx Ver ficheiro

@@ -17,6 +17,7 @@ import DrinkProductionQtyDashboard from "@/components/ProductionProcess/DrinkPro
import JobOrderOpsTable from "@/components/ProductionProcess/JobOrderOpsTable"; import JobOrderOpsTable from "@/components/ProductionProcess/JobOrderOpsTable";
import type { PrinterCombo } from "@/app/api/settings/printer"; import type { PrinterCombo } from "@/app/api/settings/printer";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { AUTH, hasAbility } from "@/authorities";


interface ProductionProcessPageProps { interface ProductionProcessPageProps {
printerCombo: PrinterCombo[]; printerCombo: PrinterCombo[];
@@ -24,9 +25,17 @@ interface ProductionProcessPageProps {


const STORAGE_KEY = 'productionProcess_selectedMatchingStock'; const STORAGE_KEY = 'productionProcess_selectedMatchingStock';


type PageTab =
| "list"
| "ops"
| "jobProcessStatus"
| "operatorKpi"
| "equipmentStatus"
| "drinkQty";

/** /**
* FP-MTMS Version Checklist | Functions Ref. No. 26 | v1.0.0 | 2026-07-20 * FP-MTMS Version Checklist | Functions Ref. No. 26 | v1.0.0 | 2026-07-20
* FP-MTMS Version Checklist | Functions Ref. No. 47 | v1.0.4 | 2026-08-06
* FP-MTMS Version Checklist | Functions Ref. No. 47 | v1.0.5 | 2026-08-06
*/ */
const ProductionProcessPage: React.FC<ProductionProcessPageProps> = ({ printerCombo }) => { const ProductionProcessPage: React.FC<ProductionProcessPageProps> = ({ printerCombo }) => {
const { t } = useTranslation(["common", "productionProcess"]); const { t } = useTranslation(["common", "productionProcess"]);
@@ -36,19 +45,27 @@ const ProductionProcessPage: React.FC<ProductionProcessPageProps> = ({ printerCo
productProcessId: number; productProcessId: number;
pickOrderId: number; pickOrderId: number;
} | null>(null); } | null>(null);
/** 0 = Production Process list; 1 = JO ops table; 2..5 = dashboards */
const [tabIndex, setTabIndex] = useState(0);
const [pageTab, setPageTab] = useState<PageTab>("list");
const [productionListState, setProductionListState] = useState(() => ({ const [productionListState, setProductionListState] = useState(() => ({
...createDefaultProductionProcessListPersistedState(), ...createDefaultProductionProcessListPersistedState(),
})); }));
const { data: session } = useSession() as { data: SessionWithTokens | null }; const { data: session } = useSession() as { data: SessionWithTokens | null };
const sessionToken = session as SessionWithTokens | null; const sessionToken = session as SessionWithTokens | null;
const abilities = session?.abilities ?? session?.user?.abilities ?? [];
/** 「查看工單流程情況」僅 ADMIN */
const canSeeOpsTable = hasAbility(abilities, AUTH.ADMIN);
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const pathname = usePathname(); const pathname = usePathname();
const router = useRouter(); const router = useRouter();
const [linkQcOpen, setLinkQcOpen] = useState(false); const [linkQcOpen, setLinkQcOpen] = useState(false);
const [linkQcSilId, setLinkQcSilId] = useState<number | null>(null); const [linkQcSilId, setLinkQcSilId] = useState<number | null>(null);


useEffect(() => {
if (!canSeeOpsTable && pageTab === "ops") {
setPageTab("list");
}
}, [canSeeOpsTable, pageTab]);

useEffect(() => { useEffect(() => {
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
try { try {
@@ -83,8 +100,8 @@ const ProductionProcessPage: React.FC<ProductionProcessPageProps> = ({ printerCo
} }
}, []); }, []);


const handleTabChange = useCallback((event: React.SyntheticEvent, newValue: number) => {
setTabIndex(newValue);
const handleTabChange = useCallback((_: React.SyntheticEvent, newValue: PageTab) => {
setPageTab(newValue);
}, []); }, []);


const openStockInLineIdQ = searchParams.get("openStockInLineId"); const openStockInLineIdQ = searchParams.get("openStockInLineId");
@@ -100,7 +117,7 @@ const ProductionProcessPage: React.FC<ProductionProcessPageProps> = ({ printerCo
if (!Number.isFinite(id) || id <= 0) return; if (!Number.isFinite(id) || id <= 0) return;
setSelectedProcessId(null); setSelectedProcessId(null);
setSelectedMatchingStock(null); setSelectedMatchingStock(null);
setTabIndex(0);
setPageTab("list");
setProductionListState((prev) => ({ setProductionListState((prev) => ({
...prev, ...prev,
pickBucket: "pending_qc", pickBucket: "pending_qc",
@@ -144,16 +161,18 @@ const ProductionProcessPage: React.FC<ProductionProcessPageProps> = ({ printerCo
return ( return (
<> <>
<Box> <Box>
<Tabs value={tabIndex} onChange={handleTabChange} sx={{ mb: 2 }}>
<Tab label={t("Production Process")} />
<Tab label={t("Job Order Ops Table")} />
<Tab label={t("Job Process Status Dashboard")} />
<Tab label={t("Operator KPI Dashboard")} />
<Tab label={t("Production Equipment Status Dashboard")} />
<Tab label={t("Drink Production Qty Dashboard")} />
<Tabs value={pageTab} onChange={handleTabChange} sx={{ mb: 2 }}>
<Tab value="list" label={t("Production Process")} />
{canSeeOpsTable && (
<Tab value="ops" label={t("Job Order Ops Table")} />
)}
<Tab value="jobProcessStatus" label={t("Job Process Status Dashboard")} />
<Tab value="operatorKpi" label={t("Operator KPI Dashboard")} />
<Tab value="equipmentStatus" label={t("Production Equipment Status Dashboard")} />
<Tab value="drinkQty" label={t("Drink Production Qty Dashboard")} />
</Tabs> </Tabs>


{tabIndex === 0 && (
{pageTab === "list" && (
<ProductionProcessList <ProductionProcessList
printerCombo={printerCombo} printerCombo={printerCombo}
listPersistedState={productionListState} listPersistedState={productionListState}
@@ -174,7 +193,7 @@ const ProductionProcessPage: React.FC<ProductionProcessPageProps> = ({ printerCo
/> />
)} )}


{tabIndex === 1 && (
{pageTab === "ops" && canSeeOpsTable && (
<JobOrderOpsTable <JobOrderOpsTable
printerCombo={printerCombo} printerCombo={printerCombo}
onSelectProcess={(jobOrderId) => { onSelectProcess={(jobOrderId) => {
@@ -183,16 +202,16 @@ const ProductionProcessPage: React.FC<ProductionProcessPageProps> = ({ printerCo
/> />
)} )}


{tabIndex === 2 && (
{pageTab === "jobProcessStatus" && (
<JobProcessStatus /> <JobProcessStatus />
)} )}
{tabIndex === 3 && (
{pageTab === "operatorKpi" && (
<OperatorKpiDashboard /> <OperatorKpiDashboard />
)} )}
{tabIndex === 4 && (
{pageTab === "equipmentStatus" && (
<EquipmentStatusDashboard /> <EquipmentStatusDashboard />
)} )}
{tabIndex === 5 && (
{pageTab === "drinkQty" && (
<DrinkProductionQtyDashboard /> <DrinkProductionQtyDashboard />
)} )}
</Box> </Box>


+ 5
- 5
src/i18n/en/productionProcess.json Ver ficheiro

@@ -79,11 +79,11 @@
"Issue": "Issue", "Issue": "Issue",
"Job Order Ops Table": "Job Order Ops Table", "Job Order Ops Table": "Job Order Ops Table",
"Pending (picked)": "Picked", "Pending (picked)": "Picked",
"Pending (not picked)": "Not picked",
"Pending (not picked)": "Pick incomplete",
"Processing (picked)": "Picked", "Processing (picked)": "Picked",
"Processing (not picked)": "Not picked",
"Processing (not picked)": "Pick incomplete",
"Picked": "Picked", "Picked": "Picked",
"Not picked": "Not picked",
"Not picked": "Pick incomplete",
"Stop (paused)": "Stop (paused)", "Stop (paused)": "Stop (paused)",
"Cancelled": "Cancelled", "Cancelled": "Cancelled",
"Reload data": "Reload data", "Reload data": "Reload data",
@@ -224,10 +224,10 @@
"Off-plan unfinished": "Off-plan unfinished", "Off-plan unfinished": "Off-plan unfinished",
"All unfinished": "Needs action", "All unfinished": "Needs action",
"Needs action": "Needs action", "Needs action": "Needs action",
"Not picked · Not started": "Not picked · Not started",
"Not picked · Not started": "Pick incomplete · Not started",
"Picked · Not started": "Picked · Not started", "Picked · Not started": "Picked · Not started",
"Picked · In production": "Picked · In production", "Picked · In production": "Picked · In production",
"Not picked · In production": "Not picked · In production",
"Not picked · In production": "Pick incomplete · In production",
"Powder_Mixture": "Powder Mixture", "Powder_Mixture": "Powder Mixture",
"Processing": "Processing", "Processing": "Processing",
"Total lines: ": "Total lines: ", "Total lines: ": "Total lines: ",


+ 5
- 5
src/i18n/zh/productionProcess.json Ver ficheiro

@@ -79,11 +79,11 @@
"Issue": "異常", "Issue": "異常",
"Job Order Ops Table": "工單生產流程", "Job Order Ops Table": "工單生產流程",
"Pending (picked)": "已提料", "Pending (picked)": "已提料",
"Pending (not picked)": "未提料",
"Pending (not picked)": "未完成提料",
"Processing (picked)": "已提料", "Processing (picked)": "已提料",
"Processing (not picked)": "未提料",
"Processing (not picked)": "未完成提料",
"Picked": "已提料", "Picked": "已提料",
"Not picked": "未提料",
"Not picked": "未完成提料",
"Stop (paused)": "暫停中", "Stop (paused)": "暫停中",
"Cancelled": "已取消", "Cancelled": "已取消",
"Reload data": "重新載入", "Reload data": "重新載入",
@@ -224,10 +224,10 @@
"Off-plan unfinished": "未按規劃完成工單", "Off-plan unfinished": "未按規劃完成工單",
"All unfinished": "需處理", "All unfinished": "需處理",
"Needs action": "需處理", "Needs action": "需處理",
"Not picked · Not started": "未提料 · 未開工",
"Not picked · Not started": "未完成提料 · 未開工",
"Picked · Not started": "已提料 · 未開工", "Picked · Not started": "已提料 · 未開工",
"Picked · In production": "已提料 · 未完成生產", "Picked · In production": "已提料 · 未完成生產",
"Not picked · In production": "未提料 · 未完成生產",
"Not picked · In production": "未完成提料 · 未完成生產",
"Powder_Mixture": "箱料粉", "Powder_Mixture": "箱料粉",
"Processing": "生產中", "Processing": "生產中",
"Total lines: ": "總數量:", "Total lines: ": "總數量:",


Carregando…
Cancelar
Guardar