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

View file

@ -116,13 +116,13 @@
<cfset cart_total = 0> <cfset cart_total = 0>
<CFQUERY name="get_queued_food" datasource="#application.datasource#" dbtype="ODBC"> <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 FROM dbo.Business_CartMaster A, dbo.BusinessMaster B, dbo.Business_ItemMaster C, Users D
WHERE A.UserID = D.UserID WHERE A.UserID = D.UserID
AND AND
A.ItemID = C.ItemID A.ItemID = C.ItemID
AND AND
B.BusinessID = C.BusinessID B.ID = C.BusinessID
AND AND
C.BusinessID = #form.bizid# C.BusinessID = #form.bizid#
AND AND
@ -170,11 +170,11 @@
</CFQUERY> </CFQUERY>
<CFQUERY name="get_last_inserted" datasource="#application.datasource#" dbtype="ODBC"> <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 FROM dbo.Business_OrderMaster O, dbo.BusinessMaster M, Users U
WHERE O.BusinessID = M.BusinessID WHERE O.BusinessID = M.BusinessID
AND AND
M.UserID = U.UserID M.UserID = U.ID
ORDER BY O.AddedOn DESC ORDER BY O.AddedOn DESC
</CFQUERY> </CFQUERY>
@ -185,7 +185,7 @@
) )
VALUES VALUES
( (
#get_last_inserted.OrderID#, #get_last_inserted.ID#,
#get_queued_food.CartID# #get_queued_food.CartID#
) )
</CFQUERY> </CFQUERY>
@ -268,7 +268,7 @@
<CFQUERY name="get_user_104_balance" datasource="#application.datasource#" dbtype="ODBC"> <CFQUERY name="get_user_104_balance" datasource="#application.datasource#" dbtype="ODBC">
SELECT balance SELECT balance
FROM Users FROM Users
WHERE UserID = 104 WHERE ID = 104
</CFQUERY> </CFQUERY>
<CFQUERY name="transfer_fees_to_UserID_104" datasource="#application.datasource#" dbtype="ODBC"> <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"> <CFQUERY name="get_user_104_balance" datasource="#application.datasource#" dbtype="ODBC">
SELECT balance SELECT balance
FROM Users FROM Users
WHERE UserID = 104 WHERE ID = 104
</CFQUERY> </CFQUERY>
<CFQUERY name="transfer_fees_to_UserID_104" datasource="#application.datasource#" dbtype="ODBC"> <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"); const tr = document.createElement("tr");
tr.innerHTML = ` tr.innerHTML = `
<td>${a.lt_Beacon_Businesses_ServicePointID}</td> <td>${a.lt_Beacon_Businesses_ServicePointID}</td>
<td>${escapeHtml((a.BeaconName || "") + " (ID " + a.BeaconID + ")")}</td> <td>${escapeHtml((a.Name || "") + " (ID " + a.BeaconID + ")")}</td>
<td>${escapeHtml((a.ServicePointName || "") + " (ID " + a.ServicePointID + ")")}</td> <td>${escapeHtml((a.Name || "") + " (ID " + a.ServicePointID + ")")}</td>
<td>${escapeHtml(a.lt_Beacon_Businesses_ServicePointNotes || "")}</td> <td>${escapeHtml(a.lt_Beacon_Businesses_ServicePointNotes || "")}</td>
<td>${escapeHtml(a.CreatedAt || "")}</td> <td>${escapeHtml(a.CreatedAt || "")}</td>
`; `;
@ -183,12 +183,12 @@ async function refreshBeacons(assignedBeaconIDs, keepSelectedBeaconID){
setSelectPlaceholder(sel, "-- Select Beacon --"); setSelectPlaceholder(sel, "-- Select Beacon --");
(out.BEACONS || []).forEach(b => { (out.BEACONS || []).forEach(b => {
const isAssigned = assignedBeaconIDs.has(String(b.BeaconID)); const isAssigned = assignedBeaconIDs.has(String(b.ID));
if (HIDE_ASSIGNED_BEACONS && isAssigned) return; if (HIDE_ASSIGNED_BEACONS && isAssigned) return;
const opt = document.createElement("option"); const opt = document.createElement("option");
opt.value = b.BeaconID; opt.value = b.ID;
opt.textContent = String(b.BeaconID) + " - " + (b.BeaconName || ""); opt.textContent = String(b.ID) + " - " + (b.Name || "");
sel.appendChild(opt); sel.appendChild(opt);
}); });
@ -203,12 +203,12 @@ async function refreshServicePoints(assignedServicePointIDs, keepSelectedService
setSelectPlaceholder(sel, "-- Select ServicePoint --"); setSelectPlaceholder(sel, "-- Select ServicePoint --");
(out.SERVICEPOINTS || []).forEach(sp => { (out.SERVICEPOINTS || []).forEach(sp => {
const isAssigned = assignedServicePointIDs.has(String(sp.ServicePointID)); const isAssigned = assignedServicePointIDs.has(String(sp.ID));
if (HIDE_ASSIGNED_SERVICEPOINTS && isAssigned) return; if (HIDE_ASSIGNED_SERVICEPOINTS && isAssigned) return;
const opt = document.createElement("option"); const opt = document.createElement("option");
opt.value = sp.ServicePointID; opt.value = sp.ID;
opt.textContent = String(sp.ServicePointID) + " - " + (sp.ServicePointName || ""); opt.textContent = String(sp.ID) + " - " + (sp.Name || "");
sel.appendChild(opt); sel.appendChild(opt);
}); });

View file

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

View file

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

View file

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

View file

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

View file

@ -1,11 +1,6 @@
<cfsetting showdebugoutput="false"> <cfsetting showdebugoutput="false">
<cfsetting enablecfoutputonly="true"> <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) Payfrit API Application.cfm (updated 2026-01-22)
@ -178,6 +173,11 @@ if (len(request._api_path)) {
// Worker app endpoints // Worker app endpoints
if (findNoCase("/api/workers/myBusinesses.cfm", request._api_path)) request._api_isPublic = true; 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 // Portal endpoints
if (findNoCase("/api/portal/stats.cfm", request._api_path)) request._api_isPublic = true; if (findNoCase("/api/portal/stats.cfm", request._api_path)) request._api_isPublic = true;

View file

@ -58,8 +58,8 @@ try {
queryExecute(" queryExecute("
UPDATE Addresses UPDATE Addresses
SET AddressIsDefaultDelivery = 0 SET AddressIsDefaultDelivery = 0
WHERE AddressUserID = :userId WHERE UserID = :userId
AND (AddressBusinessID = 0 OR AddressBusinessID IS NULL) AND (BusinessID = 0 OR BusinessID IS NULL)
AND AddressTypeID = :typeId AND AddressTypeID = :typeId
", { ", {
userId: { value: userId, cfsqltype: "cf_sql_integer" }, userId: { value: userId, cfsqltype: "cf_sql_integer" },
@ -75,18 +75,18 @@ try {
queryExecute(" queryExecute("
INSERT INTO Addresses ( INSERT INTO Addresses (
AddressID, AddressID,
AddressUserID, UserID,
AddressBusinessID, BusinessID,
AddressTypeID, AddressTypeID,
AddressLabel, AddressLabel,
AddressIsDefaultDelivery, AddressIsDefaultDelivery,
AddressLine1, Line1,
AddressLine2, Line2,
AddressCity, City,
AddressStateID, StateID,
AddressZIPCode, ZIPCode,
AddressIsDeleted, IsDeleted,
AddressAddedOn AddedOn
) VALUES ( ) VALUES (
:addressId, :addressId,
:userId, :userId,
@ -117,7 +117,7 @@ try {
}, { datasource: "payfrit" }); }, { datasource: "payfrit" });
// Get state info for response // 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" } stateId: { value: stateId, cfsqltype: "cf_sql_integer" }
}, { datasource: "payfrit" }); }, { datasource: "payfrit" });

View file

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

View file

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

View file

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

View file

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

View file

@ -8,9 +8,9 @@
* Add Schedule Fields to Categories Table * Add Schedule Fields to Categories Table
* *
* Adds time-based scheduling fields: * Adds time-based scheduling fields:
* - CategoryScheduleStart: TIME - Start time when category is available (e.g., 06:00:00 for breakfast) * - ScheduleStart: 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) * - ScheduleEnd: 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 * - 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. * Run this once to migrate the schema.
*/ */
@ -24,38 +24,38 @@ try {
FROM INFORMATION_SCHEMA.COLUMNS FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'payfrit' WHERE TABLE_SCHEMA = 'payfrit'
AND TABLE_NAME = 'Categories' AND TABLE_NAME = 'Categories'
AND COLUMN_NAME IN ('CategoryScheduleStart', 'CategoryScheduleEnd', 'CategoryScheduleDays') AND COLUMN_NAME IN ('ScheduleStart', 'ScheduleEnd', 'ScheduleDays')
", {}, { datasource: "payfrit" }); ", {}, { datasource: "payfrit" });
existingCols = valueList(qCheck.COLUMN_NAME); existingCols = valueList(qCheck.COLUMN_NAME);
added = []; added = [];
// Add CategoryScheduleStart if not exists // Add ScheduleStart if not exists
if (!listFindNoCase(existingCols, "CategoryScheduleStart")) { if (!listFindNoCase(existingCols, "ScheduleStart")) {
queryExecute(" queryExecute("
ALTER TABLE Categories ALTER TABLE Categories
ADD COLUMN CategoryScheduleStart TIME NULL ADD COLUMN ScheduleStart TIME NULL
", {}, { datasource: "payfrit" }); ", {}, { datasource: "payfrit" });
arrayAppend(added, "CategoryScheduleStart"); arrayAppend(added, "ScheduleStart");
} }
// Add CategoryScheduleEnd if not exists // Add ScheduleEnd if not exists
if (!listFindNoCase(existingCols, "CategoryScheduleEnd")) { if (!listFindNoCase(existingCols, "ScheduleEnd")) {
queryExecute(" queryExecute("
ALTER TABLE Categories ALTER TABLE Categories
ADD COLUMN CategoryScheduleEnd TIME NULL ADD COLUMN ScheduleEnd TIME NULL
", {}, { datasource: "payfrit" }); ", {}, { datasource: "payfrit" });
arrayAppend(added, "CategoryScheduleEnd"); arrayAppend(added, "ScheduleEnd");
} }
// Add CategoryScheduleDays if not exists // Add ScheduleDays if not exists
if (!listFindNoCase(existingCols, "CategoryScheduleDays")) { if (!listFindNoCase(existingCols, "ScheduleDays")) {
queryExecute(" queryExecute("
ALTER TABLE Categories ALTER TABLE Categories
ADD COLUMN CategoryScheduleDays VARCHAR(20) NULL ADD COLUMN ScheduleDays VARCHAR(20) NULL
", {}, { datasource: "payfrit" }); ", {}, { datasource: "payfrit" });
arrayAppend(added, "CategoryScheduleDays"); arrayAppend(added, "ScheduleDays");
} }
response["OK"] = true; response["OK"] = true;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -13,10 +13,10 @@
* Cleanup Categories - Final step after migration verification * Cleanup Categories - Final step after migration verification
* *
* This script: * 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) * 2. Finds orphan items (ParentID=0, no children, not in links)
* 3. Drops ItemCategoryID column * 3. Drops CategoryID column
* 4. Drops ItemIsModifierTemplate column (derived from ItemTemplateLinks now) * 4. Drops IsModifierTemplate column (derived from lt_ItemID_TemplateItemID now)
* 5. Drops Categories table * 5. Drops Categories table
* *
* Query param: ?confirm=YES to actually execute (otherwise shows verification only) * Query param: ?confirm=YES to actually execute (otherwise shows verification only)
@ -30,7 +30,7 @@ try {
// Verification Step 1: Check for items without BusinessID // Verification Step 1: Check for items without BusinessID
qNoBusinessID = queryExecute(" qNoBusinessID = queryExecute("
SELECT COUNT(*) as cnt FROM Items SELECT COUNT(*) as cnt FROM Items
WHERE ItemBusinessID IS NULL OR ItemBusinessID = 0 WHERE BusinessID IS NULL OR BusinessID = 0
", {}, { datasource: "payfrit" }); ", {}, { datasource: "payfrit" });
response.verification["itemsWithoutBusinessID"] = qNoBusinessID.cnt; response.verification["itemsWithoutBusinessID"] = qNoBusinessID.cnt;
@ -46,38 +46,38 @@ try {
qCategoryItems = queryExecute(" qCategoryItems = queryExecute("
SELECT COUNT(DISTINCT p.ItemID) as cnt SELECT COUNT(DISTINCT p.ItemID) as cnt
FROM Items p FROM Items p
INNER JOIN Items c ON c.ItemParentItemID = p.ItemID INNER JOIN Items c ON c.ParentItemID = p.ItemID
WHERE p.ItemParentItemID = 0 WHERE p.ParentItemID = 0
AND p.ItemBusinessID > 0 AND p.BusinessID > 0
AND NOT EXISTS ( 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" }); ", {}, { datasource: "payfrit" });
response.verification["categoryItemsCreated"] = qCategoryItems.cnt; 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(" qTemplates = queryExecute("
SELECT COUNT(DISTINCT tl.TemplateItemID) as cnt SELECT COUNT(DISTINCT tl.TemplateItemID) as cnt
FROM ItemTemplateLinks tl FROM lt_ItemID_TemplateItemID tl
INNER JOIN Items t ON t.ItemID = tl.TemplateItemID INNER JOIN Items t ON t.ItemID = tl.TemplateItemID
", {}, { datasource: "payfrit" }); ", {}, { datasource: "payfrit" });
response.verification["templatesInLinks"] = qTemplates.cnt; response.verification["templatesInLinks"] = qTemplates.cnt;
// Verification Step 5: Find orphans at ParentID=0 // 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(" qOrphans = queryExecute("
SELECT i.ItemID, i.ItemName, i.ItemBusinessID SELECT i.ID, i.Name, i.BusinessID
FROM Items i FROM Items i
WHERE i.ItemParentItemID = 0 WHERE i.ParentItemID = 0
AND NOT EXISTS ( 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 ( 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" }); ", {}, { datasource: "payfrit" });
response.verification["orphanCount"] = qOrphans.recordCount; response.verification["orphanCount"] = qOrphans.recordCount;
@ -85,8 +85,8 @@ try {
for (orphan in qOrphans) { for (orphan in qOrphans) {
arrayAppend(response.orphans, { arrayAppend(response.orphans, {
"ItemID": orphan.ItemID, "ItemID": orphan.ItemID,
"ItemName": orphan.ItemName, "Name": orphan.Name,
"BusinessID": orphan.ItemBusinessID "BusinessID": orphan.BusinessID
}); });
} }
@ -96,7 +96,7 @@ try {
if (!safeToCleanup) { if (!safeToCleanup) {
arrayAppend(response.steps, "VERIFICATION FAILED - Cannot cleanup yet"); 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; response["OK"] = false;
writeOutput(serializeJSON(response)); writeOutput(serializeJSON(response));
abort; abort;
@ -119,31 +119,31 @@ try {
// Execute cleanup // Execute cleanup
arrayAppend(response.steps, "Executing cleanup..."); arrayAppend(response.steps, "Executing cleanup...");
// Step 1: Drop ItemCategoryID column // Step 1: Drop CategoryID column
try { try {
queryExecute(" queryExecute("
ALTER TABLE Items DROP COLUMN ItemCategoryID ALTER TABLE Items DROP COLUMN CategoryID
", {}, { datasource: "payfrit" }); ", {}, { datasource: "payfrit" });
arrayAppend(response.steps, "Dropped ItemCategoryID column from Items"); arrayAppend(response.steps, "Dropped CategoryID column from Items");
} catch (any e) { } catch (any e) {
if (findNoCase("check that column", e.message) || findNoCase("Unknown column", e.message)) { 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 { } 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 { try {
queryExecute(" queryExecute("
ALTER TABLE Items DROP COLUMN ItemIsModifierTemplate ALTER TABLE Items DROP COLUMN IsModifierTemplate
", {}, { datasource: "payfrit" }); ", {}, { datasource: "payfrit" });
arrayAppend(response.steps, "Dropped ItemIsModifierTemplate column from Items"); arrayAppend(response.steps, "Dropped IsModifierTemplate column from Items");
} catch (any e) { } catch (any e) {
if (findNoCase("check that column", e.message) || findNoCase("Unknown column", e.message)) { 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 { } 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 { try {
// Find duplicate UserIDs for this business (keep the one with highest status or oldest) // Find duplicate UserIDs for this business (keep the one with highest status or oldest)
qDupes = queryExecute(" qDupes = queryExecute("
SELECT UserID, COUNT(*) as cnt, MIN(EmployeeID) as keepId SELECT ID, COUNT(*) as cnt, MIN(ID) as keepId
FROM lt_Users_Businesses_Employees FROM Employees
WHERE BusinessID = ? WHERE BusinessID = ?
GROUP BY UserID GROUP BY UserID
HAVING COUNT(*) > 1 HAVING COUNT(*) > 1
@ -45,8 +45,8 @@ try {
for (row in qDupes) { for (row in qDupes) {
// Delete all but the one we want to keep (the one with lowest EmployeeID) // Delete all but the one we want to keep (the one with lowest EmployeeID)
qDel = queryExecute(" qDel = queryExecute("
DELETE FROM lt_Users_Businesses_Employees DELETE FROM Employees
WHERE BusinessID = ? AND UserID = ? AND EmployeeID != ? WHERE BusinessID = ? AND UserID = ? AND ID != ?
", [ ", [
{ value: businessId, cfsqltype: "cf_sql_integer" }, { value: businessId, cfsqltype: "cf_sql_integer" },
{ value: row.UserID, cfsqltype: "cf_sql_integer" }, { value: row.UserID, cfsqltype: "cf_sql_integer" },
@ -57,19 +57,19 @@ try {
// Get remaining employees // Get remaining employees
qRemaining = queryExecute(" qRemaining = queryExecute("
SELECT e.EmployeeID, e.UserID, u.UserFirstName, u.UserLastName SELECT e.ID, e.UserID, u.FirstName, u.LastName
FROM lt_Users_Businesses_Employees e FROM Employees e
JOIN Users u ON e.UserID = u.UserID JOIN Users u ON e.UserID = u.ID
WHERE e.BusinessID = ? WHERE e.BusinessID = ?
ORDER BY e.EmployeeID ORDER BY e.ID
", [{ value: businessId, cfsqltype: "cf_sql_integer" }], { datasource: "payfrit" }); ", [{ value: businessId, cfsqltype: "cf_sql_integer" }], { datasource: "payfrit" });
remaining = []; remaining = [];
for (r in qRemaining) { for (r in qRemaining) {
arrayAppend(remaining, { arrayAppend(remaining, {
"EmployeeID": r.EmployeeID, "EmployeeID": r.ID,
"UserID": r.UserID, "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) // Keep only Lazy Daisy (BusinessID 37)
keepBusinessID = 37; keepBusinessID = 37;
// First, reassign all beacons to Lazy Daisy // Unassign all beacons from service points of other businesses
queryExecute(" queryExecute("
UPDATE lt_Beacon_Businesses_ServicePoints UPDATE ServicePoints
SET BusinessID = :keepID SET BeaconID = NULL, AssignedByUserID = NULL
WHERE BusinessID != :keepID AND BeaconID IS NOT NULL
", { keepID: keepBusinessID }, { datasource: "payfrit" }); ", { 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 // Get list of businesses to delete
qBiz = queryExecute(" qBiz = queryExecute("
SELECT BusinessID, BusinessName FROM Businesses WHERE BusinessID != :keepID SELECT ID, Name FROM Businesses WHERE ID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" }); ", { keepID: keepBusinessID }, { datasource: "payfrit" });
deletedBusinesses = []; deletedBusinesses = [];
for (i = 1; i <= qBiz.recordCount; i++) { 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"); response.steps.append("Found " & qBiz.recordCount & " businesses to delete");
// Delete related data first (foreign key constraints) // Delete related data first (foreign key constraints)
// Delete ItemTemplateLinks for items from other businesses // Delete lt_ItemID_TemplateItemID for items from other businesses
queryExecute(" queryExecute("
DELETE itl FROM ItemTemplateLinks itl DELETE itl FROM lt_ItemID_TemplateItemID itl
JOIN Items i ON i.ItemID = itl.ItemID JOIN Items i ON i.ID = itl.ItemID
WHERE i.ItemBusinessID != :keepID WHERE i.BusinessID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" }); ", { 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 // Delete Items for other businesses
qItems = queryExecute(" qItems = queryExecute("
SELECT COUNT(*) as cnt FROM Items WHERE ItemBusinessID != :keepID SELECT COUNT(*) as cnt FROM Items WHERE BusinessID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" }); ", { keepID: keepBusinessID }, { datasource: "payfrit" });
queryExecute(" queryExecute("
DELETE FROM Items WHERE ItemBusinessID != :keepID DELETE FROM Items WHERE BusinessID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" }); ", { keepID: keepBusinessID }, { datasource: "payfrit" });
response.steps.append("Deleted " & qItems.cnt & " items from other businesses"); response.steps.append("Deleted " & qItems.cnt & " items from other businesses");
// Delete Categories for other businesses // Delete Categories for other businesses
queryExecute(" queryExecute("
DELETE FROM Categories WHERE CategoryBusinessID != :keepID DELETE FROM Categories WHERE BusinessID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" }); ", { keepID: keepBusinessID }, { datasource: "payfrit" });
response.steps.append("Deleted categories from other businesses"); response.steps.append("Deleted categories from other businesses");
// Delete Hours for other businesses // Delete Hours for other businesses
queryExecute(" queryExecute("
DELETE FROM Hours WHERE HoursBusinessID != :keepID DELETE FROM Hours WHERE BusinessID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" }); ", { keepID: keepBusinessID }, { datasource: "payfrit" });
response.steps.append("Deleted hours from other businesses"); 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 { try {
queryExecute(" queryExecute("
DELETE FROM Employees WHERE EmployeeBusinessID != :keepID DELETE FROM Employees WHERE BusinessID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" }); ", { keepID: keepBusinessID }, { datasource: "payfrit" });
response.steps.append("Deleted employees from other businesses"); response.steps.append("Deleted employees from other businesses");
} catch (any e) { } catch (any e) {
response.steps.append("Skipped employees (table may not exist)"); 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 { try {
queryExecute(" queryExecute("
DELETE FROM ServicePoints WHERE ServicePointBusinessID != :keepID DELETE FROM ServicePoints WHERE BusinessID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" }); ", { keepID: keepBusinessID }, { datasource: "payfrit" });
response.steps.append("Deleted service points from other businesses"); response.steps.append("Deleted service points from other businesses");
} catch (any e) { } catch (any e) {
response.steps.append("Skipped service points (table may not exist)"); 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 { try {
queryExecute(" queryExecute("
DELETE FROM Stations WHERE StationBusinessID != :keepID DELETE FROM Stations WHERE BusinessID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" }); ", { keepID: keepBusinessID }, { datasource: "payfrit" });
response.steps.append("Deleted stations from other businesses"); response.steps.append("Deleted stations from other businesses");
} catch (any e) { } catch (any e) {
response.steps.append("Skipped stations (table may not exist)"); 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 // Finally delete the businesses themselves
queryExecute(" queryExecute("
DELETE FROM Businesses WHERE BusinessID != :keepID DELETE FROM Businesses WHERE ID != :keepID
", { keepID: keepBusinessID }, { datasource: "payfrit" }); ", { keepID: keepBusinessID }, { datasource: "payfrit" });
response.steps.append("Deleted " & arrayLen(deletedBusinesses) & " businesses"); response.steps.append("Deleted " & arrayLen(deletedBusinesses) & " businesses");
// Get beacon status // Get beacon status
qBeacons = queryExecute(" qBeacons = queryExecute("
SELECT lt.BeaconID, b.BeaconUUID, lt.BusinessID, biz.BusinessName, lt.ServicePointID SELECT sp.ID AS ServicePointID, sp.BeaconID, sp.BusinessID,
FROM lt_Beacon_Businesses_ServicePoints lt b.UUID, biz.Name AS BusinessName, sp.Name AS ServicePointName
JOIN Beacons b ON b.BeaconID = lt.BeaconID FROM ServicePoints sp
JOIN Businesses biz ON biz.BusinessID = lt.BusinessID JOIN Beacons b ON b.ID = sp.BeaconID
JOIN Businesses biz ON biz.ID = sp.BusinessID
WHERE sp.BeaconID IS NOT NULL
", {}, { datasource: "payfrit" }); ", {}, { datasource: "payfrit" });
beacons = []; beacons = [];
for (i = 1; i <= qBeacons.recordCount; i++) { for (i = 1; i <= qBeacons.recordCount; i++) {
arrayAppend(beacons, { arrayAppend(beacons, {
"BeaconID": qBeacons.BeaconID[i], "BeaconID": qBeacons.BeaconID[i],
"UUID": qBeacons.BeaconUUID[i], "UUID": qBeacons.UUID[i],
"BusinessID": qBeacons.BusinessID[i], "BusinessID": qBeacons.BusinessID[i],
"BusinessName": qBeacons.BusinessName[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 // Check the item first
qItem = queryExecute(" qItem = queryExecute("
SELECT ItemID, ItemName, ItemParentItemID, ItemIsActive, ItemIsCollapsible SELECT ID, Name, ParentItemID, IsActive, IsCollapsible
FROM Items FROM Items
WHERE ItemID = :itemId WHERE ID = :itemId
", { itemId: url.itemId }); ", { itemId: url.itemId });
if (qItem.recordCount == 0) { if (qItem.recordCount == 0) {
@ -19,25 +19,25 @@ if (qItem.recordCount == 0) {
// Get all children (direct only for display) // Get all children (direct only for display)
qChildren = queryExecute(" qChildren = queryExecute("
SELECT ItemID, ItemName SELECT ID, Name
FROM Items FROM Items
WHERE ItemParentItemID = :itemId WHERE ParentItemID = :itemId
", { itemId: url.itemId }); ", { itemId: url.itemId });
childList = []; childList = [];
for (row in qChildren) { for (row in qChildren) {
arrayAppend(childList, { "ItemID": row.ItemID, "ItemName": row.ItemName }); arrayAppend(childList, { "ItemID": row.ID, "Name": row.Name });
} }
result = { result = {
"OK": true, "OK": true,
"ACTION": url.action, "ACTION": url.action,
"ITEM": { "ITEM": {
"ItemID": qItem.ItemID, "ItemID": qItem.ID,
"ItemName": qItem.ItemName, "Name": qItem.Name,
"ItemParentItemID": qItem.ItemParentItemID, "ParentItemID": qItem.ParentItemID,
"ItemIsActive": qItem.ItemIsActive, "IsActive": qItem.IsActive,
"ItemIsCollapsible": qItem.ItemIsCollapsible "IsCollapsible": qItem.IsCollapsible
}, },
"HAS_CHILDREN": qChildren.recordCount > 0, "HAS_CHILDREN": qChildren.recordCount > 0,
"CHILD_COUNT": qChildren.recordCount, "CHILD_COUNT": qChildren.recordCount,
@ -48,14 +48,14 @@ if (url.action == "deactivate") {
// Deactivate children first // Deactivate children first
queryExecute(" queryExecute("
UPDATE Items UPDATE Items
SET ItemIsActive = 0 SET IsActive = 0
WHERE ItemParentItemID = :itemId WHERE ParentItemID = :itemId
", { itemId: url.itemId }); ", { itemId: url.itemId });
// Then deactivate the parent // Then deactivate the parent
queryExecute(" queryExecute("
UPDATE Items UPDATE Items
SET ItemIsActive = 0 SET IsActive = 0
WHERE ItemID = :itemId WHERE ItemID = :itemId
", { itemId: url.itemId }); ", { itemId: url.itemId });

View file

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

View file

@ -10,27 +10,27 @@ try {
businessIDs = [38, 39, 40, 41, 42]; businessIDs = [38, 39, 40, 41, 42];
for (bizID in businessIDs) { for (bizID in businessIDs) {
// Delete ItemTemplateLinks for items belonging to this business // Delete lt_ItemID_TemplateItemID for items belonging to this business
queryExecute(" queryExecute("
DELETE itl FROM ItemTemplateLinks itl DELETE itl FROM lt_ItemID_TemplateItemID itl
INNER JOIN Items i ON i.ItemID = itl.ItemID INNER JOIN Items i ON i.ID = itl.ItemID
WHERE i.ItemBusinessID = :bizID WHERE i.BusinessID = :bizID
", { bizID: bizID }, { datasource: "payfrit" }); ", { bizID: bizID }, { datasource: "payfrit" });
// Delete Items // 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 // 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 // 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 // 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 // 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"); response.steps.append("Deleted business " & bizID & " and all related data");
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -28,19 +28,19 @@ try {
response["ERROR"] = "BusinessID required"; response["ERROR"] = "BusinessID required";
} else { } else {
// Get address ID first // 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) { if (qBiz.recordCount == 0) {
response["ERROR"] = "Business not found"; response["ERROR"] = "Business not found";
} else { } else {
addrID = qBiz.BusinessAddressID; addrID = qBiz.AddressID;
// Delete business // 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 // Delete address if exists
if (val(addrID) > 0) { 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; response["OK"] = true;

View file

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

View file

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

View file

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

View file

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

View file

@ -1,32 +1,32 @@
<cfsetting showdebugoutput="false"> <cfsetting showdebugoutput="false">
<cfcontent type="application/json" reset="true"> <cfcontent type="application/json" reset="true">
<cfscript> <cfscript>
// One-time fix: remove # prefix from BusinessBrandColor // One-time fix: remove # prefix from BrandColor
qBefore = queryExecute(" qBefore = queryExecute("
SELECT BusinessID, BusinessBrandColor SELECT ID, BrandColor
FROM Businesses FROM Businesses
WHERE BusinessBrandColor LIKE :pattern WHERE BrandColor LIKE :pattern
", { pattern: { value: "##%", cfsqltype: "cf_sql_varchar" } }, { datasource: "payfrit" }); ", { pattern: { value: "##%", cfsqltype: "cf_sql_varchar" } }, { datasource: "payfrit" });
if (qBefore.recordCount > 0) { if (qBefore.recordCount > 0) {
queryExecute(" queryExecute("
UPDATE Businesses UPDATE Businesses
SET BusinessBrandColor = SUBSTRING(BusinessBrandColor, 2) SET BrandColor = SUBSTRING(BrandColor, 2)
WHERE BusinessBrandColor LIKE :pattern WHERE BrandColor LIKE :pattern
", { pattern: { value: "##%", cfsqltype: "cf_sql_varchar" } }, { datasource: "payfrit" }); ", { pattern: { value: "##%", cfsqltype: "cf_sql_varchar" } }, { datasource: "payfrit" });
} }
qAfter = queryExecute(" qAfter = queryExecute("
SELECT BusinessID, BusinessBrandColor SELECT ID, BrandColor
FROM Businesses FROM Businesses
WHERE BusinessBrandColor IS NOT NULL AND LENGTH(BusinessBrandColor) > 0 WHERE BrandColor IS NOT NULL AND LENGTH(BrandColor) > 0
", {}, { datasource: "payfrit" }); ", {}, { datasource: "payfrit" });
rows = []; rows = [];
for (i = 1; i <= qAfter.recordCount; i++) { for (i = 1; i <= qAfter.recordCount; i++) {
arrayAppend(rows, { arrayAppend(rows, {
"BusinessID": qAfter.BusinessID[i], "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 * 2. Re-parent the three flavors under the new group
* 3. Remove template flag from flavors * 3. Remove template flag from flavors
* 4. Mark "Choose Flavor" as the template * 4. Mark "Choose Flavor" as the template
* 5. Create ItemTemplateLinks entry for Shake -> Choose Flavor * 5. Create lt_ItemID_TemplateItemID entry for Shake -> Choose Flavor
* 6. Set ItemRequiresChildSelection=1 on the shake item * 6. Set RequiresChildSelection=1 on the shake item
*/ */
response = { "OK": false, "steps": [] }; response = { "OK": false, "steps": [] };
@ -46,19 +46,19 @@ try {
// Step 1: Create "Choose Flavor" modifier group under Shake // Step 1: Create "Choose Flavor" modifier group under Shake
queryExecute(" queryExecute("
INSERT INTO Items ( INSERT INTO Items (
ItemCategoryID, CategoryID,
ItemName, Name,
ItemDescription, Description,
ItemParentItemID, ParentItemID,
ItemPrice, Price,
ItemIsActive, IsActive,
ItemIsCheckedByDefault, IsCheckedByDefault,
ItemRequiresChildSelection, RequiresChildSelection,
ItemMaxNumSelectionReq, MaxNumSelectionReq,
ItemIsCollapsible, IsCollapsible,
ItemSortOrder, SortOrder,
ItemIsModifierTemplate, IsModifierTemplate,
ItemAddedOn AddedOn
) VALUES ( ) VALUES (
:categoryID, :categoryID,
'Choose Flavor', 'Choose Flavor',
@ -81,22 +81,22 @@ try {
// Get the new Choose Flavor ID // Get the new Choose Flavor ID
qNewGroup = queryExecute(" qNewGroup = queryExecute("
SELECT ItemID FROM Items SELECT ID FROM Items
WHERE ItemName = 'Choose Flavor' WHERE Name = 'Choose Flavor'
AND ItemParentItemID = :shakeItemID AND ParentItemID = :shakeItemID
ORDER BY ItemID DESC ORDER BY ID DESC
LIMIT 1 LIMIT 1
", { shakeItemID: shakeItemID }, { datasource: "payfrit" }); ", { shakeItemID: shakeItemID }, { datasource: "payfrit" });
chooseFlavorID = qNewGroup.ItemID; chooseFlavorID = qNewGroup.ID;
arrayAppend(response.steps, "Created 'Choose Flavor' group with ID: " & chooseFlavorID); arrayAppend(response.steps, "Created 'Choose Flavor' group with ID: " & chooseFlavorID);
// Step 2: Re-parent the three flavors under Choose Flavor // Step 2: Re-parent the three flavors under Choose Flavor
queryExecute(" queryExecute("
UPDATE Items UPDATE Items
SET ItemParentItemID = :chooseFlavorID, SET ParentItemID = :chooseFlavorID,
ItemIsModifierTemplate = 0, IsModifierTemplate = 0,
ItemIsCheckedByDefault = 0 IsCheckedByDefault = 0
WHERE ItemID IN (:chocolateID, :strawberryID, :vanillaID) WHERE ItemID IN (:chocolateID, :strawberryID, :vanillaID)
", { ", {
chooseFlavorID: chooseFlavorID, chooseFlavorID: chooseFlavorID,
@ -109,14 +109,14 @@ try {
// Step 3: Set Vanilla as default (common choice) // Step 3: Set Vanilla as default (common choice)
queryExecute(" queryExecute("
UPDATE Items SET ItemIsCheckedByDefault = 1 WHERE ItemID = :vanillaID UPDATE Items SET IsCheckedByDefault = 1 WHERE ItemID = :vanillaID
", { vanillaID: vanillaID }, { datasource: "payfrit" }); ", { vanillaID: vanillaID }, { datasource: "payfrit" });
arrayAppend(response.steps, "Set Vanilla as default flavor"); arrayAppend(response.steps, "Set Vanilla as default flavor");
// Step 4: Remove old template links for the flavors // Step 4: Remove old template links for the flavors
queryExecute(" queryExecute("
DELETE FROM ItemTemplateLinks DELETE FROM lt_ItemID_TemplateItemID
WHERE TemplateItemID IN (:chocolateID, :strawberryID, :vanillaID) WHERE TemplateItemID IN (:chocolateID, :strawberryID, :vanillaID)
", { ", {
chocolateID: chocolateID, chocolateID: chocolateID,
@ -126,9 +126,9 @@ try {
arrayAppend(response.steps, "Removed old template links for flavor items"); 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(" queryExecute("
INSERT INTO ItemTemplateLinks (ItemID, TemplateItemID, SortOrder) INSERT INTO lt_ItemID_TemplateItemID (ItemID, TemplateItemID, SortOrder)
VALUES (:shakeItemID, :chooseFlavorID, 0) VALUES (:shakeItemID, :chooseFlavorID, 0)
ON DUPLICATE KEY UPDATE SortOrder = 0 ON DUPLICATE KEY UPDATE SortOrder = 0
", { ", {
@ -138,14 +138,14 @@ try {
arrayAppend(response.steps, "Created template link: Shake -> Choose Flavor"); arrayAppend(response.steps, "Created template link: Shake -> Choose Flavor");
// Step 6: Set ItemRequiresChildSelection on shake // Step 6: Set RequiresChildSelection on shake
queryExecute(" queryExecute("
UPDATE Items UPDATE Items
SET ItemRequiresChildSelection = 1 SET RequiresChildSelection = 1
WHERE ItemID = :shakeItemID WHERE ItemID = :shakeItemID
", { shakeItemID: shakeItemID }, { datasource: "payfrit" }); ", { 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["OK"] = true;
response["chooseFlavorID"] = chooseFlavorID; response["chooseFlavorID"] = chooseFlavorID;

View file

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

View file

@ -29,7 +29,7 @@ try {
response["ERROR"] = "ChildBusinessID required"; response["ERROR"] = "ChildBusinessID required";
} else { } else {
queryExecute(" 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 }, parentId: { value = parentID > 0 ? parentID : javaCast("null", ""), cfsqltype = "cf_sql_integer", null = parentID == 0 },
childId: childID childId: childID

View file

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

View file

@ -27,20 +27,20 @@ try {
// Find all businesses with items in unified schema // Find all businesses with items in unified schema
if (len(businessFilter)) { if (len(businessFilter)) {
qBusinesses = queryExecute(" qBusinesses = queryExecute("
SELECT DISTINCT ItemBusinessID as BusinessID SELECT DISTINCT BusinessID as BusinessID
FROM Items FROM Items
WHERE ItemBusinessID = :bid AND ItemBusinessID > 0 WHERE BusinessID = :bid AND BusinessID > 0
", { bid: businessFilter }, { datasource: "payfrit" }); ", { bid: businessFilter }, { datasource: "payfrit" });
} else { } else {
qBusinesses = queryExecute(" qBusinesses = queryExecute("
SELECT DISTINCT ItemBusinessID as BusinessID SELECT DISTINCT BusinessID as BusinessID
FROM Items FROM Items
WHERE ItemBusinessID > 0 WHERE BusinessID > 0
", {}, { datasource: "payfrit" }); ", {}, { datasource: "payfrit" });
} }
for (biz in qBusinesses) { for (biz in qBusinesses) {
bizId = biz.BusinessID; bizId = biz.ID;
bizResult = { "BusinessID": bizId, "CategoriesCreated": 0, "ItemsUpdated": 0 }; bizResult = { "BusinessID": bizId, "CategoriesCreated": 0, "ItemsUpdated": 0 };
try { try {
@ -48,26 +48,26 @@ try {
qCategoryItems = queryExecute(" qCategoryItems = queryExecute("
SELECT DISTINCT SELECT DISTINCT
p.ItemID, p.ItemID,
p.ItemName, p.Name,
p.ItemSortOrder p.SortOrder
FROM Items p FROM Items p
INNER JOIN Items c ON c.ItemParentItemID = p.ItemID INNER JOIN Items c ON c.ParentItemID = p.ItemID
WHERE p.ItemBusinessID = :bizId WHERE p.BusinessID = :bizId
AND p.ItemParentItemID = 0 AND p.ParentItemID = 0
AND (p.ItemIsCollapsible = 0 OR p.ItemIsCollapsible IS NULL) AND (p.IsCollapsible = 0 OR p.IsCollapsible IS NULL)
AND NOT EXISTS ( 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" }); ", { bizId: bizId }, { datasource: "payfrit" });
sortOrder = 0; sortOrder = 0;
for (catItem in qCategoryItems) { for (catItem in qCategoryItems) {
// Check if category already exists for this business with same name // Check if category already exists for this business with same name
qExisting = queryExecute(" qExisting = queryExecute("
SELECT CategoryID FROM Categories SELECT ID FROM Categories
WHERE CategoryBusinessID = :bizId AND CategoryName = :name WHERE BusinessID = :bizId AND Name = :name
", { bizId: bizId, name: left(catItem.ItemName, 30) }, { datasource: "payfrit" }); ", { bizId: bizId, name: left(catItem.Name, 30) }, { datasource: "payfrit" });
if (qExisting.recordCount == 0) { if (qExisting.recordCount == 0) {
// Get next CategoryID // Get next CategoryID
@ -79,12 +79,12 @@ try {
// Create new category with explicit ID // Create new category with explicit ID
queryExecute(" queryExecute("
INSERT INTO Categories INSERT INTO Categories
(CategoryID, CategoryBusinessID, CategoryParentCategoryID, CategoryName, CategorySortOrder, CategoryAddedOn) (CategoryID, BusinessID, ParentCategoryID, Name, SortOrder, AddedOn)
VALUES (:catId, :bizId, 0, :name, :sortOrder, NOW()) VALUES (:catId, :bizId, 0, :name, :sortOrder, NOW())
", { ", {
catId: newCatId, catId: newCatId,
bizId: bizId, bizId: bizId,
name: left(catItem.ItemName, 30), name: left(catItem.Name, 30),
sortOrder: sortOrder sortOrder: sortOrder
}, { datasource: "payfrit" }); }, { datasource: "payfrit" });
@ -96,9 +96,9 @@ try {
// Update all children of this category Item to have the new CategoryID // Update all children of this category Item to have the new CategoryID
queryExecute(" queryExecute("
UPDATE Items UPDATE Items
SET ItemCategoryID = :catId SET CategoryID = :catId
WHERE ItemParentItemID = :parentId WHERE ParentItemID = :parentId
AND ItemBusinessID = :bizId AND BusinessID = :bizId
", { ", {
catId: newCatId, catId: newCatId,
parentId: catItem.ItemID, 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, MAX(TotalMs) as MaxMs,
ROUND(AVG(QueryCount), 1) as AvgQueries, ROUND(AVG(QueryCount), 1) as AvgQueries,
ROUND(AVG(ResponseBytes)) as AvgBytes ROUND(AVG(ResponseBytes)) as AvgBytes
FROM api_perf_log FROM ApiPerfLogs
WHERE LoggedAt > DATE_SUB(NOW(), INTERVAL :hours HOUR) WHERE LoggedAt > DATE_SUB(NOW(), INTERVAL :hours HOUR)
GROUP BY Endpoint GROUP BY Endpoint
ORDER BY Calls DESC ORDER BY Calls DESC
@ -76,7 +76,7 @@ try {
ROUND(AVG(AppMs)) as AvgAppMs, ROUND(AVG(AppMs)) as AvgAppMs,
MAX(TotalMs) as MaxMs, MAX(TotalMs) as MaxMs,
ROUND(AVG(QueryCount), 1) as AvgQueries ROUND(AVG(QueryCount), 1) as AvgQueries
FROM api_perf_log FROM ApiPerfLogs
WHERE LoggedAt > DATE_SUB(NOW(), INTERVAL :hours HOUR) WHERE LoggedAt > DATE_SUB(NOW(), INTERVAL :hours HOUR)
GROUP BY Endpoint GROUP BY Endpoint
HAVING Calls >= 3 HAVING Calls >= 3
@ -106,7 +106,7 @@ try {
q = queryExecute(" q = queryExecute("
SELECT Endpoint, TotalMs, DbMs, AppMs, QueryCount, ResponseBytes, SELECT Endpoint, TotalMs, DbMs, AppMs, QueryCount, ResponseBytes,
BusinessID, UserID, LoggedAt BusinessID, UserID, LoggedAt
FROM api_perf_log FROM ApiPerfLogs
WHERE LoggedAt > DATE_SUB(NOW(), INTERVAL :hours HOUR) WHERE LoggedAt > DATE_SUB(NOW(), INTERVAL :hours HOUR)
ORDER BY TotalMs DESC ORDER BY TotalMs DESC
LIMIT :lim LIMIT :lim
@ -144,7 +144,7 @@ try {
ROUND(AVG(QueryCount), 1) as OverallAvgQueries, ROUND(AVG(QueryCount), 1) as OverallAvgQueries,
MIN(LoggedAt) as FirstLog, MIN(LoggedAt) as FirstLog,
MAX(LoggedAt) as LastLog MAX(LoggedAt) as LastLog
FROM api_perf_log FROM ApiPerfLogs
WHERE LoggedAt > DATE_SUB(NOW(), INTERVAL :hours HOUR) WHERE LoggedAt > DATE_SUB(NOW(), INTERVAL :hours HOUR)
", { ", {
hours: { value: hours, cfsqltype: "cf_sql_integer" } hours: { value: hours, cfsqltype: "cf_sql_integer" }

View file

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

View file

@ -6,7 +6,7 @@
// One-time cleanup: delete test tasks and reset // One-time cleanup: delete test tasks and reset
try { try {
// Delete tasks 30, 31, 32 (test tasks with bad data) // 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({ writeOutput(serializeJSON({
"OK": true, "OK": true,

View file

@ -50,13 +50,13 @@ try {
// Get template details // Get template details
qTemplate = queryExecute(" qTemplate = queryExecute("
SELECT SELECT
QuickTaskTemplateTitle as Title, Title as Title,
QuickTaskTemplateDetails as Details, Details as Details,
QuickTaskTemplateCategoryID as CategoryID TaskCategoryID as CategoryID
FROM QuickTaskTemplates FROM QuickTaskTemplates
WHERE QuickTaskTemplateID = :id WHERE ID = :id
AND QuickTaskTemplateBusinessID = :businessID AND BusinessID = :businessID
AND QuickTaskTemplateIsActive = 1 AND IsActive = 1
", { ", {
id: { value: templateID, cfsqltype: "cf_sql_integer" }, id: { value: templateID, cfsqltype: "cf_sql_integer" },
businessID: { value: businessID, 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" }); 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(" queryExecute("
INSERT INTO Tasks ( INSERT INTO Tasks (
TaskBusinessID, TaskCategoryID, TaskTypeID, BusinessID, CategoryID, TaskTypeID,
TaskTitle, TaskDetails, TaskAddedOn, TaskClaimedByUserID Title, Details, CreatedOn, ClaimedByUserID
) VALUES ( ) VALUES (
:businessID, :categoryID, :typeID, :businessID, :categoryID, :typeID,
:title, :details, NOW(), 0 :title, :details, NOW(), 0

View file

@ -5,23 +5,23 @@
<cfscript> <cfscript>
try { try {
q = queryExecute(" q = queryExecute("
SELECT TaskID, TaskTitle, TaskDetails, TaskCategoryID, TaskClaimedByUserID, TaskCompletedOn, TaskAddedOn SELECT ID, Title, Details, TaskCategoryID, ClaimedByUserID, CompletedOn, CreatedOn
FROM Tasks FROM Tasks
WHERE TaskBusinessID = 47 WHERE BusinessID = 47
ORDER BY TaskID DESC ORDER BY ID DESC
LIMIT 20 LIMIT 20
", [], { datasource: "payfrit" }); ", [], { datasource: "payfrit" });
tasks = []; tasks = [];
for (row in q) { for (row in q) {
arrayAppend(tasks, { arrayAppend(tasks, {
"TaskID": row.TaskID, "TaskID": row.ID,
"Title": row.TaskTitle, "Title": row.Title,
"Details": isNull(row.TaskDetails) ? "" : row.TaskDetails, "Details": isNull(row.Details) ? "" : row.Details,
"CategoryID": row.TaskCategoryID, "CategoryID": row.ID,
"ClaimedByUserID": row.TaskClaimedByUserID, "ClaimedByUserID": row.ClaimedByUserID,
"CompletedOn": isNull(row.TaskCompletedOn) ? "" : dateTimeFormat(row.TaskCompletedOn, "yyyy-mm-dd HH:nn:ss"), "CompletedOn": isNull(row.CompletedOn) ? "" : dateTimeFormat(row.CompletedOn, "yyyy-mm-dd HH:nn:ss"),
"AddedOn": isNull(row.TaskAddedOn) ? "" : dateTimeFormat(row.TaskAddedOn, "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 // Verify template exists and belongs to this business
qCheck = queryExecute(" qCheck = queryExecute("
SELECT QuickTaskTemplateID FROM QuickTaskTemplates SELECT QuickTaskTemplateID FROM QuickTaskTemplates
WHERE QuickTaskTemplateID = :id AND QuickTaskTemplateBusinessID = :businessID WHERE ID = :id AND BusinessID = :businessID
", { ", {
id: { value: templateID, cfsqltype: "cf_sql_integer" }, id: { value: templateID, cfsqltype: "cf_sql_integer" },
businessID: { value: businessID, cfsqltype: "cf_sql_integer" } businessID: { value: businessID, cfsqltype: "cf_sql_integer" }
@ -62,8 +62,8 @@ try {
// Soft delete by setting IsActive to 0 // Soft delete by setting IsActive to 0
queryExecute(" queryExecute("
UPDATE QuickTaskTemplates SET QuickTaskTemplateIsActive = 0 UPDATE QuickTaskTemplates SET IsActive = 0
WHERE QuickTaskTemplateID = :id WHERE ID = :id
", { ", {
id: { value: templateID, cfsqltype: "cf_sql_integer" } id: { value: templateID, cfsqltype: "cf_sql_integer" }
}, { datasource: "payfrit" }); }, { datasource: "payfrit" });

View file

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

View file

@ -5,7 +5,7 @@
<cfscript> <cfscript>
try { try {
// Delete all Quick Task templates for business 1 // 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({ writeOutput(serializeJSON({
"OK": true, "OK": true,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -51,11 +51,11 @@ try {
// Get scheduled task definition // Get scheduled task definition
qDef = queryExecute(" qDef = queryExecute("
SELECT SELECT
ScheduledTaskTitle as Title, Title as Title,
ScheduledTaskDetails as Details, Details as Details,
ScheduledTaskCategoryID as CategoryID TaskCategoryID as CategoryID
FROM ScheduledTaskDefinitions FROM ScheduledTaskDefinitions
WHERE ScheduledTaskID = :id AND ScheduledTaskBusinessID = :businessID WHERE ID = :id AND BusinessID = :businessID
", { ", {
id: { value: scheduledTaskID, cfsqltype: "cf_sql_integer" }, id: { value: scheduledTaskID, cfsqltype: "cf_sql_integer" },
businessID: { value: businessID, 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" }); 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(" queryExecute("
INSERT INTO Tasks ( INSERT INTO Tasks (
TaskBusinessID, TaskCategoryID, TaskTypeID, BusinessID, CategoryID, TaskTypeID,
TaskTitle, TaskDetails, TaskAddedOn, TaskClaimedByUserID Title, Details, CreatedOn, ClaimedByUserID
) VALUES ( ) VALUES (
:businessID, :categoryID, :typeID, :businessID, :categoryID, :typeID,
:title, :details, NOW(), 0 :title, :details, NOW(), 0

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3,36 +3,57 @@
<cfcontent type="application/json; charset=utf-8" reset="true"> <cfcontent type="application/json; charset=utf-8" reset="true">
<cfscript> <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 fromBiz = 17; // In-N-Out
toBiz = 27; // Big Dean's toBiz = 27; // Big Dean's
// Remove mapping for source business
queryExecute(" queryExecute("
UPDATE lt_Beacon_Businesses_ServicePoints DELETE FROM lt_BeaconsID_BusinessesID
SET BusinessID = :toBiz WHERE BusinessID = :fromBiz
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" }); ", { 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 // Get current state
q = queryExecute(" q = queryExecute("
SELECT lt.*, b.BusinessName SELECT sp.ID AS ServicePointID, sp.BeaconID, sp.BusinessID,
FROM lt_Beacon_Businesses_ServicePoints lt b.Name AS BeaconName, biz.Name AS BusinessName,
JOIN Businesses b ON b.BusinessID = lt.BusinessID 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" }); ", {}, { datasource: "payfrit" });
rows = []; rows = [];
for (row in q) { for (row in q) {
arrayAppend(rows, { arrayAppend(rows, {
"BeaconID": row.BeaconID, "BeaconID": row.BeaconID,
"BusinessID": row.BusinessID, "BeaconName": row.BeaconName,
"BusinessName": row.BusinessName, "BusinessID": row.BusinessID,
"ServicePointID": row.ServicePointID "BusinessName": row.BusinessName,
}); "ServicePointID": row.ServicePointID,
"ServicePointName": row.ServicePointName
});
} }
writeOutput(serializeJSON({ writeOutput(serializeJSON({
"OK": true, "OK": true,
"MESSAGE": "Switched beacons from BusinessID #fromBiz# to #toBiz#", "MESSAGE": "Switched beacons from BusinessID #fromBiz# to #toBiz#",
"MAPPINGS": rows "MAPPINGS": rows
})); }));
</cfscript> </cfscript>

View file

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

View file

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

View file

@ -3,50 +3,65 @@
<cfcontent type="application/json; charset=utf-8" reset="true"> <cfcontent type="application/json; charset=utf-8" reset="true">
<cfscript> <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; beaconId = 2;
oldBusinessId = 37; // previous mapping
newBusinessId = 17; newBusinessId = 17;
// Remove old mapping
queryExecute(" queryExecute("
UPDATE lt_Beacon_Businesses_ServicePoints DELETE FROM lt_BeaconsID_BusinessesID
SET BusinessID = :newBizId WHERE BeaconID = :beaconId AND BusinessID = :oldBizId
WHERE BeaconID = :beaconId ", { beaconId: beaconId, oldBizId: oldBusinessId }, { datasource: "payfrit" });
", { newBizId: newBusinessId, beaconId: beaconId }, { 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 // Get current state
q = queryExecute(" q = queryExecute("
SELECT SELECT
b.BeaconID, b.ID AS BeaconID,
b.BeaconUUID, b.UUID,
b.BeaconName, b.Name AS BeaconName,
lt.BusinessID, b.BusinessID AS BeaconBusinessID,
lt.ServicePointID, sp.ID AS ServicePointID,
biz.BusinessName, sp.Name AS ServicePointName,
sp.ServicePointName sp.BusinessID AS ServicePointBusinessID,
FROM Beacons b biz.Name AS BusinessName
LEFT JOIN lt_Beacon_Businesses_ServicePoints lt ON lt.BeaconID = b.BeaconID FROM Beacons b
LEFT JOIN Businesses biz ON biz.BusinessID = lt.BusinessID LEFT JOIN ServicePoints sp ON sp.BeaconID = b.ID
LEFT JOIN ServicePoints sp ON sp.ServicePointID = lt.ServicePointID LEFT JOIN Businesses biz ON biz.ID = b.BusinessID
WHERE b.BeaconIsActive = 1 WHERE b.IsActive = 1
ORDER BY b.BeaconID ORDER BY b.ID
", {}, { datasource: "payfrit" }); ", {}, { datasource: "payfrit" });
rows = []; rows = [];
for (row in q) { for (row in q) {
arrayAppend(rows, { arrayAppend(rows, {
"BeaconID": row.BeaconID, "BeaconID": row.BeaconID,
"BeaconUUID": row.BeaconUUID, "UUID": row.UUID,
"BeaconName": row.BeaconName ?: "", "BeaconName": row.BeaconName,
"BusinessID": row.BusinessID ?: 0, "BeaconBusinessID": row.BeaconBusinessID,
"BusinessName": row.BusinessName ?: "", "BusinessName": row.BusinessName,
"ServicePointID": row.ServicePointID ?: 0, "ServicePointID": row.ServicePointID ?: 0,
"ServicePointName": row.ServicePointName ?: "" "ServicePointName": row.ServicePointName ?: ""
}); });
} }
writeOutput(serializeJSON({ writeOutput(serializeJSON({
"OK": true, "OK": true,
"MESSAGE": "Updated beacon #beaconId# to BusinessID #newBusinessId#", "MESSAGE": "Updated beacon #beaconId# to BusinessID #newBusinessId#",
"BEACONS": rows "BEACONS": rows
})); }));
</cfscript> </cfscript>

View file

@ -6,72 +6,68 @@
// Update Big Dean's business info // Update Big Dean's business info
businessId = 27; businessId = 27;
// Big Dean's actual address and hours // Big Dean's actual info
address = "1615 Ocean Front Walk, Santa Monica, CA 90401";
phone = "(310) 393-2666"; phone = "(310) 393-2666";
hours = "Mon-Thu: 11am-10pm, Fri-Sat: 11am-11pm, Sun: 11am-10pm"; hours = "Mon-Thu: 11am-10pm, Fri-Sat: 11am-11pm, Sun: 11am-10pm";
try { try {
// First get column names from INFORMATION_SCHEMA // Update phone and hours on Businesses table
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
queryExecute(" queryExecute("
UPDATE Businesses UPDATE Businesses
SET BusinessAddress = :address, SET Phone = :phone,
BusinessPhone = :phone, Hours = :hours
BusinessHours = :hours WHERE ID = :bizId
WHERE BusinessID = :bizId
", { ", {
address: address,
phone: phone, phone: phone,
hours: hours, hours: hours,
bizId: businessId 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 // Get updated record
updated = queryExecute(" updated = queryExecute("
SELECT BusinessID, BusinessName, BusinessAddress, BusinessPhone, BusinessHours SELECT ID, Name, Phone, Hours
FROM Businesses FROM Businesses
WHERE BusinessID = :bizId WHERE ID = :bizId
", { bizId: businessId }); ", { bizId: businessId }, { datasource: "payfrit" });
writeOutput(serializeJSON({ writeOutput(serializeJSON({
"OK": true, "OK": true,
"MESSAGE": "Updated Big Dean's info", "MESSAGE": "Updated Big Dean's info",
"COLUMNS_EXISTED": { "address": hasAddress, "phone": hasPhone, "hours": hasHours },
"BUSINESS": { "BUSINESS": {
"BusinessID": updated.BusinessID, "BusinessID": updated.ID,
"BusinessName": updated.BusinessName, "Name": updated.Name,
"BusinessAddress": updated.BusinessAddress, "Phone": updated.Phone,
"BusinessPhone": updated.BusinessPhone, "Hours": updated.Hours
"BusinessHours": updated.BusinessHours
} }
})); }));

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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