瀏覽代碼

update pick record user and putaway default warehouse

MergeProblem1
CANCERYS\kw093 19 小時之前
父節點
當前提交
e3f2b06561
共有 5 個檔案被更改,包括 119 行新增33 行删除
  1. +65
    -9
      src/components/Jodetail/newJobPickExecution.tsx
  2. +1
    -0
      src/components/PoDetail/PoInputGrid.tsx
  3. +18
    -13
      src/components/PoDetail/PutAwayForm.tsx
  4. +9
    -8
      src/components/ProductionProcess/ProductionProcessList.tsx
  5. +26
    -3
      src/components/Qc/QcStockInModal.tsx

+ 65
- 9
src/components/Jodetail/newJobPickExecution.tsx 查看文件

@@ -966,6 +966,14 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {
let newLotLineId = scannedLotData?.inventoryLotLineId;
if (!newLotLineId && scannedLotData?.stockInLineId) {
try {
if (currentUserId && selectedLotForQr.pickOrderId && selectedLotForQr.itemId) {
try {
await updateHandledBy(selectedLotForQr.pickOrderId, selectedLotForQr.itemId);
console.log(`✅ [LOT CONFIRM] Handler updated for itemId ${selectedLotForQr.itemId}`);
} catch (error) {
console.error(`❌ [LOT CONFIRM] Error updating handler (non-critical):`, error);
}
}
console.log(`🔍 [LOT CONFIRM] Fetching lot detail for stockInLineId: ${scannedLotData.stockInLineId}`);
const ld = await fetchLotDetail(scannedLotData.stockInLineId);
newLotLineId = ld.inventoryLotLineId;
@@ -1083,7 +1091,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {
} finally {
setIsConfirmingLot(false);
}
}, [expectedLotData, scannedLotData, selectedLotForQr, fetchJobOrderData]);
}, [expectedLotData, scannedLotData, selectedLotForQr, fetchJobOrderData,currentUserId, updateHandledBy ]);

const processOutsideQrCode = useCallback(async (latestQr: string) => {
// ✅ Only JSON QR supported for outside scanner (avoid false positive with lotNo)
@@ -1249,6 +1257,14 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {

console.log(`✅ [QR PROCESS] Processing exact match: lotNo=${exactMatch.lotNo}, stockOutLineId=${exactMatch.stockOutLineId}`);
try {
if (currentUserId && exactMatch.pickOrderId && exactMatch.itemId) {
try {
await updateHandledBy(exactMatch.pickOrderId, exactMatch.itemId);
console.log(`✅ [QR PROCESS] Handler updated for itemId ${exactMatch.itemId}`);
} catch (error) {
console.error(`❌ [QR PROCESS] Error updating handler (non-critical):`, error);
}
}
const res = await updateStockOutLineStatusByQRCodeAndLotNo({
pickOrderLineId: exactMatch.pickOrderLineId,
inventoryLotNo: exactMatch.lotNo,
@@ -1383,7 +1399,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {
return newMap;
});
}
}, [filterArgs?.pickOrderId, fetchJobOrderData, handleLotMismatch, lotDataIndexes, processedQrCombinations, combinedLotData, fetchStockInLineInfoCached]);
}, [filterArgs?.pickOrderId, fetchJobOrderData, handleLotMismatch, lotDataIndexes, processedQrCombinations, combinedLotData, fetchStockInLineInfoCached,currentUserId, updateHandledBy ]);

// Store in refs for immediate access in qrValues effect
processOutsideQrCodeRef.current = processOutsideQrCode;
@@ -1649,6 +1665,14 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {
}
try {
if (currentUserId && lot.pickOrderId && lot.itemId) {
try {
await updateHandledBy(lot.pickOrderId, lot.itemId);
} catch (error) {
console.error("❌ Error updating handler (non-critical):", error);
// Continue even if handler update fails
}
}
// Special case: If submitQty is 0 and all values are 0, mark as completed with qty: 0
if (submitQty === 0) {
console.log(`=== SUBMITTING ALL ZEROS CASE ===`);
@@ -1810,6 +1834,22 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {
console.log(`📦 Submitting ${scannedLots.length} scanned items using batchSubmitList...`);
try {
// ✅ 批量更新所有相关行的 handler(在提交前)
if (currentUserId) {
const uniqueItemIds = new Set(scannedLots.map(lot => lot.itemId));
const updatePromises = Array.from(uniqueItemIds).map(itemId => {
const lot = scannedLots.find(l => l.itemId === itemId);
if (lot && lot.pickOrderId) {
return updateHandledBy(lot.pickOrderId, itemId).catch(err => {
console.error(`❌ Error updating handler for itemId ${itemId}:`, err);
});
}
return Promise.resolve();
});
await Promise.all(updatePromises);
console.log(`✅ Updated handlers for ${uniqueItemIds.size} unique items`);
}

// ✅ 转换为 batchSubmitList 所需的格式
const lines: batchSubmitListLineRequest[] = scannedLots.map((lot) => {
const submitQty = lot.requiredQty || lot.pickOrderLineRequiredQty || 0;
@@ -1866,9 +1906,7 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {
} finally {
setIsSubmittingAll(false);
}
}, [combinedLotData, fetchJobOrderData, checkAndAutoAssignNext, currentUserId, filterArgs?.pickOrderId, onBackToList])

// Calculate scanned items count
}, [combinedLotData, fetchJobOrderData, checkAndAutoAssignNext, currentUserId, filterArgs?.pickOrderId, onBackToList, updateHandledBy])
const scannedItemsCount = useMemo(() => {
return combinedLotData.filter(lot => lot.stockOutLineStatus === 'checked').length;
}, [combinedLotData]);
@@ -1936,6 +1974,14 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {

const handlePickExecutionFormSubmit = useCallback(async (data: any) => {
try {
if (currentUserId && selectedLotForExecutionForm?.pickOrderId && selectedLotForExecutionForm?.itemId) {
try {
await updateHandledBy(selectedLotForExecutionForm.pickOrderId, selectedLotForExecutionForm.itemId);
console.log(`✅ [ISSUE FORM] Handler updated for itemId ${selectedLotForExecutionForm.itemId}`);
} catch (error) {
console.error(`❌ [ISSUE FORM] Error updating handler (non-critical):`, error);
}
}
console.log("Pick execution form submitted:", data);
const issueData = {
...data,
@@ -2357,12 +2403,12 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {
<Stack direction="row" spacing={1} alignItems="center">
<Button
variant="contained"
onClick={() => {
onClick={async () => {
const lotKey = `${lot.pickOrderLineId}-${lot.lotId}`;
const submitQty = lot.requiredQty || lot.pickOrderLineRequiredQty;
handlePickQtyChange(lotKey, submitQty);
handleSubmitPickQtyWithQty(lot, submitQty);
updateHandledBy(lot.pickOrderId, lot.itemId);
await handleSubmitPickQtyWithQty(lot, submitQty);
}}
disabled={
(lot.lotAvailability === 'expired' ||
@@ -2403,7 +2449,17 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBackToList }) => {
<Button
variant="outlined"
size="small"
onClick={() => handleSubmitPickQtyWithQty(lot, lot.requiredQty || lot.pickOrderLineRequiredQty || 0)}
onClick={async () => {
// ✅ 更新 handler 后再提交
if (currentUserId && lot.pickOrderId && lot.itemId) {
try {
await updateHandledBy(lot.pickOrderId, lot.itemId);
} catch (error) {
console.error("❌ Error updating handler (non-critical):", error);
}
}
await handleSubmitPickQtyWithQty(lot, lot.requiredQty || lot.pickOrderLineRequiredQty || 0);
}}
disabled={lot.stockOutLineStatus === 'completed'}
sx={{ fontSize: '0.7rem', py: 0.5, minHeight: '28px', minWidth: '90px' }}
>


+ 1
- 0
src/components/PoDetail/PoInputGrid.tsx 查看文件

@@ -954,6 +954,7 @@ const closeNewModal = useCallback(() => {
onClose={closeNewModal}
// itemDetail={modalInfo}
inputDetail={modalInfo}
warehouse={warehouse}
printerCombo={printerCombo}
printSource="stockIn"
/>


+ 18
- 13
src/components/PoDetail/PutAwayForm.tsx 查看文件

@@ -61,6 +61,7 @@ interface Props {
itemDetail: StockInLine;
warehouse?: WarehouseResult[];
disabled: boolean;
suggestedLocationCode?: string;
// qc: QcItemWithChecks[];
setRowModesModel: Dispatch<SetStateAction<GridRowModesModel>>;
setRowSelectionModel: Dispatch<SetStateAction<GridRowSelectionModel>>;
@@ -85,7 +86,7 @@ const style = {
width: "auto",
};

const PutAwayForm: React.FC<Props> = ({ itemDetail, warehouse=[], disabled, setRowModesModel, setRowSelectionModel }) => {
const PutAwayForm: React.FC<Props> = ({ itemDetail, warehouse=[], disabled, suggestedLocationCode, setRowModesModel, setRowSelectionModel }) => {
const { t } = useTranslation("purchaseOrder");
const apiRef = useGridApiRef();
const {
@@ -113,19 +114,16 @@ const PutAwayForm: React.FC<Props> = ({ itemDetail, warehouse=[], disabled, setR
group: "default",
};
const options = useMemo(() => {
const defaultLabel = suggestedLocationCode || t("W201 - 2F-A,B室");
return [
{
value: 1,
label: t("W201 - 2F-A,B室"),
group: "default",
},
{ value: 1, label: defaultLabel, group: "default" },
...filteredWarehouse.map((w) => ({
value: w.id,
label: `${w.code} - ${w.name}`,
label: defaultLabel,
group: "existing",
})),
];
}, [filteredWarehouse]);
}, [filteredWarehouse, suggestedLocationCode, t]);
const currentValue =
warehouseId > 0
? options.find((o) => o.value === warehouseId)
@@ -254,10 +252,16 @@ const PutAwayForm: React.FC<Props> = ({ itemDetail, warehouse=[], disabled, setR
flex: 2,
editable: false,
renderCell(params) {
return <span style={{fontSize:24}}>
{params.value}
</span>
}
const value = (params.value as string) ?? "";
// 目前格式像 "2F-W201-#L-08 - 2F-W201",只要左邊 LocationCode
const locationCode = value.split(" - ")[0] || value;
return (
<span style={{ fontSize: 24 }}>
{locationCode}
</span>
);
},
// renderEditCell: (params) => {
// const index = params.api.getRowIndexRelativeToVisibleRows(params.row.id)
// // console.log(index)
@@ -422,7 +426,8 @@ const PutAwayForm: React.FC<Props> = ({ itemDetail, warehouse=[], disabled, setR
disableClearable
disabled
fullWidth
defaultValue={options[0]} /// modify this later
//defaultValue={options[0]} /// modify this later
value={options[0]}
// onChange={onChange}
getOptionLabel={(option) => option.label}
options={options}


+ 9
- 8
src/components/ProductionProcess/ProductionProcessList.tsx 查看文件

@@ -14,6 +14,7 @@ import {
Grid,
} from "@mui/material";
import { useTranslation } from "react-i18next";
import { fetchItemForPutAway } from "@/app/api/stockIn/actions";
import QcStockInModal from "../Qc/QcStockInModal";
import { useSession } from "next-auth/react";
import { SessionWithTokens } from "@/config/authConfig";
@@ -49,7 +50,7 @@ const ProductProcessList: React.FC<ProductProcessListProps> = ({ onSelectProcess
const [openModal, setOpenModal] = useState<boolean>(false);
const [modalInfo, setModalInfo] = useState<StockInLineInput>();
const currentUserId = session?.id ? parseInt(session.id) : undefined;
const [suggestedLocationCode, setSuggestedLocationCode] = useState<string | null>(null);
const handleAssignPickOrder = useCallback(async (pickOrderId: number, jobOrderId?: number, productProcessId?: number) => {
if (!currentUserId) {
alert(t("Unable to get user ID"));
@@ -315,13 +316,13 @@ const ProductProcessList: React.FC<ProductProcessListProps> = ({ onSelectProcess
})}
</Grid>
<QcStockInModal
session={sessionToken}
open={openModal}
onClose={closeNewModal}
inputDetail={modalInfo}
printerCombo={printerCombo}
printSource="productionProcess"
/>
session={sessionToken}
open={openModal}
onClose={closeNewModal}
inputDetail={modalInfo}
printerCombo={printerCombo}
printSource="productionProcess"
/>
{processes.length > 0 && (
<TablePagination
component="div"


+ 26
- 3
src/components/Qc/QcStockInModal.tsx 查看文件

@@ -41,7 +41,7 @@ import { fetchStockInLineInfo } from "@/app/api/stockIn/actions";
import FgStockInForm from "../StockIn/FgStockInForm";
import LoadingComponent from "../General/LoadingComponent";
import { printFGStockInLabel, PrintFGStockInLabelRequest, fetchFGStockInLabel } from "@/app/api/jo/actions";
import { fetchItemForPutAway } from "@/app/api/stockIn/actions";
const style = {
position: "absolute",
top: "50%",
@@ -89,7 +89,7 @@ const QcStockInModal: React.FC<Props> = ({
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
// const [skipQc, setSkipQc] = useState<Boolean>(false);
// const [viewOnly, setViewOnly] = useState(false);
const [itemLocationCode, setItemLocationCode] = useState<string | null>(null);
const printerStorageKey = useMemo(
() => `qcStockInModal_selectedPrinterId_${session?.id ?? "guest"}`,
[session?.id],
@@ -143,7 +143,27 @@ const QcStockInModal: React.FC<Props> = ({
}
}
}, [open]);

useEffect(() => {
const loadItemLocationCode = async () => {
if (!stockInLineInfo?.itemId) return;
try {
const itemResult = await fetchItemForPutAway(stockInLineInfo.itemId);
const item = itemResult.item;
const locationCode = item.LocationCode || item.locationCode || null;
setItemLocationCode(locationCode);
console.log("%c [QC] item LocationCode:", "color:cyan", locationCode);
} catch (error) {
console.error("Error fetching item to get LocationCode in QC:", error);
setItemLocationCode(null);
}
};
if (stockInLineInfo && stockInLineInfo.status !== StockInStatus.REJECTED) {
loadItemLocationCode();
}
}, [stockInLineInfo]);
// Make sure stock in line info is fetched
useEffect(() => {
if (stockInLineInfo) {
@@ -695,12 +715,15 @@ const printQrcode = useCallback(

{tabIndex === 1 &&
<Box>


<PutAwayForm
itemDetail={stockInLineInfo}
warehouse={warehouse!}
disabled={viewOnly}
setRowModesModel={setPafRowModesModel}
setRowSelectionModel={setPafRowSelectionModel}
suggestedLocationCode={itemLocationCode || undefined}
/>
</Box>
}


Loading…
取消
儲存