Enhance purchase order import with advanced receiving tracking and fulfillment logic
- Implement FIFO-based receiving fulfillment tracking - Add detailed receiving history with excess and partial fulfillment support - Improve vendor name resolution and fallback handling - Optimize incremental update queries by removing redundant conditions - Enhance receiving status calculation with more granular tracking
This commit is contained in:
@@ -30,7 +30,6 @@ async function importPurchaseOrders(prodConnection, localConnection, incremental
|
||||
const incrementalWhereClause = incrementalUpdate
|
||||
? `AND (
|
||||
p.date_updated > ?
|
||||
OR p.date_modified > ?
|
||||
OR p.date_ordered > ?
|
||||
OR p.date_estin > ?
|
||||
OR r.stamp > ?
|
||||
@@ -39,7 +38,7 @@ async function importPurchaseOrders(prodConnection, localConnection, incremental
|
||||
)`
|
||||
: "";
|
||||
const incrementalParams = incrementalUpdate
|
||||
? [lastSyncTime, lastSyncTime, lastSyncTime, lastSyncTime, lastSyncTime, lastSyncTime, lastSyncTime]
|
||||
? [lastSyncTime, lastSyncTime, lastSyncTime, lastSyncTime, lastSyncTime, lastSyncTime]
|
||||
: [];
|
||||
|
||||
// First get all relevant PO IDs with basic info
|
||||
@@ -51,14 +50,14 @@ async function importPurchaseOrders(prodConnection, localConnection, incremental
|
||||
USE INDEX (idx_date_created)
|
||||
JOIN po_products pop ON p.po_id = pop.po_id
|
||||
JOIN suppliers s ON p.supplier_id = s.supplierid
|
||||
WHERE p.date_ordered >= DATE_SUB(CURRENT_DATE, INTERVAL 5 YEAR)
|
||||
WHERE p.date_ordered >= DATE_SUB(CURRENT_DATE, INTERVAL ${incrementalUpdate ? '1' : '5'} YEAR)
|
||||
${incrementalWhereClause}
|
||||
UNION
|
||||
SELECT DISTINCT r.receiving_id as po_id, rp.pid
|
||||
FROM receivings_products rp
|
||||
USE INDEX (received_date)
|
||||
LEFT JOIN receivings r ON r.receiving_id = rp.receiving_id
|
||||
WHERE rp.received_date >= DATE_SUB(CURRENT_DATE, INTERVAL 5 YEAR)
|
||||
WHERE rp.received_date >= DATE_SUB(CURRENT_DATE, INTERVAL ${incrementalUpdate ? '1' : '5'} YEAR)
|
||||
${incrementalWhereClause}
|
||||
) all_items
|
||||
`, [...incrementalParams, ...incrementalParams]);
|
||||
@@ -66,11 +65,11 @@ async function importPurchaseOrders(prodConnection, localConnection, incremental
|
||||
const [poList] = await prodConnection.query(`
|
||||
SELECT DISTINCT
|
||||
COALESCE(p.po_id, r.receiving_id) as po_id,
|
||||
CASE
|
||||
WHEN p.po_id IS NOT NULL THEN s1.companyname
|
||||
WHEN r.supplier_id IS NOT NULL THEN s2.companyname
|
||||
ELSE 'No Supplier'
|
||||
END as vendor,
|
||||
COALESCE(
|
||||
NULLIF(s1.companyname, ''),
|
||||
NULLIF(s2.companyname, ''),
|
||||
'Unknown Vendor'
|
||||
) as vendor,
|
||||
CASE WHEN p.po_id IS NOT NULL THEN DATE(p.date_ordered) END as date,
|
||||
CASE WHEN p.po_id IS NOT NULL THEN DATE(p.date_estin) END as expected_date,
|
||||
COALESCE(p.status, 50) as status,
|
||||
@@ -79,15 +78,14 @@ async function importPurchaseOrders(prodConnection, localConnection, incremental
|
||||
FROM (
|
||||
SELECT po_id FROM po
|
||||
USE INDEX (idx_date_created)
|
||||
WHERE date_ordered >= DATE_SUB(CURRENT_DATE, INTERVAL 5 YEAR)
|
||||
WHERE date_ordered >= DATE_SUB(CURRENT_DATE, INTERVAL ${incrementalUpdate ? '1' : '5'} YEAR)
|
||||
AND (date_ordered > ?
|
||||
OR date_updated > ?
|
||||
OR date_modified > ?)
|
||||
OR date_updated > ?)
|
||||
UNION
|
||||
SELECT DISTINCT r.receiving_id as po_id
|
||||
FROM receivings r
|
||||
JOIN receivings_products rp USE INDEX (received_date) ON r.receiving_id = rp.receiving_id
|
||||
WHERE rp.received_date >= DATE_SUB(CURRENT_DATE, INTERVAL 5 YEAR)
|
||||
WHERE rp.received_date >= DATE_SUB(CURRENT_DATE, INTERVAL ${incrementalUpdate ? '1' : '5'} YEAR)
|
||||
AND (rp.received_date > ?
|
||||
OR rp.stamp > ?)
|
||||
) ids
|
||||
@@ -96,7 +94,7 @@ async function importPurchaseOrders(prodConnection, localConnection, incremental
|
||||
LEFT JOIN receivings r ON ids.po_id = r.receiving_id
|
||||
LEFT JOIN suppliers s2 ON r.supplier_id = s2.supplierid
|
||||
ORDER BY po_id
|
||||
`, [lastSyncTime, lastSyncTime, lastSyncTime, lastSyncTime, lastSyncTime]);
|
||||
`, [lastSyncTime, lastSyncTime, lastSyncTime, lastSyncTime]);
|
||||
|
||||
const totalItems = total;
|
||||
let processed = 0;
|
||||
@@ -215,24 +213,52 @@ async function importPurchaseOrders(prodConnection, localConnection, incremental
|
||||
const altReceivingHistory = altReceivingMap.get(product.pid) || [];
|
||||
const noPOReceivingHistory = noPOReceivingMap.get(product.pid) || [];
|
||||
|
||||
const received = receivingHistory.reduce((sum, r) => sum + r.qty_each, 0);
|
||||
const altReceived = altReceivingHistory.reduce((sum, r) => sum + r.qty_each, 0);
|
||||
const noPOReceived = noPOReceivingHistory.reduce((sum, r) => sum + r.qty_each, 0);
|
||||
const totalReceived = received + altReceived + noPOReceived;
|
||||
// Combine all receivings and sort by date
|
||||
const allReceivings = [
|
||||
...receivingHistory.map(r => ({ ...r, type: 'original' })),
|
||||
...altReceivingHistory.map(r => ({ ...r, type: 'alternate' })),
|
||||
...noPOReceivingHistory.map(r => ({ ...r, type: 'no_po' }))
|
||||
].sort((a, b) => new Date(a.received_date) - new Date(b.received_date));
|
||||
|
||||
// Track FIFO fulfillment
|
||||
let remainingToFulfill = product.ordered;
|
||||
const fulfillmentTracking = [];
|
||||
let totalReceived = 0;
|
||||
|
||||
for (const receiving of allReceivings) {
|
||||
const qtyToApply = Math.min(remainingToFulfill, receiving.qty_each);
|
||||
if (qtyToApply > 0) {
|
||||
fulfillmentTracking.push({
|
||||
receiving_id: receiving.receiving_id,
|
||||
qty_applied: qtyToApply,
|
||||
qty_total: receiving.qty_each,
|
||||
cost: receiving.cost_each,
|
||||
date: receiving.received_date,
|
||||
received_by: receiving.received_by,
|
||||
type: receiving.type,
|
||||
remaining_qty: receiving.qty_each - qtyToApply
|
||||
});
|
||||
remainingToFulfill -= qtyToApply;
|
||||
} else {
|
||||
// Track excess receivings
|
||||
fulfillmentTracking.push({
|
||||
receiving_id: receiving.receiving_id,
|
||||
qty_applied: 0,
|
||||
qty_total: receiving.qty_each,
|
||||
cost: receiving.cost_each,
|
||||
date: receiving.received_date,
|
||||
received_by: receiving.received_by,
|
||||
type: receiving.type,
|
||||
is_excess: true
|
||||
});
|
||||
}
|
||||
totalReceived += receiving.qty_each;
|
||||
}
|
||||
|
||||
const receiving_status = !totalReceived ? 1 : // created
|
||||
totalReceived < product.ordered ? 30 : // partial
|
||||
remainingToFulfill > 0 ? 30 : // partial
|
||||
40; // full
|
||||
|
||||
const allReceivings = [...receivingHistory];
|
||||
if (altReceivingHistory.length > 0) {
|
||||
allReceivings.push(...altReceivingHistory);
|
||||
}
|
||||
if (noPOReceivingHistory.length > 0) {
|
||||
allReceivings.push(...noPOReceivingHistory);
|
||||
}
|
||||
allReceivings.sort((a, b) => new Date(a.received_date) - new Date(b.received_date));
|
||||
|
||||
const firstReceiving = allReceivings[0] || {};
|
||||
const lastReceiving = allReceivings[allReceivings.length - 1] || {};
|
||||
|
||||
@@ -250,18 +276,19 @@ async function importPurchaseOrders(prodConnection, localConnection, incremental
|
||||
case 'long_note': return po.long_note;
|
||||
case 'ordered': return product.ordered;
|
||||
case 'received': return totalReceived;
|
||||
case 'unfulfilled': return remainingToFulfill;
|
||||
case 'excess_received': return Math.max(0, totalReceived - product.ordered);
|
||||
case 'received_date': return firstReceiving.received_date || null;
|
||||
case 'last_received_date': return lastReceiving.received_date || null;
|
||||
case 'received_by': return firstReceiving.received_by || null;
|
||||
case 'receiving_status': return receiving_status;
|
||||
case 'receiving_history': return JSON.stringify(allReceivings.map(r => ({
|
||||
receiving_id: r.receiving_id,
|
||||
qty: r.qty_each,
|
||||
cost: r.cost_each,
|
||||
date: r.received_date,
|
||||
received_by: r.received_by,
|
||||
alt_po: r.is_alt_po
|
||||
})));
|
||||
case 'receiving_history': return JSON.stringify({
|
||||
fulfillment: fulfillmentTracking,
|
||||
ordered_qty: product.ordered,
|
||||
total_received: totalReceived,
|
||||
remaining_unfulfilled: remainingToFulfill,
|
||||
excess_received: Math.max(0, totalReceived - product.ordered)
|
||||
});
|
||||
default: return null;
|
||||
}
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user