Переглянути джерело

fix consumable suggestion, and scan handle and M18snc allow m18 acces or admin user to use

fix負數倉
CANCERYS\kw093 1 місяць тому
джерело
коміт
6d009d42ab
5 змінених файлів з 61 додано та 6 видалено
  1. +15
    -1
      src/app/(main)/m18Syn/layout.tsx
  2. +2
    -1
      src/app/(main)/m18Syn/page.tsx
  3. +2
    -0
      src/authorities.ts
  4. +2
    -1
      src/components/NavigationContent/NavigationContent.tsx
  5. +40
    -3
      src/components/PickOrderSearch/WorkbenchPickExecution.tsx

+ 15
- 1
src/app/(main)/m18Syn/layout.tsx Переглянути файл

@@ -1,10 +1,24 @@
import { I18nProvider } from "@/i18n";
import { authOptions } from "@/config/authConfig";
import { AUTH, hasAbility } from "@/authorities";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";

export default function M18SyncLayout({
/** FP-MTMS Version Checklist | Functions Ref. No. 67 | v1.0.0 | 2026-08-13 */
export default async function M18SyncLayout({
children,
}: {
children: React.ReactNode;
}) {
const session = await getServerSession(authOptions);
const abilities = session?.user?.abilities ?? [];
const canAccess =
hasAbility(abilities, AUTH.M18_SYNC) || hasAbility(abilities, AUTH.ADMIN);

if (!canAccess) {
redirect("/dashboard");
}

return (
<I18nProvider namespaces={["m18Sync", "navigation", "common"]}>
{children}


+ 2
- 1
src/app/(main)/m18Syn/page.tsx Переглянути файл

@@ -26,6 +26,7 @@ function TabPanel(props: TabPanelProps) {
);
}

/** FP-MTMS Version Checklist | Functions Ref. No. 67 | v1.0.0 | 2026-08-13 */
export default function M18SynPage() {
const [tabValue, setTabValue] = useState(0);

@@ -270,7 +271,7 @@ export default function M18SynPage() {
M18 Sync (by code)
</Typography>
<Typography variant="body2" color="textSecondary" sx={{ mb: 2 }}>
ADMIN only. Sync Purchase Order, Delivery Order, or product/material from M18 using document or item code.
Requires 行政 (ADMIN) or M18同步 (M18_SYNC). Sync Purchase Order, Delivery Order, or product/material from M18 using document or item code.
</Typography>

<Tabs value={tabValue} onChange={(_, v) => setTabValue(v)} aria-label="M18 sync by code" centered variant="fullWidth">


+ 2
- 0
src/authorities.ts Переглянути файл

@@ -25,6 +25,8 @@ export const AUTH = {
*/
PRODUCT_PROCESS: "PRODUCT_PROCESS",
REPORT_MGMT: "REPORT_MGMT",
/** FP-MTMS Version Checklist | Functions Ref. No. 67 | v1.0.0 | 2026-08-13 — Manual M18 sync page (/m18Syn): ADMIN or this ability */
M18_SYNC: "M18_SYNC",
} as const;

/**


+ 2
- 1
src/components/NavigationContent/NavigationContent.tsx Переглянути файл

@@ -61,6 +61,7 @@ interface NavigationItem {
requiredAbility?: string | string[];
}

/** FP-MTMS Version Checklist | Functions Ref. No. 67 | v1.0.0 | 2026-08-13 */
const NavigationContent: React.FC = () => {
const { data: session, status } = useSession();
const abilities = session?.user?.abilities ?? [];
@@ -241,7 +242,7 @@ const NavigationContent: React.FC = () => {
icon: <Sync />,
labelKey: "nav.m18Sync",
path: "/m18Syn",
requiredAbility: [AUTH.ADMIN],
requiredAbility: [AUTH.M18_SYNC, AUTH.ADMIN],
isHidden: false,
},
{


+ 40
- 3
src/components/PickOrderSearch/WorkbenchPickExecution.tsx Переглянути файл

@@ -228,6 +228,16 @@ const isCheckedStatus = (status: string | undefined): boolean =>
const isRejectedStatus = (status: string | undefined): boolean =>
String(status || "").toLowerCase() === "rejected";

const isPendingSolStatus = (status: string | undefined): boolean => {
const s = String(status || "").toLowerCase();
return (
s === "pending" ||
s === "partially_completed" ||
s === "partially_complete" ||
s === ""
);
};

function safeDisplayTargetDate(targetDate: string | number[]): string {
try {
if (Array.isArray(targetDate) && targetDate.length >= 3) {
@@ -346,7 +356,7 @@ function mapHierarchicalToPickOrders(data: unknown): PickOrderTopRow[] {
});
}

/** FP-MTMS Version Checklist | Functions Ref. No. 28 | v1.0.2 | 2026-08-03 */
/** FP-MTMS Version Checklist | Functions Ref. No. 28 | v1.0.3 | 2026-08-13 */
const WorkbenchPickExecution: React.FC<Props> = ({ filterArgs }) => {
const { t } = useTranslation("pickOrder");
const { data: session } = useSession() as { data: SessionWithTokens | null };
@@ -1221,8 +1231,35 @@ const WorkbenchPickExecution: React.FC<Props> = ({ filterArgs }) => {
releaseProcessedQr(latest);
return;
}
const expectedPool = activeForUom.length > 0 ? activeForUom : allForUom;
let expectedRow = pickExpectedRowForSubstitution(expectedPool) || allForUom[0];
// Align DO/JO: when no active suggested lot, bind to pending noLot / unavailable
// rows instead of a completed row that still has lotNo.
let expectedRow: LotRow | undefined;
if (activeForUom.length > 0) {
expectedRow = pickExpectedRowForSubstitution(activeForUom) || allForUom[0];
} else {
const switchable = allForUom.find(
(r) =>
r.stockOutLineId > 0 &&
isPendingSolStatus(r.status) &&
!isCompletedStatus(r.status) &&
!isCheckedStatus(r.status) &&
(isNoLotWorkbenchRow(r) ||
isRejectedStatus(r.status) ||
isInventoryLotLineUnavailable(r) ||
isLotAvailabilityExpired(r)),
);
expectedRow =
switchable ||
allForUom.find(
(r) =>
r.stockOutLineId > 0 &&
isPendingSolStatus(r.status) &&
!isCompletedStatus(r.status) &&
!isCheckedStatus(r.status),
) ||
pickExpectedRowForSubstitution(allForUom) ||
allForUom[0];
}
if (!expectedRow) {
setError(t("Scanned item is not found in current line"));
startTransition(() => {


Завантаження…
Відмінити
Зберегти