Add printer QR code printing and Windows name auto-update

- Add printerqrcode.asp for single printer QR code labels (ULINE S-20135)
- Add printerqrbatch.asp for batch printing multiple printers (6 per page)
- Add auto-update of Windows name when changing associated machine
- Windows name follows naming standard: [CSF]-[Machine]-[Vendor][Model]
- Remove UDC integrations from displaymachine.asp
- Add toner_inventory.csv for HP printer toner tracking
- Fix printerqrbatch.asp to show all printers, not just CSF ones

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2025-12-19 16:38:38 -05:00
parent 51f4c078c7
commit 84c2120652
5 changed files with 1844 additions and 1638 deletions

View File

@@ -113,23 +113,6 @@
Response.End Response.End
End If End If
' Check if machine has UDC data (only for equipment with machinenumber)
Dim rsUDCCheck, hasUDCData, strSQL2, machineNum
hasUDCData = False
machineNum = rs("machinenumber") & ""
If machineNum <> "" Then
strSQL2 = "SELECT COUNT(*) as cnt FROM udcparts p " & _
"JOIN udcsessions s ON p.sessionid = s.sessionid " & _
"WHERE s.machinenumber = ?"
Set rsUDCCheck = ExecuteParameterizedQuery(objConn, strSQL2, Array(machineNum))
If Not rsUDCCheck Is Nothing Then
If Not rsUDCCheck.EOF Then
If CLng(rsUDCCheck("cnt") & "0") > 0 Then hasUDCData = True
End If
rsUDCCheck.Close
Set rsUDCCheck = Nothing
End If
End If
%> %>
<body class="bg-theme <%=Server.HTMLEncode(theme)%>"> <body class="bg-theme <%=Server.HTMLEncode(theme)%>">
@@ -192,11 +175,6 @@
<li class="nav-item"> <li class="nav-item">
<a href="javascript:void();" data-target="#applications" data-toggle="pill" class="nav-link"><i class="zmdi zmdi-apps"></i> <span class="hidden-xs">Applications</span></a> <a href="javascript:void();" data-target="#applications" data-toggle="pill" class="nav-link"><i class="zmdi zmdi-apps"></i> <span class="hidden-xs">Applications</span></a>
</li> </li>
<% End If %>
<% If hasUDCData Then %>
<li class="nav-item">
<a href="javascript:void();" data-target="#udc" data-toggle="pill" class="nav-link"><i class="zmdi zmdi-chart"></i> <span class="hidden-xs">UDC</span></a>
</li>
<% End If %> <% End If %>
<li class="nav-item"> <li class="nav-item">
<a href="./machineedit.asp?machineid=<%=Server.HTMLEncode(machineid)%>" class="nav-link" style="background: linear-gradient(45deg, #667eea 0%, #764ba2 100%); color: white;"><i class="zmdi zmdi-edit"></i> <span class="hidden-xs">Edit Machine</span></a> <a href="./machineedit.asp?machineid=<%=Server.HTMLEncode(machineid)%>" class="nav-link" style="background: linear-gradient(45deg, #667eea 0%, #764ba2 100%); color: white;"><i class="zmdi zmdi-edit"></i> <span class="hidden-xs">Edit Machine</span></a>
@@ -819,338 +797,6 @@ End If
</table> </table>
</div> </div>
</div> </div>
<% End If %>
<% If hasUDCData Then %>
<div class="tab-pane" id="udc">
<h5 class="mb-3">UDC Performance Data</h5>
<!-- Today's Stats Summary Cards -->
<div class="row mb-4">
<%
' Get today's UDC stats for this machine
Dim rsUDCToday, todayParts, todayOOT, todayAvgCycle, todayLastBadge
strSQL2 = "SELECT COUNT(*) as partstoday, " & _
"SUM(ootcount) as oottoday, " & _
"AVG(cycletime) as avgcycle, " & _
"(SELECT badgenumber FROM udcparts p2 JOIN udcsessions s2 ON p2.sessionid = s2.sessionid " & _
" WHERE s2.machinenumber = ? ORDER BY p2.programend DESC LIMIT 1) as lastbadge " & _
"FROM udcparts p " & _
"JOIN udcsessions s ON p.sessionid = s.sessionid " & _
"WHERE s.machinenumber = ? AND DATE(p.programstart) = CURDATE()"
Set rsUDCToday = ExecuteParameterizedQuery(objConn, strSQL2, Array(rs("machinenumber") & "", rs("machinenumber") & ""))
If Not rsUDCToday.EOF Then
todayParts = CLng(rsUDCToday("partstoday") & "0")
todayOOT = CLng(rsUDCToday("oottoday") & "0")
If Not IsNull(rsUDCToday("avgcycle")) Then
todayAvgCycle = FormatNumber(CDbl(rsUDCToday("avgcycle")) / 60, 1)
Else
todayAvgCycle = "0"
End If
todayLastBadge = rsUDCToday("lastbadge") & ""
Else
todayParts = 0
todayOOT = 0
todayAvgCycle = "0"
todayLastBadge = ""
End If
rsUDCToday.Close
Set rsUDCToday = Nothing
%>
<div class="col-md-3">
<div class="card bg-success text-white">
<div class="card-body text-center">
<h3 class="mb-0"><%=todayParts%></h3>
<small>Parts Today</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-info text-white">
<div class="card-body text-center">
<h3 class="mb-0"><%=todayAvgCycle%>m</h3>
<small>Avg Cycle Time</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card <% If todayOOT > 0 Then Response.Write("bg-danger") Else Response.Write("bg-secondary") End If %> text-white">
<div class="card-body text-center">
<h3 class="mb-0"><%=todayOOT%></h3>
<small>OOT Today</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-primary text-white">
<div class="card-body text-center">
<h3 class="mb-0"><%If todayLastBadge <> "" Then Response.Write(Server.HTMLEncode(todayLastBadge)) Else Response.Write("-")%></h3>
<small>Current Operator</small>
</div>
</div>
</div>
</div>
<!-- Recent Activity Log -->
<h6 class="mb-2"><i class="zmdi zmdi-time-restore"></i> Recent Activity</h6>
<div class="table-responsive mb-4">
<table class="table table-hover table-striped table-sm">
<thead>
<tr>
<th>Time</th>
<th>Type</th>
<th>Badge</th>
<th>Details</th>
</tr>
</thead>
<tbody>
<%
' Get recent activity (violations + badge changes) for this machine
Dim rsActivity, actBadge
strSQL2 = "SELECT * FROM (" & _
"SELECT eventtime, 'Violation' as acttype, badgenumber, " & _
"CONCAT(crossingdesc, ': ', previousval, ' -> ', currentval) as details " & _
"FROM udcviolations WHERE machinenumber = ? " & _
"UNION ALL " & _
"SELECT eventtime, 'Badge Change' as acttype, badgenumber, details " & _
"FROM udcheaderupdates WHERE machinenumber = ? " & _
") combined ORDER BY eventtime DESC LIMIT 15"
Set rsActivity = ExecuteParameterizedQuery(objConn, strSQL2, Array(rs("machinenumber") & "", rs("machinenumber") & ""))
If rsActivity.EOF Then
Response.Write("<tr><td colspan='4' class='text-muted text-center'>No recent activity</td></tr>")
Else
Do While Not rsActivity.EOF
If rsActivity("acttype") = "Violation" Then
actBadge = "<span class='badge badge-light'>Setting Change</span>"
Else
actBadge = "<span class='badge badge-info'>Badge</span>"
End If
Response.Write("<tr>")
Response.Write("<td class='small'>" & Server.HTMLEncode(rsActivity("eventtime") & "") & "</td>")
Response.Write("<td>" & actBadge & "</td>")
Response.Write("<td>" & Server.HTMLEncode(rsActivity("badgenumber") & "") & "</td>")
Response.Write("<td class='small'>" & Server.HTMLEncode(rsActivity("details") & "") & "</td>")
Response.Write("</tr>")
rsActivity.MoveNext
Loop
End If
rsActivity.Close
Set rsActivity = Nothing
%>
</tbody>
</table>
</div>
<!-- Tool Health Section -->
<h6 class="mb-2 mt-4"><i class="zmdi zmdi-settings"></i> Tool Health</h6>
<%
' Get tool health summary for this machine (last 30 days)
Dim rsToolSummary, toolCount, toolMeasurements, toolOOT, toolLastCheck
strSQL2 = "SELECT COUNT(DISTINCT t.toolnumber) as unique_tools, " & _
"COUNT(*) as total_measurements, " & _
"SUM(t.oot) as oot_count, " & _
"MAX(t.eventtime) as last_check " & _
"FROM udctooldata t " & _
"JOIN udcsessions s ON t.sessionid = s.sessionid " & _
"WHERE s.machinenumber = ? AND t.eventtime >= DATE_SUB(NOW(), INTERVAL 30 DAY)"
Set rsToolSummary = ExecuteParameterizedQuery(objConn, strSQL2, Array(rs("machinenumber") & ""))
If Not rsToolSummary.EOF Then
toolCount = CLng(rsToolSummary("unique_tools") & "0")
toolMeasurements = CLng(rsToolSummary("total_measurements") & "0")
toolOOT = CLng(rsToolSummary("oot_count") & "0")
toolLastCheck = rsToolSummary("last_check") & ""
Else
toolCount = 0
toolMeasurements = 0
toolOOT = 0
toolLastCheck = ""
End If
rsToolSummary.Close
Set rsToolSummary = Nothing
If toolMeasurements > 0 Then
%>
<!-- Tool Health Summary Cards -->
<div class="row mb-3">
<div class="col-md-3">
<div class="card bg-secondary text-white">
<div class="card-body text-center py-2">
<h4 class="mb-0"><%=toolCount%></h4>
<small>Tools Monitored</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-info text-white">
<div class="card-body text-center py-2">
<h4 class="mb-0"><%=toolMeasurements%></h4>
<small>Measurements (30d)</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card <% If toolOOT > 0 Then Response.Write("bg-danger") Else Response.Write("bg-success") End If %> text-white">
<div class="card-body text-center py-2">
<h4 class="mb-0"><%=toolOOT%></h4>
<small>Out of Tolerance</small>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card bg-primary text-white">
<div class="card-body text-center py-2">
<h6 class="mb-0"><%If toolLastCheck <> "" Then Response.Write(Server.HTMLEncode(Left(toolLastCheck, 16))) Else Response.Write("-")%></h6>
<small>Last Tool Check</small>
</div>
</div>
</div>
</div>
<!-- Tool Status Table -->
<div class="table-responsive mb-3">
<table class="table table-hover table-striped table-sm">
<thead>
<tr>
<th>Tool #</th>
<th>Description</th>
<th>Checks</th>
<th>Avg Dev</th>
<th>Max Dev</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<%
' Get tool status by tool number
Dim rsTools, toolStatus, toolStatusClass, toolAvgDev, toolMaxDev, toolDevPct
strSQL2 = "SELECT t.toolnumber, " & _
"MAX(t.description) as description, " & _
"COUNT(*) as measurements, " & _
"ROUND(AVG(t.deviation), 4) as avg_deviation, " & _
"ROUND(MAX(ABS(t.deviation)), 4) as max_deviation, " & _
"MAX(ABS(t.minval)) as tolerance_ref, " & _
"SUM(t.oot) as oot_count " & _
"FROM udctooldata t " & _
"JOIN udcsessions s ON t.sessionid = s.sessionid " & _
"WHERE s.machinenumber = ? AND t.eventtime >= DATE_SUB(NOW(), INTERVAL 30 DAY) " & _
"GROUP BY t.toolnumber " & _
"ORDER BY oot_count DESC, measurements DESC " & _
"LIMIT 10"
Set rsTools = ExecuteParameterizedQuery(objConn, strSQL2, Array(rs("machinenumber") & ""))
If rsTools.EOF Then
Response.Write("<tr><td colspan='6' class='text-muted text-center'>No tool data available</td></tr>")
Else
Do While Not rsTools.EOF
' Calculate status based on OOT and deviation
If CLng(rsTools("oot_count") & "0") > 0 Then
toolStatus = "<i class='zmdi zmdi-alert-circle'></i> <strong>OOT</strong>"
toolStatusClass = "bg-danger text-white"
Else
toolStatus = "<i class='zmdi zmdi-check-circle text-success'></i> OK"
toolStatusClass = ""
End If
If Not IsNull(rsTools("avg_deviation")) Then
toolAvgDev = FormatNumber(CDbl(rsTools("avg_deviation")), 4)
Else
toolAvgDev = "-"
End If
If Not IsNull(rsTools("max_deviation")) Then
toolMaxDev = FormatNumber(CDbl(rsTools("max_deviation")), 4)
Else
toolMaxDev = "-"
End If
Response.Write("<tr class='" & toolStatusClass & "'>")
Response.Write("<td><strong>" & Server.HTMLEncode(rsTools("toolnumber") & "") & "</strong></td>")
Response.Write("<td class='small'>" & Server.HTMLEncode(Left(rsTools("description") & "", 30)) & "</td>")
Response.Write("<td>" & rsTools("measurements") & "</td>")
Response.Write("<td>" & toolAvgDev & "</td>")
Response.Write("<td>" & toolMaxDev & "</td>")
Response.Write("<td>" & toolStatus & "</td>")
Response.Write("</tr>")
rsTools.MoveNext
Loop
End If
rsTools.Close
Set rsTools = Nothing
%>
</tbody>
</table>
</div>
<%
' Check for recent OOT events
Dim rsOOT, ootEventCount
strSQL2 = "SELECT COUNT(*) as cnt FROM udctooldata t " & _
"JOIN udcsessions s ON t.sessionid = s.sessionid " & _
"WHERE s.machinenumber = ? AND t.oot = 1 AND t.eventtime >= DATE_SUB(NOW(), INTERVAL 7 DAY)"
Set rsOOT = ExecuteParameterizedQuery(objConn, strSQL2, Array(rs("machinenumber") & ""))
ootEventCount = 0
If Not rsOOT.EOF Then ootEventCount = CLng(rsOOT("cnt") & "0")
rsOOT.Close
Set rsOOT = Nothing
If ootEventCount > 0 Then
%>
<!-- Recent OOT Events -->
<h6 class="mb-2"><i class="zmdi zmdi-alert-triangle text-warning"></i> Recent Out-of-Tolerance Events (7 days)</h6>
<div class="table-responsive mb-3">
<table class="table table-hover table-sm">
<thead class="thead-light">
<tr>
<th>Time</th>
<th>Tool #</th>
<th>Description</th>
<th>Actual</th>
<th>Min/Max</th>
<th>Deviation</th>
</tr>
</thead>
<tbody>
<%
Dim rsOOTEvents
strSQL2 = "SELECT t.eventtime, t.toolnumber, t.description, " & _
"t.actualval, t.minval, t.maxval, t.deviation " & _
"FROM udctooldata t " & _
"JOIN udcsessions s ON t.sessionid = s.sessionid " & _
"WHERE s.machinenumber = ? AND t.oot = 1 AND t.eventtime >= DATE_SUB(NOW(), INTERVAL 7 DAY) " & _
"ORDER BY t.eventtime DESC LIMIT 10"
Set rsOOTEvents = ExecuteParameterizedQuery(objConn, strSQL2, Array(rs("machinenumber") & ""))
Do While Not rsOOTEvents.EOF
Response.Write("<tr class='bg-warning'>")
Response.Write("<td class='small'>" & Server.HTMLEncode(rsOOTEvents("eventtime") & "") & "</td>")
Response.Write("<td><strong>" & Server.HTMLEncode(rsOOTEvents("toolnumber") & "") & "</strong></td>")
Response.Write("<td class='small'>" & Server.HTMLEncode(Left(rsOOTEvents("description") & "", 25)) & "</td>")
Response.Write("<td>" & FormatNumber(CDbl(rsOOTEvents("actualval") & "0"), 4) & "</td>")
Response.Write("<td class='small'>" & FormatNumber(CDbl(rsOOTEvents("minval") & "0"), 4) & " / " & FormatNumber(CDbl(rsOOTEvents("maxval") & "0"), 4) & "</td>")
Response.Write("<td class='text-danger'><strong>" & FormatNumber(CDbl(rsOOTEvents("deviation") & "0"), 4) & "</strong></td>")
Response.Write("</tr>")
rsOOTEvents.MoveNext
Loop
rsOOTEvents.Close
Set rsOOTEvents = Nothing
%>
</tbody>
</table>
</div>
<%
End If ' ootEventCount > 0
Else ' toolMeasurements = 0
%>
<div class="alert alert-secondary">
<i class="zmdi zmdi-info-outline"></i> No tool measurement data available for this machine.
</div>
<%
End If ' toolMeasurements > 0
%>
<!-- Link to Full Dashboard -->
<div class="text-center">
<a href="./displayudc.asp?machine=<%=Server.URLEncode(rs("machinenumber") & "")%>" class="btn btn-primary">
<i class="zmdi zmdi-chart"></i> View Full UDC Dashboard
</a>
</div>
</div>
<% End If %> <% End If %>
</div> </div>
</div> </div>

View File

@@ -86,6 +86,7 @@
<img src="./images/printers/<%=Server.HTMLEncode(rs("image") & "")%>" alt="profile-image" class="profile"> <img src="./images/printers/<%=Server.HTMLEncode(rs("image") & "")%>" alt="profile-image" class="profile">
<h5 class="card-title"><%=Server.HTMLEncode(rs("vendor") & "")%></h5> <h5 class="card-title"><%=Server.HTMLEncode(rs("vendor") & "")%></h5>
<p class="card-text"><a href="<%=Server.HTMLEncode(rs("documentationpath") & "")%>" title="Click to Access Support Docs" target="_blank"><%=Server.HTMLEncode(rs("modelnumber") & "")%></a></p> <p class="card-text"><a href="<%=Server.HTMLEncode(rs("documentationpath") & "")%>" title="Click to Access Support Docs" target="_blank"><%=Server.HTMLEncode(rs("modelnumber") & "")%></a></p>
<a href="./printerqrcode.asp?printerid=<%=Server.HTMLEncode(printerid)%>" target="_blank" class="btn btn-primary btn-sm mt-3">Print QR Code</a>
</div> </div>
</div> </div>
</div> </div>
@@ -489,7 +490,8 @@ End If
<div class="form-group row"> <div class="form-group row">
<label class="col-lg-3 col-form-label form-control-label">Windows Name:</label> <label class="col-lg-3 col-form-label form-control-label">Windows Name:</label>
<div class="col-lg-9"> <div class="col-lg-9">
<input class="form-control" type="text" name="printerwindowsname" value="<%=Server.HTMLEncode(rs("printerwindowsname") & "")%>" placeholder="<%=Server.HTMLEncode(rs("printerwindowsname") & "")%>"> <input class="form-control" type="text" id="printerwindowsname" name="printerwindowsname" value="<%=Server.HTMLEncode(rs("printerwindowsname") & "")%>" placeholder="<%=Server.HTMLEncode(rs("printerwindowsname") & "")%>">
<small class="form-text text-muted">Auto-updates when you change the associated machine</small>
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
@@ -502,8 +504,8 @@ End If
<div class="form-group row"> <div class="form-group row">
<label class="col-lg-3 col-form-label form-control-label">Associated Machine <span class="text-muted">(Optional)</span></label> <label class="col-lg-3 col-form-label form-control-label">Associated Machine <span class="text-muted">(Optional)</span></label>
<div class="col-lg-9"> <div class="col-lg-9">
<select name="machineid" class="form-control"> <select name="machineid" id="machineid" class="form-control" onchange="updateWindowsName()">
<option value='<%=Server.HTMLEncode(rs("machineid") & "")%>'><%=Server.HTMLEncode(rs("machinenumber") & "")%></option> <option value='<%=Server.HTMLEncode(rs("machineid") & "")%>' data-machinenumber='<%=Server.HTMLEncode(rs("machinenumber") & "")%>'><%=Server.HTMLEncode(rs("machinenumber") & "")%></option>
<% <%
' SECURITY: Query is safe (no user input), HTML encode output ' SECURITY: Query is safe (no user input), HTML encode output
' Get all machines for dropdown ' Get all machines for dropdown
@@ -528,7 +530,9 @@ End If
' Only output if we have display text ' Only output if we have display text
If displayName <> "" Then If displayName <> "" Then
Response.Write("<option value='" & Server.HTMLEncode(rsMachines("machineid") & "") & "'>" & Server.HTMLEncode(displayName) & "</option>") Dim machineNumOnly
machineNumOnly = Trim(rsMachines("machinenumber") & "")
Response.Write("<option value='" & Server.HTMLEncode(rsMachines("machineid") & "") & "' data-machinenumber='" & Server.HTMLEncode(machineNumOnly) & "'>" & Server.HTMLEncode(displayName) & "</option>")
End If End If
End If End If
rsMachines.MoveNext rsMachines.MoveNext
@@ -1272,6 +1276,75 @@ End If
document.getElementById('fqdn').value = fqdn; document.getElementById('fqdn').value = fqdn;
} }
} }
// Store current printer info for Windows name generation
var printerVendor = '<%=Replace(Server.HTMLEncode(rs("vendor") & ""), "'", "\'")%>';
var printerModel = '<%=Replace(Server.HTMLEncode(rs("modelnumber") & ""), "'", "\'")%>';
var printerCSFName = '<%=Replace(Server.HTMLEncode(rs("printercsfname") & ""), "'", "\'")%>';
// Auto-update Windows name when associated machine changes
function updateWindowsName() {
var machineSelect = document.getElementById('machineid');
var selectedOption = machineSelect.options[machineSelect.selectedIndex];
var machineNumber = selectedOption.getAttribute('data-machinenumber') || '';
if (machineNumber) {
// Clean machine name (remove spaces)
var cleanMachine = machineNumber.replace(/\s+/g, '').replace('Machine', '');
// Clean model name (remove spaces)
var cleanModel = printerModel.replace(/\s+/g, '');
// Extract short description from model
var shortDescription = '';
if (cleanModel.indexOf('ColorLaserJet') > -1) {
shortDescription = 'ColorLaserJet';
} else if (cleanModel.indexOf('LaserJetPro') > -1) {
shortDescription = 'LaserJetPro';
} else if (cleanModel.indexOf('LaserJet') > -1) {
shortDescription = 'LaserJet';
} else if (cleanModel.indexOf('Altalink') > -1) {
shortDescription = 'Altalink';
} else if (cleanModel.indexOf('Versalink') > -1) {
shortDescription = 'Versalink';
} else if (cleanModel.indexOf('DesignJet') > -1) {
shortDescription = 'DesignJet';
} else if (cleanModel.indexOf('DTC') > -1) {
shortDescription = 'DTC';
} else {
// Extract letters until first digit
for (var i = 0; i < cleanModel.length; i++) {
var c = cleanModel.charAt(i);
if (c >= '0' && c <= '9') break;
shortDescription += c;
}
if (shortDescription === '') shortDescription = cleanModel;
}
// Build standard name following installprinter.asp logic
var windowsName = '';
var csfName = printerCSFName.trim();
if (csfName !== '' && csfName !== 'NONE' && csfName.toLowerCase() !== 'gage lab ') {
if (cleanMachine !== '' && csfName.toLowerCase() !== cleanMachine.toLowerCase()) {
// CSFName-MachineNumber-VendorModel
windowsName = csfName + '-' + cleanMachine + '-' + printerVendor + shortDescription;
} else {
// CSFName-VendorModel
windowsName = csfName + '-' + printerVendor + shortDescription;
}
} else {
if (cleanMachine !== '') {
// MachineNumber-VendorModel
windowsName = cleanMachine + '-' + printerVendor + shortDescription;
} else {
windowsName = 'Printer-' + printerVendor + shortDescription;
}
}
document.getElementById('printerwindowsname').value = windowsName;
}
}
</script> </script>
</body> </body>

284
printerqrbatch.asp Normal file
View File

@@ -0,0 +1,284 @@
<%@ Language=VBScript %>
<%
Option Explicit
Dim objConn, rs
%>
<!DOCTYPE html>
<html>
<head>
<!--#include file="./includes/sql.asp"-->
<title>Batch Print QR Codes</title>
<script src="https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js"></script>
<style>
/* ULINE S-20135: 8.5x11 sheet, 3x3 labels, 2 cols x 3 rows */
/* Measured: top 0.875in, sides 1.1875in, gaps 0.125in */
@page { size: letter; margin: 0; }
body { font-family: Arial, sans-serif; background: #f0f0f0; margin: 0; padding: 20px; }
.no-print { margin-bottom: 20px; }
.controls { background: white; padding: 20px; border-radius: 8px; margin-bottom: 20px; }
.controls h3 { margin-top: 0; }
.print-btn { padding: 10px 30px; font-size: 16px; cursor: pointer; background: #667eea; color: white; border: none; border-radius: 5px; margin-right: 10px; }
.print-btn:disabled { background: #ccc; cursor: not-allowed; }
.clear-btn { padding: 10px 20px; font-size: 14px; cursor: pointer; background: #dc3545; color: white; border: none; border-radius: 5px; margin-right: 10px; }
.select-all-btn { padding: 10px 20px; font-size: 14px; cursor: pointer; background: #28a745; color: white; border: none; border-radius: 5px; }
.printer-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 10px; max-height: 300px; overflow-y: auto; border: 1px solid #ddd; padding: 10px; background: #fafafa; }
.printer-item { display: flex; align-items: center; padding: 8px; background: white; border: 1px solid #ddd; border-radius: 4px; cursor: pointer; }
.printer-item:hover { background: #f0f0f0; }
.printer-item.selected { background: #e7f1ff; border-color: #667eea; }
.printer-item input { margin-right: 10px; }
.printer-item label { cursor: pointer; flex: 1; }
.printer-item .model { font-size: 11px; color: #666; }
.selected-count { font-weight: bold; margin: 10px 0; }
.selected-count .count { color: #667eea; }
.selected-count .pages { color: #28a745; }
.sheets-container { display: flex; flex-direction: column; gap: 20px; }
.print-sheet { width: 8.5in; height: 11in; background: white; margin: 0 auto; position: relative; border: 1px solid #ccc; page-break-after: always; }
.print-sheet:last-child { page-break-after: auto; }
.sheet-label { position: absolute; top: -25px; left: 0; font-size: 12px; color: #666; }
.label {
width: 3in;
height: 3in;
position: absolute;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0.1in;
box-sizing: border-box;
border: 1px dashed #ccc;
}
.label.filled { border: 2px solid #667eea; }
.label.empty { background: #fafafa; }
/* Label positions - top: 0.875in, left: 1.1875in, gap: 0.125in */
.pos-1 { top: 0.875in; left: 1.1875in; }
.pos-2 { top: 0.875in; left: 4.3125in; }
.pos-3 { top: 4in; left: 1.1875in; }
.pos-4 { top: 4in; left: 4.3125in; }
.pos-5 { top: 7.125in; left: 1.1875in; }
.pos-6 { top: 7.125in; left: 4.3125in; }
.model-name { font-size: 11pt; font-weight: bold; text-align: center; margin-bottom: 0.1in; color: #000; }
.qr-container { text-align: center; }
.info-section { margin-top: 0.1in; display: flex; flex-direction: column; align-items: center; }
.info-inner { text-align: left; }
.info-row { font-size: 9pt; color: #333; margin: 1px 0; white-space: nowrap; }
.csf-name { font-size: 12pt; font-weight: bold; font-family: monospace; text-align: center; margin-bottom: 2px; }
.empty-label { color: #999; font-size: 14px; }
@media print {
body { padding: 0; margin: 0; background: white; }
.no-print { display: none; }
.sheets-container { gap: 0; }
.print-sheet { border: none; margin: 0; width: 8.5in; height: 11in; overflow: hidden; }
.sheet-label { display: none; }
.label { border: none !important; }
.label.empty { visibility: hidden; }
}
</style>
</head>
<body>
<%
Dim strSQL
strSQL = "SELECT p.printerid, p.printercsfname, p.printerwindowsname, p.ipaddress, m.modelnumber " & _
"FROM printers p LEFT JOIN models m ON p.modelid = m.modelnumberid " & _
"WHERE p.isactive = 1 " & _
"ORDER BY COALESCE(NULLIF(p.printercsfname, ''), p.printerwindowsname, CONCAT('Printer-', p.printerid))"
Set rs = objConn.Execute(strSQL)
%>
<div class="no-print">
<div class="controls">
<h3>Batch Print Printer QR Codes</h3>
<p>Select printers to print (6 per page):</p>
<div class="printer-grid">
<%
Dim displayName
Do While Not rs.EOF
displayName = rs("printercsfname") & ""
If displayName = "" Then
displayName = rs("printerwindowsname") & ""
End If
If displayName = "" Then
displayName = "Printer-" & rs("printerid")
End If
%>
<div class="printer-item" onclick="togglePrinter(this, <%=rs("printerid")%>, '<%=Server.HTMLEncode(Replace(rs("printercsfname") & "", "'", "\'"))%>', '<%=Server.HTMLEncode(Replace(rs("printerwindowsname") & "", "'", "\'"))%>', '<%=Server.HTMLEncode(Replace(rs("ipaddress") & "", "'", "\'"))%>', '<%=Server.HTMLEncode(Replace(rs("modelnumber") & "", "'", "\'"))%>')">
<input type="checkbox" id="printer-<%=rs("printerid")%>">
<label>
<strong><%=Server.HTMLEncode(displayName)%></strong>
<div class="model"><%=Server.HTMLEncode(rs("modelnumber") & "")%></div>
</label>
</div>
<%
rs.MoveNext
Loop
rs.Close
Set rs = Nothing
objConn.Close
%>
</div>
<div class="selected-count">
Selected: <span class="count" id="selectedCount">0</span> printers
(<span class="pages" id="pageCount">0</span> pages)
</div>
<button class="print-btn" id="printBtn" onclick="window.print()" disabled>Print QR Codes</button>
<button class="clear-btn" onclick="clearSelection()">Clear All</button>
<button class="select-all-btn" onclick="selectAll()">Select All</button>
</div>
</div>
<div class="sheets-container" id="sheetsContainer">
<!-- Sheets will be generated dynamically -->
</div>
<script>
var selectedPrinters = [];
var allPrinters = [];
// Store all printer data on load
document.querySelectorAll('.printer-item').forEach(function(el) {
var onclick = el.getAttribute('onclick');
var match = onclick.match(/togglePrinter\(this, (\d+), '([^']*)', '([^']*)', '([^']*)', '([^']*)'\)/);
if (match) {
allPrinters.push({
id: parseInt(match[1]),
csf: match[2].replace(/\\'/g, "'"),
win: match[3].replace(/\\'/g, "'"),
ip: match[4].replace(/\\'/g, "'"),
model: match[5].replace(/\\'/g, "'"),
element: el
});
}
});
function togglePrinter(el, id, csf, win, ip, model) {
var checkbox = el.querySelector('input[type="checkbox"]');
var index = selectedPrinters.findIndex(function(p) { return p.id === id; });
if (index > -1) {
selectedPrinters.splice(index, 1);
checkbox.checked = false;
el.classList.remove('selected');
} else {
selectedPrinters.push({ id: id, csf: csf, win: win, ip: ip, model: model });
checkbox.checked = true;
el.classList.add('selected');
}
updateSheets();
}
function clearSelection() {
selectedPrinters = [];
document.querySelectorAll('.printer-item').forEach(function(el) {
el.classList.remove('selected');
el.querySelector('input[type="checkbox"]').checked = false;
});
updateSheets();
}
function selectAll() {
selectedPrinters = [];
allPrinters.forEach(function(p) {
selectedPrinters.push({ id: p.id, csf: p.csf, win: p.win, ip: p.ip, model: p.model });
p.element.classList.add('selected');
p.element.querySelector('input[type="checkbox"]').checked = true;
});
updateSheets();
}
function updateSheets() {
var numPrinters = selectedPrinters.length;
var numPages = Math.ceil(numPrinters / 6) || 0;
document.getElementById('selectedCount').textContent = numPrinters;
document.getElementById('pageCount').textContent = numPages;
document.getElementById('printBtn').disabled = numPrinters === 0;
var container = document.getElementById('sheetsContainer');
container.innerHTML = '';
if (numPrinters === 0) {
return;
}
for (var page = 0; page < numPages; page++) {
var sheet = document.createElement('div');
sheet.className = 'print-sheet';
sheet.style.position = 'relative';
var sheetLabel = document.createElement('div');
sheetLabel.className = 'sheet-label';
sheetLabel.textContent = 'Page ' + (page + 1) + ' of ' + numPages;
sheet.appendChild(sheetLabel);
for (var pos = 1; pos <= 6; pos++) {
var printerIndex = (page * 6) + (pos - 1);
var printer = selectedPrinters[printerIndex];
var label = document.createElement('div');
label.className = 'label pos-' + pos + (printer ? ' filled' : ' empty');
if (printer) {
var qrId = 'qr-' + page + '-' + pos;
label.innerHTML =
'<div class="model-name">' + escapeHtml(printer.model) + '</div>' +
'<div class="qr-container"><div id="' + qrId + '"></div></div>' +
'<div class="info-section">' +
'<div class="csf-name">' + escapeHtml(printer.csf) + '</div>' +
'<div class="info-inner">' +
(printer.win ? '<div class="info-row">' + escapeHtml(printer.win) + '</div>' : '') +
(printer.ip ? '<div class="info-row">' + escapeHtml(printer.ip) + '</div>' : '') +
'</div>' +
'</div>';
} else {
label.innerHTML = '<span class="empty-label">Empty</span>';
}
sheet.appendChild(label);
}
container.appendChild(sheet);
// Generate QR codes for this sheet
for (var pos = 1; pos <= 6; pos++) {
var printerIndex = (page * 6) + (pos - 1);
var printer = selectedPrinters[printerIndex];
if (printer) {
var qrId = 'qr-' + page + '-' + pos;
var qrEl = document.getElementById(qrId);
if (qrEl) {
var qrUrl = 'https://tsgwp00525.rd.ds.ge.com/shopdb/displayprinter.asp?printerid=' + printer.id;
new QRCode(qrEl, {
text: qrUrl,
width: 144,
height: 144,
correctLevel: QRCode.CorrectLevel.M
});
}
}
}
}
}
function escapeHtml(text) {
if (!text) return '';
var div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
</script>
</body>
</html>

154
printerqrcode.asp Normal file
View File

@@ -0,0 +1,154 @@
<%@ Language=VBScript %>
<%
Option Explicit
Dim objConn, rs
%>
<!DOCTYPE html>
<html>
<head>
<!--#include file="./includes/sql.asp"-->
<title>Print QR Code</title>
<script src="https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js"></script>
<style>
/* ULINE S-20135: 8.5x11 sheet, 3x3 labels, 2 cols x 3 rows */
/* Measured: top 0.875in, sides 1.1875in, gaps 0.125in */
@page { size: letter; margin: 0; }
body { font-family: Arial, sans-serif; background: #f0f0f0; margin: 0; padding: 20px; }
.no-print { margin-bottom: 20px; text-align: center; }
.print-btn { padding: 10px 30px; font-size: 16px; cursor: pointer; background: #667eea; color: white; border: none; border-radius: 5px; margin: 5px; }
.position-select { padding: 8px; font-size: 14px; margin-left: 10px; }
.print-sheet { width: 8.5in; height: 11in; background: white; margin: 0 auto; position: relative; border: 1px solid #ccc; }
.label {
width: 3in;
height: 3in;
position: absolute;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 0.1in;
box-sizing: border-box;
}
/* Label positions - top: 0.875in, left: 1.1875in, gap: 0.125in */
.pos-1 { top: 0.875in; left: 1.1875in; }
.pos-2 { top: 0.875in; left: 4.3125in; }
.pos-3 { top: 4in; left: 1.1875in; }
.pos-4 { top: 4in; left: 4.3125in; }
.pos-5 { top: 7.125in; left: 1.1875in; }
.pos-6 { top: 7.125in; left: 4.3125in; }
.label.inactive { border: 1px dashed #ccc; }
.label.active { border: 2px solid #667eea; }
.model-name { font-size: 11pt; font-weight: bold; text-align: center; margin-bottom: 0.1in; color: #000; }
.qr-container { text-align: center; }
.info-section { margin-top: 0.1in; display: flex; flex-direction: column; align-items: center; }
.info-inner { text-align: left; }
.info-row { font-size: 9pt; color: #333; margin: 1px 0; white-space: nowrap; }
.csf-name { font-size: 12pt; font-weight: bold; font-family: monospace; text-align: center; margin-bottom: 2px; }
@media print {
body { padding: 0; margin: 0; background: white; }
.no-print { display: none; }
.print-sheet { border: none; margin: 0; width: 8.5in; height: 11in; overflow: hidden; }
.label { border: none !important; }
.label.inactive { visibility: hidden; }
}
</style>
</head>
<body>
<%
Dim printerid, strSQL, csfName, winName, ipAddr, modelName
printerid = Request.QueryString("printerid")
If printerid = "" Then printerid = "0"
If Not IsNumeric(printerid) Then printerid = "0"
strSQL = "SELECT p.printercsfname, p.printerwindowsname, p.ipaddress, mo.modelnumber FROM printers p LEFT JOIN models mo ON p.modelid = mo.modelnumberid WHERE p.printerid = " & CLng(printerid)
Set rs = objConn.Execute(strSQL)
If rs.EOF Then
csfName = "NOT FOUND"
winName = ""
ipAddr = ""

49
toner_inventory.csv Normal file
View File

@@ -0,0 +1,49 @@
Toner Model,HP Part Number,Color,Quantity,Location,Compatible Printers,Notes
HP 42A,Q5942A,Black,0,,LaserJet 4250/4350,Standard yield ~10000 pages
HP 42X,Q5942X,Black,0,,LaserJet 4250/4350,High yield ~20000 pages
HP 90A,CE390A,Black,0,,LaserJet M601/M602/M603/M4555,Standard yield ~10000 pages
HP 90X,CE390X,Black,0,,LaserJet M602/M603/M4555,High yield ~24000 pages
HP 37A,CF237A,Black,0,,LaserJet M607/M608/M609/M631/M632/M633,Standard yield ~11000 pages
HP 37X,CF237X,Black,0,,LaserJet M607/M608/M609/M631/M632/M633,High yield ~25000 pages
HP 148A,W1480A,Black,0,,LaserJet Pro 4001/4002/4003/4004,Standard yield ~2900 pages
HP 148X,W1480X,Black,0,,LaserJet Pro 4001/4002/4003/4004,High yield ~9500 pages
HP 87A,CF287A,Black,0,,LaserJet M501/M506/M527,Standard yield ~9000 pages
HP 87X,CF287X,Black,0,,LaserJet M501/M506/M527,High yield ~18000 pages
HP 147A,W1470A,Black,0,,LaserJet M610/M611/M612/M406/M407,Standard yield ~10500 pages
HP 147X,W1470X,Black,0,,LaserJet M610/M611/M612/M406/M407,High yield ~25200 pages
HP 55A,CE255A,Black,0,,LaserJet P3015/M521/M525,Standard yield ~6000 pages
HP 55X,CE255X,Black,0,,LaserJet P3015/M521/M525,High yield ~12500 pages
HP 58A,CF258A,Black,0,,LaserJet Pro M404/M428,Standard yield ~3000 pages
HP 58X,CF258X,Black,0,,LaserJet Pro M404/M428,High yield ~10000 pages
HP 414A,W2020A,Black,0,,Color LaserJet Pro M454/M479,Standard yield ~2400 pages
HP 414A,W2021A,Cyan,0,,Color LaserJet Pro M454/M479,Standard yield ~2100 pages
HP 414A,W2022A,Yellow,0,,Color LaserJet Pro M454/M479,Standard yield ~2100 pages
HP 414A,W2023A,Magenta,0,,Color LaserJet Pro M454/M479,Standard yield ~2100 pages
HP 414X,W2020X,Black,0,,Color LaserJet Pro M454/M479,High yield ~7500 pages
HP 414X,W2021X,Cyan,0,,Color LaserJet Pro M454/M479,High yield ~6000 pages
HP 414X,W2022X,Yellow,0,,Color LaserJet Pro M454/M479,High yield ~6000 pages
HP 414X,W2023X,Magenta,0,,Color LaserJet Pro M454/M479,High yield ~6000 pages
HP 206A,W2110A,Black,0,,Color LaserJet Pro M254/M255/M282/M283,Standard yield ~1350 pages
HP 206A,W2111A,Cyan,0,,Color LaserJet Pro M254/M255/M282/M283,Standard yield ~1250 pages
HP 206A,W2112A,Yellow,0,,Color LaserJet Pro M254/M255/M282/M283,Standard yield ~1250 pages
HP 206A,W2113A,Magenta,0,,Color LaserJet Pro M254/M255/M282/M283,Standard yield ~1250 pages
HP 206X,W2110X,Black,0,,Color LaserJet Pro M254/M255/M282/M283,High yield ~3150 pages
HP 206X,W2111X,Cyan,0,,Color LaserJet Pro M254/M255/M282/M283,High yield ~2450 pages
HP 206X,W2112X,Yellow,0,,Color LaserJet Pro M254/M255/M282/M283,High yield ~2450 pages
HP 206X,W2113X,Magenta,0,,Color LaserJet Pro M254/M255/M282/M283,High yield ~2450 pages
HP 201A,CF400A,Black,0,,Color LaserJet Pro M252/M277,Standard yield ~1500 pages
HP 201A,CF401A,Cyan,0,,Color LaserJet Pro M252/M277,Standard yield ~1400 pages
HP 201A,CF402A,Yellow,0,,Color LaserJet Pro M252/M277,Standard yield ~1400 pages
HP 201A,CF403A,Magenta,0,,Color LaserJet Pro M252/M277,Standard yield ~1400 pages
HP 201X,CF400X,Black,0,,Color LaserJet Pro M252/M277,High yield ~2800 pages
HP 201X,CF401X,Cyan,0,,Color LaserJet Pro M252/M277,High yield ~2300 pages
HP 201X,CF402X,Yellow,0,,Color LaserJet Pro M252/M277,High yield ~2300 pages
HP 201X,CF403X,Magenta,0,,Color LaserJet Pro M252/M277,High yield ~2300 pages
HP 304A,CC530A,Black,0,,Color LaserJet CP2025/CM2320,Standard yield ~3500 pages
HP 304A,CC531A,Cyan,0,,Color LaserJet CP2025/CM2320,Standard yield ~2800 pages
HP 304A,CC532A,Yellow,0,,Color LaserJet CP2025/CM2320,Standard yield ~2800 pages
HP 304A,CC533A,Magenta,0,,Color LaserJet CP2025/CM2320,Standard yield ~2800 pages
HP 131A,CF210A,Black,0,,LaserJet Pro 200 M251/M276,Standard yield ~1600 pages
HP 131A,CF211A,Cyan,0,,LaserJet Pro 200 M251/M276,Standard yield ~1800 pages
HP 131A,CF212A,Yellow,0,,LaserJet Pro 200 M251/M276,Standard yield ~1800 pages
HP 131A,CF213A,Magenta,0,,LaserJet Pro 200 M251/M276,Standard yield ~1800 pages
1 Toner Model HP Part Number Color Quantity Location Compatible Printers Notes
2 HP 42A Q5942A Black 0 LaserJet 4250/4350 Standard yield ~10000 pages
3 HP 42X Q5942X Black 0 LaserJet 4250/4350 High yield ~20000 pages
4 HP 90A CE390A Black 0 LaserJet M601/M602/M603/M4555 Standard yield ~10000 pages
5 HP 90X CE390X Black 0 LaserJet M602/M603/M4555 High yield ~24000 pages
6 HP 37A CF237A Black 0 LaserJet M607/M608/M609/M631/M632/M633 Standard yield ~11000 pages
7 HP 37X CF237X Black 0 LaserJet M607/M608/M609/M631/M632/M633 High yield ~25000 pages
8 HP 148A W1480A Black 0 LaserJet Pro 4001/4002/4003/4004 Standard yield ~2900 pages
9 HP 148X W1480X Black 0 LaserJet Pro 4001/4002/4003/4004 High yield ~9500 pages
10 HP 87A CF287A Black 0 LaserJet M501/M506/M527 Standard yield ~9000 pages
11 HP 87X CF287X Black 0 LaserJet M501/M506/M527 High yield ~18000 pages
12 HP 147A W1470A Black 0 LaserJet M610/M611/M612/M406/M407 Standard yield ~10500 pages
13 HP 147X W1470X Black 0 LaserJet M610/M611/M612/M406/M407 High yield ~25200 pages
14 HP 55A CE255A Black 0 LaserJet P3015/M521/M525 Standard yield ~6000 pages
15 HP 55X CE255X Black 0 LaserJet P3015/M521/M525 High yield ~12500 pages
16 HP 58A CF258A Black 0 LaserJet Pro M404/M428 Standard yield ~3000 pages
17 HP 58X CF258X Black 0 LaserJet Pro M404/M428 High yield ~10000 pages
18 HP 414A W2020A Black 0 Color LaserJet Pro M454/M479 Standard yield ~2400 pages
19 HP 414A W2021A Cyan 0 Color LaserJet Pro M454/M479 Standard yield ~2100 pages
20 HP 414A W2022A Yellow 0 Color LaserJet Pro M454/M479 Standard yield ~2100 pages
21 HP 414A W2023A Magenta 0 Color LaserJet Pro M454/M479 Standard yield ~2100 pages
22 HP 414X W2020X Black 0 Color LaserJet Pro M454/M479 High yield ~7500 pages
23 HP 414X W2021X Cyan 0 Color LaserJet Pro M454/M479 High yield ~6000 pages
24 HP 414X W2022X Yellow 0 Color LaserJet Pro M454/M479 High yield ~6000 pages
25 HP 414X W2023X Magenta 0 Color LaserJet Pro M454/M479 High yield ~6000 pages
26 HP 206A W2110A Black 0 Color LaserJet Pro M254/M255/M282/M283 Standard yield ~1350 pages
27 HP 206A W2111A Cyan 0 Color LaserJet Pro M254/M255/M282/M283 Standard yield ~1250 pages
28 HP 206A W2112A Yellow 0 Color LaserJet Pro M254/M255/M282/M283 Standard yield ~1250 pages
29 HP 206A W2113A Magenta 0 Color LaserJet Pro M254/M255/M282/M283 Standard yield ~1250 pages
30 HP 206X W2110X Black 0 Color LaserJet Pro M254/M255/M282/M283 High yield ~3150 pages
31 HP 206X W2111X Cyan 0 Color LaserJet Pro M254/M255/M282/M283 High yield ~2450 pages
32 HP 206X W2112X Yellow 0 Color LaserJet Pro M254/M255/M282/M283 High yield ~2450 pages
33 HP 206X W2113X Magenta 0 Color LaserJet Pro M254/M255/M282/M283 High yield ~2450 pages
34 HP 201A CF400A Black 0 Color LaserJet Pro M252/M277 Standard yield ~1500 pages
35 HP 201A CF401A Cyan 0 Color LaserJet Pro M252/M277 Standard yield ~1400 pages
36 HP 201A CF402A Yellow 0 Color LaserJet Pro M252/M277 Standard yield ~1400 pages
37 HP 201A CF403A Magenta 0 Color LaserJet Pro M252/M277 Standard yield ~1400 pages
38 HP 201X CF400X Black 0 Color LaserJet Pro M252/M277 High yield ~2800 pages
39 HP 201X CF401X Cyan 0 Color LaserJet Pro M252/M277 High yield ~2300 pages
40 HP 201X CF402X Yellow 0 Color LaserJet Pro M252/M277 High yield ~2300 pages
41 HP 201X CF403X Magenta 0 Color LaserJet Pro M252/M277 High yield ~2300 pages
42 HP 304A CC530A Black 0 Color LaserJet CP2025/CM2320 Standard yield ~3500 pages
43 HP 304A CC531A Cyan 0 Color LaserJet CP2025/CM2320 Standard yield ~2800 pages
44 HP 304A CC532A Yellow 0 Color LaserJet CP2025/CM2320 Standard yield ~2800 pages
45 HP 304A CC533A Magenta 0 Color LaserJet CP2025/CM2320 Standard yield ~2800 pages
46 HP 131A CF210A Black 0 LaserJet Pro 200 M251/M276 Standard yield ~1600 pages
47 HP 131A CF211A Cyan 0 LaserJet Pro 200 M251/M276 Standard yield ~1800 pages
48 HP 131A CF212A Yellow 0 LaserJet Pro 200 M251/M276 Standard yield ~1800 pages
49 HP 131A CF213A Magenta 0 LaserJet Pro 200 M251/M276 Standard yield ~1800 pages