Normalize database column and table names across entire codebase

Update all SQL queries, query result references, and ColdFusion code to match
the renamed database schema. Tables use plural CamelCase, PKs are all `ID`,
column prefixes stripped (e.g. BusinessName→Name, UserFirstName→FirstName).

Key changes:
- Strip table-name prefixes from all column references (Businesses, Users,
  Addresses, Hours, Menus, Categories, Items, Stations, Orders,
  OrderLineItems, Tasks, TaskCategories, TaskRatings, QuickTaskTemplates,
  ScheduledTaskDefinitions, ChatMessages, Beacons, ServicePoints, Employees,
  VisitorTrackings, ApiPerfLogs, tt_States, tt_Days, tt_AddressTypes,
  tt_OrderTypes, tt_TaskTypes)
- Rename PK references from {TableName}ID to ID in all queries
- Rewrite 7 admin beacon files to use ServicePoints.BeaconID instead of
  dropped lt_Beacon_Businesses_ServicePoints link table
- Rewrite beacon assignment files (list, save, delete) for new schema
- Fix FK references incorrectly changed to ID (OrderLineItems.OrderID,
  Categories.MenuID, Tasks.CategoryID, ServicePoints.BeaconID)
- Update Addresses: AddressLat→Latitude, AddressLng→Longitude
- Update Users: UserPassword→Password, UserIsEmailVerified→IsEmailVerified,
  UserIsActive→IsActive, UserBalance→Balance, etc.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
John Mizerek 2026-01-30 15:39:12 -08:00
parent dc9db32b58
commit 1210249f54
240 changed files with 6644 additions and 5542 deletions

View file

@ -24,9 +24,9 @@
<cfif request.UserID NEQ 0>
<cfquery name="check_user" datasource="#application.datasource#">
SELECT UserFirstName, UserBalance, UserImageExtension
SELECT FirstName, Balance, ImageExtension
FROM Users
WHERE UserID = #request.UserID#
WHERE ID = #request.UserID#
</cfquery>
</cfif>
@ -320,8 +320,8 @@
<cfif find("logout.cfm", request.cgiPath) EQ 0>
<cfif request.UserID NEQ 0>
<cfif check_user.UserImageExtension gt ""><img src="#application.image_display_prefix#users/thumbs/#request.UserID#.#check_user.UserImageExtension#" border="0" alt=""><br></cfif>
Hi, <cfif check_user.UserFirstName gt "">#check_user.UserFirstName#<br>#dollarformat(check_user.UserBalance)#<cfelse>Payfrit User</cfif><br>
<cfif check_user.ImageExtension gt ""><img src="#application.image_display_prefix#users/thumbs/#request.UserID#.#check_user.ImageExtension#" border="0" alt=""><br></cfif>
Hi, <cfif check_user.FirstName gt "">#check_user.FirstName#<br>#dollarformat(check_user.Balance)#<cfelse>Payfrit User</cfif><br>
<cfelse>
<form action="#application.wwwrootprefix#index.cfm" method="post" name="login_form" id="login_form">

View file

@ -116,13 +116,13 @@
<cfset cart_total = 0>
<CFQUERY name="get_queued_food" datasource="#application.datasource#" dbtype="ODBC">
SELECT A.CartID, A.AddedOn, A.Quantity, A.SpecialRemark, B.BusinessName, B.UserID, C.ItemName, A.Price, D.UserFirstName, D.LaerFirstName, D.Balance
SELECT A.CartID, A.AddedOn, A.Quantity, A.SpecialRemark, B.Name, B.UserID, C.Name, A.Price, D.FirstName, D.LaerFirstName, D.Balance
FROM dbo.Business_CartMaster A, dbo.BusinessMaster B, dbo.Business_ItemMaster C, Users D
WHERE A.UserID = D.UserID
AND
A.ItemID = C.ItemID
AND
B.BusinessID = C.BusinessID
B.ID = C.BusinessID
AND
C.BusinessID = #form.bizid#
AND
@ -170,11 +170,11 @@
</CFQUERY>
<CFQUERY name="get_last_inserted" datasource="#application.datasource#" dbtype="ODBC">
SELECT TOP 1 O.OrderID, M.UserID as person_to_pay_for_orderID, U.Balance
SELECT TOP 1 O.ID, M.UserID as person_to_pay_for_orderID, U.Balance
FROM dbo.Business_OrderMaster O, dbo.BusinessMaster M, Users U
WHERE O.BusinessID = M.BusinessID
AND
M.UserID = U.UserID
M.UserID = U.ID
ORDER BY O.AddedOn DESC
</CFQUERY>
@ -185,7 +185,7 @@
)
VALUES
(
#get_last_inserted.OrderID#,
#get_last_inserted.ID#,
#get_queued_food.CartID#
)
</CFQUERY>
@ -268,7 +268,7 @@
<CFQUERY name="get_user_104_balance" datasource="#application.datasource#" dbtype="ODBC">
SELECT balance
FROM Users
WHERE UserID = 104
WHERE ID = 104
</CFQUERY>
<CFQUERY name="transfer_fees_to_UserID_104" datasource="#application.datasource#" dbtype="ODBC">
@ -346,7 +346,7 @@
<CFQUERY name="get_user_104_balance" datasource="#application.datasource#" dbtype="ODBC">
SELECT balance
FROM Users
WHERE UserID = 104
WHERE ID = 104
</CFQUERY>
<CFQUERY name="transfer_fees_to_UserID_104" datasource="#application.datasource#" dbtype="ODBC">

View file

@ -164,8 +164,8 @@ async function refreshAssignments(){
const tr = document.createElement("tr");
tr.innerHTML = `
<td>${a.lt_Beacon_Businesses_ServicePointID}</td>
<td>${escapeHtml((a.BeaconName || "") + " (ID " + a.BeaconID + ")")}</td>
<td>${escapeHtml((a.ServicePointName || "") + " (ID " + a.ServicePointID + ")")}</td>
<td>${escapeHtml((a.Name || "") + " (ID " + a.BeaconID + ")")}</td>
<td>${escapeHtml((a.Name || "") + " (ID " + a.ServicePointID + ")")}</td>
<td>${escapeHtml(a.lt_Beacon_Businesses_ServicePointNotes || "")}</td>
<td>${escapeHtml(a.CreatedAt || "")}</td>
`;
@ -183,12 +183,12 @@ async function refreshBeacons(assignedBeaconIDs, keepSelectedBeaconID){
setSelectPlaceholder(sel, "-- Select Beacon --");
(out.BEACONS || []).forEach(b => {
const isAssigned = assignedBeaconIDs.has(String(b.BeaconID));
const isAssigned = assignedBeaconIDs.has(String(b.ID));
if (HIDE_ASSIGNED_BEACONS && isAssigned) return;
const opt = document.createElement("option");
opt.value = b.BeaconID;
opt.textContent = String(b.BeaconID) + " - " + (b.BeaconName || "");
opt.value = b.ID;
opt.textContent = String(b.ID) + " - " + (b.Name || "");
sel.appendChild(opt);
});
@ -203,12 +203,12 @@ async function refreshServicePoints(assignedServicePointIDs, keepSelectedService
setSelectPlaceholder(sel, "-- Select ServicePoint --");
(out.SERVICEPOINTS || []).forEach(sp => {
const isAssigned = assignedServicePointIDs.has(String(sp.ServicePointID));
const isAssigned = assignedServicePointIDs.has(String(sp.ID));
if (HIDE_ASSIGNED_SERVICEPOINTS && isAssigned) return;
const opt = document.createElement("option");
opt.value = sp.ServicePointID;
opt.textContent = String(sp.ServicePointID) + " - " + (sp.ServicePointName || "");
opt.value = sp.ID;
opt.textContent = String(sp.ID) + " - " + (sp.Name || "");
sel.appendChild(opt);
});

View file

@ -25,7 +25,7 @@
</head>
<body>
<h2>Beacons</h2>
<div class="warn">Required: BeaconName</div>
<div class="warn">Required: Name</div>
<div class="ok" id="jsStatus">(JS not loaded yet)</div>
<div class="row">
@ -38,8 +38,8 @@
</div>
<div>
<label>BeaconName (required)</label><br>
<input id="BeaconName" placeholder="Front Door" required>
<label>Name (required)</label><br>
<input id="Name" placeholder="Front Door" required>
</div>
<div>
@ -160,13 +160,13 @@ function escapeHtml(s){
}
function loadIntoForm(b){
document.getElementById("BeaconID").value = b.BeaconID || "";
document.getElementById("BeaconName").value = b.BeaconName || "";
document.getElementById("BeaconID").value = b.ID || "";
document.getElementById("Name").value = b.Name || "";
document.getElementById("UUID").value = b.UUID || "";
document.getElementById("NamespaceId").value = b.NamespaceId || "";
document.getElementById("InstanceId").value = b.InstanceId || "";
document.getElementById("IsActive").value = ("" + (b.IsActive ?? 1));
document.getElementById("DelBeaconID").value = b.BeaconID || "";
document.getElementById("DelBeaconID").value = b.ID || "";
}
async function refresh() {
@ -180,8 +180,8 @@ async function refresh() {
for (const b of items) {
const tr = document.createElement("tr");
tr.innerHTML = `
<td>${b.BeaconID}</td>
<td>${escapeHtml(b.BeaconName||"")}</td>
<td>${b.ID}</td>
<td>${escapeHtml(b.Name||"")}</td>
<td>${escapeHtml(b.UUID||"")}</td>
<td>${escapeHtml(b.NamespaceId||"")}</td>
<td>${escapeHtml(b.InstanceId||"")}</td>
@ -194,15 +194,15 @@ async function refresh() {
}
async function saveBeacon() {
const name = (document.getElementById("BeaconName").value || "").trim();
const name = (document.getElementById("Name").value || "").trim();
if (!name) {
show({ OK:false, ERROR:"missing_beacon_name", MESSAGE:"BeaconName is required" });
show({ OK:false, ERROR:"missing_beacon_name", MESSAGE:"Name is required" });
return;
}
const body = {
BeaconID: valIntOrNull("BeaconID"),
BeaconName: name,
Name: name,
UUID: (document.getElementById("UUID").value || "").trim(),
NamespaceId: (document.getElementById("NamespaceId").value || "").trim(),
InstanceId: (document.getElementById("InstanceId").value || "").trim(),

View file

@ -7,16 +7,16 @@
<cfparam name="users_to_email" default="">
<cfquery name="select_users_to_email" datasource="#application.datasource#">
SELECT U.UserEmailAddress
SELECT U.EmailAddress
FROM Users U
WHERE UserID in (0,1,2)
</cfquery>
<cfoutput query="select_users_to_email">
#UserEmailAddress#,
#EmailAddress#,
<cfset users_to_email=listappend(users_to_email, #UserEmailAddress#)>
<cfset users_to_email=listappend(users_to_email, #EmailAddress#)>
</cfoutput><br><br>
@ -95,18 +95,18 @@
<cfloop index="the_email_address" list="#users_to_email#">
<cfquery name="get_user_email" datasource="#application.datasource#">
SELECT UserUUID
SELECT UUID
FROM Users
WHERE UserEmailAddress = '#the_email_address#'
WHERE EmailAddress = '#the_email_address#'
AND
UserIsEmailverified = 1
AND
UserIsContactVerified > 0
IsContactVerified > 0
</cfquery>
<cfset form.this_email_body = form.email_body & "
instant unsubscribe link: https://www.payfrit.com/remove_me.cfm?UserUUID="&#get_user_email.UserUUID#>
instant unsubscribe link: https://www.payfrit.com/remove_me.cfm?UUID="&#get_user_email.UUID#>
<cfmail to="#the_email_address#" from="admin@payfrit.com" subject="#form.email_subject#" type="HTML">
#HTMLCodeFormat(form.this_email_body)#</cfmail>

View file

@ -5,14 +5,14 @@
<cfif form.mode eq "start">
<CFQUERY name="get_verified_users" datasource="#application.datasource#">
SELECT U.UserID, U.UserEmailAddress, U.UserContactNumber,U.UserAddedOn
SELECT U.ID, U.EmailAddress, U.ContactNumber,U.AddedOn
FROM Users U
WHERE U.UserIsEmailVerified = 1
WHERE U.IsEmailVerified = 1
AND
U.UserIsCOntactVerified > 0
AND
U.UserID > 435
ORDER BY U.UserID DESC
U.ID > 435
ORDER BY U.ID DESC
</cfquery>
@ -48,16 +48,16 @@
</form>
</td>
<td>#UserEmailAddress#</td>
<td>#UserContactNumber#</td>
<td>#dateformat(UserAddedOn, "mmmm dd, YYYY")# at #timeformat(UserAddedOn, "hh:nn tt")#</td>
<td>#EmailAddress#</td>
<td>#ContactNumber#</td>
<td>#dateformat(AddedOn, "mmmm dd, YYYY")# at #timeformat(AddedOn, "hh:nn tt")#</td>
<td>
<CFQUERY name="get_orders" datasource="#application.datasource#">
SELECT O.OrderUUID
SELECT O.UUID
FROM Orders O
WHERE O.OrderUserID = #get_verified_users.UserID#
ORDER BY O.OrderID DESC
WHERE O.UserID = #get_verified_users.ID#
ORDER BY O.ID DESC
</cfquery>
<cfparam name="looper" default="">
@ -66,7 +66,7 @@
<cfloop query="get_orders">
<cfset looper=incrementvalue(looper)>
<a href="https://payfr.it/show_order.cfm?OrderUUID=#get_orders.OrderUUID#&is_admin_view=1" target="_blank">#looper#</a>, &nbsp;
<a href="https://payfr.it/show_order.cfm?UUID=#get_orders.UUID#&is_admin_view=1" target="_blank">#looper#</a>, &nbsp;
</cfloop>
</td>
@ -79,10 +79,10 @@
<cfelseif form.mode eq "user_traffic">
<CFQUERY name="get_user_traffic" datasource="#application.datasource#">
SELECT V.VisitorTrackingPageMode, V.VisitorTrackingAddedOn
FROM VisitorTracking V
WHERE V.VisitorTrackingUserID = #form.chip#
ORDER BY V.VisitorTrackingAddedOn DESC
SELECT V.PageMode, V.AddedOn
FROM VisitorTrackings V
WHERE V.UserID = #form.chip#
ORDER BY V.AddedOn DESC
</cfquery>
<table>
@ -92,8 +92,8 @@
</tr>
<cfoutput query="get_user_traffic">
<tr>
<td>#VisitorTrackingPageMode#</td>
<td>#VisitorTrackingAddedOn#</td>
<td>#PageMode#</td>
<td>#AddedOn#</td>
</tr>
</cfoutput>
</table>

View file

@ -25,7 +25,7 @@
</head>
<body>
<h2>ServicePoints</h2>
<div class="warn">Required: ServicePointName</div>
<div class="warn">Required: Name</div>
<div class="ok" id="jsStatus">(JS not loaded yet)</div>
<div class="row">
@ -38,18 +38,18 @@
</div>
<div>
<label>ServicePointName (required)</label><br>
<input id="ServicePointName" placeholder="Front Counter" required>
<label>Name (required)</label><br>
<input id="Name" placeholder="Front Counter" required>
</div>
<div>
<label>ServicePointTypeID</label><br>
<input id="ServicePointTypeID" placeholder="0">
<label>TypeID</label><br>
<input id="TypeID" placeholder="0">
</div>
<div>
<label>ServicePointCode</label><br>
<input id="ServicePointCode" placeholder="COUNTER">
<label>Code</label><br>
<input id="Code" placeholder="COUNTER">
</div>
<div>
@ -160,14 +160,14 @@ function escapeHtml(s){
}
function loadIntoForm(sp){
document.getElementById("ServicePointID").value = sp.ServicePointID || "";
document.getElementById("ServicePointName").value = sp.ServicePointName || "";
document.getElementById("ServicePointTypeID").value = (sp.ServicePointTypeID ?? 0);
document.getElementById("ServicePointCode").value = sp.ServicePointCode || "";
document.getElementById("ServicePointID").value = sp.ID || "";
document.getElementById("Name").value = sp.Name || "";
document.getElementById("TypeID").value = (sp.TypeID ?? 0);
document.getElementById("Code").value = sp.Code || "";
document.getElementById("Description").value = sp.Description || "";
document.getElementById("SortOrder").value = (sp.SortOrder ?? 0);
document.getElementById("IsActive").value = ("" + (sp.IsActive ?? 1));
document.getElementById("DelServicePointID").value = sp.ServicePointID || "";
document.getElementById("DelServicePointID").value = sp.ID || "";
}
async function refresh() {
@ -181,10 +181,10 @@ async function refresh() {
for (const sp of items) {
const tr = document.createElement("tr");
tr.innerHTML = `
<td>${sp.ServicePointID}</td>
<td>${escapeHtml(sp.ServicePointName||"")}</td>
<td>${sp.ServicePointTypeID}</td>
<td>${escapeHtml(sp.ServicePointCode||"")}</td>
<td>${sp.ID}</td>
<td>${escapeHtml(sp.Name||"")}</td>
<td>${sp.TypeID}</td>
<td>${escapeHtml(sp.Code||"")}</td>
<td>${sp.SortOrder}</td>
<td>${sp.IsActive}</td>
`;
@ -195,17 +195,17 @@ async function refresh() {
}
async function saveSP() {
const name = (document.getElementById("ServicePointName").value || "").trim();
const name = (document.getElementById("Name").value || "").trim();
if (!name) {
show({ OK:false, ERROR:"missing_servicepoint_name", MESSAGE:"ServicePointName is required" });
show({ OK:false, ERROR:"missing_servicepoint_name", MESSAGE:"Name is required" });
return;
}
const body = {
ServicePointID: valIntOrNull("ServicePointID"),
ServicePointName: name,
ServicePointTypeID: valIntOrZero("ServicePointTypeID"),
ServicePointCode: (document.getElementById("ServicePointCode").value || "").trim(),
Name: name,
TypeID: valIntOrZero("TypeID"),
Code: (document.getElementById("Code").value || "").trim(),
Description: (document.getElementById("Description").value || "").trim(),
SortOrder: valIntOrZero("SortOrder"),
IsActive: parseInt(document.getElementById("IsActive").value, 10)

View file

@ -1,11 +1,6 @@
<cfsetting showdebugoutput="false">
<cfsetting enablecfoutputonly="true">
<!--- Performance timing: start clock as early as possible --->
<cfset request._perf_start = getTickCount()>
<cfset request._perf_queryCount = 0>
<cfset request._perf_queryTimeMs = 0>
<!---
Payfrit API Application.cfm (updated 2026-01-22)
@ -178,6 +173,11 @@ if (len(request._api_path)) {
// Worker app endpoints
if (findNoCase("/api/workers/myBusinesses.cfm", request._api_path)) request._api_isPublic = true;
if (findNoCase("/api/workers/tierStatus.cfm", request._api_path)) request._api_isPublic = true;
if (findNoCase("/api/workers/createAccount.cfm", request._api_path)) request._api_isPublic = true;
if (findNoCase("/api/workers/onboardingLink.cfm", request._api_path)) request._api_isPublic = true;
if (findNoCase("/api/workers/earlyUnlock.cfm", request._api_path)) request._api_isPublic = true;
if (findNoCase("/api/workers/ledger.cfm", request._api_path)) request._api_isPublic = true;
// Portal endpoints
if (findNoCase("/api/portal/stats.cfm", request._api_path)) request._api_isPublic = true;

View file

@ -58,8 +58,8 @@ try {
queryExecute("
UPDATE Addresses
SET AddressIsDefaultDelivery = 0
WHERE AddressUserID = :userId
AND (AddressBusinessID = 0 OR AddressBusinessID IS NULL)
WHERE UserID = :userId
AND (BusinessID = 0 OR BusinessID IS NULL)
AND AddressTypeID = :typeId
", {
userId: { value: userId, cfsqltype: "cf_sql_integer" },
@ -75,18 +75,18 @@ try {
queryExecute("
INSERT INTO Addresses (
AddressID,
AddressUserID,
AddressBusinessID,
UserID,
BusinessID,
AddressTypeID,
AddressLabel,
AddressIsDefaultDelivery,
AddressLine1,
AddressLine2,
AddressCity,
AddressStateID,
AddressZIPCode,
AddressIsDeleted,
AddressAddedOn
Line1,
Line2,
City,
StateID,
ZIPCode,
IsDeleted,
AddedOn
) VALUES (
:addressId,
:userId,
@ -117,7 +117,7 @@ try {
}, { datasource: "payfrit" });
// Get state info for response
qState = queryExecute("SELECT tt_StateAbbreviation as StateAbbreviation, tt_StateName as StateName FROM tt_States WHERE tt_StateID = :stateId", {
qState = queryExecute("SELECT Abbreviation as StateAbbreviation, Name as StateName FROM tt_States WHERE ID = :stateId", {
stateId: { value: stateId, cfsqltype: "cf_sql_integer" }
}, { datasource: "payfrit" });

View file

@ -76,11 +76,11 @@ if (addressId <= 0) {
try {
// First, get the address details so we can find all matching duplicates
qAddr = queryExecute("
SELECT AddressLine1, AddressLine2, AddressCity, AddressStateID, AddressZIPCode
SELECT Line1, Line2, City, StateID, ZIPCode
FROM Addresses
WHERE AddressID = :addressId
AND AddressUserID = :userId
AND AddressIsDeleted = 0
WHERE ID = :addressId
AND UserID = :userId
AND IsDeleted = 0
", {
addressId: { value = addressId, cfsqltype = "cf_sql_integer" },
userId: { value = userId, cfsqltype = "cf_sql_integer" }
@ -93,21 +93,21 @@ try {
// Soft-delete ALL addresses that match the same Line1, Line2, City, StateID, ZIPCode
qDelete = queryExecute("
UPDATE Addresses
SET AddressIsDeleted = 1
WHERE AddressUserID = :userId
AND AddressLine1 = :line1
AND AddressLine2 = :line2
AND AddressCity = :city
AND AddressStateID = :stateId
AND AddressZIPCode = :zip
AND AddressIsDeleted = 0
SET IsDeleted = 1
WHERE UserID = :userId
AND Line1 = :line1
AND Line2 = :line2
AND City = :city
AND StateID = :stateId
AND ZIPCode = :zip
AND IsDeleted = 0
", {
userId: { value = userId, cfsqltype = "cf_sql_integer" },
line1: { value = qAddr.AddressLine1, cfsqltype = "cf_sql_varchar", null = !len(qAddr.AddressLine1) },
line2: { value = qAddr.AddressLine2, cfsqltype = "cf_sql_varchar", null = !len(qAddr.AddressLine2) },
city: { value = qAddr.AddressCity, cfsqltype = "cf_sql_varchar", null = !len(qAddr.AddressCity) },
stateId: { value = qAddr.AddressStateID, cfsqltype = "cf_sql_integer" },
zip: { value = qAddr.AddressZIPCode, cfsqltype = "cf_sql_varchar", null = !len(qAddr.AddressZIPCode) }
line1: { value = qAddr.Line1, cfsqltype = "cf_sql_varchar", null = !len(qAddr.Line1) },
line2: { value = qAddr.Line2, cfsqltype = "cf_sql_varchar", null = !len(qAddr.Line2) },
city: { value = qAddr.City, cfsqltype = "cf_sql_varchar", null = !len(qAddr.City) },
stateId: { value = qAddr.StateID, cfsqltype = "cf_sql_integer" },
zip: { value = qAddr.ZIPCode, cfsqltype = "cf_sql_varchar", null = !len(qAddr.ZIPCode) }
});
writeOutput(serializeJSON({

View file

@ -49,22 +49,22 @@ try {
// Get user's delivery addresses
qAddresses = queryExecute("
SELECT
a.AddressID,
a.AddressIsDefaultDelivery,
a.AddressLine1,
a.AddressLine2,
a.AddressCity,
a.AddressStateID,
s.tt_StateAbbreviation as StateAbbreviation,
s.tt_StateName as StateName,
a.AddressZIPCode
a.ID,
a.IsDefaultDelivery,
a.Line1,
a.Line2,
a.City,
a.StateID,
s.Abbreviation as StateAbbreviation,
s.Name as StateName,
a.ZIPCode
FROM Addresses a
LEFT JOIN tt_States s ON a.AddressStateID = s.tt_StateID
WHERE a.AddressUserID = :userId
AND (a.AddressBusinessID = 0 OR a.AddressBusinessID IS NULL)
LEFT JOIN tt_States s ON a.StateID = s.ID
WHERE a.UserID = :userId
AND (a.BusinessID = 0 OR a.BusinessID IS NULL)
AND a.AddressTypeID = 2
AND a.AddressIsDeleted = 0
ORDER BY a.AddressIsDefaultDelivery DESC, a.AddressID DESC
AND a.IsDeleted = 0
ORDER BY a.IsDefaultDelivery DESC, a.ID DESC
", {
userId: { value = userId, cfsqltype = "cf_sql_integer" }
});
@ -72,15 +72,15 @@ try {
addresses = [];
for (row in qAddresses) {
arrayAppend(addresses, {
"AddressID": row.AddressID,
"IsDefault": row.AddressIsDefaultDelivery == 1,
"Line1": row.AddressLine1,
"Line2": row.AddressLine2 ?: "",
"City": row.AddressCity,
"StateID": row.AddressStateID,
"AddressID": row.ID,
"IsDefault": row.IsDefaultDelivery == 1,
"Line1": row.Line1,
"Line2": row.Line2 ?: "",
"City": row.City,
"StateID": row.StateID,
"StateAbbr": row.StateAbbreviation ?: "",
"ZIPCode": row.AddressZIPCode,
"DisplayText": row.AddressLine1 & (len(row.AddressLine2) ? ", " & row.AddressLine2 : "") & ", " & row.AddressCity & ", " & (row.StateAbbreviation ?: "") & " " & row.AddressZIPCode
"ZIPCode": row.ZIPCode,
"DisplayText": row.Line1 & (len(row.Line2) ? ", " & row.Line2 : "") & ", " & row.City & ", " & (row.StateAbbreviation ?: "") & " " & row.ZIPCode
});
}

View file

@ -41,11 +41,11 @@ try {
// Verify address belongs to user
qCheck = queryExecute("
SELECT AddressID
SELECT ID
FROM Addresses
WHERE AddressID = :addressId
AND AddressUserID = :userId
AND AddressIsDeleted = 0
WHERE ID = :addressId
AND UserID = :userId
AND IsDeleted = 0
", {
addressId: { value: addressId, cfsqltype: "cf_sql_integer" },
userId: { value: userId, cfsqltype: "cf_sql_integer" }
@ -64,8 +64,8 @@ try {
queryExecute("
UPDATE Addresses
SET AddressIsDefaultDelivery = 0
WHERE AddressUserID = :userId
AND (AddressBusinessID = 0 OR AddressBusinessID IS NULL)
WHERE UserID = :userId
AND (BusinessID = 0 OR BusinessID IS NULL)
AND AddressTypeID LIKE '%2%'
", {
userId: { value: userId, cfsqltype: "cf_sql_integer" }
@ -75,7 +75,7 @@ try {
queryExecute("
UPDATE Addresses
SET AddressIsDefaultDelivery = 1
WHERE AddressID = :addressId
WHERE ID = :addressId
", {
addressId: { value: addressId, cfsqltype: "cf_sql_integer" }
}, { datasource: "payfrit" });

View file

@ -2,19 +2,13 @@
<cfsetting enablecfoutputonly="true">
<cfcontent type="application/json; charset=utf-8">
<!--- List US states for address forms (cached 24h - static data) --->
<!--- List US states for address forms --->
<cfscript>
try {
cached = appCacheGet("states", 86400);
if (!isNull(cached)) {
writeOutput(cached);
abort;
}
qStates = queryExecute("
SELECT tt_StateID as StateID, tt_StateAbbreviation as StateAbbreviation, tt_StateName as StateName
SELECT tt_StateID as StateID, Abbreviation as StateAbbreviation, Name as StateName
FROM tt_States
ORDER BY tt_StateName
ORDER BY Name
", {}, { datasource: "payfrit" });
states = [];
@ -26,12 +20,10 @@ try {
});
}
jsonResponse = serializeJSON({
writeOutput(serializeJSON({
"OK": true,
"STATES": states
});
appCachePut("states", jsonResponse);
writeOutput(jsonResponse);
}));
} catch (any e) {
writeOutput(serializeJSON({

View file

@ -2,25 +2,25 @@
<cfcontent type="application/json; charset=utf-8" reset="true">
<cfscript>
// Add TaskCategoryIsActive column to TaskCategories table
// Add IsActive column to TaskCategories table
try {
// Check if column exists
qCheck = queryExecute("
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'payfrit'
AND TABLE_NAME = 'TaskCategories'
AND COLUMN_NAME = 'TaskCategoryIsActive'
AND COLUMN_NAME = 'IsActive'
", [], { datasource: "payfrit" });
if (qCheck.recordCount == 0) {
queryExecute("
ALTER TABLE TaskCategories
ADD COLUMN TaskCategoryIsActive TINYINT(1) NOT NULL DEFAULT 1
ADD COLUMN IsActive TINYINT(1) NOT NULL DEFAULT 1
", [], { datasource: "payfrit" });
writeOutput(serializeJSON({
"OK": true,
"MESSAGE": "Column TaskCategoryIsActive added to TaskCategories"
"MESSAGE": "Column IsActive added to TaskCategories"
}));
} else {
writeOutput(serializeJSON({

View file

@ -8,9 +8,9 @@
* Add Schedule Fields to Categories Table
*
* Adds time-based scheduling fields:
* - CategoryScheduleStart: TIME - Start time when category is available (e.g., 06:00:00 for breakfast)
* - CategoryScheduleEnd: TIME - End time when category stops being available (e.g., 11:00:00)
* - CategoryScheduleDays: VARCHAR(20) - Comma-separated list of day IDs (1=Sun, 2=Mon, etc.) or NULL for all days
* - ScheduleStart: TIME - Start time when category is available (e.g., 06:00:00 for breakfast)
* - ScheduleEnd: TIME - End time when category stops being available (e.g., 11:00:00)
* - ScheduleDays: VARCHAR(20) - Comma-separated list of day IDs (1=Sun, 2=Mon, etc.) or NULL for all days
*
* Run this once to migrate the schema.
*/
@ -24,38 +24,38 @@ try {
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'payfrit'
AND TABLE_NAME = 'Categories'
AND COLUMN_NAME IN ('CategoryScheduleStart', 'CategoryScheduleEnd', 'CategoryScheduleDays')
AND COLUMN_NAME IN ('ScheduleStart', 'ScheduleEnd', 'ScheduleDays')
", {}, { datasource: "payfrit" });
existingCols = valueList(qCheck.COLUMN_NAME);
added = [];
// Add CategoryScheduleStart if not exists
if (!listFindNoCase(existingCols, "CategoryScheduleStart")) {
// Add ScheduleStart if not exists
if (!listFindNoCase(existingCols, "ScheduleStart")) {
queryExecute("
ALTER TABLE Categories
ADD COLUMN CategoryScheduleStart TIME NULL
ADD COLUMN ScheduleStart TIME NULL
", {}, { datasource: "payfrit" });
arrayAppend(added, "CategoryScheduleStart");
arrayAppend(added, "ScheduleStart");
}
// Add CategoryScheduleEnd if not exists
if (!listFindNoCase(existingCols, "CategoryScheduleEnd")) {
// Add ScheduleEnd if not exists
if (!listFindNoCase(existingCols, "ScheduleEnd")) {
queryExecute("
ALTER TABLE Categories
ADD COLUMN CategoryScheduleEnd TIME NULL
ADD COLUMN ScheduleEnd TIME NULL
", {}, { datasource: "payfrit" });
arrayAppend(added, "CategoryScheduleEnd");
arrayAppend(added, "ScheduleEnd");
}
// Add CategoryScheduleDays if not exists
if (!listFindNoCase(existingCols, "CategoryScheduleDays")) {
// Add ScheduleDays if not exists
if (!listFindNoCase(existingCols, "ScheduleDays")) {
queryExecute("
ALTER TABLE Categories
ADD COLUMN CategoryScheduleDays VARCHAR(20) NULL
ADD COLUMN ScheduleDays VARCHAR(20) NULL
", {}, { datasource: "payfrit" });
arrayAppend(added, "CategoryScheduleDays");
arrayAppend(added, "ScheduleDays");
}
response["OK"] = true;

View file

@ -15,8 +15,8 @@ try {
// Find the Fountain Soda item we created
qFountain = queryExecute("
SELECT ItemID, ItemName FROM Items
WHERE ItemBusinessID = :bizId AND ItemName = 'Fountain Soda'
SELECT ID, Name FROM Items
WHERE BusinessID = :bizId AND Name = 'Fountain Soda'
", { bizId: bigDeansBusinessId }, { datasource: "payfrit" });
if (qFountain.recordCount == 0) {
@ -31,13 +31,13 @@ try {
// Update Fountain Soda to require child selection and be collapsible
queryExecute("
UPDATE Items
SET ItemRequiresChildSelection = 1, ItemIsCollapsible = 1
SET RequiresChildSelection = 1, IsCollapsible = 1
WHERE ItemID = :itemId
", { itemId: fountainId }, { datasource: "payfrit" });
// Check if modifiers already exist
qExisting = queryExecute("
SELECT COUNT(*) as cnt FROM Items WHERE ItemParentItemID = :parentId
SELECT COUNT(*) as cnt FROM Items WHERE ParentItemID = :parentId
", { parentId: fountainId }, { datasource: "payfrit" });
if (qExisting.cnt > 0) {
@ -53,10 +53,10 @@ try {
queryExecute("
INSERT INTO Items (
ItemID, ItemBusinessID, ItemCategoryID, ItemParentItemID,
ItemName, ItemDescription, ItemPrice, ItemIsActive,
ItemSortOrder, ItemIsCollapsible, ItemRequiresChildSelection,
ItemMaxNumSelectionReq, ItemAddedOn
ItemID, BusinessID, CategoryID, ParentItemID,
Name, Description, Price, IsActive,
SortOrder, IsCollapsible, RequiresChildSelection,
MaxNumSelectionReq, AddedOn
) VALUES (
:itemId, :bizId, 0, :parentId,
'Size', 'Choose your size', 0, 1,
@ -80,10 +80,10 @@ try {
qMaxItem = queryExecute("SELECT COALESCE(MAX(ItemID), 0) + 1 as nextId FROM Items", {}, { datasource: "payfrit" });
queryExecute("
INSERT INTO Items (
ItemID, ItemBusinessID, ItemCategoryID, ItemParentItemID,
ItemName, ItemDescription, ItemPrice, ItemIsActive,
ItemSortOrder, ItemIsCollapsible, ItemIsCheckedByDefault,
ItemAddedOn
ItemID, BusinessID, CategoryID, ParentItemID,
Name, Description, Price, IsActive,
SortOrder, IsCollapsible, IsCheckedByDefault,
AddedOn
) VALUES (
:itemId, :bizId, 0, :parentId,
:name, '', :price, 1,
@ -108,10 +108,10 @@ try {
queryExecute("
INSERT INTO Items (
ItemID, ItemBusinessID, ItemCategoryID, ItemParentItemID,
ItemName, ItemDescription, ItemPrice, ItemIsActive,
ItemSortOrder, ItemIsCollapsible, ItemRequiresChildSelection,
ItemMaxNumSelectionReq, ItemAddedOn
ItemID, BusinessID, CategoryID, ParentItemID,
Name, Description, Price, IsActive,
SortOrder, IsCollapsible, RequiresChildSelection,
MaxNumSelectionReq, AddedOn
) VALUES (
:itemId, :bizId, 0, :parentId,
'Flavor', 'Choose your drink', 0, 1,
@ -139,10 +139,10 @@ try {
qMaxItem = queryExecute("SELECT COALESCE(MAX(ItemID), 0) + 1 as nextId FROM Items", {}, { datasource: "payfrit" });
queryExecute("
INSERT INTO Items (
ItemID, ItemBusinessID, ItemCategoryID, ItemParentItemID,
ItemName, ItemDescription, ItemPrice, ItemIsActive,
ItemSortOrder, ItemIsCollapsible, ItemIsCheckedByDefault,
ItemAddedOn
ItemID, BusinessID, CategoryID, ParentItemID,
Name, Description, Price, IsActive,
SortOrder, IsCollapsible, IsCheckedByDefault,
AddedOn
) VALUES (
:itemId, :bizId, 0, :parentId,
:name, '', 0, 1,

View file

@ -5,7 +5,7 @@
<cfscript>
/**
* Add ItemCategoryID column to Items table
* Add CategoryID column to Items table
*/
response = { "OK": false };
@ -17,30 +17,30 @@ try {
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'payfrit'
AND TABLE_NAME = 'Items'
AND COLUMN_NAME = 'ItemCategoryID'
AND COLUMN_NAME = 'CategoryID'
", {}, { datasource: "payfrit" });
if (qCheck.recordCount > 0) {
response["OK"] = true;
response["MESSAGE"] = "ItemCategoryID column already exists";
response["MESSAGE"] = "CategoryID column already exists";
} else {
// Add the column
queryExecute("
ALTER TABLE Items
ADD COLUMN ItemCategoryID INT NULL DEFAULT 0 AFTER ItemParentItemID
ADD COLUMN CategoryID INT NULL DEFAULT 0 AFTER ParentItemID
", {}, { datasource: "payfrit" });
// Add index for performance
try {
queryExecute("
CREATE INDEX idx_items_categoryid ON Items(ItemCategoryID)
CREATE INDEX idx_items_categoryid ON Items(CategoryID)
", {}, { datasource: "payfrit" });
} catch (any indexErr) {
// Index might already exist
}
response["OK"] = true;
response["MESSAGE"] = "ItemCategoryID column added successfully";
response["MESSAGE"] = "CategoryID column added successfully";
}
} catch (any e) {

View file

@ -6,7 +6,7 @@
try {
// Check if columns already exist
checkCols = queryExecute(
"SHOW COLUMNS FROM Addresses LIKE 'AddressLat'",
"SHOW COLUMNS FROM Addresses LIKE 'Latitude'",
[],
{ datasource = "payfrit" }
);
@ -15,8 +15,8 @@ try {
// Add the columns
queryExecute(
"ALTER TABLE Addresses
ADD COLUMN AddressLat DECIMAL(10,7) NULL,
ADD COLUMN AddressLng DECIMAL(10,7) NULL",
ADD COLUMN Latitude DECIMAL(10,7) NULL,
ADD COLUMN Longitude DECIMAL(10,7) NULL",
[],
{ datasource = "payfrit" }
);

View file

@ -9,18 +9,18 @@ try {
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'payfrit'
AND TABLE_NAME = 'tt_TaskTypes'
AND COLUMN_NAME = 'tt_TaskTypeCategoryID'
AND COLUMN_NAME = 'TaskCategoryID'
", [], { datasource: "payfrit" });
if (qCheck.recordCount == 0) {
queryExecute("
ALTER TABLE tt_TaskTypes
ADD COLUMN tt_TaskTypeCategoryID INT NULL
ADD COLUMN TaskCategoryID INT NULL
", [], { datasource: "payfrit" });
writeOutput(serializeJSON({
"OK": true,
"MESSAGE": "Column tt_TaskTypeCategoryID added to tt_TaskTypes"
"MESSAGE": "Column TaskCategoryID added to tt_TaskTypes"
}));
} else {
writeOutput(serializeJSON({

View file

@ -3,7 +3,7 @@
<cfcontent type="application/json; charset=utf-8" reset="true">
<cfscript>
// Add TaskSourceType and TaskSourceID columns to Tasks table
// Add SourceType and SourceID columns to Tasks table
// These are needed for chat persistence feature
result = { "OK": true, "STEPS": [] };
@ -15,30 +15,30 @@ try {
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'payfrit'
AND TABLE_NAME = 'Tasks'
AND COLUMN_NAME IN ('TaskSourceType', 'TaskSourceID')
AND COLUMN_NAME IN ('SourceType', 'SourceID')
", [], { datasource: "payfrit" });
existingCols = valueList(cols.COLUMN_NAME);
arrayAppend(result.STEPS, "Existing columns: #existingCols#");
// Add TaskSourceType if missing
if (!listFindNoCase(existingCols, "TaskSourceType")) {
// Add SourceType if missing
if (!listFindNoCase(existingCols, "SourceType")) {
queryExecute("
ALTER TABLE Tasks ADD COLUMN TaskSourceType VARCHAR(50) NULL
ALTER TABLE Tasks ADD COLUMN SourceType VARCHAR(50) NULL
", [], { datasource: "payfrit" });
arrayAppend(result.STEPS, "Added TaskSourceType column");
arrayAppend(result.STEPS, "Added SourceType column");
} else {
arrayAppend(result.STEPS, "TaskSourceType already exists");
arrayAppend(result.STEPS, "SourceType already exists");
}
// Add TaskSourceID if missing
if (!listFindNoCase(existingCols, "TaskSourceID")) {
// Add SourceID if missing
if (!listFindNoCase(existingCols, "SourceID")) {
queryExecute("
ALTER TABLE Tasks ADD COLUMN TaskSourceID INT NULL
ALTER TABLE Tasks ADD COLUMN SourceID INT NULL
", [], { datasource: "payfrit" });
arrayAppend(result.STEPS, "Added TaskSourceID column");
arrayAppend(result.STEPS, "Added SourceID column");
} else {
arrayAppend(result.STEPS, "TaskSourceID already exists");
arrayAppend(result.STEPS, "SourceID already exists");
}
// Verify columns now exist
@ -47,7 +47,7 @@ try {
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'payfrit'
AND TABLE_NAME = 'Tasks'
AND COLUMN_NAME IN ('TaskSourceType', 'TaskSourceID')
AND COLUMN_NAME IN ('SourceType', 'SourceID')
", [], { datasource: "payfrit" });
result.COLUMNS = [];

View file

@ -6,49 +6,49 @@
// Show all beacons with their current business/service point assignments
q = queryExecute("
SELECT
b.BeaconID,
b.BeaconUUID,
b.BeaconName,
lt.BusinessID,
lt.ServicePointID,
biz.BusinessName,
sp.ServicePointName
b.ID,
b.UUID,
b.Name,
sp_link.BusinessID,
sp_link.ID,
biz.Name,
sp.Name
FROM Beacons b
LEFT JOIN lt_Beacon_Businesses_ServicePoints lt ON lt.BeaconID = b.BeaconID
LEFT JOIN Businesses biz ON biz.BusinessID = lt.BusinessID
LEFT JOIN ServicePoints sp ON sp.ServicePointID = lt.ServicePointID
WHERE b.BeaconIsActive = 1
ORDER BY b.BeaconID
LEFT JOIN ServicePoints sp_link ON sp_link.BeaconID = b.ID
LEFT JOIN Businesses biz ON biz.ID = sp_link.BusinessID
LEFT JOIN ServicePoints sp ON sp.ID = sp_link.ID
WHERE b.IsActive = 1
ORDER BY b.ID
", {}, { datasource: "payfrit" });
rows = [];
for (row in q) {
arrayAppend(rows, {
"BeaconID": row.BeaconID,
"BeaconUUID": row.BeaconUUID,
"BeaconName": row.BeaconName ?: "",
"BeaconID": row.ID,
"UUID": row.UUID,
"Name": row.Name ?: "",
"BusinessID": row.BusinessID ?: 0,
"BusinessName": row.BusinessName ?: "",
"Name": row.Name ?: "",
"ServicePointID": row.ServicePointID ?: 0,
"ServicePointName": row.ServicePointName ?: ""
"Name": row.Name ?: ""
});
}
// Also get service points for reference
spQuery = queryExecute("
SELECT sp.ServicePointID, sp.ServicePointName, sp.ServicePointBusinessID, b.BusinessName
SELECT sp.ID, sp.Name, sp.BusinessID, b.Name
FROM ServicePoints sp
JOIN Businesses b ON b.BusinessID = sp.ServicePointBusinessID
ORDER BY sp.ServicePointBusinessID, sp.ServicePointID
JOIN Businesses b ON b.ID = sp.BusinessID
ORDER BY sp.BusinessID, sp.ID
", {}, { datasource: "payfrit" });
servicePoints = [];
for (sp in spQuery) {
arrayAppend(servicePoints, {
"ServicePointID": sp.ServicePointID,
"ServicePointName": sp.ServicePointName,
"BusinessID": sp.ServicePointBusinessID,
"BusinessName": sp.BusinessName
"ServicePointID": sp.ID,
"Name": sp.Name,
"BusinessID": sp.BusinessID,
"Name": sp.Name
});
}

View file

@ -5,16 +5,16 @@
<cfscript>
// Check Big Dean's owner
q = queryExecute("
SELECT b.BusinessID, b.BusinessName, b.BusinessUserID
SELECT b.ID, b.Name, b.UserID
FROM Businesses b
WHERE b.BusinessID = 27
WHERE b.ID = 27
", {}, { datasource: "payfrit" });
// Get users
users = queryExecute("
SELECT *
FROM Users
ORDER BY UserID
ORDER BY ID
LIMIT 20
", {}, { datasource: "payfrit" });
@ -23,9 +23,9 @@ colNames = users.getColumnNames();
writeOutput(serializeJSON({
"OK": true,
"BigDeans": {
"BusinessID": q.BusinessID,
"BusinessName": q.BusinessName,
"BusinessUserID": q.BusinessUserID
"BusinessID": q.ID,
"Name": q.Name,
"UserID": q.UserID
},
"UserColumns": colNames,
"UserCount": users.recordCount

View file

@ -12,9 +12,9 @@ if (!len(phone)) {
}
q = queryExecute("
SELECT UserID, UserFirstName, UserLastName, UserEmail, UserPhone, UserIsContactVerified
SELECT ID, FirstName, LastName, EmailAddress, ContactNumber, IsContactVerified
FROM Users
WHERE UserPhone = :phone OR UserEmail = :phone
WHERE ContactNumber = :phone OR EmailAddress = :phone
LIMIT 1
", { phone: phone }, { datasource: "payfrit" });
@ -25,11 +25,11 @@ if (q.recordCount EQ 0) {
writeOutput(serializeJSON({
"OK": true,
"UserID": q.UserID,
"FirstName": q.UserFirstName,
"LastName": q.UserLastName,
"Email": q.UserEmail,
"Phone": q.UserPhone,
"Verified": q.UserIsContactVerified
"UserID": q.ID,
"FirstName": q.FirstName,
"LastName": q.LastName,
"Email": q.EmailAddress,
"Phone": q.ContactNumber,
"Verified": q.IsContactVerified
}));
</cfscript>

View file

@ -5,7 +5,8 @@
<cfscript>
/**
* Cleanup Lazy Daisy Beacons
* - Removes duplicate beacons created by setupBeaconTables
* - Unassigns beacons 7, 8, 9 from service points
* - Deletes beacons 7, 8, 9
* - Updates original beacons with proper names
*/
response = { "OK": false, "steps": [] };
@ -13,52 +14,50 @@ response = { "OK": false, "steps": [] };
try {
lazyDaisyID = 37;
// Delete duplicate assignments for beacons 7, 8, 9
// Unassign beacons 7, 8, 9 from any service points
queryExecute("
DELETE FROM lt_Beacon_Businesses_ServicePoints
UPDATE ServicePoints
SET BeaconID = NULL, AssignedByUserID = NULL
WHERE BeaconID IN (7, 8, 9) AND BusinessID = :bizId
", { bizId: lazyDaisyID }, { datasource: "payfrit" });
response.steps.append("Deleted duplicate assignments for beacons 7, 8, 9");
response.steps.append("Unassigned beacons 7, 8, 9 from service points");
// Delete duplicate beacons 7, 8, 9
queryExecute("
DELETE FROM Beacons
WHERE BeaconID IN (7, 8, 9) AND BeaconBusinessID = :bizId
WHERE ID IN (7, 8, 9) AND BusinessID = :bizId
", { bizId: lazyDaisyID }, { datasource: "payfrit" });
response.steps.append("Deleted duplicate beacons 7, 8, 9");
// Update original beacons with names based on their service point assignments
// Beacon 4 -> Table 1 (ServicePointID 4)
// Beacon 5 -> Table 2 (ServicePointID 5)
// Beacon 6 -> Table 3 (ServicePointID 6)
queryExecute("
UPDATE Beacons SET BeaconName = 'Beacon - Table 1'
WHERE BeaconID = 4 AND BeaconBusinessID = :bizId
UPDATE Beacons SET Name = 'Beacon - Table 1'
WHERE ID = 4 AND BusinessID = :bizId
", { bizId: lazyDaisyID }, { datasource: "payfrit" });
response.steps.append("Updated Beacon 4 name to 'Beacon - Table 1'");
queryExecute("
UPDATE Beacons SET BeaconName = 'Beacon - Table 2'
WHERE BeaconID = 5 AND BeaconBusinessID = :bizId
UPDATE Beacons SET Name = 'Beacon - Table 2'
WHERE ID = 5 AND BusinessID = :bizId
", { bizId: lazyDaisyID }, { datasource: "payfrit" });
response.steps.append("Updated Beacon 5 name to 'Beacon - Table 2'");
queryExecute("
UPDATE Beacons SET BeaconName = 'Beacon - Table 3'
WHERE BeaconID = 6 AND BeaconBusinessID = :bizId
UPDATE Beacons SET Name = 'Beacon - Table 3'
WHERE ID = 6 AND BusinessID = :bizId
", { bizId: lazyDaisyID }, { datasource: "payfrit" });
response.steps.append("Updated Beacon 6 name to 'Beacon - Table 3'");
// Get final status
qFinal = queryExecute("
SELECT lt.BeaconID, b.BeaconUUID, b.BeaconName, lt.BusinessID, biz.BusinessName, lt.ServicePointID, sp.ServicePointName
FROM lt_Beacon_Businesses_ServicePoints lt
JOIN Beacons b ON b.BeaconID = lt.BeaconID
JOIN Businesses biz ON biz.BusinessID = lt.BusinessID
LEFT JOIN ServicePoints sp ON sp.ServicePointID = lt.ServicePointID
WHERE lt.BusinessID = :bizId
ORDER BY lt.BeaconID
SELECT sp.ID AS ServicePointID, sp.BeaconID, sp.BusinessID,
b.Name AS BeaconName, b.UUID, sp.Name AS ServicePointName,
biz.Name AS BusinessName
FROM ServicePoints sp
JOIN Beacons b ON b.ID = sp.BeaconID
JOIN Businesses biz ON biz.ID = sp.BusinessID
WHERE sp.BusinessID = :bizId AND sp.BeaconID IS NOT NULL
ORDER BY sp.BeaconID
", { bizId: lazyDaisyID }, { datasource: "payfrit" });
beacons = [];
@ -66,8 +65,9 @@ try {
arrayAppend(beacons, {
"BeaconID": qFinal.BeaconID[i],
"BeaconName": qFinal.BeaconName[i],
"UUID": qFinal.BeaconUUID[i],
"UUID": qFinal.UUID[i],
"BusinessName": qFinal.BusinessName[i],
"ServicePointID": qFinal.ServicePointID[i],
"ServicePointName": qFinal.ServicePointName[i]
});
}

View file

@ -13,10 +13,10 @@
* Cleanup Categories - Final step after migration verification
*
* This script:
* 1. Verifies all Items have ItemBusinessID set
* 1. Verifies all Items have BusinessID set
* 2. Finds orphan items (ParentID=0, no children, not in links)
* 3. Drops ItemCategoryID column
* 4. Drops ItemIsModifierTemplate column (derived from ItemTemplateLinks now)
* 3. Drops CategoryID column
* 4. Drops IsModifierTemplate column (derived from lt_ItemID_TemplateItemID now)
* 5. Drops Categories table
*
* Query param: ?confirm=YES to actually execute (otherwise shows verification only)
@ -30,7 +30,7 @@ try {
// Verification Step 1: Check for items without BusinessID
qNoBusinessID = queryExecute("
SELECT COUNT(*) as cnt FROM Items
WHERE ItemBusinessID IS NULL OR ItemBusinessID = 0
WHERE BusinessID IS NULL OR BusinessID = 0
", {}, { datasource: "payfrit" });
response.verification["itemsWithoutBusinessID"] = qNoBusinessID.cnt;
@ -46,38 +46,38 @@ try {
qCategoryItems = queryExecute("
SELECT COUNT(DISTINCT p.ItemID) as cnt
FROM Items p
INNER JOIN Items c ON c.ItemParentItemID = p.ItemID
WHERE p.ItemParentItemID = 0
AND p.ItemBusinessID > 0
INNER JOIN Items c ON c.ParentItemID = p.ItemID
WHERE p.ParentItemID = 0
AND p.BusinessID > 0
AND NOT EXISTS (
SELECT 1 FROM ItemTemplateLinks tl WHERE tl.TemplateItemID = p.ItemID
SELECT 1 FROM lt_ItemID_TemplateItemID tl WHERE tl.TemplateItemID = p.ItemID
)
", {}, { datasource: "payfrit" });
response.verification["categoryItemsCreated"] = qCategoryItems.cnt;
// Verification Step 4: Check templates exist (in ItemTemplateLinks)
// Verification Step 4: Check templates exist (in lt_ItemID_TemplateItemID)
qTemplates = queryExecute("
SELECT COUNT(DISTINCT tl.TemplateItemID) as cnt
FROM ItemTemplateLinks tl
FROM lt_ItemID_TemplateItemID tl
INNER JOIN Items t ON t.ItemID = tl.TemplateItemID
", {}, { datasource: "payfrit" });
response.verification["templatesInLinks"] = qTemplates.cnt;
// Verification Step 5: Find orphans at ParentID=0
// Orphan = ParentID=0, no children pointing to it, not in ItemTemplateLinks
// Orphan = ParentID=0, no children pointing to it, not in lt_ItemID_TemplateItemID
qOrphans = queryExecute("
SELECT i.ItemID, i.ItemName, i.ItemBusinessID
SELECT i.ID, i.Name, i.BusinessID
FROM Items i
WHERE i.ItemParentItemID = 0
WHERE i.ParentItemID = 0
AND NOT EXISTS (
SELECT 1 FROM Items child WHERE child.ItemParentItemID = i.ItemID
SELECT 1 FROM Items child WHERE child.ParentItemID = i.ID
)
AND NOT EXISTS (
SELECT 1 FROM ItemTemplateLinks tl WHERE tl.TemplateItemID = i.ItemID
SELECT 1 FROM lt_ItemID_TemplateItemID tl WHERE tl.TemplateItemID = i.ID
)
ORDER BY i.ItemBusinessID, i.ItemName
ORDER BY i.BusinessID, i.Name
", {}, { datasource: "payfrit" });
response.verification["orphanCount"] = qOrphans.recordCount;
@ -85,8 +85,8 @@ try {
for (orphan in qOrphans) {
arrayAppend(response.orphans, {
"ItemID": orphan.ItemID,
"ItemName": orphan.ItemName,
"BusinessID": orphan.ItemBusinessID
"Name": orphan.Name,
"BusinessID": orphan.BusinessID
});
}
@ -96,7 +96,7 @@ try {
if (!safeToCleanup) {
arrayAppend(response.steps, "VERIFICATION FAILED - Cannot cleanup yet");
arrayAppend(response.steps, "- " & qNoBusinessID.cnt & " items still missing ItemBusinessID");
arrayAppend(response.steps, "- " & qNoBusinessID.cnt & " items still missing BusinessID");
response["OK"] = false;
writeOutput(serializeJSON(response));
abort;
@ -119,31 +119,31 @@ try {
// Execute cleanup
arrayAppend(response.steps, "Executing cleanup...");
// Step 1: Drop ItemCategoryID column
// Step 1: Drop CategoryID column
try {
queryExecute("
ALTER TABLE Items DROP COLUMN ItemCategoryID
ALTER TABLE Items DROP COLUMN CategoryID
", {}, { datasource: "payfrit" });
arrayAppend(response.steps, "Dropped ItemCategoryID column from Items");
arrayAppend(response.steps, "Dropped CategoryID column from Items");
} catch (any e) {
if (findNoCase("check that column", e.message) || findNoCase("Unknown column", e.message)) {
arrayAppend(response.steps, "ItemCategoryID column already dropped");
arrayAppend(response.steps, "CategoryID column already dropped");
} else {
arrayAppend(response.steps, "Warning dropping ItemCategoryID: " & e.message);
arrayAppend(response.steps, "Warning dropping CategoryID: " & e.message);
}
}
// Step 2: Drop ItemIsModifierTemplate column (now derived from ItemTemplateLinks)
// Step 2: Drop IsModifierTemplate column (now derived from lt_ItemID_TemplateItemID)
try {
queryExecute("
ALTER TABLE Items DROP COLUMN ItemIsModifierTemplate
ALTER TABLE Items DROP COLUMN IsModifierTemplate
", {}, { datasource: "payfrit" });
arrayAppend(response.steps, "Dropped ItemIsModifierTemplate column from Items");
arrayAppend(response.steps, "Dropped IsModifierTemplate column from Items");
} catch (any e) {
if (findNoCase("check that column", e.message) || findNoCase("Unknown column", e.message)) {
arrayAppend(response.steps, "ItemIsModifierTemplate column already dropped");
arrayAppend(response.steps, "IsModifierTemplate column already dropped");
} else {
arrayAppend(response.steps, "Warning dropping ItemIsModifierTemplate: " & e.message);
arrayAppend(response.steps, "Warning dropping IsModifierTemplate: " & e.message);
}
}

View file

@ -34,8 +34,8 @@ if (businessId <= 0) {
try {
// Find duplicate UserIDs for this business (keep the one with highest status or oldest)
qDupes = queryExecute("
SELECT UserID, COUNT(*) as cnt, MIN(EmployeeID) as keepId
FROM lt_Users_Businesses_Employees
SELECT ID, COUNT(*) as cnt, MIN(ID) as keepId
FROM Employees
WHERE BusinessID = ?
GROUP BY UserID
HAVING COUNT(*) > 1
@ -45,8 +45,8 @@ try {
for (row in qDupes) {
// Delete all but the one we want to keep (the one with lowest EmployeeID)
qDel = queryExecute("
DELETE FROM lt_Users_Businesses_Employees
WHERE BusinessID = ? AND UserID = ? AND EmployeeID != ?
DELETE FROM Employees
WHERE BusinessID = ? AND UserID = ? AND ID != ?
", [
{ value: businessId, cfsqltype: "cf_sql_integer" },
{ value: row.UserID, cfsqltype: "cf_sql_integer" },
@ -57,19 +57,19 @@ try {
// Get remaining employees
qRemaining = queryExecute("
SELECT e.EmployeeID, e.UserID, u.UserFirstName, u.UserLastName
FROM lt_Users_Businesses_Employees e
JOIN Users u ON e.UserID = u.UserID
SELECT e.ID, e.UserID, u.FirstName, u.LastName
FROM Employees e
JOIN Users u ON e.UserID = u.ID
WHERE e.BusinessID = ?
ORDER BY e.EmployeeID
ORDER BY e.ID
", [{ value: businessId, cfsqltype: "cf_sql_integer" }], { datasource: "payfrit" });
remaining = [];
for (r in qRemaining) {
arrayAppend(remaining, {
"EmployeeID": r.EmployeeID,
"EmployeeID": r.ID,
"UserID": r.UserID,
"Name": trim(r.UserFirstName & " " & r.UserLastName)
"Name": trim(r.FirstName & " " & r.LastName)
});
}

View file

@ -9,106 +9,120 @@ try {
// Keep only Lazy Daisy (BusinessID 37)
keepBusinessID = 37;
// First, reassign all beacons to Lazy Daisy
// Unassign all beacons from service points of other businesses
queryExecute("
UPDATE lt_Beacon_Businesses_ServicePoints
SET BusinessID = :keepID
UPDATE ServicePoints
SET BeaconID = NULL, AssignedByUserID = NULL
WHERE BusinessID != :keepID AND BeaconID IS NOT NULL
", { keepID: keepBusinessID }, { datasource: "payfrit" });
response.steps.append("Reassigned all beacons to Lazy Daisy");
response.steps.append("Unassigned beacons from other businesses' service points");
// Get list of businesses to delete
qBiz = queryExecute("
SELECT BusinessID, BusinessName FROM Businesses WHERE BusinessID != :keepID
SELECT ID, Name FROM Businesses WHERE ID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" });
deletedBusinesses = [];
for (i = 1; i <= qBiz.recordCount; i++) {
arrayAppend(deletedBusinesses, qBiz.BusinessName[i]);
arrayAppend(deletedBusinesses, qBiz.Name[i]);
}
response.steps.append("Found " & qBiz.recordCount & " businesses to delete");
// Delete related data first (foreign key constraints)
// Delete ItemTemplateLinks for items from other businesses
// Delete lt_ItemID_TemplateItemID for items from other businesses
queryExecute("
DELETE itl FROM ItemTemplateLinks itl
JOIN Items i ON i.ItemID = itl.ItemID
WHERE i.ItemBusinessID != :keepID
DELETE itl FROM lt_ItemID_TemplateItemID itl
JOIN Items i ON i.ID = itl.ItemID
WHERE i.BusinessID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" });
response.steps.append("Deleted ItemTemplateLinks for other businesses");
response.steps.append("Deleted lt_ItemID_TemplateItemID for other businesses");
// Delete Items for other businesses
qItems = queryExecute("
SELECT COUNT(*) as cnt FROM Items WHERE ItemBusinessID != :keepID
SELECT COUNT(*) as cnt FROM Items WHERE BusinessID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" });
queryExecute("
DELETE FROM Items WHERE ItemBusinessID != :keepID
DELETE FROM Items WHERE BusinessID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" });
response.steps.append("Deleted " & qItems.cnt & " items from other businesses");
// Delete Categories for other businesses
queryExecute("
DELETE FROM Categories WHERE CategoryBusinessID != :keepID
DELETE FROM Categories WHERE BusinessID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" });
response.steps.append("Deleted categories from other businesses");
// Delete Hours for other businesses
queryExecute("
DELETE FROM Hours WHERE HoursBusinessID != :keepID
DELETE FROM Hours WHERE BusinessID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" });
response.steps.append("Deleted hours from other businesses");
// Delete Employees for other businesses (skip if table doesn't exist)
// Delete Employees for other businesses
try {
queryExecute("
DELETE FROM Employees WHERE EmployeeBusinessID != :keepID
DELETE FROM Employees WHERE BusinessID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" });
response.steps.append("Deleted employees from other businesses");
} catch (any e) {
response.steps.append("Skipped employees (table may not exist)");
}
// Delete ServicePoints for other businesses (skip if table doesn't exist)
// Delete ServicePoints for other businesses
try {
queryExecute("
DELETE FROM ServicePoints WHERE ServicePointBusinessID != :keepID
DELETE FROM ServicePoints WHERE BusinessID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" });
response.steps.append("Deleted service points from other businesses");
} catch (any e) {
response.steps.append("Skipped service points (table may not exist)");
}
// Delete Stations for other businesses (skip if table doesn't exist)
// Delete Stations for other businesses
try {
queryExecute("
DELETE FROM Stations WHERE StationBusinessID != :keepID
DELETE FROM Stations WHERE BusinessID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" });
response.steps.append("Deleted stations from other businesses");
} catch (any e) {
response.steps.append("Skipped stations (table may not exist)");
}
// Delete beacon-business mappings for other businesses
try {
queryExecute("
DELETE FROM lt_BeaconsID_BusinessesID WHERE BusinessID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" });
response.steps.append("Deleted beacon mappings for other businesses");
} catch (any e) {
response.steps.append("Skipped beacon mappings (table may not exist)");
}
// Finally delete the businesses themselves
queryExecute("
DELETE FROM Businesses WHERE BusinessID != :keepID
DELETE FROM Businesses WHERE ID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" });
response.steps.append("Deleted " & arrayLen(deletedBusinesses) & " businesses");
// Get beacon status
qBeacons = queryExecute("
SELECT lt.BeaconID, b.BeaconUUID, lt.BusinessID, biz.BusinessName, lt.ServicePointID
FROM lt_Beacon_Businesses_ServicePoints lt
JOIN Beacons b ON b.BeaconID = lt.BeaconID
JOIN Businesses biz ON biz.BusinessID = lt.BusinessID
SELECT sp.ID AS ServicePointID, sp.BeaconID, sp.BusinessID,
b.UUID, biz.Name AS BusinessName, sp.Name AS ServicePointName
FROM ServicePoints sp
JOIN Beacons b ON b.ID = sp.BeaconID
JOIN Businesses biz ON biz.ID = sp.BusinessID
WHERE sp.BeaconID IS NOT NULL
", {}, { datasource: "payfrit" });
beacons = [];
for (i = 1; i <= qBeacons.recordCount; i++) {
arrayAppend(beacons, {
"BeaconID": qBeacons.BeaconID[i],
"UUID": qBeacons.BeaconUUID[i],
"UUID": qBeacons.UUID[i],
"BusinessID": qBeacons.BusinessID[i],
"BusinessName": qBeacons.BusinessName[i],
"ServicePointID": qBeacons.ServicePointID[i]
"ServicePointID": qBeacons.ServicePointID[i],
"ServicePointName": qBeacons.ServicePointName[i]
});
}

View file

@ -7,9 +7,9 @@ param name="url.action" default="check"; // "check" or "deactivate"
// Check the item first
qItem = queryExecute("
SELECT ItemID, ItemName, ItemParentItemID, ItemIsActive, ItemIsCollapsible
SELECT ID, Name, ParentItemID, IsActive, IsCollapsible
FROM Items
WHERE ItemID = :itemId
WHERE ID = :itemId
", { itemId: url.itemId });
if (qItem.recordCount == 0) {
@ -19,25 +19,25 @@ if (qItem.recordCount == 0) {
// Get all children (direct only for display)
qChildren = queryExecute("
SELECT ItemID, ItemName
SELECT ID, Name
FROM Items
WHERE ItemParentItemID = :itemId
WHERE ParentItemID = :itemId
", { itemId: url.itemId });
childList = [];
for (row in qChildren) {
arrayAppend(childList, { "ItemID": row.ItemID, "ItemName": row.ItemName });
arrayAppend(childList, { "ItemID": row.ID, "Name": row.Name });
}
result = {
"OK": true,
"ACTION": url.action,
"ITEM": {
"ItemID": qItem.ItemID,
"ItemName": qItem.ItemName,
"ItemParentItemID": qItem.ItemParentItemID,
"ItemIsActive": qItem.ItemIsActive,
"ItemIsCollapsible": qItem.ItemIsCollapsible
"ItemID": qItem.ID,
"Name": qItem.Name,
"ParentItemID": qItem.ParentItemID,
"IsActive": qItem.IsActive,
"IsCollapsible": qItem.IsCollapsible
},
"HAS_CHILDREN": qChildren.recordCount > 0,
"CHILD_COUNT": qChildren.recordCount,
@ -48,14 +48,14 @@ if (url.action == "deactivate") {
// Deactivate children first
queryExecute("
UPDATE Items
SET ItemIsActive = 0
WHERE ItemParentItemID = :itemId
SET IsActive = 0
WHERE ParentItemID = :itemId
", { itemId: url.itemId });
// Then deactivate the parent
queryExecute("
UPDATE Items
SET ItemIsActive = 0
SET IsActive = 0
WHERE ItemID = :itemId
", { itemId: url.itemId });

View file

@ -5,13 +5,13 @@
// Delete cart orders (status 0) to reset for testing
result = queryExecute("
DELETE FROM OrderLineItems
WHERE OrderLineItemOrderID IN (
SELECT OrderID FROM Orders WHERE OrderStatusID = 0
WHERE OrderID IN (
SELECT ID FROM Orders WHERE StatusID = 0
)
", {}, { datasource = "payfrit" });
result2 = queryExecute("
DELETE FROM Orders WHERE OrderStatusID = 0
DELETE FROM Orders WHERE StatusID = 0
", {}, { datasource = "payfrit" });
writeOutput(serializeJSON({

View file

@ -10,27 +10,27 @@ try {
businessIDs = [38, 39, 40, 41, 42];
for (bizID in businessIDs) {
// Delete ItemTemplateLinks for items belonging to this business
// Delete lt_ItemID_TemplateItemID for items belonging to this business
queryExecute("
DELETE itl FROM ItemTemplateLinks itl
INNER JOIN Items i ON i.ItemID = itl.ItemID
WHERE i.ItemBusinessID = :bizID
DELETE itl FROM lt_ItemID_TemplateItemID itl
INNER JOIN Items i ON i.ID = itl.ItemID
WHERE i.BusinessID = :bizID
", { bizID: bizID }, { datasource: "payfrit" });
// Delete Items
queryExecute("DELETE FROM Items WHERE ItemBusinessID = :bizID", { bizID: bizID }, { datasource: "payfrit" });
queryExecute("DELETE FROM Items WHERE BusinessID = :bizID", { bizID: bizID }, { datasource: "payfrit" });
// Delete Categories
queryExecute("DELETE FROM Categories WHERE CategoryBusinessID = :bizID", { bizID: bizID }, { datasource: "payfrit" });
queryExecute("DELETE FROM Categories WHERE BusinessID = :bizID", { bizID: bizID }, { datasource: "payfrit" });
// Delete Hours
queryExecute("DELETE FROM Hours WHERE HoursBusinessID = :bizID", { bizID: bizID }, { datasource: "payfrit" });
queryExecute("DELETE FROM Hours WHERE BusinessID = :bizID", { bizID: bizID }, { datasource: "payfrit" });
// Delete Addresses linked to this business
queryExecute("DELETE FROM Addresses WHERE AddressBusinessID = :bizID", { bizID: bizID }, { datasource: "payfrit" });
queryExecute("DELETE FROM Addresses WHERE BusinessID = :bizID", { bizID: bizID }, { datasource: "payfrit" });
// Delete the Business itself
queryExecute("DELETE FROM Businesses WHERE BusinessID = :bizID", { bizID: bizID }, { datasource: "payfrit" });
queryExecute("DELETE FROM Businesses WHERE ID = :bizID", { bizID: bizID }, { datasource: "payfrit" });
response.steps.append("Deleted business " & bizID & " and all related data");
}

View file

@ -5,9 +5,9 @@
try {
result = queryExecute("
UPDATE Tasks
SET TaskCompletedOn = NOW()
SET CompletedOn = NOW()
WHERE TaskTypeID = 2
AND TaskCompletedOn IS NULL
AND CompletedOn IS NULL
", {}, { datasource: "payfrit" });
affected = result.recordCount ?: 0;

View file

@ -15,24 +15,24 @@ try {
// First, check if Big Dean's has a Beverages/Drinks category
qExistingCat = queryExecute("
SELECT CategoryID, CategoryName FROM Categories
WHERE CategoryBusinessID = :bizId AND (CategoryName LIKE '%Drink%' OR CategoryName LIKE '%Beverage%')
SELECT ID, Name FROM Categories
WHERE BusinessID = :bizId AND (Name LIKE '%Drink%' OR Name LIKE '%Beverage%')
", { bizId: bigDeansBusinessId }, { datasource: "payfrit" });
if (qExistingCat.recordCount > 0) {
drinksCategoryId = qExistingCat.CategoryID;
response["CategoryNote"] = "Using existing category: " & qExistingCat.CategoryName;
response["CategoryNote"] = "Using existing category: " & qExistingCat.Name;
} else {
// Create a new Beverages category for Big Dean's
qMaxCat = queryExecute("SELECT COALESCE(MAX(CategoryID), 0) + 1 as nextId FROM Categories", {}, { datasource: "payfrit" });
drinksCategoryId = qMaxCat.nextId;
qMaxSort = queryExecute("
SELECT COALESCE(MAX(CategorySortOrder), 0) + 1 as nextSort FROM Categories WHERE CategoryBusinessID = :bizId
SELECT COALESCE(MAX(SortOrder), 0) + 1 as nextSort FROM Categories WHERE BusinessID = :bizId
", { bizId: bigDeansBusinessId }, { datasource: "payfrit" });
queryExecute("
INSERT INTO Categories (CategoryID, CategoryBusinessID, CategoryParentCategoryID, CategoryName, CategorySortOrder, CategoryAddedOn)
INSERT INTO Categories (CategoryID, BusinessID, ParentCategoryID, Name, SortOrder, AddedOn)
VALUES (:catId, :bizId, 0, 'Beverages', :sortOrder, NOW())
", {
catId: drinksCategoryId,
@ -61,8 +61,8 @@ try {
for (drink in drinks) {
// Check if item already exists
qExists = queryExecute("
SELECT ItemID FROM Items
WHERE ItemBusinessID = :bizId AND ItemName = :name AND ItemCategoryID = :catId
SELECT ID FROM Items
WHERE BusinessID = :bizId AND Name = :name AND CategoryID = :catId
", { bizId: bigDeansBusinessId, name: drink.name, catId: drinksCategoryId }, { datasource: "payfrit" });
if (qExists.recordCount == 0) {
@ -72,10 +72,10 @@ try {
queryExecute("
INSERT INTO Items (
ItemID, ItemBusinessID, ItemCategoryID, ItemParentItemID,
ItemName, ItemDescription, ItemPrice, ItemIsActive,
ItemSortOrder, ItemIsCollapsible, ItemRequiresChildSelection,
ItemAddedOn
ItemID, BusinessID, CategoryID, ParentItemID,
Name, Description, Price, IsActive,
SortOrder, IsCollapsible, RequiresChildSelection,
AddedOn
) VALUES (
:itemId, :bizId, :catId, 0,
:name, :desc, :price, 1,
@ -103,10 +103,10 @@ try {
qMaxOpt = queryExecute("SELECT COALESCE(MAX(ItemID), 0) + 1 as nextId FROM Items", {}, { datasource: "payfrit" });
queryExecute("
INSERT INTO Items (
ItemID, ItemBusinessID, ItemCategoryID, ItemParentItemID,
ItemName, ItemDescription, ItemPrice, ItemIsActive,
ItemSortOrder, ItemIsCollapsible, ItemIsCheckedByDefault,
ItemAddedOn
ItemID, BusinessID, CategoryID, ParentItemID,
Name, Description, Price, IsActive,
SortOrder, IsCollapsible, IsCheckedByDefault,
AddedOn
) VALUES (
:itemId, :bizId, 0, :parentId,
:name, '', 0, 1,

View file

@ -20,69 +20,74 @@ try {
uuid = beaconUUIDs[i];
// Check if beacon exists
qB = queryExecute("SELECT BeaconID FROM Beacons WHERE BeaconUUID = :uuid", { uuid: uuid }, { datasource: "payfrit" });
qB = queryExecute("SELECT ID FROM Beacons WHERE UUID = :uuid", { uuid: uuid }, { datasource: "payfrit" });
if (qB.recordCount == 0) {
queryExecute("INSERT INTO Beacons (BeaconUUID, BeaconBusinessID) VALUES (:uuid, :bizID)", { uuid: uuid, bizID: lazyDaisyID }, { datasource: "payfrit" });
queryExecute("INSERT INTO Beacons (UUID, BusinessID) VALUES (:uuid, :bizID)", { uuid: uuid, bizID: lazyDaisyID }, { datasource: "payfrit" });
qNew = queryExecute("SELECT LAST_INSERT_ID() as id", {}, { datasource: "payfrit" });
beaconID = qNew.id;
response.steps.append("Created beacon " & beaconID & " with UUID: " & uuid);
} else {
beaconID = qB.BeaconID;
beaconID = qB.ID;
response.steps.append("Beacon exists: " & beaconID & " with UUID: " & uuid);
}
}
// Get service point Table 1
qSP = queryExecute("
SELECT ServicePointID FROM ServicePoints
WHERE ServicePointBusinessID = :bizID AND ServicePointName = 'Table 1'
SELECT ID FROM ServicePoints
WHERE BusinessID = :bizID AND Name = 'Table 1'
", { bizID: lazyDaisyID }, { datasource: "payfrit" });
if (qSP.recordCount == 0) {
queryExecute("
INSERT INTO ServicePoints (ServicePointBusinessID, ServicePointName, ServicePointTypeID)
VALUES (:bizID, 'Table 1', 1)
INSERT INTO ServicePoints (BusinessID, Name)
VALUES (:bizID, 'Table 1')
", { bizID: lazyDaisyID }, { datasource: "payfrit" });
qSP = queryExecute("SELECT LAST_INSERT_ID() as id", {}, { datasource: "payfrit" });
servicePointID = qSP.id;
response.steps.append("Created service point 'Table 1' (ID: " & servicePointID & ")");
} else {
servicePointID = qSP.ServicePointID;
servicePointID = qSP.ID;
response.steps.append("Found service point 'Table 1' (ID: " & servicePointID & ")");
}
// Get all beacons and map them
qBeacons = queryExecute("SELECT BeaconID, BeaconUUID FROM Beacons", {}, { datasource: "payfrit" });
// Assign all beacons to the Table 1 service point
qBeacons = queryExecute("SELECT ID, UUID FROM Beacons WHERE BusinessID = :bizID", { bizID: lazyDaisyID }, { datasource: "payfrit" });
for (i = 1; i <= qBeacons.recordCount; i++) {
beaconID = qBeacons.BeaconID[i];
beaconID = qBeacons.ID[i];
// Delete old mapping if exists
queryExecute("DELETE FROM lt_Beacon_Businesses_ServicePoints WHERE BeaconID = :beaconID", { beaconID: beaconID }, { datasource: "payfrit" });
// Create new mapping
// Unassign this beacon from any existing service point
queryExecute("
INSERT INTO lt_Beacon_Businesses_ServicePoints (BeaconID, BusinessID, ServicePointID)
VALUES (:beaconID, :bizID, :spID)
UPDATE ServicePoints SET BeaconID = NULL, AssignedByUserID = NULL
WHERE BeaconID = :beaconID
", { beaconID: beaconID }, { datasource: "payfrit" });
// Assign beacon to Table 1 service point
queryExecute("
UPDATE ServicePoints SET BeaconID = :beaconID, AssignedByUserID = 1
WHERE ID = :spID AND BusinessID = :bizID
", { beaconID: beaconID, bizID: lazyDaisyID, spID: servicePointID }, { datasource: "payfrit" });
response.steps.append("Mapped beacon " & beaconID & " to Lazy Daisy, Table 1");
response.steps.append("Assigned beacon " & beaconID & " to Table 1");
}
// Get final status
qFinal = queryExecute("
SELECT lt.BeaconID, b.BeaconUUID, lt.BusinessID, biz.BusinessName, lt.ServicePointID, sp.ServicePointName
FROM lt_Beacon_Businesses_ServicePoints lt
JOIN Beacons b ON b.BeaconID = lt.BeaconID
JOIN Businesses biz ON biz.BusinessID = lt.BusinessID
LEFT JOIN ServicePoints sp ON sp.ServicePointID = lt.ServicePointID
SELECT sp.ID AS ServicePointID, sp.BeaconID, sp.BusinessID,
b.Name AS BeaconName, b.UUID, sp.Name AS ServicePointName,
biz.Name AS BusinessName
FROM ServicePoints sp
JOIN Beacons b ON b.ID = sp.BeaconID
JOIN Businesses biz ON biz.ID = sp.BusinessID
WHERE sp.BeaconID IS NOT NULL
", {}, { datasource: "payfrit" });
beacons = [];
for (i = 1; i <= qFinal.recordCount; i++) {
arrayAppend(beacons, {
"BeaconID": qFinal.BeaconID[i],
"UUID": qFinal.BeaconUUID[i],
"UUID": qFinal.UUID[i],
"BusinessID": qFinal.BusinessID[i],
"BusinessName": qFinal.BusinessName[i],
"ServicePointID": qFinal.ServicePointID[i],

View file

@ -5,11 +5,11 @@ try {
// Create ChatMessages table
queryExecute("
CREATE TABLE IF NOT EXISTS ChatMessages (
MessageID INT AUTO_INCREMENT PRIMARY KEY,
ID INT AUTO_INCREMENT PRIMARY KEY,
TaskID INT NOT NULL,
SenderUserID INT NOT NULL,
SenderType ENUM('customer', 'worker') NOT NULL,
MessageText TEXT NOT NULL,
MessageBody TEXT NOT NULL,
IsRead TINYINT(1) DEFAULT 0,
CreatedOn DATETIME DEFAULT NOW(),
@ -21,13 +21,13 @@ try {
// Also add a "Chat" category if it doesn't exist for business 17
existing = queryExecute("
SELECT TaskCategoryID FROM TaskCategories
WHERE TaskCategoryBusinessID = 17 AND TaskCategoryName = 'Chat'
SELECT ID FROM TaskCategories
WHERE BusinessID = 17 AND Name = 'Chat'
", {}, { datasource: "payfrit" });
if (existing.recordCount == 0) {
queryExecute("
INSERT INTO TaskCategories (TaskCategoryBusinessID, TaskCategoryName, TaskCategoryColor)
INSERT INTO TaskCategories (BusinessID, Name, Color)
VALUES (17, 'Chat', '##2196F3')
", {}, { datasource: "payfrit" });
}

View file

@ -32,47 +32,47 @@ try {
queryExecute("
CREATE TABLE Menus (
MenuID INT AUTO_INCREMENT PRIMARY KEY,
MenuBusinessID INT NOT NULL,
MenuName VARCHAR(100) NOT NULL,
MenuDescription VARCHAR(500) NULL,
MenuDaysActive INT NOT NULL DEFAULT 127,
MenuStartTime TIME NULL,
MenuEndTime TIME NULL,
MenuSortOrder INT NOT NULL DEFAULT 0,
MenuIsActive TINYINT NOT NULL DEFAULT 1,
MenuAddedOn DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX idx_menus_business (MenuBusinessID),
INDEX idx_menus_active (MenuBusinessID, MenuIsActive)
BusinessID INT NOT NULL,
Name VARCHAR(100) NOT NULL,
Description VARCHAR(500) NULL,
DaysActive INT NOT NULL DEFAULT 127,
StartTime TIME NULL,
EndTime TIME NULL,
SortOrder INT NOT NULL DEFAULT 0,
IsActive TINYINT NOT NULL DEFAULT 1,
AddedOn DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX idx_menus_business (BusinessID),
INDEX idx_menus_active (BusinessID, IsActive)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
", {}, { datasource: "payfrit" });
response["OK"] = true;
response["MESSAGE"] = "Menus table created successfully";
response["SCHEMA"] = {
"MenuDaysActive": "Bitmask: 1=Sun, 2=Mon, 4=Tue, 8=Wed, 16=Thu, 32=Fri, 64=Sat (127 = all days)"
"DaysActive": "Bitmask: 1=Sun, 2=Mon, 4=Tue, 8=Wed, 16=Thu, 32=Fri, 64=Sat (127 = all days)"
};
}
// Check if CategoryMenuID column exists in Categories table
// Check if MenuID column exists in Categories table
qCatCol = queryExecute("
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'payfrit'
AND TABLE_NAME = 'Categories'
AND COLUMN_NAME = 'CategoryMenuID'
AND COLUMN_NAME = 'MenuID'
", {}, { datasource: "payfrit" });
if (qCatCol.recordCount == 0) {
// Add CategoryMenuID column to Categories table
// Add MenuID column to Categories table
queryExecute("
ALTER TABLE Categories
ADD COLUMN CategoryMenuID INT NULL DEFAULT NULL AFTER CategoryBusinessID,
ADD INDEX idx_categories_menu (CategoryMenuID)
ADD COLUMN MenuID INT NULL DEFAULT NULL AFTER BusinessID,
ADD INDEX idx_categories_menu (MenuID)
", {}, { datasource: "payfrit" });
response["CATEGORIES_UPDATED"] = true;
response["CATEGORIES_MESSAGE"] = "Added CategoryMenuID column to Categories table";
response["CATEGORIES_MESSAGE"] = "Added MenuID column to Categories table";
} else {
response["CATEGORIES_UPDATED"] = false;
response["CATEGORIES_MESSAGE"] = "CategoryMenuID column already exists";
response["CATEGORIES_MESSAGE"] = "MenuID column already exists";
}
} catch (any e) {

View file

@ -12,7 +12,7 @@
*
* POST body:
* {
* "BusinessName": "Century Casino",
* "Name": "Century Casino",
* "UserID": 1,
* "ChildBusinessIDs": [47, 48] // Optional: link existing businesses as children
* }
@ -36,13 +36,13 @@ response = { "OK": false };
try {
data = readJsonBody();
BusinessName = structKeyExists(data, "BusinessName") ? trim(data.BusinessName) : "";
Name = structKeyExists(data, "Name") ? trim(data.Name) : "";
UserID = structKeyExists(data, "UserID") ? val(data.UserID) : 0;
ChildBusinessIDs = structKeyExists(data, "ChildBusinessIDs") && isArray(data.ChildBusinessIDs) ? data.ChildBusinessIDs : [];
if (!len(BusinessName)) {
if (!len(Name)) {
response["ERROR"] = "missing_name";
response["MESSAGE"] = "BusinessName is required";
response["MESSAGE"] = "Name is required";
writeOutput(serializeJSON(response));
abort;
}
@ -56,7 +56,7 @@ try {
// Create minimal address record (just a placeholder)
queryExecute("
INSERT INTO Addresses (AddressLine1, AddressUserID, AddressTypeID, AddressAddedOn)
INSERT INTO Addresses (Line1, UserID, AddressTypeID, AddedOn)
VALUES ('Parent Business - No Physical Location', :userID, 2, NOW())
", {
userID: UserID
@ -67,10 +67,10 @@ try {
// Create parent business (no menu, no hours, just a shell)
queryExecute("
INSERT INTO Businesses (BusinessName, BusinessUserID, BusinessAddressID, BusinessParentBusinessID, BusinessDeliveryZipCodes, BusinessAddedOn)
INSERT INTO Businesses (Name, UserID, AddressID, ParentBusinessID, BusinessDeliveryZipCodes, AddedOn)
VALUES (:name, :userId, :addressId, NULL, '', NOW())
", {
name: BusinessName,
name: Name,
userId: UserID,
addressId: addressId
}, { datasource = "payfrit" });
@ -80,7 +80,7 @@ try {
// Link address back to business
queryExecute("
UPDATE Addresses SET AddressBusinessID = :bizId WHERE AddressID = :addrId
UPDATE Addresses SET BusinessID = :bizId WHERE ID = :addrId
", {
bizId: newBusinessID,
addrId: addressId
@ -92,7 +92,7 @@ try {
childID = val(childID);
if (childID > 0) {
queryExecute("
UPDATE Businesses SET BusinessParentBusinessID = :parentId WHERE BusinessID = :childId
UPDATE Businesses SET ParentBusinessID = :parentId WHERE ID = :childId
", {
parentId: newBusinessID,
childId: childID
@ -103,7 +103,7 @@ try {
response["OK"] = true;
response["BusinessID"] = newBusinessID;
response["BusinessName"] = BusinessName;
response["Name"] = Name;
response["MESSAGE"] = "Parent business created";
if (arrayLen(linkedChildren) > 0) {
response["LinkedChildren"] = linkedChildren;

View file

@ -9,22 +9,22 @@ bizId = 27;
deactivatedIds = [11177, 11180, 11183, 11186, 11190, 11193, 11196, 11199, 11204, 11212, 11220, 11259];
qDeactivated = queryExecute("
SELECT i.ItemID, i.ItemName, i.ItemParentItemID, i.ItemIsActive, i.ItemIsCollapsible,
(SELECT COUNT(*) FROM Items c WHERE c.ItemParentItemID = i.ItemID) as ChildCount,
(SELECT GROUP_CONCAT(c.ItemName) FROM Items c WHERE c.ItemParentItemID = i.ItemID) as Children
SELECT i.ID, i.Name, i.ParentItemID, i.IsActive, i.IsCollapsible,
(SELECT COUNT(*) FROM Items c WHERE c.ParentItemID = i.ID) as ChildCount,
(SELECT GROUP_CONCAT(c.Name) FROM Items c WHERE c.ParentItemID = i.ID) as Children
FROM Items i
WHERE i.ItemID IN (:ids)
ORDER BY i.ItemID
WHERE i.ID IN (:ids)
ORDER BY i.ID
", { ids: { value: arrayToList(deactivatedIds), list: true } }, { datasource: "payfrit" });
items = [];
for (row in qDeactivated) {
arrayAppend(items, {
"ItemID": row.ItemID,
"ItemName": row.ItemName,
"ParentID": row.ItemParentItemID,
"IsActive": row.ItemIsActive,
"IsCollapsible": row.ItemIsCollapsible,
"ItemID": row.ID,
"Name": row.Name,
"ParentID": row.ParentItemID,
"IsActive": row.IsActive,
"IsCollapsible": row.IsCollapsible,
"ChildCount": row.ChildCount,
"Children": row.Children
});

View file

@ -9,24 +9,24 @@ bizId = 27;
qLinks = queryExecute("
SELECT
tl.ItemID as MenuItemID,
mi.ItemName as MenuItemName,
mi.ItemParentItemID,
mi.Name as MenuName,
mi.ParentItemID,
tl.TemplateItemID,
t.ItemName as TemplateName,
t.Name as TemplateName,
tl.SortOrder
FROM ItemTemplateLinks tl
JOIN Items mi ON mi.ItemID = tl.ItemID
FROM lt_ItemID_TemplateItemID tl
JOIN Items mi ON mi.ID = tl.ItemID
JOIN Items t ON t.ItemID = tl.TemplateItemID
WHERE mi.ItemBusinessID = :bizId
ORDER BY mi.ItemParentItemID, mi.ItemName, tl.SortOrder
WHERE mi.BusinessID = :bizId
ORDER BY mi.ParentItemID, mi.Name, tl.SortOrder
", { bizId: bizId }, { datasource: "payfrit" });
links = [];
for (row in qLinks) {
arrayAppend(links, {
"MenuItemID": row.MenuItemID,
"MenuItemName": row.MenuItemName,
"ParentItemID": row.ItemParentItemID,
"MenuName": row.MenuName,
"ParentItemID": row.ParentItemID,
"TemplateItemID": row.TemplateItemID,
"TemplateName": row.TemplateName
});
@ -34,20 +34,20 @@ for (row in qLinks) {
// Get burgers specifically (parent = 11271)
qBurgers = queryExecute("
SELECT ItemID, ItemName FROM Items
WHERE ItemBusinessID = :bizId AND ItemParentItemID = 11271 AND ItemIsActive = 1
ORDER BY ItemSortOrder
SELECT ID, Name FROM Items
WHERE BusinessID = :bizId AND ParentItemID = 11271 AND IsActive = 1
ORDER BY SortOrder
", { bizId: bizId }, { datasource: "payfrit" });
burgers = [];
for (row in qBurgers) {
// Get templates for this burger
qBurgerTemplates = queryExecute("
SELECT tl.TemplateItemID, t.ItemName as TemplateName
FROM ItemTemplateLinks tl
SELECT tl.TemplateItemID, t.Name as TemplateName
FROM lt_ItemID_TemplateItemID tl
JOIN Items t ON t.ItemID = tl.TemplateItemID
WHERE tl.ItemID = :itemId
", { itemId: row.ItemID }, { datasource: "payfrit" });
", { itemId: row.ID }, { datasource: "payfrit" });
templates = [];
for (t in qBurgerTemplates) {
@ -55,8 +55,8 @@ for (row in qBurgers) {
}
arrayAppend(burgers, {
"ItemID": row.ItemID,
"ItemName": row.ItemName,
"ItemID": row.ID,
"Name": row.Name,
"Templates": templates
});
}

View file

@ -9,38 +9,38 @@ businessID = 27;
qCategories = queryExecute("
SELECT DISTINCT
p.ItemID as CategoryID,
p.ItemName as CategoryName,
p.ItemSortOrder
p.Name as Name,
p.SortOrder
FROM Items p
INNER JOIN Items c ON c.ItemParentItemID = p.ItemID
WHERE p.ItemBusinessID = :businessID
AND p.ItemParentItemID = 0
AND p.ItemIsActive = 1
INNER JOIN Items c ON c.ParentItemID = p.ItemID
WHERE p.BusinessID = :businessID
AND p.ParentItemID = 0
AND p.IsActive = 1
AND NOT EXISTS (
SELECT 1 FROM ItemTemplateLinks tl WHERE tl.TemplateItemID = p.ItemID
SELECT 1 FROM lt_ItemID_TemplateItemID tl WHERE tl.TemplateItemID = p.ItemID
)
ORDER BY p.ItemSortOrder, p.ItemName
ORDER BY p.SortOrder, p.Name
", { businessID: businessID });
cats = [];
for (c in qCategories) {
arrayAppend(cats, {
"CategoryID": c.CategoryID,
"CategoryName": c.CategoryName
"CategoryID": c.ID,
"Name": c.Name
});
}
// Also check raw counts
rawCount = queryExecute("
SELECT COUNT(*) as cnt FROM Items
WHERE ItemBusinessID = :bizId AND ItemParentItemID = 0 AND ItemIsActive = 1
WHERE BusinessID = :bizId AND ParentItemID = 0 AND IsActive = 1
", { bizId: businessID });
childrenCount = queryExecute("
SELECT COUNT(DISTINCT c.ItemParentItemID) as cnt
SELECT COUNT(DISTINCT c.ParentItemID) as cnt
FROM Items c
INNER JOIN Items p ON p.ItemID = c.ItemParentItemID
WHERE p.ItemBusinessID = :bizId AND p.ItemParentItemID = 0
INNER JOIN Items p ON p.ItemID = c.ParentItemID
WHERE p.BusinessID = :bizId AND p.ParentItemID = 0
", { bizId: businessID });
writeOutput(serializeJSON({

View file

@ -9,24 +9,24 @@ bizId = 27;
qLinks = queryExecute("
SELECT
tl.ItemID as MenuItemID,
mi.ItemName as MenuItemName,
mi.ItemParentItemID as MenuItemParentID,
mi.Name as MenuName,
mi.ParentItemID as MenuItemParentID,
tl.TemplateItemID,
t.ItemName as TemplateName,
t.ItemIsActive as TemplateActive,
t.Name as TemplateName,
t.IsActive as TemplateActive,
tl.SortOrder
FROM ItemTemplateLinks tl
JOIN Items mi ON mi.ItemID = tl.ItemID
FROM lt_ItemID_TemplateItemID tl
JOIN Items mi ON mi.ID = tl.ItemID
JOIN Items t ON t.ItemID = tl.TemplateItemID
WHERE mi.ItemBusinessID = :bizId
ORDER BY mi.ItemName, tl.SortOrder
WHERE mi.BusinessID = :bizId
ORDER BY mi.Name, tl.SortOrder
", { bizId: bizId }, { datasource: "payfrit" });
links = [];
for (row in qLinks) {
arrayAppend(links, {
"MenuItemID": row.MenuItemID,
"MenuItemName": row.MenuItemName,
"MenuName": row.MenuName,
"MenuItemParentID": row.MenuItemParentID,
"TemplateItemID": row.TemplateItemID,
"TemplateName": row.TemplateName,
@ -37,20 +37,20 @@ for (row in qLinks) {
// Get all templates that exist for this business
qTemplates = queryExecute("
SELECT ItemID, ItemName, ItemIsActive, ItemParentItemID
SELECT ID, Name, IsActive, ParentItemID
FROM Items
WHERE ItemBusinessID = :bizId
AND ItemIsCollapsible = 1
ORDER BY ItemName
WHERE BusinessID = :bizId
AND IsCollapsible = 1
ORDER BY Name
", { bizId: bizId }, { datasource: "payfrit" });
templates = [];
for (row in qTemplates) {
arrayAppend(templates, {
"ItemID": row.ItemID,
"ItemName": row.ItemName,
"IsActive": row.ItemIsActive,
"ParentID": row.ItemParentItemID
"ItemID": row.ID,
"Name": row.Name,
"IsActive": row.IsActive,
"ParentID": row.ParentItemID
});
}

View file

@ -5,67 +5,67 @@
<cfscript>
bizId = 27;
// Check the template items themselves (IDs from ItemTemplateLinks)
// Check the template items themselves (IDs from lt_ItemID_TemplateItemID)
templateIds = "11267, 11251, 11246, 11224, 11233, 11230, 11240, 11243, 11237, 11227";
qTemplates = queryExecute("
SELECT ItemID, ItemName, ItemIsCollapsible, ItemIsActive, ItemParentItemID, ItemBusinessID
SELECT ID, Name, IsCollapsible, IsActive, ParentItemID, BusinessID
FROM Items
WHERE ItemID IN (#templateIds#)
ORDER BY ItemName
WHERE ID IN (#templateIds#)
ORDER BY Name
", {}, { datasource: "payfrit" });
templates = [];
for (row in qTemplates) {
arrayAppend(templates, {
"ItemID": row.ItemID,
"ItemName": row.ItemName,
"IsCollapsible": row.ItemIsCollapsible,
"IsActive": row.ItemIsActive,
"ParentID": row.ItemParentItemID,
"BusinessID": row.ItemBusinessID
"ItemID": row.ID,
"Name": row.Name,
"IsCollapsible": row.IsCollapsible,
"IsActive": row.IsActive,
"ParentID": row.ParentItemID,
"BusinessID": row.BusinessID
});
}
// Also check what other templates might exist for burgers
// Look for items that are in ItemTemplateLinks but NOT linked to burgers
// Look for items that are in lt_ItemID_TemplateItemID but NOT linked to burgers
qMissingTemplates = queryExecute("
SELECT DISTINCT t.ItemID, t.ItemName, t.ItemIsCollapsible, t.ItemIsActive
SELECT DISTINCT t.ItemID, t.Name, t.IsCollapsible, t.IsActive
FROM Items t
WHERE t.ItemBusinessID = :bizId
AND t.ItemParentItemID = 0
WHERE t.BusinessID = :bizId
AND t.ParentItemID = 0
AND t.ItemID NOT IN (
SELECT i.ItemID FROM Items i WHERE i.ItemBusinessID = :bizId AND i.ItemCategoryID > 0
SELECT i.ID FROM Items i WHERE i.BusinessID = :bizId AND i.CategoryID > 0
)
AND EXISTS (SELECT 1 FROM Items child WHERE child.ItemParentItemID = t.ItemID)
ORDER BY t.ItemName
AND EXISTS (SELECT 1 FROM Items child WHERE child.ParentItemID = t.ItemID)
ORDER BY t.Name
", { bizId: bizId }, { datasource: "payfrit" });
potentialTemplates = [];
for (row in qMissingTemplates) {
arrayAppend(potentialTemplates, {
"ItemID": row.ItemID,
"ItemName": row.ItemName,
"IsCollapsible": row.ItemIsCollapsible,
"IsActive": row.ItemIsActive
"ItemID": row.ID,
"Name": row.Name,
"IsCollapsible": row.IsCollapsible,
"IsActive": row.IsActive
});
}
// What templates SHOULD burgers have? Let's see all templates used by ANY item
qAllTemplateUsage = queryExecute("
SELECT t.ItemID, t.ItemName, COUNT(tl.ItemID) as UsageCount
FROM ItemTemplateLinks tl
SELECT t.ItemID, t.Name, COUNT(tl.ItemID) as UsageCount
FROM lt_ItemID_TemplateItemID tl
JOIN Items t ON t.ItemID = tl.TemplateItemID
JOIN Items mi ON mi.ItemID = tl.ItemID AND mi.ItemBusinessID = :bizId
GROUP BY t.ItemID, t.ItemName
ORDER BY t.ItemName
JOIN Items mi ON mi.ID = tl.ItemID AND mi.BusinessID = :bizId
GROUP BY t.ItemID, t.Name
ORDER BY t.Name
", { bizId: bizId }, { datasource: "payfrit" });
allTemplates = [];
for (row in qAllTemplateUsage) {
arrayAppend(allTemplates, {
"TemplateID": row.ItemID,
"TemplateName": row.ItemName,
"TemplateID": row.ID,
"TemplateName": row.Name,
"UsageCount": row.UsageCount
});
}

View file

@ -7,41 +7,41 @@ bizId = 27;
// Get the template items themselves
qTemplates = queryExecute("
SELECT ItemID, ItemName, ItemIsCollapsible, ItemIsActive, ItemParentItemID, ItemBusinessID
SELECT ID, Name, IsCollapsible, IsActive, ParentItemID, BusinessID
FROM Items
WHERE ItemID IN (11267, 11251, 11246, 11224, 11233, 11230, 11240, 11243, 11237, 11227)
ORDER BY ItemName
WHERE ID IN (11267, 11251, 11246, 11224, 11233, 11230, 11240, 11243, 11237, 11227)
ORDER BY Name
", {}, { datasource: "payfrit" });
templates = [];
for (row in qTemplates) {
arrayAppend(templates, {
"ItemID": row.ItemID,
"ItemName": row.ItemName,
"IsCollapsible": row.ItemIsCollapsible,
"IsActive": row.ItemIsActive,
"ParentID": row.ItemParentItemID,
"BusinessID": row.ItemBusinessID
"ItemID": row.ID,
"Name": row.Name,
"IsCollapsible": row.IsCollapsible,
"IsActive": row.IsActive,
"ParentID": row.ParentItemID,
"BusinessID": row.BusinessID
});
}
// What templates are used by burgers vs all items?
qBurgerLinks = queryExecute("
SELECT mi.ItemID, mi.ItemName, GROUP_CONCAT(t.ItemName ORDER BY tl.SortOrder) as Templates
SELECT mi.ID, mi.Name, GROUP_CONCAT(t.Name ORDER BY tl.SortOrder) as Templates
FROM Items mi
JOIN ItemTemplateLinks tl ON tl.ItemID = mi.ItemID
JOIN lt_ItemID_TemplateItemID tl ON tl.ItemID = mi.ID
JOIN Items t ON t.ItemID = tl.TemplateItemID
WHERE mi.ItemBusinessID = :bizId
AND mi.ItemParentItemID = 11271
GROUP BY mi.ItemID, mi.ItemName
ORDER BY mi.ItemName
WHERE mi.BusinessID = :bizId
AND mi.ParentItemID = 11271
GROUP BY mi.ID, mi.Name
ORDER BY mi.Name
", { bizId: bizId }, { datasource: "payfrit" });
burgerLinks = [];
for (row in qBurgerLinks) {
arrayAppend(burgerLinks, {
"ItemID": row.ItemID,
"ItemName": row.ItemName,
"ItemID": row.ID,
"Name": row.Name,
"Templates": row.Templates
});
}
@ -49,20 +49,20 @@ for (row in qBurgerLinks) {
// Also check: are there templates that SHOULD be linked to burgers?
// (e.g., Add Cheese, etc.)
qCheeseTemplate = queryExecute("
SELECT ItemID, ItemName, ItemParentItemID, ItemIsActive
SELECT ID, Name, ParentItemID, IsActive
FROM Items
WHERE ItemBusinessID = :bizId
AND ItemName LIKE '%Cheese%'
ORDER BY ItemName
WHERE BusinessID = :bizId
AND Name LIKE '%Cheese%'
ORDER BY Name
", { bizId: bizId }, { datasource: "payfrit" });
cheeseItems = [];
for (row in qCheeseTemplate) {
arrayAppend(cheeseItems, {
"ItemID": row.ItemID,
"ItemName": row.ItemName,
"ParentID": row.ItemParentItemID,
"IsActive": row.ItemIsActive
"ItemID": row.ID,
"Name": row.Name,
"ParentID": row.ParentItemID,
"IsActive": row.IsActive
});
}

View file

@ -26,36 +26,36 @@
<cftry>
<!--- Get raw employee records --- >
<cfset qEmployees = queryExecute("
SELECT e.*, b.BusinessName
FROM lt_Users_Businesses_Employees e
INNER JOIN Businesses b ON b.BusinessID = e.BusinessID
SELECT e.*, b.Name
FROM Employees e
INNER JOIN Businesses b ON b.ID = e.BusinessID
WHERE e.UserID = ?
ORDER BY b.BusinessName ASC
ORDER BY b.Name ASC
", [ { value = UserID, cfsqltype = "cf_sql_integer" } ], { datasource = "payfrit" })>
<cfset employees = []>
<cfloop query="qEmployees">
<cfset arrayAppend(employees, {
"EmployeeID": qEmployees.EmployeeID,
"EmployeeID": qEmployees.ID,
"UserID": qEmployees.UserID,
"BusinessID": qEmployees.BusinessID,
"BusinessName": qEmployees.BusinessName,
"EmployeeIsActive": qEmployees.EmployeeIsActive
"Name": qEmployees.Name,
"IsActive": qEmployees.IsActive
})>
</cfloop>
<!--- Check for duplicate businesses --- >
<cfset qDuplicates = queryExecute("
SELECT BusinessName, COUNT(*) AS cnt
SELECT Name, COUNT(*) AS cnt
FROM Businesses
GROUP BY BusinessName
GROUP BY Name
HAVING COUNT(*) > 1
", [], { datasource = "payfrit" })>
<cfset duplicates = []>
<cfloop query="qDuplicates">
<cfset arrayAppend(duplicates, {
"BusinessName": qDuplicates.BusinessName,
"Name": qDuplicates.Name,
"Count": qDuplicates.cnt
})>
</cfloop>

View file

@ -10,11 +10,11 @@ try {
messages = [];
for (row in qAll) {
arrayAppend(messages, {
"MessageID": row.MessageID,
"MessageID": row.ID,
"TaskID": row.TaskID,
"SenderUserID": row.SenderUserID,
"SenderType": row.SenderType,
"MessageText": left(row.MessageText, 100),
"MessageBody": left(row.MessageBody, 100),
"CreatedOn": row.CreatedOn
});
}

View file

@ -10,54 +10,54 @@ response = { "OK": true };
try {
// Get Fountain Drinks item
qFountain = queryExecute("
SELECT ItemID, ItemName, ItemParentItemID, ItemPrice, ItemIsCollapsible, ItemRequiresChildSelection
SELECT ID, Name, ParentItemID, Price, IsCollapsible, RequiresChildSelection
FROM Items
WHERE ItemBusinessID = 17 AND ItemName LIKE '%Fountain%'
WHERE BusinessID = 17 AND Name LIKE '%Fountain%'
", {}, { datasource: "payfrit" });
response["FountainDrinks"] = [];
for (row in qFountain) {
fountainItem = {
"ItemID": row.ItemID,
"ItemName": row.ItemName,
"ItemPrice": row.ItemPrice,
"ItemIsCollapsible": row.ItemIsCollapsible,
"ItemRequiresChildSelection": row.ItemRequiresChildSelection,
"ItemID": row.ID,
"Name": row.Name,
"Price": row.Price,
"IsCollapsible": row.IsCollapsible,
"RequiresChildSelection": row.RequiresChildSelection,
"Children": []
};
// Get children of this item
qChildren = queryExecute("
SELECT ItemID, ItemName, ItemParentItemID, ItemPrice, ItemIsCollapsible, ItemRequiresChildSelection, ItemIsCheckedByDefault
SELECT ID, Name, ParentItemID, Price, IsCollapsible, RequiresChildSelection, IsCheckedByDefault
FROM Items
WHERE ItemParentItemID = :parentId
ORDER BY ItemSortOrder
", { parentId: row.ItemID }, { datasource: "payfrit" });
WHERE ParentItemID = :parentId
ORDER BY SortOrder
", { parentId: row.ID }, { datasource: "payfrit" });
for (child in qChildren) {
childItem = {
"ItemID": child.ItemID,
"ItemName": child.ItemName,
"ItemPrice": child.ItemPrice,
"ItemIsCollapsible": child.ItemIsCollapsible,
"ItemIsCheckedByDefault": child.ItemIsCheckedByDefault,
"Name": child.Name,
"Price": child.Price,
"IsCollapsible": child.IsCollapsible,
"IsCheckedByDefault": child.IsCheckedByDefault,
"Grandchildren": []
};
// Get grandchildren
qGrandchildren = queryExecute("
SELECT ItemID, ItemName, ItemPrice, ItemIsCheckedByDefault
SELECT ID, Name, Price, IsCheckedByDefault
FROM Items
WHERE ItemParentItemID = :parentId
ORDER BY ItemSortOrder
WHERE ParentItemID = :parentId
ORDER BY SortOrder
", { parentId: child.ItemID }, { datasource: "payfrit" });
for (gc in qGrandchildren) {
arrayAppend(childItem.Grandchildren, {
"ItemID": gc.ItemID,
"ItemName": gc.ItemName,
"ItemPrice": gc.ItemPrice,
"ItemIsCheckedByDefault": gc.ItemIsCheckedByDefault
"Name": gc.Name,
"Price": gc.Price,
"IsCheckedByDefault": gc.IsCheckedByDefault
});
}

View file

@ -14,10 +14,10 @@ if (structKeyExists(data, "Phone") && len(data.Phone)) {
phone = reReplace(data.Phone, "[^0-9]", "", "all");
qUser = queryExecute("
SELECT UserID, UserFirstName, UserLastName, UserEmailAddress, UserContactNumber
SELECT ID, FirstName, LastName, EmailAddress, ContactNumber
FROM Users
WHERE REPLACE(REPLACE(REPLACE(UserContactNumber, '-', ''), '(', ''), ')', '') LIKE ?
OR UserContactNumber LIKE ?
WHERE REPLACE(REPLACE(REPLACE(ContactNumber, '-', ''), '(', ''), ')', '') LIKE ?
OR ContactNumber LIKE ?
", [
{ value: "%" & phone & "%", cfsqltype: "cf_sql_varchar" },
{ value: "%" & phone & "%", cfsqltype: "cf_sql_varchar" }
@ -28,35 +28,35 @@ if (structKeyExists(data, "Phone") && len(data.Phone)) {
abort;
}
userId = qUser.UserID;
userId = qUser.ID;
qEmployees = queryExecute("
SELECT e.EmployeeID, e.BusinessID, e.EmployeeStatusID,
CAST(e.EmployeeIsActive AS UNSIGNED) AS EmployeeIsActive,
b.BusinessName
FROM lt_Users_Businesses_Employees e
JOIN Businesses b ON e.BusinessID = b.BusinessID
SELECT e.ID, e.BusinessID, e.StatusID,
CAST(e.IsActive AS UNSIGNED) AS IsActive,
b.Name
FROM Employees e
JOIN Businesses b ON e.BusinessID = b.ID
WHERE e.UserID = ?
", [{ value: userId, cfsqltype: "cf_sql_integer" }], { datasource: "payfrit" });
employees = [];
for (row in qEmployees) {
arrayAppend(employees, {
"EmployeeID": row.EmployeeID,
"EmployeeID": row.ID,
"BusinessID": row.BusinessID,
"BusinessName": row.BusinessName,
"StatusID": row.EmployeeStatusID,
"IsActive": row.EmployeeIsActive
"Name": row.Name,
"StatusID": row.StatusID,
"IsActive": row.IsActive
});
}
writeOutput(serializeJSON({
"OK": true,
"USER": {
"UserID": qUser.UserID,
"Name": trim(qUser.UserFirstName & " " & qUser.UserLastName),
"Email": qUser.UserEmailAddress,
"Phone": qUser.UserContactNumber
"UserID": qUser.ID,
"Name": trim(qUser.FirstName & " " & qUser.LastName),
"Email": qUser.EmailAddress,
"Phone": qUser.ContactNumber
},
"EMPLOYEES": employees
}));
@ -67,23 +67,23 @@ if (structKeyExists(data, "Phone") && len(data.Phone)) {
businessId = structKeyExists(data, "BusinessID") ? val(data.BusinessID) : 17;
q = queryExecute("
SELECT EmployeeID, UserID, EmployeeStatusID, EmployeeIsActive,
CAST(EmployeeIsActive AS UNSIGNED) AS IsActiveInt
FROM lt_Users_Businesses_Employees
SELECT ID, UserID, StatusID, IsActive,
CAST(IsActive AS UNSIGNED) AS IsActiveInt
FROM Employees
WHERE BusinessID = ?
", [{ value: businessId, cfsqltype: "cf_sql_integer" }], { datasource: "payfrit" });
rows = [];
for (r in q) {
arrayAppend(rows, {
"EmployeeID": r.EmployeeID,
"EmployeeID": r.ID,
"UserID": r.UserID,
"StatusID": r.EmployeeStatusID,
"RawIsActive": r.EmployeeIsActive,
"StatusID": r.StatusID,
"RawIsActive": r.IsActive,
"CastIsActive": r.IsActiveInt,
"ValRaw": val(r.EmployeeIsActive),
"ValRaw": val(r.IsActive),
"ValCast": val(r.IsActiveInt),
"EqRaw1": r.EmployeeIsActive == 1,
"EqRaw1": r.IsActive == 1,
"EqCast1": r.IsActiveInt == 1
});
}

View file

@ -13,8 +13,8 @@ try {
// Close all open chats action
if (structKeyExists(data, "action") && data.action == "closeAllChats") {
queryExecute("
UPDATE Tasks SET TaskCompletedOn = NOW()
WHERE TaskTypeID = 2 AND TaskCompletedOn IS NULL
UPDATE Tasks SET CompletedOn = NOW()
WHERE TaskTypeID = 2 AND CompletedOn IS NULL
", {}, { datasource: "payfrit" });
writeOutput(serializeJSON({ "OK": true, "MESSAGE": "All open chats closed" }));
abort;
@ -24,38 +24,38 @@ if (structKeyExists(data, "action") && data.action == "closeAllChats") {
<cftry>
<cfset qTasks = queryExecute("
SELECT
t.TaskID,
t.TaskBusinessID,
t.TaskOrderID,
t.TaskClaimedByUserID,
t.TaskClaimedOn,
t.TaskCompletedOn,
o.OrderStatusID
t.ID,
t.BusinessID,
t.OrderID,
t.ClaimedByUserID,
t.ClaimedOn,
t.CompletedOn,
o.StatusID
FROM Tasks t
LEFT JOIN Orders o ON o.OrderID = t.TaskOrderID
ORDER BY t.TaskID DESC
LEFT JOIN Orders o ON o.ID = t.OrderID
ORDER BY t.ID DESC
LIMIT 20
", [], { datasource = "payfrit" })>
<cfset tasks = []>
<cfloop query="qTasks">
<cfset arrayAppend(tasks, {
"TaskID": qTasks.TaskID,
"TaskBusinessID": qTasks.TaskBusinessID,
"TaskOrderID": qTasks.TaskOrderID,
"TaskClaimedByUserID": qTasks.TaskClaimedByUserID,
"TaskClaimedOn": isNull(qTasks.TaskClaimedOn) ? "NULL" : dateTimeFormat(qTasks.TaskClaimedOn, "yyyy-mm-dd HH:nn:ss"),
"TaskCompletedOn": isNull(qTasks.TaskCompletedOn) ? "NULL" : dateTimeFormat(qTasks.TaskCompletedOn, "yyyy-mm-dd HH:nn:ss"),
"OrderStatusID": isNull(qTasks.OrderStatusID) ? "NULL" : qTasks.OrderStatusID
"TaskID": qTasks.ID,
"BusinessID": qTasks.BusinessID,
"OrderID": qTasks.OrderID,
"ClaimedByUserID": qTasks.ClaimedByUserID,
"ClaimedOn": isNull(qTasks.ClaimedOn) ? "NULL" : dateTimeFormat(qTasks.ClaimedOn, "yyyy-mm-dd HH:nn:ss"),
"CompletedOn": isNull(qTasks.CompletedOn) ? "NULL" : dateTimeFormat(qTasks.CompletedOn, "yyyy-mm-dd HH:nn:ss"),
"StatusID": isNull(qTasks.StatusID) ? "NULL" : qTasks.StatusID
})>
</cfloop>
<cfset qStats = queryExecute("
SELECT
COUNT(*) as Total,
SUM(CASE WHEN TaskClaimedByUserID > 0 AND TaskCompletedOn IS NULL THEN 1 ELSE 0 END) as ClaimedNotCompleted,
SUM(CASE WHEN TaskClaimedByUserID = 0 OR TaskClaimedByUserID IS NULL THEN 1 ELSE 0 END) as Unclaimed,
SUM(CASE WHEN TaskCompletedOn IS NOT NULL THEN 1 ELSE 0 END) as Completed
SUM(CASE WHEN ClaimedByUserID > 0 AND CompletedOn IS NULL THEN 1 ELSE 0 END) as ClaimedNotCompleted,
SUM(CASE WHEN ClaimedByUserID = 0 OR ClaimedByUserID IS NULL THEN 1 ELSE 0 END) as Unclaimed,
SUM(CASE WHEN CompletedOn IS NOT NULL THEN 1 ELSE 0 END) as Completed
FROM Tasks
", [], { datasource = "payfrit" })>

View file

@ -4,19 +4,19 @@
<cftry>
<cfset qAll = queryExecute("
SELECT TaskID, TaskClaimedByUserID, TaskCompletedOn, TaskOrderID,
CASE WHEN TaskCompletedOn IS NULL THEN 'YES_NULL' ELSE 'NOT_NULL' END AS IsNull
SELECT ID, ClaimedByUserID, CompletedOn, OrderID,
CASE WHEN CompletedOn IS NULL THEN 'YES_NULL' ELSE 'NOT_NULL' END AS IsNull
FROM Tasks
ORDER BY TaskID DESC
ORDER BY ID DESC
", [], { datasource = "payfrit" })>
<cfset tasks = []>
<cfloop query="qAll">
<cfset arrayAppend(tasks, {
"TaskID": qAll.TaskID,
"TaskClaimedByUserID": qAll.TaskClaimedByUserID,
"TaskOrderID": qAll.TaskOrderID,
"TaskCompletedOn": len(trim(qAll.TaskCompletedOn)) ? toString(qAll.TaskCompletedOn) : "",
"TaskID": qAll.ID,
"ClaimedByUserID": qAll.ClaimedByUserID,
"OrderID": qAll.OrderID,
"CompletedOn": len(trim(qAll.CompletedOn)) ? toString(qAll.CompletedOn) : "",
"IsNull": qAll.IsNull
})>
</cfloop>

View file

@ -3,39 +3,39 @@
<cfcontent type="application/json; charset=utf-8" reset="true">
<cfscript>
// Check ItemTemplateLinks for Big Dean's (BusinessID 27)
// Check lt_ItemID_TemplateItemID for Big Dean's (BusinessID 27)
bizId = 27;
// Count total links
qCount = queryExecute("SELECT COUNT(*) as cnt FROM ItemTemplateLinks", {}, { datasource: "payfrit" });
qCount = queryExecute("SELECT COUNT(*) as cnt FROM lt_ItemID_TemplateItemID", {}, { datasource: "payfrit" });
// Get template item IDs for this business
qTemplates = queryExecute("
SELECT DISTINCT tl.TemplateItemID, i.ItemName
FROM ItemTemplateLinks tl
JOIN Items i ON i.ItemID = tl.TemplateItemID
WHERE i.ItemBusinessID = :bizId
SELECT DISTINCT tl.TemplateItemID, i.Name
FROM lt_ItemID_TemplateItemID tl
JOIN Items i ON i.ID = tl.TemplateItemID
WHERE i.BusinessID = :bizId
", { bizId: bizId }, { datasource: "payfrit" });
templates = [];
for (row in qTemplates) {
arrayAppend(templates, { "TemplateItemID": row.TemplateItemID, "ItemName": row.ItemName });
arrayAppend(templates, { "TemplateItemID": row.TemplateItemID, "Name": row.Name });
}
// Get items that should be categories (ParentItemID=0, not templates)
qCategories = queryExecute("
SELECT i.ItemID, i.ItemName, i.ItemParentItemID, i.ItemIsCollapsible
SELECT i.ID, i.Name, i.ParentItemID, i.IsCollapsible
FROM Items i
WHERE i.ItemBusinessID = :bizId
AND i.ItemParentItemID = 0
AND i.ItemIsCollapsible = 0
AND NOT EXISTS (SELECT 1 FROM ItemTemplateLinks tl WHERE tl.TemplateItemID = i.ItemID)
ORDER BY i.ItemSortOrder
WHERE i.BusinessID = :bizId
AND i.ParentItemID = 0
AND i.IsCollapsible = 0
AND NOT EXISTS (SELECT 1 FROM lt_ItemID_TemplateItemID tl WHERE tl.TemplateItemID = i.ID)
ORDER BY i.SortOrder
", { bizId: bizId }, { datasource: "payfrit" });
categories = [];
for (row in qCategories) {
arrayAppend(categories, { "ItemID": row.ItemID, "ItemName": row.ItemName });
arrayAppend(categories, { "ItemID": row.ID, "Name": row.Name });
}
writeOutput(serializeJSON({

View file

@ -20,10 +20,10 @@ if (len(phone) == 0) {
// Find user by phone
qUser = queryExecute("
SELECT UserID, UserFirstName, UserLastName, UserEmailAddress, UserContactNumber
SELECT ID, FirstName, LastName, EmailAddress, ContactNumber
FROM Users
WHERE REPLACE(REPLACE(REPLACE(UserContactNumber, '-', ''), '(', ''), ')', '') LIKE ?
OR UserContactNumber LIKE ?
WHERE REPLACE(REPLACE(REPLACE(ContactNumber, '-', ''), '(', ''), ')', '') LIKE ?
OR ContactNumber LIKE ?
", [
{ value: "%" & phone & "%", cfsqltype: "cf_sql_varchar" },
{ value: "%" & phone & "%", cfsqltype: "cf_sql_varchar" }
@ -34,36 +34,36 @@ if (qUser.recordCount == 0) {
abort;
}
userId = qUser.UserID;
userId = qUser.ID;
// Get all employee records for this user
qEmployees = queryExecute("
SELECT e.EmployeeID, e.BusinessID, e.EmployeeStatusID,
CAST(e.EmployeeIsActive AS UNSIGNED) AS EmployeeIsActive,
b.BusinessName
FROM lt_Users_Businesses_Employees e
JOIN Businesses b ON e.BusinessID = b.BusinessID
SELECT e.ID, e.BusinessID, e.StatusID,
CAST(e.IsActive AS UNSIGNED) AS IsActive,
b.Name
FROM Employees e
JOIN Businesses b ON e.BusinessID = b.ID
WHERE e.UserID = ?
", [{ value: userId, cfsqltype: "cf_sql_integer" }], { datasource: "payfrit" });
employees = [];
for (row in qEmployees) {
arrayAppend(employees, {
"EmployeeID": row.EmployeeID,
"EmployeeID": row.ID,
"BusinessID": row.BusinessID,
"BusinessName": row.BusinessName,
"StatusID": row.EmployeeStatusID,
"IsActive": row.EmployeeIsActive
"Name": row.Name,
"StatusID": row.StatusID,
"IsActive": row.IsActive
});
}
writeOutput(serializeJSON({
"OK": true,
"USER": {
"UserID": qUser.UserID,
"Name": trim(qUser.UserFirstName & " " & qUser.UserLastName),
"Email": qUser.UserEmailAddress,
"Phone": qUser.UserContactNumber
"UserID": qUser.ID,
"Name": trim(qUser.FirstName & " " & qUser.LastName),
"Email": qUser.EmailAddress,
"Phone": qUser.ContactNumber
},
"EMPLOYEES": employees
}));

View file

@ -28,19 +28,19 @@ try {
response["ERROR"] = "BusinessID required";
} else {
// Get address ID first
qBiz = queryExecute("SELECT BusinessAddressID FROM Businesses WHERE BusinessID = :id", { id: bizID }, { datasource = "payfrit" });
qBiz = queryExecute("SELECT AddressID FROM Businesses WHERE ID = :id", { id: bizID }, { datasource = "payfrit" });
if (qBiz.recordCount == 0) {
response["ERROR"] = "Business not found";
} else {
addrID = qBiz.BusinessAddressID;
addrID = qBiz.AddressID;
// Delete business
queryExecute("DELETE FROM Businesses WHERE BusinessID = :id", { id: bizID }, { datasource = "payfrit" });
queryExecute("DELETE FROM Businesses WHERE ID = :id", { id: bizID }, { datasource = "payfrit" });
// Delete address if exists
if (val(addrID) > 0) {
queryExecute("DELETE FROM Addresses WHERE AddressID = :id", { id: addrID }, { datasource = "payfrit" });
queryExecute("DELETE FROM Addresses WHERE ID = :id", { id: addrID }, { datasource = "payfrit" });
}
response["OK"] = true;

View file

@ -13,7 +13,7 @@
* Delete Orphan Modifiers for In and Out Burger (BusinessID=17)
*
* This script deletes duplicate modifier items that are no longer needed
* because we now use ItemTemplateLinks.
* because we now use lt_ItemID_TemplateItemID.
*
* The orphan items are level-1 modifiers (direct children of parent items)
* that have been replaced by template links.
@ -29,18 +29,18 @@ try {
qOrphans = queryExecute("
SELECT
m.ItemID,
m.ItemName,
m.ItemParentItemID,
p.ItemName as ParentName
m.Name,
m.ParentItemID,
p.Name as ParentName
FROM Items m
INNER JOIN Items p ON p.ItemID = m.ItemParentItemID
INNER JOIN Categories c ON c.CategoryID = p.ItemCategoryID
WHERE c.CategoryBusinessID = :businessID
AND m.ItemParentItemID > 0
AND p.ItemParentItemID = 0
AND (m.ItemIsModifierTemplate IS NULL OR m.ItemIsModifierTemplate = 0)
AND m.ItemIsActive = 1
ORDER BY m.ItemName
INNER JOIN Items p ON p.ItemID = m.ParentItemID
INNER JOIN Categories c ON c.ID = p.CategoryID
WHERE c.BusinessID = :businessID
AND m.ParentItemID > 0
AND p.ParentItemID = 0
AND (m.IsModifierTemplate IS NULL OR m.IsModifierTemplate = 0)
AND m.IsActive = 1
ORDER BY m.Name
", { businessID: businessID }, { datasource: "payfrit" });
arrayAppend(response.deleted, "Found " & qOrphans.recordCount & " orphan modifiers to delete");
@ -50,23 +50,23 @@ try {
try {
// Delete children of this orphan (options within the modifier group)
qDeleteChildren = queryExecute("
DELETE FROM Items WHERE ItemParentItemID = :orphanID
DELETE FROM Items WHERE ParentItemID = :orphanID
", { orphanID: orphan.ItemID }, { datasource: "payfrit" });
// Delete the orphan itself
qDeleteOrphan = queryExecute("
DELETE FROM Items WHERE ItemID = :orphanID
DELETE FROM Items WHERE ID = :orphanID
", { orphanID: orphan.ItemID }, { datasource: "payfrit" });
arrayAppend(response.deleted, {
"ItemID": orphan.ItemID,
"ItemName": orphan.ItemName,
"Name": orphan.Name,
"WasUnder": orphan.ParentName
});
} catch (any deleteErr) {
arrayAppend(response.errors, {
"ItemID": orphan.ItemID,
"ItemName": orphan.ItemName,
"Name": orphan.Name,
"Error": deleteErr.message
});
}

View file

@ -11,7 +11,7 @@
<cfscript>
/**
* Delete orphan Items at ParentID=0
* Orphan = ParentID=0, no children, not in ItemTemplateLinks
* Orphan = ParentID=0, no children, not in lt_ItemID_TemplateItemID
*/
response = { "OK": false, "deleted": 0, "orphans": [] };
@ -19,24 +19,24 @@ response = { "OK": false, "deleted": 0, "orphans": [] };
try {
// Find orphans
qOrphans = queryExecute("
SELECT i.ItemID, i.ItemName, i.ItemBusinessID
SELECT i.ID, i.Name, i.BusinessID
FROM Items i
WHERE i.ItemParentItemID = 0
WHERE i.ParentItemID = 0
AND NOT EXISTS (
SELECT 1 FROM Items child WHERE child.ItemParentItemID = i.ItemID
SELECT 1 FROM Items child WHERE child.ParentItemID = i.ID
)
AND NOT EXISTS (
SELECT 1 FROM ItemTemplateLinks tl WHERE tl.TemplateItemID = i.ItemID
SELECT 1 FROM lt_ItemID_TemplateItemID tl WHERE tl.TemplateItemID = i.ID
)
ORDER BY i.ItemBusinessID, i.ItemName
ORDER BY i.BusinessID, i.Name
", {}, { datasource: "payfrit" });
orphanIDs = [];
for (orphan in qOrphans) {
arrayAppend(response.orphans, {
"ItemID": orphan.ItemID,
"ItemName": orphan.ItemName,
"BusinessID": orphan.ItemBusinessID
"Name": orphan.Name,
"BusinessID": orphan.BusinessID
});
arrayAppend(orphanIDs, orphan.ItemID);
}
@ -44,7 +44,7 @@ try {
// Delete them by ID list
if (arrayLen(orphanIDs) > 0) {
queryExecute("
DELETE FROM Items WHERE ItemID IN (#arrayToList(orphanIDs)#)
DELETE FROM Items WHERE ID IN (#arrayToList(orphanIDs)#)
", {}, { datasource: "payfrit" });
}

View file

@ -14,16 +14,16 @@
* Eliminate Categories Table - Schema Migration
*
* Final unified schema:
* - Everything is an Item with ItemBusinessID
* - Everything is an Item with BusinessID
* - ParentID=0 items are either categories or templates (derived from usage)
* - Categories = items at ParentID=0 that have menu items as children
* - Templates = items at ParentID=0 that appear in ItemTemplateLinks
* - Templates = items at ParentID=0 that appear in lt_ItemID_TemplateItemID
* - Orphans = ParentID=0 items that are neither (cleanup candidates)
*
* Steps:
* 1. Add ItemBusinessID column to Items
* 1. Add BusinessID column to Items
* 2. For each Category: create Item, re-parent menu items, set BusinessID
* 3. Set ItemBusinessID on templates based on linked items
* 3. Set BusinessID on templates based on linked items
*
* Query param: ?dryRun=1 to preview without making changes
*/
@ -34,30 +34,30 @@ try {
dryRun = structKeyExists(url, "dryRun") && url.dryRun == 1;
response["dryRun"] = dryRun;
// Step 1: Add ItemBusinessID column if it doesn't exist
// Step 1: Add BusinessID column if it doesn't exist
try {
if (!dryRun) {
queryExecute("
ALTER TABLE Items ADD COLUMN ItemBusinessID INT DEFAULT 0 AFTER ItemID
ALTER TABLE Items ADD COLUMN BusinessID INT DEFAULT 0 AFTER ItemID
", {}, { datasource: "payfrit" });
}
arrayAppend(response.steps, "Added ItemBusinessID column to Items table");
arrayAppend(response.steps, "Added BusinessID column to Items table");
} catch (any e) {
if (findNoCase("Duplicate column", e.message)) {
arrayAppend(response.steps, "ItemBusinessID column already exists");
arrayAppend(response.steps, "BusinessID column already exists");
} else {
throw(e);
}
}
// Step 2: Add index on ItemBusinessID
// Step 2: Add index on BusinessID
try {
if (!dryRun) {
queryExecute("
CREATE INDEX idx_item_business ON Items (ItemBusinessID)
CREATE INDEX idx_item_business ON Items (BusinessID)
", {}, { datasource: "payfrit" });
}
arrayAppend(response.steps, "Added index on ItemBusinessID");
arrayAppend(response.steps, "Added index on BusinessID");
} catch (any e) {
if (findNoCase("Duplicate key name", e.message)) {
arrayAppend(response.steps, "Index idx_item_business already exists");
@ -66,7 +66,7 @@ try {
}
}
// Step 3: Drop foreign key constraint on ItemCategoryID if it exists
// Step 3: Drop foreign key constraint on CategoryID if it exists
try {
if (!dryRun) {
queryExecute("
@ -84,9 +84,9 @@ try {
// Step 4: Get all Categories
qCategories = queryExecute("
SELECT CategoryID, CategoryBusinessID, CategoryName
SELECT ID, BusinessID, Name
FROM Categories
ORDER BY CategoryBusinessID, CategoryName
ORDER BY BusinessID, Name
", {}, { datasource: "payfrit" });
arrayAppend(response.steps, "Found " & qCategories.recordCount & " categories to migrate");
@ -94,29 +94,29 @@ try {
// Step 4: Migrate each category
for (cat in qCategories) {
migration = {
"oldCategoryID": cat.CategoryID,
"categoryName": cat.CategoryName,
"businessID": cat.CategoryBusinessID,
"oldCategoryID": cat.ID,
"categoryName": cat.Name,
"businessID": cat.BusinessID,
"newItemID": 0,
"itemsUpdated": 0
};
if (!dryRun) {
// Create new Item for this category (ParentID=0, no template flag needed)
// Note: ItemCategoryID set to 0 temporarily until we drop that column
// Note: CategoryID set to 0 temporarily until we drop that column
queryExecute("
INSERT INTO Items (
ItemBusinessID,
ItemCategoryID,
ItemName,
ItemDescription,
ItemParentItemID,
ItemPrice,
ItemIsActive,
ItemIsCheckedByDefault,
ItemRequiresChildSelection,
ItemSortOrder,
ItemAddedOn
BusinessID,
CategoryID,
Name,
Description,
ParentItemID,
Price,
IsActive,
IsCheckedByDefault,
RequiresChildSelection,
SortOrder,
AddedOn
) VALUES (
:businessID,
0,
@ -131,56 +131,56 @@ try {
NOW()
)
", {
businessID: cat.CategoryBusinessID,
categoryName: cat.CategoryName
businessID: cat.BusinessID,
categoryName: cat.Name
}, { datasource: "payfrit" });
// Get the new Item ID
qNewItem = queryExecute("
SELECT ItemID FROM Items
WHERE ItemBusinessID = :businessID
AND ItemName = :categoryName
AND ItemParentItemID = 0
ORDER BY ItemID DESC
SELECT ID FROM Items
WHERE BusinessID = :businessID
AND Name = :categoryName
AND ParentItemID = 0
ORDER BY ID DESC
LIMIT 1
", {
businessID: cat.CategoryBusinessID,
categoryName: cat.CategoryName
businessID: cat.BusinessID,
categoryName: cat.Name
}, { datasource: "payfrit" });
newItemID = qNewItem.ItemID;
newItemID = qNewItem.ID;
migration["newItemID"] = newItemID;
// Update menu items in this category:
// - Set ItemParentItemID = newItemID (for top-level items only)
// - Set ItemBusinessID = businessID (for all items)
// - Set ParentItemID = newItemID (for top-level items only)
// - Set BusinessID = businessID (for all items)
queryExecute("
UPDATE Items
SET ItemBusinessID = :businessID,
ItemParentItemID = :newItemID
WHERE ItemCategoryID = :categoryID
AND ItemParentItemID = 0
SET BusinessID = :businessID,
ParentItemID = :newItemID
WHERE CategoryID = :categoryID
AND ParentItemID = 0
", {
businessID: cat.CategoryBusinessID,
businessID: cat.BusinessID,
newItemID: newItemID,
categoryID: cat.CategoryID
categoryID: cat.ID
}, { datasource: "payfrit" });
// Set ItemBusinessID on ALL items in this category (including nested)
// Set BusinessID on ALL items in this category (including nested)
queryExecute("
UPDATE Items
SET ItemBusinessID = :businessID
WHERE ItemCategoryID = :categoryID
AND (ItemBusinessID IS NULL OR ItemBusinessID = 0)
SET BusinessID = :businessID
WHERE CategoryID = :categoryID
AND (BusinessID IS NULL OR BusinessID = 0)
", {
businessID: cat.CategoryBusinessID,
categoryID: cat.CategoryID
businessID: cat.BusinessID,
categoryID: cat.ID
}, { datasource: "payfrit" });
// Count how many were updated
qCount = queryExecute("
SELECT COUNT(*) as cnt FROM Items
WHERE ItemParentItemID = :newItemID
WHERE ParentItemID = :newItemID
", { newItemID: newItemID }, { datasource: "payfrit" });
migration["itemsUpdated"] = qCount.cnt;
@ -188,9 +188,9 @@ try {
// Dry run - count what would be updated
qCount = queryExecute("
SELECT COUNT(*) as cnt FROM Items
WHERE ItemCategoryID = :categoryID
AND ItemParentItemID = 0
", { categoryID: cat.CategoryID }, { datasource: "payfrit" });
WHERE CategoryID = :categoryID
AND ParentItemID = 0
", { categoryID: cat.ID }, { datasource: "payfrit" });
migration["itemsToUpdate"] = qCount.cnt;
}
@ -198,37 +198,37 @@ try {
arrayAppend(response.migrations, migration);
}
// Step 5: Set ItemBusinessID for templates (items in ItemTemplateLinks)
// Step 5: Set BusinessID for templates (items in lt_ItemID_TemplateItemID)
// Templates get their BusinessID from the items they're linked to
if (!dryRun) {
queryExecute("
UPDATE Items t
INNER JOIN ItemTemplateLinks tl ON tl.TemplateItemID = t.ItemID
INNER JOIN Items i ON i.ItemID = tl.ItemID
SET t.ItemBusinessID = i.ItemBusinessID
WHERE (t.ItemBusinessID IS NULL OR t.ItemBusinessID = 0)
AND i.ItemBusinessID > 0
INNER JOIN lt_ItemID_TemplateItemID tl ON tl.TemplateItemID = t.ItemID
INNER JOIN Items i ON i.ID = tl.ItemID
SET t.BusinessID = i.BusinessID
WHERE (t.BusinessID IS NULL OR t.BusinessID = 0)
AND i.BusinessID > 0
", {}, { datasource: "payfrit" });
arrayAppend(response.steps, "Set ItemBusinessID on templates from linked items");
arrayAppend(response.steps, "Set BusinessID on templates from linked items");
// Set ItemBusinessID on template children (options)
// Set BusinessID on template children (options)
queryExecute("
UPDATE Items c
INNER JOIN Items t ON t.ItemID = c.ItemParentItemID
SET c.ItemBusinessID = t.ItemBusinessID
WHERE t.ItemBusinessID > 0
AND (c.ItemBusinessID IS NULL OR c.ItemBusinessID = 0)
INNER JOIN Items t ON t.ItemID = c.ParentItemID
SET c.BusinessID = t.BusinessID
WHERE t.BusinessID > 0
AND (c.BusinessID IS NULL OR c.BusinessID = 0)
", {}, { datasource: "payfrit" });
arrayAppend(response.steps, "Set ItemBusinessID on template children");
arrayAppend(response.steps, "Set BusinessID on template children");
// Make sure templates have ParentID=0 (they live at top level)
queryExecute("
UPDATE Items t
INNER JOIN ItemTemplateLinks tl ON tl.TemplateItemID = t.ItemID
SET t.ItemParentItemID = 0
WHERE t.ItemParentItemID != 0
INNER JOIN lt_ItemID_TemplateItemID tl ON tl.TemplateItemID = t.ItemID
SET t.ParentItemID = 0
WHERE t.ParentItemID != 0
", {}, { datasource: "payfrit" });
arrayAppend(response.steps, "Ensured templates have ParentItemID=0");

View file

@ -14,32 +14,32 @@ fakeCategories = [11177, 11180, 11183, 11186, 11190, 11193, 11196, 11199, 11204,
// Deactivate these items (or we could delete them, but deactivate is safer)
for (itemId in fakeCategories) {
queryExecute("
UPDATE Items SET ItemIsActive = 0 WHERE ItemID = :itemId AND ItemBusinessID = :bizId
UPDATE Items SET IsActive = 0 WHERE ItemID = :itemId AND BusinessID = :bizId
", { itemId: itemId, bizId: bizId }, { datasource: "payfrit" });
}
// Also deactivate their children (modifier options that belong to these fake parents)
for (itemId in fakeCategories) {
queryExecute("
UPDATE Items SET ItemIsActive = 0 WHERE ItemParentItemID = :itemId AND ItemBusinessID = :bizId
UPDATE Items SET IsActive = 0 WHERE ParentItemID = :itemId AND BusinessID = :bizId
", { itemId: itemId, bizId: bizId }, { datasource: "payfrit" });
}
// Now verify what categories remain
qCategories = queryExecute("
SELECT i.ItemID, i.ItemName
SELECT i.ID, i.Name
FROM Items i
WHERE i.ItemBusinessID = :bizId
AND i.ItemParentItemID = 0
AND i.ItemIsActive = 1
AND i.ItemIsCollapsible = 0
AND NOT EXISTS (SELECT 1 FROM ItemTemplateLinks tl WHERE tl.TemplateItemID = i.ItemID)
ORDER BY i.ItemSortOrder
WHERE i.BusinessID = :bizId
AND i.ParentItemID = 0
AND i.IsActive = 1
AND i.IsCollapsible = 0
AND NOT EXISTS (SELECT 1 FROM lt_ItemID_TemplateItemID tl WHERE tl.TemplateItemID = i.ID)
ORDER BY i.SortOrder
", { bizId: bizId }, { datasource: "payfrit" });
categories = [];
for (row in qCategories) {
arrayAppend(categories, { "ItemID": row.ItemID, "ItemName": row.ItemName });
arrayAppend(categories, { "ItemID": row.ID, "Name": row.Name });
}
writeOutput(serializeJSON({

View file

@ -21,8 +21,8 @@ actions = [];
// First, let's see what templates already exist and are active for burgers
qExistingLinks = queryExecute("
SELECT tl.ItemID as MenuItemID, tl.TemplateItemID, t.ItemName as TemplateName
FROM ItemTemplateLinks tl
SELECT tl.ItemID as MenuItemID, tl.TemplateItemID, t.Name as TemplateName
FROM lt_ItemID_TemplateItemID tl
JOIN Items t ON t.ItemID = tl.TemplateItemID
WHERE tl.ItemID IN (:burgerIds)
", { burgerIds: { value: arrayToList(burgerIds), list: true } }, { datasource: "payfrit" });
@ -33,8 +33,8 @@ for (row in qExistingLinks) {
// Reactivate template 11196 (Extras with Add Cheese)
if (!dryRun) {
queryExecute("UPDATE Items SET ItemIsActive = 1 WHERE ItemID = 11196", {}, { datasource: "payfrit" });
queryExecute("UPDATE Items SET ItemIsActive = 1 WHERE ItemParentItemID = 11196", {}, { datasource: "payfrit" });
queryExecute("UPDATE Items SET IsActive = 1 WHERE ItemID = 11196", {}, { datasource: "payfrit" });
queryExecute("UPDATE Items SET IsActive = 1 WHERE ParentItemID = 11196", {}, { datasource: "payfrit" });
}
arrayAppend(actions, { action: dryRun ? "WOULD_REACTIVATE" : "REACTIVATED", itemID: 11196, name: "Extras (Add Cheese, Add Onions)" });
@ -42,13 +42,13 @@ arrayAppend(actions, { action: dryRun ? "WOULD_REACTIVATE" : "REACTIVATED", item
for (burgerId in burgerIds) {
// Check if link already exists
qCheck = queryExecute("
SELECT COUNT(*) as cnt FROM ItemTemplateLinks WHERE ItemID = :burgerId AND TemplateItemID = 11196
SELECT COUNT(*) as cnt FROM lt_ItemID_TemplateItemID WHERE ItemID = :burgerId AND TemplateItemID = 11196
", { burgerId: burgerId }, { datasource: "payfrit" });
if (qCheck.cnt EQ 0) {
if (!dryRun) {
queryExecute("
INSERT INTO ItemTemplateLinks (ItemID, TemplateItemID, SortOrder)
INSERT INTO lt_ItemID_TemplateItemID (ItemID, TemplateItemID, SortOrder)
VALUES (:burgerId, 11196, 2)
", { burgerId: burgerId }, { datasource: "payfrit" });
}
@ -59,17 +59,17 @@ for (burgerId in burgerIds) {
// Verify the result
if (!dryRun) {
qVerify = queryExecute("
SELECT mi.ItemID, mi.ItemName, GROUP_CONCAT(t.ItemName ORDER BY tl.SortOrder) as Templates
SELECT mi.ID, mi.Name, GROUP_CONCAT(t.Name ORDER BY tl.SortOrder) as Templates
FROM Items mi
LEFT JOIN ItemTemplateLinks tl ON tl.ItemID = mi.ItemID
LEFT JOIN lt_ItemID_TemplateItemID tl ON tl.ItemID = mi.ID
LEFT JOIN Items t ON t.ItemID = tl.TemplateItemID
WHERE mi.ItemID IN (:burgerIds)
GROUP BY mi.ItemID, mi.ItemName
WHERE mi.ID IN (:burgerIds)
GROUP BY mi.ID, mi.Name
", { burgerIds: { value: arrayToList(burgerIds), list: true } }, { datasource: "payfrit" });
result = [];
for (row in qVerify) {
arrayAppend(result, { itemID: row.ItemID, name: row.ItemName, templates: row.Templates });
arrayAppend(result, { itemID: row.ID, name: row.Name, templates: row.Templates });
}
} else {
result = "Dry run - no changes made";

View file

@ -1,32 +1,32 @@
<cfsetting showdebugoutput="false">
<cfcontent type="application/json" reset="true">
<cfscript>
// One-time fix: remove # prefix from BusinessBrandColor
// One-time fix: remove # prefix from BrandColor
qBefore = queryExecute("
SELECT BusinessID, BusinessBrandColor
SELECT ID, BrandColor
FROM Businesses
WHERE BusinessBrandColor LIKE :pattern
WHERE BrandColor LIKE :pattern
", { pattern: { value: "##%", cfsqltype: "cf_sql_varchar" } }, { datasource: "payfrit" });
if (qBefore.recordCount > 0) {
queryExecute("
UPDATE Businesses
SET BusinessBrandColor = SUBSTRING(BusinessBrandColor, 2)
WHERE BusinessBrandColor LIKE :pattern
SET BrandColor = SUBSTRING(BrandColor, 2)
WHERE BrandColor LIKE :pattern
", { pattern: { value: "##%", cfsqltype: "cf_sql_varchar" } }, { datasource: "payfrit" });
}
qAfter = queryExecute("
SELECT BusinessID, BusinessBrandColor
SELECT ID, BrandColor
FROM Businesses
WHERE BusinessBrandColor IS NOT NULL AND LENGTH(BusinessBrandColor) > 0
WHERE BrandColor IS NOT NULL AND LENGTH(BrandColor) > 0
", {}, { datasource: "payfrit" });
rows = [];
for (i = 1; i <= qAfter.recordCount; i++) {
arrayAppend(rows, {
"BusinessID": qAfter.BusinessID[i],
"BusinessBrandColor": qAfter.BusinessBrandColor[i]
"BrandColor": qAfter.BrandColor[i]
});
}

View file

@ -30,8 +30,8 @@
* 2. Re-parent the three flavors under the new group
* 3. Remove template flag from flavors
* 4. Mark "Choose Flavor" as the template
* 5. Create ItemTemplateLinks entry for Shake -> Choose Flavor
* 6. Set ItemRequiresChildSelection=1 on the shake item
* 5. Create lt_ItemID_TemplateItemID entry for Shake -> Choose Flavor
* 6. Set RequiresChildSelection=1 on the shake item
*/
response = { "OK": false, "steps": [] };
@ -46,19 +46,19 @@ try {
// Step 1: Create "Choose Flavor" modifier group under Shake
queryExecute("
INSERT INTO Items (
ItemCategoryID,
ItemName,
ItemDescription,
ItemParentItemID,
ItemPrice,
ItemIsActive,
ItemIsCheckedByDefault,
ItemRequiresChildSelection,
ItemMaxNumSelectionReq,
ItemIsCollapsible,
ItemSortOrder,
ItemIsModifierTemplate,
ItemAddedOn
CategoryID,
Name,
Description,
ParentItemID,
Price,
IsActive,
IsCheckedByDefault,
RequiresChildSelection,
MaxNumSelectionReq,
IsCollapsible,
SortOrder,
IsModifierTemplate,
AddedOn
) VALUES (
:categoryID,
'Choose Flavor',
@ -81,22 +81,22 @@ try {
// Get the new Choose Flavor ID
qNewGroup = queryExecute("
SELECT ItemID FROM Items
WHERE ItemName = 'Choose Flavor'
AND ItemParentItemID = :shakeItemID
ORDER BY ItemID DESC
SELECT ID FROM Items
WHERE Name = 'Choose Flavor'
AND ParentItemID = :shakeItemID
ORDER BY ID DESC
LIMIT 1
", { shakeItemID: shakeItemID }, { datasource: "payfrit" });
chooseFlavorID = qNewGroup.ItemID;
chooseFlavorID = qNewGroup.ID;
arrayAppend(response.steps, "Created 'Choose Flavor' group with ID: " & chooseFlavorID);
// Step 2: Re-parent the three flavors under Choose Flavor
queryExecute("
UPDATE Items
SET ItemParentItemID = :chooseFlavorID,
ItemIsModifierTemplate = 0,
ItemIsCheckedByDefault = 0
SET ParentItemID = :chooseFlavorID,
IsModifierTemplate = 0,
IsCheckedByDefault = 0
WHERE ItemID IN (:chocolateID, :strawberryID, :vanillaID)
", {
chooseFlavorID: chooseFlavorID,
@ -109,14 +109,14 @@ try {
// Step 3: Set Vanilla as default (common choice)
queryExecute("
UPDATE Items SET ItemIsCheckedByDefault = 1 WHERE ItemID = :vanillaID
UPDATE Items SET IsCheckedByDefault = 1 WHERE ItemID = :vanillaID
", { vanillaID: vanillaID }, { datasource: "payfrit" });
arrayAppend(response.steps, "Set Vanilla as default flavor");
// Step 4: Remove old template links for the flavors
queryExecute("
DELETE FROM ItemTemplateLinks
DELETE FROM lt_ItemID_TemplateItemID
WHERE TemplateItemID IN (:chocolateID, :strawberryID, :vanillaID)
", {
chocolateID: chocolateID,
@ -126,9 +126,9 @@ try {
arrayAppend(response.steps, "Removed old template links for flavor items");
// Step 5: Create ItemTemplateLinks for Shake -> Choose Flavor
// Step 5: Create lt_ItemID_TemplateItemID for Shake -> Choose Flavor
queryExecute("
INSERT INTO ItemTemplateLinks (ItemID, TemplateItemID, SortOrder)
INSERT INTO lt_ItemID_TemplateItemID (ItemID, TemplateItemID, SortOrder)
VALUES (:shakeItemID, :chooseFlavorID, 0)
ON DUPLICATE KEY UPDATE SortOrder = 0
", {
@ -138,14 +138,14 @@ try {
arrayAppend(response.steps, "Created template link: Shake -> Choose Flavor");
// Step 6: Set ItemRequiresChildSelection on shake
// Step 6: Set RequiresChildSelection on shake
queryExecute("
UPDATE Items
SET ItemRequiresChildSelection = 1
SET RequiresChildSelection = 1
WHERE ItemID = :shakeItemID
", { shakeItemID: shakeItemID }, { datasource: "payfrit" });
arrayAppend(response.steps, "Set ItemRequiresChildSelection=1 on Shake item");
arrayAppend(response.steps, "Set RequiresChildSelection=1 on Shake item");
response["OK"] = true;
response["chooseFlavorID"] = chooseFlavorID;

View file

@ -75,17 +75,17 @@ function buildAddressString(line1, line2, city, zipCode) {
<cfif structKeyExists(url, "geocode") AND structKeyExists(url, "addressId")>
<cfset addressId = val(url.addressId)>
<cfquery name="addr" datasource="payfrit">
SELECT AddressLine1, AddressLine2, AddressCity, AddressZIPCode
FROM Addresses WHERE AddressID = <cfqueryparam value="#addressId#" cfsqltype="cf_sql_integer">
SELECT Line1, Line2, City, ZIPCode
FROM Addresses WHERE ID = <cfqueryparam value="#addressId#" cfsqltype="cf_sql_integer">
</cfquery>
<cfif addr.recordCount GT 0>
<cfset fullAddress = buildAddressString(addr.AddressLine1, addr.AddressLine2, addr.AddressCity, addr.AddressZIPCode)>
<cfset fullAddress = buildAddressString(addr.Line1, addr.Line2, addr.City, addr.ZIPCode)>
<cfset geo = geocodeAddress(fullAddress)>
<cfif geo.success>
<cfquery datasource="payfrit">
UPDATE Addresses SET AddressLat = <cfqueryparam value="#geo.lat#" cfsqltype="cf_sql_decimal">,
AddressLng = <cfqueryparam value="#geo.lng#" cfsqltype="cf_sql_decimal">
WHERE AddressID = <cfqueryparam value="#addressId#" cfsqltype="cf_sql_integer">
UPDATE Addresses SET Latitude = <cfqueryparam value="#geo.lat#" cfsqltype="cf_sql_decimal">,
Longitude = <cfqueryparam value="#geo.lng#" cfsqltype="cf_sql_decimal">
WHERE ID = <cfqueryparam value="#addressId#" cfsqltype="cf_sql_integer">
</cfquery>
<cfoutput><div class="success">Geocoded Address ID #addressId#: #geo.lat#, #geo.lng#</div></cfoutput>
<cfelse>
@ -96,21 +96,21 @@ function buildAddressString(line1, line2, city, zipCode) {
<cfif structKeyExists(url, "geocodeAll")>
<cfquery name="missing" datasource="payfrit">
SELECT AddressID, AddressLine1, AddressLine2, AddressCity, AddressZIPCode
SELECT ID, Line1, Line2, City, ZIPCode
FROM Addresses
WHERE (AddressLat IS NULL OR AddressLat = 0)
AND AddressLine1 IS NOT NULL AND AddressLine1 != ''
WHERE (Latitude IS NULL OR Latitude = 0)
AND Line1 IS NOT NULL AND Line1 != ''
</cfquery>
<cfset successCount = 0>
<cfset failCount = 0>
<cfloop query="missing">
<cfset fullAddress = buildAddressString(missing.AddressLine1, missing.AddressLine2, missing.AddressCity, missing.AddressZIPCode)>
<cfset fullAddress = buildAddressString(missing.Line1, missing.Line2, missing.City, missing.ZIPCode)>
<cfset geo = geocodeAddress(fullAddress)>
<cfif geo.success>
<cfquery datasource="payfrit">
UPDATE Addresses SET AddressLat = <cfqueryparam value="#geo.lat#" cfsqltype="cf_sql_decimal">,
AddressLng = <cfqueryparam value="#geo.lng#" cfsqltype="cf_sql_decimal">
WHERE AddressID = <cfqueryparam value="#missing.AddressID#" cfsqltype="cf_sql_integer">
UPDATE Addresses SET Latitude = <cfqueryparam value="#geo.lat#" cfsqltype="cf_sql_decimal">,
Longitude = <cfqueryparam value="#geo.lng#" cfsqltype="cf_sql_decimal">
WHERE ID = <cfqueryparam value="#missing.ID#" cfsqltype="cf_sql_integer">
</cfquery>
<cfset successCount = successCount + 1>
<cfelse>
@ -123,23 +123,23 @@ function buildAddressString(line1, line2, city, zipCode) {
<cfquery name="addresses" datasource="payfrit">
SELECT
b.BusinessID,
b.BusinessName,
a.AddressID,
a.AddressLine1,
a.AddressLine2,
a.AddressCity,
a.AddressZIPCode,
a.AddressLat,
a.AddressLng
b.ID,
b.Name,
a.ID,
a.Line1,
a.Line2,
a.City,
a.ZIPCode,
a.Latitude,
a.Longitude
FROM Businesses b
LEFT JOIN Addresses a ON b.BusinessAddressID = a.AddressID
ORDER BY b.BusinessName
LEFT JOIN Addresses a ON b.AddressID = a.ID
ORDER BY b.Name
</cfquery>
<cfset missingCount = 0>
<cfloop query="addresses">
<cfif (NOT len(addresses.AddressLat) OR val(addresses.AddressLat) EQ 0) AND len(addresses.AddressLine1)>
<cfif (NOT len(addresses.Latitude) OR val(addresses.Latitude) EQ 0) AND len(addresses.Line1)>
<cfset missingCount = missingCount + 1>
</cfif>
</cfloop>
@ -166,31 +166,31 @@ function buildAddressString(line1, line2, city, zipCode) {
<cfloop query="addresses">
<tr>
<td>
#addresses.BusinessName#
<cfif len(addresses.AddressLat) AND val(addresses.AddressLat) NEQ 0>
#addresses.Name#
<cfif len(addresses.Latitude) AND val(addresses.Latitude) NEQ 0>
<span class="has-coords">#chr(10003)#</span>
<cfelseif len(addresses.AddressLine1)>
<cfelseif len(addresses.Line1)>
<span class="no-coords">#chr(9679)#</span>
</cfif>
</td>
<td class="address">
<cfif len(addresses.AddressLine1)>
#addresses.AddressLine1#<cfif len(addresses.AddressLine2)>, #addresses.AddressLine2#</cfif><br>
#addresses.AddressCity# #addresses.AddressZIPCode#
<cfif len(addresses.Line1)>
#addresses.Line1#<cfif len(addresses.Line2)>, #addresses.Line2#</cfif><br>
#addresses.City# #addresses.ZIPCode#
<cfelse>
<em style="color:##666;">No address</em>
</cfif>
</td>
<td class="coords">
<cfif len(addresses.AddressLat) AND val(addresses.AddressLat) NEQ 0>
#numberFormat(addresses.AddressLat, "_.______")#<br>
#numberFormat(addresses.AddressLng, "_.______")#
<cfif len(addresses.Latitude) AND val(addresses.Latitude) NEQ 0>
#numberFormat(addresses.Latitude, "_.______")#<br>
#numberFormat(addresses.Longitude, "_.______")#
<cfelse>
-
</cfif>
</td>
<td>
<cfif len(addresses.AddressLine1) AND len(addresses.AddressID)>
<cfif len(addresses.Line1) AND len(addresses.AddressID)>
<a href="?geocode=1&addressId=#addresses.AddressID#"><button class="btn-lookup">Lookup</button></a>
</cfif>
</td>

View file

@ -29,7 +29,7 @@ try {
response["ERROR"] = "ChildBusinessID required";
} else {
queryExecute("
UPDATE Businesses SET BusinessParentBusinessID = :parentId WHERE BusinessID = :childId
UPDATE Businesses SET ParentBusinessID = :parentId WHERE ID = :childId
", {
parentId: { value = parentID > 0 ? parentID : javaCast("null", ""), cfsqltype = "cf_sql_integer", null = parentID == 0 },
childId: childID

View file

@ -26,13 +26,13 @@ try {
// Step 1: Get all parent items (burgers, combos, etc.)
qParentItems = queryExecute("
SELECT i.ItemID, i.ItemName
SELECT i.ID, i.Name
FROM Items i
INNER JOIN Categories c ON c.CategoryID = i.ItemCategoryID
WHERE c.CategoryBusinessID = :businessID
AND i.ItemParentItemID = 0
AND i.ItemIsActive = 1
ORDER BY i.ItemName
INNER JOIN Categories c ON c.ID = i.CategoryID
WHERE c.BusinessID = :businessID
AND i.ParentItemID = 0
AND i.IsActive = 1
ORDER BY i.Name
", { businessID: businessID }, { datasource: "payfrit" });
arrayAppend(response.steps, "Found " & qParentItems.recordCount & " parent items");
@ -41,20 +41,20 @@ try {
qModifiers = queryExecute("
SELECT
m.ItemID,
m.ItemName,
m.ItemParentItemID,
m.ItemPrice,
m.ItemIsCheckedByDefault,
m.ItemSortOrder,
p.ItemName as ParentName
m.Name,
m.ParentItemID,
m.Price,
m.IsCheckedByDefault,
m.SortOrder,
p.Name as ParentName
FROM Items m
INNER JOIN Items p ON p.ItemID = m.ItemParentItemID
INNER JOIN Categories c ON c.CategoryID = p.ItemCategoryID
WHERE c.CategoryBusinessID = :businessID
AND m.ItemParentItemID > 0
AND m.ItemIsActive = 1
AND p.ItemParentItemID = 0
ORDER BY m.ItemName, m.ItemID
INNER JOIN Items p ON p.ItemID = m.ParentItemID
INNER JOIN Categories c ON c.ID = p.CategoryID
WHERE c.BusinessID = :businessID
AND m.ParentItemID > 0
AND m.IsActive = 1
AND p.ParentItemID = 0
ORDER BY m.Name, m.ItemID
", { businessID: businessID }, { datasource: "payfrit" });
arrayAppend(response.steps, "Found " & qModifiers.recordCount & " level-1 modifiers");
@ -62,17 +62,17 @@ try {
// Step 3: Group modifiers by name to find duplicates
modifiersByName = {};
for (mod in qModifiers) {
modName = mod.ItemName;
modName = mod.Name;
if (!structKeyExists(modifiersByName, modName)) {
modifiersByName[modName] = [];
}
arrayAppend(modifiersByName[modName], {
"ItemID": mod.ItemID,
"ParentItemID": mod.ItemParentItemID,
"ParentItemID": mod.ParentItemID,
"ParentName": mod.ParentName,
"Price": mod.ItemPrice,
"IsDefault": mod.ItemIsCheckedByDefault,
"SortOrder": mod.ItemSortOrder
"Price": mod.Price,
"IsDefault": mod.IsCheckedByDefault,
"SortOrder": mod.SortOrder
});
}
@ -91,7 +91,7 @@ try {
// Mark as template
queryExecute("
UPDATE Items SET ItemIsModifierTemplate = 1 WHERE ItemID = :itemID
UPDATE Items SET IsModifierTemplate = 1 WHERE ItemID = :itemID
", { itemID: templateItemID }, { datasource: "payfrit" });
templateMap[modName] = templateItemID;
@ -111,7 +111,7 @@ try {
// Insert link (ignore duplicates)
try {
queryExecute("
INSERT INTO ItemTemplateLinks (ItemID, TemplateItemID, SortOrder)
INSERT INTO lt_ItemID_TemplateItemID (ItemID, TemplateItemID, SortOrder)
VALUES (:itemID, :templateID, :sortOrder)
ON DUPLICATE KEY UPDATE SortOrder = :sortOrder
", {
@ -134,7 +134,7 @@ try {
if (i > 1) {
arrayAppend(orphanItems, {
"ItemID": inst.ItemID,
"ItemName": modName,
"Name": modName,
"WasUnder": inst.ParentName
});
}
@ -143,12 +143,12 @@ try {
// Single instance - still mark as template for consistency
singleItem = instances[1];
queryExecute("
UPDATE Items SET ItemIsModifierTemplate = 1 WHERE ItemID = :itemID
UPDATE Items SET IsModifierTemplate = 1 WHERE ItemID = :itemID
", { itemID: singleItem.ItemID }, { datasource: "payfrit" });
// Create link
queryExecute("
INSERT INTO ItemTemplateLinks (ItemID, TemplateItemID, SortOrder)
INSERT INTO lt_ItemID_TemplateItemID (ItemID, TemplateItemID, SortOrder)
VALUES (:itemID, :templateID, :sortOrder)
ON DUPLICATE KEY UPDATE SortOrder = :sortOrder
", {

View file

@ -27,20 +27,20 @@ try {
// Find all businesses with items in unified schema
if (len(businessFilter)) {
qBusinesses = queryExecute("
SELECT DISTINCT ItemBusinessID as BusinessID
SELECT DISTINCT BusinessID as BusinessID
FROM Items
WHERE ItemBusinessID = :bid AND ItemBusinessID > 0
WHERE BusinessID = :bid AND BusinessID > 0
", { bid: businessFilter }, { datasource: "payfrit" });
} else {
qBusinesses = queryExecute("
SELECT DISTINCT ItemBusinessID as BusinessID
SELECT DISTINCT BusinessID as BusinessID
FROM Items
WHERE ItemBusinessID > 0
WHERE BusinessID > 0
", {}, { datasource: "payfrit" });
}
for (biz in qBusinesses) {
bizId = biz.BusinessID;
bizId = biz.ID;
bizResult = { "BusinessID": bizId, "CategoriesCreated": 0, "ItemsUpdated": 0 };
try {
@ -48,26 +48,26 @@ try {
qCategoryItems = queryExecute("
SELECT DISTINCT
p.ItemID,
p.ItemName,
p.ItemSortOrder
p.Name,
p.SortOrder
FROM Items p
INNER JOIN Items c ON c.ItemParentItemID = p.ItemID
WHERE p.ItemBusinessID = :bizId
AND p.ItemParentItemID = 0
AND (p.ItemIsCollapsible = 0 OR p.ItemIsCollapsible IS NULL)
INNER JOIN Items c ON c.ParentItemID = p.ItemID
WHERE p.BusinessID = :bizId
AND p.ParentItemID = 0
AND (p.IsCollapsible = 0 OR p.IsCollapsible IS NULL)
AND NOT EXISTS (
SELECT 1 FROM ItemTemplateLinks tl WHERE tl.TemplateItemID = p.ItemID
SELECT 1 FROM lt_ItemID_TemplateItemID tl WHERE tl.TemplateItemID = p.ItemID
)
ORDER BY p.ItemSortOrder, p.ItemName
ORDER BY p.SortOrder, p.Name
", { bizId: bizId }, { datasource: "payfrit" });
sortOrder = 0;
for (catItem in qCategoryItems) {
// Check if category already exists for this business with same name
qExisting = queryExecute("
SELECT CategoryID FROM Categories
WHERE CategoryBusinessID = :bizId AND CategoryName = :name
", { bizId: bizId, name: left(catItem.ItemName, 30) }, { datasource: "payfrit" });
SELECT ID FROM Categories
WHERE BusinessID = :bizId AND Name = :name
", { bizId: bizId, name: left(catItem.Name, 30) }, { datasource: "payfrit" });
if (qExisting.recordCount == 0) {
// Get next CategoryID
@ -79,12 +79,12 @@ try {
// Create new category with explicit ID
queryExecute("
INSERT INTO Categories
(CategoryID, CategoryBusinessID, CategoryParentCategoryID, CategoryName, CategorySortOrder, CategoryAddedOn)
(CategoryID, BusinessID, ParentCategoryID, Name, SortOrder, AddedOn)
VALUES (:catId, :bizId, 0, :name, :sortOrder, NOW())
", {
catId: newCatId,
bizId: bizId,
name: left(catItem.ItemName, 30),
name: left(catItem.Name, 30),
sortOrder: sortOrder
}, { datasource: "payfrit" });
@ -96,9 +96,9 @@ try {
// Update all children of this category Item to have the new CategoryID
queryExecute("
UPDATE Items
SET ItemCategoryID = :catId
WHERE ItemParentItemID = :parentId
AND ItemBusinessID = :bizId
SET CategoryID = :catId
WHERE ParentItemID = :parentId
AND BusinessID = :bizId
", {
catId: newCatId,
parentId: catItem.ItemID,

View file

@ -0,0 +1,299 @@
<cfsetting showdebugoutput="false">
<cfsetting enablecfoutputonly="false">
<cfscript>
// Localhost-only protection
remoteAddr = cgi.REMOTE_ADDR;
if (remoteAddr != "127.0.0.1" && remoteAddr != "::1" && remoteAddr != "0:0:0:0:0:0:0:1" && remoteAddr != "10.10.0.2") {
writeOutput("Forbidden"); abort;
}
</cfscript>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>API Performance Dashboard</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; background: #0f1117; color: #e1e4e8; padding: 20px; }
h1 { font-size: 22px; font-weight: 600; margin-bottom: 16px; color: #fff; }
.controls { display: flex; gap: 10px; margin-bottom: 20px; align-items: center; flex-wrap: wrap; }
.controls label { font-size: 13px; color: #8b949e; }
.controls select, .controls input { background: #1c1f26; border: 1px solid #30363d; color: #e1e4e8; padding: 6px 10px; border-radius: 6px; font-size: 13px; }
.controls button { background: #238636; color: #fff; border: none; padding: 6px 16px; border-radius: 6px; font-size: 13px; cursor: pointer; font-weight: 500; }
.controls button:hover { background: #2ea043; }
.controls button.secondary { background: #30363d; }
.controls button.secondary:hover { background: #3d444d; }
.summary-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin-bottom: 24px; }
.card { background: #1c1f26; border: 1px solid #30363d; border-radius: 8px; padding: 16px; }
.card .label { font-size: 11px; color: #8b949e; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 4px; }
.card .value { font-size: 28px; font-weight: 700; color: #fff; }
.card .unit { font-size: 13px; color: #8b949e; font-weight: 400; }
.section { margin-bottom: 28px; }
.section h2 { font-size: 15px; font-weight: 600; margin-bottom: 10px; color: #c9d1d9; display: flex; align-items: center; gap: 8px; }
.section h2 .badge { background: #30363d; padding: 2px 8px; border-radius: 10px; font-size: 11px; font-weight: 500; color: #8b949e; }
table { width: 100%; border-collapse: collapse; font-size: 13px; }
thead th { text-align: left; padding: 8px 12px; border-bottom: 1px solid #30363d; color: #8b949e; font-weight: 500; font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap; }
thead th.num { text-align: right; }
tbody td { padding: 8px 12px; border-bottom: 1px solid #1c1f26; }
tbody td.num { text-align: right; font-variant-numeric: tabular-nums; }
tbody tr:hover { background: #1c1f26; }
tbody td.endpoint { font-family: 'SF Mono', 'Fira Code', monospace; font-size: 12px; color: #58a6ff; }
.bar-cell { position: relative; }
.bar-bg { position: absolute; left: 0; top: 2px; bottom: 2px; border-radius: 3px; opacity: 0.15; }
.bar-db { background: #f0883e; }
.bar-app { background: #58a6ff; }
.bar-wrap { display: flex; height: 18px; border-radius: 3px; overflow: hidden; min-width: 80px; }
.bar-seg-db { background: #f0883e; height: 100%; }
.bar-seg-app { background: #58a6ff; height: 100%; }
.legend { display: flex; gap: 16px; font-size: 11px; color: #8b949e; margin-bottom: 12px; }
.legend span { display: flex; align-items: center; gap: 4px; }
.legend .dot { width: 10px; height: 10px; border-radius: 2px; }
.legend .dot.db { background: #f0883e; }
.legend .dot.app { background: #58a6ff; }
.status { padding: 8px 12px; background: #1c1f26; border-radius: 6px; font-size: 12px; color: #8b949e; margin-bottom: 16px; }
.status.error { border: 1px solid #da3633; color: #f85149; }
.status.loading { border: 1px solid #30363d; }
.tag { display: inline-block; padding: 1px 6px; border-radius: 4px; font-size: 11px; font-weight: 500; }
.tag.fast { background: #23863620; color: #3fb950; }
.tag.ok { background: #d29b0020; color: #d29922; }
.tag.slow { background: #da363320; color: #f85149; }
.auto-refresh { display: flex; align-items: center; gap: 6px; margin-left: auto; }
.auto-refresh input[type=checkbox] { accent-color: #238636; }
.timestamp { font-size: 11px; color: #484f58; }
</style>
</head>
<body>
<h1>API Performance Dashboard</h1>
<div class="controls">
<label>Time range:
<select id="hours">
<option value="1">Last 1 hour</option>
<option value="6">Last 6 hours</option>
<option value="24" selected>Last 24 hours</option>
<option value="72">Last 3 days</option>
<option value="168">Last 7 days</option>
<option value="720">Last 30 days</option>
</select>
</label>
<label>Rows:
<input type="number" id="limit" value="20" min="5" max="100" style="width:60px">
</label>
<button onclick="loadAll()">Refresh</button>
<div class="auto-refresh">
<input type="checkbox" id="autoRefresh">
<label for="autoRefresh" style="font-size:12px;color:#8b949e;cursor:pointer">Auto-refresh 30s</label>
</div>
<span class="timestamp" id="lastUpdated"></span>
</div>
<div id="status" class="status loading">Loading...</div>
<div id="summarySection" class="section" style="display:none">
<div class="summary-cards" id="summaryCards"></div>
</div>
<div class="legend">
<span><span class="dot db"></span> DB time</span>
<span><span class="dot app"></span> App time</span>
</div>
<div id="countSection" class="section" style="display:none">
<h2>Top Endpoints by Volume <span class="badge" id="countBadge"></span></h2>
<div style="overflow-x:auto"><table id="countTable"><thead></thead><tbody></tbody></table></div>
</div>
<div id="latencySection" class="section" style="display:none">
<h2>Top Endpoints by Latency <span class="badge" id="latencyBadge"></span></h2>
<div style="overflow-x:auto"><table id="latencyTable"><thead></thead><tbody></tbody></table></div>
</div>
<div id="slowSection" class="section" style="display:none">
<h2>Slowest Individual Requests <span class="badge" id="slowBadge"></span></h2>
<div style="overflow-x:auto"><table id="slowTable"><thead></thead><tbody></tbody></table></div>
</div>
<script>
const API = 'perf.cfm';
let refreshTimer = null;
function qs(s) { return document.querySelector(s); }
function fmt(n) { return n == null ? '-' : Number(n).toLocaleString(); }
function fmtMs(n) {
if (n == null) return '-';
n = Number(n);
if (n < 100) return '<span class="tag fast">' + n + 'ms</span>';
if (n < 500) return '<span class="tag ok">' + n + 'ms</span>';
return '<span class="tag slow">' + n + 'ms</span>';
}
function fmtBytes(n) {
if (!n) return '-';
n = Number(n);
if (n < 1024) return n + ' B';
if (n < 1048576) return (n / 1024).toFixed(1) + ' KB';
return (n / 1048576).toFixed(1) + ' MB';
}
function pct(part, total) { return total > 0 ? Math.round(part / total * 100) : 0; }
function timeBar(dbMs, appMs) {
var total = (dbMs || 0) + (appMs || 0);
if (total === 0) return '';
var dbPct = pct(dbMs, total);
var appPct = 100 - dbPct;
return '<div class="bar-wrap" title="DB: ' + dbMs + 'ms / App: ' + appMs + 'ms">'
+ '<div class="bar-seg-db" style="width:' + dbPct + '%"></div>'
+ '<div class="bar-seg-app" style="width:' + appPct + '%"></div>'
+ '</div>';
}
function endpointName(ep) {
return ep.replace(/^\/api\//, '');
}
async function fetchView(view) {
var h = qs('#hours').value;
var l = qs('#limit').value;
var r = await fetch(API + '?view=' + view + '&hours=' + h + '&limit=' + l);
return r.json();
}
async function loadAll() {
qs('#status').className = 'status loading';
qs('#status').textContent = 'Loading...';
qs('#status').style.display = '';
try {
var [summary, count, latency, slow] = await Promise.all([
fetchView('summary'), fetchView('count'), fetchView('latency'), fetchView('slow')
]);
if (!summary.OK) throw new Error(summary.MESSAGE || summary.ERROR);
qs('#status').style.display = 'none';
// Summary cards
var s = summary.DATA;
qs('#summaryCards').innerHTML =
card('Total Requests', fmt(s.TotalRequests), '') +
card('Unique Endpoints', fmt(s.UniqueEndpoints), '') +
card('Avg Latency', s.OverallAvgMs || 0, 'ms') +
card('Max Latency', s.OverallMaxMs || 0, 'ms') +
card('Avg DB Time', s.OverallAvgDbMs || 0, 'ms') +
card('Avg App Time', s.OverallAvgAppMs || 0, 'ms') +
card('Avg Queries', s.OverallAvgQueries || 0, '/req') +
card('Data Since', s.FirstLog || 'none', '');
qs('#summarySection').style.display = '';
// Count table
renderCountTable(count.DATA || []);
// Latency table
renderLatencyTable(latency.DATA || []);
// Slow table
renderSlowTable(slow.DATA || []);
qs('#lastUpdated').textContent = 'Updated ' + new Date().toLocaleTimeString();
} catch (e) {
qs('#status').className = 'status error';
qs('#status').textContent = 'Error: ' + e.message;
qs('#status').style.display = '';
}
}
function card(label, value, unit) {
return '<div class="card"><div class="label">' + label + '</div><div class="value">' + value + ' <span class="unit">' + unit + '</span></div></div>';
}
function renderCountTable(data) {
if (!data.length) { qs('#countSection').style.display = 'none'; return; }
qs('#countBadge').textContent = data.length + ' endpoints';
var maxCalls = Math.max(...data.map(r => r.Calls));
var html = '<thead><tr><th>Endpoint</th><th class="num">Calls</th><th class="num">Avg</th><th class="num">DB</th><th class="num">App</th><th>DB / App Split</th><th class="num">Max</th><th class="num">Queries</th><th class="num">Avg Size</th></tr></thead><tbody>';
data.forEach(function(r) {
html += '<tr>'
+ '<td class="endpoint">' + endpointName(r.Endpoint) + '</td>'
+ '<td class="num">' + fmt(r.Calls) + '</td>'
+ '<td class="num">' + fmtMs(r.AvgMs) + '</td>'
+ '<td class="num">' + fmt(r.AvgDbMs) + 'ms</td>'
+ '<td class="num">' + fmt(r.AvgAppMs) + 'ms</td>'
+ '<td>' + timeBar(r.AvgDbMs, r.AvgAppMs) + '</td>'
+ '<td class="num">' + fmtMs(r.MaxMs) + '</td>'
+ '<td class="num">' + r.AvgQueries + '</td>'
+ '<td class="num">' + fmtBytes(r.AvgBytes) + '</td>'
+ '</tr>';
});
html += '</tbody>';
qs('#countTable').innerHTML = html;
qs('#countSection').style.display = '';
}
function renderLatencyTable(data) {
if (!data.length) { qs('#latencySection').style.display = 'none'; return; }
qs('#latencyBadge').textContent = data.length + ' endpoints';
var html = '<thead><tr><th>Endpoint</th><th class="num">Calls</th><th class="num">Avg</th><th class="num">DB</th><th class="num">App</th><th>DB / App Split</th><th class="num">Max</th><th class="num">Queries</th></tr></thead><tbody>';
data.forEach(function(r) {
html += '<tr>'
+ '<td class="endpoint">' + endpointName(r.Endpoint) + '</td>'
+ '<td class="num">' + fmt(r.Calls) + '</td>'
+ '<td class="num">' + fmtMs(r.AvgMs) + '</td>'
+ '<td class="num">' + fmt(r.AvgDbMs) + 'ms</td>'
+ '<td class="num">' + fmt(r.AvgAppMs) + 'ms</td>'
+ '<td>' + timeBar(r.AvgDbMs, r.AvgAppMs) + '</td>'
+ '<td class="num">' + fmtMs(r.MaxMs) + '</td>'
+ '<td class="num">' + r.AvgQueries + '</td>'
+ '</tr>';
});
html += '</tbody>';
qs('#latencyTable').innerHTML = html;
qs('#latencySection').style.display = '';
}
function renderSlowTable(data) {
if (!data.length) { qs('#slowSection').style.display = 'none'; return; }
qs('#slowBadge').textContent = data.length + ' requests';
var html = '<thead><tr><th>Endpoint</th><th class="num">Total</th><th class="num">DB</th><th class="num">App</th><th>DB / App Split</th><th class="num">Queries</th><th class="num">Size</th><th class="num">Biz</th><th>When</th></tr></thead><tbody>';
data.forEach(function(r) {
html += '<tr>'
+ '<td class="endpoint">' + endpointName(r.Endpoint) + '</td>'
+ '<td class="num">' + fmtMs(r.TotalMs) + '</td>'
+ '<td class="num">' + fmt(r.DbMs) + 'ms</td>'
+ '<td class="num">' + fmt(r.AppMs) + 'ms</td>'
+ '<td>' + timeBar(r.DbMs, r.AppMs) + '</td>'
+ '<td class="num">' + r.QueryCount + '</td>'
+ '<td class="num">' + fmtBytes(r.ResponseBytes) + '</td>'
+ '<td class="num">' + (r.BusinessID || '-') + '</td>'
+ '<td style="font-size:11px;color:#8b949e;white-space:nowrap">' + (r.LoggedAt || '') + '</td>'
+ '</tr>';
});
html += '</tbody>';
qs('#slowTable').innerHTML = html;
qs('#slowSection').style.display = '';
}
// Auto-refresh
qs('#autoRefresh').addEventListener('change', function() {
if (this.checked) {
refreshTimer = setInterval(loadAll, 30000);
} else {
clearInterval(refreshTimer);
refreshTimer = null;
}
});
// Load on page open
loadAll();
</script>
</body>
</html>

View file

@ -40,7 +40,7 @@ try {
MAX(TotalMs) as MaxMs,
ROUND(AVG(QueryCount), 1) as AvgQueries,
ROUND(AVG(ResponseBytes)) as AvgBytes
FROM api_perf_log
FROM ApiPerfLogs
WHERE LoggedAt > DATE_SUB(NOW(), INTERVAL :hours HOUR)
GROUP BY Endpoint
ORDER BY Calls DESC
@ -76,7 +76,7 @@ try {
ROUND(AVG(AppMs)) as AvgAppMs,
MAX(TotalMs) as MaxMs,
ROUND(AVG(QueryCount), 1) as AvgQueries
FROM api_perf_log
FROM ApiPerfLogs
WHERE LoggedAt > DATE_SUB(NOW(), INTERVAL :hours HOUR)
GROUP BY Endpoint
HAVING Calls >= 3
@ -106,7 +106,7 @@ try {
q = queryExecute("
SELECT Endpoint, TotalMs, DbMs, AppMs, QueryCount, ResponseBytes,
BusinessID, UserID, LoggedAt
FROM api_perf_log
FROM ApiPerfLogs
WHERE LoggedAt > DATE_SUB(NOW(), INTERVAL :hours HOUR)
ORDER BY TotalMs DESC
LIMIT :lim
@ -144,7 +144,7 @@ try {
ROUND(AVG(QueryCount), 1) as OverallAvgQueries,
MIN(LoggedAt) as FirstLog,
MAX(LoggedAt) as LastLog
FROM api_perf_log
FROM ApiPerfLogs
WHERE LoggedAt > DATE_SUB(NOW(), INTERVAL :hours HOUR)
", {
hours: { value: hours, cfsqltype: "cf_sql_integer" }

View file

@ -2,25 +2,25 @@
<cfcontent type="application/json; charset=utf-8" reset="true">
<cfscript>
// Add QuickTaskTemplateTypeID column to QuickTaskTemplates table if it doesn't exist
// Add TaskTypeID column to QuickTaskTemplates table if it doesn't exist
try {
// Check if column exists
qCheck = queryExecute("
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'payfrit'
AND TABLE_NAME = 'QuickTaskTemplates'
AND COLUMN_NAME = 'QuickTaskTemplateTypeID'
AND COLUMN_NAME = 'TaskTypeID'
", [], { datasource: "payfrit" });
if (qCheck.recordCount == 0) {
queryExecute("
ALTER TABLE QuickTaskTemplates
ADD COLUMN QuickTaskTemplateTypeID INT NULL AFTER QuickTaskTemplateCategoryID
ADD COLUMN TaskTypeID INT NULL AFTER TaskCategoryID
", [], { datasource: "payfrit" });
writeOutput(serializeJSON({
"OK": true,
"MESSAGE": "Column QuickTaskTemplateTypeID added to QuickTaskTemplates"
"MESSAGE": "Column TaskTypeID added to QuickTaskTemplates"
}));
} else {
writeOutput(serializeJSON({

View file

@ -6,7 +6,7 @@
// One-time cleanup: delete test tasks and reset
try {
// Delete tasks 30, 31, 32 (test tasks with bad data)
queryExecute("DELETE FROM Tasks WHERE TaskID IN (30, 31, 32)", [], { datasource: "payfrit" });
queryExecute("DELETE FROM Tasks WHERE ID IN (30, 31, 32)", [], { datasource: "payfrit" });
writeOutput(serializeJSON({
"OK": true,

View file

@ -50,13 +50,13 @@ try {
// Get template details
qTemplate = queryExecute("
SELECT
QuickTaskTemplateTitle as Title,
QuickTaskTemplateDetails as Details,
QuickTaskTemplateCategoryID as CategoryID
Title as Title,
Details as Details,
TaskCategoryID as CategoryID
FROM QuickTaskTemplates
WHERE QuickTaskTemplateID = :id
AND QuickTaskTemplateBusinessID = :businessID
AND QuickTaskTemplateIsActive = 1
WHERE ID = :id
AND BusinessID = :businessID
AND IsActive = 1
", {
id: { value: templateID, cfsqltype: "cf_sql_integer" },
businessID: { value: businessID, cfsqltype: "cf_sql_integer" }
@ -66,11 +66,11 @@ try {
apiAbort({ "OK": false, "ERROR": "not_found", "MESSAGE": "Template not found" });
}
// Create the task (TaskClaimedByUserID=0 means unclaimed/pending)
// Create the task (ClaimedByUserID=0 means unclaimed/pending)
queryExecute("
INSERT INTO Tasks (
TaskBusinessID, TaskCategoryID, TaskTypeID,
TaskTitle, TaskDetails, TaskAddedOn, TaskClaimedByUserID
BusinessID, CategoryID, TaskTypeID,
Title, Details, CreatedOn, ClaimedByUserID
) VALUES (
:businessID, :categoryID, :typeID,
:title, :details, NOW(), 0

View file

@ -5,23 +5,23 @@
<cfscript>
try {
q = queryExecute("
SELECT TaskID, TaskTitle, TaskDetails, TaskCategoryID, TaskClaimedByUserID, TaskCompletedOn, TaskAddedOn
SELECT ID, Title, Details, TaskCategoryID, ClaimedByUserID, CompletedOn, CreatedOn
FROM Tasks
WHERE TaskBusinessID = 47
ORDER BY TaskID DESC
WHERE BusinessID = 47
ORDER BY ID DESC
LIMIT 20
", [], { datasource: "payfrit" });
tasks = [];
for (row in q) {
arrayAppend(tasks, {
"TaskID": row.TaskID,
"Title": row.TaskTitle,
"Details": isNull(row.TaskDetails) ? "" : row.TaskDetails,
"CategoryID": row.TaskCategoryID,
"ClaimedByUserID": row.TaskClaimedByUserID,
"CompletedOn": isNull(row.TaskCompletedOn) ? "" : dateTimeFormat(row.TaskCompletedOn, "yyyy-mm-dd HH:nn:ss"),
"AddedOn": isNull(row.TaskAddedOn) ? "" : dateTimeFormat(row.TaskAddedOn, "yyyy-mm-dd HH:nn:ss")
"TaskID": row.ID,
"Title": row.Title,
"Details": isNull(row.Details) ? "" : row.Details,
"CategoryID": row.ID,
"ClaimedByUserID": row.ClaimedByUserID,
"CompletedOn": isNull(row.CompletedOn) ? "" : dateTimeFormat(row.CompletedOn, "yyyy-mm-dd HH:nn:ss"),
"AddedOn": isNull(row.CreatedOn) ? "" : dateTimeFormat(row.CreatedOn, "yyyy-mm-dd HH:nn:ss")
});
}

View file

@ -50,7 +50,7 @@ try {
// Verify template exists and belongs to this business
qCheck = queryExecute("
SELECT QuickTaskTemplateID FROM QuickTaskTemplates
WHERE QuickTaskTemplateID = :id AND QuickTaskTemplateBusinessID = :businessID
WHERE ID = :id AND BusinessID = :businessID
", {
id: { value: templateID, cfsqltype: "cf_sql_integer" },
businessID: { value: businessID, cfsqltype: "cf_sql_integer" }
@ -62,8 +62,8 @@ try {
// Soft delete by setting IsActive to 0
queryExecute("
UPDATE QuickTaskTemplates SET QuickTaskTemplateIsActive = 0
WHERE QuickTaskTemplateID = :id
UPDATE QuickTaskTemplates SET IsActive = 0
WHERE ID = :id
", {
id: { value: templateID, cfsqltype: "cf_sql_integer" }
}, { datasource: "payfrit" });

View file

@ -45,22 +45,22 @@ try {
// Get quick task templates for this business
q = queryExecute("
SELECT
qt.QuickTaskTemplateID,
qt.QuickTaskTemplateName as Name,
qt.QuickTaskTemplateCategoryID as CategoryID,
qt.QuickTaskTemplateTitle as Title,
qt.QuickTaskTemplateDetails as Details,
qt.QuickTaskTemplateIcon as Icon,
qt.QuickTaskTemplateColor as Color,
qt.QuickTaskTemplateSortOrder as SortOrder,
qt.QuickTaskTemplateIsActive as IsActive,
tc.TaskCategoryName as CategoryName,
tc.TaskCategoryColor as CategoryColor
qt.ID,
qt.Name as Name,
qt.TaskCategoryID as CategoryID,
qt.Title as Title,
qt.Details as Details,
qt.Icon as Icon,
qt.Color as Color,
qt.SortOrder as SortOrder,
qt.IsActive as IsActive,
tc.Name as Name,
tc.Color as CategoryColor
FROM QuickTaskTemplates qt
LEFT JOIN TaskCategories tc ON qt.QuickTaskTemplateCategoryID = tc.TaskCategoryID
WHERE qt.QuickTaskTemplateBusinessID = :businessID
AND qt.QuickTaskTemplateIsActive = 1
ORDER BY qt.QuickTaskTemplateSortOrder, qt.QuickTaskTemplateID
LEFT JOIN TaskCategories tc ON qt.TaskCategoryID = tc.ID
WHERE qt.BusinessID = :businessID
AND qt.IsActive = 1
ORDER BY qt.SortOrder, qt.ID
", {
businessID: { value: businessID, cfsqltype: "cf_sql_integer" }
}, { datasource: "payfrit" });
@ -68,7 +68,7 @@ try {
templates = [];
for (row in q) {
arrayAppend(templates, {
"QuickTaskTemplateID": row.QuickTaskTemplateID,
"QuickTaskTemplateID": row.ID,
"Name": row.Name,
"CategoryID": isNull(row.CategoryID) ? "" : row.CategoryID,
"Title": row.Title,
@ -77,7 +77,7 @@ try {
"Color": isNull(row.Color) ? "##6366f1" : row.Color,
"SortOrder": row.SortOrder,
"IsActive": row.IsActive,
"CategoryName": isNull(row.CategoryName) ? "" : row.CategoryName,
"Name": isNull(row.Name) ? "" : row.Name,
"CategoryColor": isNull(row.CategoryColor) ? "" : row.CategoryColor
});
}

View file

@ -5,7 +5,7 @@
<cfscript>
try {
// Delete all Quick Task templates for business 1
queryExecute("DELETE FROM QuickTaskTemplates WHERE QuickTaskTemplateBusinessID = 1", [], { datasource: "payfrit" });
queryExecute("DELETE FROM QuickTaskTemplates WHERE BusinessID = 1", [], { datasource: "payfrit" });
writeOutput(serializeJSON({
"OK": true,

View file

@ -70,7 +70,7 @@ try {
// UPDATE existing template
qCheck = queryExecute("
SELECT QuickTaskTemplateID FROM QuickTaskTemplates
WHERE QuickTaskTemplateID = :id AND QuickTaskTemplateBusinessID = :businessID
WHERE ID = :id AND BusinessID = :businessID
", {
id: { value: templateID, cfsqltype: "cf_sql_integer" },
businessID: { value: businessID, cfsqltype: "cf_sql_integer" }
@ -82,13 +82,13 @@ try {
queryExecute("
UPDATE QuickTaskTemplates SET
QuickTaskTemplateName = :name,
QuickTaskTemplateTitle = :title,
QuickTaskTemplateDetails = :details,
QuickTaskTemplateCategoryID = :categoryID,
QuickTaskTemplateIcon = :icon,
QuickTaskTemplateColor = :color
WHERE QuickTaskTemplateID = :id
Name = :name,
Title = :title,
Details = :details,
TaskCategoryID = :categoryID,
Icon = :icon,
Color = :color
WHERE ID = :id
", {
name: { value: templateName, cfsqltype: "cf_sql_varchar" },
title: { value: templateTitle, cfsqltype: "cf_sql_varchar" },
@ -109,8 +109,8 @@ try {
// INSERT new template
// Get next sort order
qSort = queryExecute("
SELECT COALESCE(MAX(QuickTaskTemplateSortOrder), 0) + 1 as nextSort
FROM QuickTaskTemplates WHERE QuickTaskTemplateBusinessID = :businessID
SELECT COALESCE(MAX(SortOrder), 0) + 1 as nextSort
FROM QuickTaskTemplates WHERE BusinessID = :businessID
", {
businessID: { value: businessID, cfsqltype: "cf_sql_integer" }
}, { datasource: "payfrit" });
@ -119,9 +119,9 @@ try {
queryExecute("
INSERT INTO QuickTaskTemplates (
QuickTaskTemplateBusinessID, QuickTaskTemplateName, QuickTaskTemplateTitle,
QuickTaskTemplateDetails, QuickTaskTemplateCategoryID,
QuickTaskTemplateIcon, QuickTaskTemplateColor, QuickTaskTemplateSortOrder
BusinessID, Name, Title,
Details, TaskCategoryID,
Icon, Color, SortOrder
) VALUES (
:businessID, :name, :title, :details, :categoryID, :icon, :color, :sortOrder
)

View file

@ -15,20 +15,20 @@ try {
// Create QuickTaskTemplates table
queryExecute("
CREATE TABLE IF NOT EXISTS QuickTaskTemplates (
QuickTaskTemplateID INT AUTO_INCREMENT PRIMARY KEY,
QuickTaskTemplateBusinessID INT NOT NULL,
QuickTaskTemplateName VARCHAR(100) NOT NULL,
QuickTaskTemplateCategoryID INT NULL,
QuickTaskTemplateTypeID INT NULL,
QuickTaskTemplateTitle VARCHAR(255) NOT NULL,
QuickTaskTemplateDetails TEXT NULL,
QuickTaskTemplateIcon VARCHAR(30) DEFAULT 'add_box',
QuickTaskTemplateColor VARCHAR(20) DEFAULT '##6366f1',
QuickTaskTemplateSortOrder INT DEFAULT 0,
QuickTaskTemplateIsActive BIT(1) DEFAULT b'1',
QuickTaskTemplateCreatedOn DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_business_active (QuickTaskTemplateBusinessID, QuickTaskTemplateIsActive),
INDEX idx_sort (QuickTaskTemplateBusinessID, QuickTaskTemplateSortOrder)
ID INT AUTO_INCREMENT PRIMARY KEY,
BusinessID INT NOT NULL,
Name VARCHAR(100) NOT NULL,
TaskCategoryID INT NULL,
TaskTypeID INT NULL,
Title VARCHAR(255) NOT NULL,
Details TEXT NULL,
Icon VARCHAR(30) DEFAULT 'add_box',
Color VARCHAR(20) DEFAULT '##6366f1',
SortOrder INT DEFAULT 0,
IsActive BIT(1) DEFAULT b'1',
CreatedOn DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_business_active (BusinessID, IsActive),
INDEX idx_sort (BusinessID, SortOrder)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
", [], { datasource: "payfrit" });

View file

@ -5,42 +5,42 @@
<cfscript>
businessId = 27; // Big Dean's
// Categories are items with ItemParentItemID=0 AND ItemIsCollapsible=0
// Modifier templates are items with ItemParentItemID=0 AND ItemIsCollapsible=1
// Categories are items with ParentItemID=0 AND IsCollapsible=0
// Modifier templates are items with ParentItemID=0 AND IsCollapsible=1
// Menu items are children of categories
// Modifiers are children of menu items or modifier templates
// Get category IDs (NOT modifier templates)
categoryIds = queryExecute("
SELECT ItemID
SELECT ID
FROM Items
WHERE ItemBusinessID = :bizId
AND ItemParentItemID = 0
AND ItemIsCollapsible = 0
WHERE BusinessID = :bizId
AND ParentItemID = 0
AND IsCollapsible = 0
", { bizId: businessId }, { datasource: "payfrit" });
catIdList = "";
for (cat in categoryIds) {
catIdList = listAppend(catIdList, cat.ItemID);
catIdList = listAppend(catIdList, cat.ID);
}
// Now get actual menu items (direct children of categories)
// Exclude items that are template options (their parent is a collapsible modifier group)
items = queryExecute("
SELECT i.ItemID, i.ItemName
SELECT i.ID, i.Name
FROM Items i
WHERE i.ItemBusinessID = :bizId
AND i.ItemParentItemID IN (#catIdList#)
AND i.ItemIsCollapsible = 0
WHERE i.BusinessID = :bizId
AND i.ParentItemID IN (#catIdList#)
AND i.IsCollapsible = 0
AND NOT EXISTS (
SELECT 1 FROM ItemTemplateLinks tl WHERE tl.TemplateItemID = i.ItemID
SELECT 1 FROM lt_ItemID_TemplateItemID tl WHERE tl.TemplateItemID = i.ID
)
", { bizId: businessId }, { datasource: "payfrit" });
updated = [];
for (item in items) {
itemName = lcase(item.ItemName);
itemName = lcase(item.Name);
newPrice = 0;
// Drinks - $3-6
@ -99,13 +99,13 @@ for (item in items) {
queryExecute("
UPDATE Items
SET ItemPrice = :price
SET Price = :price
WHERE ItemID = :itemId
", { price: newPrice, itemId: item.ItemID }, { datasource: "payfrit" });
", { price: newPrice, itemId: item.ID }, { datasource: "payfrit" });
arrayAppend(updated, {
"ItemID": item.ItemID,
"ItemName": item.ItemName,
"ItemID": item.ID,
"Name": item.Name,
"NewPrice": newPrice
});
}
@ -113,15 +113,15 @@ for (item in items) {
// Update modifier prices (children of menu items, NOT direct children of categories)
// Modifiers are items whose parent is NOT a category (i.e., parent is a menu item or modifier group)
modifiers = queryExecute("
SELECT ItemID, ItemName
SELECT ID, Name
FROM Items
WHERE ItemBusinessID = :bizId
AND ItemParentItemID > 0
AND ItemParentItemID NOT IN (#catIdList#)
WHERE BusinessID = :bizId
AND ParentItemID > 0
AND ParentItemID NOT IN (#catIdList#)
", { bizId: businessId }, { datasource: "payfrit" });
for (mod in modifiers) {
modName = lcase(mod.ItemName);
modName = lcase(mod.Name);
modPrice = 0;
// Proteins are expensive add-ons
@ -150,7 +150,7 @@ for (mod in modifiers) {
queryExecute("
UPDATE Items
SET ItemPrice = :price
SET Price = :price
WHERE ItemID = :itemId
", { price: modPrice, itemId: mod.ItemID }, { datasource: "payfrit" });
}
@ -158,17 +158,17 @@ for (mod in modifiers) {
// Reset category prices to $0 (shouldn't have prices for reporting)
queryExecute("
UPDATE Items
SET ItemPrice = 0
WHERE ItemBusinessID = :bizId
AND ItemParentItemID = 0
SET Price = 0
WHERE BusinessID = :bizId
AND ParentItemID = 0
", { bizId: businessId }, { datasource: "payfrit" });
// Reset modifier group prices to $0 (only options have prices)
queryExecute("
UPDATE Items
SET ItemPrice = 0
WHERE ItemBusinessID = :bizId
AND ItemIsCollapsible = 1
SET Price = 0
WHERE BusinessID = :bizId
AND IsCollapsible = 1
", { bizId: businessId }, { datasource: "payfrit" });
writeOutput(serializeJSON({

View file

@ -50,7 +50,7 @@ try {
// Verify exists and belongs to business
qCheck = queryExecute("
SELECT ScheduledTaskID FROM ScheduledTaskDefinitions
WHERE ScheduledTaskID = :id AND ScheduledTaskBusinessID = :businessID
WHERE ID = :id AND BusinessID = :businessID
", {
id: { value: taskID, cfsqltype: "cf_sql_integer" },
businessID: { value: businessID, cfsqltype: "cf_sql_integer" }
@ -62,7 +62,7 @@ try {
// Hard delete the definition
queryExecute("
DELETE FROM ScheduledTaskDefinitions WHERE ScheduledTaskID = :id
DELETE FROM ScheduledTaskDefinitions WHERE ID = :id
", {
id: { value: taskID, cfsqltype: "cf_sql_integer" }
}, { datasource: "payfrit" });

View file

@ -45,24 +45,24 @@ try {
// Get scheduled task definitions for this business
q = queryExecute("
SELECT
st.ScheduledTaskID,
st.ScheduledTaskName as Name,
st.ScheduledTaskCategoryID as CategoryID,
st.ScheduledTaskTitle as Title,
st.ScheduledTaskDetails as Details,
st.ScheduledTaskCronExpression as CronExpression,
COALESCE(st.ScheduledTaskScheduleType, 'cron') as ScheduleType,
st.ScheduledTaskIntervalMinutes as IntervalMinutes,
st.ScheduledTaskIsActive as IsActive,
st.ScheduledTaskLastRunOn as LastRunOn,
st.ScheduledTaskNextRunOn as NextRunOn,
st.ScheduledTaskCreatedOn as CreatedOn,
tc.TaskCategoryName as CategoryName,
tc.TaskCategoryColor as CategoryColor
st.ID,
st.Name as Name,
st.TaskCategoryID as CategoryID,
st.Title as Title,
st.Details as Details,
st.CronExpression as CronExpression,
COALESCE(st.ScheduleType, 'cron') as ScheduleType,
st.IntervalMinutes as IntervalMinutes,
st.IsActive as IsActive,
st.LastRunOn as LastRunOn,
st.NextRunOn as NextRunOn,
st.CreatedOn as CreatedOn,
tc.Name as Name,
tc.Color as CategoryColor
FROM ScheduledTaskDefinitions st
LEFT JOIN TaskCategories tc ON st.ScheduledTaskCategoryID = tc.TaskCategoryID
WHERE st.ScheduledTaskBusinessID = :businessID
ORDER BY st.ScheduledTaskIsActive DESC, st.ScheduledTaskName
LEFT JOIN TaskCategories tc ON st.TaskCategoryID = tc.ID
WHERE st.BusinessID = :businessID
ORDER BY st.IsActive DESC, st.Name
", {
businessID: { value: businessID, cfsqltype: "cf_sql_integer" }
}, { datasource: "payfrit" });
@ -70,7 +70,7 @@ try {
scheduledTasks = [];
for (row in q) {
arrayAppend(scheduledTasks, {
"ScheduledTaskID": row.ScheduledTaskID,
"ScheduledTaskID": row.ID,
"Name": row.Name,
"CategoryID": isNull(row.CategoryID) ? "" : row.CategoryID,
"Title": row.Title,
@ -82,7 +82,7 @@ try {
"LastRunOn": isNull(row.LastRunOn) ? "" : dateTimeFormat(row.LastRunOn, "yyyy-mm-dd HH:nn:ss"),
"NextRunOn": isNull(row.NextRunOn) ? "" : dateTimeFormat(row.NextRunOn, "yyyy-mm-dd HH:nn:ss"),
"CreatedOn": dateTimeFormat(row.CreatedOn, "yyyy-mm-dd HH:nn:ss"),
"CategoryName": isNull(row.CategoryName) ? "" : row.CategoryName,
"Name": isNull(row.Name) ? "" : row.Name,
"CategoryColor": isNull(row.CategoryColor) ? "" : row.CategoryColor
});
}

View file

@ -51,11 +51,11 @@ try {
// Get scheduled task definition
qDef = queryExecute("
SELECT
ScheduledTaskTitle as Title,
ScheduledTaskDetails as Details,
ScheduledTaskCategoryID as CategoryID
Title as Title,
Details as Details,
TaskCategoryID as CategoryID
FROM ScheduledTaskDefinitions
WHERE ScheduledTaskID = :id AND ScheduledTaskBusinessID = :businessID
WHERE ID = :id AND BusinessID = :businessID
", {
id: { value: scheduledTaskID, cfsqltype: "cf_sql_integer" },
businessID: { value: businessID, cfsqltype: "cf_sql_integer" }
@ -65,11 +65,11 @@ try {
apiAbort({ "OK": false, "ERROR": "not_found", "MESSAGE": "Scheduled task not found" });
}
// Create the task (TaskClaimedByUserID=0 means unclaimed/pending)
// Create the task (ClaimedByUserID=0 means unclaimed/pending)
queryExecute("
INSERT INTO Tasks (
TaskBusinessID, TaskCategoryID, TaskTypeID,
TaskTitle, TaskDetails, TaskAddedOn, TaskClaimedByUserID
BusinessID, CategoryID, TaskTypeID,
Title, Details, CreatedOn, ClaimedByUserID
) VALUES (
:businessID, :categoryID, :typeID,
:title, :details, NOW(), 0

View file

@ -68,26 +68,26 @@ try {
dueTasks = queryExecute("
SELECT
ScheduledTaskID,
ScheduledTaskBusinessID as BusinessID,
ScheduledTaskCategoryID as CategoryID,
ScheduledTaskTitle as Title,
ScheduledTaskDetails as Details,
ScheduledTaskCronExpression as CronExpression,
COALESCE(ScheduledTaskScheduleType, 'cron') as ScheduleType,
ScheduledTaskIntervalMinutes as IntervalMinutes
BusinessID as BusinessID,
TaskCategoryID as CategoryID,
Title as Title,
Details as Details,
CronExpression as CronExpression,
COALESCE(ScheduleType, 'cron') as ScheduleType,
IntervalMinutes as IntervalMinutes
FROM ScheduledTaskDefinitions
WHERE ScheduledTaskIsActive = 1
AND ScheduledTaskNextRunOn <= NOW()
WHERE IsActive = 1
AND NextRunOn <= NOW()
", {}, { datasource: "payfrit" });
createdTasks = [];
for (task in dueTasks) {
// Create the actual task (TaskClaimedByUserID=0 means unclaimed/pending)
// Create the actual task (ClaimedByUserID=0 means unclaimed/pending)
queryExecute("
INSERT INTO Tasks (
TaskBusinessID, TaskCategoryID, TaskTypeID,
TaskTitle, TaskDetails, TaskAddedOn, TaskClaimedByUserID
BusinessID, CategoryID, TaskTypeID,
Title, Details, CreatedOn, ClaimedByUserID
) VALUES (
:businessID, :categoryID, :typeID,
:title, :details, NOW(), 0
@ -117,16 +117,16 @@ try {
queryExecute("
UPDATE ScheduledTaskDefinitions SET
ScheduledTaskLastRunOn = NOW(),
ScheduledTaskNextRunOn = :nextRun
WHERE ScheduledTaskID = :id
LastRunOn = NOW(),
NextRunOn = :nextRun
WHERE ID = :id
", {
nextRun: { value: nextRun, cfsqltype: "cf_sql_timestamp", null: isNull(nextRun) },
id: { value: task.ScheduledTaskID, cfsqltype: "cf_sql_integer" }
id: { value: task.ID, cfsqltype: "cf_sql_integer" }
}, { datasource: "payfrit" });
arrayAppend(createdTasks, {
"ScheduledTaskID": task.ScheduledTaskID,
"ScheduledTaskID": task.ID,
"TaskID": qNew.newID,
"BusinessID": task.BusinessID,
"Title": task.Title

View file

@ -158,7 +158,7 @@ try {
// UPDATE existing
qCheck = queryExecute("
SELECT ScheduledTaskID FROM ScheduledTaskDefinitions
WHERE ScheduledTaskID = :id AND ScheduledTaskBusinessID = :businessID
WHERE ID = :id AND BusinessID = :businessID
", {
id: { value: taskID, cfsqltype: "cf_sql_integer" },
businessID: { value: businessID, cfsqltype: "cf_sql_integer" }
@ -170,16 +170,16 @@ try {
queryExecute("
UPDATE ScheduledTaskDefinitions SET
ScheduledTaskName = :name,
ScheduledTaskTitle = :title,
ScheduledTaskDetails = :details,
ScheduledTaskCategoryID = :categoryID,
ScheduledTaskCronExpression = :cron,
ScheduledTaskScheduleType = :scheduleType,
ScheduledTaskIntervalMinutes = :intervalMinutes,
ScheduledTaskIsActive = :isActive,
ScheduledTaskNextRunOn = :nextRun
WHERE ScheduledTaskID = :id
Name = :name,
Title = :title,
Details = :details,
TaskCategoryID = :categoryID,
CronExpression = :cron,
ScheduleType = :scheduleType,
IntervalMinutes = :intervalMinutes,
IsActive = :isActive,
NextRunOn = :nextRun
WHERE ID = :id
", {
name: { value: taskName, cfsqltype: "cf_sql_varchar" },
title: { value: taskTitle, cfsqltype: "cf_sql_varchar" },
@ -204,10 +204,10 @@ try {
// INSERT new
queryExecute("
INSERT INTO ScheduledTaskDefinitions (
ScheduledTaskBusinessID, ScheduledTaskName, ScheduledTaskTitle,
ScheduledTaskDetails, ScheduledTaskCategoryID,
ScheduledTaskCronExpression, ScheduledTaskScheduleType, ScheduledTaskIntervalMinutes,
ScheduledTaskIsActive, ScheduledTaskNextRunOn
BusinessID, Name, Title,
Details, TaskCategoryID,
CronExpression, ScheduleType, IntervalMinutes,
IsActive, NextRunOn
) VALUES (
:businessID, :name, :title, :details, :categoryID, :cron, :scheduleType, :intervalMinutes, :isActive, :nextRun
)
@ -230,8 +230,8 @@ try {
// Create the first task immediately
queryExecute("
INSERT INTO Tasks (
TaskBusinessID, TaskCategoryID, TaskTypeID,
TaskTitle, TaskDetails, TaskAddedOn, TaskClaimedByUserID
BusinessID, CategoryID, TaskTypeID,
Title, Details, CreatedOn, ClaimedByUserID
) VALUES (
:businessID, :categoryID, :typeID,
:title, :details, NOW(), 0
@ -262,9 +262,9 @@ try {
queryExecute("
UPDATE ScheduledTaskDefinitions
SET ScheduledTaskLastRunOn = NOW(),
ScheduledTaskNextRunOn = :nextRun
WHERE ScheduledTaskID = :id
SET LastRunOn = NOW(),
NextRunOn = :nextRun
WHERE ID = :id
", {
nextRun: { value: actualNextRun, cfsqltype: "cf_sql_timestamp", null: isNull(actualNextRun) },
id: { value: newScheduledTaskID, cfsqltype: "cf_sql_integer" }

View file

@ -15,23 +15,23 @@ try {
// Create ScheduledTaskDefinitions table
queryExecute("
CREATE TABLE IF NOT EXISTS ScheduledTaskDefinitions (
ScheduledTaskID INT AUTO_INCREMENT PRIMARY KEY,
ScheduledTaskBusinessID INT NOT NULL,
ScheduledTaskName VARCHAR(100) NOT NULL,
ScheduledTaskCategoryID INT NULL,
ScheduledTaskTypeID INT NULL,
ScheduledTaskTitle VARCHAR(255) NOT NULL,
ScheduledTaskDetails TEXT NULL,
ScheduledTaskCronExpression VARCHAR(100) NOT NULL,
ScheduledTaskScheduleType VARCHAR(20) DEFAULT 'cron',
ScheduledTaskIntervalMinutes INT NULL,
ScheduledTaskIsActive BIT(1) DEFAULT b'1',
ScheduledTaskLastRunOn DATETIME NULL,
ScheduledTaskNextRunOn DATETIME NULL,
ScheduledTaskCreatedOn DATETIME DEFAULT CURRENT_TIMESTAMP,
ScheduledTaskCreatedByUserID INT NULL,
INDEX idx_business (ScheduledTaskBusinessID),
INDEX idx_active_next (ScheduledTaskIsActive, ScheduledTaskNextRunOn)
ID INT AUTO_INCREMENT PRIMARY KEY,
BusinessID INT NOT NULL,
Name VARCHAR(100) NOT NULL,
TaskCategoryID INT NULL,
TaskTypeID INT NULL,
Title VARCHAR(255) NOT NULL,
Details TEXT NULL,
CronExpression VARCHAR(100) NOT NULL,
ScheduleType VARCHAR(20) DEFAULT 'cron',
IntervalMinutes INT NULL,
IsActive BIT(1) DEFAULT b'1',
LastRunOn DATETIME NULL,
NextRunOn DATETIME NULL,
CreatedOn DATETIME DEFAULT CURRENT_TIMESTAMP,
CreatedByUserID INT NULL,
INDEX idx_business (BusinessID),
INDEX idx_active_next (IsActive, NextRunOn)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
", [], { datasource: "payfrit" });
@ -39,7 +39,7 @@ try {
try {
queryExecute("
ALTER TABLE ScheduledTaskDefinitions
ADD COLUMN ScheduledTaskScheduleType VARCHAR(20) DEFAULT 'cron' AFTER ScheduledTaskCronExpression
ADD COLUMN ScheduleType VARCHAR(20) DEFAULT 'cron' AFTER CronExpression
", [], { datasource: "payfrit" });
} catch (any e) {
// Column likely already exists
@ -48,7 +48,7 @@ try {
try {
queryExecute("
ALTER TABLE ScheduledTaskDefinitions
ADD COLUMN ScheduledTaskIntervalMinutes INT NULL AFTER ScheduledTaskScheduleType
ADD COLUMN IntervalMinutes INT NULL AFTER ScheduleType
", [], { datasource: "payfrit" });
} catch (any e) {
// Column likely already exists

View file

@ -99,11 +99,11 @@ try {
// Verify exists and get cron expression and schedule type
qCheck = queryExecute("
SELECT ScheduledTaskID, ScheduledTaskCronExpression as CronExpression,
COALESCE(ScheduledTaskScheduleType, 'cron') as ScheduleType,
ScheduledTaskIntervalMinutes as IntervalMinutes
SELECT ScheduledTaskID, CronExpression as CronExpression,
COALESCE(ScheduleType, 'cron') as ScheduleType,
IntervalMinutes as IntervalMinutes
FROM ScheduledTaskDefinitions
WHERE ScheduledTaskID = :id AND ScheduledTaskBusinessID = :businessID
WHERE ID = :id AND BusinessID = :businessID
", {
id: { value: taskID, cfsqltype: "cf_sql_integer" },
businessID: { value: businessID, cfsqltype: "cf_sql_integer" }
@ -123,16 +123,16 @@ try {
// Cron-based: use cron parser
nextRunOn = calculateNextRun(qCheck.CronExpression);
}
nextRunUpdate = ", ScheduledTaskNextRunOn = :nextRun";
nextRunUpdate = ", NextRunOn = :nextRun";
}
// Update status
if (isActive) {
queryExecute("
UPDATE ScheduledTaskDefinitions SET
ScheduledTaskIsActive = :isActive,
ScheduledTaskNextRunOn = :nextRun
WHERE ScheduledTaskID = :id
IsActive = :isActive,
NextRunOn = :nextRun
WHERE ID = :id
", {
isActive: { value: isActive, cfsqltype: "cf_sql_bit" },
nextRun: { value: nextRunOn, cfsqltype: "cf_sql_timestamp" },
@ -140,8 +140,8 @@ try {
}, { datasource: "payfrit" });
} else {
queryExecute("
UPDATE ScheduledTaskDefinitions SET ScheduledTaskIsActive = :isActive
WHERE ScheduledTaskID = :id
UPDATE ScheduledTaskDefinitions SET IsActive = :isActive
WHERE ID = :id
", {
isActive: { value: isActive, cfsqltype: "cf_sql_bit" },
id: { value: taskID, cfsqltype: "cf_sql_integer" }

View file

@ -21,8 +21,8 @@ if (businessId <= 0 || userId <= 0) {
try {
// Update employee record
queryExecute("
UPDATE lt_Users_Businesses_Employees
SET EmployeeIsActive = ?
UPDATE Employees
SET IsActive = ?
WHERE BusinessID = ? AND UserID = ?
", [
{ value: isActive, cfsqltype: "cf_sql_bit" },
@ -32,12 +32,12 @@ try {
// Get updated record
q = queryExecute("
SELECT e.EmployeeID, e.BusinessID, e.UserID, e.EmployeeStatusID,
CAST(e.EmployeeIsActive AS UNSIGNED) AS EmployeeIsActive,
b.BusinessName, u.UserFirstName, u.UserLastName
FROM lt_Users_Businesses_Employees e
JOIN Businesses b ON e.BusinessID = b.BusinessID
JOIN Users u ON e.UserID = u.UserID
SELECT e.ID, e.BusinessID, e.UserID, e.StatusID,
CAST(e.IsActive AS UNSIGNED) AS IsActive,
b.Name, u.FirstName, u.LastName
FROM Employees e
JOIN Businesses b ON e.BusinessID = b.ID
JOIN Users u ON e.UserID = u.ID
WHERE e.BusinessID = ? AND e.UserID = ?
", [
{ value: businessId, cfsqltype: "cf_sql_integer" },
@ -49,13 +49,13 @@ try {
"OK": true,
"MESSAGE": "Employee updated",
"EMPLOYEE": {
"EmployeeID": q.EmployeeID,
"EmployeeID": q.ID,
"BusinessID": q.BusinessID,
"BusinessName": q.BusinessName,
"Name": q.Name,
"UserID": q.UserID,
"UserName": trim(q.UserFirstName & " " & q.UserLastName),
"StatusID": q.EmployeeStatusID,
"IsActive": q.EmployeeIsActive
"UserName": trim(q.FirstName & " " & q.LastName),
"StatusID": q.StatusID,
"IsActive": q.IsActive
}
}));
} else {

View file

@ -7,11 +7,11 @@ response = { "OK": false };
try {
queryExecute("
UPDATE Businesses SET BusinessHeaderImageExtension = 'jpg' WHERE BusinessID = 37
UPDATE Businesses SET HeaderImageExtension = 'jpg' WHERE ID = 37
", {}, { datasource: "payfrit" });
response.OK = true;
response.message = "Set BusinessHeaderImageExtension to 'jpg' for business 37";
response.message = "Set HeaderImageExtension to 'jpg' for business 37";
} catch (any e) {
response.error = e.message;
}

View file

@ -5,7 +5,7 @@
<cfscript>
/**
* Setup Lazy Daisy Beacons
* Creates a beacon for each service point and links them
* Creates a beacon for each service point and assigns them
*/
response = { "OK": false, "steps": [] };
@ -14,10 +14,10 @@ try {
// Get all service points for Lazy Daisy
qServicePoints = queryExecute("
SELECT ServicePointID, ServicePointName
SELECT ID, Name
FROM ServicePoints
WHERE ServicePointBusinessID = :bizID AND ServicePointIsActive = 1
ORDER BY ServicePointID
WHERE BusinessID = :bizID AND IsActive = 1
ORDER BY SortOrder, ID
", { bizID: lazyDaisyID }, { datasource: "payfrit" });
response.steps.append("Found " & qServicePoints.recordCount & " service points for Lazy Daisy");
@ -25,20 +25,20 @@ try {
// Create a beacon for each service point
beaconsCreated = 0;
for (sp in qServicePoints) {
beaconName = "Beacon - " & sp.ServicePointName;
beaconName = "Beacon - " & sp.Name;
// Check if beacon already exists for this business with this name
qExisting = queryExecute("
SELECT BeaconID FROM Beacons
WHERE BeaconBusinessID = :bizId AND BeaconName = :name
SELECT ID FROM Beacons
WHERE BusinessID = :bizId AND Name = :name
", { bizId: lazyDaisyID, name: beaconName }, { datasource: "payfrit" });
if (qExisting.recordCount == 0) {
// Generate a unique UUID for this beacon (32 hex chars, no dashes)
beaconUUID = "PAYFRIT00037" & numberFormat(sp.ServicePointID, "0000000000000000000");
beaconUUID = "PAYFRIT00037" & numberFormat(sp.ID, "0000000000000000000");
queryExecute("
INSERT INTO Beacons (BeaconBusinessID, BeaconName, BeaconUUID, BeaconIsActive)
INSERT INTO Beacons (BusinessID, Name, UUID, IsActive)
VALUES (:bizId, :name, :uuid, 1)
", {
bizId: lazyDaisyID,
@ -49,18 +49,18 @@ try {
qNewBeacon = queryExecute("SELECT LAST_INSERT_ID() as id", {}, { datasource: "payfrit" });
newBeaconId = qNewBeacon.id;
// Create assignment to service point
// Assign beacon directly to service point
queryExecute("
INSERT INTO lt_Beacon_Businesses_ServicePoints
(BeaconID, BusinessID, ServicePointID, lt_Beacon_Businesses_ServicePointAssignedByUserID)
VALUES (:beaconId, :bizId, :spId, 1)
UPDATE ServicePoints
SET BeaconID = :beaconId, AssignedByUserID = 1
WHERE ID = :spId AND BusinessID = :bizId
", {
beaconId: newBeaconId,
bizId: lazyDaisyID,
spId: sp.ServicePointID
spId: sp.ID
}, { datasource: "payfrit" });
response.steps.append("Created beacon '" & beaconName & "' (ID: " & newBeaconId & ") -> " & sp.ServicePointName);
response.steps.append("Created beacon '" & beaconName & "' (ID: " & newBeaconId & ") -> " & sp.Name);
beaconsCreated++;
} else {
response.steps.append("Beacon '" & beaconName & "' already exists, skipping");
@ -69,13 +69,14 @@ try {
// Get final status
qFinal = queryExecute("
SELECT lt.BeaconID, b.BeaconUUID, b.BeaconName, lt.BusinessID, biz.BusinessName, lt.ServicePointID, sp.ServicePointName
FROM lt_Beacon_Businesses_ServicePoints lt
JOIN Beacons b ON b.BeaconID = lt.BeaconID
JOIN Businesses biz ON biz.BusinessID = lt.BusinessID
LEFT JOIN ServicePoints sp ON sp.ServicePointID = lt.ServicePointID
WHERE lt.BusinessID = :bizId
ORDER BY sp.ServicePointName
SELECT sp.ID AS ServicePointID, sp.BeaconID, sp.BusinessID,
b.Name AS BeaconName, b.UUID, sp.Name AS ServicePointName,
biz.Name AS BusinessName
FROM ServicePoints sp
JOIN Beacons b ON b.ID = sp.BeaconID
JOIN Businesses biz ON biz.ID = sp.BusinessID
WHERE sp.BusinessID = :bizId AND sp.BeaconID IS NOT NULL
ORDER BY sp.Name
", { bizId: lazyDaisyID }, { datasource: "payfrit" });
beacons = [];
@ -83,8 +84,9 @@ try {
arrayAppend(beacons, {
"BeaconID": qFinal.BeaconID[i],
"BeaconName": qFinal.BeaconName[i],
"UUID": qFinal.BeaconUUID[i],
"UUID": qFinal.UUID[i],
"BusinessName": qFinal.BusinessName[i],
"ServicePointID": qFinal.ServicePointID[i],
"ServicePointName": qFinal.ServicePointName[i]
});
}

View file

@ -13,19 +13,19 @@ try {
// Hours: Mon-Thu: 11am-10pm, Fri-Sat: 11am-11pm, Sun: 11am-10pm
// Get California StateID
qState = queryExecute("SELECT tt_StateID FROM tt_States WHERE tt_StateAbbreviation = 'CA' LIMIT 1");
qState = queryExecute("SELECT tt_StateID FROM tt_States WHERE Abbreviation = 'CA' LIMIT 1");
stateId = qState.recordCount > 0 ? qState.tt_StateID : 5; // Default to 5 if not found
// Check if Big Dean's already has an address
existingAddr = queryExecute("
SELECT AddressID FROM Addresses
WHERE AddressBusinessID = :bizId AND AddressUserID = 0
SELECT ID FROM Addresses
WHERE BusinessID = :bizId AND UserID = 0
", { bizId: businessId });
if (existingAddr.recordCount == 0) {
// Insert new address
queryExecute("
INSERT INTO Addresses (AddressUserID, AddressBusinessID, AddressTypeID, AddressLine1, AddressCity, AddressStateID, AddressZIPCode, AddressIsDeleted, AddressAddedOn)
INSERT INTO Addresses (UserID, BusinessID, AddressTypeID, Line1, City, StateID, ZIPCode, IsDeleted, AddedOn)
VALUES (0, :bizId, '2', :line1, :city, :stateId, :zip, 0, NOW())
", {
bizId: businessId,
@ -39,8 +39,8 @@ try {
// Update existing address
queryExecute("
UPDATE Addresses
SET AddressLine1 = :line1, AddressCity = :city, AddressStateID = :stateId, AddressZIPCode = :zip
WHERE AddressBusinessID = :bizId AND AddressUserID = 0
SET Line1 = :line1, City = :city, StateID = :stateId, ZIPCode = :zip
WHERE BusinessID = :bizId AND UserID = 0
", {
bizId: businessId,
line1: "1615 Ocean Front Walk",
@ -53,7 +53,7 @@ try {
// Check existing hours for this business
existingHours = queryExecute("
SELECT COUNT(*) as cnt FROM Hours WHERE HoursBusinessID = :bizId
SELECT COUNT(*) as cnt FROM Hours WHERE BusinessID = :bizId
", { bizId: businessId });
if (existingHours.cnt == 0) {
@ -64,39 +64,39 @@ try {
// Sun: 11am-10pm (day 1)
// Sunday (1): 11am-10pm
queryExecute("INSERT INTO Hours (HoursBusinessID, HoursDayID, HoursOpenTime, HoursClosingTime) VALUES (:bizId, 1, '11:00:00', '22:00:00')", { bizId: businessId });
queryExecute("INSERT INTO Hours (BusinessID, DayID, OpenTime, ClosingTime) VALUES (:bizId, 1, '11:00:00', '22:00:00')", { bizId: businessId });
// Monday (2): 11am-10pm
queryExecute("INSERT INTO Hours (HoursBusinessID, HoursDayID, HoursOpenTime, HoursClosingTime) VALUES (:bizId, 2, '11:00:00', '22:00:00')", { bizId: businessId });
queryExecute("INSERT INTO Hours (BusinessID, DayID, OpenTime, ClosingTime) VALUES (:bizId, 2, '11:00:00', '22:00:00')", { bizId: businessId });
// Tuesday (3): 11am-10pm
queryExecute("INSERT INTO Hours (HoursBusinessID, HoursDayID, HoursOpenTime, HoursClosingTime) VALUES (:bizId, 3, '11:00:00', '22:00:00')", { bizId: businessId });
queryExecute("INSERT INTO Hours (BusinessID, DayID, OpenTime, ClosingTime) VALUES (:bizId, 3, '11:00:00', '22:00:00')", { bizId: businessId });
// Wednesday (4): 11am-10pm
queryExecute("INSERT INTO Hours (HoursBusinessID, HoursDayID, HoursOpenTime, HoursClosingTime) VALUES (:bizId, 4, '11:00:00', '22:00:00')", { bizId: businessId });
queryExecute("INSERT INTO Hours (BusinessID, DayID, OpenTime, ClosingTime) VALUES (:bizId, 4, '11:00:00', '22:00:00')", { bizId: businessId });
// Thursday (5): 11am-10pm
queryExecute("INSERT INTO Hours (HoursBusinessID, HoursDayID, HoursOpenTime, HoursClosingTime) VALUES (:bizId, 5, '11:00:00', '22:00:00')", { bizId: businessId });
queryExecute("INSERT INTO Hours (BusinessID, DayID, OpenTime, ClosingTime) VALUES (:bizId, 5, '11:00:00', '22:00:00')", { bizId: businessId });
// Friday (6): 11am-11pm
queryExecute("INSERT INTO Hours (HoursBusinessID, HoursDayID, HoursOpenTime, HoursClosingTime) VALUES (:bizId, 6, '11:00:00', '23:00:00')", { bizId: businessId });
queryExecute("INSERT INTO Hours (BusinessID, DayID, OpenTime, ClosingTime) VALUES (:bizId, 6, '11:00:00', '23:00:00')", { bizId: businessId });
// Saturday (7): 11am-11pm
queryExecute("INSERT INTO Hours (HoursBusinessID, HoursDayID, HoursOpenTime, HoursClosingTime) VALUES (:bizId, 7, '11:00:00', '23:00:00')", { bizId: businessId });
queryExecute("INSERT INTO Hours (BusinessID, DayID, OpenTime, ClosingTime) VALUES (:bizId, 7, '11:00:00', '23:00:00')", { bizId: businessId });
response["HOURS_ACTION"] = "inserted 7 days";
} else {
// Update existing hours
// Mon-Thu: 11am-10pm
queryExecute("UPDATE Hours SET HoursOpenTime = '11:00:00', HoursClosingTime = '22:00:00' WHERE HoursBusinessID = :bizId AND HoursDayID IN (1, 2, 3, 4, 5)", { bizId: businessId });
queryExecute("UPDATE Hours SET OpenTime = '11:00:00', ClosingTime = '22:00:00' WHERE BusinessID = :bizId AND DayID IN (1, 2, 3, 4, 5)", { bizId: businessId });
// Fri-Sat: 11am-11pm
queryExecute("UPDATE Hours SET HoursOpenTime = '11:00:00', HoursClosingTime = '23:00:00' WHERE HoursBusinessID = :bizId AND HoursDayID IN (6, 7)", { bizId: businessId });
queryExecute("UPDATE Hours SET OpenTime = '11:00:00', ClosingTime = '23:00:00' WHERE BusinessID = :bizId AND DayID IN (6, 7)", { bizId: businessId });
response["HOURS_ACTION"] = "updated";
}
// Update phone on Businesses table (if column exists)
try {
queryExecute("UPDATE Businesses SET BusinessPhone = :phone WHERE BusinessID = :bizId", {
queryExecute("UPDATE Businesses SET Phone = :phone WHERE ID = :bizId", {
phone: "(310) 393-2666",
bizId: businessId
});
@ -107,35 +107,35 @@ try {
// Verify the data
address = queryExecute("
SELECT a.*, s.tt_StateAbbreviation
SELECT a.*, s.Abbreviation
FROM Addresses a
LEFT JOIN tt_States s ON s.tt_StateID = a.AddressStateID
WHERE a.AddressBusinessID = :bizId AND a.AddressUserID = 0
LEFT JOIN tt_States s ON s.ID = a.StateID
WHERE a.BusinessID = :bizId AND a.UserID = 0
", { bizId: businessId });
hours = queryExecute("
SELECT h.*, d.tt_DayName
FROM Hours h
JOIN tt_Days d ON d.tt_DayID = h.HoursDayID
WHERE h.HoursBusinessID = :bizId
ORDER BY h.HoursDayID
JOIN tt_Days d ON d.ID = h.DayID
WHERE h.BusinessID = :bizId
ORDER BY h.DayID
", { bizId: businessId });
response["OK"] = true;
response["BUSINESS_ID"] = businessId;
response["ADDRESS"] = address.recordCount > 0 ? {
"line1": address.AddressLine1,
"city": address.AddressCity,
"state": address.tt_StateAbbreviation,
"zip": address.AddressZIPCode
"line1": address.Line1,
"city": address.City,
"state": address.Abbreviation,
"zip": address.ZIPCode
} : "not found";
hoursArr = [];
for (h in hours) {
arrayAppend(hoursArr, {
"day": h.tt_DayName,
"open": timeFormat(h.HoursOpenTime, "h:mm tt"),
"close": timeFormat(h.HoursClosingTime, "h:mm tt")
"open": timeFormat(h.OpenTime, "h:mm tt"),
"close": timeFormat(h.ClosingTime, "h:mm tt")
});
}
response["HOURS"] = hoursArr;

View file

@ -9,19 +9,19 @@ response = { "OK": false };
try {
// Check if Big Dean's already has stations
existing = queryExecute("
SELECT COUNT(*) as cnt FROM Stations WHERE StationBusinessID = :bizId
SELECT COUNT(*) as cnt FROM Stations WHERE BusinessID = :bizId
", { bizId: businessId });
if (existing.cnt == 0) {
// Insert Kitchen station
queryExecute("
INSERT INTO Stations (StationBusinessID, StationName, StationColor, StationSortOrder, StationIsActive)
INSERT INTO Stations (BusinessID, Name, Color, SortOrder, IsActive)
VALUES (:bizId, 'Kitchen', :color1, 1, 1)
", { bizId: businessId, color1: "##FF5722" });
// Insert Bar station
queryExecute("
INSERT INTO Stations (StationBusinessID, StationName, StationColor, StationSortOrder, StationIsActive)
INSERT INTO Stations (BusinessID, Name, Color, SortOrder, IsActive)
VALUES (:bizId, 'Bar', :color2, 2, 1)
", { bizId: businessId, color2: "##2196F3" });
@ -32,18 +32,18 @@ try {
// Get current stations
stations = queryExecute("
SELECT StationID, StationName, StationColor, StationSortOrder
SELECT ID, Name, Color, SortOrder
FROM Stations
WHERE StationBusinessID = :bizId AND StationIsActive = 1
ORDER BY StationSortOrder
WHERE BusinessID = :bizId AND IsActive = 1
ORDER BY SortOrder
", { bizId: businessId });
stationArr = [];
for (s in stations) {
arrayAppend(stationArr, {
"StationID": s.StationID,
"StationName": s.StationName,
"StationColor": s.StationColor
"StationID": s.ID,
"Name": s.Name,
"Color": s.Color
});
}

View file

@ -9,67 +9,62 @@ try {
lazyDaisyID = 37;
// Get all beacons
qBeacons = queryExecute("SELECT BeaconID, BeaconUUID FROM Beacons", {}, { datasource: "payfrit" });
qBeacons = queryExecute("SELECT ID, UUID FROM Beacons", {}, { datasource: "payfrit" });
response.steps.append("Found " & qBeacons.recordCount & " beacons");
// Create service point for Table 1 if it doesn't exist
qSP = queryExecute("
SELECT ServicePointID FROM ServicePoints
WHERE ServicePointBusinessID = :bizID AND ServicePointName = 'Table 1'
SELECT ID FROM ServicePoints
WHERE BusinessID = :bizID AND Name = 'Table 1'
", { bizID: lazyDaisyID }, { datasource: "payfrit" });
if (qSP.recordCount == 0) {
queryExecute("
INSERT INTO ServicePoints (ServicePointBusinessID, ServicePointName, ServicePointTypeID)
VALUES (:bizID, 'Table 1', 1)
INSERT INTO ServicePoints (BusinessID, Name)
VALUES (:bizID, 'Table 1')
", { bizID: lazyDaisyID }, { datasource: "payfrit" });
qSP = queryExecute("SELECT LAST_INSERT_ID() as id", {}, { datasource: "payfrit" });
servicePointID = qSP.id;
response.steps.append("Created service point 'Table 1' (ID: " & servicePointID & ")");
} else {
servicePointID = qSP.ServicePointID;
servicePointID = qSP.ID;
response.steps.append("Found existing service point 'Table 1' (ID: " & servicePointID & ")");
}
// Map all beacons to Lazy Daisy with Table 1
// Assign all beacons to the Table 1 service point
for (i = 1; i <= qBeacons.recordCount; i++) {
beaconID = qBeacons.BeaconID[i];
beaconID = qBeacons.ID[i];
// Check if mapping exists
qMap = queryExecute("
SELECT * FROM lt_Beacon_Businesses_ServicePoints WHERE BeaconID = :beaconID
// Unassign this beacon from any other service point first
queryExecute("
UPDATE ServicePoints SET BeaconID = NULL, AssignedByUserID = NULL
WHERE BeaconID = :beaconID
", { beaconID: beaconID }, { datasource: "payfrit" });
if (qMap.recordCount == 0) {
queryExecute("
INSERT INTO lt_Beacon_Businesses_ServicePoints (BeaconID, BusinessID, ServicePointID)
VALUES (:beaconID, :bizID, :spID)
", { beaconID: beaconID, bizID: lazyDaisyID, spID: servicePointID }, { datasource: "payfrit" });
response.steps.append("Created mapping for beacon " & beaconID);
} else {
queryExecute("
UPDATE lt_Beacon_Businesses_ServicePoints
SET BusinessID = :bizID, ServicePointID = :spID
WHERE BeaconID = :beaconID
", { beaconID: beaconID, bizID: lazyDaisyID, spID: servicePointID }, { datasource: "payfrit" });
response.steps.append("Updated mapping for beacon " & beaconID);
}
// Assign beacon to Table 1 service point
queryExecute("
UPDATE ServicePoints SET BeaconID = :beaconID, AssignedByUserID = 1
WHERE ID = :spID AND BusinessID = :bizID
", { beaconID: beaconID, bizID: lazyDaisyID, spID: servicePointID }, { datasource: "payfrit" });
response.steps.append("Assigned beacon " & beaconID & " to Table 1");
}
// Get final status
qFinal = queryExecute("
SELECT lt.BeaconID, b.BeaconUUID, lt.BusinessID, biz.BusinessName, lt.ServicePointID, sp.ServicePointName
FROM lt_Beacon_Businesses_ServicePoints lt
JOIN Beacons b ON b.BeaconID = lt.BeaconID
JOIN Businesses biz ON biz.BusinessID = lt.BusinessID
LEFT JOIN ServicePoints sp ON sp.ServicePointID = lt.ServicePointID
SELECT sp.ID AS ServicePointID, sp.BeaconID, sp.BusinessID,
b.Name AS BeaconName, b.UUID, sp.Name AS ServicePointName,
biz.Name AS BusinessName
FROM ServicePoints sp
JOIN Beacons b ON b.ID = sp.BeaconID
JOIN Businesses biz ON biz.ID = sp.BusinessID
WHERE sp.BeaconID IS NOT NULL
", {}, { datasource: "payfrit" });
beacons = [];
for (i = 1; i <= qFinal.recordCount; i++) {
arrayAppend(beacons, {
"BeaconID": qFinal.BeaconID[i],
"UUID": qFinal.BeaconUUID[i],
"UUID": qFinal.UUID[i],
"BusinessID": qFinal.BusinessID[i],
"BusinessName": qFinal.BusinessName[i],
"ServicePointID": qFinal.ServicePointID[i],

View file

@ -11,32 +11,32 @@
<cfscript>
/**
* Setup Modifier Templates system
* 1. Add ItemIsModifierTemplate column to Items
* 2. Create ItemTemplateLinks table
* 1. Add IsModifierTemplate column to Items
* 2. Create lt_ItemID_TemplateItemID table
*/
response = { "OK": false, "steps": [] };
try {
// Step 1: Add ItemIsModifierTemplate column if it doesn't exist
// Step 1: Add IsModifierTemplate column if it doesn't exist
try {
queryExecute("
ALTER TABLE Items ADD COLUMN ItemIsModifierTemplate TINYINT(1) DEFAULT 0
ALTER TABLE Items ADD COLUMN IsModifierTemplate TINYINT(1) DEFAULT 0
", {}, { datasource: "payfrit" });
arrayAppend(response.steps, "Added ItemIsModifierTemplate column");
arrayAppend(response.steps, "Added IsModifierTemplate column");
} catch (any e) {
if (findNoCase("Duplicate column", e.message)) {
arrayAppend(response.steps, "ItemIsModifierTemplate column already exists");
arrayAppend(response.steps, "IsModifierTemplate column already exists");
} else {
arrayAppend(response.steps, "Error adding column: " & e.message);
}
}
// Step 2: Create ItemTemplateLinks table if it doesn't exist
// Step 2: Create lt_ItemID_TemplateItemID table if it doesn't exist
try {
queryExecute("
CREATE TABLE IF NOT EXISTS ItemTemplateLinks (
LinkID INT AUTO_INCREMENT PRIMARY KEY,
CREATE TABLE IF NOT EXISTS lt_ItemID_TemplateItemID (
ID INT AUTO_INCREMENT PRIMARY KEY,
ItemID INT NOT NULL,
TemplateItemID INT NOT NULL,
SortOrder INT DEFAULT 0,
@ -45,9 +45,9 @@ try {
INDEX idx_template (TemplateItemID)
)
", {}, { datasource: "payfrit" });
arrayAppend(response.steps, "Created ItemTemplateLinks table");
arrayAppend(response.steps, "Created lt_ItemID_TemplateItemID table");
} catch (any e) {
arrayAppend(response.steps, "ItemTemplateLinks: " & e.message);
arrayAppend(response.steps, "lt_ItemID_TemplateItemID: " & e.message);
}
response["OK"] = true;

View file

@ -13,20 +13,20 @@
<cfset queryExecute("
CREATE TABLE IF NOT EXISTS Stations (
StationID INT AUTO_INCREMENT PRIMARY KEY,
StationBusinessID INT NOT NULL,
StationName VARCHAR(100) NOT NULL,
StationColor VARCHAR(7) DEFAULT '##666666',
StationSortOrder INT DEFAULT 0,
StationIsActive TINYINT(1) DEFAULT 1,
StationAddedOn DATETIME DEFAULT NOW(),
FOREIGN KEY (StationBusinessID) REFERENCES Businesses(BusinessID)
BusinessID INT NOT NULL,
Name VARCHAR(100) NOT NULL,
Color VARCHAR(7) DEFAULT '##666666',
SortOrder INT DEFAULT 0,
IsActive TINYINT(1) DEFAULT 1,
AddedOn DATETIME DEFAULT NOW(),
FOREIGN KEY (BusinessID) REFERENCES Businesses(BusinessID)
)
", [], { datasource = "payfrit" })>
<!--- Add ItemStationID column to Items table if it doesn't exist --->
<!--- Add StationID column to Items table if it doesn't exist --->
<cftry>
<cfset queryExecute("
ALTER TABLE Items ADD COLUMN ItemStationID INT DEFAULT NULL
ALTER TABLE Items ADD COLUMN StationID INT DEFAULT NULL
", [], { datasource = "payfrit" })>
<cfset stationColumnAdded = true>
<cfcatch>
@ -39,7 +39,7 @@
<cfif stationColumnAdded>
<cftry>
<cfset queryExecute("
ALTER TABLE Items ADD FOREIGN KEY (ItemStationID) REFERENCES Stations(StationID)
ALTER TABLE Items ADD FOREIGN KEY (StationID) REFERENCES Stations(StationID)
", [], { datasource = "payfrit" })>
<cfcatch></cfcatch>
</cftry>
@ -47,12 +47,12 @@
<!--- Create some default stations for business 1 (In and Out Burger) if none exist --->
<cfset qCheck = queryExecute("
SELECT COUNT(*) AS cnt FROM Stations WHERE StationBusinessID = 1
SELECT COUNT(*) AS cnt FROM Stations WHERE BusinessID = 1
", [], { datasource = "payfrit" })>
<cfif qCheck.cnt EQ 0>
<cfset queryExecute("
INSERT INTO Stations (StationBusinessID, StationName, StationColor, StationSortOrder) VALUES
INSERT INTO Stations (BusinessID, Name, Color, SortOrder) VALUES
(1, 'Grill', '##FF5722', 1),
(1, 'Fry', '##FFC107', 2),
(1, 'Drinks', '##2196F3', 3),

View file

@ -3,36 +3,57 @@
<cfcontent type="application/json; charset=utf-8" reset="true">
<cfscript>
// Switch all beacons from one business to another
// Switch beacon mapping from one business to another via join table.
// Beacons.BusinessID (owner) is NOT touched.
fromBiz = 17; // In-N-Out
toBiz = 27; // Big Dean's
// Remove mapping for source business
queryExecute("
UPDATE lt_Beacon_Businesses_ServicePoints
SET BusinessID = :toBiz
WHERE BusinessID = :fromBiz
DELETE FROM lt_BeaconsID_BusinessesID
WHERE BusinessID = :fromBiz
", { fromBiz: fromBiz }, { datasource: "payfrit" });
// Add mapping for target business (for beacons owned by source)
queryExecute("
INSERT INTO lt_BeaconsID_BusinessesID (BeaconID, BusinessID)
SELECT ID, :toBiz FROM Beacons WHERE BusinessID = :fromBiz
ON DUPLICATE KEY UPDATE ID = ID
", { toBiz: toBiz, fromBiz: fromBiz }, { datasource: "payfrit" });
// Clear ServicePoints.BeaconID for source business (no longer valid)
queryExecute("
UPDATE ServicePoints
SET BeaconID = NULL, AssignedByUserID = NULL
WHERE BusinessID = :fromBiz AND BeaconID IS NOT NULL
", { fromBiz: fromBiz }, { datasource: "payfrit" });
// Get current state
q = queryExecute("
SELECT lt.*, b.BusinessName
FROM lt_Beacon_Businesses_ServicePoints lt
JOIN Businesses b ON b.BusinessID = lt.BusinessID
SELECT sp.ID AS ServicePointID, sp.BeaconID, sp.BusinessID,
b.Name AS BeaconName, biz.Name AS BusinessName,
sp.Name AS ServicePointName
FROM ServicePoints sp
JOIN Beacons b ON b.ID = sp.BeaconID
JOIN Businesses biz ON biz.ID = sp.BusinessID
WHERE sp.BeaconID IS NOT NULL
", {}, { datasource: "payfrit" });
rows = [];
for (row in q) {
arrayAppend(rows, {
"BeaconID": row.BeaconID,
"BusinessID": row.BusinessID,
"BusinessName": row.BusinessName,
"ServicePointID": row.ServicePointID
});
arrayAppend(rows, {
"BeaconID": row.BeaconID,
"BeaconName": row.BeaconName,
"BusinessID": row.BusinessID,
"BusinessName": row.BusinessName,
"ServicePointID": row.ServicePointID,
"ServicePointName": row.ServicePointName
});
}
writeOutput(serializeJSON({
"OK": true,
"MESSAGE": "Switched beacons from BusinessID #fromBiz# to #toBiz#",
"MAPPINGS": rows
"OK": true,
"MESSAGE": "Switched beacons from BusinessID #fromBiz# to #toBiz#",
"MAPPINGS": rows
}));
</cfscript>

View file

@ -7,10 +7,10 @@
<cfset queryExecute(
"
INSERT INTO Tasks (
TaskBusinessID,
TaskOrderID,
TaskClaimedByUserID,
TaskAddedOn
BusinessID,
OrderID,
ClaimedByUserID,
CreatedOn
) VALUES (
1,
999,

View file

@ -2,16 +2,16 @@
<cfcontent type="application/json; charset=utf-8" reset="true">
<cfscript>
qTask = queryExecute("
SELECT TaskID, TaskTypeID, TaskClaimedByUserID, TaskCompletedOn
SELECT ID, TaskTypeID, ClaimedByUserID, CompletedOn
FROM Tasks
WHERE TaskID = 57
WHERE ID = 57
", [], { datasource: "payfrit" });
writeOutput(serializeJSON({
"OK": true,
"TaskID": qTask.TaskID,
"TaskID": qTask.ID,
"TaskTypeID": qTask.TaskTypeID,
"TaskClaimedByUserID": qTask.TaskClaimedByUserID,
"TaskCompletedOn": qTask.TaskCompletedOn
"ClaimedByUserID": qTask.ClaimedByUserID,
"CompletedOn": qTask.CompletedOn
}));
</cfscript>

View file

@ -3,50 +3,65 @@
<cfcontent type="application/json; charset=utf-8" reset="true">
<cfscript>
// Update Beacon 2 to point to In-N-Out (BusinessID 17)
// Update beacon mapping via join table. Owner (Beacons.BusinessID) is NOT changed.
beaconId = 2;
oldBusinessId = 37; // previous mapping
newBusinessId = 17;
// Remove old mapping
queryExecute("
UPDATE lt_Beacon_Businesses_ServicePoints
SET BusinessID = :newBizId
WHERE BeaconID = :beaconId
", { newBizId: newBusinessId, beaconId: beaconId }, { datasource: "payfrit" });
DELETE FROM lt_BeaconsID_BusinessesID
WHERE BeaconID = :beaconId AND BusinessID = :oldBizId
", { beaconId: beaconId, oldBizId: oldBusinessId }, { datasource: "payfrit" });
// Add new mapping
queryExecute("
INSERT INTO lt_BeaconsID_BusinessesID (BeaconID, BusinessID)
VALUES (:beaconId, :newBizId)
ON DUPLICATE KEY UPDATE ID = ID
", { beaconId: beaconId, newBizId: newBusinessId }, { datasource: "payfrit" });
// Clear ServicePoints.BeaconID for old business where this beacon was assigned
queryExecute("
UPDATE ServicePoints
SET BeaconID = NULL, AssignedByUserID = NULL
WHERE BeaconID = :beaconId AND BusinessID = :oldBizId
", { beaconId: beaconId, oldBizId: oldBusinessId }, { datasource: "payfrit" });
// Get current state
q = queryExecute("
SELECT
b.BeaconID,
b.BeaconUUID,
b.BeaconName,
lt.BusinessID,
lt.ServicePointID,
biz.BusinessName,
sp.ServicePointName
FROM Beacons b
LEFT JOIN lt_Beacon_Businesses_ServicePoints lt ON lt.BeaconID = b.BeaconID
LEFT JOIN Businesses biz ON biz.BusinessID = lt.BusinessID
LEFT JOIN ServicePoints sp ON sp.ServicePointID = lt.ServicePointID
WHERE b.BeaconIsActive = 1
ORDER BY b.BeaconID
SELECT
b.ID AS BeaconID,
b.UUID,
b.Name AS BeaconName,
b.BusinessID AS BeaconBusinessID,
sp.ID AS ServicePointID,
sp.Name AS ServicePointName,
sp.BusinessID AS ServicePointBusinessID,
biz.Name AS BusinessName
FROM Beacons b
LEFT JOIN ServicePoints sp ON sp.BeaconID = b.ID
LEFT JOIN Businesses biz ON biz.ID = b.BusinessID
WHERE b.IsActive = 1
ORDER BY b.ID
", {}, { datasource: "payfrit" });
rows = [];
for (row in q) {
arrayAppend(rows, {
"BeaconID": row.BeaconID,
"BeaconUUID": row.BeaconUUID,
"BeaconName": row.BeaconName ?: "",
"BusinessID": row.BusinessID ?: 0,
"BusinessName": row.BusinessName ?: "",
"ServicePointID": row.ServicePointID ?: 0,
"ServicePointName": row.ServicePointName ?: ""
});
arrayAppend(rows, {
"BeaconID": row.BeaconID,
"UUID": row.UUID,
"BeaconName": row.BeaconName,
"BeaconBusinessID": row.BeaconBusinessID,
"BusinessName": row.BusinessName,
"ServicePointID": row.ServicePointID ?: 0,
"ServicePointName": row.ServicePointName ?: ""
});
}
writeOutput(serializeJSON({
"OK": true,
"MESSAGE": "Updated beacon #beaconId# to BusinessID #newBusinessId#",
"BEACONS": rows
"OK": true,
"MESSAGE": "Updated beacon #beaconId# to BusinessID #newBusinessId#",
"BEACONS": rows
}));
</cfscript>

View file

@ -6,72 +6,68 @@
// Update Big Dean's business info
businessId = 27;
// Big Dean's actual address and hours
address = "1615 Ocean Front Walk, Santa Monica, CA 90401";
// Big Dean's actual info
phone = "(310) 393-2666";
hours = "Mon-Thu: 11am-10pm, Fri-Sat: 11am-11pm, Sun: 11am-10pm";
try {
// First get column names from INFORMATION_SCHEMA
cols = queryExecute("
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'payfrit' AND TABLE_NAME = 'Businesses'
ORDER BY ORDINAL_POSITION
");
colNames = [];
for (c in cols) {
arrayAppend(colNames, c.COLUMN_NAME);
}
// Check if we have the columns we need
hasAddress = arrayFindNoCase(colNames, "BusinessAddress") > 0;
hasPhone = arrayFindNoCase(colNames, "BusinessPhone") > 0;
hasHours = arrayFindNoCase(colNames, "BusinessHours") > 0;
// Add columns if missing
if (!hasAddress) {
queryExecute("ALTER TABLE Businesses ADD COLUMN BusinessAddress VARCHAR(255)");
}
if (!hasPhone) {
queryExecute("ALTER TABLE Businesses ADD COLUMN BusinessPhone VARCHAR(50)");
}
if (!hasHours) {
queryExecute("ALTER TABLE Businesses ADD COLUMN BusinessHours VARCHAR(255)");
}
// Update with new info
// Update phone and hours on Businesses table
queryExecute("
UPDATE Businesses
SET BusinessAddress = :address,
BusinessPhone = :phone,
BusinessHours = :hours
WHERE BusinessID = :bizId
SET Phone = :phone,
Hours = :hours
WHERE ID = :bizId
", {
address: address,
phone: phone,
hours: hours,
bizId: businessId
});
}, { datasource: "payfrit" });
// Update or insert address in Addresses table
qAddr = queryExecute("
SELECT ID FROM Addresses
WHERE BusinessID = :bizId AND IsDeleted = 0
LIMIT 1
", { bizId: businessId }, { datasource: "payfrit" });
if (qAddr.recordCount > 0) {
queryExecute("
UPDATE Addresses
SET Line1 = :line1, City = :city, ZIPCode = :zip
WHERE ID = :addrId
", {
line1: "1615 Ocean Front Walk",
city: "Santa Monica",
zip: "90401",
addrId: qAddr.ID
}, { datasource: "payfrit" });
} else {
queryExecute("
INSERT INTO Addresses (BusinessID, UserID, AddressTypeID, Line1, City, ZIPCode, AddedOn)
VALUES (:bizId, 0, 'business', :line1, :city, :zip, NOW())
", {
bizId: businessId,
line1: "1615 Ocean Front Walk",
city: "Santa Monica",
zip: "90401"
}, { datasource: "payfrit" });
}
// Get updated record
updated = queryExecute("
SELECT BusinessID, BusinessName, BusinessAddress, BusinessPhone, BusinessHours
SELECT ID, Name, Phone, Hours
FROM Businesses
WHERE BusinessID = :bizId
", { bizId: businessId });
WHERE ID = :bizId
", { bizId: businessId }, { datasource: "payfrit" });
writeOutput(serializeJSON({
"OK": true,
"MESSAGE": "Updated Big Dean's info",
"COLUMNS_EXISTED": { "address": hasAddress, "phone": hasPhone, "hours": hasHours },
"BUSINESS": {
"BusinessID": updated.BusinessID,
"BusinessName": updated.BusinessName,
"BusinessAddress": updated.BusinessAddress,
"BusinessPhone": updated.BusinessPhone,
"BusinessHours": updated.BusinessHours
"BusinessID": updated.ID,
"Name": updated.Name,
"Phone": updated.Phone,
"Hours": updated.Hours
}
}));

View file

@ -34,28 +34,20 @@ if (!structKeyExists(request,"BusinessID") || !isNumeric(request.BusinessID) ||
/* ---------- INPUT ---------- */
data = readJsonBody();
if (
!structKeyExists(data,"lt_Beacon_Businesses_ServicePointID")
|| !isNumeric(data.lt_Beacon_Businesses_ServicePointID)
|| int(data.lt_Beacon_Businesses_ServicePointID) LTE 0
){
apiAbort({OK=false,ERROR="missing_lt_Beacon_Businesses_ServicePointID"});
if (!structKeyExists(data,"ServicePointID") || !isNumeric(data.ServicePointID) || int(data.ServicePointID) LTE 0){
apiAbort({OK=false,ERROR="missing_ServicePointID"});
}
RelID = int(data.lt_Beacon_Businesses_ServicePointID);
ServicePointID = int(data.ServicePointID);
</cfscript>
<!--- Confirm the row exists for this BusinessID (and capture what it was) --->
<!--- Confirm the service point exists for this business and has a beacon assigned --->
<cfquery name="qFind" datasource="payfrit">
SELECT
lt_Beacon_Businesses_ServicePointID,
BeaconID,
ServicePointID
FROM lt_Beacon_Businesses_ServicePoints
WHERE lt_Beacon_Businesses_ServicePointID =
<cfqueryparam cfsqltype="cf_sql_integer" value="#RelID#">
AND BusinessID =
<cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
SELECT ID, BeaconID
FROM ServicePoints
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#ServicePointID#">
AND BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
AND BeaconID IS NOT NULL
LIMIT 1
</cfquery>
@ -63,28 +55,28 @@ RelID = int(data.lt_Beacon_Businesses_ServicePointID);
<cfoutput>#serializeJSON({
"OK"=false,
"ERROR"="not_found",
"lt_Beacon_Businesses_ServicePointID"=RelID,
"ServicePointID"=ServicePointID,
"BusinessID"=(request.BusinessID & "")
})#</cfoutput>
<cfabort>
</cfif>
<!--- Delete it --->
<cfset removedBeaconID = qFind.BeaconID>
<!--- Unassign beacon from service point --->
<cfquery datasource="payfrit">
DELETE FROM lt_Beacon_Businesses_ServicePoints
WHERE lt_Beacon_Businesses_ServicePointID =
<cfqueryparam cfsqltype="cf_sql_integer" value="#RelID#">
AND BusinessID =
<cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
LIMIT 1
UPDATE ServicePoints
SET BeaconID = NULL,
AssignedByUserID = NULL
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#ServicePointID#">
AND BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
</cfquery>
<cfoutput>#serializeJSON({
"OK"=true,
"ERROR"="",
"ACTION"="deleted",
"lt_Beacon_Businesses_ServicePointID"=RelID,
"BeaconID"=qFind.BeaconID,
"ServicePointID"=qFind.ServicePointID,
"ACTION"="unassigned",
"ServicePointID"=ServicePointID,
"BeaconID"=removedBeaconID,
"BusinessID"=(request.BusinessID & "")
})#</cfoutput>

View file

@ -17,32 +17,29 @@ if (!structKeyExists(request, "BusinessID") || !isNumeric(request.BusinessID) ||
<cfquery name="q" datasource="payfrit">
SELECT
lt.lt_Beacon_Businesses_ServicePointID,
lt.BeaconID,
lt.BusinessID,
lt.ServicePointID,
lt.lt_Beacon_Businesses_ServicePointNotes,
b.BeaconName,
b.BeaconUUID,
sp.ServicePointName
FROM lt_Beacon_Businesses_ServicePoints lt
JOIN Beacons b ON b.BeaconID = lt.BeaconID
LEFT JOIN ServicePoints sp ON sp.ServicePointID = lt.ServicePointID
WHERE lt.BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
ORDER BY b.BeaconName, sp.ServicePointName
sp.ID AS ServicePointID,
sp.BeaconID,
sp.BusinessID,
sp.AssignedByUserID,
b.Name AS BeaconName,
b.UUID,
sp.Name AS ServicePointName
FROM ServicePoints sp
JOIN Beacons b ON b.ID = sp.BeaconID
WHERE sp.BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
AND sp.BeaconID IS NOT NULL
ORDER BY b.Name, sp.Name
</cfquery>
<cfset assignments = []>
<cfloop query="q">
<cfset arrayAppend(assignments, {
"lt_Beacon_Businesses_ServicePointID" = q.lt_Beacon_Businesses_ServicePointID,
"ServicePointID" = q.ServicePointID,
"BeaconID" = q.BeaconID,
"BusinessID" = q.BusinessID,
"ServicePointID" = q.ServicePointID,
"BeaconName" = q.BeaconName,
"BeaconUUID" = q.BeaconUUID,
"ServicePointName"= q.ServicePointName,
"lt_Beacon_Businesses_ServicePointNotes" = q.lt_Beacon_Businesses_ServicePointNotes
"UUID" = q.UUID,
"ServicePointName"= q.ServicePointName
})>
</cfloop>

View file

@ -26,11 +26,6 @@ function readJsonBody(){
return parsed;
}
function normStr(v){
if (isNull(v)) return "";
return trim(toString(v));
}
/* ---------- AUTH CONTEXT ---------- */
if (!structKeyExists(request,"BusinessID") || !isNumeric(request.BusinessID) || request.BusinessID LTE 0){
apiAbort({OK=false,ERROR="no_business_selected"});
@ -48,43 +43,45 @@ if (!structKeyExists(data,"ServicePointID") || !isNumeric(data.ServicePointID) |
BeaconID = int(data.BeaconID);
ServicePointID = int(data.ServicePointID);
Notes = "";
if (structKeyExists(data,"Notes")){
Notes = left(normStr(data.Notes), 255);
}
</cfscript>
<!--- Validate Beacon belongs to Business OR to Business's parent --->
<!--- Validate Beacon is allowed for this Business:
Owner (Beacons.BusinessID) OR join table (lt_BeaconsID_BusinessesID) OR parent's owner --->
<cfquery name="qBiz" datasource="payfrit">
SELECT BusinessID, BusinessParentBusinessID
SELECT ID, ParentBusinessID
FROM Businesses
WHERE BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
LIMIT 1
</cfquery>
<cfquery name="qB" datasource="payfrit">
SELECT BeaconID
FROM Beacons
WHERE BeaconID = <cfqueryparam cfsqltype="cf_sql_integer" value="#BeaconID#">
SELECT b.ID
FROM Beacons b
WHERE b.ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#BeaconID#">
AND (
BeaconBusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
<cfif qBiz.recordCount GT 0 AND len(trim(qBiz.BusinessParentBusinessID)) GT 0 AND isNumeric(qBiz.BusinessParentBusinessID) AND qBiz.BusinessParentBusinessID GT 0>
OR BeaconBusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#qBiz.BusinessParentBusinessID#">
b.BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
<cfif qBiz.recordCount GT 0 AND len(trim(qBiz.ParentBusinessID)) GT 0 AND isNumeric(qBiz.ParentBusinessID) AND qBiz.ParentBusinessID GT 0>
OR b.BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#qBiz.ParentBusinessID#">
</cfif>
OR EXISTS (
SELECT 1 FROM lt_BeaconsID_BusinessesID lt
WHERE lt.BeaconID = b.ID
AND lt.BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
)
)
LIMIT 1
</cfquery>
<cfif qB.recordCount EQ 0>
<cfoutput>#serializeJSON({OK=false,ERROR="beacon_not_found_for_business"})#</cfoutput>
<cfoutput>#serializeJSON({OK=false,ERROR="beacon_not_allowed"})#</cfoutput>
<cfabort>
</cfif>
<!--- Validate ServicePoint belongs to Business --->
<cfquery name="qS" datasource="payfrit">
SELECT ServicePointID
SELECT ID
FROM ServicePoints
WHERE ServicePointID = <cfqueryparam cfsqltype="cf_sql_integer" value="#ServicePointID#">
AND ServicePointBusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#ServicePointID#">
AND BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
LIMIT 1
</cfquery>
<cfif qS.recordCount EQ 0>
@ -92,14 +89,12 @@ if (structKeyExists(data,"Notes")){
<cfabort>
</cfif>
<!--- Check if THIS BUSINESS already has this exact beacon+servicepoint combo --->
<!--- (Multiple businesses CAN share the same beacon, but one business shouldn't duplicate) --->
<!--- Check if this ServicePoint already has this beacon assigned --->
<cfquery name="qDuplicate" datasource="payfrit">
SELECT lt_Beacon_Businesses_ServicePointID
FROM lt_Beacon_Businesses_ServicePoints
WHERE BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
SELECT ID
FROM ServicePoints
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#ServicePointID#">
AND BeaconID = <cfqueryparam cfsqltype="cf_sql_integer" value="#BeaconID#">
AND ServicePointID = <cfqueryparam cfsqltype="cf_sql_integer" value="#ServicePointID#">
LIMIT 1
</cfquery>
<cfif qDuplicate.recordCount GT 0>
@ -107,31 +102,19 @@ if (structKeyExists(data,"Notes")){
<cfabort>
</cfif>
<!--- INSERT --->
<!--- Assign beacon to service point --->
<cfquery datasource="payfrit">
INSERT INTO lt_Beacon_Businesses_ServicePoints
(BusinessID, BeaconID, ServicePointID,
lt_Beacon_Businesses_ServicePointAssignedByUserID,
lt_Beacon_Businesses_ServicePointNotes)
VALUES
(
<cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">,
<cfqueryparam cfsqltype="cf_sql_integer" value="#BeaconID#">,
<cfqueryparam cfsqltype="cf_sql_integer" value="#ServicePointID#">,
<cfqueryparam cfsqltype="cf_sql_integer" value="1">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#Notes#" null="#(len(Notes) EQ 0)#">
)
</cfquery>
<cfquery name="qID" datasource="payfrit">
SELECT LAST_INSERT_ID() AS NewID
UPDATE ServicePoints
SET BeaconID = <cfqueryparam cfsqltype="cf_sql_integer" value="#BeaconID#">,
AssignedByUserID = <cfqueryparam cfsqltype="cf_sql_integer" value="1">
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#ServicePointID#">
AND BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
</cfquery>
<cfoutput>#serializeJSON({
"OK"=true,
"ACTION"="inserted",
"lt_Beacon_Businesses_ServicePointID"=qID.NewID,
"BeaconID"=BeaconID,
"ACTION"="assigned",
"ServicePointID"=ServicePointID,
"BeaconID"=BeaconID,
"BusinessID"=(request.BusinessID & "")
})#</cfoutput>

View file

@ -71,9 +71,9 @@ try {
// Check if email is already used by another verified account
qEmailCheck = queryExecute("
SELECT UserID FROM Users
WHERE UserEmailAddress = :email
AND UserIsEmailVerified = 1
SELECT ID FROM Users
WHERE EmailAddress = :email
AND IsEmailVerified = 1
AND UserID != :userId
LIMIT 1
", {
@ -87,19 +87,19 @@ try {
// Get current user UUID for email confirmation link
qUser = queryExecute("
SELECT UserUUID FROM Users WHERE UserID = :userId
SELECT UUID FROM Users WHERE ID = :userId
", { userId: { value: userId, cfsqltype: "cf_sql_integer" } }, { datasource: "payfrit" });
// Update user profile AND mark account as verified/active
// This completes the signup process
queryExecute("
UPDATE Users
SET UserFirstName = :firstName,
UserLastName = :lastName,
UserEmailAddress = :email,
UserIsEmailVerified = 0,
UserIsContactVerified = 1,
UserIsActive = 1
SET FirstName = :firstName,
LastName = :lastName,
EmailAddress = :email,
IsEmailVerified = 0,
IsContactVerified = 1,
IsActive = 1
WHERE UserID = :userId
", {
firstName: { value: firstName, cfsqltype: "cf_sql_varchar" },
@ -109,7 +109,7 @@ try {
}, { datasource: "payfrit" });
// Send confirmation email (non-blocking - don't fail if mail fails)
confirmLink = "https://biz.payfrit.com/confirm_email.cfm?UUID=" & qUser.UserUUID;
confirmLink = "https://biz.payfrit.com/confirm_email.cfm?UUID=" & qUser.UUID;
emailBody = "
<p>Welcome to Payfrit, #firstName#!</p>
<p>Please click the link below to confirm your email address:</p>

View file

@ -12,7 +12,7 @@
{ "username": "...", "password": "..." }
OUTPUT (JSON):
{ OK:true, ERROR:"", UserID:123, UserFirstName:"...", Token:"..." }
{ OK:true, ERROR:"", UserID:123, FirstName:"...", Token:"..." }
Uses existing UserTokens table:
TokenID (auto), UserID, Token, CreatedAt (DEFAULT CURRENT_TIMESTAMP)
@ -55,16 +55,16 @@ if (!len(username) || !len(password)) {
try {
q = queryExecute(
"
SELECT UserID, UserFirstName
SELECT ID, FirstName
FROM Users
WHERE
(
(UserEmailAddress = ?) OR
(UserContactNumber = ?)
(EmailAddress = ?) OR
(ContactNumber = ?)
)
AND UserPassword = ?
AND UserIsEmailVerified = 1
AND UserIsContactVerified > 0
AND Password = ?
AND IsEmailVerified = 1
AND IsContactVerified > 0
LIMIT 1
",
[
@ -84,7 +84,7 @@ try {
queryExecute(
"INSERT INTO UserTokens (UserID, Token) VALUES (?, ?)",
[
{ value = q.UserID, cfsqltype = "cf_sql_integer" },
{ value = q.ID, cfsqltype = "cf_sql_integer" },
{ value = token, cfsqltype = "cf_sql_varchar" }
],
{ datasource = "payfrit" }
@ -92,15 +92,15 @@ try {
// Optional: also set session for browser tools
lock timeout="15" throwontimeout="yes" type="exclusive" scope="session" {
session.UserID = q.UserID;
session.UserID = q.ID;
}
request.UserID = q.UserID;
request.UserID = q.ID;
writeOutput(serializeJSON({
"OK": true,
"ERROR": "",
"UserID": q.UserID,
"UserFirstName": q.UserFirstName,
"UserID": q.ID,
"FirstName": q.FirstName,
"Token": token
}));
abort;

View file

@ -53,10 +53,10 @@ try {
// Find verified account with this phone
qUser = queryExecute("
SELECT UserID, UserUUID
SELECT ID, UUID
FROM Users
WHERE UserContactNumber = :phone
AND UserIsContactVerified = 1
WHERE ContactNumber = :phone
AND IsContactVerified = 1
LIMIT 1
", { phone: { value: phone, cfsqltype: "cf_sql_varchar" } }, { datasource: "payfrit" });
@ -65,14 +65,14 @@ try {
}
// If user has no UUID (legacy account), generate one
userUUID = qUser.UserUUID;
userUUID = qUser.UUID;
if (!len(trim(userUUID))) {
userUUID = replace(createUUID(), "-", "", "all");
queryExecute("
UPDATE Users SET UserUUID = :uuid WHERE UserID = :userId
UPDATE Users SET UUID = :uuid WHERE UserID = :userId
", {
uuid: { value: userUUID, cfsqltype: "cf_sql_varchar" },
userId: { value: qUser.UserID, cfsqltype: "cf_sql_integer" }
userId: { value: qUser.ID, cfsqltype: "cf_sql_integer" }
}, { datasource: "payfrit" });
}
@ -80,11 +80,11 @@ try {
otp = generateOTP();
queryExecute("
UPDATE Users
SET UserMobileVerifyCode = :otp
SET MobileVerifyCode = :otp
WHERE UserID = :userId
", {
otp: { value: otp, cfsqltype: "cf_sql_varchar" },
userId: { value: qUser.UserID, cfsqltype: "cf_sql_integer" }
userId: { value: qUser.ID, cfsqltype: "cf_sql_integer" }
}, { datasource: "payfrit" });
// Send OTP via Twilio (skip on dev server)

Some files were not shown because too many files have changed in this diff Show more