checkpoint
This commit is contained in:
parent
848544ba53
commit
363964d9c6
14 changed files with 1513 additions and 565 deletions
|
|
@ -5,7 +5,7 @@
|
|||
sessiontimeout="#CreateTimeSpan(0,0,30,0)#"
|
||||
clientstorage="cookie">
|
||||
|
||||
<CFSET application.datasource = "payfrit">
|
||||
<CFSET application.datasource = "payfrit_local">
|
||||
<cfset application.businessMasterObj = new library.cfc.businessMaster(odbc = application.datasource) />
|
||||
<cfset application.twilioObj = new library.cfc.twilio() />
|
||||
|
||||
|
|
|
|||
|
|
@ -2,53 +2,70 @@
|
|||
<cfsetting enablecfoutputonly="true">
|
||||
|
||||
<!---
|
||||
FILE: C:\lucee\tomcat\webapps\ROOT\biz.payfrit.com\api\Application.cfm
|
||||
Payfrit API Application.cfm
|
||||
|
||||
MVP CHANGE:
|
||||
- Public allowlist for:
|
||||
/api/businesses/list.cfm
|
||||
/api/servicepoints/list.cfm
|
||||
FIX: Provide a DEFAULT datasource so endpoints can call queryExecute()
|
||||
without specifying { datasource="payfrit" } every time.
|
||||
|
||||
IMPORTANT:
|
||||
- Do NOT rely on exact SCRIPT_NAME equality; in some deployments it may include
|
||||
the site folder prefix (e.g. /biz.payfrit.com/api/...).
|
||||
- So we allowlist by "contains" match.
|
||||
Token-auth gate for /api endpoints.
|
||||
|
||||
Public allowlist (NO auth):
|
||||
- /api/login.cfm
|
||||
- /api/logout.cfm
|
||||
- /api/auth/login.cfm
|
||||
- /api/auth/logout.cfm
|
||||
- /api/businesses/list.cfm
|
||||
- /api/servicepoints/list.cfm
|
||||
|
||||
Authenticated requests should send:
|
||||
- Header: X-User-Token: <token from /api/auth/login.cfm>
|
||||
- Header: X-Business-ID: <selected BusinessID> (required for most endpoints)
|
||||
--->
|
||||
|
||||
<cfset request.IsApiRequest = true>
|
||||
<cfinclude template="../Application.cfm">
|
||||
|
||||
<cfcontent type="application/json; charset=utf-8" reset="true">
|
||||
<cfheader name="Cache-Control" value="no-store">
|
||||
<!--- OPTION A: default datasource for the whole app (THIS fixes "missing datasource") --->
|
||||
<cfapplication
|
||||
name="payfrit_api"
|
||||
sessionmanagement="true"
|
||||
clientmanagement="false"
|
||||
setclientcookies="false"
|
||||
datasource="payfrit"
|
||||
>
|
||||
|
||||
<cfscript>
|
||||
function apiAbort(obj) {
|
||||
writeOutput(serializeJSON(obj));
|
||||
function apiAbort(payload) {
|
||||
writeOutput(serializeJSON(payload));
|
||||
abort;
|
||||
}
|
||||
|
||||
// Determine current request path
|
||||
scriptName = "";
|
||||
function headerValue(name) {
|
||||
k = "HTTP_" & ucase(reReplace(arguments.name, "[^A-Za-z0-9]", "_", "all"));
|
||||
if (structKeyExists(cgi, k)) return trim(cgi[k]);
|
||||
return "";
|
||||
}
|
||||
|
||||
// Determine request path
|
||||
request._api_scriptName = "";
|
||||
if (structKeyExists(cgi, "SCRIPT_NAME")) {
|
||||
scriptName = cgi.SCRIPT_NAME;
|
||||
request._api_scriptName = cgi.SCRIPT_NAME;
|
||||
} else if (structKeyExists(cgi, "PATH_INFO")) {
|
||||
scriptName = cgi.PATH_INFO;
|
||||
request._api_scriptName = cgi.PATH_INFO;
|
||||
}
|
||||
request._api_path = lcase(request._api_scriptName);
|
||||
|
||||
// Public allowlist
|
||||
request._api_isPublic = false;
|
||||
if (len(request._api_path)) {
|
||||
if (findNoCase("/api/login.cfm", request._api_path)) request._api_isPublic = true;
|
||||
if (findNoCase("/api/logout.cfm", request._api_path)) request._api_isPublic = true;
|
||||
|
||||
if (findNoCase("/api/auth/login.cfm", request._api_path)) request._api_isPublic = true;
|
||||
if (findNoCase("/api/auth/logout.cfm", request._api_path)) request._api_isPublic = true;
|
||||
|
||||
if (findNoCase("/api/businesses/list.cfm", request._api_path)) request._api_isPublic = true;
|
||||
if (findNoCase("/api/servicepoints/list.cfm", request._api_path)) request._api_isPublic = true;
|
||||
}
|
||||
|
||||
// MVP allowlist: PUBLIC endpoint(s) under /api
|
||||
isPublicEndpoint = false;
|
||||
if (len(scriptName)) {
|
||||
// Use contains (case-insensitive) so it works whether SCRIPT_NAME is:
|
||||
// /api/servicepoints/list.cfm OR /biz.payfrit.com/api/servicepoints/list.cfm
|
||||
if (findNoCase("/api/businesses/list.cfm", scriptName) GT 0) {
|
||||
isPublicEndpoint = true;
|
||||
}
|
||||
if (findNoCase("/api/servicepoints/list.cfm", scriptName) GT 0) {
|
||||
isPublicEndpoint = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy auth from session if present
|
||||
// Carry session values into request (if present)
|
||||
if (!structKeyExists(request, "UserID") && structKeyExists(session, "UserID")) {
|
||||
request.UserID = Duplicate(session.UserID);
|
||||
}
|
||||
|
|
@ -56,13 +73,39 @@ if (!structKeyExists(request, "BusinessID") && structKeyExists(session, "Busines
|
|||
request.BusinessID = Duplicate(session.BusinessID);
|
||||
}
|
||||
|
||||
// Enforce auth for all /api endpoints EXCEPT allowlisted public endpoints
|
||||
if (!isPublicEndpoint) {
|
||||
// Token auth: X-User-Token -> request.UserID
|
||||
request._api_userToken = headerValue("X-User-Token");
|
||||
if (len(request._api_userToken)) {
|
||||
try {
|
||||
request._api_qTok = queryExecute(
|
||||
"SELECT UserID FROM UserTokens WHERE Token = ? LIMIT 1",
|
||||
[ { value = request._api_userToken, cfsqltype = "cf_sql_varchar" } ],
|
||||
{ datasource = "payfrit" }
|
||||
);
|
||||
|
||||
if (request._api_qTok.recordCount EQ 1) {
|
||||
request.UserID = request._api_qTok.UserID;
|
||||
session.UserID = request._api_qTok.UserID;
|
||||
}
|
||||
} catch (any e) {
|
||||
// ignore; treated as unauthenticated
|
||||
}
|
||||
}
|
||||
|
||||
// Business header: X-Business-ID -> request.BusinessID
|
||||
request._api_hdrBiz = headerValue("X-Business-ID");
|
||||
if (len(request._api_hdrBiz) && isNumeric(request._api_hdrBiz)) {
|
||||
request.BusinessID = int(request._api_hdrBiz);
|
||||
session.BusinessID = request.BusinessID;
|
||||
}
|
||||
|
||||
// Enforce auth (except public)
|
||||
if (!request._api_isPublic) {
|
||||
if (!structKeyExists(request, "UserID") || !isNumeric(request.UserID) || request.UserID LTE 0) {
|
||||
apiAbort({ OK=false, ERROR="not_logged_in" });
|
||||
apiAbort({ "OK": false, "ERROR": "not_logged_in" });
|
||||
}
|
||||
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" });
|
||||
}
|
||||
}
|
||||
</cfscript>
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
<cfsetting showdebugoutput="false">
|
||||
<cfsetting enablecfoutputonly="true">
|
||||
|
||||
<cfcontent type="application/json; charset=utf-8" reset="true">
|
||||
<cfheader name="Cache-Control" value="no-store">
|
||||
|
||||
<cfscript>
|
||||
function apiAbort(obj){ writeOutput(serializeJSON(obj)); abort; }
|
||||
|
||||
function readJsonBody(){
|
||||
raw = toString(getHttpRequestData().content);
|
||||
if (isNull(raw) || len(trim(raw)) EQ 0) return {};
|
||||
try { parsed = deserializeJSON(raw); }
|
||||
catch(any e){ apiAbort({ OK=false, ERROR="bad_json", MESSAGE="Invalid JSON body" }); }
|
||||
if (!isStruct(parsed)) return {};
|
||||
return parsed;
|
||||
}
|
||||
|
||||
data = readJsonBody();
|
||||
|
||||
if (!structKeyExists(request,"UserID") || !isNumeric(request.UserID) || request.UserID LTE 0) apiAbort({OK=false,ERROR="not_logged_in"});
|
||||
if (!structKeyExists(request,"BusinessID") || !isNumeric(request.BusinessID) || request.BusinessID LTE 0) apiAbort({OK=false,ERROR="no_business_selected"});
|
||||
|
||||
if (!structKeyExists(data,"ServicePointID") || !isNumeric(data.ServicePointID) || int(data.ServicePointID) LTE 0) {
|
||||
apiAbort({OK=false,ERROR="missing_servicepoint_id"});
|
||||
}
|
||||
spid = int(data.ServicePointID);
|
||||
</cfscript>
|
||||
|
||||
<cfquery datasource="#application.datasource#">
|
||||
UPDATE ServicePoints
|
||||
SET IsActive = 0
|
||||
WHERE ServicePointID = <cfqueryparam cfsqltype="cf_sql_integer" value="#spid#">
|
||||
AND BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
|
||||
</cfquery>
|
||||
|
||||
<cfquery name="qCheck" datasource="#application.datasource#">
|
||||
SELECT ServicePointID
|
||||
FROM ServicePoints
|
||||
WHERE ServicePointID = <cfqueryparam cfsqltype="cf_sql_integer" value="#spid#">
|
||||
AND BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
|
||||
LIMIT 1
|
||||
</cfquery>
|
||||
|
||||
<cfif qCheck.recordCount EQ 0>
|
||||
<cfoutput>#serializeJSON({ OK=false, ERROR="not_found" })#</cfoutput>
|
||||
<cfabort>
|
||||
</cfif>
|
||||
|
||||
<cfoutput>#serializeJSON({ OK=true, ERROR="", ServicePointID=spid })#</cfoutput>
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
<cfsetting showdebugoutput="false">
|
||||
<cfsetting enablecfoutputonly="true">
|
||||
|
||||
<cfcontent type="application/json; charset=utf-8" reset="true">
|
||||
<cfheader name="Cache-Control" value="no-store">
|
||||
|
||||
<cfscript>
|
||||
function apiAbort(obj){ writeOutput(serializeJSON(obj)); abort; }
|
||||
|
||||
function readJsonBody(){
|
||||
raw = toString(getHttpRequestData().content);
|
||||
if (isNull(raw) || len(trim(raw)) EQ 0) return {};
|
||||
try { parsed = deserializeJSON(raw); }
|
||||
catch(any e){ apiAbort({ OK=false, ERROR="bad_json", MESSAGE="Invalid JSON body" }); }
|
||||
if (!isStruct(parsed)) return {};
|
||||
return parsed;
|
||||
}
|
||||
|
||||
data = readJsonBody();
|
||||
|
||||
if (!structKeyExists(request,"UserID") || !isNumeric(request.UserID) || request.UserID LTE 0) apiAbort({OK=false,ERROR="not_logged_in"});
|
||||
if (!structKeyExists(request,"BusinessID") || !isNumeric(request.BusinessID) || request.BusinessID LTE 0) apiAbort({OK=false,ERROR="no_business_selected"});
|
||||
|
||||
if (!structKeyExists(data,"ServicePointID") || !isNumeric(data.ServicePointID) || int(data.ServicePointID) LTE 0) {
|
||||
apiAbort({OK=false,ERROR="missing_servicepoint_id"});
|
||||
}
|
||||
|
||||
spid = int(data.ServicePointID);
|
||||
</cfscript>
|
||||
|
||||
<cfquery name="q" datasource="#application.datasource#">
|
||||
SELECT
|
||||
ServicePointID,
|
||||
BusinessID,
|
||||
ServicePointName,
|
||||
ServicePointTypeID,
|
||||
ServicePointCode,
|
||||
Description,
|
||||
SortOrder,
|
||||
IsActive,
|
||||
CreatedAt,
|
||||
UpdatedAt
|
||||
FROM ServicePoints
|
||||
WHERE ServicePointID = <cfqueryparam cfsqltype="cf_sql_integer" value="#spid#">
|
||||
AND BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
|
||||
LIMIT 1
|
||||
</cfquery>
|
||||
|
||||
<cfif q.recordCount EQ 0>
|
||||
<cfoutput>#serializeJSON({ OK=false, ERROR="not_found" })#</cfoutput>
|
||||
<cfabort>
|
||||
</cfif>
|
||||
|
||||
<cfset sp = {
|
||||
"ServicePointID"=q.ServicePointID,
|
||||
"BusinessID"=q.BusinessID,
|
||||
"ServicePointName"=q.ServicePointName,
|
||||
"ServicePointTypeID"=q.ServicePointTypeID,
|
||||
"ServicePointCode"=q.ServicePointCode,
|
||||
"Description"=q.Description,
|
||||
"SortOrder"=q.SortOrder,
|
||||
"IsActive"=q.IsActive,
|
||||
"CreatedAt"=(q.CreatedAt & ""),
|
||||
"UpdatedAt"=(q.UpdatedAt & "")
|
||||
}>
|
||||
|
||||
<cfoutput>#serializeJSON({ OK=true, ERROR="", SERVICEPOINT=sp })#</cfoutput>
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
<cfsetting showdebugoutput="false">
|
||||
<cfsetting enablecfoutputonly="true">
|
||||
|
||||
<cfcontent type="application/json; charset=utf-8" reset="true">
|
||||
<cfheader name="Cache-Control" value="no-store">
|
||||
|
||||
<cfscript>
|
||||
function apiAbort(obj) {
|
||||
writeOutput(serializeJSON(obj));
|
||||
abort;
|
||||
}
|
||||
|
||||
function readJsonBody() {
|
||||
raw = toString(getHttpRequestData().content);
|
||||
if (isNull(raw) || len(trim(raw)) EQ 0) return {};
|
||||
try {
|
||||
parsed = deserializeJSON(raw);
|
||||
} catch(any e) {
|
||||
apiAbort({ OK=false, ERROR="bad_json", MESSAGE="Invalid JSON body" });
|
||||
}
|
||||
if (!isStruct(parsed)) return {};
|
||||
return parsed;
|
||||
}
|
||||
|
||||
data = readJsonBody();
|
||||
|
||||
if (!structKeyExists(request, "UserID") || !isNumeric(request.UserID) || request.UserID LTE 0) {
|
||||
apiAbort({ OK=false, ERROR="not_logged_in" });
|
||||
}
|
||||
if (!structKeyExists(request, "BusinessID") || !isNumeric(request.BusinessID) || request.BusinessID LTE 0) {
|
||||
apiAbort({ OK=false, ERROR="no_business_selected" });
|
||||
}
|
||||
|
||||
onlyActive = true;
|
||||
if (structKeyExists(data, "onlyActive")) {
|
||||
if (isBoolean(data.onlyActive)) {
|
||||
onlyActive = data.onlyActive;
|
||||
} else if (isNumeric(data.onlyActive)) {
|
||||
onlyActive = (int(data.onlyActive) EQ 1);
|
||||
} else if (isSimpleValue(data.onlyActive)) {
|
||||
onlyActive = (lcase(trim(toString(data.onlyActive))) EQ "true");
|
||||
}
|
||||
}
|
||||
</cfscript>
|
||||
|
||||
<cfquery name="q" datasource="#application.datasource#">
|
||||
SELECT
|
||||
ServicePointID,
|
||||
BusinessID,
|
||||
ServicePointName,
|
||||
ServicePointTypeID,
|
||||
ServicePointCode,
|
||||
Description,
|
||||
SortOrder,
|
||||
IsActive,
|
||||
CreatedAt,
|
||||
UpdatedAt
|
||||
FROM ServicePoints
|
||||
WHERE BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
|
||||
<cfif onlyActive>
|
||||
AND IsActive = 1
|
||||
</cfif>
|
||||
ORDER BY SortOrder, ServicePointName, ServicePointID
|
||||
</cfquery>
|
||||
|
||||
<cfset sps = []>
|
||||
<cfloop query="q">
|
||||
<cfset arrayAppend(sps, {
|
||||
"ServicePointID"=q.ServicePointID,
|
||||
"BusinessID"=q.BusinessID,
|
||||
"ServicePointName"=q.ServicePointName,
|
||||
"ServicePointTypeID"=q.ServicePointTypeID,
|
||||
"ServicePointCode"=q.ServicePointCode,
|
||||
"Description"=q.Description,
|
||||
"SortOrder"=q.SortOrder,
|
||||
"IsActive"=q.IsActive,
|
||||
"CreatedAt"=(q.CreatedAt & ""),
|
||||
"UpdatedAt"=(q.UpdatedAt & "")
|
||||
})>
|
||||
</cfloop>
|
||||
|
||||
<cfoutput>#serializeJSON({ OK=true, ERROR="", BUSINESSID=(request.BusinessID & ""), COUNT=arrayLen(sps), SERVICEPOINTS=sps })#</cfoutput>
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
<cfsetting showdebugoutput="false">
|
||||
<cfsetting enablecfoutputonly="true">
|
||||
|
||||
<cfcontent type="application/json; charset=utf-8" reset="true">
|
||||
<cfheader name="Cache-Control" value="no-store">
|
||||
|
||||
<cfscript>
|
||||
function apiAbort(obj){ writeOutput(serializeJSON(obj)); abort; }
|
||||
|
||||
function readJsonBody(){
|
||||
raw = toString(getHttpRequestData().content);
|
||||
if (isNull(raw) || len(trim(raw)) EQ 0) return {};
|
||||
try { parsed = deserializeJSON(raw); }
|
||||
catch(any e){ apiAbort({ OK=false, ERROR="bad_json", MESSAGE="Invalid JSON body" }); }
|
||||
if (!isStruct(parsed)) return {};
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function normStr(v){
|
||||
if (isNull(v)) return "";
|
||||
return trim(toString(v));
|
||||
}
|
||||
|
||||
data = readJsonBody();
|
||||
|
||||
if (!structKeyExists(request,"UserID") || !isNumeric(request.UserID) || request.UserID LTE 0) apiAbort({OK=false,ERROR="not_logged_in"});
|
||||
if (!structKeyExists(request,"BusinessID") || !isNumeric(request.BusinessID) || request.BusinessID LTE 0) apiAbort({OK=false,ERROR="no_business_selected"});
|
||||
|
||||
if (!structKeyExists(data,"ServicePointName") || len(normStr(data.ServicePointName)) EQ 0) {
|
||||
apiAbort({OK=false,ERROR="missing_servicepoint_name",MESSAGE="ServicePointName is required"});
|
||||
}
|
||||
|
||||
spid = 0;
|
||||
if (structKeyExists(data,"ServicePointID") && isNumeric(data.ServicePointID) && int(data.ServicePointID) GT 0) {
|
||||
spid = int(data.ServicePointID);
|
||||
}
|
||||
|
||||
spName = normStr(data.ServicePointName);
|
||||
spTypeID = (structKeyExists(data,"ServicePointTypeID") && isNumeric(data.ServicePointTypeID)) ? int(data.ServicePointTypeID) : 0;
|
||||
spCode = structKeyExists(data,"ServicePointCode") ? normStr(data.ServicePointCode) : "";
|
||||
descr = structKeyExists(data,"Description") ? normStr(data.Description) : "";
|
||||
sortOrd = (structKeyExists(data,"SortOrder") && isNumeric(data.SortOrder)) ? int(data.SortOrder) : 0;
|
||||
|
||||
isActive = 1;
|
||||
if (structKeyExists(data,"IsActive")) {
|
||||
if (isBoolean(data.IsActive)) isActive = (data.IsActive ? 1 : 0);
|
||||
else if (isNumeric(data.IsActive)) isActive = int(data.IsActive);
|
||||
else if (isSimpleValue(data.IsActive)) isActive = (lcase(trim(toString(data.IsActive))) EQ "true" ? 1 : 0);
|
||||
}
|
||||
</cfscript>
|
||||
|
||||
<cfif spid GT 0>
|
||||
<cfquery datasource="#application.datasource#">
|
||||
UPDATE ServicePoints
|
||||
SET
|
||||
ServicePointName = <cfqueryparam cfsqltype="cf_sql_varchar" value="#spName#">,
|
||||
ServicePointTypeID = <cfqueryparam cfsqltype="cf_sql_integer" value="#spTypeID#">,
|
||||
ServicePointCode = <cfqueryparam cfsqltype="cf_sql_varchar" value="#spCode#" null="#(len(spCode) EQ 0)#">,
|
||||
Description = <cfqueryparam cfsqltype="cf_sql_varchar" value="#descr#" null="#(len(descr) EQ 0)#">,
|
||||
SortOrder = <cfqueryparam cfsqltype="cf_sql_integer" value="#sortOrd#">,
|
||||
IsActive = <cfqueryparam cfsqltype="cf_sql_tinyint" value="#isActive#">
|
||||
WHERE ServicePointID = <cfqueryparam cfsqltype="cf_sql_integer" value="#spid#">
|
||||
AND BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
|
||||
</cfquery>
|
||||
|
||||
<cfquery name="qCheck" datasource="#application.datasource#">
|
||||
SELECT ServicePointID
|
||||
FROM ServicePoints
|
||||
WHERE ServicePointID = <cfqueryparam cfsqltype="cf_sql_integer" value="#spid#">
|
||||
AND BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
|
||||
LIMIT 1
|
||||
</cfquery>
|
||||
|
||||
<cfif qCheck.recordCount EQ 0>
|
||||
<cfoutput>#serializeJSON({ OK=false, ERROR="not_found" })#</cfoutput>
|
||||
<cfabort>
|
||||
</cfif>
|
||||
<cfelse>
|
||||
<cfquery datasource="#application.datasource#">
|
||||
INSERT INTO ServicePoints (
|
||||
BusinessID,
|
||||
ServicePointName,
|
||||
ServicePointTypeID,
|
||||
ServicePointCode,
|
||||
Description,
|
||||
SortOrder,
|
||||
IsActive
|
||||
) VALUES (
|
||||
<cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">,
|
||||
<cfqueryparam cfsqltype="cf_sql_varchar" value="#spName#">,
|
||||
<cfqueryparam cfsqltype="cf_sql_integer" value="#spTypeID#">,
|
||||
<cfqueryparam cfsqltype="cf_sql_varchar" value="#spCode#" null="#(len(spCode) EQ 0)#">,
|
||||
<cfqueryparam cfsqltype="cf_sql_varchar" value="#descr#" null="#(len(descr) EQ 0)#">,
|
||||
<cfqueryparam cfsqltype="cf_sql_integer" value="#sortOrd#">,
|
||||
<cfqueryparam cfsqltype="cf_sql_tinyint" value="#isActive#">
|
||||
)
|
||||
</cfquery>
|
||||
|
||||
<cfquery name="qId" datasource="#application.datasource#">
|
||||
SELECT LAST_INSERT_ID() AS ServicePointID
|
||||
</cfquery>
|
||||
<cfset spid = qId.ServicePointID>
|
||||
</cfif>
|
||||
|
||||
<cfquery name="qOut" datasource="#application.datasource#">
|
||||
SELECT
|
||||
ServicePointID,
|
||||
BusinessID,
|
||||
ServicePointName,
|
||||
ServicePointTypeID,
|
||||
ServicePointCode,
|
||||
Description,
|
||||
SortOrder,
|
||||
IsActive,
|
||||
CreatedAt,
|
||||
UpdatedAt
|
||||
FROM ServicePoints
|
||||
WHERE ServicePointID = <cfqueryparam cfsqltype="cf_sql_integer" value="#spid#">
|
||||
AND BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
|
||||
LIMIT 1
|
||||
</cfquery>
|
||||
|
||||
<cfset sp = {
|
||||
"ServicePointID"=qOut.ServicePointID,
|
||||
"BusinessID"=qOut.BusinessID,
|
||||
"ServicePointName"=qOut.ServicePointName,
|
||||
"ServicePointTypeID"=qOut.ServicePointTypeID,
|
||||
"ServicePointCode"=qOut.ServicePointCode,
|
||||
"Description"=qOut.Description,
|
||||
"SortOrder"=qOut.SortOrder,
|
||||
"IsActive"=qOut.IsActive,
|
||||
"CreatedAt"=(qOut.CreatedAt & ""),
|
||||
"UpdatedAt"=(qOut.UpdatedAt & "")
|
||||
}>
|
||||
|
||||
<cfoutput>#serializeJSON({ OK=true, ERROR="", SERVICEPOINT=sp })#</cfoutput>
|
||||
114
api/auth/login.cfm
Normal file
114
api/auth/login.cfm
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
<cfsetting showdebugoutput="false">
|
||||
<cfsetting enablecfoutputonly="true">
|
||||
|
||||
<cfscript>
|
||||
/*
|
||||
PATH:
|
||||
C:\lucee\tomcat\webapps\ROOT\biz.payfrit.com\api\auth\login.cfm
|
||||
|
||||
INPUT (JSON):
|
||||
{ "username": "...", "password": "..." }
|
||||
|
||||
OUTPUT (JSON):
|
||||
{ OK:true, ERROR:"", UserID:123, UserFirstName:"...", Token:"..." }
|
||||
|
||||
Uses existing UserTokens table:
|
||||
TokenID (auto), UserID, Token, CreatedAt (DEFAULT CURRENT_TIMESTAMP)
|
||||
-> INSERT does NOT include CreatedAt.
|
||||
*/
|
||||
|
||||
function apiAbort(required struct payload) {
|
||||
writeOutput(serializeJSON(payload));
|
||||
abort;
|
||||
}
|
||||
|
||||
function readJsonBody() {
|
||||
var raw = getHttpRequestData().content;
|
||||
if (isNull(raw)) raw = "";
|
||||
if (!len(trim(raw))) return {};
|
||||
try {
|
||||
var data = deserializeJSON(raw);
|
||||
if (isStruct(data)) return data;
|
||||
} catch (any e) {}
|
||||
return {};
|
||||
}
|
||||
|
||||
function normalizeUsername(required string u) {
|
||||
var x = trim(arguments.u);
|
||||
x = replace(x, " ", "", "all");
|
||||
x = replace(x, "(", "", "all");
|
||||
x = replace(x, ")", "", "all");
|
||||
x = replace(x, "-", "", "all");
|
||||
return x;
|
||||
}
|
||||
|
||||
var data = readJsonBody();
|
||||
var username = structKeyExists(data, "username") ? normalizeUsername("" & data.username) : "";
|
||||
var password = structKeyExists(data, "password") ? ("" & data.password) : "";
|
||||
|
||||
if (!len(username) || !len(password)) {
|
||||
apiAbort({ "OK": false, "ERROR": "missing_fields" });
|
||||
}
|
||||
|
||||
try {
|
||||
var q = queryExecute(
|
||||
"
|
||||
SELECT UserID, UserFirstName
|
||||
FROM Users
|
||||
WHERE
|
||||
(
|
||||
(UserEmailAddress = ?) OR
|
||||
(UserContactNumber = ?)
|
||||
)
|
||||
AND UserPassword = ?
|
||||
AND UserIsEmailVerified = 1
|
||||
AND UserIsContactVerified > 0
|
||||
LIMIT 1
|
||||
",
|
||||
[
|
||||
{ value = username, cfsqltype = "cf_sql_varchar" },
|
||||
{ value = username, cfsqltype = "cf_sql_varchar" },
|
||||
{ value = hash(password), cfsqltype = "cf_sql_varchar" }
|
||||
],
|
||||
{ datasource = "payfrit" }
|
||||
);
|
||||
|
||||
if (q.recordCount NEQ 1) {
|
||||
apiAbort({ "OK": false, "ERROR": "bad_credentials" });
|
||||
}
|
||||
|
||||
var token = replace(createUUID(), "-", "", "all");
|
||||
|
||||
queryExecute(
|
||||
"INSERT INTO UserTokens (UserID, Token) VALUES (?, ?)",
|
||||
[
|
||||
{ value = q.UserID, cfsqltype = "cf_sql_integer" },
|
||||
{ value = token, cfsqltype = "cf_sql_varchar" }
|
||||
],
|
||||
{ datasource = "payfrit" }
|
||||
);
|
||||
|
||||
// Optional: also set session for browser tools
|
||||
cflock timeout="15" throwontimeout="yes" type="exclusive" scope="session" {
|
||||
session.UserID = q.UserID;
|
||||
}
|
||||
request.UserID = q.UserID;
|
||||
|
||||
writeOutput(serializeJSON({
|
||||
"OK": true,
|
||||
"ERROR": "",
|
||||
"UserID": q.UserID,
|
||||
"UserFirstName": q.UserFirstName,
|
||||
"Token": token
|
||||
}));
|
||||
abort;
|
||||
|
||||
} catch (any e) {
|
||||
apiAbort({
|
||||
"OK": false,
|
||||
"ERROR": "server_error",
|
||||
"MESSAGE": "DB error during login",
|
||||
"DETAIL": e.message
|
||||
});
|
||||
}
|
||||
</cfscript>
|
||||
|
|
@ -1,56 +1,17 @@
|
|||
<cfsetting showdebugoutput="false">
|
||||
<cfsetting enablecfoutputonly="true">
|
||||
|
||||
<cfcontent type="application/json; charset=utf-8" reset="true">
|
||||
<cfheader name="Cache-Control" value="no-store">
|
||||
|
||||
<cfscript>
|
||||
/*
|
||||
PUBLIC MVP endpoint
|
||||
Returns Businesses list with STRICT casing:
|
||||
- Businesses
|
||||
- BusinessID
|
||||
- BusinessName
|
||||
|
||||
Lucee-safe JSON output (NO encodeForJSON)
|
||||
*/
|
||||
|
||||
function jsonString(val) {
|
||||
// serializeJSON("abc") -> "\"abc\""
|
||||
// we strip the surrounding quotes
|
||||
var s = serializeJSON(toString(val));
|
||||
return mid(s, 2, len(s) - 2);
|
||||
}
|
||||
|
||||
function apiAbort(obj) {
|
||||
writeOutput('{');
|
||||
writeOutput('"OK":false');
|
||||
|
||||
if (structKeyExists(obj, "ERROR")) {
|
||||
writeOutput(',"ERROR":"' & jsonString(obj.ERROR) & '"');
|
||||
}
|
||||
if (structKeyExists(obj, "DETAIL")) {
|
||||
writeOutput(',"DETAIL":"' & jsonString(obj.DETAIL) & '"');
|
||||
}
|
||||
|
||||
writeOutput('}');
|
||||
function apiAbort(payload) {
|
||||
writeOutput(serializeJSON(payload));
|
||||
abort;
|
||||
}
|
||||
|
||||
/* ---- datasource resolution (unchanged) ---- */
|
||||
dsn = "";
|
||||
if (structKeyExists(application, "datasource")) {
|
||||
dsn = application.datasource;
|
||||
} else if (structKeyExists(application, "dsn")) {
|
||||
dsn = application.dsn;
|
||||
}
|
||||
|
||||
if (!len(dsn)) {
|
||||
apiAbort({
|
||||
ERROR = "missing_datasource",
|
||||
DETAIL = "No datasource configured"
|
||||
});
|
||||
}
|
||||
|
||||
/* ---- query ---- */
|
||||
q = queryExecute(
|
||||
try {
|
||||
q = queryExecute(
|
||||
"
|
||||
SELECT
|
||||
BusinessID,
|
||||
|
|
@ -59,23 +20,33 @@ q = queryExecute(
|
|||
ORDER BY BusinessName
|
||||
",
|
||||
[],
|
||||
{ datasource = dsn }
|
||||
);
|
||||
{ datasource = "payfrit" }
|
||||
);
|
||||
|
||||
/* ---- output ---- */
|
||||
writeOutput('{');
|
||||
writeOutput('"OK":true');
|
||||
writeOutput(',"COUNT":' & q.recordCount);
|
||||
writeOutput(',"Businesses":[');
|
||||
// Convert query -> array of structs (JSON-native)
|
||||
rows = [];
|
||||
for (i = 1; i <= q.recordCount; i++) {
|
||||
arrayAppend(rows, {
|
||||
"BusinessID": q.BusinessID[i],
|
||||
"BusinessName": q.BusinessName[i]
|
||||
});
|
||||
}
|
||||
|
||||
for (i = 1; i LTE q.recordCount; i = i + 1) {
|
||||
if (i GT 1) writeOutput(',');
|
||||
// Provide BOTH keys to satisfy any Flutter casing expectation
|
||||
writeOutput(serializeJSON({
|
||||
"OK": true,
|
||||
"ERROR": "",
|
||||
"VERSION": "businesses_list_v3",
|
||||
"BUSINESSES": rows,
|
||||
"Businesses": rows
|
||||
}));
|
||||
abort;
|
||||
|
||||
writeOutput('{');
|
||||
writeOutput('"BusinessID":' & q.BusinessID[i]);
|
||||
writeOutput(',"BusinessName":"' & jsonString(q.BusinessName[i]) & '"');
|
||||
writeOutput('}');
|
||||
} catch (any e) {
|
||||
apiAbort({
|
||||
"OK": false,
|
||||
"ERROR": "server_error",
|
||||
"DETAIL": e.message
|
||||
});
|
||||
}
|
||||
|
||||
writeOutput(']}');
|
||||
</cfscript>
|
||||
|
|
|
|||
98
api/menu/items.cfm
Normal file
98
api/menu/items.cfm
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
<cfsetting showdebugoutput="false">
|
||||
<cfsetting enablecfoutputonly="true">
|
||||
|
||||
<cffunction name="readJsonBody" access="public" returntype="struct" output="false">
|
||||
<cfset var raw = getHttpRequestData().content>
|
||||
<cfif isNull(raw) OR len(trim(raw)) EQ 0>
|
||||
<cfreturn {}>
|
||||
</cfif>
|
||||
<cftry>
|
||||
<cfset var data = deserializeJSON(raw)>
|
||||
<cfif isStruct(data)>
|
||||
<cfreturn data>
|
||||
<cfelse>
|
||||
<cfreturn {}>
|
||||
</cfif>
|
||||
<cfcatch>
|
||||
<cfreturn {}>
|
||||
</cfcatch>
|
||||
</cftry>
|
||||
</cffunction>
|
||||
|
||||
<cffunction name="apiAbort" access="public" returntype="void" output="true">
|
||||
<cfargument name="payload" type="struct" required="true">
|
||||
<cfcontent type="application/json; charset=utf-8">
|
||||
<cfoutput>#serializeJSON(arguments.payload)#</cfoutput>
|
||||
<cfabort>
|
||||
</cffunction>
|
||||
|
||||
<cfset data = readJsonBody()>
|
||||
<cfset BusinessID = 0>
|
||||
<cfif structKeyExists(data, "BusinessID")>
|
||||
<cfset BusinessID = val(data.BusinessID)>
|
||||
</cfif>
|
||||
|
||||
<cfif BusinessID LTE 0>
|
||||
<cfset apiAbort({ "OK": false, "ERROR": "missing_businessid", "MESSAGE": "BusinessID is required.", "DETAIL": "" })>
|
||||
</cfif>
|
||||
|
||||
<cftry>
|
||||
<cfset q = queryExecute(
|
||||
"
|
||||
SELECT
|
||||
i.ItemID,
|
||||
i.ItemCategoryID,
|
||||
i.ItemName,
|
||||
i.ItemDescription,
|
||||
i.ItemParentItemID,
|
||||
i.ItemPrice,
|
||||
i.ItemIsActive,
|
||||
i.ItemIsCheckedByDefault,
|
||||
i.ItemRequiresChildSelection,
|
||||
i.ItemMaxNumSelectionReq,
|
||||
i.ItemIsCollapsible,
|
||||
i.ItemSortOrder
|
||||
FROM Items i
|
||||
INNER JOIN Categories c
|
||||
ON c.CategoryID = i.ItemCategoryID
|
||||
WHERE c.CategoryBusinessID = ?
|
||||
ORDER BY i.ItemParentItemID, i.ItemSortOrder, i.ItemID
|
||||
",
|
||||
[ { value = BusinessID, cfsqltype = "cf_sql_integer" } ],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfset rows = []>
|
||||
<cfloop query="q">
|
||||
<cfset arrayAppend(rows, {
|
||||
"ItemID": q.ItemID,
|
||||
"ItemCategoryID": q.ItemCategoryID,
|
||||
"ItemName": q.ItemName,
|
||||
"ItemDescription": q.ItemDescription,
|
||||
"ItemParentItemID": q.ItemParentItemID,
|
||||
"ItemPrice": q.ItemPrice,
|
||||
"ItemIsActive": q.ItemIsActive,
|
||||
"ItemIsCheckedByDefault": q.ItemIsCheckedByDefault,
|
||||
"ItemRequiresChildSelection": q.ItemRequiresChildSelection,
|
||||
"ItemMaxNumSelectionReq": q.ItemMaxNumSelectionReq,
|
||||
"ItemIsCollapsible": q.ItemIsCollapsible,
|
||||
"ItemSortOrder": q.ItemSortOrder
|
||||
})>
|
||||
</cfloop>
|
||||
|
||||
<cfset apiAbort({
|
||||
"OK": true,
|
||||
"ERROR": "",
|
||||
"Items": rows,
|
||||
"COUNT": arrayLen(rows)
|
||||
})>
|
||||
|
||||
<cfcatch>
|
||||
<cfset apiAbort({
|
||||
"OK": false,
|
||||
"ERROR": "server_error",
|
||||
"MESSAGE": "DB error loading items",
|
||||
"DETAIL": cfcatch.message
|
||||
})>
|
||||
</cfcatch>
|
||||
</cftry>
|
||||
135
api/orders/getCart.cfm
Normal file
135
api/orders/getCart.cfm
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
<cfsetting showdebugoutput="false">
|
||||
<cfsetting enablecfoutputonly="true">
|
||||
|
||||
<cffunction name="readJsonBody" access="public" returntype="struct" output="false">
|
||||
<cfset var raw = getHttpRequestData().content>
|
||||
<cfif isNull(raw) OR len(trim(raw)) EQ 0>
|
||||
<cfreturn {}>
|
||||
</cfif>
|
||||
<cftry>
|
||||
<cfset var data = deserializeJSON(raw)>
|
||||
<cfif isStruct(data)>
|
||||
<cfreturn data>
|
||||
<cfelse>
|
||||
<cfreturn {}>
|
||||
</cfif>
|
||||
<cfcatch>
|
||||
<cfreturn {}>
|
||||
</cfcatch>
|
||||
</cftry>
|
||||
</cffunction>
|
||||
|
||||
<cffunction name="apiAbort" access="public" returntype="void" output="true">
|
||||
<cfargument name="payload" type="struct" required="true">
|
||||
<cfcontent type="application/json; charset=utf-8">
|
||||
<cfoutput>#serializeJSON(arguments.payload)#</cfoutput>
|
||||
<cfabort>
|
||||
</cffunction>
|
||||
|
||||
<cfset data = readJsonBody()>
|
||||
<cfset OrderID = val( structKeyExists(data,"OrderID") ? data.OrderID : 0 )>
|
||||
|
||||
<cfif OrderID LTE 0>
|
||||
<cfset apiAbort({ "OK": false, "ERROR": "missing_orderid", "MESSAGE": "OrderID is required.", "DETAIL": "" })>
|
||||
</cfif>
|
||||
|
||||
<cftry>
|
||||
<cfset qOrder = queryExecute(
|
||||
"
|
||||
SELECT
|
||||
OrderID,
|
||||
OrderUUID,
|
||||
OrderUserID,
|
||||
OrderBusinessID,
|
||||
OrderBusinessDeliveryMultiplier,
|
||||
OrderTypeID,
|
||||
OrderDeliveryFee,
|
||||
OrderStatusID,
|
||||
OrderAddressID,
|
||||
OrderPaymentID,
|
||||
OrderRemarks,
|
||||
OrderAddedOn,
|
||||
OrderLastEditedOn,
|
||||
OrderSubmittedOn,
|
||||
OrderServicePointID
|
||||
FROM Orders
|
||||
WHERE OrderID = ?
|
||||
LIMIT 1
|
||||
",
|
||||
[ { value = OrderID, cfsqltype = "cf_sql_integer" } ],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfif qOrder.recordCount EQ 0>
|
||||
<cfset apiAbort({ "OK": false, "ERROR": "not_found", "MESSAGE": "Order not found.", "DETAIL": "" })>
|
||||
</cfif>
|
||||
|
||||
<cfset qLI = queryExecute(
|
||||
"
|
||||
SELECT
|
||||
OrderLineItemID,
|
||||
OrderLineItemParentOrderLineItemID,
|
||||
OrderLineItemOrderID,
|
||||
OrderLineItemItemID,
|
||||
OrderLineItemStatusID,
|
||||
OrderLineItemPrice,
|
||||
OrderLineItemQuantity,
|
||||
OrderLineItemRemark,
|
||||
OrderLineItemIsDeleted,
|
||||
OrderLineItemAddedOn
|
||||
FROM OrderLineItems
|
||||
WHERE OrderLineItemOrderID = ?
|
||||
ORDER BY OrderLineItemID
|
||||
",
|
||||
[ { value = OrderID, cfsqltype = "cf_sql_integer" } ],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfset rows = []>
|
||||
<cfloop query="qLI">
|
||||
<cfset arrayAppend(rows, {
|
||||
"OrderLineItemID": qLI.OrderLineItemID,
|
||||
"OrderLineItemParentOrderLineItemID": qLI.OrderLineItemParentOrderLineItemID,
|
||||
"OrderLineItemOrderID": qLI.OrderLineItemOrderID,
|
||||
"OrderLineItemItemID": qLI.OrderLineItemItemID,
|
||||
"OrderLineItemStatusID": qLI.OrderLineItemStatusID,
|
||||
"OrderLineItemPrice": qLI.OrderLineItemPrice,
|
||||
"OrderLineItemQuantity": qLI.OrderLineItemQuantity,
|
||||
"OrderLineItemRemark": qLI.OrderLineItemRemark,
|
||||
"OrderLineItemIsDeleted": qLI.OrderLineItemIsDeleted,
|
||||
"OrderLineItemAddedOn": qLI.OrderLineItemAddedOn
|
||||
})>
|
||||
</cfloop>
|
||||
|
||||
<cfset apiAbort({
|
||||
"OK": true,
|
||||
"ERROR": "",
|
||||
"Order": {
|
||||
"OrderID": qOrder.OrderID,
|
||||
"OrderUUID": qOrder.OrderUUID,
|
||||
"OrderUserID": qOrder.OrderUserID,
|
||||
"OrderBusinessID": qOrder.OrderBusinessID,
|
||||
"OrderBusinessDeliveryMultiplier": qOrder.OrderBusinessDeliveryMultiplier,
|
||||
"OrderTypeID": qOrder.OrderTypeID,
|
||||
"OrderDeliveryFee": qOrder.OrderDeliveryFee,
|
||||
"OrderStatusID": qOrder.OrderStatusID,
|
||||
"OrderAddressID": qOrder.OrderAddressID,
|
||||
"OrderPaymentID": qOrder.OrderPaymentID,
|
||||
"OrderRemarks": qOrder.OrderRemarks,
|
||||
"OrderAddedOn": qOrder.OrderAddedOn,
|
||||
"OrderLastEditedOn": qOrder.OrderLastEditedOn,
|
||||
"OrderSubmittedOn": qOrder.OrderSubmittedOn,
|
||||
"OrderServicePointID": qOrder.OrderServicePointID
|
||||
},
|
||||
"OrderLineItems": rows
|
||||
})>
|
||||
|
||||
<cfcatch>
|
||||
<cfset apiAbort({
|
||||
"OK": false,
|
||||
"ERROR": "server_error",
|
||||
"MESSAGE": "DB error loading cart",
|
||||
"DETAIL": cfcatch.message
|
||||
})>
|
||||
</cfcatch>
|
||||
</cftry>
|
||||
263
api/orders/getOrCreateCart.cfm
Normal file
263
api/orders/getOrCreateCart.cfm
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
<cfsetting showdebugoutput="false">
|
||||
<cfsetting enablecfoutputonly="true">
|
||||
|
||||
<cffunction name="readJsonBody" access="public" returntype="struct" output="false">
|
||||
<cfset var raw = getHttpRequestData().content>
|
||||
<cfif isNull(raw) OR len(trim(raw)) EQ 0>
|
||||
<cfreturn {}>
|
||||
</cfif>
|
||||
<cftry>
|
||||
<cfset var data = deserializeJSON(raw)>
|
||||
<cfif isStruct(data)>
|
||||
<cfreturn data>
|
||||
<cfelse>
|
||||
<cfreturn {}>
|
||||
</cfif>
|
||||
<cfcatch>
|
||||
<cfreturn {}>
|
||||
</cfcatch>
|
||||
</cftry>
|
||||
</cffunction>
|
||||
|
||||
<cffunction name="apiAbort" access="public" returntype="void" output="true">
|
||||
<cfargument name="payload" type="struct" required="true">
|
||||
<cfcontent type="application/json; charset=utf-8">
|
||||
<cfoutput>#serializeJSON(arguments.payload)#</cfoutput>
|
||||
<cfabort>
|
||||
</cffunction>
|
||||
|
||||
<cffunction name="loadCartPayload" access="public" returntype="struct" output="false">
|
||||
<cfargument name="OrderID" type="numeric" required="true">
|
||||
|
||||
<cfset var out = {}>
|
||||
<cfset var qOrder = queryExecute(
|
||||
"
|
||||
SELECT
|
||||
OrderID,
|
||||
OrderUUID,
|
||||
OrderUserID,
|
||||
OrderBusinessID,
|
||||
OrderBusinessDeliveryMultiplier,
|
||||
OrderTypeID,
|
||||
OrderDeliveryFee,
|
||||
OrderStatusID,
|
||||
OrderAddressID,
|
||||
OrderPaymentID,
|
||||
OrderRemarks,
|
||||
OrderAddedOn,
|
||||
OrderLastEditedOn,
|
||||
OrderSubmittedOn,
|
||||
OrderServicePointID
|
||||
FROM Orders
|
||||
WHERE OrderID = ?
|
||||
LIMIT 1
|
||||
",
|
||||
[ { value = arguments.OrderID, cfsqltype = "cf_sql_integer" } ],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfif qOrder.recordCount EQ 0>
|
||||
<cfreturn { "OK": false, "ERROR": "not_found", "MESSAGE": "Order not found", "DETAIL": "" }>
|
||||
</cfif>
|
||||
|
||||
<cfset out.Order = {
|
||||
"OrderID": qOrder.OrderID,
|
||||
"OrderUUID": qOrder.OrderUUID,
|
||||
"OrderUserID": qOrder.OrderUserID,
|
||||
"OrderBusinessID": qOrder.OrderBusinessID,
|
||||
"OrderBusinessDeliveryMultiplier": qOrder.OrderBusinessDeliveryMultiplier,
|
||||
"OrderTypeID": qOrder.OrderTypeID,
|
||||
"OrderDeliveryFee": qOrder.OrderDeliveryFee,
|
||||
"OrderStatusID": qOrder.OrderStatusID,
|
||||
"OrderAddressID": qOrder.OrderAddressID,
|
||||
"OrderPaymentID": qOrder.OrderPaymentID,
|
||||
"OrderRemarks": qOrder.OrderRemarks,
|
||||
"OrderAddedOn": qOrder.OrderAddedOn,
|
||||
"OrderLastEditedOn": qOrder.OrderLastEditedOn,
|
||||
"OrderSubmittedOn": qOrder.OrderSubmittedOn,
|
||||
"OrderServicePointID": qOrder.OrderServicePointID
|
||||
}>
|
||||
|
||||
<cfset var qLI = queryExecute(
|
||||
"
|
||||
SELECT
|
||||
OrderLineItemID,
|
||||
OrderLineItemParentOrderLineItemID,
|
||||
OrderLineItemOrderID,
|
||||
OrderLineItemItemID,
|
||||
OrderLineItemStatusID,
|
||||
OrderLineItemPrice,
|
||||
OrderLineItemQuantity,
|
||||
OrderLineItemRemark,
|
||||
OrderLineItemIsDeleted,
|
||||
OrderLineItemAddedOn
|
||||
FROM OrderLineItems
|
||||
WHERE OrderLineItemOrderID = ?
|
||||
ORDER BY OrderLineItemID
|
||||
",
|
||||
[ { value = arguments.OrderID, cfsqltype = "cf_sql_integer" } ],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfset var rows = []>
|
||||
<cfloop query="qLI">
|
||||
<cfset arrayAppend(rows, {
|
||||
"OrderLineItemID": qLI.OrderLineItemID,
|
||||
"OrderLineItemParentOrderLineItemID": qLI.OrderLineItemParentOrderLineItemID,
|
||||
"OrderLineItemOrderID": qLI.OrderLineItemOrderID,
|
||||
"OrderLineItemItemID": qLI.OrderLineItemItemID,
|
||||
"OrderLineItemStatusID": qLI.OrderLineItemStatusID,
|
||||
"OrderLineItemPrice": qLI.OrderLineItemPrice,
|
||||
"OrderLineItemQuantity": qLI.OrderLineItemQuantity,
|
||||
"OrderLineItemRemark": qLI.OrderLineItemRemark,
|
||||
"OrderLineItemIsDeleted": qLI.OrderLineItemIsDeleted,
|
||||
"OrderLineItemAddedOn": qLI.OrderLineItemAddedOn
|
||||
})>
|
||||
</cfloop>
|
||||
|
||||
<cfset out.OrderLineItems = rows>
|
||||
<cfset out.OK = true>
|
||||
<cfset out.ERROR = "">
|
||||
<cfreturn out>
|
||||
</cffunction>
|
||||
|
||||
<cfset data = readJsonBody()>
|
||||
|
||||
<cfset BusinessID = val( structKeyExists(data,"BusinessID") ? data.BusinessID : 0 )>
|
||||
<cfset OrderServicePointID = val( structKeyExists(data,"OrderServicePointID") ? data.OrderServicePointID : 0 )>
|
||||
<cfset OrderTypeID = val( structKeyExists(data,"OrderTypeID") ? data.OrderTypeID : 0 )>
|
||||
<cfset OrderUserID = val( structKeyExists(data,"OrderUserID") ? data.OrderUserID : 0 )>
|
||||
|
||||
<cfif BusinessID LTE 0 OR OrderServicePointID LTE 0 OR OrderTypeID NEQ 1 OR OrderUserID LTE 0>
|
||||
<cfset apiAbort({
|
||||
"OK": false,
|
||||
"ERROR": "missing_params",
|
||||
"MESSAGE": "BusinessID, OrderServicePointID, OrderTypeID=1, and OrderUserID are required.",
|
||||
"DETAIL": ""
|
||||
})>
|
||||
</cfif>
|
||||
|
||||
<cftry>
|
||||
<!--- Find existing cart (OrderStatusID=0 assumed cart) --->
|
||||
<cfset qFind = queryExecute(
|
||||
"
|
||||
SELECT OrderID
|
||||
FROM Orders
|
||||
WHERE OrderUserID = ?
|
||||
AND OrderBusinessID = ?
|
||||
AND OrderTypeID = 1
|
||||
AND OrderStatusID = 0
|
||||
AND OrderServicePointID = ?
|
||||
ORDER BY OrderID DESC
|
||||
LIMIT 1
|
||||
",
|
||||
[
|
||||
{ value = OrderUserID, cfsqltype = "cf_sql_integer" },
|
||||
{ value = BusinessID, cfsqltype = "cf_sql_integer" },
|
||||
{ value = OrderServicePointID, cfsqltype = "cf_sql_integer" }
|
||||
],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfif qFind.recordCount GT 0>
|
||||
<cfset payload = loadCartPayload(qFind.OrderID)>
|
||||
<cfset apiAbort(payload)>
|
||||
</cfif>
|
||||
|
||||
<!--- Create new cart order --->
|
||||
<cfset qBiz = queryExecute(
|
||||
"
|
||||
SELECT BusinessDeliveryMultiplier, BusinessDeliveryFlatFee
|
||||
FROM Businesses
|
||||
WHERE BusinessID = ?
|
||||
LIMIT 1
|
||||
",
|
||||
[ { value = BusinessID, cfsqltype = "cf_sql_integer" } ],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfif qBiz.recordCount EQ 0>
|
||||
<cfset apiAbort({ "OK": false, "ERROR": "bad_business", "MESSAGE": "Business not found", "DETAIL": "" })>
|
||||
</cfif>
|
||||
|
||||
<cfset nowDt = now()>
|
||||
<cfset newUUID = createUUID()>
|
||||
|
||||
<!--- Generate new OrderID (table is not auto-inc in SSOT) --->
|
||||
<cfset qNext = queryExecute(
|
||||
"SELECT IFNULL(MAX(OrderID),0) + 1 AS NextID FROM Orders",
|
||||
[],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
<cfset NewOrderID = qNext.NextID>
|
||||
|
||||
<cfset queryExecute(
|
||||
"
|
||||
INSERT INTO Orders (
|
||||
OrderID,
|
||||
OrderUUID,
|
||||
OrderUserID,
|
||||
OrderBusinessID,
|
||||
OrderBusinessDeliveryMultiplier,
|
||||
OrderTypeID,
|
||||
OrderDeliveryFee,
|
||||
OrderStatusID,
|
||||
OrderAddressID,
|
||||
OrderPaymentID,
|
||||
OrderRemarks,
|
||||
OrderAddedOn,
|
||||
OrderLastEditedOn,
|
||||
OrderSubmittedOn,
|
||||
OrderServicePointID
|
||||
) VALUES (
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
1,
|
||||
?,
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
?,
|
||||
?,
|
||||
NULL,
|
||||
?
|
||||
)
|
||||
",
|
||||
[
|
||||
{ value = NewOrderID, cfsqltype = "cf_sql_integer" },
|
||||
{ value = newUUID, cfsqltype = "cf_sql_varchar" },
|
||||
{ value = OrderUserID, cfsqltype = "cf_sql_integer" },
|
||||
{ value = BusinessID, cfsqltype = "cf_sql_integer" },
|
||||
{ value = qBiz.BusinessDeliveryMultiplier, cfsqltype = "cf_sql_decimal" },
|
||||
{ value = qBiz.BusinessDeliveryFlatFee, cfsqltype = "cf_sql_decimal" },
|
||||
{ value = nowDt, cfsqltype = "cf_sql_timestamp" },
|
||||
{ value = nowDt, cfsqltype = "cf_sql_timestamp" },
|
||||
{ value = OrderServicePointID, cfsqltype = "cf_sql_integer" }
|
||||
],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<!--- Per your rule: OrderID is determined by selecting highest after creation --->
|
||||
<cfset qLatest = queryExecute(
|
||||
"SELECT MAX(OrderID) AS OrderID FROM Orders",
|
||||
[],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
<cfset FinalOrderID = qLatest.OrderID>
|
||||
|
||||
<cfset payload = loadCartPayload(FinalOrderID)>
|
||||
<cfset apiAbort(payload)>
|
||||
|
||||
<cfcatch>
|
||||
<cfset apiAbort({
|
||||
"OK": false,
|
||||
"ERROR": "server_error",
|
||||
"MESSAGE": "DB error creating cart",
|
||||
"DETAIL": cfcatch.message
|
||||
})>
|
||||
</cfcatch>
|
||||
</cftry>
|
||||
399
api/orders/setLineItem.cfm
Normal file
399
api/orders/setLineItem.cfm
Normal file
|
|
@ -0,0 +1,399 @@
|
|||
<cfsetting showdebugoutput="false">
|
||||
<cfsetting enablecfoutputonly="true">
|
||||
|
||||
<cffunction name="readJsonBody" access="public" returntype="struct" output="false">
|
||||
<cfset var raw = getHttpRequestData().content>
|
||||
<cfif isNull(raw) OR len(trim(raw)) EQ 0>
|
||||
<cfreturn {}>
|
||||
</cfif>
|
||||
<cftry>
|
||||
<cfset var data = deserializeJSON(raw)>
|
||||
<cfif isStruct(data)>
|
||||
<cfreturn data>
|
||||
<cfelse>
|
||||
<cfreturn {}>
|
||||
</cfif>
|
||||
<cfcatch>
|
||||
<cfreturn {}>
|
||||
</cfcatch>
|
||||
</cftry>
|
||||
</cffunction>
|
||||
|
||||
<cffunction name="apiAbort" access="public" returntype="void" output="true">
|
||||
<cfargument name="payload" type="struct" required="true">
|
||||
<cfcontent type="application/json; charset=utf-8">
|
||||
<cfoutput>#serializeJSON(arguments.payload)#</cfoutput>
|
||||
<cfabort>
|
||||
</cffunction>
|
||||
|
||||
<cffunction name="nextId" access="public" returntype="numeric" output="false">
|
||||
<cfargument name="tableName" type="string" required="true">
|
||||
<cfargument name="idField" type="string" required="true">
|
||||
|
||||
<cfset var q = queryExecute(
|
||||
"SELECT IFNULL(MAX(#arguments.idField#),0) + 1 AS NextID FROM #arguments.tableName#",
|
||||
[],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
<cfreturn q.NextID>
|
||||
</cffunction>
|
||||
|
||||
<cffunction name="attachDefaultChildren" access="public" returntype="void" output="false">
|
||||
<cfargument name="OrderID" type="numeric" required="true">
|
||||
<cfargument name="ParentLineItemID" type="numeric" required="true">
|
||||
<cfargument name="ParentItemID" type="numeric" required="true">
|
||||
|
||||
<!--- Find immediate children where checked by default --->
|
||||
<cfset var qKids = queryExecute(
|
||||
"
|
||||
SELECT ItemID, ItemPrice
|
||||
FROM Items
|
||||
WHERE ItemParentItemID = ?
|
||||
AND ItemIsCheckedByDefault = 1
|
||||
AND ItemIsActive = b'1'
|
||||
ORDER BY ItemSortOrder, ItemID
|
||||
",
|
||||
[ { value = arguments.ParentItemID, cfsqltype = "cf_sql_integer" } ],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfloop query="qKids">
|
||||
<!--- If existing, undelete; else insert new --->
|
||||
<cfset var qExisting = queryExecute(
|
||||
"
|
||||
SELECT OrderLineItemID
|
||||
FROM OrderLineItems
|
||||
WHERE OrderLineItemOrderID = ?
|
||||
AND OrderLineItemParentOrderLineItemID = ?
|
||||
AND OrderLineItemItemID = ?
|
||||
LIMIT 1
|
||||
",
|
||||
[
|
||||
{ value = arguments.OrderID, cfsqltype = "cf_sql_integer" },
|
||||
{ value = arguments.ParentLineItemID, cfsqltype = "cf_sql_integer" },
|
||||
{ value = qKids.ItemID, cfsqltype = "cf_sql_integer" }
|
||||
],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfif qExisting.recordCount GT 0>
|
||||
<cfset queryExecute(
|
||||
"
|
||||
UPDATE OrderLineItems
|
||||
SET OrderLineItemIsDeleted = b'0'
|
||||
WHERE OrderLineItemID = ?
|
||||
",
|
||||
[ { value = qExisting.OrderLineItemID, cfsqltype = "cf_sql_integer" } ],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
<cfset attachDefaultChildren(arguments.OrderID, qExisting.OrderLineItemID, qKids.ItemID)>
|
||||
<cfelse>
|
||||
<cfset var NewLIID = nextId("OrderLineItems","OrderLineItemID")>
|
||||
<cfset queryExecute(
|
||||
"
|
||||
INSERT INTO OrderLineItems (
|
||||
OrderLineItemID,
|
||||
OrderLineItemParentOrderLineItemID,
|
||||
OrderLineItemOrderID,
|
||||
OrderLineItemItemID,
|
||||
OrderLineItemStatusID,
|
||||
OrderLineItemPrice,
|
||||
OrderLineItemQuantity,
|
||||
OrderLineItemRemark,
|
||||
OrderLineItemIsDeleted,
|
||||
OrderLineItemAddedOn
|
||||
) VALUES (
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
0,
|
||||
?,
|
||||
1,
|
||||
NULL,
|
||||
b'0',
|
||||
?
|
||||
)
|
||||
",
|
||||
[
|
||||
{ value = NewLIID, cfsqltype = "cf_sql_integer" },
|
||||
{ value = arguments.ParentLineItemID, cfsqltype = "cf_sql_integer" },
|
||||
{ value = arguments.OrderID, cfsqltype = "cf_sql_integer" },
|
||||
{ value = qKids.ItemID, cfsqltype = "cf_sql_integer" },
|
||||
{ value = qKids.ItemPrice, cfsqltype = "cf_sql_decimal" },
|
||||
{ value = now(), cfsqltype = "cf_sql_timestamp" }
|
||||
],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
<cfset attachDefaultChildren(arguments.OrderID, NewLIID, qKids.ItemID)>
|
||||
</cfif>
|
||||
</cfloop>
|
||||
</cffunction>
|
||||
|
||||
<cffunction name="loadCartPayload" access="public" returntype="struct" output="false">
|
||||
<cfargument name="OrderID" type="numeric" required="true">
|
||||
|
||||
<cfset var out = {}>
|
||||
<cfset var qOrder = queryExecute(
|
||||
"
|
||||
SELECT
|
||||
OrderID,
|
||||
OrderUUID,
|
||||
OrderUserID,
|
||||
OrderBusinessID,
|
||||
OrderBusinessDeliveryMultiplier,
|
||||
OrderTypeID,
|
||||
OrderDeliveryFee,
|
||||
OrderStatusID,
|
||||
OrderAddressID,
|
||||
OrderPaymentID,
|
||||
OrderRemarks,
|
||||
OrderAddedOn,
|
||||
OrderLastEditedOn,
|
||||
OrderSubmittedOn,
|
||||
OrderServicePointID
|
||||
FROM Orders
|
||||
WHERE OrderID = ?
|
||||
LIMIT 1
|
||||
",
|
||||
[ { value = arguments.OrderID, cfsqltype = "cf_sql_integer" } ],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfif qOrder.recordCount EQ 0>
|
||||
<cfreturn { "OK": false, "ERROR": "not_found", "MESSAGE": "Order not found", "DETAIL": "" }>
|
||||
</cfif>
|
||||
|
||||
<cfset out.Order = {
|
||||
"OrderID": qOrder.OrderID,
|
||||
"OrderUUID": qOrder.OrderUUID,
|
||||
"OrderUserID": qOrder.OrderUserID,
|
||||
"OrderBusinessID": qOrder.OrderBusinessID,
|
||||
"OrderBusinessDeliveryMultiplier": qOrder.OrderBusinessDeliveryMultiplier,
|
||||
"OrderTypeID": qOrder.OrderTypeID,
|
||||
"OrderDeliveryFee": qOrder.OrderDeliveryFee,
|
||||
"OrderStatusID": qOrder.OrderStatusID,
|
||||
"OrderAddressID": qOrder.OrderAddressID,
|
||||
"OrderPaymentID": qOrder.OrderPaymentID,
|
||||
"OrderRemarks": qOrder.OrderRemarks,
|
||||
"OrderAddedOn": qOrder.OrderAddedOn,
|
||||
"OrderLastEditedOn": qOrder.OrderLastEditedOn,
|
||||
"OrderSubmittedOn": qOrder.OrderSubmittedOn,
|
||||
"OrderServicePointID": qOrder.OrderServicePointID
|
||||
}>
|
||||
|
||||
<cfset var qLI = queryExecute(
|
||||
"
|
||||
SELECT
|
||||
OrderLineItemID,
|
||||
OrderLineItemParentOrderLineItemID,
|
||||
OrderLineItemOrderID,
|
||||
OrderLineItemItemID,
|
||||
OrderLineItemStatusID,
|
||||
OrderLineItemPrice,
|
||||
OrderLineItemQuantity,
|
||||
OrderLineItemRemark,
|
||||
OrderLineItemIsDeleted,
|
||||
OrderLineItemAddedOn
|
||||
FROM OrderLineItems
|
||||
WHERE OrderLineItemOrderID = ?
|
||||
ORDER BY OrderLineItemID
|
||||
",
|
||||
[ { value = arguments.OrderID, cfsqltype = "cf_sql_integer" } ],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfset var rows = []>
|
||||
<cfloop query="qLI">
|
||||
<cfset arrayAppend(rows, {
|
||||
"OrderLineItemID": qLI.OrderLineItemID,
|
||||
"OrderLineItemParentOrderLineItemID": qLI.OrderLineItemParentOrderLineItemID,
|
||||
"OrderLineItemOrderID": qLI.OrderLineItemOrderID,
|
||||
"OrderLineItemItemID": qLI.OrderLineItemItemID,
|
||||
"OrderLineItemStatusID": qLI.OrderLineItemStatusID,
|
||||
"OrderLineItemPrice": qLI.OrderLineItemPrice,
|
||||
"OrderLineItemQuantity": qLI.OrderLineItemQuantity,
|
||||
"OrderLineItemRemark": qLI.OrderLineItemRemark,
|
||||
"OrderLineItemIsDeleted": qLI.OrderLineItemIsDeleted,
|
||||
"OrderLineItemAddedOn": qLI.OrderLineItemAddedOn
|
||||
})>
|
||||
</cfloop>
|
||||
|
||||
<cfset out.OrderLineItems = rows>
|
||||
<cfset out.OK = true>
|
||||
<cfset out.ERROR = "">
|
||||
<cfreturn out>
|
||||
</cffunction>
|
||||
|
||||
<cfset data = readJsonBody()>
|
||||
|
||||
<cfset OrderID = val( structKeyExists(data,"OrderID") ? data.OrderID : 0 )>
|
||||
<cfset ParentLineItemID = val( structKeyExists(data,"ParentOrderLineItemID") ? data.ParentOrderLineItemID : 0 )>
|
||||
<cfset ItemID = val( structKeyExists(data,"ItemID") ? data.ItemID : 0 )>
|
||||
<cfset IsSelected = false>
|
||||
<cfif structKeyExists(data, "IsSelected")>
|
||||
<cfset IsSelected = (data.IsSelected EQ true OR data.IsSelected EQ 1 OR (isSimpleValue(data.IsSelected) AND lcase(toString(data.IsSelected)) EQ "true"))>
|
||||
</cfif>
|
||||
<cfset Quantity = structKeyExists(data,"Quantity") ? val(data.Quantity) : 0>
|
||||
<cfset Remark = structKeyExists(data,"Remark") ? toString(data.Remark) : "">
|
||||
|
||||
<cfif OrderID LTE 0 OR ItemID LTE 0>
|
||||
<cfset apiAbort({ "OK": false, "ERROR": "missing_params", "MESSAGE": "OrderID and ItemID are required.", "DETAIL": "" })>
|
||||
</cfif>
|
||||
|
||||
<cftry>
|
||||
<!--- Load item price --->
|
||||
<cfset qItem = queryExecute(
|
||||
"
|
||||
SELECT ItemID, ItemPrice, ItemParentItemID, ItemIsActive
|
||||
FROM Items
|
||||
WHERE ItemID = ?
|
||||
LIMIT 1
|
||||
",
|
||||
[ { value = ItemID, cfsqltype = "cf_sql_integer" } ],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfif qItem.recordCount EQ 0 OR qItem.ItemIsActive NEQ true>
|
||||
<cfset apiAbort({ "OK": false, "ERROR": "bad_item", "MESSAGE": "Item not found or inactive.", "DETAIL": "" })>
|
||||
</cfif>
|
||||
|
||||
<!--- Root vs modifier rules --->
|
||||
<cfif ParentLineItemID EQ 0>
|
||||
<!--- Root item quantity required when selecting --->
|
||||
<cfif IsSelected AND Quantity LTE 0>
|
||||
<cfset apiAbort({ "OK": false, "ERROR": "bad_quantity", "MESSAGE": "Root line items require Quantity > 0.", "DETAIL": "" })>
|
||||
</cfif>
|
||||
<cfelse>
|
||||
<!--- Modifier quantity is implicitly tied => force 1 when selecting --->
|
||||
<cfif IsSelected>
|
||||
<cfset Quantity = 1>
|
||||
<cfelse>
|
||||
<cfset Quantity = 1>
|
||||
</cfif>
|
||||
</cfif>
|
||||
|
||||
<!--- Find existing line item (by order, parent LI, item) --->
|
||||
<cfset qExisting = queryExecute(
|
||||
"
|
||||
SELECT OrderLineItemID
|
||||
FROM OrderLineItems
|
||||
WHERE OrderLineItemOrderID = ?
|
||||
AND OrderLineItemParentOrderLineItemID = ?
|
||||
AND OrderLineItemItemID = ?
|
||||
LIMIT 1
|
||||
",
|
||||
[
|
||||
{ value = OrderID, cfsqltype = "cf_sql_integer" },
|
||||
{ value = ParentLineItemID, cfsqltype = "cf_sql_integer" },
|
||||
{ value = ItemID, cfsqltype = "cf_sql_integer" }
|
||||
],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfif qExisting.recordCount GT 0>
|
||||
<!--- Update existing --->
|
||||
<cfif IsSelected>
|
||||
<cfset queryExecute(
|
||||
"
|
||||
UPDATE OrderLineItems
|
||||
SET
|
||||
OrderLineItemIsDeleted = b'0',
|
||||
OrderLineItemQuantity = ?,
|
||||
OrderLineItemPrice = ?,
|
||||
OrderLineItemRemark = ?,
|
||||
OrderLineItemStatusID = 0
|
||||
WHERE OrderLineItemID = ?
|
||||
",
|
||||
[
|
||||
{ value = Quantity, cfsqltype = "cf_sql_integer" },
|
||||
{ value = qItem.ItemPrice, cfsqltype = "cf_sql_decimal" },
|
||||
{ value = (len(trim(Remark)) EQ 0 ? javacast("null","") : Remark), cfsqltype = "cf_sql_varchar", null = (len(trim(Remark)) EQ 0) },
|
||||
{ value = qExisting.OrderLineItemID, cfsqltype = "cf_sql_integer" }
|
||||
],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<!--- Attach default children for this node (recursively) --->
|
||||
<cfset attachDefaultChildren(OrderID, qExisting.OrderLineItemID, ItemID)>
|
||||
<cfelse>
|
||||
<cfset queryExecute(
|
||||
"
|
||||
UPDATE OrderLineItems
|
||||
SET OrderLineItemIsDeleted = b'1'
|
||||
WHERE OrderLineItemID = ?
|
||||
",
|
||||
[ { value = qExisting.OrderLineItemID, cfsqltype = "cf_sql_integer" } ],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
</cfif>
|
||||
<cfelse>
|
||||
<!--- Insert new if selecting, otherwise no-op --->
|
||||
<cfif IsSelected>
|
||||
<cfset NewLIID = nextId("OrderLineItems","OrderLineItemID")>
|
||||
|
||||
<cfset queryExecute(
|
||||
"
|
||||
INSERT INTO OrderLineItems (
|
||||
OrderLineItemID,
|
||||
OrderLineItemParentOrderLineItemID,
|
||||
OrderLineItemOrderID,
|
||||
OrderLineItemItemID,
|
||||
OrderLineItemStatusID,
|
||||
OrderLineItemPrice,
|
||||
OrderLineItemQuantity,
|
||||
OrderLineItemRemark,
|
||||
OrderLineItemIsDeleted,
|
||||
OrderLineItemAddedOn
|
||||
) VALUES (
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
0,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
b'0',
|
||||
?
|
||||
)
|
||||
",
|
||||
[
|
||||
{ value = NewLIID, cfsqltype = "cf_sql_integer" },
|
||||
{ value = ParentLineItemID, cfsqltype = "cf_sql_integer" },
|
||||
{ value = OrderID, cfsqltype = "cf_sql_integer" },
|
||||
{ value = ItemID, cfsqltype = "cf_sql_integer" },
|
||||
{ value = qItem.ItemPrice, cfsqltype = "cf_sql_decimal" },
|
||||
{ value = (ParentLineItemID EQ 0 ? Quantity : 1), cfsqltype = "cf_sql_integer" },
|
||||
{ value = (len(trim(Remark)) EQ 0 ? javacast("null","") : Remark), cfsqltype = "cf_sql_varchar", null = (len(trim(Remark)) EQ 0) },
|
||||
{ value = now(), cfsqltype = "cf_sql_timestamp" }
|
||||
],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfset attachDefaultChildren(OrderID, NewLIID, ItemID)>
|
||||
</cfif>
|
||||
</cfif>
|
||||
|
||||
<!--- Touch order last edited --->
|
||||
<cfset queryExecute(
|
||||
"UPDATE Orders SET OrderLastEditedOn = ? WHERE OrderID = ?",
|
||||
[
|
||||
{ value = now(), cfsqltype = "cf_sql_timestamp" },
|
||||
{ value = OrderID, cfsqltype = "cf_sql_integer" }
|
||||
],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfset payload = loadCartPayload(OrderID)>
|
||||
<cfset apiAbort(payload)>
|
||||
|
||||
<cfcatch>
|
||||
<cfset apiAbort({
|
||||
"OK": false,
|
||||
"ERROR": "server_error",
|
||||
"MESSAGE": "DB error setting line item",
|
||||
"DETAIL": cfcatch.message
|
||||
})>
|
||||
</cfcatch>
|
||||
</cftry>
|
||||
261
api/orders/submit.cfm
Normal file
261
api/orders/submit.cfm
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
<cfsetting showdebugoutput="false">
|
||||
<cfsetting enablecfoutputonly="true">
|
||||
|
||||
<cffunction name="readJsonBody" access="public" returntype="struct" output="false">
|
||||
<cfset var raw = getHttpRequestData().content>
|
||||
<cfif isNull(raw) OR len(trim(raw)) EQ 0>
|
||||
<cfreturn {}>
|
||||
</cfif>
|
||||
<cftry>
|
||||
<cfset var data = deserializeJSON(raw)>
|
||||
<cfif isStruct(data)>
|
||||
<cfreturn data>
|
||||
<cfelse>
|
||||
<cfreturn {}>
|
||||
</cfif>
|
||||
<cfcatch>
|
||||
<cfreturn {}>
|
||||
</cfcatch>
|
||||
</cftry>
|
||||
</cffunction>
|
||||
|
||||
<cffunction name="apiAbort" access="public" returntype="void" output="true">
|
||||
<cfargument name="payload" type="struct" required="true">
|
||||
<cfcontent type="application/json; charset=utf-8">
|
||||
<cfoutput>#serializeJSON(arguments.payload)#</cfoutput>
|
||||
<cfabort>
|
||||
</cffunction>
|
||||
|
||||
<cffunction name="buildLineItemsGraph" access="public" returntype="struct" output="false">
|
||||
<cfargument name="OrderID" type="numeric" required="true">
|
||||
|
||||
<cfset var out = {}>
|
||||
<cfset out.items = {}> <!--- lineItemId -> struct --->
|
||||
<cfset out.children = {}> <!--- parentLineItemId -> array(lineItemId) --->
|
||||
<cfset out.itemMeta = {}> <!--- ItemID -> struct(meta) --->
|
||||
|
||||
<cfset var qLI = queryExecute(
|
||||
"
|
||||
SELECT
|
||||
OrderLineItemID,
|
||||
OrderLineItemParentOrderLineItemID,
|
||||
OrderLineItemItemID,
|
||||
OrderLineItemIsDeleted
|
||||
FROM OrderLineItems
|
||||
WHERE OrderLineItemOrderID = ?
|
||||
ORDER BY OrderLineItemID
|
||||
",
|
||||
[ { value = arguments.OrderID, cfsqltype = "cf_sql_integer" } ],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfif qLI.recordCount EQ 0>
|
||||
<cfreturn out>
|
||||
</cfif>
|
||||
|
||||
<cfset var itemIds = []>
|
||||
<cfloop query="qLI">
|
||||
<cfset out.items[qLI.OrderLineItemID] = {
|
||||
"id": qLI.OrderLineItemID,
|
||||
"parentId": qLI.OrderLineItemParentOrderLineItemID,
|
||||
"itemId": qLI.OrderLineItemItemID,
|
||||
"isDeleted": (qLI.OrderLineItemIsDeleted EQ true)
|
||||
}>
|
||||
<cfif NOT structKeyExists(out.children, qLI.OrderLineItemParentOrderLineItemID)>
|
||||
<cfset out.children[qLI.OrderLineItemParentOrderLineItemID] = []>
|
||||
</cfif>
|
||||
<cfset arrayAppend(out.children[qLI.OrderLineItemParentOrderLineItemID], qLI.OrderLineItemID)>
|
||||
<cfset arrayAppend(itemIds, qLI.OrderLineItemItemID)>
|
||||
</cfloop>
|
||||
|
||||
<!--- Load meta for involved items --->
|
||||
<cfset var uniq = {} >
|
||||
<cfloop array="#itemIds#" index="iid">
|
||||
<cfset uniq[iid] = true>
|
||||
</cfloop>
|
||||
<cfset var uniqIds = structKeyArray(uniq)>
|
||||
<cfif arrayLen(uniqIds) GT 0>
|
||||
<cfset var inList = arrayToList(uniqIds)>
|
||||
<cfset var qMeta = queryExecute(
|
||||
"
|
||||
SELECT
|
||||
ItemID,
|
||||
ItemRequiresChildSelection,
|
||||
ItemMaxNumSelectionReq
|
||||
FROM Items
|
||||
WHERE ItemID IN (#inList#)
|
||||
",
|
||||
[],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfloop query="qMeta">
|
||||
<cfset out.itemMeta[qMeta.ItemID] = {
|
||||
"requires": qMeta.ItemRequiresChildSelection,
|
||||
"maxSel": qMeta.ItemMaxNumSelectionReq
|
||||
}>
|
||||
</cfloop>
|
||||
</cfif>
|
||||
|
||||
<cfreturn out>
|
||||
</cffunction>
|
||||
|
||||
<cffunction name="hasSelectedDescendant" access="public" returntype="boolean" output="false">
|
||||
<cfargument name="graph" type="struct" required="true">
|
||||
<cfargument name="lineItemId" type="numeric" required="true">
|
||||
|
||||
<cfset var stack = []>
|
||||
<cfif structKeyExists(arguments.graph.children, arguments.lineItemId)>
|
||||
<cfset stack = duplicate(arguments.graph.children[arguments.lineItemId])>
|
||||
</cfif>
|
||||
|
||||
<cfloop condition="arrayLen(stack) GT 0">
|
||||
<cfset var id = stack[arrayLen(stack)]>
|
||||
<cfset arrayDeleteAt(stack, arrayLen(stack))>
|
||||
|
||||
<cfif structKeyExists(arguments.graph.items, id)>
|
||||
<cfset var node = arguments.graph.items[id]>
|
||||
<cfif NOT node.isDeleted>
|
||||
<cfreturn true>
|
||||
</cfif>
|
||||
<cfif structKeyExists(arguments.graph.children, id)>
|
||||
<cfset var kids = arguments.graph.children[id]>
|
||||
<cfloop array="#kids#" index="kidId">
|
||||
<cfset arrayAppend(stack, kidId)>
|
||||
</cfloop>
|
||||
</cfif>
|
||||
</cfif>
|
||||
</cfloop>
|
||||
|
||||
<cfreturn false>
|
||||
</cffunction>
|
||||
|
||||
<cfset data = readJsonBody()>
|
||||
<cfset OrderID = val( structKeyExists(data,"OrderID") ? data.OrderID : 0 )>
|
||||
|
||||
<cfif OrderID LTE 0>
|
||||
<cfset apiAbort({ "OK": false, "ERROR": "missing_orderid", "MESSAGE": "OrderID is required.", "DETAIL": "" })>
|
||||
</cfif>
|
||||
|
||||
<cftry>
|
||||
<cfset qOrder = queryExecute(
|
||||
"
|
||||
SELECT OrderID, OrderStatusID, OrderTypeID
|
||||
FROM Orders
|
||||
WHERE OrderID = ?
|
||||
LIMIT 1
|
||||
",
|
||||
[ { value = OrderID, cfsqltype = "cf_sql_integer" } ],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfif qOrder.recordCount EQ 0>
|
||||
<cfset apiAbort({ "OK": false, "ERROR": "not_found", "MESSAGE": "Order not found.", "DETAIL": "" })>
|
||||
</cfif>
|
||||
|
||||
<cfif qOrder.OrderStatusID NEQ 0>
|
||||
<cfset apiAbort({ "OK": false, "ERROR": "bad_state", "MESSAGE": "Order is not in cart state.", "DETAIL": "" })>
|
||||
</cfif>
|
||||
|
||||
<cfif qOrder.OrderTypeID NEQ 1>
|
||||
<cfset apiAbort({ "OK": false, "ERROR": "bad_type", "MESSAGE": "Only dine-in orders (OrderTypeID=1) are supported in this MVP submit endpoint.", "DETAIL": "" })>
|
||||
</cfif>
|
||||
|
||||
<!--- Must have at least one non-deleted root line item --->
|
||||
<cfset qRoots = queryExecute(
|
||||
"
|
||||
SELECT COUNT(*) AS Cnt
|
||||
FROM OrderLineItems
|
||||
WHERE OrderLineItemOrderID = ?
|
||||
AND OrderLineItemParentOrderLineItemID = 0
|
||||
AND OrderLineItemIsDeleted = b'0'
|
||||
",
|
||||
[ { value = OrderID, cfsqltype = "cf_sql_integer" } ],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfif qRoots.Cnt LTE 0>
|
||||
<cfset apiAbort({ "OK": false, "ERROR": "empty_order", "MESSAGE": "Order has no items.", "DETAIL": "" })>
|
||||
</cfif>
|
||||
|
||||
<!--- Validate requires-descendant and max immediate selections --->
|
||||
<cfset graph = buildLineItemsGraph(OrderID)>
|
||||
|
||||
<!--- Loop all non-deleted nodes and validate --->
|
||||
<cfloop collection="#graph.items#" item="k">
|
||||
<cfset node = graph.items[k]>
|
||||
<cfif node.isDeleted>
|
||||
<!--- skip deleted nodes --->
|
||||
<cfcontinue>
|
||||
</cfif>
|
||||
|
||||
<cfset meta = structKeyExists(graph.itemMeta, node.itemId) ? graph.itemMeta[node.itemId] : { "requires": 0, "maxSel": 0 }>
|
||||
|
||||
<!--- max immediate selections --->
|
||||
<cfset maxSel = val(meta.maxSel)>
|
||||
<cfif maxSel GT 0>
|
||||
<cfset selCount = 0>
|
||||
<cfif structKeyExists(graph.children, node.id)>
|
||||
<cfset kids = graph.children[node.id]>
|
||||
<cfloop array="#kids#" index="kidId">
|
||||
<cfif structKeyExists(graph.items, kidId)>
|
||||
<cfset kidNode = graph.items[kidId]>
|
||||
<cfif NOT kidNode.isDeleted>
|
||||
<cfset selCount = selCount + 1>
|
||||
</cfif>
|
||||
</cfif>
|
||||
</cfloop>
|
||||
</cfif>
|
||||
|
||||
<cfif selCount GT maxSel>
|
||||
<cfset apiAbort({
|
||||
"OK": false,
|
||||
"ERROR": "max_selection_exceeded",
|
||||
"MESSAGE": "Too many selections under a modifier group.",
|
||||
"DETAIL": "LineItemID #node.id# has #selCount# immediate children selected; max is #maxSel#."
|
||||
})>
|
||||
</cfif>
|
||||
</cfif>
|
||||
|
||||
<!--- requires descendant selection --->
|
||||
<cfif val(meta.requires) EQ 1>
|
||||
<cfif NOT hasSelectedDescendant(graph, node.id)>
|
||||
<cfset apiAbort({
|
||||
"OK": false,
|
||||
"ERROR": "required_selection_missing",
|
||||
"MESSAGE": "A required modifier selection is missing.",
|
||||
"DETAIL": "LineItemID #node.id# requires at least one descendant selection."
|
||||
})>
|
||||
</cfif>
|
||||
</cfif>
|
||||
</cfloop>
|
||||
|
||||
<!--- Submit: mark submitted + status 1 --->
|
||||
<cfset queryExecute(
|
||||
"
|
||||
UPDATE Orders
|
||||
SET
|
||||
OrderStatusID = 1,
|
||||
OrderSubmittedOn = ?,
|
||||
OrderLastEditedOn = ?
|
||||
WHERE OrderID = ?
|
||||
",
|
||||
[
|
||||
{ value = now(), cfsqltype = "cf_sql_timestamp" },
|
||||
{ value = now(), cfsqltype = "cf_sql_timestamp" },
|
||||
{ value = OrderID, cfsqltype = "cf_sql_integer" }
|
||||
],
|
||||
{ datasource = "payfrit" }
|
||||
)>
|
||||
|
||||
<cfset apiAbort({ "OK": true, "ERROR": "", "OrderID": OrderID, "MESSAGE": "submitted" })>
|
||||
|
||||
<cfcatch>
|
||||
<cfset apiAbort({
|
||||
"OK": false,
|
||||
"ERROR": "server_error",
|
||||
"MESSAGE": "DB error submitting order",
|
||||
"DETAIL": cfcatch.message
|
||||
})>
|
||||
</cfcatch>
|
||||
</cftry>
|
||||
|
|
@ -1,137 +1,136 @@
|
|||
<cfsetting showdebugoutput="false">
|
||||
<cfsetting enablecfoutputonly="true">
|
||||
|
||||
<cfcontent type="application/json; charset=utf-8" reset="true">
|
||||
<cfheader name="Cache-Control" value="no-store">
|
||||
<cfcontent type="application/json; charset=utf-8">
|
||||
|
||||
<cfscript>
|
||||
/*
|
||||
FILE: C:\lucee\tomcat\webapps\ROOT\biz.payfrit.com\api\servicepoints\list.cfm
|
||||
|
||||
MVP:
|
||||
- PUBLIC (no login required)
|
||||
- Reads BusinessID from JSON body
|
||||
- Optional: onlyActive (default true)
|
||||
- Returns hump-case JSON:
|
||||
OK, ERROR, BusinessID, COUNT, ServicePoints
|
||||
*/
|
||||
|
||||
function jsonString(val) {
|
||||
// Lucee-safe string escaping:
|
||||
// serializeJSON("abc") -> "\"abc\"" ; strip surrounding quotes
|
||||
s = serializeJSON(toString(val));
|
||||
return mid(s, 2, len(s) - 2);
|
||||
}
|
||||
|
||||
function apiAbort(obj) {
|
||||
writeOutput('{');
|
||||
writeOutput('"OK":false');
|
||||
|
||||
if (structKeyExists(obj, "ERROR")) {
|
||||
writeOutput(',"ERROR":"' & jsonString(obj.ERROR) & '"');
|
||||
}
|
||||
if (structKeyExists(obj, "MESSAGE")) {
|
||||
writeOutput(',"MESSAGE":"' & jsonString(obj.MESSAGE) & '"');
|
||||
}
|
||||
if (structKeyExists(obj, "DETAIL")) {
|
||||
writeOutput(',"DETAIL":"' & jsonString(obj.DETAIL) & '"');
|
||||
}
|
||||
|
||||
writeOutput('}');
|
||||
function apiAbort(payload) {
|
||||
writeOutput(serializeJSON(payload));
|
||||
abort;
|
||||
}
|
||||
|
||||
function readJsonBody() {
|
||||
raw = toString(getHttpRequestData().content);
|
||||
if (isNull(raw) || len(trim(raw)) EQ 0) return {};
|
||||
// Resolve BusinessID tolerant MVP way
|
||||
bizId = 0;
|
||||
|
||||
if (structKeyExists(request, "BusinessID") && isNumeric(request.BusinessID)) {
|
||||
bizId = int(request.BusinessID);
|
||||
}
|
||||
|
||||
if (bizId LTE 0) {
|
||||
try {
|
||||
parsed = deserializeJSON(raw);
|
||||
} catch(any e) {
|
||||
apiAbort({ ERROR="bad_json", MESSAGE="Invalid JSON body" });
|
||||
raw = toString(getHttpRequestData().content);
|
||||
if (len(trim(raw))) {
|
||||
body = deserializeJSON(raw);
|
||||
if (isStruct(body) && structKeyExists(body, "BusinessID") && isNumeric(body.BusinessID)) {
|
||||
bizId = int(body.BusinessID);
|
||||
}
|
||||
}
|
||||
} catch (any e) {}
|
||||
}
|
||||
|
||||
if (bizId LTE 0 && structKeyExists(url, "BusinessID") && isNumeric(url.BusinessID)) {
|
||||
bizId = int(url.BusinessID);
|
||||
}
|
||||
|
||||
if (bizId LTE 0) {
|
||||
apiAbort({ "OK": false, "ERROR": "missing_businessid", "DETAIL": "" });
|
||||
}
|
||||
|
||||
try {
|
||||
// Detect the correct business FK column in ServicePoints
|
||||
qCols = queryExecute(
|
||||
"
|
||||
SELECT COLUMN_NAME
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'ServicePoints'
|
||||
",
|
||||
[],
|
||||
{ datasource: "payfrit" }
|
||||
);
|
||||
|
||||
cols = [];
|
||||
for (r in qCols) arrayAppend(cols, r.COLUMN_NAME);
|
||||
|
||||
// Candidates in preferred order (add more if needed)
|
||||
candidates = [
|
||||
"BusinessID",
|
||||
"BusinessId",
|
||||
"ServicePointBusinessID",
|
||||
"ServicePointsBusinessID",
|
||||
"Business_ID",
|
||||
"Business",
|
||||
"BusinessFk",
|
||||
"BusinessFK"
|
||||
];
|
||||
|
||||
bizCol = "";
|
||||
// First: exact candidate match
|
||||
for (c in candidates) {
|
||||
for (colName in cols) {
|
||||
if (lcase(colName) EQ lcase(c)) {
|
||||
bizCol = colName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (len(bizCol)) break;
|
||||
}
|
||||
|
||||
if (!isStruct(parsed)) return {};
|
||||
return parsed;
|
||||
}
|
||||
|
||||
data = readJsonBody();
|
||||
|
||||
// Require BusinessID in JSON body for public MVP endpoint
|
||||
if (!structKeyExists(data, "BusinessID") || !isNumeric(data.BusinessID) || int(data.BusinessID) LTE 0) {
|
||||
apiAbort({ ERROR="missing_businessid", MESSAGE="Body must include numeric BusinessID" });
|
||||
}
|
||||
BusinessID = int(data.BusinessID);
|
||||
|
||||
// Default: only active service points unless onlyActive is explicitly false/0
|
||||
onlyActive = true;
|
||||
if (structKeyExists(data, "onlyActive")) {
|
||||
if (isBoolean(data.onlyActive)) {
|
||||
onlyActive = data.onlyActive;
|
||||
} else if (isNumeric(data.onlyActive)) {
|
||||
onlyActive = (int(data.onlyActive) EQ 1);
|
||||
} else if (isSimpleValue(data.onlyActive)) {
|
||||
onlyActive = (lcase(trim(toString(data.onlyActive))) EQ "true");
|
||||
// Second: heuristic: any column containing "business" and "id"
|
||||
if (!len(bizCol)) {
|
||||
for (colName in cols) {
|
||||
if (findNoCase("business", colName) && findNoCase("id", colName)) {
|
||||
bizCol = colName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Datasource (use existing app config)
|
||||
dsn = "";
|
||||
if (structKeyExists(application, "datasource") && len(trim(toString(application.datasource)))) {
|
||||
dsn = trim(toString(application.datasource));
|
||||
} else if (structKeyExists(application, "dsn") && len(trim(toString(application.dsn)))) {
|
||||
dsn = trim(toString(application.dsn));
|
||||
}
|
||||
if (!len(dsn)) {
|
||||
apiAbort({ ERROR="missing_datasource", MESSAGE="application.datasource is not set" });
|
||||
}
|
||||
</cfscript>
|
||||
if (!len(bizCol)) {
|
||||
apiAbort({
|
||||
"OK": false,
|
||||
"ERROR": "schema_mismatch",
|
||||
"DETAIL": "Could not find a BusinessID-like column in ServicePoints. Available columns: " & arrayToList(cols, ", ")
|
||||
});
|
||||
}
|
||||
|
||||
<cfquery name="q" datasource="#dsn#">
|
||||
// Build SQL using detected column name (safe because it comes from INFORMATION_SCHEMA)
|
||||
sql = "
|
||||
SELECT
|
||||
ServicePointID,
|
||||
ServicePointBusinessID,
|
||||
ServicePointName,
|
||||
ServicePointTypeID,
|
||||
ServicePointCode,
|
||||
ServicePointDescription,
|
||||
ServicePointSortOrder,
|
||||
ServicePointIsActive,
|
||||
ServicePointCreatedAt,
|
||||
ServicePointUpdatedAt
|
||||
ServicePointName
|
||||
FROM ServicePoints
|
||||
WHERE ServicePointBusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#BusinessID#">
|
||||
<cfif onlyActive>
|
||||
AND ServicePointIsActive = 1
|
||||
</cfif>
|
||||
ORDER BY ServicePointSortOrder, ServicePointName, ServicePointID
|
||||
</cfquery>
|
||||
WHERE #bizCol# = ?
|
||||
ORDER BY ServicePointName
|
||||
";
|
||||
|
||||
<cfscript>
|
||||
// Manual JSON output for exact key casing
|
||||
writeOutput('{');
|
||||
writeOutput('"OK":true');
|
||||
writeOutput(',"ERROR":""');
|
||||
writeOutput(',"BusinessID":' & BusinessID);
|
||||
writeOutput(',"COUNT":' & q.recordCount);
|
||||
writeOutput(',"ServicePoints":[');
|
||||
q = queryExecute(
|
||||
sql,
|
||||
[ { value: bizId, cfsqltype: "cf_sql_integer" } ],
|
||||
{ datasource: "payfrit" }
|
||||
);
|
||||
|
||||
for (i = 1; i LTE q.recordCount; i = i + 1) {
|
||||
if (i GT 1) writeOutput(',');
|
||||
servicePoints = [];
|
||||
for (row in q) {
|
||||
arrayAppend(servicePoints, {
|
||||
"ServicePointID": row.ServicePointID,
|
||||
"ServicePointName": row.ServicePointName
|
||||
});
|
||||
}
|
||||
|
||||
writeOutput('{');
|
||||
writeOutput('"ServicePointID":' & q.ServicePointID[i]);
|
||||
writeOutput(',"BusinessID":' & q.ServicePointBusinessID[i]);
|
||||
writeOutput(',"ServicePointName":"' & jsonString(q.ServicePointName[i]) & '"');
|
||||
writeOutput(',"ServicePointTypeID":' & q.ServicePointTypeID[i]);
|
||||
writeOutput(',"ServicePointCode":' & (isNull(q.ServicePointCode[i]) ? 'null' : '"' & jsonString(q.ServicePointCode[i]) & '"'));
|
||||
writeOutput(',"ServicePointDescription":' & (isNull(q.ServicePointDescription[i]) ? 'null' : '"' & jsonString(q.ServicePointDescription[i]) & '"'));
|
||||
writeOutput(',"ServicePointSortOrder":' & q.ServicePointSortOrder[i]);
|
||||
writeOutput(',"ServicePointIsActive":' & q.ServicePointIsActive[i]);
|
||||
writeOutput(',"ServicePointCreatedAt":"' & jsonString(q.ServicePointCreatedAt[i] & "") & '"');
|
||||
writeOutput(',"ServicePointUpdatedAt":"' & jsonString(q.ServicePointUpdatedAt[i] & "") & '"');
|
||||
writeOutput('}');
|
||||
writeOutput(serializeJSON({
|
||||
"OK": true,
|
||||
"ERROR": "",
|
||||
"DETAIL": "",
|
||||
"BusinessID": bizId,
|
||||
"BusinessColumn": bizCol,
|
||||
"COUNT": arrayLen(servicePoints),
|
||||
"ServicePoints": servicePoints
|
||||
}));
|
||||
} catch (any e) {
|
||||
apiAbort({
|
||||
"OK": false,
|
||||
"ERROR": "db_error",
|
||||
"DETAIL": e.message
|
||||
});
|
||||
}
|
||||
|
||||
writeOutput(']}');
|
||||
</cfscript>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue