Selaa lähdekoodia

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

完成工單/完成 API 改為僅 ADMIN;「工單生產流程」tab 僅 ADMIN 可見;「已提料」改為全部 POL completed;文案「未提料」→「未完成提料」/Pick incomplete。
fix負數倉
CANCERYS\kw093 1 kuukausi sitten
vanhempi
commit
6d0531a412
8 muutettua tiedostoa jossa 65 lisäystä ja 50 poistoa
  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 Näytä tiedosto

@@ -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: {
/** Job order / process date(YYYY-MM-DD) */
date?: string | null;


+ 2
- 2
src/authorities.ts Näytä tiedosto

@@ -20,7 +20,7 @@ export const AUTH = {
JOB_MAT: "JOB_MAT",
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",
@@ -28,7 +28,7 @@ export const AUTH = {
} 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).
*/
export function hasAbility(


+ 3
- 4
src/components/ProductionProcess/JobOrderOpsTable.tsx Näytä tiedosto

@@ -223,7 +223,7 @@ interface JobOrderOpsTableProps {
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> = ({
onSelectProcess,
printerCombo = [],
@@ -232,10 +232,9 @@ const JobOrderOpsTable: React.FC<JobOrderOpsTableProps> = ({
const { data: session } = useSession() as { data: SessionWithTokens | null };
const sessionToken = session as SessionWithTokens | null;
const abilities = session?.abilities ?? session?.user?.abilities ?? [];
/** 取消工單:僅 ADMIN */
/** 取消工單 / 完成工單:僅 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(
() => (printerCombo || []).filter((p) => p.type === "Label"),


+ 1
- 1
src/components/ProductionProcess/ProductionProcessJobOrderDetail.tsx Näytä tiedosto

@@ -53,7 +53,7 @@ interface ProductProcessJobOrderDetailProps {
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> = ({
jobOrderId,
onBack,


+ 10
- 13
src/components/ProductionProcess/ProductionProcessList.tsx Näytä tiedosto

@@ -178,7 +178,7 @@ function isWaitingQcPutAway(
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> = ({
onSelectProcess,
printerCombo,
@@ -209,8 +209,8 @@ const ProductProcessList: React.FC<ProductProcessListProps> = ({
const [modalInfo, setModalInfo] = useState<StockInLineInput>();
const currentUserId = session?.id ? parseInt(session.id) : undefined;
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";

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

{statusLower !== "completed" && (
{canManageUpdateJo && statusLower !== "completed" && (
<Button
variant="contained"
size="small"
disabled={!canManageUpdateJo}
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")}


+ 38
- 19
src/components/ProductionProcess/ProductionProcessPage.tsx Näytä tiedosto

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

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

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. 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 { t } = useTranslation(["common", "productionProcess"]);
@@ -36,19 +45,27 @@ const ProductionProcessPage: React.FC<ProductionProcessPageProps> = ({ printerCo
productProcessId: number;
pickOrderId: number;
} | 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(() => ({
...createDefaultProductionProcessListPersistedState(),
}));
const { data: session } = useSession() as { data: 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 pathname = usePathname();
const router = useRouter();
const [linkQcOpen, setLinkQcOpen] = useState(false);
const [linkQcSilId, setLinkQcSilId] = useState<number | null>(null);

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

useEffect(() => {
if (typeof window !== 'undefined') {
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");
@@ -100,7 +117,7 @@ const ProductionProcessPage: React.FC<ProductionProcessPageProps> = ({ printerCo
if (!Number.isFinite(id) || id <= 0) return;
setSelectedProcessId(null);
setSelectedMatchingStock(null);
setTabIndex(0);
setPageTab("list");
setProductionListState((prev) => ({
...prev,
pickBucket: "pending_qc",
@@ -144,16 +161,18 @@ const ProductionProcessPage: React.FC<ProductionProcessPageProps> = ({ printerCo
return (
<>
<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>

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

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

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


+ 5
- 5
src/i18n/en/productionProcess.json Näytä tiedosto

@@ -79,11 +79,11 @@
"Issue": "Issue",
"Job Order Ops Table": "Job Order Ops Table",
"Pending (picked)": "Picked",
"Pending (not picked)": "Not picked",
"Pending (not picked)": "Pick incomplete",
"Processing (picked)": "Picked",
"Processing (not picked)": "Not picked",
"Processing (not picked)": "Pick incomplete",
"Picked": "Picked",
"Not picked": "Not picked",
"Not picked": "Pick incomplete",
"Stop (paused)": "Stop (paused)",
"Cancelled": "Cancelled",
"Reload data": "Reload data",
@@ -224,10 +224,10 @@
"Off-plan unfinished": "Off-plan unfinished",
"All unfinished": "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 · 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",
"Processing": "Processing",
"Total lines: ": "Total lines: ",


+ 5
- 5
src/i18n/zh/productionProcess.json Näytä tiedosto

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


Ladataan…
Peruuta
Tallenna