Appearance
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:137isOnlineOrderingPaymentEnabled();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-31docblock "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). Noapi_keyexists; Viva auth uses config-level ISV OAuth (VivaWalletService.php:35). - P2 —
tips.enabled/tips.percentageswrong keys & wrong feature. Real keys arecollect_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_enabledfabricated. COD is a deferred payment method (Enums/PaymentMethodOptions.php), no such toggles. - P2 —
stripe.live_modetest/live field fabricated. The per-location flag ispayment_test_mode(Location.php:151). - P2/P3 — Routes
/settings/payments/addand/settings/payments/:iddon'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_timemis-attributed as an OO field; it's a Location setting (Location.php:115,175; not inStoreOnlineOrderingRequest/$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, scalarminimum_order_amount,scheduled_enabled,max_days_ahead). Real shape usestakeout.enabled,delivery.enabled,minimum_order_amount.{enabled,amount},schedule_order_for_future_days.*(Constants.php:548-663). - P3 —
minutes_before_pickup_delivery_timedefault 600 unverified (not in defaults).
menu-items.md
- P2 — Doc file paths wrong. Real list view
src/views/items/Items.vue, formsrc/components/forms/items/ItemForm.vue, controllerApi/ItemController.php(doc saysindex.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:10240KB ≈ 10MB). - P3 —
use_default_prep_timedefault misstated; pricemin:0overstated.
modifiers.md
- P2 (extensive) — Fabricated field schema.
multi_select,min_selections,max_selections,free_options_count,sort_order,required,price_adjustment,available,default_selected— none 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, notindex.vue). - P3 — "share group if same label" claim unsupported.
categories.md
- P1 — Fabricated
menu_id/ "Menu Assignment". Categories have no menu reference; items referencecategory_id(RawModels/Category.php;StoreCategoryRequest.php). - P1 — Fabricated
visibletoggle,availabilityschedule,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 ispages/menus/categories/index.vue→ReportingCategories.vue. - P2 — Name/description max (100/300) not enforced; image limit 5MB wrong (10MB); SVG accepted but omitted.
- P3 —
sort_ordershown 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/lngnullable); 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_enableddevice-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/2with 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 ismenu_item_columns(2/3); order display isorder_number_display(queue_number/order_number/both); tipping comes from the location'scollect_tips. - P2 — Device fields
menu_id/printer_id/enablednot on the Device model (menus live on the profile). - P2 — Device status "online/offline/error" wrong —
getStatus()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_idwrong (it'sdefault_menu_id+custom_default_menu+extra_menu_ids). - P2 — Table fields
qr_code/activedon'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 perStoreLoyaltyRequest.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_neededthreshold (RawModels/Reward.php;LoyaltyService.php:633-657). - P1 —
max_redemption_percentagecap fabricated;earn_on subtotal/totalfabricated (points always on order total,LoyaltyService.php:483);excluded_categoriesfabricated. - 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.
typeoptions,auto_apply/promo_code, "stackable",minimum_order_amount,bogo.*,max_discount,usage_limit,applicable_items,channels,dining_options,free_item_id,activedo 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; theEnums/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/couponsis wrong. No coupons route/page/controller exists — coupons are Offers created with method "Code" (manage under/marketing/offers). Frontmatter route,applicable_pages,CouponController.php, andviews/marketing/coupons/are all fabricated. - P2 — Fabricated fields:
discount_type(free_shipping),max_discount,per_customer_limit(numeric — real is booleanlimit_one_usage_per_customer),first_order_only,applicable_to(all/categories/items),usage_limit, standaloneactive,description. - P1 — "Free shipping" discount type & calculation fabricated (existing FAQ +
FREEDELIVERYexample). - 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'sonline_ordering/kiosk/in_house/deliveroo/uber_eats/manualare 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. Noconfirmed/preparing/completed/refunded(Enums/OrderStatuses.php). - P2 — Dining option values wrong (Delivery/For Here/Pickup/Takeout; "Dine In" deprecated).
- P2 — Separate
payment_statusenum fabricated (filtering uses the singlestatusenum). - P1 — Refund presented as working, but it isn't wired — refund button commented out in
OrderDetail.vue; storerefundOrderis 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).
Recommended follow-up
- Correction pass per feature doc — rewrite
## Fields,## Route, paths, and## Examplesto 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). - Fix the existing fabricated
## FAQs/## Troubleshootingentries flagged above (ASAP, free-options-count, free-shipping, remote-restart, refund-as-working). - Extend the audit to the remaining ~95 feature/integration docs — the same fabrication pattern is likely present.
- 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).