Skip to content

KB Accuracy Audit — Feature Docs vs Back-Office Code

Date: 2026-06-12 Scope: 12 high-traffic merchant feature docs, each cross-checked by an AI agent against the actual upvendo-backoffice and upvendo-backend source. Why this exists: While generating grounded merchant Q&A (the ## FAQs expansions in this PR), every feature doc was verified field-by-field against code. The verification surfaced that the docs' ## Fields sections, routes, component paths, and several whole features contain fabricated or stale content — field IDs, toggles, and routes that do not exist in the product. Emily reads these docs, so this is a direct cause of wrong merchant-facing answers (independent of the retrieval-scoring issue tracked separately).

Status: This is a correction backlog. The Q&A added in this PR is code-verified and correct; the ## Fields / ## Examples sections flagged below still need correcting in follow-up PRs. Treat every "Field ID" in an unaudited feature doc as suspect until verified against code.


How to read this

Each entry is a claim in the existing doc that contradicts the code, with the code evidence. "Fabricated" = the field/route/feature does not exist anywhere in the codebase. Priorities: P1 = actively misleads merchants (wrong route/provider/feature), P2 = wrong field IDs/labels/limits in ## Fields, P3 = stale examples / imprecise wording.


payments.md

  • P1 — "Stripe is ONLY for subscription billing, not customer payments" is false. Stripe also processes online-ordering customer payments via connected accounts (PaymentProfile.php:137 isOnlineOrderingPaymentEnabled(); PaymentService.php:1351-1353,1555) and in-person Stripe Terminal payments (PaymentService.php:236-426; DeviceService.php:602-635). The doc even contradicts its own terminal-pairing section.
  • P1 — "Viva Wallet is the primary provider for online ordering" is wrong. Online ordering always uses Stripe; Viva is the kiosk/in-person default only (PaymentProfile.php:28-31 docblock "Online ordering always uses Stripe regardless of this value").
  • P2 — Fabricated profile fields viva_wallet.enabled, viva_wallet.merchant_id, viva_wallet.api_key. The add form has only Name, Country, in-person provider (NewPaymentProfileRequest.php:29-56). No api_key exists; Viva auth uses config-level ISV OAuth (VivaWalletService.php:35).
  • P2 — tips.enabled / tips.percentages wrong keys & wrong feature. Real keys are collect_tips.enabled / collect_tips.options (default [10,15,20]) on Location/Device settings, not payment profiles (Constants.php:259-263,296-300).
  • P2 — cash.pickup_enabled / cash.delivery_enabled fabricated. COD is a deferred payment method (Enums/PaymentMethodOptions.php), no such toggles.
  • P2 — stripe.live_mode test/live field fabricated. The per-location flag is payment_test_mode (Location.php:151).
  • P2/P3 — Routes /settings/payments/add and /settings/payments/:id don't exist. Add is a modal; detail route is /settings/payments/profiles/:id (additional-routes.ts:20).
  • P3 — Viva source-code generation is deprecated (new locations use 'Default'; LocationService.php:336-338).

online-ordering.md

  • P1 — Route & nav wrong. Doc says /pickup-and-delivery/online-ordering / "Pickup & Delivery → Online Ordering". Actual is /online/online-ordering, nav "Online → Online Ordering" (typed-router.d.ts:109; src/views/online/online-ordering/).
  • P2 — average_prep_time mis-attributed as an OO field; it's a Location setting (Location.php:115,175; not in StoreOnlineOrderingRequest/$DEFAULT_ONLINE_ORDERING_SETTING).
  • P2 — asap_enabled ("Allow ASAP Orders") fabricated — no such field anywhere. The existing FAQ about "ASAP disabled" describes a non-existent toggle.
  • P2 — JSON example field names fabricated (pickup_enabled, delivery_enabled, scalar minimum_order_amount, scheduled_enabled, max_days_ahead). Real shape uses takeout.enabled, delivery.enabled, minimum_order_amount.{enabled,amount}, schedule_order_for_future_days.* (Constants.php:548-663).
  • P3 — minutes_before_pickup_delivery_time default 600 unverified (not in defaults).
  • P2 — Doc file paths wrong. Real list view src/views/items/Items.vue, form src/components/forms/items/ItemForm.vue, controller Api/ItemController.php (doc says index.vue, views/items/forms/ItemForm.vue, MenuItemController.php).
  • P2 — Description max length wrong (doc 500; form is 300, backend has no max).
  • P2 — Item name max 100 wrong/unverified (backend required|string, no max).
  • P2 — Status enum wrong. Real values Active/Inactive/Unavailable/Hidden; "Unavailable" (not "inactive") drives sold-out/stock-0 (ItemService.php:320-322).
  • P2 — Image size limit wrong (doc 5MB; backend max:10240 KB ≈ 10MB).
  • P3 — use_default_prep_time default misstated; price min:0 overstated.

modifiers.md

  • P2 (extensive) — Fabricated field schema. multi_select, min_selections, max_selections, free_options_count, sort_order, required, price_adjustment, available, default_selectednone exist. Real fields: details.name, details.description, pos_name, pricing, price, tax_rate_code, visibility, settings.{is_mandatory, allow_select_more_than_one, allow_same_modifier_more_than_one, max_selected} (StoreModifierGroupRequest.php; RawModels/ModifierGroup.php:35-58; RawModels/Modifier.php).
  • P1 — "Free options count" pricing logic fabricated (existing FAQ + "Free Options Calculation" section). Pricing is only No charge / Individual / Group (Enums/PricingOptions.php).
  • P2 — Modifier "negative price adjustment" wrong — a modifier stores an absolute Money $price, not a +/- adjustment.
  • P2 — View path wrong (ModifierGroups.vue, not index.vue).
  • P3 — "share group if same label" claim unsupported.

categories.md

  • P1 — Fabricated menu_id / "Menu Assignment". Categories have no menu reference; items reference category_id (RawModels/Category.php; StoreCategoryRequest.php).
  • P1 — Fabricated visible toggle, availability schedule, default_tax_rate_id. None exist; the "Category Visibility" flowchart and "different availability per day" FAQ are unsupported.
  • P2 — Controller path wrong (Api/BackOffice/CategoryController.php); view is pages/menus/categories/index.vueReportingCategories.vue.
  • P2 — Name/description max (100/300) not enforced; image limit 5MB wrong (10MB); SVG accepted but omitted.
  • P3 — sort_order shown as editable but the form doesn't expose it.

locations.md

  • P1 — "Billing Profile required" and "Payment Profile required" are false. Both are nullable (StoreLocationRequest.php:71-79,135-144; payment field :rules="[]"). Only Branding is required, and only on add.
  • P2 — "Unique location name" not enforced (rule commented out, StoreLocationRequest.php:104-108).
  • P2 — Lat/Lng "Required: Yes" not enforced for save (pinpoint.lat/lng nullable); geolocation only gates online-ordering readiness.
  • P3 — Missing: the Active/Inactive Status selector + deactivation flow; Restricted Dates range + "repeats every year".

kiosk.md

  • P1 — upsell_enabled device-profile field does not exist (already corrected in a prior PR; upsells are the separate Upsell Groups feature).
  • P2 — Dining options wrong. Real fields dining_option_1/2 with values For Here / Takeout / None (DeviceProfile.php:51-54; DiningOptions.php). Doc's "Dine In"/"Takeaway" toggles are wrong; dineIn() is @deprecated.
  • P2 — Idle timeout fields fabricated. Real: idle_timeout_type (standard|custom) + idle_timeout_seconds + show_warning_for_seconds; standard is 45s/15s, not "default 120, min 30/max 600" (DeviceProfile.php:57-58,163-179).
  • P2 — Fabricated profile fields: theme, languages, default_language, show_images, image_size, category_layout, tips_enabled/tip_percentages, sound_enabled. Layout is menu_item_columns (2/3); order display is order_number_display (queue_number/order_number/both); tipping comes from the location's collect_tips.
  • P2 — Device fields menu_id/printer_id/enabled not on the Device model (menus live on the profile).
  • P2 — Device status "online/offline/error" wronggetStatus() returns only Online/Offline (Device.php:336-352).
  • P1 — "Remotely restart a kiosk via Firebase Cloud Messaging" unverified — no such command found in device views or controllers (existing FAQ claims it).

qr-ordering.md

  • P2 (extensive) — Fabricated settings fields: payment_required, allow_multiple_orders, session_timeout, call_waiter_enabled, request_bill_enabled, qr_style, qr_include_logo, qr_size. Real settings: sections, custom_availability, accept_orders_until, schedule, custom_default_menu/default_menu_id/extra_menu_ids, require_name, require_email (StoreQrOrderingRequest.php:23-55; Constants.php:311-319).
  • P2 — menu_id wrong (it's default_menu_id + custom_default_menu + extra_menu_ids).
  • P2 — Table fields qr_code/active don't exist; name limits inconsistent (section 25, custom name 20, auto label 10).
  • P2 — Controller/view paths wrong (Api/BackOffice/QrOrderingController.php; pages/in-house/qr-ordering/index.vue).

loyalty.md

  • P2 (extensive) — Fabricated fields: enabled, points_per_currency, point_value, min_redemption_points, max_redemption_percentage, points_expiry_days, earn_on, excluded_categories, welcome_bonus. Real fields per StoreLoyaltyRequest.php:96-214 / RawModels/Loyalty.php:35-60.
  • P1 — "Point Value / points×value redemption" model fabricated. Rewards redeem by reaching a fixed points_needed/visits_needed threshold (RawModels/Reward.php; LoyaltyService.php:633-657).
  • P1 — max_redemption_percentage cap fabricated; earn_on subtotal/total fabricated (points always on order total, LoyaltyService.php:483); excluded_categories fabricated.
  • P2 — Expiry is in months (enum 3/6/12/18/24), not "days, min 30" (Enums/LoyaltyMonthsToExpirePoints.php).
  • P2 — Controller/view paths wrong (Api/BackOffice/LoyaltyController.php; LoyaltyWizard.vue).

offers.md

  • P1 — Missing offer type ASSIGNED_DISCOUNT (Enums/OfferTypes.php:10).
  • P2 (extensive) — Field schema largely fabricated. type options, auto_apply/promo_code, "stackable", minimum_order_amount, bogo.*, max_discount, usage_limit, applicable_items, channels, dining_options, free_item_id, active do not match. Real: discount_unit (percent/amount/free), OfferMethods (Automatic/Code), can_be_combined, minimum_purchase_type, requirement_applies_to/benefit_*, limit_discount_usage*/limit_one_usage_per_customer, offer_applies_to (RawModels/Offer.php; the Enums/Offer*).
  • P2 — Discount-code max 20 wrong (UI enforces 10).
  • P1 — "Deactivate" UI action doesn't exist — Active offers are taken down via Archive/Unarchive (Offers.vue getAvailableActions).
  • P3 — "Loyalty stacks with offers" / "stackable marker" unverified.

coupons.md

  • P1 — Entire route /marketing/coupons is wrong. No coupons route/page/controller exists — coupons are Offers created with method "Code" (manage under /marketing/offers). Frontmatter route, applicable_pages, CouponController.php, and views/marketing/coupons/ are all fabricated.
  • P2 — Fabricated fields: discount_type (free_shipping), max_discount, per_customer_limit (numeric — real is boolean limit_one_usage_per_customer), first_order_only, applicable_to (all/categories/items), usage_limit, standalone active, description.
  • P1 — "Free shipping" discount type & calculation fabricated (existing FAQ + FREEDELIVERY example).
  • P2 — Code max 20 wrong (10); name is customer-facing, not "internal".

transactions.md

  • P2 — Channel values wrong. Real ChannelOptions: Kiosk, POS, Online Ordering, Table Qr Ordering, Uber Eats, Takeaway, Shopify (display-cased). Doc's online_ordering/kiosk/in_house/deliveroo/uber_eats/manual are wrong (Enums/ChannelOptions.php:7-13).
  • P2 — Order status values wrong. Real OrderStatuses: Unpaid, Awaiting Capture, Awaiting Invoice, Awaiting Payment, Pending, Complete, Queued, In Progress, Ready, Cancelled. No confirmed/preparing/completed/refunded (Enums/OrderStatuses.php).
  • P2 — Dining option values wrong (Delivery/For Here/Pickup/Takeout; "Dine In" deprecated).
  • P2 — Separate payment_status enum fabricated (filtering uses the single status enum).
  • P1 — Refund presented as working, but it isn't wired — refund button commented out in OrderDetail.vue; store refundOrder is a simulated mock (store/modules/transaction.ts:122-145).
  • P1 — Manual status-progression actions (Confirm / Mark Preparing / Mark Ready / Complete) don't exist — controller exposes only show, dataTable, stats, export, resendReceipt, retrySync, deleteTestTransaction.
  • P2 — Controller/view paths wrong (Api/BackOffice/TransactionController.php; Transactions.vue).

  1. Correction pass per feature doc — rewrite ## Fields, ## Route, paths, and ## Examples to match code, using the same per-feature-agent + code-verification method that produced this audit. Highest-leverage docs first (payments, transactions, offers/coupons, modifiers, kiosk, loyalty — the worst offenders).
  2. Fix the existing fabricated ## FAQs/## Troubleshooting entries flagged above (ASAP, free-options-count, free-shipping, remote-restart, refund-as-working).
  3. Extend the audit to the remaining ~95 feature/integration docs — the same fabrication pattern is likely present.
  4. Process change: require code-verification for any new or AI-generated KB content before it ships (the Q&A in this PR was generated under that rule).