Danvas
Danvas
DashboardSupportWelcome

👤 USER DOCS

User Guides

Getting Started

Getting StartedDashboard & OnboardingApp Settings

Tutorials

Tutorial: Setting Up Shift Tasks & ClosersTutorial: Managing Incidents in the InboxTutorial: Tracking Compliance & Sync StatusTutorial: Operational Workflows with the AI AssistantTutorial: Building & Deploying Custom Checklists

Daily Operations (Staff)

Shift Workspace & TasksService Day SetupDaily Line-UpStaff Service Day ReportsForms

Communication & Chat

Messages & AnnouncementsUnified Operations InboxAI Assistant

Manager & Admin Guides

Daily Line-Up SetupStaff SchedulingManaging LocationsNPS and Guest FeedbackCouponsContacts and Guest HistoryManager CloseoutsDaily Line-Up & ComplianceAnalyticsIncident ReportingWhistleblower Concerns & FeedbackAdmin Tools

⚙️ DEVELOPER DOCS

Getting Started

Getting StartedDevelopmentDeployment Guide

Architecture

Architecture OverviewExplanation: AI Integration & Tenant SecurityExplanation: Dynamic Forms Engine DesignExplanation: Compliance Ledger DesignExplanation: Live Sync & Data FreshnessData FlowArchitecture Decision Records

Core Domain

Core DomainDatabase ReferenceLocations DomainAuth & RBACScheduling DomainReports DomainIncidents DomainUnified Operations InboxLive Sync Data FreshnessToast Sync PipelineNotifications DomainCoupons and Guest NPSAudit Log & Compliance ArchitectureDesign Audit FindingsAI Chat IntegrationAnalytics & Tips Integration

Frontend

Frontend ArchitectureFormsLoading SkeletonsComponentsPWA & Offline ShellScreenshots

API Reference

API Reference

Endpoints

POS Sales APIOptimization Data APISchedule Shifts APIEmployee Export APIReports APIIncidents APIAI Chat APIPush Notifications APIWebhooks APICron API

Contributing

ContributingCode Examples

Security

Security & Compliance

Release Notes

What's New

Analytics & Tips Integration

Tip marts, performance facts, cron writers, and read surfaces for Intelligence → Analytics

Restaurant analytics in Danvas are Neon-native: warehouse fact tables land in public, app-owned marts live in analytics, and Intelligence → Analytics pages read those tables through server actions and TanStack Query. There is no external Analytics API roundtrip for tips or team-performance charts.

Related: ADR-0076 (tip calculation model), Analytics user guide, analytics upstream sync contract.


Architecture

graph TB
    subgraph Upstream["Toast sync"]
        TOAST[Toast POS + labor]
        FACTS[canonical facts<br/>f_checks, f_orders, f_time_entries]
    end

    subgraph Writers["Dagster"]
        D1[daily_certified_metrics_job]
        WEEKLY[completed_week_metrics_job]
        REPAIR[approval-bound repair]
        UPGRADES[Upgrade daily + weekly assets]
    end

    subgraph Marts["analytics schema"]
        TMD[tip_metrics_daily]
        TMH[tip_metrics_hourly]
        TMW[tip_metrics_weekly]
        EUS[employee_upgrade_scores_v2]
    end

    subgraph Readers["apps/app"]
        TIPS_UI[/analytics/tips]
        PERF[/analytics/sales-by-employee …]
        UP_UI[/analytics/upgrades]
        DASH[Dashboard tip enrichment]
    end

    TOAST --> FACTS --> D1 --> TMD
    D1 --> TMH
    TMD --> WEEKLY --> TMW
    REPAIR --> D1
    REPAIR --> WEEKLY
    UPGRADES --> EUS
    TMD --> TIPS_UI
    TMH --> PERF
    EUS --> UP_UI
    TMD --> DASH

Layer roles

LayerTablesPurpose
Factanalytics.f_*Normalized canonical warehouse facts — inputs to mart writers (replaces staging)
Martanalytics.tip_metrics_*, analytics.employee_upgrade_scores_v2 (+ weekly canonical evidence)App-owned rollups — sole read path for tips, performance, and coaching

Staging Deprecation & Canonical Fact Tables (Issue #908)

The legacy staging tables (analytics.stg_orders, analytics.stg_checks, analytics.stg_selections) were permanently dropped in migration 0155_mixed_paladin.sql to eliminate dual-writing overhead and raw payload parsing drift. They have been replaced with canonical fact models (analytics.f_orders, analytics.f_checks, etc.) as the source-of-truth warehouse layer.

Creation and Refresh:

  • Pipeline Ingest: Dagster's single-flight live_ingestion_job reads Toast source payloads and populates the canonical f_orders, f_checks, and f_time_entries tables.
  • Mart Refresh: The partitioned daily_certified_metrics_job rebuilds tip_metrics_daily and tip_metrics_hourly from canonical facts. The separate completed_week_metrics_job owns tip_metrics_weekly.
  • Degraded-State Handling: Since analytics tables are managed by background syncs and can lag or be temporarily absent (e.g., during database migrations or pipeline backfills), all app query surfaces (e.g., dashboard, lineup pacing, historical metrics) wrap analytics/fact table queries in try-catch blocks using isMissingRelationError(err). If missing, the app degrades gracefully (returning zeroed figures or empty datasets and marking analyticsUnavailable: true or logging a warning) rather than causing HTTP 500 errors.

Join keys use location_slug and employee_key — never Toast GUIDs in app read paths (ADR-0063).


Tip metrics marts

Schema: packages/database/src/schema/analytics.ts.

tip_metrics_daily

Primary key: (business_date, location_slug, employee_key).

Written by the partitioned Dagster tip_metrics_daily asset from certified facts, Toast time entries, and job mappings. Implements ADR-0076: charged tips, declared cash, estimated cash (18–30% band), combined tips, Bar Bot redistribution, and tip variance.

Key columns: actual_tips, declared_cash_tips, estimated_cash_tips, total_combined_tips, tip_pct, tip_variance (manager-only), gross_sales, hours_worked, sph, effective_hourly_pay, upgrade_score.

tip_metrics_hourly

Primary key: (business_date, hour_start, location_slug, employee_key).

Built by the partitioned Dagster D-1 asset from accepted analytics.f_time_entries plus certified order/check facts. Carries labor, sales splits, transactions, and guest counts at UTC hour grain with restaurant-local display fields for Team Performance and dashboard enrichment.

tip_metrics_weekly

Primary key: (week_start, location_slug, employee_key).

Aggregated from all seven finalized tip_metrics_daily partitions by the Dagster tip_metrics_weekly asset (weeks start Saturday, America/Chicago). tip_pct is a ratio of additive charged-tip and tipped-payment sums.


Writers and schedules

JobScheduleWhat it does
daily_certified_metrics_job05:00 America/ChicagoRebuilds the requested finalized daily tip partition and its hourly rows
completed_week_metrics_job06:00 Saturday America/ChicagoRebuilds the completed Saturday-Friday weekly partition after finality gates

Historical backfill

The app-side date-range writer is retired. Historical replay uses exact Dagster partitions or an approval-bound #989 repair manifest. Production repair requires an exact branch, scope, plan digest, checks, and rollback identity.

Coverage expectations after 1-year backfill (hartalliance, 2026-06-17):

  • Hourly — populated for all days with neon_fct_employee_hourly rows (~250–1,300 rows/day).
  • Daily — sparse before comp mart rollout (~May 2026); requires staging data for rebuild-comp. Older dates may show dailyRows: 0 while hourly still backfills.
  • Weekly — accumulates from daily; expect rows once daily mart has coverage for that week.

Historical rows created before the additive tipped-payment columns remain null until an approved repair replaces their partitions. Weekly materialization fails closed rather than treating those nulls as measured zeroes.


Read surfaces

UI routeData sourceNotes
/analytics/tipstip_metrics_dailyLeaderboards and detail views; tip variance gated to manager/admin
/analytics/sales-by-employee, /analytics/open-tabs, /analytics/avg-check, /analytics/sales-houranalytics.tip_metrics_hourlyTeam Performance section; server actions in apps/app/src/features/analytics/performance/
/analytics/upgradesgoverned Upgrade v2 daily and weekly servingProtein/side upgrade coaching
/analytics/submissionspublic form tablesAdmin-only platform usage
Dashboard historical tip enrichmenttip_metrics_dailyMust filter by location_slug, not Toast location GUID (ADR-0076 item 7)

Location scoping uses getAccessibleLocations(); all queries include authorized location_slug filters.


Upgrade scores

analytics.employee_upgrade_scores_v2 and analytics.employee_upgrade_score_weeks store the approved daily and weekly Upgrade components. They are owned by the same explicit D-1 and completed-week Dagster jobs. See CONTEXT.md (Upgrade Score, Protein/Side Revenue).


Operational checks

-- Tip mart freshness
SELECT 'daily', COUNT(*), MIN(business_date), MAX(business_date)
FROM analytics.tip_metrics_daily
UNION ALL
SELECT 'hourly', COUNT(*), MIN(business_date), MAX(business_date)
FROM analytics.tip_metrics_hourly
UNION ALL
SELECT 'weekly', COUNT(*), MIN(week_start), MAX(week_start)
FROM analytics.tip_metrics_weekly;

Dagster materialization and blocking-check records are the automation evidence. The retired /cron/rebuild-comp route returns HTTP 410 and is not a health source or fallback writer.


Related

Analytics User Guide

Database Schemas

Cron API

Live Freshness

AI Chat Integration

Technical details of the Vercel AI Gateway integration, AI tool schemas, and dashboard AI chat

Frontend Architecture

Technical design of the Next.js App Router and design system

On this page

ArchitectureLayer rolesStaging Deprecation & Canonical Fact Tables (Issue #908)Tip metrics martstip_metrics_dailytip_metrics_hourlytip_metrics_weeklyWriters and schedulesHistorical backfillRead surfacesUpgrade scoresOperational checksRelated