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/setup/importBusiness.cfm
John Mizerek 1210249f54 Normalize database column and table names across entire codebase
Update all SQL queries, query result references, and ColdFusion code to match
the renamed database schema. Tables use plural CamelCase, PKs are all `ID`,
column prefixes stripped (e.g. BusinessName→Name, UserFirstName→FirstName).

Key changes:
- Strip table-name prefixes from all column references (Businesses, Users,
  Addresses, Hours, Menus, Categories, Items, Stations, Orders,
  OrderLineItems, Tasks, TaskCategories, TaskRatings, QuickTaskTemplates,
  ScheduledTaskDefinitions, ChatMessages, Beacons, ServicePoints, Employees,
  VisitorTrackings, ApiPerfLogs, tt_States, tt_Days, tt_AddressTypes,
  tt_OrderTypes, tt_TaskTypes)
- Rename PK references from {TableName}ID to ID in all queries
- Rewrite 7 admin beacon files to use ServicePoints.BeaconID instead of
  dropped lt_Beacon_Businesses_ServicePoints link table
- Rewrite beacon assignment files (list, save, delete) for new schema
- Fix FK references incorrectly changed to ID (OrderLineItems.OrderID,
  Categories.MenuID, Tasks.CategoryID, ServicePoints.BeaconID)
- Update Addresses: AddressLat→Latitude, AddressLng→Longitude
- Update Users: UserPassword→Password, UserIsEmailVerified→IsEmailVerified,
  UserIsActive→IsActive, UserBalance→Balance, etc.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 15:39:12 -08:00

366 lines
14 KiB
Text

<cfsetting showdebugoutput="false">
<cfsetting enablecfoutputonly="true">
<cfsetting requesttimeout="300">
<cfcontent type="application/json; charset=utf-8" reset="true">
<cfscript>
/**
* Import Business from Scraped Data
*
* Creates a complete business with:
* - Business record with contact info
* - Categories as Items (ParentID=0)
* - Menu items as children of categories
* - Modifier templates linked via lt_ItemID_TemplateItemID
* - Modifier options as children of templates
*
* POST JSON structure:
* {
* "business": {
* "name": "Big Dean's Ocean Front Cafe",
* "address": "1615 Ocean Front Walk",
* "city": "Santa Monica",
* "state": "CA",
* "zip": "90401",
* "phone": "(310) 393-2666",
* "email": "owner@bigdeans.com",
* "ownerPhone": "3101234567",
* "website": "https://bigdeansoceanfrontcafe.com",
* "logoUrl": "https://...",
* "headerUrl": "https://...",
* "hours": {
* "mon": "12:00 PM - 7:00 PM",
* "tue": "11:00 AM - 8:00 PM",
* ...
* }
* },
* "categories": [
* {
* "name": "World Famous Burgers",
* "sortOrder": 1,
* "items": [
* {
* "name": "Big Dean's Burger",
* "description": "The burger that made Santa Monica famous!",
* "price": 12.99,
* "modifiers": ["onion_style", "add_cheese"] // template references
* }
* ]
* }
* ],
* "modifierTemplates": [
* {
* "id": "onion_style",
* "name": "Onions",
* "required": true,
* "maxSelections": 1,
* "options": [
* { "name": "No Onions", "price": 0, "isDefault": false },
* { "name": "Grilled Onions", "price": 0, "isDefault": true },
* { "name": "Raw Onions", "price": 0, "isDefault": false }
* ]
* }
* ],
* "ownerUserID": 2, // optional, defaults to 1
* "dryRun": false // optional, if true just validates without inserting
* }
*/
response = { "OK": false, "steps": [], "errors": [], "warnings": [] };
try {
requestBody = toString(getHttpRequestData().content);
if (!len(requestBody)) {
throw(message="No request body provided");
}
data = deserializeJSON(requestBody);
dryRun = structKeyExists(data, "dryRun") && data.dryRun == true;
if (dryRun) {
response.steps.append("DRY RUN MODE - no changes will be made");
}
// Validate required fields
if (!structKeyExists(data, "business") || !structKeyExists(data.business, "name")) {
throw(message="business.name is required");
}
biz = data.business;
ownerUserID = structKeyExists(data, "ownerUserID") ? val(data.ownerUserID) : 1;
// Step 1: Create or find business
response.steps.append("Step 1: Creating business record...");
qCheck = queryExecute("
SELECT ID FROM Businesses WHERE Name = :name
", { name: biz.name }, { datasource: "payfrit" });
if (qCheck.recordCount > 0) {
BusinessID = qCheck.BusinessID;
response.steps.append("Business already exists with ID: " & BusinessID);
response.warnings.append("Existing business found - will add to existing menu");
} else if (!dryRun) {
queryExecute("
INSERT INTO Businesses (Name, UserID, AddressID, BusinessDeliveryZipCodes, AddedOn) VALUES (:name, :ownerID, 0, '', NOW())
", {
name: biz.name, ownerID: ownerUserID
}, { datasource: "payfrit" });
qNew = queryExecute("SELECT LAST_INSERT_ID() as id", {}, { datasource: "payfrit" });
BusinessID = qNew.id;
response.steps.append("Created business with ID: " & BusinessID);
} else {
BusinessID = 0;
response.steps.append("Would create business: " & biz.name);
}
// Step 2: Create modifier templates first (they need to exist before items reference them)
response.steps.append("Step 2: Creating modifier templates...");
templateMap = {}; // Maps template string ID to database ItemID
templates = structKeyExists(data, "modifierTemplates") ? data.modifierTemplates : [];
for (tmpl in templates) {
templateStringID = tmpl.id;
templateName = tmpl.name;
required = structKeyExists(tmpl, "required") && tmpl.required == true;
maxSelections = structKeyExists(tmpl, "maxSelections") ? val(tmpl.maxSelections) : 0;
if (!dryRun) {
// Check if template already exists for this business
qTmpl = queryExecute("
SELECT ID FROM Items
WHERE BusinessID = :bizID
AND Name = :name
AND ParentItemID = 0
AND ItemID IN (SELECT TemplateItemID FROM lt_ItemID_TemplateItemID)
", { bizID: BusinessID, name: templateName }, { datasource: "payfrit" });
if (qTmpl.recordCount > 0) {
templateItemID = qTmpl.ItemID;
response.steps.append("Template exists: " & templateName & " (ID: " & templateItemID & ")");
} else {
// Create template as Item at ParentID=0, with IsCollapsible=1 to mark it as a template
// This ensures the API filter excludes it from categories
queryExecute("
INSERT INTO Items (
BusinessID, Name, ParentItemID, Price,
IsActive, RequiresChildSelection, MaxNumSelectionReq, SortOrder, IsCollapsible
) VALUES (
:bizID, :name, 0, 0, 1, :required, :maxSelect, 0, 1
)
", {
bizID: BusinessID,
name: templateName,
required: required ? 1 : 0,
maxSelect: maxSelections
}, { datasource: "payfrit" });
qNewTmpl = queryExecute("SELECT LAST_INSERT_ID() as id", {}, { datasource: "payfrit" });
templateItemID = qNewTmpl.id;
response.steps.append("Created template: " & templateName & " (ID: " & templateItemID & ")");
}
templateMap[templateStringID] = templateItemID;
// Create template options
options = structKeyExists(tmpl, "options") ? tmpl.options : [];
optionOrder = 1;
for (opt in options) {
optName = opt.name;
optPrice = structKeyExists(opt, "price") ? val(opt.price) : 0;
optDefault = structKeyExists(opt, "isDefault") && opt.isDefault == true;
qOpt = queryExecute("
SELECT ID FROM Items
WHERE BusinessID = :bizID AND Name = :name AND ParentItemID = :parentID
", { bizID: BusinessID, name: optName, parentID: templateItemID }, { datasource: "payfrit" });
if (qOpt.recordCount == 0) {
queryExecute("
INSERT INTO Items (
BusinessID, Name, ParentItemID, Price,
IsActive, IsCheckedByDefault, SortOrder
) VALUES (
:bizID, :name, :parentID, :price, 1, :isDefault, :sortOrder
)
", {
bizID: BusinessID,
name: optName,
parentID: templateItemID,
price: optPrice,
isDefault: optDefault ? 1 : 0,
sortOrder: optionOrder
}, { datasource: "payfrit" });
}
optionOrder++;
}
} else {
response.steps.append("Would create template: " & templateName & " with " & arrayLen(tmpl.options) & " options");
templateMap[templateStringID] = 0;
}
}
// Step 3: Create categories (as Items at ParentID=0)
response.steps.append("Step 3: Creating categories...");
categoryMap = {}; // Maps category name to ItemID
categories = structKeyExists(data, "categories") ? data.categories : [];
catOrder = 1;
for (cat in categories) {
catName = cat.name;
if (!dryRun) {
// Check if category exists (Item at ParentID=0, not a template)
qCat = queryExecute("
SELECT ID FROM Items
WHERE BusinessID = :bizID
AND Name = :name
AND ParentItemID = 0
AND ItemID NOT IN (SELECT TemplateItemID FROM lt_ItemID_TemplateItemID)
", { bizID: BusinessID, name: catName }, { datasource: "payfrit" });
if (qCat.recordCount > 0) {
categoryItemID = qCat.ItemID;
response.steps.append("Category exists: " & catName);
} else {
queryExecute("
INSERT INTO Items (
BusinessID, Name, ParentItemID, Price,
IsActive, SortOrder
) VALUES (
:bizID, :name, 0, 0, 1, :sortOrder
)
", {
bizID: BusinessID,
name: catName,
sortOrder: catOrder
}, { datasource: "payfrit" });
qNewCat = queryExecute("SELECT LAST_INSERT_ID() as id", {}, { datasource: "payfrit" });
categoryItemID = qNewCat.id;
response.steps.append("Created category: " & catName & " (ID: " & categoryItemID & ")");
}
categoryMap[catName] = categoryItemID;
} else {
response.steps.append("Would create category: " & catName);
categoryMap[catName] = 0;
}
catOrder++;
}
// Step 4: Create menu items as children of categories
response.steps.append("Step 4: Creating menu items...");
totalItems = 0;
totalLinks = 0;
for (cat in categories) {
catName = cat.name;
categoryItemID = categoryMap[catName];
items = structKeyExists(cat, "items") ? cat.items : [];
itemOrder = 1;
for (item in items) {
itemName = item.name;
itemDesc = structKeyExists(item, "description") ? item.description : "";
itemPrice = structKeyExists(item, "price") ? val(item.price) : 0;
if (!dryRun) {
// Check if item exists
qItem = queryExecute("
SELECT ID FROM Items
WHERE BusinessID = :bizID AND Name = :name AND ParentItemID = :parentID
", { bizID: BusinessID, name: itemName, parentID: categoryItemID }, { datasource: "payfrit" });
if (qItem.recordCount > 0) {
menuItemID = qItem.ID;
} else {
queryExecute("
INSERT INTO Items (
BusinessID, Name, Description, ParentItemID,
Price, IsActive, SortOrder
) VALUES (
:bizID, :name, :desc, :parentID, :price, 1, :sortOrder
)
", {
bizID: BusinessID,
name: itemName,
desc: itemDesc,
parentID: categoryItemID,
price: itemPrice,
sortOrder: itemOrder
}, { datasource: "payfrit" });
qNewItem = queryExecute("SELECT LAST_INSERT_ID() as id", {}, { datasource: "payfrit" });
menuItemID = qNewItem.id;
}
// Link modifier templates to this item
modifiers = structKeyExists(item, "modifiers") ? item.modifiers : [];
modOrder = 1;
for (modRef in modifiers) {
if (structKeyExists(templateMap, modRef)) {
templateItemID = templateMap[modRef];
// Check if link exists
qLink = queryExecute("
SELECT 1 FROM lt_ItemID_TemplateItemID
WHERE ItemID = :itemID AND TemplateItemID = :templateID
", { itemID: menuItemID, templateID: templateItemID }, { datasource: "payfrit" });
if (qLink.recordCount == 0) {
queryExecute("
INSERT INTO lt_ItemID_TemplateItemID (ItemID, TemplateItemID, SortOrder)
VALUES (:itemID, :templateID, :sortOrder)
", {
itemID: menuItemID,
templateID: templateItemID,
sortOrder: modOrder
}, { datasource: "payfrit" });
totalLinks++;
}
modOrder++;
} else {
response.warnings.append("Unknown modifier reference: " & modRef & " on item: " & itemName);
}
}
}
totalItems++;
itemOrder++;
}
}
response.steps.append("Created " & totalItems & " menu items with " & totalLinks & " template links");
// Summary
response.OK = true;
response.summary = {
"businessID": BusinessID,
"businessName": biz.name,
"categoriesCreated": arrayLen(categories),
"templatesCreated": arrayLen(templates),
"itemsCreated": totalItems,
"templateLinksCreated": totalLinks,
"dryRun": dryRun
};
if (dryRun) {
response.steps.append("DRY RUN COMPLETE - no changes were made");
} else {
response.steps.append("IMPORT COMPLETE!");
}
} catch (any e) {
response.errors.append(e.message);
if (len(e.detail)) {
response.errors.append(e.detail);
}
}
writeOutput(serializeJSON(response));
</cfscript>