/** * Update Business Settings * Updates settings for the currently selected business * * POST: { * TaxRatePercent: 8.25 (percentage, will be converted to decimal) * -- OR -- * TaxRate: 0.0825 (decimal, stored directly) * } * * Requires: request.BusinessID (set by auth middleware) */ function apiAbort(obj) { writeOutput(serializeJSON(obj)); abort; } function readJsonBody() { raw = toString(getHttpRequestData().content); if (isNull(raw) || len(trim(raw)) EQ 0) { apiAbort({ OK: false, ERROR: "missing_body" }); } try { parsed = deserializeJSON(raw); } catch (any e) { apiAbort({ OK: false, ERROR: "bad_json", MESSAGE: "Invalid JSON body" }); } if (!isStruct(parsed)) { apiAbort({ OK: false, ERROR: "bad_json", MESSAGE: "JSON must be an object" }); } return parsed; } if (!structKeyExists(request, "BusinessID") || !isNumeric(request.BusinessID) || request.BusinessID LTE 0) { apiAbort({ OK: false, ERROR: "no_business_selected" }); } try { data = readJsonBody(); updates = []; params = { businessId: request.BusinessID }; // Handle tax rate (accept either percent or decimal) if (structKeyExists(data, "TaxRatePercent") && isNumeric(data.TaxRatePercent)) { taxRate = data.TaxRatePercent / 100; if (taxRate < 0 || taxRate > 0.5) { apiAbort({ OK: false, ERROR: "invalid_tax_rate", MESSAGE: "Tax rate must be between 0% and 50%" }); } arrayAppend(updates, "BusinessTaxRate = :taxRate"); params.taxRate = { value: taxRate, cfsqltype: "cf_sql_decimal" }; } else if (structKeyExists(data, "TaxRate") && isNumeric(data.TaxRate)) { taxRate = data.TaxRate; if (taxRate < 0 || taxRate > 0.5) { apiAbort({ OK: false, ERROR: "invalid_tax_rate", MESSAGE: "Tax rate must be between 0 and 0.5" }); } arrayAppend(updates, "BusinessTaxRate = :taxRate"); params.taxRate = { value: taxRate, cfsqltype: "cf_sql_decimal" }; } // Add more updatable fields as needed if (structKeyExists(data, "Name") && len(trim(data.Name))) { arrayAppend(updates, "BusinessName = :businessName"); params.businessName = { value: left(trim(data.Name), 100), cfsqltype: "cf_sql_varchar" }; } if (structKeyExists(data, "Phone")) { arrayAppend(updates, "BusinessPhone = :phone"); params.phone = { value: left(trim(data.Phone), 20), cfsqltype: "cf_sql_varchar" }; } // Track whether we need to update address separately hasAddressUpdate = false; addrFields = {}; if (structKeyExists(data, "Address") && len(trim(data.Address))) { hasAddressUpdate = true; addrFields.Line1 = left(trim(data.Address), 100); } if (structKeyExists(data, "City")) { hasAddressUpdate = true; addrFields.City = left(trim(data.City), 50); } if (structKeyExists(data, "Zip")) { hasAddressUpdate = true; addrFields.ZIPCode = left(trim(data.Zip), 10); } if (arrayLen(updates) == 0 && !hasAddressUpdate) { apiAbort({ OK: false, ERROR: "no_fields", MESSAGE: "No valid fields to update" }); } // Build and execute Businesses update if (arrayLen(updates) > 0) { sql = "UPDATE Businesses SET " & arrayToList(updates, ", ") & " WHERE BusinessID = :businessId"; queryExecute(sql, params, { datasource: "payfrit" }); } // Update address in Addresses table if needed if (hasAddressUpdate) { qExistingAddr = queryExecute(" SELECT AddressID AS ID FROM Addresses WHERE (AddressBusinessID = :businessId OR AddressID = (SELECT BusinessAddressID FROM Businesses WHERE BusinessID = :businessId)) AND AddressIsDeleted = 0 LIMIT 1 ", { businessId: request.BusinessID }, { datasource: "payfrit" }); if (qExistingAddr.recordCount > 0) { addrUpdates = []; addrParams = { addrId: qExistingAddr.ID }; if (structKeyExists(addrFields, "Line1")) { arrayAppend(addrUpdates, "AddressLine1 = :line1"); addrParams.line1 = { value: addrFields.Line1, cfsqltype: "cf_sql_varchar" }; } if (structKeyExists(addrFields, "City")) { arrayAppend(addrUpdates, "AddressCity = :city"); addrParams.city = { value: addrFields.City, cfsqltype: "cf_sql_varchar" }; } if (structKeyExists(addrFields, "ZIPCode")) { arrayAppend(addrUpdates, "AddressZIPCode = :zip"); addrParams.zip = { value: addrFields.ZIPCode, cfsqltype: "cf_sql_varchar" }; } if (arrayLen(addrUpdates) > 0) { queryExecute("UPDATE Addresses SET " & arrayToList(addrUpdates, ", ") & " WHERE AddressID = :addrId", addrParams, { datasource: "payfrit" }); } } } // Return updated settings q = queryExecute(" SELECT b.BusinessID AS ID, b.BusinessName AS Name, b.BusinessTaxRate AS TaxRate, b.BusinessPhone AS Phone FROM Businesses b WHERE b.BusinessID = :businessId LIMIT 1 ", { businessId: request.BusinessID }, { datasource: "payfrit" }); qAddr = queryExecute(" SELECT a.AddressLine1 AS Line1, a.AddressCity AS City, a.AddressZIPCode AS ZIPCode, s.tt_StateAbbreviation AS State FROM Addresses a LEFT JOIN tt_States s ON s.tt_StateID = a.AddressStateID WHERE (a.AddressBusinessID = :businessId OR a.AddressID = (SELECT BusinessAddressID FROM Businesses WHERE BusinessID = :businessId)) AND a.AddressIsDeleted = 0 LIMIT 1 ", { businessId: request.BusinessID }, { datasource: "payfrit" }); taxRateRaw = isNumeric(q.TaxRate) ? q.TaxRate : 0; taxRatePercent = taxRateRaw * 100; writeOutput(serializeJSON({ "OK": true, "MESSAGE": "Settings updated", "SETTINGS": { "BusinessID": q.ID, "Name": q.Name, "TaxRate": taxRateRaw, "TaxRatePercent": taxRatePercent, "Address": qAddr.recordCount > 0 ? (qAddr.Line1 ?: "") : "", "City": qAddr.recordCount > 0 ? (qAddr.City ?: "") : "", "State": qAddr.recordCount > 0 ? (qAddr.State ?: "") : "", "Zip": qAddr.recordCount > 0 ? (qAddr.ZIPCode ?: "") : "", "Phone": q.Phone ?: "", "Email": "" } })); } catch (any e) { apiAbort({ OK: false, ERROR: "server_error", MESSAGE: e.message }); }