Grant-based system allowing businesses to share service points with other businesses. Includes grant CRUD API, time/eligibility/economics policies, enforcement at cart creation and order submit, Stripe payment routing for owner fees, and portal UI for managing grants. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
108 lines
3.5 KiB
Text
108 lines
3.5 KiB
Text
<cfsetting showdebugoutput="false">
|
|
<cfsetting enablecfoutputonly="true">
|
|
<cfcontent type="application/json; charset=utf-8" reset="true">
|
|
<cfheader name="Cache-Control" value="no-store">
|
|
|
|
<cfscript>
|
|
data = {};
|
|
try {
|
|
raw = toString(getHttpRequestData().content);
|
|
if (len(trim(raw))) {
|
|
data = deserializeJSON(raw);
|
|
if (!isStruct(data)) data = {};
|
|
}
|
|
} catch (any e) { data = {}; }
|
|
|
|
grantID = val(data.GrantID ?: 0);
|
|
if (grantID LTE 0) {
|
|
apiAbort({ "OK": false, "ERROR": "missing_grantid", "MESSAGE": "GrantID is required." });
|
|
}
|
|
|
|
callerUserID = val(structKeyExists(request, "UserID") ? request.UserID : 0);
|
|
if (callerUserID LTE 0) {
|
|
apiAbort({ "OK": false, "ERROR": "not_authenticated" });
|
|
}
|
|
|
|
// Load grant with business/SP names
|
|
qGrant = queryExecute(
|
|
"SELECT
|
|
g.*,
|
|
ob.Name AS OwnerBusinessName,
|
|
gb.Name AS GuestBusinessName,
|
|
sp.Name AS ServicePointName,
|
|
sp.TypeID AS ServicePointTypeID,
|
|
cu.FirstName AS CreatedByFirstName,
|
|
cu.LastName AS CreatedByLastName,
|
|
au.FirstName AS AcceptedByFirstName,
|
|
au.LastName AS AcceptedByLastName
|
|
FROM ServicePointGrants g
|
|
JOIN Businesses ob ON ob.ID = g.OwnerBusinessID
|
|
JOIN Businesses gb ON gb.ID = g.GuestBusinessID
|
|
JOIN ServicePoints sp ON sp.ID = g.ServicePointID
|
|
LEFT JOIN Users cu ON cu.ID = g.CreatedByUserID
|
|
LEFT JOIN Users au ON au.ID = g.AcceptedByUserID
|
|
WHERE g.ID = ?
|
|
LIMIT 1",
|
|
[{ value = grantID, cfsqltype = "cf_sql_integer" }],
|
|
{ datasource = "payfrit" }
|
|
);
|
|
|
|
if (qGrant.recordCount == 0) {
|
|
apiAbort({ "OK": false, "ERROR": "not_found", "MESSAGE": "Grant not found." });
|
|
}
|
|
|
|
// Load history
|
|
qHistory = queryExecute(
|
|
"SELECT h.*, u.FirstName, u.LastName
|
|
FROM ServicePointGrantHistory h
|
|
LEFT JOIN Users u ON u.ID = h.ActorUserID
|
|
WHERE h.GrantID = ?
|
|
ORDER BY h.CreatedOn DESC",
|
|
[{ value = grantID, cfsqltype = "cf_sql_integer" }],
|
|
{ datasource = "payfrit" }
|
|
);
|
|
|
|
history = [];
|
|
for (row in qHistory) {
|
|
arrayAppend(history, {
|
|
"ID": row.ID,
|
|
"Action": row.Action,
|
|
"ActorUserID": row.ActorUserID,
|
|
"ActorName": trim((row.FirstName ?: "") & " " & (row.LastName ?: "")),
|
|
"ActorBusinessID": row.ActorBusinessID,
|
|
"PreviousData": row.PreviousData ?: "",
|
|
"NewData": row.NewData ?: "",
|
|
"CreatedOn": row.CreatedOn
|
|
});
|
|
}
|
|
|
|
grant = {
|
|
"GrantID": qGrant.ID,
|
|
"UUID": qGrant.UUID,
|
|
"OwnerBusinessID": qGrant.OwnerBusinessID,
|
|
"GuestBusinessID": qGrant.GuestBusinessID,
|
|
"ServicePointID": qGrant.ServicePointID,
|
|
"StatusID": qGrant.StatusID,
|
|
"EconomicsType": qGrant.EconomicsType,
|
|
"EconomicsValue": qGrant.EconomicsValue,
|
|
"EligibilityScope": qGrant.EligibilityScope,
|
|
"TimePolicyType": qGrant.TimePolicyType,
|
|
"TimePolicyData": qGrant.TimePolicyData ?: "",
|
|
"CreatedOn": qGrant.CreatedOn,
|
|
"AcceptedOn": qGrant.AcceptedOn ?: "",
|
|
"RevokedOn": qGrant.RevokedOn ?: "",
|
|
"LastEditedOn": qGrant.LastEditedOn,
|
|
"OwnerBusinessName": qGrant.OwnerBusinessName,
|
|
"GuestBusinessName": qGrant.GuestBusinessName,
|
|
"ServicePointName": qGrant.ServicePointName,
|
|
"ServicePointTypeID": qGrant.ServicePointTypeID,
|
|
"CreatedByName": trim((qGrant.CreatedByFirstName ?: "") & " " & (qGrant.CreatedByLastName ?: "")),
|
|
"AcceptedByName": trim((qGrant.AcceptedByFirstName ?: "") & " " & (qGrant.AcceptedByLastName ?: ""))
|
|
};
|
|
|
|
writeOutput(serializeJSON({
|
|
"OK": true,
|
|
"Grant": grant,
|
|
"History": history
|
|
}));
|
|
</cfscript>
|