{"id":3505,"date":"2026-10-02T17:05:12","date_gmt":"2026-10-02T22:05:12","guid":{"rendered":"https:\/\/xlinesoft.com\/blog\/?p=3505"},"modified":"2026-10-02T17:05:12","modified_gmt":"2026-10-02T22:05:12","slug":"build-an-equipment-checkout-system-with-qr-code-scanning","status":"publish","type":"post","link":"https:\/\/xlinesoft.com\/blog\/2026\/10\/02\/build-an-equipment-checkout-system-with-qr-code-scanning\/","title":{"rendered":"Build an Equipment Checkout System with QR Code Scanning"},"content":{"rendered":"<p>Equipment checkout applications need to do more than store a list of assets. The application should prevent duplicate checkouts, show only actions that match the current asset status, record returns, flag overdue equipment, and let staff find an item quickly.<\/p>\n<p>In this tutorial, we will build that workflow in PHPRunner or ASPRunner.NET and add camera-based QR code scanning. The JavaScript is the same in both products. Server events are provided in PHP and C#.<\/p>\n<p><a href=\"https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2026\/10\/assetmanagement.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2026\/10\/assetmanagement-1024x615.png\" alt=\"\" width=\"810\" height=\"486\" class=\"alignnone size-large wp-image-3510\" srcset=\"https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2026\/10\/assetmanagement-1024x615.png 1024w, https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2026\/10\/assetmanagement-600x360.png 600w, https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2026\/10\/assetmanagement-768x461.png 768w, https:\/\/xlinesoft.com\/blog\/wp-content\/uploads\/2026\/10\/assetmanagement.png 1504w\" sizes=\"auto, (max-width: 810px) 100vw, 810px\" \/><\/a><br \/>\n<!--more--><\/p>\n<h3>1. Creating the tables and relationships<\/h3>\n<p>This example uses three tables. Adjust the field types for your database if necessary.<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"sql\" name=\"mshighlighter\" >\r\nCREATE TABLE assets (\r\n    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,\r\n    asset_tag VARCHAR(50) NOT NULL,\r\n    name VARCHAR(150) NOT NULL,\r\n    category VARCHAR(100),\r\n    serial_number VARCHAR(100),\r\n    location VARCHAR(150),\r\n    status VARCHAR(30) NOT NULL DEFAULT 'Available',\r\n    `condition` VARCHAR(30) DEFAULT 'Good',\r\n    notes TEXT\r\n);\r\n\r\nCREATE TABLE employees (\r\n    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,\r\n    full_name VARCHAR(150) NOT NULL,\r\n    email VARCHAR(255),\r\n    department VARCHAR(100),\r\n    active TINYINT NOT NULL DEFAULT 1\r\n);\r\n\r\nCREATE TABLE asset_checkouts (\r\n    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,\r\n    asset_id INT NOT NULL,\r\n    employee_id INT NOT NULL,\r\n    checked_out_at DATETIME,\r\n    due_date DATE,\r\n    returned_at DATETIME,\r\n    condition_out VARCHAR(30),\r\n    condition_in VARCHAR(30),\r\n    notes TEXT\r\n);<\/textarea><\/pre>\n<\/div>\n<p>Add all three tables to the project. Create master-details relationships from <strong>assets.id<\/strong> to <strong>asset_checkouts.asset_id<\/strong> and from <strong>employees.id<\/strong> to <strong>asset_checkouts.employee_id<\/strong>.<\/p>\n<p>Set <strong>employee_id<\/strong> to a Lookup wizard that displays <strong>employees.full_name<\/strong>. Use fixed lookup values for <strong>assets.status<\/strong>, <strong>assets.condition<\/strong>, <strong>asset_checkouts.condition_out<\/strong>, and <strong>asset_checkouts.condition_in<\/strong>.<\/p>\n<h3>2. Showing the active checkout on the Assets pages<\/h3>\n<p>Open the SQL Query for <strong>assets<\/strong> and use the following MySQL query. It adds the due date and employee from the most recent checkout that has not been returned.<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"sql\" name=\"mshighlighter\" >\r\nSELECT\r\n    a.id,\r\n    a.asset_tag,\r\n    a.name,\r\n    a.category,\r\n    a.serial_number,\r\n    a.location,\r\n    a.status,\r\n    a.`condition`,\r\n    a.notes,\r\n    ac.due_date,\r\n    ac.employee_id\r\nFROM assets a\r\nLEFT JOIN asset_checkouts ac\r\n    ON ac.id = (\r\n        SELECT MAX(ac2.id)\r\n        FROM asset_checkouts ac2\r\n        WHERE ac2.asset_id = a.id\r\n          AND ac2.returned_at IS NULL\r\n    )<\/textarea><\/pre>\n<\/div>\n<p>Add <strong>due_date<\/strong> and <strong>employee_id<\/strong> to the Assets List and View pages. Configure <strong>employee_id<\/strong> as a Lookup wizard so the employee name is displayed.<\/p>\n<h3>3. Adding the Check out button<\/h3>\n<p>Open the Assets List page in <strong>Page Designer<\/strong>, switch to Advanced Grid, and add a custom button inside the grid. Set its caption to &#8216;Check out&#8217; and its Item ID to <strong>checkout_button<\/strong>. Add the same button to the Assets View page.<\/p>\n<p>Add the appropriate code to the button&#8217;s <strong>Server<\/strong> section.<\/p>\n<p><strong>PHP<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"php\" name=\"mshighlighter\" >\r\n$record = $button->getCurrentRecord();\r\n\r\nif (!$record || $record[\"status\"] !== \"Available\") {\r\n    $result[\"error\"] = \"This equipment is no longer available.\";\r\n    return;\r\n}\r\n\r\n$result[\"asset_id\"] = $record[\"id\"];<\/textarea><\/pre>\n<\/div>\n<p><strong>C#<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"\" name=\"mshighlighter\" >\r\ndynamic record = button.getCurrentRecord();\r\n\r\nif (!XVar.Pack(record) || Convert.ToString(record[\"status\"]) != \"Available\")\r\n{\r\n    result[\"error\"] = \"This equipment is no longer available.\";\r\n    return;\r\n}\r\n\r\nresult[\"asset_id\"] = record[\"id\"];<\/textarea><\/pre>\n<\/div>\n<p>Add this JavaScript to <strong>Client After<\/strong>:<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"javascript\" name=\"mshighlighter\" >\r\nif (result[\"error\"]) {\r\n    swal(result[\"error\"]);\r\n    return;\r\n}\r\n\r\nwindow.popup = Runner.displayPopup({\r\n    url: Runner.pages.getUrl(\"asset_checkouts\", \"add\")\r\n        + \"?mastertable=assets&masterkey1=\"\r\n        + encodeURIComponent(result[\"asset_id\"]),\r\n    width: 650,\r\n    height: 700,\r\n    header: \"Check Out Equipment\"\r\n});<\/textarea><\/pre>\n<\/div>\n<h3>4. Saving a checkout<\/h3>\n<p>On the Asset Checkouts Add page, keep <strong>employee_id<\/strong>, <strong>due_date<\/strong>, and <strong>notes<\/strong> visible. The remaining values will be set on the server.<\/p>\n<p>Add this code to <strong>Before record added<\/strong>.<\/p>\n<p><strong>PHP<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"php\" name=\"mshighlighter\" >\r\n$asset = $pageObject->getMasterRecord();\r\n\r\nif (!$asset || $asset[\"status\"] !== \"Available\") {\r\n    $message = \"This equipment is no longer available.\";\r\n    return false;\r\n}\r\n\r\n$values[\"asset_id\"] = $asset[\"id\"];\r\n$values[\"checked_out_at\"] = date(\"Y-m-d H:i:s\");\r\n$values[\"condition_out\"] = $asset[\"condition\"];\r\n$values[\"returned_at\"] = null;\r\n$values[\"condition_in\"] = null;\r\n\r\nreturn true;<\/textarea><\/pre>\n<\/div>\n<p><strong>C#<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"\" name=\"mshighlighter\" >\r\ndynamic asset = pageObject.getMasterRecord();\r\n\r\nif (!XVar.Pack(asset) || Convert.ToString(asset[\"status\"]) != \"Available\")\r\n{\r\n    message = \"This equipment is no longer available.\";\r\n    return false;\r\n}\r\n\r\nvalues[\"asset_id\"] = asset[\"id\"];\r\nvalues[\"checked_out_at\"] = DateTime.Now;\r\nvalues[\"condition_out\"] = asset[\"condition\"];\r\nvalues[\"returned_at\"] = null;\r\nvalues[\"condition_in\"] = null;\r\n\r\nreturn true;<\/textarea><\/pre>\n<\/div>\n<p>Add this code to <strong>After record added<\/strong>.<\/p>\n<p><strong>PHP<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"php\" name=\"mshighlighter\" >\r\nDB::Update(\r\n    \"assets\",\r\n    array(\"status\" => \"Checked Out\"),\r\n    array(\"id\" => $values[\"asset_id\"])\r\n);\r\n\r\n$pageObject->stopPRG = true;\r\n$pageObject->setProxyValue(\"checkoutSaved\", true);<\/textarea><\/pre>\n<\/div>\n<p><strong>C#<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"\" name=\"mshighlighter\" >\r\ndynamic data = XVar.Array();\r\ndynamic keyValues = XVar.Array();\r\n\r\ndata[\"status\"] = \"Checked Out\";\r\nkeyValues[\"id\"] = values[\"asset_id\"];\r\nDB.Update(\"assets\", data, keyValues);\r\n\r\npageObject.stopPRG = true;\r\npageObject.setProxyValue(\"checkoutSaved\", true);<\/textarea><\/pre>\n<\/div>\n<p>Add this JavaScript to the Add page&#8217;s <strong>JavaScript OnLoad<\/strong> event:<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"javascript\" name=\"mshighlighter\" >\r\nif (proxy.checkoutSaved && window.parent && window.parent.popup) {\r\n    window.parent.location.reload();\r\n    window.parent.popup.close();\r\n}<\/textarea><\/pre>\n<\/div>\n<h3>5. Adding the Return button<\/h3>\n<p>Add another custom button to the Assets List grid and View page. Set its caption to &#8216;Return&#8217; and its Item ID to <strong>return_button<\/strong>.<\/p>\n<p>Add the appropriate code to the button&#8217;s <strong>Server<\/strong> section.<\/p>\n<p><strong>PHP<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"php\" name=\"mshighlighter\" >\r\n$asset = $button->getCurrentRecord();\r\n\r\nif (!$asset || $asset[\"status\"] !== \"Checked Out\") {\r\n    $result[\"error\"] = \"This equipment is not checked out.\";\r\n    return;\r\n}\r\n\r\n$where = DB::PrepareSQL(\r\n    \"asset_id = :1 AND returned_at IS NULL\",\r\n    $asset[\"id\"]\r\n);\r\n$rs = DB::Select(\"asset_checkouts\", $where);\r\n$checkout = $rs->fetchAssoc();\r\n\r\nif (!$checkout) {\r\n    $result[\"error\"] = \"The active checkout record was not found.\";\r\n    return;\r\n}\r\n\r\n$result[\"checkout_id\"] = $checkout[\"id\"];<\/textarea><\/pre>\n<\/div>\n<p><strong>C#<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"\" name=\"mshighlighter\" >\r\ndynamic asset = button.getCurrentRecord();\r\n\r\nif (!XVar.Pack(asset) || Convert.ToString(asset[\"status\"]) != \"Checked Out\")\r\n{\r\n    result[\"error\"] = \"This equipment is not checked out.\";\r\n    return;\r\n}\r\n\r\nstring where = DB.PrepareSQL(\r\n    \"asset_id = :1 AND returned_at IS NULL\",\r\n    asset[\"id\"]\r\n);\r\ndynamic rs = DB.Select(\"asset_checkouts\", where);\r\ndynamic checkout = rs.fetchAssoc();\r\n\r\nif (!XVar.Pack(checkout))\r\n{\r\n    result[\"error\"] = \"The active checkout record was not found.\";\r\n    return;\r\n}\r\n\r\nresult[\"checkout_id\"] = checkout[\"id\"];<\/textarea><\/pre>\n<\/div>\n<p>Add this JavaScript to <strong>Client After<\/strong>:<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"javascript\" name=\"mshighlighter\" >\r\nif (result[\"error\"]) {\r\n    swal(result[\"error\"]);\r\n    return;\r\n}\r\n\r\nwindow.popup = Runner.displayPopup({\r\n    url: Runner.pages.getUrl(\"asset_checkouts\", \"edit\")\r\n        + \"?editid1=\"\r\n        + encodeURIComponent(result[\"checkout_id\"]),\r\n    width: 650,\r\n    height: 500,\r\n    header: \"Return Equipment\"\r\n});<\/textarea><\/pre>\n<\/div>\n<h3>6. Processing the return<\/h3>\n<p>Use the Asset Checkouts Edit page only for returns. Keep <strong>condition_in<\/strong> and <strong>notes<\/strong> visible and remove ordinary Edit links to this table from other pages.<\/p>\n<p>Add this code to <strong>Before record updated<\/strong>.<\/p>\n<p><strong>PHP<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"php\" name=\"mshighlighter\" >\r\nif (!empty($oldvalues[\"returned_at\"])) {\r\n    $message = \"This equipment has already been returned.\";\r\n    return false;\r\n}\r\n\r\n$values[\"returned_at\"] = date(\"Y-m-d H:i:s\");\r\nreturn true;<\/textarea><\/pre>\n<\/div>\n<p><strong>C#<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"\" name=\"mshighlighter\" >\r\nif (XVar.Pack(oldvalues[\"returned_at\"]))\r\n{\r\n    message = \"This equipment has already been returned.\";\r\n    return false;\r\n}\r\n\r\nvalues[\"returned_at\"] = DateTime.Now;\r\nreturn true;<\/textarea><\/pre>\n<\/div>\n<p>Add this code to <strong>After record updated<\/strong>.<\/p>\n<p><strong>PHP<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"php\" name=\"mshighlighter\" >\r\n$status = $values[\"condition_in\"] === \"Damaged\"\r\n    ? \"Maintenance\"\r\n    : \"Available\";\r\n\r\nDB::Update(\r\n    \"assets\",\r\n    array(\r\n        \"status\" => $status,\r\n        \"condition\" => $values[\"condition_in\"]\r\n    ),\r\n    array(\"id\" => $oldvalues[\"asset_id\"])\r\n);\r\n\r\n$pageObject->stopPRG = true;\r\n$pageObject->setProxyValue(\"returnSaved\", true);<\/textarea><\/pre>\n<\/div>\n<p><strong>C#<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"\" name=\"mshighlighter\" >\r\nstring status = Convert.ToString(values[\"condition_in\"]) == \"Damaged\"\r\n    ? \"Maintenance\"\r\n    : \"Available\";\r\n\r\ndynamic data = XVar.Array();\r\ndynamic keyValues = XVar.Array();\r\ndata[\"status\"] = status;\r\ndata[\"condition\"] = values[\"condition_in\"];\r\nkeyValues[\"id\"] = oldvalues[\"asset_id\"];\r\nDB.Update(\"assets\", data, keyValues);\r\n\r\npageObject.stopPRG = true;\r\npageObject.setProxyValue(\"returnSaved\", true);<\/textarea><\/pre>\n<\/div>\n<p>Add this JavaScript to the Edit page&#8217;s <strong>JavaScript OnLoad<\/strong> event:<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"javascript\" name=\"mshighlighter\" >\r\nif (proxy.returnSaved && window.parent && window.parent.popup) {\r\n    window.parent.location.reload();\r\n    window.parent.popup.close();\r\n}<\/textarea><\/pre>\n<\/div>\n<h3>7. Showing only valid actions<\/h3>\n<p>Open <strong>Assets List page &#8211; After record processed<\/strong>. Use the Item IDs assigned earlier so each row shows only the action that applies to its current status.<\/p>\n<p><strong>PHP<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"php\" name=\"mshighlighter\" >\r\nif ($data[\"status\"] !== \"Available\") {\r\n    $pageObject->hideItem(\"checkout_button\", $recordId);\r\n}\r\n\r\nif ($data[\"status\"] !== \"Checked Out\") {\r\n    $pageObject->hideItem(\"return_button\", $recordId);\r\n}\r\n\r\nif (\r\n    !empty($data[\"due_date\"])\r\n    && strtotime($data[\"due_date\"]) < strtotime(date(\"Y-m-d\"))\r\n) {\r\n    $record[\"css\"] = \"background: #ffdddd;\";\r\n}\r\n[\/php]\r\n<p><strong>C#<\/strong><\/p>\r\n[code]\r\nif (Convert.ToString(data[\"status\"]) != \"Available\")\r\n{\r\n    pageObject.hideItem(\"checkout_button\", recordId);\r\n}\r\n\r\nif (Convert.ToString(data[\"status\"]) != \"Checked Out\")\r\n{\r\n    pageObject.hideItem(\"return_button\", recordId);\r\n}\r\n\r\nDateTime dueDate;\r\nif (\r\n    DateTime.TryParse(Convert.ToString(data[\"due_date\"]), out dueDate)\r\n    && dueDate.Date < DateTime.Today\r\n)\r\n{\r\n    record[\"css\"] = \"background: #ffdddd;\";\r\n}\r\n[\/code]\r\n<p>On <strong>Assets View page - Before display<\/strong>, hide the buttons without a record ID.<\/p>\r\n<p><strong>PHP<\/strong><\/p>\r\n[php]\r\n$asset = $pageObject->getCurrentRecord();\r\n\r\nif ($asset[\"status\"] !== \"Available\") {\r\n    $pageObject->hideItem(\"checkout_button\");\r\n}\r\n\r\nif ($asset[\"status\"] !== \"Checked Out\") {\r\n    $pageObject->hideItem(\"return_button\");\r\n}<\/textarea><\/pre>\n<\/div>\n<p><strong>C#<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"\" name=\"mshighlighter\" >\r\ndynamic asset = pageObject.getCurrentRecord();\r\n\r\nif (Convert.ToString(asset[\"status\"]) != \"Available\")\r\n{\r\n    pageObject.hideItem(\"checkout_button\");\r\n}\r\n\r\nif (Convert.ToString(asset[\"status\"]) != \"Checked Out\")\r\n{\r\n    pageObject.hideItem(\"return_button\");\r\n}<\/textarea><\/pre>\n<\/div>\n<h3>8. Adding camera-based QR scanning<\/h3>\n<p>Create QR labels whose encoded value is the corresponding <strong>asset_tag<\/strong>. The scanner only needs that value, so the labels can be produced by the application or by a separate label-printing tool.<\/p>\n<p>Add a custom button above the Assets List grid. Set its caption to &#8216;Scan QR&#8217; and its Item ID to <strong>scan_qr_button<\/strong>. Add the following JavaScript to <strong>Client Before<\/strong>. The script loads the <a href=\"https:\/\/github.com\/mebjas\/html5-qrcode\" target=\"_blank\" rel=\"noopener\">html5-qrcode<\/a> library when it is first needed.<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"javascript\" name=\"mshighlighter\" >\r\nfunction openAssetScanner() {\r\n    var scanner;\r\n    var matched = false;\r\n    var closing = false;\r\n\r\n    Runner.displayPopup({\r\n        html: '<div id=\"asset_qr_reader\" style=\"width:100%;\"><\/div>',\r\n        width: 600,\r\n        height: 650,\r\n        header: \"Scan Equipment Label\",\r\n\r\n        afterCreate: function(popup) {\r\n            scanner = new Html5QrcodeScanner(\r\n                \"asset_qr_reader\",\r\n                {\r\n                    fps: 10,\r\n                    qrbox: { width: 250, height: 250 },\r\n                    formatsToSupport: [\r\n                        Html5QrcodeSupportedFormats.QR_CODE\r\n                    ],\r\n                    supportedScanTypes: [\r\n                        Html5QrcodeScanType.SCAN_TYPE_CAMERA\r\n                    ]\r\n                },\r\n                false\r\n            );\r\n\r\n            scanner.render(function(decodedText) {\r\n                if (matched || closing) {\r\n                    return;\r\n                }\r\n\r\n                matched = true;\r\n                params[\"asset_tag\"] = decodedText.trim();\r\n                popup.close();\r\n            });\r\n        },\r\n\r\n        beforeClose: function(popup) {\r\n            if (closing) {\r\n                return true;\r\n            }\r\n\r\n            closing = true;\r\n\r\n            scanner.clear().then(function() {\r\n                popup.close();\r\n\r\n                if (matched) {\r\n                    ajax.submit();\r\n                }\r\n            }).catch(function() {\r\n                closing = false;\r\n                matched = false;\r\n                swal(\"Stop scanning before closing the window.\");\r\n            });\r\n\r\n            return false;\r\n        }\r\n    });\r\n}\r\n\r\nif (window.Html5QrcodeScanner) {\r\n    openAssetScanner();\r\n} else {\r\n    $.getScript(\r\n        \"https:\/\/unpkg.com\/html5-qrcode@2.3.8\/html5-qrcode.min.js\"\r\n    ).done(openAssetScanner).fail(function() {\r\n        swal(\"The QR scanner library could not be loaded.\");\r\n    });\r\n}\r\n\r\nreturn false;<\/textarea><\/pre>\n<\/div>\n<p>Add the appropriate lookup code to the button&#8217;s <strong>Server<\/strong> section.<\/p>\n<p><strong>PHP<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"php\" name=\"mshighlighter\" >\r\n$tag = trim((string)$params[\"asset_tag\"]);\r\n$rs = DB::Select(\"assets\", array(\"asset_tag\" => $tag));\r\n$asset = $rs->fetchAssoc();\r\n\r\nif (!$asset) {\r\n    $result[\"error\"] = \"No equipment matches this QR code.\";\r\n    return;\r\n}\r\n\r\n$result[\"asset_id\"] = $asset[\"id\"];<\/textarea><\/pre>\n<\/div>\n<p><strong>C#<\/strong><\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"\" name=\"mshighlighter\" >\r\nstring tag = Convert.ToString(parameters[\"asset_tag\"]).Trim();\r\ndynamic values = XVar.Array();\r\nvalues[\"asset_tag\"] = tag;\r\n\r\ndynamic rs = DB.Select(\"assets\", values);\r\ndynamic asset = rs.fetchAssoc();\r\n\r\nif (!XVar.Pack(asset))\r\n{\r\n    result[\"error\"] = \"No equipment matches this QR code.\";\r\n    return;\r\n}\r\n\r\nresult[\"asset_id\"] = asset[\"id\"];<\/textarea><\/pre>\n<\/div>\n<p>Add this JavaScript to <strong>Client After<\/strong>:<\/p>\n<div class=\"my-syntax-highlighter\">\n<pre><textarea id=\"mshighlighter\" class=\"mshighlighter\" language=\"javascript\" name=\"mshighlighter\" >\r\nif (result[\"error\"]) {\r\n    swal(result[\"error\"]);\r\n    return;\r\n}\r\n\r\nlocation.href = Runner.pages.getUrl(\"assets\", \"view\")\r\n    + \"?editid1=\"\r\n    + encodeURIComponent(result[\"asset_id\"]);<\/textarea><\/pre>\n<\/div>\n<p>Camera access normally requires HTTPS, except during local development. Rebuild the project, open the Assets List page on a phone or laptop with a camera, scan an asset label, and confirm that the correct View page opens. Then test checkout, return, damaged-item handling, conditional buttons, and overdue highlighting.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Create an equipment checkout workflow in PHPRunner and ASPRunner.NET with status-aware actions, popup forms, overdue highlighting, and camera-based QR code scanning.<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[16,1,8],"tags":[],"class_list":["post-3505","post","type-post","status-publish","format-standard","hentry","category-asp-net","category-php-category","category-tutorials"],"_links":{"self":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts\/3505","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/comments?post=3505"}],"version-history":[{"count":2,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts\/3505\/revisions"}],"predecessor-version":[{"id":3511,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/posts\/3505\/revisions\/3511"}],"wp:attachment":[{"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/media?parent=3505"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/categories?post=3505"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/xlinesoft.com\/blog\/wp-json\/wp\/v2\/tags?post=3505"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}