This repository has been archived on 2026-03-21. You can view files and clone it, but cannot push or open issues or pull requests.
payfrit-biz/api/beacons/lookup.cfm
John Mizerek b8cf2ce150 Fix beacon sharding to support Major/Minor starting from 0
- allocate_business_namespace: Start Major from 0 (was 1)
- allocate_servicepoint_minor: Start Minor from 0 (was 1)
- register_beacon_hardware: Set BeaconMinor if empty instead of rejecting
- lookup: Allow Major=0 in validation (was LTE 0, now LT 0)
- servicepoints/save: Auto-allocate BeaconMinor on insert, include in response

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-09 13:43:32 -08:00

172 lines
6.1 KiB
Text

<cfsetting showdebugoutput="false">
<cfsetting enablecfoutputonly="true">
<cfcontent type="application/json; charset=utf-8" reset="true">
<cftry>
<cfset requestData = deserializeJSON(toString(getHttpRequestData().content))>
<!--- ============================================================================
SHARDING FORMAT: { "Beacons": [{ "UUID": "...", "Major": 49, "Minor": 15 }] }
Resolves via BeaconShards -> Businesses (BeaconMajor) -> ServicePoints (BeaconMinor)
============================================================================ --->
<cfif structKeyExists(requestData, "Beacons") AND isArray(requestData.Beacons) AND arrayLen(requestData.Beacons) GT 0>
<cfset beacons = []>
<cfloop array="#requestData.Beacons#" index="beaconData">
<cfset uuid = "">
<cfset major = 0>
<cfset minor = -1>
<!--- Extract UUID (normalize: remove dashes, add back in standard format) --->
<cfif structKeyExists(beaconData, "UUID")>
<cfset rawUuid = uCase(reReplace(beaconData.UUID, "-", "", "all"))>
<cfif len(rawUuid) EQ 32>
<!--- Convert to standard UUID format with dashes for DB lookup --->
<cfset uuid = lCase(mid(rawUuid,1,8) & "-" & mid(rawUuid,9,4) & "-" & mid(rawUuid,13,4) & "-" & mid(rawUuid,17,4) & "-" & mid(rawUuid,21,12))>
</cfif>
</cfif>
<cfif structKeyExists(beaconData, "Major") AND isNumeric(beaconData.Major)>
<cfset major = int(beaconData.Major)>
</cfif>
<cfif structKeyExists(beaconData, "Minor") AND isNumeric(beaconData.Minor)>
<cfset minor = int(beaconData.Minor)>
</cfif>
<cfif len(uuid) EQ 0 OR major LT 0 OR minor LT 0>
<cfcontinue>
</cfif>
<!--- Resolve via sharding: UUID -> Shard -> Business (Major) -> ServicePoint (Minor) --->
<cfquery name="qShard" datasource="payfrit">
SELECT
biz.ID AS BusinessID,
biz.Name AS BusinessName,
biz.ParentBusinessID,
COALESCE(parent.Name, '') AS ParentBusinessName,
sp.ID AS ServicePointID,
sp.Name AS ServicePointName,
(SELECT COUNT(*) FROM Businesses WHERE ParentBusinessID = biz.ID) AS ChildCount
FROM BeaconShards bs
JOIN Businesses biz ON biz.BeaconShardID = bs.ID AND biz.BeaconMajor = <cfqueryparam cfsqltype="cf_sql_smallint" value="#major#">
LEFT JOIN ServicePoints sp ON sp.BusinessID = biz.ID AND sp.BeaconMinor = <cfqueryparam cfsqltype="cf_sql_smallint" value="#minor#"> AND sp.IsActive = 1
LEFT JOIN Businesses parent ON biz.ParentBusinessID = parent.ID
WHERE bs.UUID = <cfqueryparam cfsqltype="cf_sql_varchar" value="#uuid#">
AND bs.IsActive = 1
AND biz.IsDemo = 0
AND biz.IsPrivate = 0
LIMIT 1
</cfquery>
<cfif qShard.recordCount GT 0>
<cfset arrayAppend(beacons, {
"UUID" = uCase(reReplace(uuid, "-", "", "all")),
"Major" = major,
"Minor" = minor,
"BeaconID" = 0,
"BeaconName" = qShard.ServicePointName,
"BusinessID" = qShard.BusinessID,
"BusinessName" = qShard.BusinessName,
"ServicePointID" = val(qShard.ServicePointID),
"ServicePointName" = qShard.ServicePointName,
"ParentBusinessID" = val(qShard.ParentBusinessID),
"ParentBusinessName" = qShard.ParentBusinessName,
"HasChildren" = qShard.ChildCount GT 0
})>
</cfif>
</cfloop>
<cfoutput>#serializeJSON({
"OK" = true,
"ERROR" = "",
"BEACONS" = beacons
})#</cfoutput>
<cfabort>
</cfif>
<!--- ============================================================================
LEGACY FORMAT: { "UUIDs": ["uuid1", "uuid2", ...] }
Resolves via old Beacons table
============================================================================ --->
<cfif NOT structKeyExists(requestData, "UUIDs") OR NOT isArray(requestData.UUIDs) OR arrayLen(requestData.UUIDs) EQ 0>
<cfoutput>#serializeJSON({
"OK" = true,
"ERROR" = "",
"BEACONS" = []
})#</cfoutput>
<cfabort>
</cfif>
<!--- Clean and normalize UUIDs (remove dashes, uppercase) --->
<cfset cleanUUIDs = []>
<cfloop array="#requestData.UUIDs#" index="uuid">
<cfset cleanUUID = uCase(reReplace(uuid, "-", "", "all"))>
<cfif len(cleanUUID) EQ 32>
<cfset arrayAppend(cleanUUIDs, cleanUUID)>
</cfif>
</cfloop>
<cfif arrayLen(cleanUUIDs) EQ 0>
<cfoutput>#serializeJSON({
"OK" = true,
"ERROR" = "",
"BEACONS" = []
})#</cfoutput>
<cfabort>
</cfif>
<!--- Query for matching beacons with business info --->
<cfquery name="qBeacons" datasource="payfrit">
SELECT
b.ID AS BeaconID,
b.Name AS BeaconName,
b.UUID,
COALESCE(sp.ID, 0) AS ServicePointID,
COALESCE(sp.Name, '') AS ServicePointName,
COALESCE(sp.BusinessID, lt.BusinessID, b.BusinessID) AS BusinessID,
biz.Name AS BusinessName,
biz.ParentBusinessID AS ParentBusinessID,
COALESCE(parent.Name, '') AS ParentBusinessName,
(SELECT COUNT(*) FROM Businesses WHERE ParentBusinessID = biz.ID) AS ChildCount
FROM Beacons b
LEFT JOIN ServicePoints sp ON sp.BeaconID = b.ID
LEFT JOIN lt_BeaconsID_BusinessesID lt ON lt.BeaconID = b.ID
INNER JOIN Businesses biz ON COALESCE(sp.BusinessID, lt.BusinessID, b.BusinessID) = biz.ID
LEFT JOIN Businesses parent ON biz.ParentBusinessID = parent.ID
WHERE b.UUID IN (<cfqueryparam value="#arrayToList(cleanUUIDs)#" cfsqltype="cf_sql_varchar" list="true">)
AND b.IsActive = 1
AND biz.IsDemo = 0
AND biz.IsPrivate = 0
</cfquery>
<cfset beacons = []>
<cfloop query="qBeacons">
<cfset arrayAppend(beacons, {
"UUID" = qBeacons.UUID,
"BeaconID" = qBeacons.BeaconID,
"BeaconName" = qBeacons.BeaconName,
"BusinessID" = qBeacons.BusinessID,
"BusinessName" = qBeacons.BusinessName,
"ServicePointID" = qBeacons.ServicePointID,
"ServicePointName" = qBeacons.ServicePointName,
"ParentBusinessID" = val(qBeacons.ParentBusinessID),
"ParentBusinessName" = qBeacons.ParentBusinessName,
"HasChildren" = qBeacons.ChildCount GT 0
})>
</cfloop>
<cfoutput>#serializeJSON({
"OK" = true,
"ERROR" = "",
"BEACONS" = beacons
})#</cfoutput>
<cfcatch type="any">
<cfoutput>#serializeJSON({
"OK" = false,
"ERROR" = cfcatch.message
})#</cfoutput>
</cfcatch>
</cftry>