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>
This commit is contained in:
John Mizerek 2026-02-09 13:42:19 -08:00
parent 872897eabc
commit b8cf2ce150
5 changed files with 32 additions and 15 deletions

View file

@ -117,9 +117,9 @@ if (bizId LTE 0) {
<cfset shardUUID = qShard.UUID> <cfset shardUUID = qShard.UUID>
<!--- Find next available Major within this shard ---> <!--- Find next available Major within this shard --->
<!--- Major values: 0-65535 (uint16), we start from 1 to avoid 0 ---> <!--- Major values: 0-65535 (uint16), starting from 0 --->
<cfquery name="qMaxMajor" datasource="payfrit"> <cfquery name="qMaxMajor" datasource="payfrit">
SELECT COALESCE(MAX(BeaconMajor), 0) AS MaxMajor SELECT COALESCE(MAX(BeaconMajor), -1) AS MaxMajor
FROM Businesses FROM Businesses
WHERE BeaconShardID = <cfqueryparam cfsqltype="cf_sql_integer" value="#shardId#"> WHERE BeaconShardID = <cfqueryparam cfsqltype="cf_sql_integer" value="#shardId#">
</cfquery> </cfquery>

View file

@ -111,9 +111,9 @@ if (spId LTE 0) {
</cfif> </cfif>
<!--- Find next available Minor within this business ---> <!--- Find next available Minor within this business --->
<!--- Minor values: 0-65535 (uint16), we start from 1 to avoid 0 ---> <!--- Minor values: 0-65535 (uint16), starting from 0 --->
<cfquery name="qMaxMinor" datasource="payfrit"> <cfquery name="qMaxMinor" datasource="payfrit">
SELECT COALESCE(MAX(BeaconMinor), 0) AS MaxMinor SELECT COALESCE(MAX(BeaconMinor), -1) AS MaxMinor
FROM ServicePoints FROM ServicePoints
WHERE BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#bizId#"> WHERE BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#bizId#">
</cfquery> </cfquery>

View file

@ -165,7 +165,14 @@ firmwareVersion = normStr(structKeyExists(data, "FirmwareVersion") ? data.Firmwa
<cfset apiAbort({ OK=false, ERROR="servicepoint_business_mismatch", MESSAGE="Service point does not belong to this business" })> <cfset apiAbort({ OK=false, ERROR="servicepoint_business_mismatch", MESSAGE="Service point does not belong to this business" })>
</cfif> </cfif>
<cfif qSP.BeaconMinor NEQ minor> <!--- If service point doesn't have a minor yet, set it now --->
<cfif isNull(qSP.BeaconMinor) OR len(trim(qSP.BeaconMinor)) EQ 0>
<cfquery datasource="payfrit">
UPDATE ServicePoints
SET BeaconMinor = <cfqueryparam cfsqltype="cf_sql_smallint" value="#minor#">
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#spId#">
</cfquery>
<cfelseif qSP.BeaconMinor NEQ minor>
<cfset apiAbort({ <cfset apiAbort({
OK=false, OK=false,
ERROR="minor_mismatch", ERROR="minor_mismatch",

View file

@ -35,7 +35,7 @@
<cfset minor = int(beaconData.Minor)> <cfset minor = int(beaconData.Minor)>
</cfif> </cfif>
<cfif len(uuid) EQ 0 OR major LTE 0 OR minor LT 0> <cfif len(uuid) EQ 0 OR major LT 0 OR minor LT 0>
<cfcontinue> <cfcontinue>
</cfif> </cfif>

View file

@ -89,7 +89,17 @@ if (structKeyExists(data, "IsActive")) {
</cfif> </cfif>
<cfelse> <cfelse>
<!--- Insert ---> <!--- Auto-allocate BeaconMinor if not provided --->
<cfif beaconMinor LT 0>
<cfquery name="qMaxMinor" datasource="payfrit">
SELECT COALESCE(MAX(BeaconMinor), -1) AS MaxMinor
FROM ServicePoints
WHERE BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
</cfquery>
<cfset beaconMinor = qMaxMinor.MaxMinor + 1>
</cfif>
<!--- Insert with BeaconMinor --->
<cfquery datasource="payfrit"> <cfquery datasource="payfrit">
INSERT INTO ServicePoints ( INSERT INTO ServicePoints (
BusinessID, BusinessID,
@ -97,16 +107,16 @@ if (structKeyExists(data, "IsActive")) {
Code, Code,
TypeID, TypeID,
IsActive, IsActive,
SortOrder SortOrder,
<cfif beaconMinor GTE 0>, BeaconMinor</cfif> BeaconMinor
) VALUES ( ) VALUES (
<cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">, <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#spName#">, <cfqueryparam cfsqltype="cf_sql_varchar" value="#spName#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#spCode#" null="#(len(spCode) EQ 0)#">, <cfqueryparam cfsqltype="cf_sql_varchar" value="#spCode#" null="#(len(spCode) EQ 0)#">,
<cfqueryparam cfsqltype="cf_sql_integer" value="#spTypeID#">, <cfqueryparam cfsqltype="cf_sql_integer" value="#spTypeID#">,
<cfqueryparam cfsqltype="cf_sql_tinyint" value="#isActive#">, <cfqueryparam cfsqltype="cf_sql_tinyint" value="#isActive#">,
<cfqueryparam cfsqltype="cf_sql_integer" value="#sortOrder#"> <cfqueryparam cfsqltype="cf_sql_integer" value="#sortOrder#">,
<cfif beaconMinor GTE 0>, <cfqueryparam cfsqltype="cf_sql_smallint" value="#beaconMinor#"></cfif> <cfqueryparam cfsqltype="cf_sql_smallint" value="#beaconMinor#">
) )
</cfquery> </cfquery>
@ -138,10 +148,10 @@ if (structKeyExists(data, "IsActive")) {
"BusinessID" = qOut.BusinessID, "BusinessID" = qOut.BusinessID,
"Name" = qOut.Name, "Name" = qOut.Name,
"Code" = qOut.Code, "Code" = qOut.Code,
"TypeID"= qOut.TypeID, "TypeID" = qOut.TypeID,
"IsActive" = qOut.IsActive, "IsActive" = qOut.IsActive,
"SortOrder" = qOut.SortOrder, "SortOrder" = qOut.SortOrder,
"BeaconMinor" = val(qOut.BeaconMinor) "BeaconMinor" = isNull(qOut.BeaconMinor) ? "" : qOut.BeaconMinor
}> }>
<cfoutput>#serializeJSON({ OK=true, ERROR="", SERVICEPOINT=servicePoint })#</cfoutput> <cfoutput>#serializeJSON({ OK=true, ERROR="", SERVICEPOINT=servicePoint })#</cfoutput>