- analyzeMenuUrl.cfm: Detect subcategories from Toast subgroups and
Claude API responses, preserve hierarchy with parentCategoryName
- setup-wizard.html: Display subcategories indented under parents
throughout wizard flow (categories step, items review, summary, preview)
- menu-builder.html: Show subcategories nested in outline modal view
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- saveCategory.cfm: Accept ParentCategoryID, enforce max 2-level nesting
- items.cfm: Include ParentCategoryID on virtual category rows for Android
- getForBuilder.cfm: Return ParentCategoryID in builder API response
- saveFromBuilder.cfm: Persist ParentCategoryID on save, track JS-to-DB id mapping
- saveWizard.cfm: Two-pass category creation (parents first, then subcategories)
- menu-builder.html: Parent category dropdown in properties, visual nesting in canvas,
add subcategory button, renderItemCard() extracted for reuse
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Large menus (20+ categories) were getting truncated JSON responses
at 8192 tokens, causing parse failures.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Staff cash_debit goes to the worker (they keep the cash).
Admin/Manager cash_debit goes to the business owner (restaurant has it).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create tt_StaffRoles lookup table (Staff, Manager, Admin)
- Add RoleID column to Employees table (default: Staff)
- Wire portal role dropdown to addTeamMember API
- Return RoleName in team list and RoleID to Android
- Skip worker payout ledger and cash_debit for Manager/Admin roles
on cash task completion (they collect on behalf of the restaurant)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Return HasChildren boolean so Android can route food court parents
directly to business selector. Exclude child businesses from top-level
restaurant list.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- open.cfm: accept optional ApprovalMode param (0=auto, 1=manual, NULL=business default)
- addOrder.cfm: check tab ApprovalMode first, fall back to business TabApprovalRequired
- getActive.cfm: return resolved ApprovalRequired in tab response
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New api/tabs/ directory with 13 endpoints: open, close, cancel, get, getActive,
addOrder, increaseAuth, addMember, removeMember, getPresence, approveOrder,
rejectOrder, pendingOrders
- New api/presence/heartbeat.cfm for beacon-based user presence tracking
- New cron/expireTabs.cfm for idle tab expiry and presence cleanup
- Modified submit.cfm for tab-aware order submission (skip payment, update running total)
- Modified getOrCreateCart.cfm to auto-detect active tab and set TabID on new carts
- Modified webhook.cfm to handle tab capture events (metadata type=tab_close)
- Modified businesses/get.cfm and updateTabs.cfm with new tab config columns
- Updated portal tab settings UI with auth amounts, max members, approval toggle
- Added tab and presence endpoints to Application.cfm public allowlist
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Changed tt_TaskTypeID to ID in task type CRUD operations to match
the migrated dev database schema.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The tt_TaskTypes table uses 'ID' not 'tt_TaskTypeID'.
This was causing "Failed to load services" in the portal.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add getTimeInZone() and getDayInZone() helpers in Application.cfm
- Update items.cfm and getForBuilder.cfm to use business timezone
- Menu availability now correctly respects each business's timezone
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Both values MUST be set per business. If not configured, the menu
API will return an error instead of silently using defaults.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
If PayfritFee is not configured for a business, the payment intent
creation now errors instead of silently using 5%. This ensures fees
are always explicitly set per business.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add SubmittedOn = COALESCE(SubmittedOn, NOW()) to webhook for KDS timer
- Add test mode webhook secret for dev.payfrit.com
- Keep live mode webhook secret for biz.payfrit.com
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Moved fee calculation before PI check so we can compare amounts.
If existing PaymentIntent has different amount than current cart,
update it via Stripe API before returning.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When user abandons checkout and retries, retrieve the existing
PaymentIntent from Stripe. If still usable (requires_payment_method,
requires_confirmation, requires_action), return its client_secret.
If already succeeded, block with clear error. If terminal/canceled,
clear and create new one.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Returns customer ID, ephemeral key, and publishable key needed for
Stripe Payment Sheet to display saved payment methods on iOS/Android.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Get user info (StripeCustomerId, email, name) when creating PaymentIntent
- Create Stripe Customer if user doesn't have one
- Add customer and setup_future_usage=off_session to PaymentIntent
- Cards are automatically saved after successful payment
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The ## escape sequence was missing the closing # for variable interpolation,
causing 500 errors on all webhook requests. Fixed ##metaTipID# -> ###metaTipID#
and similar patterns.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Cancel Task now leaves order untouched (customer can pay another way)
- Standardized SMS text to "Your Payfrit code is:" across all endpoints
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Check for existing active (uncompleted) cash task before creating
a new one. Prevents duplicate "Pay With Cash" tasks if order status
changes are triggered multiple times.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When worker passes CancelOrder: true to complete.cfm:
- Skips all cash validation and financial processing
- Sets order status to 6 (cancelled)
- Completes the task normally
- Returns OrderCancelled: true in response
For when customer changes their mind about paying with cash.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixed complete.cfm, getDetails.cfm, listMine.cfm, listPending.cfm to pull
PayfritFee from Businesses table with 0.05 fallback if not set.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Matches the lookup.cfm approach:
- UUID from BeaconShards
- Major from Businesses.BeaconMajor
- Minor from ServicePoints.BeaconMinor
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Looks up actual ShardUUID, Major, Minor from BeaconHardware
instead of deriving from Business/ServicePoint tables.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- UUID from BeaconShards via Business.BeaconShardID
- Major from Business.BeaconMajor
- Minor = ServicePointID
Replaces old beacon lookup via ServicePoints.BeaconID
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Looks up task type by name for the business instead of hardcoding ID
- Dine-in: 'Deliver to Table'
- Takeaway: 'Order Ready for Pickup'
- Delivery: 'Deliver to Address'
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The duplicate check was preventing delivery task creation if ANY task
existed for the order (e.g., Cash or Chat tasks). Now only checks for
TaskTypeID=1 (delivery/pickup tasks) to allow proper task creation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Improved feature descriptions
- Updated main description
- Added "coming soon" note for delivery
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When a cash task is completed, the order now goes to status 1 (submitted)
instead of directly to status 5 (complete). This allows the normal kitchen
flow to proceed: kitchen sees order → prepares → marks status 3 → delivery
task is auto-created.
Also sets PaymentStatus, PaymentCompletedOn, and SubmittedOn to match the
Stripe webhook behavior for paid orders.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- getActiveCart now returns existing cart for user
- Optional BusinessID parameter to filter by specific business
- Used by Android app for cart recovery when scanning at a business
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Skip cash validation and processing for tasks with no linked order.
These are likely test tasks that were created before order linking.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Returns order total in cents as integer for Flutter Works app
to display exact cash amount due for cash payment tasks.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Auto-create cash task when order status transitions to 3 (Final Prep)
and has a pending cash payment (Payments.PaymentPaidInCash > 0)
- Task includes OrderID so Android can display OrderTotal
- Task title includes service point name when available
- Fix duplicate task check: was WHERE ID = ?, now WHERE OrderID = ?
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>