File: D:/web/hyperflight/apps/stockflow/index-v14.asp
<%
Option Explicit
Response.Buffer = True ' Ensure buffering is on so we can manually Flush it later
' ======================================================================
' 0. AJAX Endpoint for Incremental Search (Auto-suggest)
' ======================================================================
If Request.QueryString("action") = "search" Then
Dim searchQuery, searchConn, searchCmd, searchRs, jsonStr, isFirst
Dim safeCode, safeName, searchParam
searchQuery = Trim(Request.QueryString("q"))
Response.ContentType = "application/json"
If Len(searchQuery) > 1 Then
Set searchConn = Server.CreateObject("ADODB.Connection")
searchConn.Open "DSN=MySQL_hyperflight;"
Set searchCmd = Server.CreateObject("ADODB.Command")
Set searchCmd.ActiveConnection = searchConn
searchCmd.CommandType = 1 ' adCmdText
searchParam = "%" & searchQuery & "%"
' STEP 1: Fast Search by Product Code First
searchCmd.CommandText = "SELECT ProductCode, ProductName FROM products WHERE ProductCode LIKE ? AND ProductDisabled = 0 ORDER BY ProductCode ASC LIMIT 15"
searchCmd.Parameters.Append searchCmd.CreateParameter("@qCode", 200, 1, 100, searchParam)
Set searchRs = searchCmd.Execute()
' STEP 2: Fallback to Product Name if Code search found nothing
If searchRs.EOF Then
searchRs.Close
searchCmd.Parameters.Delete(0) ' Clear the previous parameter to reuse the command
searchCmd.CommandText = "SELECT ProductCode, ProductName FROM products WHERE ProductName LIKE ? AND ProductDisabled = 0 ORDER BY ProductCode ASC LIMIT 15"
searchCmd.Parameters.Append searchCmd.CreateParameter("@qName", 200, 1, 100, searchParam)
Set searchRs = searchCmd.Execute()
End If
' STEP 3: Build the JSON Response
jsonStr = "["
isFirst = True
Do While Not searchRs.EOF
If Not isFirst Then jsonStr = jsonStr & ","
' Safely escape quotes and backslashes for JSON formatting
safeCode = Replace(Replace(searchRs("ProductCode") & "", "\", "\\"), """", "\""")
safeName = Replace(Replace(searchRs("ProductName") & "", "\", "\\"), """", "\""")
' Output as an object: {"c": "Code", "n": "Name"}
jsonStr = jsonStr & "{""c"":""" & safeCode & """, ""n"":""" & safeName & """}"
isFirst = False
searchRs.MoveNext
Loop
jsonStr = jsonStr & "]"
Response.Write jsonStr
searchRs.Close
Set searchRs = Nothing
Set searchCmd = Nothing
searchConn.Close
Set searchConn = Nothing
Else
Response.Write "[]"
End If
Response.End
End If
' ======================================================================
' ADO Constants for Parameterized Queries
Const adCmdText = 1
Const adParamInput = 1
Const adVarChar = 200
Const adInteger = 3
Dim productCode, conn, cmd, rs, rsSnap
Dim productID, recordedNumInStock, currentCalculatedStock, discrepancy
Dim productName
Dim transactionDate, transType, qty, ref, status, prevStockSys, stockAfterSys
Dim isMatchedOrder, isMatchedInvoice, isDuplicate, showRow
Dim startingStockInput, startingStock, startDate, snapshotInfo
Dim auditVisibility, auditSync
Dim sqlOrders, sqlInvoices, sqlAudit, finalSql
Dim displayQty, cssClass, rowClass, syncBadge
Dim rowCount ' For the flush buffer
' Initialize variables from form submission
productCode = UCase(Trim(Request.Form("ProductCode")))
startingStockInput = Trim(Request.Form("StartingStock"))
startDate = Trim(Request.Form("StartDate"))
auditVisibility = Request.Form("AuditVisibility")
auditSync = Request.Form("AuditSync")
' Set defaults for first load
If auditVisibility = "" Then auditVisibility = "3" ' Default to Clean View
If auditSync = "" Then auditSync = "0" ' Default to Display Only
productID = 0
recordedNumInStock = 0
startingStock = 0
snapshotInfo = "Manual Baseline (or 0 if blank)"
productName = ""
If productCode <> "" Then
Set conn = Server.CreateObject("ADODB.Connection")
conn.Open "DSN=MySQL_hyperflight;"
' 1. Get ProductID, Current System Stock, and Name
Set cmd = Server.CreateObject("ADODB.Command")
Set cmd.ActiveConnection = conn
cmd.CommandType = adCmdText
cmd.CommandText = "SELECT ProductID, NumInStock, ProductName FROM products WHERE ProductCode = ?"
cmd.Parameters.Append cmd.CreateParameter("@ProdCode", adVarChar, adParamInput, 20, productCode)
Set rs = cmd.Execute()
If Not rs.EOF Then
productID = rs("ProductID")
If Not IsNull(rs("NumInStock")) Then recordedNumInStock = CLng(rs("NumInStock"))
If Not IsNull(rs("ProductName")) Then productName = rs("ProductName")
End If
rs.Close
Set cmd = Nothing
' 2. Auto-fetch Starting Stock from Snapshots
If productID > 0 Then
If startingStockInput <> "" And IsNumeric(startingStockInput) Then
startingStock = CLng(startingStockInput)
snapshotInfo = "Manual Input"
ElseIf startDate <> "" Then
Set cmd = Server.CreateObject("ADODB.Command")
Set cmd.ActiveConnection = conn
cmd.CommandType = adCmdText
cmd.CommandText = "SELECT NumInStock, DateOfStock, TimeOfStock FROM stock_snapshots WHERE ProductID = ? AND DateOfStock < ? ORDER BY DateOfStock DESC LIMIT 1"
cmd.Parameters.Append cmd.CreateParameter("@ProdID", adInteger, adParamInput, , productID)
cmd.Parameters.Append cmd.CreateParameter("@SnapDate", adVarChar, adParamInput, 20, startDate)
Set rsSnap = cmd.Execute()
If Not rsSnap.EOF Then
If Not IsNull(rsSnap("NumInStock")) Then startingStock = CLng(rsSnap("NumInStock"))
snapshotInfo = "Auto-Snapshot from " & rsSnap("DateOfStock")
Else
snapshotInfo = "No prior snapshot found. Defaulting to 0."
End If
rsSnap.Close
Set cmd = Nothing
End If
End If
currentCalculatedStock = startingStock
' 3. Dynamic Query Builder with Fuzzy Matching Flags & Sort Priority
If productID > 0 Then
Set cmd = Server.CreateObject("ADODB.Command")
Set cmd.ActiveConnection = conn
cmd.CommandType = adCmdText
' Base Queries (SortPriority 1 for human actions)
sqlOrders = "SELECT o.DateTimeOrdered AS TransDate, 'OUT' AS TransType, od.Qty AS Amount, CAST(o.OrderNo AS VARCHAR(50)) AS Ref, o.Status, od.PrevStock AS PrevStockSys, NULL AS StockAfterSys, 0 AS IsMatchedOrder, 0 AS IsMatchedInvoice, 1 AS SortPriority, " & _
"'Order' AS SourceType, CAST(o.OrderNo AS VARCHAR(50)) AS SourceRef, NULL AS AuditNote, NULL AS RestockOrderNo " & _
"FROM orders o " & _
"INNER JOIN orderdetails od ON o.OrderNo = od.OrderNo " & _
"WHERE od.ProductID = ? " & _
"AND o.Status IN ('COMPLETED', 'PAYMENT RECEIVED', 'AWAITING PAYMENT', 'PAYMENT ON ACCOUNT') " & _
"AND o.IsInvoice = 1 "
sqlInvoices = "SELECT CAST(pi.InvoiceDate AS DATETIME) AS TransDate, 'IN' AS TransType, pil.QtyAddedToStock AS Amount, CAST(pi.PurchaseInvoiceID AS VARCHAR(50)) AS Ref, pi.InvoiceStatus AS Status, NULL AS PrevStockSys, NULL AS StockAfterSys, 0 AS IsMatchedOrder, 0 AS IsMatchedInvoice, 1 AS SortPriority, " & _
"'Invoice' AS SourceType, CAST(pi.PurchaseInvoiceID AS VARCHAR(50)) AS SourceRef, NULL AS AuditNote, NULL AS RestockOrderNo " & _
"FROM purchase_invoices pi " & _
"INNER JOIN purchase_invoice_lines pil ON pi.PurchaseInvoiceID = pil.PurchaseInvoiceID " & _
"INNER JOIN purchase_order_lines pol ON pil.PurchaseOrderLineID = pol.PurchaseOrderLineID " & _
"WHERE pol.ProductID = ? "
' Audit Query (SortPriority 2 for database triggers)
sqlAudit = "SELECT sa.DateTimeUpdated AS TransDate, 'ADJ' AS TransType, (sa.StockAfter - sa.StockBefore) AS Amount, sa.UserName AS Ref, sa.PCName AS Status, sa.StockBefore AS PrevStockSys, sa.StockAfter AS StockAfterSys, " & _
"IFNULL((SELECT 1 FROM orders o INNER JOIN orderdetails od ON o.OrderNo = od.OrderNo WHERE od.ProductID = sa.ProductID AND -od.Qty = (sa.StockAfter - sa.StockBefore) AND o.IsInvoice = 1 AND o.Status IN ('COMPLETED', 'PAYMENT RECEIVED', 'AWAITING PAYMENT', 'PAYMENT ON ACCOUNT') AND o.DateTimeOrdered BETWEEN sa.DateTimeUpdated - INTERVAL 2 MINUTE AND sa.DateTimeUpdated + INTERVAL 2 MINUTE LIMIT 1), 0) AS IsMatchedOrder, " & _
"IFNULL((SELECT 1 FROM purchase_invoice_lines pil INNER JOIN purchase_invoices pi ON pil.PurchaseInvoiceID = pi.PurchaseInvoiceID INNER JOIN purchase_order_lines pol ON pil.PurchaseOrderLineID = pol.PurchaseOrderLineID WHERE pol.ProductID = sa.ProductID AND pil.QtyAddedToStock = (sa.StockAfter - sa.StockBefore) AND pi.InvoiceDate BETWEEN sa.DateTimeUpdated - INTERVAL 60 DAY AND sa.DateTimeUpdated + INTERVAL 1 DAY LIMIT 1), 0) AS IsMatchedInvoice, " & _
"2 AS SortPriority, " & _
"sa.SourceType, CAST(sa.SourceRef AS VARCHAR(50)) AS SourceRef, sa.AuditNote, " & _
"(SELECT CAST(OrderNo AS VARCHAR(50)) FROM restocks r WHERE r.RestockID = sa.SourceRef LIMIT 1) AS RestockOrderNo " & _
"FROM stock_audit sa " & _
"WHERE sa.ProductID = ? AND sa.AuditType = 'Main' "
If auditVisibility = "1" Then
sqlAudit = sqlAudit & " AND IFNULL(sa.PCName, '') <> '-WEBSITE-' "
End If
' Apply Date Filters
If startDate <> "" Then
sqlOrders = sqlOrders & " AND o.DateTimeOrdered >= ? "
sqlInvoices = sqlInvoices & " AND pi.InvoiceDate >= ? "
sqlAudit = sqlAudit & " AND sa.DateTimeUpdated >= ? "
End If
' Combine queries based on visibility selection
If auditVisibility = "0" Then
finalSql = sqlOrders & " UNION ALL " & sqlInvoices & " ORDER BY TransDate ASC, SortPriority ASC"
ElseIf auditVisibility = "4" Then
finalSql = sqlAudit & " ORDER BY TransDate ASC, SortPriority ASC"
Else
finalSql = sqlOrders & " UNION ALL " & sqlInvoices & " UNION ALL " & sqlAudit & " ORDER BY TransDate ASC, SortPriority ASC"
End If
cmd.CommandText = finalSql
' Append parameters sequentially
If auditVisibility = "0" Then
cmd.Parameters.Append cmd.CreateParameter("@ProdID1", adInteger, adParamInput, , productID)
If startDate <> "" Then cmd.Parameters.Append cmd.CreateParameter("@StartDate1", adVarChar, adParamInput, 20, startDate & " 00:00:00")
cmd.Parameters.Append cmd.CreateParameter("@ProdID2", adInteger, adParamInput, , productID)
If startDate <> "" Then cmd.Parameters.Append cmd.CreateParameter("@StartDate2", adVarChar, adParamInput, 20, startDate)
ElseIf auditVisibility = "4" Then
cmd.Parameters.Append cmd.CreateParameter("@ProdID3", adInteger, adParamInput, , productID)
If startDate <> "" Then cmd.Parameters.Append cmd.CreateParameter("@StartDate3", adVarChar, adParamInput, 20, startDate & " 00:00:00")
Else
cmd.Parameters.Append cmd.CreateParameter("@ProdID1", adInteger, adParamInput, , productID)
If startDate <> "" Then cmd.Parameters.Append cmd.CreateParameter("@StartDate1", adVarChar, adParamInput, 20, startDate & " 00:00:00")
cmd.Parameters.Append cmd.CreateParameter("@ProdID2", adInteger, adParamInput, , productID)
If startDate <> "" Then cmd.Parameters.Append cmd.CreateParameter("@StartDate2", adVarChar, adParamInput, 20, startDate)
cmd.Parameters.Append cmd.CreateParameter("@ProdID3", adInteger, adParamInput, , productID)
If startDate <> "" Then cmd.Parameters.Append cmd.CreateParameter("@StartDate3", adVarChar, adParamInput, 20, startDate & " 00:00:00")
End If
Set rs = cmd.Execute()
End If
End If
%>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Stock Flow Checker</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
.table-responsive { max-width: 1300px; }
.qty-in { color: #198754; font-weight: bold; }
.qty-out { color: #dc3545; font-weight: bold; }
.qty-adj { color: #0dcaf0; font-weight: bold; }
.drift-warning { background-color: #fff3cd !important; }
.sync-row { background-color: #e2e3e5 !important; border-left: 4px solid #0d6efd; }
.duplicate-row { opacity: 0.55; background-color: #f8f9fa !important; font-style: italic; }
#ProductCode { text-transform: uppercase; }
.product-header-img { max-width: 80px; max-height: 80px; object-fit: contain; border: 1px solid #dee2e6; padding: 4px; border-radius: 6px; background: #fff;}
</style>
</head>
<body class="bg-light pb-5">
<div class="container-fluid mt-5 px-4">
<div class="card shadow-sm mb-4 mx-auto" style="max-width: 1300px;">
<div class="card-header bg-primary text-white">
<h4 class="mb-0">Stock Flow Checker (Hybrid Mode)</h4>
</div>
<div class="card-body bg-light">
<form method="post" action="" class="row g-3">
<div class="col-md-3">
<label for="ProductCode" class="form-label fw-bold mb-1">Product Code:</label>
<input type="text" id="ProductCode" name="ProductCode" class="form-control" list="productCodes" autocomplete="off" placeholder="e.g. KST..." value="<%=Server.HTMLEncode(productCode)%>" required>
<datalist id="productCodes"></datalist>
</div>
<div class="col-md-3">
<label for="StartDate" class="form-label fw-bold mb-1">Start Date:</label>
<input type="date" id="StartDate" name="StartDate" class="form-control" value="<%=Server.HTMLEncode(startDate)%>">
</div>
<div class="col-md-2">
<label for="StartingStock" class="form-label fw-bold mb-1">Override Start:</label>
<input type="number" id="StartingStock" name="StartingStock" class="form-control" placeholder="Auto" value="<%=Server.HTMLEncode(startingStockInput)%>">
</div>
<div class="col-md-4 d-flex align-items-end">
<button type="submit" class="btn btn-primary w-100 fw-bold">Run Ledger</button>
</div>
<div class="col-md-4">
<label for="AuditVisibility" class="form-label fw-bold mb-1">Audit Trail Visibility:</label>
<select id="AuditVisibility" name="AuditVisibility" class="form-select">
<option value="0" <% If auditVisibility="0" Then Response.Write "selected" %>>Hide Audit Events (Orders/Invoices Only)</option>
<option value="1" <% If auditVisibility="1" Then Response.Write "selected" %>>Show PC Updates Only (NEIL, etc.)</option>
<option value="2" <% If auditVisibility="2" Then Response.Write "selected" %>>Show ALL Events (Show Matched as Grey)</option>
<option value="3" <% If auditVisibility="3" Then Response.Write "selected" %>>Show ALL Events (Hide Matched)</option>
<option value="4" <% If auditVisibility="4" Then Response.Write "selected" %>>Show Audit Events Only (Raw DB Triggers)</option>
</select>
</div>
<div class="col-md-4">
<label for="AuditSync" class="form-label fw-bold mb-1">Audit Syncing:</label>
<select id="AuditSync" name="AuditSync" class="form-select">
<option value="0" <% If auditSync="0" Then Response.Write "selected" %>>Display Only (Don't alter math)</option>
<option value="1" <% If auditSync="1" Then Response.Write "selected" %>>Sync Calc. Stock to Audit (Checkpoints)</option>
</select>
</div>
</form>
</div>
</div>
<% If productCode <> "" Then %>
<% If productID = 0 Then %>
<div class="alert alert-warning mx-auto" style="max-width: 1300px;">
Product code <strong><%=Server.HTMLEncode(productCode)%></strong> was not found in the database.
</div>
<% ElseIf IsObject(rs) Then %>
<div class="d-flex align-items-center mb-4 bg-white p-3 shadow-sm rounded border mx-auto" style="max-width: 1300px;">
<img src="/getpic.asp?code=<%=Server.URLEncode(productCode)%>" alt="Product Image" class="me-3 product-header-img" onerror="this.style.display='none'">
<div>
<h3 class="mb-1 text-primary fw-bold"><%=Server.HTMLEncode(productCode)%></h3>
<% If productName <> "" Then %>
<h5 class="text-secondary mb-0"><%=Server.HTMLEncode(productName)%></h5>
<% Else %>
<h5 class="text-muted mb-0 fst-italic">No product name available</h5>
<% End If %>
</div>
</div>
<div class="table-responsive bg-white shadow-sm rounded mx-auto" style="max-width: 1300px;">
<table class="table table-hover table-bordered mb-0">
<thead class="table-dark">
<tr>
<th>Date</th>
<th>Type</th>
<th>Reference / User</th>
<th>Status / PC</th>
<th>Audit Source</th>
<th>Audit Note</th>
<th>Sys PrevStock</th>
<th>Qty Change</th>
<th>Calc. Stock</th>
</tr>
</thead>
<tbody>
<tr class="table-secondary">
<td><em><% If startDate <> "" Then Response.Write Server.HTMLEncode(startDate) Else Response.Write "Baseline" End If %></em></td>
<td colspan="7"><em><%=snapshotInfo%></em></td>
<td class="fw-bold fs-5"><%=startingStock%></td>
</tr>
<%
If rs.EOF Then
Response.Write "<tr><td colspan='9' class='text-center'>No transactions found.</td></tr>"
Else
rowCount = 0
Do While Not rs.EOF
rowCount = rowCount + 1
' FLUSH BUFFER EVERY 100 ROWS TO PREVENT IIS TIMEOUTS & MEMORY ERRORS
If rowCount Mod 100 = 0 Then Response.Flush()
If IsNull(rs("TransDate")) Then transactionDate = "Not Recorded" Else transactionDate = rs("TransDate")
transType = rs("TransType")
qty = 0
If Not IsNull(rs("Amount")) Then qty = CLng(rs("Amount"))
ref = rs("Ref")
status = rs("Status")
If Not IsNull(rs("PrevStockSys")) Then prevStockSys = CLng(rs("PrevStockSys")) Else prevStockSys = "-"
If Not IsNull(rs("StockAfterSys")) Then stockAfterSys = CLng(rs("StockAfterSys")) Else stockAfterSys = "-"
If Not IsNull(rs("IsMatchedOrder")) Then isMatchedOrder = CInt(rs("IsMatchedOrder")) Else isMatchedOrder = 0
If Not IsNull(rs("IsMatchedInvoice")) Then isMatchedInvoice = CInt(rs("IsMatchedInvoice")) Else isMatchedInvoice = 0
' --- EXTRACT NEW AUDIT COLUMNS ---
Dim sourceType, sourceRef, auditNote, restockOrderNo
If Not IsNull(rs("SourceType")) Then sourceType = rs("SourceType") Else sourceType = ""
If Not IsNull(rs("SourceRef")) Then sourceRef = rs("SourceRef") Else sourceRef = ""
If Not IsNull(rs("AuditNote")) Then auditNote = rs("AuditNote") Else auditNote = ""
If Not IsNull(rs("RestockOrderNo")) Then restockOrderNo = rs("RestockOrderNo") Else restockOrderNo = ""
' PRE-FORMAT: If we found an Order No for this Restock, append it!
If sourceType = "Restock" And restockOrderNo <> "" Then
sourceRef = sourceRef & " (Order " & restockOrderNo & ")"
End If
isDuplicate = False
If transType = "ADJ" And (isMatchedOrder = 1 Or isMatchedInvoice = 1) Then isDuplicate = True
showRow = True
If isDuplicate And auditVisibility = "3" Then showRow = False
Dim displayType, badgeClass
rowClass = ""
syncBadge = ""
cssClass = ""
' =========================================================
' 1. NEW LOGIC: Trust the explicit SourceType completely!
' =========================================================
If transType = "ADJ" And sourceType <> "" Then
ref = sourceRef
If isDuplicate And auditVisibility <> "4" Then
displayType = "ADJ (Matched " & sourceType & ")"
badgeClass = "badge bg-light text-secondary border border-secondary"
Else
If sourceType = "Invoice" Or sourceType = "Restock" Or sourceType = "Cancel" Then
displayType = "IN (" & sourceType & ")"
badgeClass = "badge bg-success"
ElseIf sourceType = "Order" Or sourceType = "Timeout" Then
displayType = "OUT (" & sourceType & ")"
badgeClass = "badge bg-danger"
Else ' Manual
displayType = "ADJ (" & sourceType & ")"
badgeClass = "badge bg-info text-dark"
End If
End If
' =========================================================
' 2. OLD LOGIC: Fallback for historical "blind" audit rows
' =========================================================
ElseIf transType = "ADJ" Then
If isDuplicate And auditVisibility <> "4" Then
displayType = "ADJ (Matched)"
badgeClass = "badge bg-light text-secondary border border-secondary"
Else
displayType = "ADJ (Audit)"
badgeClass = "badge border border-info text-dark"
End If
ElseIf transType = "IN" Then
displayType = "IN (Purchase)"
badgeClass = "badge bg-success"
ElseIf transType = "OUT" Then
displayType = "OUT (Order)"
badgeClass = "badge bg-danger"
End If
If qty <> 0 Then
If auditVisibility = "4" Then isDuplicate = False
If Not isDuplicate Then
If transType = "IN" Or transType = "ADJ" Then
currentCalculatedStock = currentCalculatedStock + qty
ElseIf transType = "OUT" Then
currentCalculatedStock = currentCalculatedStock - qty
End If
End If
If transType = "ADJ" And auditSync = "1" And stockAfterSys <> "-" Then
currentCalculatedStock = stockAfterSys
rowClass = "sync-row"
syncBadge = " <span class='badge bg-primary ms-1' title='Calculated stock was re-synced to match this audit log'>Synced</span>"
End If
If transType = "OUT" Then
displayQty = "-" & qty
Else
If qty > 0 Then displayQty = "+" & qty Else displayQty = CStr(qty)
End If
' Apply styling for Duplicates vs Primary rows
If isDuplicate Then
cssClass = "text-muted fw-bold"
rowClass = rowClass & " duplicate-row"
If isMatchedOrder = 1 And sourceType = "" Then ref = ref & " (Order Match)"
If isMatchedInvoice = 1 And sourceType = "" Then ref = ref & " (Invoice Match)"
Else
If badgeClass = "badge bg-success" Then
cssClass = "qty-in"
ElseIf badgeClass = "badge bg-danger" Then
cssClass = "qty-out"
ElseIf qty > 0 Then
cssClass = "qty-adj"
Else
cssClass = "qty-out"
End If
End If
If Not isDuplicate And rowClass = "" And prevStockSys <> "-" Then
Dim expectedStockBeforeTx : expectedStockBeforeTx = currentCalculatedStock
If transType = "OUT" Then expectedStockBeforeTx = currentCalculatedStock + qty
If transType = "ADJ" And auditSync = "0" Then expectedStockBeforeTx = currentCalculatedStock - qty
If expectedStockBeforeTx <> prevStockSys Then rowClass = "drift-warning"
End If
If showRow Then
%>
<tr class="<%=rowClass%>">
<td><%=transactionDate%></td>
<!-- RENDER THE UPGRADED BADGE -->
<td><span class="<%=badgeClass%>"><%=displayType%></span></td>
<td><%=ref%></td>
<td><span class="badge bg-secondary"><%=status%></span></td>
<!-- RENDER NEW COLUMNS -->
<td>
<% If sourceType <> "" Then %>
<span class="badge bg-light text-dark border border-dark"><%=sourceType%><% If sourceRef <> "" Then Response.Write " " & sourceRef End If %></span>
<% End If %>
</td>
<td class="text-secondary" style="max-width: 250px; font-size: 0.9em; word-wrap: break-word;">
<em><%=Server.HTMLEncode(auditNote)%></em>
</td>
<td class="text-muted"><%=prevStockSys%></td>
<td class="<%=cssClass%>"><%=displayQty%></td>
<td class="fw-bold fs-5"><%=currentCalculatedStock%><%=syncBadge%></td>
</tr>
<%
End If
End If
rs.MoveNext
Loop
End If
rs.Close
Set rs = Nothing
Set cmd = Nothing
conn.Close
Set conn = Nothing
%>
</tbody>
</table>
</div>
<% discrepancy = currentCalculatedStock - recordedNumInStock %>
<div class="card mt-4 shadow-sm mx-auto" style="max-width: 1300px;">
<div class="card-header bg-dark text-white fw-bold">
Audit Result
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item d-flex justify-content-between align-items-center">
Starting Stock Baseline
<span class="badge bg-secondary rounded-pill fs-6"><%=startingStock%></span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<strong>Expected Stock Level</strong>
<span class="badge bg-primary rounded-pill fs-6"><%=currentCalculatedStock%></span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
Database `NumInStock`
<span class="badge bg-info rounded-pill fs-6"><%=recordedNumInStock%></span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center fw-bold">
Discrepancy (Expected vs Database)
<% If discrepancy = 0 Then %>
<span class="badge bg-success rounded-pill fs-6">Match (0)</span>
<% Else %>
<span class="badge bg-warning text-dark rounded-pill fs-6"><%=discrepancy%></span>
<% End If %>
</li>
</ul>
</div>
<% End If %>
<% End If %>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
const input = document.getElementById("ProductCode");
const datalist = document.getElementById("productCodes");
let timeoutId;
input.addEventListener("input", function() {
clearTimeout(timeoutId);
const query = this.value;
if (query.length > 1) {
timeoutId = setTimeout(() => {
fetch("?action=search&q=" + encodeURIComponent(query))
.then(response => response.json())
.then(data => {
datalist.innerHTML = "";
data.forEach(item => {
let option = document.createElement("option");
option.value = item.c;
option.text = item.n; // Sets the label in the dropdown
datalist.appendChild(option);
});
})
.catch(err => console.error("Search error:", err));
}, 250);
} else {
datalist.innerHTML = "";
}
});
});
</script>
</body>
</html>