| @@ -76,7 +76,129 @@ export interface JobOrderDetail { | |||||
| pickLines: any[]; | pickLines: any[]; | ||||
| status: string; | status: string; | ||||
| } | } | ||||
| export interface UnassignedJobOrderPickOrder { | |||||
| pickOrderId: number; | |||||
| pickOrderCode: string; | |||||
| pickOrderConsoCode: string; | |||||
| pickOrderTargetDate: string; | |||||
| pickOrderStatus: string; | |||||
| jobOrderId: number; | |||||
| jobOrderCode: string; | |||||
| jobOrderName: string; | |||||
| reqQty: number; | |||||
| uom: string; | |||||
| planStart: string; | |||||
| planEnd: string; | |||||
| } | |||||
| export interface AssignJobOrderResponse { | |||||
| id: number | null; | |||||
| code: string | null; | |||||
| name: string | null; | |||||
| type: string | null; | |||||
| message: string | null; | |||||
| errorPosition: string | null; | |||||
| } | |||||
| export const recordSecondScanIssue = cache(async ( | |||||
| pickOrderId: number, | |||||
| itemId: number, | |||||
| data: { | |||||
| qty: number; | |||||
| isMissing: boolean; | |||||
| isBad: boolean; | |||||
| reason: string; | |||||
| createdBy: number; | |||||
| } | |||||
| ) => { | |||||
| return serverFetchJson<any>( | |||||
| `${BASE_API_URL}/jo/second-scan-issue/${pickOrderId}/${itemId}`, | |||||
| { | |||||
| method: "POST", | |||||
| headers: { "Content-Type": "application/json" }, | |||||
| body: JSON.stringify(data), | |||||
| next: { tags: ["jo-second-scan"] }, | |||||
| }, | |||||
| ); | |||||
| }); | |||||
| export const updateSecondQrScanStatus = cache(async (pickOrderId: number, itemId: number) => { | |||||
| return serverFetchJson<any>( | |||||
| `${BASE_API_URL}/jo/second-scan-qr/${pickOrderId}/${itemId}`, | |||||
| { | |||||
| method: "POST", | |||||
| next: { tags: ["jo-second-scan"] }, | |||||
| }, | |||||
| ); | |||||
| }); | |||||
| export const submitSecondScanQuantity = cache(async ( | |||||
| pickOrderId: number, | |||||
| itemId: number, | |||||
| data: { qty: number; isMissing?: boolean; isBad?: boolean; reason?: string } | |||||
| ) => { | |||||
| return serverFetchJson<any>( | |||||
| `${BASE_API_URL}/jo/second-scan-submit/${pickOrderId}/${itemId}`, | |||||
| { | |||||
| method: "POST", | |||||
| headers: { "Content-Type": "application/json" }, | |||||
| body: JSON.stringify(data), | |||||
| next: { tags: ["jo-second-scan"] }, | |||||
| }, | |||||
| ); | |||||
| }); | |||||
| // 获取未分配的 Job Order pick orders | |||||
| export const fetchUnassignedJobOrderPickOrders = cache(async () => { | |||||
| return serverFetchJson<UnassignedJobOrderPickOrder[]>( | |||||
| `${BASE_API_URL}/jo/unassigned-job-order-pick-orders`, | |||||
| { | |||||
| method: "GET", | |||||
| next: { tags: ["jo-unassigned"] }, | |||||
| }, | |||||
| ); | |||||
| }); | |||||
| // 分配 Job Order pick order 给用户 | |||||
| export const assignJobOrderPickOrder = async (pickOrderId: number, userId: number) => { | |||||
| return serverFetchJson<AssignJobOrderResponse>( | |||||
| `${BASE_API_URL}/jo/assign-job-order-pick-order/${pickOrderId}/${userId}`, | |||||
| { | |||||
| method: "POST", | |||||
| headers: { "Content-Type": "application/json" }, | |||||
| } | |||||
| ); | |||||
| }; | |||||
| // 获取 Job Order 分层数据 | |||||
| export const fetchJobOrderLotsHierarchical = cache(async (userId: number) => { | |||||
| return serverFetchJson<any>( | |||||
| `${BASE_API_URL}/jo/all-lots-hierarchical/${userId}`, | |||||
| { | |||||
| method: "GET", | |||||
| next: { tags: ["jo-hierarchical"] }, | |||||
| }, | |||||
| ); | |||||
| }); | |||||
| // 获取已完成的 Job Order pick orders | |||||
| export const fetchCompletedJobOrderPickOrders = cache(async (userId: number) => { | |||||
| return serverFetchJson<any>( | |||||
| `${BASE_API_URL}/jo/completed-job-order-pick-orders/${userId}`, | |||||
| { | |||||
| method: "GET", | |||||
| next: { tags: ["jo-completed"] }, | |||||
| }, | |||||
| ); | |||||
| }); | |||||
| // 获取已完成的 Job Order pick order records | |||||
| export const fetchCompletedJobOrderPickOrderRecords = cache(async (userId: number) => { | |||||
| return serverFetchJson<any[]>( | |||||
| `${BASE_API_URL}/jo/completed-job-order-pick-order-records/${userId}`, | |||||
| { | |||||
| method: "GET", | |||||
| next: { tags: ["jo-records"] }, | |||||
| }, | |||||
| ); | |||||
| }); | |||||
| export const fetchJobOrderDetailByCode = cache(async (code: string) => { | export const fetchJobOrderDetailByCode = cache(async (code: string) => { | ||||
| return serverFetchJson<JobOrderDetail>( | return serverFetchJson<JobOrderDetail>( | ||||
| `${BASE_API_URL}/jo/detailByCode/${code}`, | `${BASE_API_URL}/jo/detailByCode/${code}`, | ||||
| @@ -59,7 +59,7 @@ import { useQrCodeScannerContext } from '../QrCodeScannerProvider/QrCodeScannerP | |||||
| import { useSession } from "next-auth/react"; | import { useSession } from "next-auth/react"; | ||||
| import { SessionWithTokens } from "@/config/authConfig"; | import { SessionWithTokens } from "@/config/authConfig"; | ||||
| import { fetchStockInLineInfo } from "@/app/api/po/actions"; | import { fetchStockInLineInfo } from "@/app/api/po/actions"; | ||||
| import GoodPickExecutionForm from "./GoodPickExecutionForm"; | |||||
| import GoodPickExecutionForm from "./JobPickExecutionForm"; | |||||
| import FGPickOrderCard from "./FGPickOrderCard"; | import FGPickOrderCard from "./FGPickOrderCard"; | ||||
| interface Props { | interface Props { | ||||
| @@ -99,7 +99,7 @@ interface PickOrderData { | |||||
| lots: any[]; | lots: any[]; | ||||
| } | } | ||||
| const GoodPickExecutionRecord: React.FC<Props> = ({ filterArgs }) => { | |||||
| const FInishedJobOrderRecord: React.FC<Props> = ({ filterArgs }) => { | |||||
| const { t } = useTranslation("pickOrder"); | const { t } = useTranslation("pickOrder"); | ||||
| const router = useRouter(); | const router = useRouter(); | ||||
| const { data: session } = useSession() as { data: SessionWithTokens | null }; | const { data: session } = useSession() as { data: SessionWithTokens | null }; | ||||
| @@ -437,4 +437,4 @@ const GoodPickExecutionRecord: React.FC<Props> = ({ filterArgs }) => { | |||||
| ); | ); | ||||
| }; | }; | ||||
| export default GoodPickExecutionRecord; | |||||
| export default FInishedJobOrderRecord; | |||||
| @@ -81,7 +81,7 @@ const PickExecutionForm: React.FC<PickExecutionFormProps> = ({ | |||||
| const [errors, setErrors] = useState<FormErrors>({}); | const [errors, setErrors] = useState<FormErrors>({}); | ||||
| const [loading, setLoading] = useState(false); | const [loading, setLoading] = useState(false); | ||||
| const [handlers, setHandlers] = useState<Array<{ id: number; name: string }>>([]); | const [handlers, setHandlers] = useState<Array<{ id: number; name: string }>>([]); | ||||
| const [verifiedQty, setVerifiedQty] = useState<number>(0); | |||||
| // 计算剩余可用数量 | // 计算剩余可用数量 | ||||
| const calculateRemainingAvailableQty = useCallback((lot: LotPickData) => { | const calculateRemainingAvailableQty = useCallback((lot: LotPickData) => { | ||||
| const remainingQty = lot.inQty - lot.outQty; | const remainingQty = lot.inQty - lot.outQty; | ||||
| @@ -123,17 +123,15 @@ const PickExecutionForm: React.FC<PickExecutionFormProps> = ({ | |||||
| } | } | ||||
| }; | }; | ||||
| // 计算剩余可用数量 | |||||
| const remainingAvailableQty = calculateRemainingAvailableQty(selectedLot); | |||||
| const requiredQty = calculateRequiredQty(selectedLot); | |||||
| // ✅ Initialize verified quantity to the received quantity (actualPickQty) | |||||
| const initialVerifiedQty = selectedLot.actualPickQty || 0; | |||||
| setVerifiedQty(initialVerifiedQty); | |||||
| console.log("=== PickExecutionForm Debug ==="); | console.log("=== PickExecutionForm Debug ==="); | ||||
| console.log("selectedLot:", selectedLot); | console.log("selectedLot:", selectedLot); | ||||
| console.log("inQty:", selectedLot.inQty); | |||||
| console.log("outQty:", selectedLot.outQty); | |||||
| console.log("holdQty:", selectedLot.holdQty); | |||||
| console.log("availableQty:", selectedLot.availableQty); | |||||
| console.log("calculated remainingAvailableQty:", remainingAvailableQty); | |||||
| console.log("initialVerifiedQty:", initialVerifiedQty); | |||||
| console.log("=== End Debug ==="); | console.log("=== End Debug ==="); | ||||
| setFormData({ | setFormData({ | ||||
| pickOrderId: pickOrderId, | pickOrderId: pickOrderId, | ||||
| pickOrderCode: selectedPickOrderLine.pickOrderCode, | pickOrderCode: selectedPickOrderLine.pickOrderCode, | ||||
| @@ -147,18 +145,24 @@ const PickExecutionForm: React.FC<PickExecutionFormProps> = ({ | |||||
| lotNo: selectedLot.lotNo, | lotNo: selectedLot.lotNo, | ||||
| storeLocation: selectedLot.location, | storeLocation: selectedLot.location, | ||||
| requiredQty: selectedLot.requiredQty, | requiredQty: selectedLot.requiredQty, | ||||
| actualPickQty: selectedLot.actualPickQty || 0, | |||||
| actualPickQty: initialVerifiedQty, // ✅ Use the initial value | |||||
| missQty: 0, | missQty: 0, | ||||
| badItemQty: 0, // 初始化为 0,用户需要手动输入 | |||||
| badItemQty: 0, | |||||
| issueRemark: '', | issueRemark: '', | ||||
| pickerName: '', | pickerName: '', | ||||
| handledBy: undefined, | handledBy: undefined, | ||||
| }); | }); | ||||
| } | } | ||||
| }, [open, selectedLot, selectedPickOrderLine, pickOrderId, pickOrderCreateDate, calculateRemainingAvailableQty]); | |||||
| }, [open, selectedLot, selectedPickOrderLine, pickOrderId, pickOrderCreateDate]); | |||||
| const handleInputChange = useCallback((field: keyof PickExecutionIssueData, value: any) => { | const handleInputChange = useCallback((field: keyof PickExecutionIssueData, value: any) => { | ||||
| setFormData(prev => ({ ...prev, [field]: value })); | setFormData(prev => ({ ...prev, [field]: value })); | ||||
| // ✅ Update verified quantity state when actualPickQty changes | |||||
| if (field === 'actualPickQty') { | |||||
| setVerifiedQty(value); | |||||
| } | |||||
| // 清除错误 | // 清除错误 | ||||
| if (errors[field as keyof FormErrors]) { | if (errors[field as keyof FormErrors]) { | ||||
| setErrors(prev => ({ ...prev, [field]: undefined })); | setErrors(prev => ({ ...prev, [field]: undefined })); | ||||
| @@ -169,21 +173,21 @@ const PickExecutionForm: React.FC<PickExecutionFormProps> = ({ | |||||
| const validateForm = (): boolean => { | const validateForm = (): boolean => { | ||||
| const newErrors: FormErrors = {}; | const newErrors: FormErrors = {}; | ||||
| if (formData.actualPickQty === undefined || formData.actualPickQty < 0) { | |||||
| if (verifiedQty === undefined || verifiedQty < 0) { | |||||
| newErrors.actualPickQty = t('Qty is required'); | newErrors.actualPickQty = t('Qty is required'); | ||||
| } | } | ||||
| // ✅ FIXED: Check if actual pick qty exceeds remaining available qty | |||||
| if (formData.actualPickQty && formData.actualPickQty > remainingAvailableQty) { | |||||
| newErrors.actualPickQty = t('Qty is not allowed to be greater than remaining available qty'); | |||||
| // ✅ Check if verified qty exceeds received qty | |||||
| if (verifiedQty > (selectedLot?.actualPickQty || 0)) { | |||||
| newErrors.actualPickQty = t('Verified quantity cannot exceed received quantity'); | |||||
| } | } | ||||
| // ✅ FIXED: Check if actual pick qty exceeds required qty (use original required qty) | |||||
| if (formData.actualPickQty && formData.actualPickQty > (selectedLot?.requiredQty || 0)) { | |||||
| // ✅ Check if verified qty exceeds required qty | |||||
| if (verifiedQty > (selectedLot?.requiredQty || 0)) { | |||||
| newErrors.actualPickQty = t('Qty is not allowed to be greater than required qty'); | newErrors.actualPickQty = t('Qty is not allowed to be greater than required qty'); | ||||
| } | } | ||||
| // ✅ NEW: Require either missQty > 0 OR badItemQty > 0 (at least one issue must be reported) | |||||
| // ✅ Require either missQty > 0 OR badItemQty > 0 | |||||
| const hasMissQty = formData.missQty && formData.missQty > 0; | const hasMissQty = formData.missQty && formData.missQty > 0; | ||||
| const hasBadItemQty = formData.badItemQty && formData.badItemQty > 0; | const hasBadItemQty = formData.badItemQty && formData.badItemQty > 0; | ||||
| @@ -203,7 +207,13 @@ const PickExecutionForm: React.FC<PickExecutionFormProps> = ({ | |||||
| setLoading(true); | setLoading(true); | ||||
| try { | try { | ||||
| await onSubmit(formData as PickExecutionIssueData); | |||||
| // ✅ Use the verified quantity in the submission | |||||
| const submissionData = { | |||||
| ...formData, | |||||
| actualPickQty: verifiedQty | |||||
| } as PickExecutionIssueData; | |||||
| await onSubmit(submissionData); | |||||
| onClose(); | onClose(); | ||||
| } catch (error) { | } catch (error) { | ||||
| console.error('Error submitting pick execution issue:', error); | console.error('Error submitting pick execution issue:', error); | ||||
| @@ -215,6 +225,7 @@ const PickExecutionForm: React.FC<PickExecutionFormProps> = ({ | |||||
| const handleClose = () => { | const handleClose = () => { | ||||
| setFormData({}); | setFormData({}); | ||||
| setErrors({}); | setErrors({}); | ||||
| setVerifiedQty(0); | |||||
| onClose(); | onClose(); | ||||
| }; | }; | ||||
| @@ -257,8 +268,8 @@ const PickExecutionForm: React.FC<PickExecutionFormProps> = ({ | |||||
| <Grid item xs={6}> | <Grid item xs={6}> | ||||
| <TextField | <TextField | ||||
| fullWidth | fullWidth | ||||
| label={t('Remaining Available Qty')} | |||||
| value={remainingAvailableQty} | |||||
| label={t('Received Qty')} | |||||
| value={formData.actualPickQty || 0} | |||||
| disabled | disabled | ||||
| variant="outlined" | variant="outlined" | ||||
| // helperText={t('Available in warehouse')} | // helperText={t('Available in warehouse')} | ||||
| @@ -268,12 +279,16 @@ const PickExecutionForm: React.FC<PickExecutionFormProps> = ({ | |||||
| <Grid item xs={12}> | <Grid item xs={12}> | ||||
| <TextField | <TextField | ||||
| fullWidth | fullWidth | ||||
| label={t('Actual Pick Qty')} | |||||
| label={t('Verified Qty')} | |||||
| type="number" | type="number" | ||||
| value={formData.actualPickQty || 0} | |||||
| onChange={(e) => handleInputChange('actualPickQty', parseFloat(e.target.value) || 0)} | |||||
| value={verifiedQty} // ✅ Use the separate state | |||||
| onChange={(e) => { | |||||
| const newValue = parseFloat(e.target.value) || 0; | |||||
| setVerifiedQty(newValue); | |||||
| handleInputChange('actualPickQty', newValue); | |||||
| }} | |||||
| error={!!errors.actualPickQty} | error={!!errors.actualPickQty} | ||||
| helperText={errors.actualPickQty || `${t('Max')}: ${Math.min(remainingAvailableQty, selectedLot?.requiredQty || 0)}`} | |||||
| helperText={errors.actualPickQty || `${t('Max')}: ${Math.min(selectedLot?.actualPickQty || 0, selectedLot?.requiredQty || 0)}`} | |||||
| variant="outlined" | variant="outlined" | ||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| @@ -17,13 +17,21 @@ import { | |||||
| } from "@/app/utils/formatUtil"; | } from "@/app/utils/formatUtil"; | ||||
| import { Button, Grid, Stack, Tab, Tabs, TabsProps, Typography, Box } from "@mui/material"; | import { Button, Grid, Stack, Tab, Tabs, TabsProps, Typography, Box } from "@mui/material"; | ||||
| import Jodetail from "./Jodetail" | import Jodetail from "./Jodetail" | ||||
| import PickExecution from "./GoodPickExecution"; | |||||
| import PickExecution from "./JobPickExecution"; | |||||
| import { fetchAllItemsInClient, ItemCombo } from "@/app/api/settings/item/actions"; | import { fetchAllItemsInClient, ItemCombo } from "@/app/api/settings/item/actions"; | ||||
| import { fetchPickOrderClient, autoAssignAndReleasePickOrder, autoAssignAndReleasePickOrderByStore } from "@/app/api/pickOrder/actions"; | import { fetchPickOrderClient, autoAssignAndReleasePickOrder, autoAssignAndReleasePickOrderByStore } from "@/app/api/pickOrder/actions"; | ||||
| import { useSession } from "next-auth/react"; | import { useSession } from "next-auth/react"; | ||||
| import { SessionWithTokens } from "@/config/authConfig"; | import { SessionWithTokens } from "@/config/authConfig"; | ||||
| import PickExecutionDetail from "./GoodPickExecutiondetail"; | |||||
| import GoodPickExecutionRecord from "./GoodPickExecutionRecord"; | |||||
| import JobPickExecutionsecondscan from "./JobPickExecutionsecondscan"; | |||||
| import FInishedJobOrderRecord from "./FInishedJobOrderRecord"; | |||||
| import JobPickExecution from "./JobPickExecution"; | |||||
| import { | |||||
| fetchUnassignedJobOrderPickOrders, | |||||
| assignJobOrderPickOrder, | |||||
| fetchJobOrderLotsHierarchical, | |||||
| fetchCompletedJobOrderPickOrders, | |||||
| fetchCompletedJobOrderPickOrderRecords | |||||
| } from "@/app/api/jo/actions"; | |||||
| interface Props { | interface Props { | ||||
| pickOrders: PickOrderResult[]; | pickOrders: PickOrderResult[]; | ||||
| } | } | ||||
| @@ -48,6 +56,8 @@ const JodetailSearch: React.FC<Props> = ({ pickOrders }) => { | |||||
| const [tabIndex, setTabIndex] = useState(0); | const [tabIndex, setTabIndex] = useState(0); | ||||
| const [totalCount, setTotalCount] = useState<number>(); | const [totalCount, setTotalCount] = useState<number>(); | ||||
| const [isAssigning, setIsAssigning] = useState(false); | const [isAssigning, setIsAssigning] = useState(false); | ||||
| const [unassignedOrders, setUnassignedOrders] = useState<any[]>([]); | |||||
| const [isLoadingUnassigned, setIsLoadingUnassigned] = useState(false); | |||||
| const [hideCompletedUntilNext, setHideCompletedUntilNext] = useState<boolean>( | const [hideCompletedUntilNext, setHideCompletedUntilNext] = useState<boolean>( | ||||
| typeof window !== 'undefined' && localStorage.getItem('hideCompletedUntilNext') === 'true' | typeof window !== 'undefined' && localStorage.getItem('hideCompletedUntilNext') === 'true' | ||||
| ); | ); | ||||
| @@ -125,7 +135,47 @@ const JodetailSearch: React.FC<Props> = ({ pickOrders }) => { | |||||
| } | } | ||||
| }; | }; | ||||
| // ✅ Manual assignment handler - uses the action function | // ✅ Manual assignment handler - uses the action function | ||||
| const loadUnassignedOrders = useCallback(async () => { | |||||
| setIsLoadingUnassigned(true); | |||||
| try { | |||||
| const orders = await fetchUnassignedJobOrderPickOrders(); | |||||
| setUnassignedOrders(orders); | |||||
| } catch (error) { | |||||
| console.error("Error loading unassigned orders:", error); | |||||
| } finally { | |||||
| setIsLoadingUnassigned(false); | |||||
| } | |||||
| }, []); | |||||
| // 分配订单给当前用户 | |||||
| const handleAssignOrder = useCallback(async (pickOrderId: number) => { | |||||
| if (!currentUserId) { | |||||
| console.error("Missing user id in session"); | |||||
| return; | |||||
| } | |||||
| try { | |||||
| const result = await assignJobOrderPickOrder(pickOrderId, currentUserId); | |||||
| if (result.message === "Successfully assigned") { | |||||
| console.log("✅ Successfully assigned pick order"); | |||||
| // 刷新数据 | |||||
| window.dispatchEvent(new CustomEvent('pickOrderAssigned')); | |||||
| // 重新加载未分配订单列表 | |||||
| loadUnassignedOrders(); | |||||
| } else { | |||||
| console.warn("⚠️ Assignment failed:", result.message); | |||||
| alert(`Assignment failed: ${result.message}`); | |||||
| } | |||||
| } catch (error) { | |||||
| console.error("❌ Error assigning order:", error); | |||||
| alert("Error occurred during assignment"); | |||||
| } | |||||
| }, [currentUserId, loadUnassignedOrders]); | |||||
| // 在组件加载时获取未分配订单 | |||||
| useEffect(() => { | |||||
| loadUnassignedOrders(); | |||||
| }, [loadUnassignedOrders]); | |||||
| const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( | const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( | ||||
| (_e, newValue) => { | (_e, newValue) => { | ||||
| @@ -333,80 +383,33 @@ const JodetailSearch: React.FC<Props> = ({ pickOrders }) => { | |||||
| <Stack rowGap={2}> | <Stack rowGap={2}> | ||||
| <Grid container alignItems="center"> | <Grid container alignItems="center"> | ||||
| <Grid item xs={8}> | <Grid item xs={8}> | ||||
| <Box mb={2}> | |||||
| <Typography variant="h4" marginInlineEnd={2}> | |||||
| {t("Finished Good Order")} | |||||
| </Typography> | |||||
| </Box> | |||||
| </Grid> | </Grid> | ||||
| {/* Last 2 buttons aligned right */} | {/* Last 2 buttons aligned right */} | ||||
| <Grid item xs={6} > | <Grid item xs={6} > | ||||
| <Stack direction="row" spacing={1}> | |||||
| <Button | |||||
| variant="contained" | |||||
| onClick={() => handleAssignByStore("2/F")} | |||||
| disabled={isAssigning} | |||||
| > | |||||
| {isAssigning ? t("Assigning pick order...") : t("Pick Execution 2/F")} | |||||
| </Button> | |||||
| <Button | |||||
| variant="contained" | |||||
| onClick={() => handleAssignByStore("4/F")} | |||||
| disabled={isAssigning} | |||||
| > | |||||
| {isAssigning ? t("Assigning pick order...") : t("Pick Execution 4/F")} | |||||
| </Button> | |||||
| </Stack> | |||||
| {/* Unassigned Job Orders */} | |||||
| {unassignedOrders.length > 0 && ( | |||||
| <Box sx={{ mt: 2, p: 2, border: '1px solid #e0e0e0', borderRadius: 1 }}> | |||||
| <Typography variant="h6" gutterBottom> | |||||
| {t("Unassigned Job Orders")} ({unassignedOrders.length}) | |||||
| </Typography> | |||||
| <Stack direction="row" spacing={1} flexWrap="wrap"> | |||||
| {unassignedOrders.map((order) => ( | |||||
| <Button | |||||
| key={order.pickOrderId} | |||||
| variant="outlined" | |||||
| size="small" | |||||
| onClick={() => handleAssignOrder(order.pickOrderId)} | |||||
| disabled={isLoadingUnassigned} | |||||
| > | |||||
| {order.pickOrderCode} - {order.jobOrderName} | |||||
| </Button> | |||||
| ))} | |||||
| </Stack> | |||||
| </Box> | |||||
| )} | |||||
| </Grid> | </Grid> | ||||
| {/* ✅ Updated print buttons with completion status */} | |||||
| <Grid item xs={6} display="flex" justifyContent="flex-end"> | |||||
| <Stack direction="row" spacing={1}> | |||||
| {/* | |||||
| <Button | |||||
| variant={hideCompletedUntilNext ? "contained" : "outlined"} | |||||
| color={hideCompletedUntilNext ? "warning" : "inherit"} | |||||
| onClick={() => { | |||||
| const next = !hideCompletedUntilNext; | |||||
| setHideCompletedUntilNext(next); | |||||
| if (next) localStorage.setItem('hideCompletedUntilNext', 'true'); | |||||
| else localStorage.removeItem('hideCompletedUntilNext'); | |||||
| window.dispatchEvent(new Event('pickOrderAssigned')); // ask detail to re-fetch | |||||
| }} | |||||
| > | |||||
| {hideCompletedUntilNext ? t("Hide Completed: ON") : t("Hide Completed: OFF")} | |||||
| </Button> | |||||
| */} | |||||
| <Button | |||||
| variant="contained" | |||||
| disabled={!printButtonsEnabled} | |||||
| title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""} | |||||
| > | |||||
| {t("Print Draft")} | |||||
| </Button> | |||||
| <Button | |||||
| variant="contained" | |||||
| disabled={!printButtonsEnabled} | |||||
| title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""} | |||||
| > | |||||
| {t("Print Pick Order and DN Label")} | |||||
| </Button> | |||||
| <Button | |||||
| variant="contained" | |||||
| disabled={!printButtonsEnabled} | |||||
| title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""} | |||||
| > | |||||
| {t("Print Pick Order")} | |||||
| </Button> | |||||
| <Button | |||||
| variant="contained" | |||||
| disabled={!printButtonsEnabled} | |||||
| title={!printButtonsEnabled ? t("All lots must be completed before printing") : ""} | |||||
| > | |||||
| {t("Print DN Label")} | |||||
| </Button> | |||||
| </Stack> | |||||
| </Grid> | |||||
| </Grid> | </Grid> | ||||
| @@ -419,8 +422,8 @@ const JodetailSearch: React.FC<Props> = ({ pickOrders }) => { | |||||
| }}> | }}> | ||||
| <Tabs value={tabIndex} onChange={handleTabChange} variant="scrollable"> | <Tabs value={tabIndex} onChange={handleTabChange} variant="scrollable"> | ||||
| <Tab label={t("Pick Order Detail")} iconPosition="end" /> | <Tab label={t("Pick Order Detail")} iconPosition="end" /> | ||||
| <Tab label={t("Finished Good Detail")} iconPosition="end" /> | |||||
| <Tab label={t("Finished Good Record")} iconPosition="end" /> | |||||
| <Tab label={t("Job order match")} iconPosition="end" /> | |||||
| <Tab label={t("Finished Job Order Record")} iconPosition="end" /> | |||||
| </Tabs> | </Tabs> | ||||
| </Box> | </Box> | ||||
| @@ -429,9 +432,9 @@ const JodetailSearch: React.FC<Props> = ({ pickOrders }) => { | |||||
| <Box sx={{ | <Box sx={{ | ||||
| p: 2 | p: 2 | ||||
| }}> | }}> | ||||
| {tabIndex === 0 && <PickExecution filterArgs={filterArgs} />} | |||||
| {tabIndex === 1 && <PickExecutionDetail filterArgs={filterArgs} />} | |||||
| {tabIndex === 2 && <GoodPickExecutionRecord filterArgs={filterArgs} />} | |||||
| {tabIndex === 0 && <JobPickExecution filterArgs={filterArgs} />} | |||||
| {tabIndex === 1 && <JobPickExecutionsecondscan filterArgs={filterArgs} />} | |||||
| {tabIndex === 2 && <FInishedJobOrderRecord filterArgs={filterArgs} />} | |||||
| </Box> | </Box> | ||||
| </Box> | </Box> | ||||
| ); | ); | ||||