File: D:/web/circ.itp/reports/rad-reorder-gap.asp
<%@ Language="VBScript" %>
<%
Option Explicit
Const DB_DSN = "MySQL_castironradcen"
' Helper: format VBScript Date as ISO yyyy-mm-dd for HTML5 date inputs
Function FormatDateISO(d)
Dim y, m, dd
y = Year(d)
m = Month(d)
dd = Day(d)
FormatDateISO = y & "-" & Right("0" & m, 2) & "-" & Right("0" & dd, 2)
End Function
' Helper: Null to empty string
Function Nz(val)
If IsNull(val) Then
Nz = ""
Else
Nz = val
End If
End Function
Dim conn, rs1, rs2
Dim startDate2, endDate2, minGapDays, maxGapDays, hasRun
Dim today, defaultStart2, defaultEnd2, defaultMaxGap
today = Date()
defaultStart2 = FormatDateISO(DateSerial(Year(today), 1, 1)) ' start of this year
defaultEnd2 = FormatDateISO(today) ' today
defaultMaxGap = 365 ' default max gap in days
' ---- Read & validate request values (SQL-injection safe for these simple types) ----
Dim rawStart2, rawEnd2, rawMinGap, rawMaxGap
rawStart2 = Trim(Request("start_date_2"))
rawEnd2 = Trim(Request("end_date_2"))
rawMinGap = Trim(Request("min_gap_days"))
rawMaxGap = Trim(Request("max_gap_days"))
' HTML5 date input will submit yyyy-mm-dd; validate strictly with RegExp
Dim re
Set re = New RegExp
re.Pattern = "^\d{4}-\d{2}-\d{2}$"
re.IgnoreCase = False
re.Global = False
If rawStart2 = "" Then
startDate2 = defaultStart2
ElseIf re.Test(rawStart2) Then
startDate2 = rawStart2
Else
startDate2 = defaultStart2
End If
If rawEnd2 = "" Then
endDate2 = defaultEnd2
ElseIf re.Test(rawEnd2) Then
endDate2 = rawEnd2
Else
endDate2 = defaultEnd2
End If
Set re = Nothing
' Min gap (days)
If rawMinGap = "" Then
minGapDays = 5
Else
On Error Resume Next
minGapDays = CLng(rawMinGap)
If Err.Number <> 0 Or minGapDays < 0 Then minGapDays = 5
Err.Clear
On Error GoTo 0
End If
' Max gap (days)
If rawMaxGap = "" Then
maxGapDays = defaultMaxGap
Else
On Error Resume Next
maxGapDays = CLng(rawMaxGap)
If Err.Number <> 0 Or maxGapDays < 0 Then maxGapDays = defaultMaxGap
Err.Clear
On Error GoTo 0
End If
hasRun = (Request("run") <> "")
If hasRun Then
On Error Resume Next
Set conn = Server.CreateObject("ADODB.Connection")
conn.Open "DSN=" & DB_DSN & ";"
If Err.Number <> 0 Then
Response.Write "<div class=""alert alert-danger mt-3""><strong>Error opening database connection:</strong> " & _
Server.HTMLEncode(Err.Description) & "</div>"
Response.End
End If
On Error GoTo 0
' Set MySQL session variables (values already validated)
conn.Execute "SET @start_date_2 = '" & startDate2 & "'"
conn.Execute "SET @end_date_2 = '" & endDate2 & "'"
conn.Execute "SET @min_gap_days = " & CStr(minGapDays)
conn.Execute "SET @max_gap_days = " & CStr(maxGapDays)
' --- customers placing an order for 1 or more rads, no date range ---
conn.Execute "DROP TEMPORARY TABLE IF EXISTS all_rad_orders"
conn.Execute _
"CREATE TEMPORARY TABLE all_rad_orders " & _
"SELECT " & _
" ROW_NUMBER() OVER (PARTITION BY EmailAddress ORDER BY DatePaid) AS RowNo, " & _
" OrderNo, DatePaid, EmailAddress, Postcode, " & _
" SUM(QtyRads) AS QtyRads " & _
"FROM ( " & _
" SELECT o.OrderNo, EmailAddress, Postcode, DATE(DateTimePaid) AS DatePaid, " & _
" IF(AttributeValue = 'Radiator', Qty - QtyRestocked, 0) AS QtyRads " & _
" FROM orders o " & _
" INNER JOIN orderdetails od ON od.OrderNo = o.OrderNo " & _
" LEFT JOIN product_attributes pa ON pa.ProductID = od.ProductID AND pa.AttributeID = 1 " & _
" LEFT JOIN order_detail_options odo ON odo.OrderDetailID = od.OrderDetailID AND odo.OptionName = 'Paint Finish' " & _
" WHERE PaymentReceived " & _
" AND Status <> 'CANCELLED' " & _
" AND od.SubproductOrderDetailID IS NULL " & _
" AND (o.Exchange = FALSE OR INSTR(o.ExchangeReason, 'Damaged in transit') > 0) " & _
" ORDER BY OrderNo " & _
") a " & _
"GROUP BY OrderNo " & _
"HAVING QtyRads > 0 " & _
"ORDER BY EmailAddress, RowNo"
' --- first order of 1 or more rads ---
conn.Execute "DROP TEMPORARY TABLE IF EXISTS first_rad_orders"
conn.Execute _
"CREATE TEMPORARY TABLE first_rad_orders " & _
"SELECT * FROM all_rad_orders " & _
"WHERE RowNo = 1 AND DatePaid BETWEEN @start_date_2 AND @end_date_2 " & _
"ORDER BY EmailAddress, RowNo"
' --- second order of 1 or more rads ---
conn.Execute "DROP TEMPORARY TABLE IF EXISTS second_rad_orders"
conn.Execute _
"CREATE TEMPORARY TABLE second_rad_orders " & _
"SELECT * FROM all_rad_orders " & _
"WHERE RowNo = 2 AND DatePaid BETWEEN @start_date_2 AND @end_date_2 " & _
"ORDER BY EmailAddress, RowNo"
' --- join the two to select customers who placed a second order ---
conn.Execute "DROP TEMPORARY TABLE IF EXISTS first_and_second_rad_orders"
conn.Execute _
"CREATE TEMPORARY TABLE first_and_second_rad_orders " & _
"SELECT " & _
" o1.RowNo AS RowNo1, o1.DatePaid AS DateOrder1, o1.OrderNo AS OrderNo1, " & _
" o1.EmailAddress AS EmailAddress1, o1.Postcode AS Postcode1, o1.QtyRads AS QtyRads1, " & _
" o2.RowNo AS RowNo2, o2.DatePaid AS DateOrder2, o2.OrderNo AS OrderNo2, " & _
" o2.EmailAddress AS EmailAddress2, o2.Postcode AS Postcode2, o2.QtyRads AS QtyRads2, " & _
" DATEDIFF(o2.DatePaid, o1.DatePaid) AS DateDiffDays " & _
"FROM first_rad_orders o1 " & _
"INNER JOIN second_rad_orders o2 ON o1.EmailAddress = o2.EmailAddress"
' --- Result 1: all first->second orders with min/max gap ---
Set rs1 = Server.CreateObject("ADODB.Recordset")
rs1.Open _
"SELECT COUNT(*) AS Customers, " & _
" ROUND(AVG(DateDiffDays), 0) AS FirstToSecondAverageDays, " & _
" MIN(DateDiffDays) AS MinDays, " & _
" MAX(DateDiffDays) AS MaxDays " & _
"FROM first_and_second_rad_orders " & _
"WHERE DateDiffDays >= @min_gap_days " & _
" AND DateDiffDays <= @max_gap_days", _
conn, 0, 1
' --- Result 2: subset where first order had exactly 1 rad, with min/max gap ---
Set rs2 = Server.CreateObject("ADODB.Recordset")
rs2.Open _
"SELECT COUNT(*) AS Customers, " & _
" ROUND(AVG(DateDiffDays), 0) AS FirstToSecondAverageDays, " & _
" MIN(DateDiffDays) AS MinDays, " & _
" MAX(DateDiffDays) AS MaxDays " & _
"FROM first_and_second_rad_orders " & _
"WHERE DateDiffDays >= @min_gap_days " & _
" AND DateDiffDays <= @max_gap_days " & _
" AND QtyRads1 = 1", _
conn, 0, 1
End If
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Time Between First and Second Rad Orders</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
<div class="container my-4">
<h1 class="h3 mb-3">Time Between First and Second Radiator Orders</h1>
<form method="get"
action="<%= Server.HTMLEncode(Request.ServerVariables("SCRIPT_NAME")) %>"
class="row gy-2 gx-3 align-items-end border rounded p-3">
<div class="col-sm-3">
<label for="start_date_2" class="form-label">Start date</label>
<input type="date" name="start_date_2" id="start_date_2"
class="form-control"
value="<%= Server.HTMLEncode(startDate2) %>" />
</div>
<div class="col-sm-3">
<label for="end_date_2" class="form-label">End date</label>
<input type="date" name="end_date_2" id="end_date_2"
class="form-control"
value="<%= Server.HTMLEncode(endDate2) %>" />
</div>
<div class="col-sm-2">
<label for="min_gap_days" class="form-label">Min gap (days)</label>
<input type="number" name="min_gap_days" id="min_gap_days"
class="form-control"
value="<%= Server.HTMLEncode(CStr(minGapDays)) %>" />
</div>
<div class="col-sm-2">
<label for="max_gap_days" class="form-label">Max gap (days)</label>
<input type="number" name="max_gap_days" id="max_gap_days"
class="form-control"
value="<%= Server.HTMLEncode(CStr(maxGapDays)) %>" />
</div>
<div class="col-sm-2 text-sm-end">
<input type="hidden" name="run" value="1" />
<button type="submit" class="btn btn-primary w-100">
Run query
</button>
</div>
</form>
<%
If hasRun Then
If rs1 Is Nothing Or rs1.EOF Then
%>
<div class="alert alert-warning mt-3">
<strong>No data returned</strong> for the selected parameters.
</div>
<%
Else
rs1.MoveFirst
rs2.MoveFirst
%>
<table class="table table-bordered table-striped table-sm mt-4">
<thead class="table-light">
<tr>
<th scope="col">Scenario</th>
<th scope="col" class="text-end">Customers</th>
<th scope="col" class="text-end">Average gap (days)</th>
<th scope="col" class="text-end">Min gap (days)</th>
<th scope="col" class="text-end">Max gap (days)</th>
</tr>
</thead>
<tbody>
<tr>
<td>First → second order (any qty of rads)</td>
<td class="text-end"><%= Nz(rs1("Customers")) %></td>
<td class="text-end"><%= Nz(rs1("FirstToSecondAverageDays")) %></td>
<td class="text-end"><%= Nz(rs1("MinDays")) %></td>
<td class="text-end"><%= Nz(rs1("MaxDays")) %></td>
</tr>
<tr>
<td>First → second order (first order = 1 rad)</td>
<td class="text-end"><%= Nz(rs2("Customers")) %></td>
<td class="text-end"><%= Nz(rs2("FirstToSecondAverageDays")) %></td>
<td class="text-end"><%= Nz(rs2("MinDays")) %></td>
<td class="text-end"><%= Nz(rs2("MaxDays")) %></td>
</tr>
</tbody>
</table>
<%
End If
' Cleanup
If Not rs1 Is Nothing Then
If rs1.State <> 0 Then rs1.Close
Set rs1 = Nothing
End If
If Not rs2 Is Nothing Then
If rs2.State <> 0 Then rs2.Close
Set rs2 = Nothing
End If
If Not conn Is Nothing Then
If conn.State <> 0 Then conn.Close
Set conn = Nothing
End If
End If
%>
</div>
</body>
</html>