For inverted display: when user deselects a default-checked item, keep it
in cart with Quantity=0 instead of deleting. Cart displays "NO X" for these.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
For inverted groups, when user deselects a default item, keep it in cart
with Quantity=0 instead of deleting. This allows cart to display "NO X".
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Child items need to know if their parent group is inverted for proper
display logic (showing "NO X" instead of listing selected items).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Required for inverted modifier display (showing "NO X" for removed defaults
instead of listing all selected items).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When deselecting a modifier (IsSelected=false), Quantity was incorrectly
set to 1 instead of 0, preventing the removal logic from working.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Works app can now display inverted modifier groups with "NO" prefix
for removed defaults, matching KDS behavior.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CFML was failing to compile analyzeMenuUrl.cfm because ' contains
a # character that Lucee interprets as variable expression start.
Escaped all 4 occurrences to &##39;.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When enabled on a modifier group, KDS and cart only show removed
defaults (e.g., "NO Mustard") instead of listing all selected items.
Useful for groups where all options are checked by default.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Parse JSON-LD structured menu data from saved Uber Eats pages
(categories, items, prices, descriptions, business info)
- Show save-and-upload instructions when user pastes Uber Eats URL
- Always show header image upload step (was skipped for URL imports)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cash orders have no card processing fee. Now checks PaymentPaidInCash
to determine payment type and skips the Stripe fee calculation + display
for cash orders.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
KDS doesn't send auth tokens, so markStationDone needs to be a public
route like listForKDS and updateStatus already are.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously station-filtered view skipped the acknowledge step and went
straight to "Mark Station Done" for new orders. Now new orders show
"Start Preparing" first, then "Mark Station Done" once preparing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- getOrCreateCart: only require ServicePointID for dine-in (OrderTypeID=1)
- get.cfm + items.cfm: return OrderTypes from Businesses table
- saveOrderTypes.cfm: new endpoint to save business order type config
- KDS: add PICKUP/DELIVERY badges on order cards
- Portal: add Order Types toggle card in settings (Dine-In always on, Takeaway toggle)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The WooCommerce fast path was returning empty business info. Now the
Playwright script extracts it from the page and the CFML passes it through.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- checkbox groups get MaxNumSelectionReq=0 (unlimited), radio/select get 1
- Pre-checked options (e.g. preselected condiments) saved with IsCheckedByDefault=1
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The wizard checks item.imageUrl to decide whether to skip the image
upload step and download images from remote URLs instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The beacon app sends "Phone" (uppercase) but loginOTP.cfm checked for
"phone" (lowercase). With preserveCaseForStructKey=true this fails.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Changed from cover/fixed-height to contain/aspect-ratio 3:1 so the
full 1200x400 header image is visible without cropping.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Detects WooCommerce sites from Playwright HTML (woocommerce, wc-add-to-cart,
tm-extra-product-options). Runs woo-modifiers.js which navigates all product
pages, extracts items with categories, and scrapes TMEPO/variation modifiers.
Falls through to Claude if extraction fails.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The revert from the other session disabled this. 123456 must work
alongside real codes on production for Apple reviewer testing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- items.cfm: return BrandColorLight in menu response
- saveWizard.cfm: save BrandColorLight during business creation
- setup-wizard.html: second color picker for light brand color
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Updated the portal settings brand color picker to show both dark
and light colors. Light swatch shown alongside dark swatch.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New BrandColorLight column on Businesses table. Menu builder cards
(categories, subcategories, items) get a very subtle tint from the
light brand color. White when no color is set. Brand color picker
now has both dark and light fields.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Staff cash goes to worker payout ledger, admin/manager cash deletes
pending payout and reverses withholding. Add RoleID to myBusinesses
response. Various order and webhook improvements.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Station assignment was a standalone page with no sidebar. Added the
same portal sidebar so users can navigate between pages. Also added
Stations link to menu-builder sidebar.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds button in the outline modal to toggle modifier group display
under each item. Each group shows a + to expand into full options
with prices. Useful for debugging modifier assignments.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the conditional out of the nested template literal to avoid
parsing issues with arrow functions inside backtick strings. Also
skip empty mod-groups divs on items without modifiers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Outline button reveals modifier groups under each item with a +
expand button to drill into individual options and prices. Helps
debug imported menus before saving.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Post-process Claude menu extraction to detect when >60% of categories
have exactly 1 item (a common misparse). Collapses pseudo-categories
into the nearest preceding real (0-item) category.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Added explicit guidance that categories are broad section headings
(5-15 typical) and items are individual products (30-150 typical).
Prevents Claude from treating each menu item as its own category.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
For URL imports (Toast, Grubhub, etc.) there's no header image
to show, so skip straight from business info to categories
instead of showing an empty "Choose Image / Skip" prompt.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Claude API rejects images when the declared media type doesn't
match the actual content. Now detects JPEG/PNG/GIF/WebP from
base64 magic bytes instead of trusting file extensions or
Content-Type headers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Check PI state on Stripe before capture/cancel (requires_capture,
canceled, requires_payment_method, etc.)
- Add application_fee_amount for Stripe Connect (was missing — all
money went to connected account on auto-close)
- Re-verify tab StatusID=1 before processing (prevents race with
user-initiated close)
- Add WHERE StatusID=1 to closing UPDATE (prevents overwriting
concurrent user close)
- Log full Stripe response status and HTTP code for debugging
- Handle already-cancelled PIs gracefully (mark tab expired)
- Handle unconfirmed PIs (cancel and mark expired)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cancel.cfm previously only blocked cancel if orders had StatusID >= 1
(submitted to kitchen). If submitOrder() failed after addOrderToTab()
succeeded, the order stayed at StatusID 0 but was linked to the tab
with ApprovalStatus='approved'. This allowed cancelling the tab free.
Now checks TabOrders directly — any approved orders with non-zero
subtotals block the cancel, regardless of kitchen submission status.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Detect Grubhub URLs and fetch menu data directly via their
REST API instead of scraping HTML. Gets anonymous auth token,
then fetches full restaurant data including categories, items,
modifiers, prices, hours, lat/lng, tax rate, and item images.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Toast provides latitude/longitude in the location object. Extract
in analyzeMenuUrl.cfm and pass through to saveWizard.cfm, which
now includes lat/lng in the address INSERT. Skips the background
Nominatim geocode when coordinates are already available.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Convert cfthread from tag-based to cfscript syntax. Lucee 7
no longer allows </cfscript> mid-file to switch to tag mode.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Parses upcomingSchedules from ROOT_QUERY.restaurantV2.schedule
and formats as "Mon 7:30am-6:30pm, Tue 7:30am-6:30pm" text string
that the setup wizard's parseHoursString() can consume.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add item.prices array support to first code path (was only checking
item.price scalar, but Toast now uses prices: [4.50] array)
- Extract individual address fields (addressLine1, city, state, zip)
from ROOT_QUERY restaurant data for saveWizard compatibility
- Update modifier extraction URL detection to match any toasttab.com
domain (not just order.toasttab.com)
- Update slug-based URL construction to use www.toasttab.com/local/order/
format instead of deprecated order.toasttab.com/online/ format
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each station can mark their items done independently. When all
stations are done, the order auto-promotes to Ready (status 3)
and delivery/pickup tasks are created automatically.
- New markStationDone.cfm endpoint for per-station completion
- Extract task creation into shared _createOrderTasks.cfm include
- Add line item StatusID to listForKDS.cfm response
- KDS shows per-station "Mark Station Done" button when filtered
- Done items display with strikethrough and checkmark
- Manager view retains full manual control (no station selected)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
If a user has a live-mode StripeCustomerId but the API is running in
test mode (or vice versa), the PI creation fails. Now validates the
customer with Stripe first and creates a new one if invalid.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
MySQL rejects ORDER BY o.SubmittedOn when SELECT DISTINCT has
DATE_FORMAT(o.SubmittedOn) AS SubmittedOn. Use the alias instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
API returns Name, Price, ParentItemID, CategoryID, StationID but JS
was referencing ItemName, ItemPrice, ItemParentItemID, etc. causing
all items to be filtered out and not displayed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>