CANCERYS\kw093 pirms 2 mēnešiem
vecāks
revīzija
c8f5d7bf99
6 mainītis faili ar 148 papildinājumiem un 54 dzēšanām
  1. +1
    -8
      src/components/DoDetail/DoInfoCard.tsx
  2. +1
    -1
      src/components/FinishedGoodSearch/FGPickOrderCard.tsx
  3. +24
    -28
      src/components/FinishedGoodSearch/GoodPickExecutionForm.tsx
  4. +99
    -15
      src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx
  5. +18
    -1
      src/i18n/zh/do.json
  6. +5
    -1
      src/i18n/zh/pickOrder.json

+ 1
- 8
src/components/DoDetail/DoInfoCard.tsx Parādīt failu

@@ -54,14 +54,7 @@ const DoInfoCard: React.FC<Props> = ({
disabled={true}
/>
</Grid>
<Grid item xs={6}>
<TextField
{...register("currencyCode")}
label={t("Currency Code")}
fullWidth
disabled={true}
/>
</Grid>
<Grid item xs={6}>
<TextField
{...register("orderDate")}


+ 1
- 1
src/components/FinishedGoodSearch/FGPickOrderCard.tsx Parādīt failu

@@ -108,7 +108,7 @@ const FGPickOrderCard: React.FC<Props> = ({ fgOrder, onQrCodeClick }) => {
onClick={() => onQrCodeClick(fgOrder.pickOrderId)}
sx={{ minWidth: 120 }}
>
{t("View QR Code")}
{t("Print DN/Label")}
</Button>
</Grid>
</Grid>


+ 24
- 28
src/components/FinishedGoodSearch/GoodPickExecutionForm.tsx Parādīt failu

@@ -87,10 +87,12 @@ const PickExecutionForm: React.FC<PickExecutionFormProps> = ({
const remainingQty = lot.inQty - lot.outQty;
return Math.max(0, remainingQty);
}, []);
const calculateRequiredQty = useCallback((lot: LotPickData) => {
const requiredQty = lot.requiredQty-(lot.actualPickQty||0);
return Math.max(0, requiredQty);
}, []);
const calculateRequiredQty = useCallback((lot: LotPickData) => {
// ✅ Use the original required quantity, not subtracting actualPickQty
// The actualPickQty in the form should be independent of the database value
return lot.requiredQty || 0;
}, []);
// 获取处理人员列表
useEffect(() => {
const fetchHandlers = async () => {
@@ -166,36 +168,30 @@ const calculateRequiredQty = useCallback((lot: LotPickData) => {
// ✅ Update form validation to require either missQty > 0 OR badItemQty > 0
const validateForm = (): boolean => {
const newErrors: FormErrors = {};
if (formData.actualPickQty === undefined || formData.actualPickQty < 0) {
newErrors.actualPickQty = t('pickOrder.validation.actualPickQtyRequired');
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');
}
// ✅ FIXED: Check if actual pick qty exceeds required qty (use original required qty)
if (formData.actualPickQty && formData.actualPickQty > (selectedLot?.requiredQty || 0)) {
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)
const hasMissQty = formData.missQty && formData.missQty > 0;
const hasBadItemQty = formData.badItemQty && formData.badItemQty > 0;
if (!hasMissQty && !hasBadItemQty) {
newErrors.missQty = t('pickOrder.validation.mustReportMissOrBadItems');
newErrors.badItemQty = t('pickOrder.validation.mustReportMissOrBadItems');
newErrors.missQty = t('At least one issue must be reported');
newErrors.badItemQty = t('At least one issue must be reported');
}
if (formData.missQty && formData.missQty < 0) {
newErrors.missQty = t('pickOrder.validation.missQtyInvalid');
}
if (formData.badItemQty && formData.badItemQty < 0) {
newErrors.badItemQty = t('pickOrder.validation.badItemQtyInvalid');
}
if (formData.badItemQty && formData.badItemQty > 0 && !formData.issueRemark) {
newErrors.issueRemark = t('pickOrder.validation.issueRemarkRequired');
}
if (formData.badItemQty && formData.badItemQty > 0 && !formData.handledBy) {
newErrors.handledBy = t('pickOrder.validation.handlerRequired');
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
@@ -251,7 +247,7 @@ const calculateRequiredQty = useCallback((lot: LotPickData) => {
<TextField
fullWidth
label={t('Required Qty')}
value={requiredQty || 0}
value={selectedLot?.requiredQty || 0}
disabled
variant="outlined"
// helperText={t('Still need to pick')}
@@ -277,7 +273,7 @@ const calculateRequiredQty = useCallback((lot: LotPickData) => {
value={formData.actualPickQty || 0}
onChange={(e) => handleInputChange('actualPickQty', parseFloat(e.target.value) || 0)}
error={!!errors.actualPickQty}
// helperText={errors.actualPickQty || t('Enter the quantity actually picked')}
helperText={errors.actualPickQty || `${t('Max')}: ${Math.min(remainingAvailableQty, selectedLot?.requiredQty || 0)}`}
variant="outlined"
/>
</Grid>


+ 99
- 15
src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx Parādīt failu

@@ -458,7 +458,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
// ✅ Use current data without refreshing to avoid infinite loop
const currentLotData = combinedLotData;
console.log(`�� Available lots:`, currentLotData.map(lot => lot.lotNo));
console.log(` Available lots:`, currentLotData.map(lot => lot.lotNo));
const matchingLots = currentLotData.filter(lot =>
lot.lotNo === lotNo ||
@@ -477,18 +477,17 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
try {
let successCount = 0;
let existsCount = 0;
let errorCount = 0;
for (const matchingLot of matchingLots) {
console.log(`🔄 Processing pick order line ${matchingLot.pickOrderLineId} for lot ${lotNo}`);
if (matchingLot.stockOutLineId) {
// ✅ FIXED: Use matchingLot.stockOutLineId instead of selectedLotForQr.stockOutLineId
// ✅ FIXED: Only update status to 'checked', keep qty at 0
const stockOutLineUpdate = await updateStockOutLineStatus({
id: matchingLot.stockOutLineId, // ✅ Use the correct ID
id: matchingLot.stockOutLineId,
status: 'checked',
qty: matchingLot.stockOutLineQty || matchingLot.requiredQty || 0
qty: 0 // ✅ Keep qty at 0 until user actually submits
});
console.log(`Update stock out line result for line ${matchingLot.pickOrderLineId}:`, stockOutLineUpdate);
@@ -500,12 +499,12 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
errorCount++;
}
} else {
// ✅ If no stock out line exists, create one
// ✅ If no stock out line exists, create one with qty = 0
const createStockOutLineData = {
consoCode: matchingLot.pickOrderConsoCode,
pickOrderLineId: matchingLot.pickOrderLineId,
inventoryLotLineId: matchingLot.lotId,
qty: matchingLot.requiredQty || 0
qty: 0 // ✅ Create with qty = 0
};
const createResult = await createStockOutLine(createStockOutLineData);
@@ -719,13 +718,19 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
}
try {
// ✅ FIXED: Calculate cumulative quantity correctly
const currentActualPickQty = lot.actualPickQty || 0;
const cumulativeQty = currentActualPickQty + newQty;
// ✅ FIXED: Determine status based on cumulative quantity vs required quantity
let newStatus = 'partially_completed';
if (cumulativeQty >= lot.requiredQty) {
newStatus = 'completed';
} else if (cumulativeQty > 0) {
newStatus = 'partially_completed';
} else {
newStatus = 'checked'; // QR scanned but no quantity submitted yet
}
console.log(`=== PICK QUANTITY SUBMISSION DEBUG ===`);
@@ -740,7 +745,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
await updateStockOutLineStatus({
id: lot.stockOutLineId,
status: newStatus,
qty: cumulativeQty
qty: cumulativeQty // ✅ Use cumulative quantity
});
if (newQty > 0) {
@@ -752,12 +757,11 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => {
});
}
// ✅ FIXED: Use the proper API function instead of direct fetch
// ✅ Check if pick order is completed when lot status becomes 'completed'
if (newStatus === 'completed' && lot.pickOrderConsoCode) {
console.log(`✅ Lot ${lot.lotNo} completed, checking if pick order ${lot.pickOrderConsoCode} is complete...`);
try {
// ✅ Use the imported API function instead of direct fetch
const completionResponse = await checkAndCompletePickOrderByConsoCode(lot.pickOrderConsoCode);
console.log(`✅ Pick order completion check result:`, completionResponse);
@@ -937,7 +941,83 @@ const paginatedData = useMemo(() => {
const endIndex = startIndex + paginationController.pageSize;
return combinedLotData.slice(startIndex, endIndex); // ✅ No sorting needed
}, [combinedLotData, paginationController]);

const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: number) => {
if (!lot.stockOutLineId) {
console.error("No stock out line found for this lot");
return;
}
try {
// ✅ FIXED: Calculate cumulative quantity correctly
const currentActualPickQty = lot.actualPickQty || 0;
const cumulativeQty = currentActualPickQty + submitQty;
// ✅ FIXED: Determine status based on cumulative quantity vs required quantity
let newStatus = 'partially_completed';
if (cumulativeQty >= lot.requiredQty) {
newStatus = 'completed';
} else if (cumulativeQty > 0) {
newStatus = 'partially_completed';
} else {
newStatus = 'checked'; // QR scanned but no quantity submitted yet
}
console.log(`=== PICK QUANTITY SUBMISSION DEBUG ===`);
console.log(`Lot: ${lot.lotNo}`);
console.log(`Required Qty: ${lot.requiredQty}`);
console.log(`Current Actual Pick Qty: ${currentActualPickQty}`);
console.log(`New Submitted Qty: ${submitQty}`);
console.log(`Cumulative Qty: ${cumulativeQty}`);
console.log(`New Status: ${newStatus}`);
console.log(`=====================================`);
await updateStockOutLineStatus({
id: lot.stockOutLineId,
status: newStatus,
qty: cumulativeQty // ✅ Use cumulative quantity
});
if (submitQty > 0) {
await updateInventoryLotLineQuantities({
inventoryLotLineId: lot.lotId,
qty: submitQty,
status: 'available',
operation: 'pick'
});
}
// ✅ Check if pick order is completed when lot status becomes 'completed'
if (newStatus === 'completed' && lot.pickOrderConsoCode) {
console.log(`✅ Lot ${lot.lotNo} completed, checking if pick order ${lot.pickOrderConsoCode} is complete...`);
try {
const completionResponse = await checkAndCompletePickOrderByConsoCode(lot.pickOrderConsoCode);
console.log(`✅ Pick order completion check result:`, completionResponse);
if (completionResponse.code === "SUCCESS") {
console.log(`�� Pick order ${lot.pickOrderConsoCode} completed successfully!`);
} else if (completionResponse.message === "not completed") {
console.log(`⏳ Pick order not completed yet, more lines remaining`);
} else {
console.error(`❌ Error checking completion: ${completionResponse.message}`);
}
} catch (error) {
console.error("Error checking pick order completion:", error);
}
}
await fetchAllCombinedLotData();
console.log("Pick quantity submitted successfully!");
setTimeout(() => {
checkAndAutoAssignNext();
}, 1000);
} catch (error) {
console.error("Error submitting pick quantity:", error);
}
}, [fetchAllCombinedLotData, checkAndAutoAssignNext]);


// ✅ Add these functions after line 395
@@ -1117,8 +1197,10 @@ const paginatedData = useMemo(() => {
<TableCell align="right">
{(() => {
const inQty = lot.inQty || 0;
const requiredQty = lot.requiredQty || 0;
const actualPickQty = lot.actualPickQty || 0;
const outQty = lot.outQty || 0;
const result = inQty - outQty;
const result = requiredQty;
return result.toLocaleString()+'('+lot.uomShortDesc+')';
})()}
</TableCell>
@@ -1143,10 +1225,12 @@ const paginatedData = useMemo(() => {
<Button
variant="contained"
onClick={() => {
// Submit with default lot required pick qty
const lotKey = `${lot.pickOrderLineId}-${lot.lotId}`;
handlePickQtyChange(lotKey, lot.requiredQty || lot.pickOrderLineRequiredQty);
handleSubmitPickQty(lot);
const submitQty = lot.requiredQty || lot.pickOrderLineRequiredQty;
// Submit with default lot required pick qty
handlePickQtyChange(lotKey, submitQty);
handleSubmitPickQtyWithQty(lot, submitQty);
}}
disabled={
(lot.lotAvailability === 'expired' ||


+ 18
- 1
src/i18n/zh/do.json Parādīt failu

@@ -18,6 +18,23 @@
"Details": "詳情",
"Pending": "待處理",
"Receiving": "接收中",
"Completed": "已完成"
"Completed": "已完成",
"Shop Code": "店鋪編號",
"Supplier Code": "供應商編號",
"Estimated Arrival Date": "預計到貨日期",
"Item No.": "商品編號",
"Item Name": "商品名稱",
"Quantity": "數量",
"uom": "單位",
"Lot No.": "批號",
"Expiry Date": "有效期",
"Location": "庫位",
"Price": "價格",
"Action": "操作",
"Create": "新增",
"Edit": "編輯",
"Delete": "刪除",
"Release": "放單",
"Back": "返回"

}

+ 5
- 1
src/i18n/zh/pickOrder.json Parādīt failu

@@ -257,5 +257,9 @@
"Pick Execution Detail":"進行提料詳情",
"Submit Required Pick Qty":"提交所需提料數量",
"Scan Result":"掃描結果",
"Ticket No.":"提票號碼"
"Ticket No.":"提票號碼",
"Start QR Scan":"開始QR掃描",
"Stop QR Scan":"停止QR掃描",
"Scanning...":"掃描中...",
"Print DN/Label":"列印送貨單/標籤"
}

Notiek ielāde…
Atcelt
Saglabāt