Demo / Staging Dataset Developer Guide
April 9, 2026 · View on GitHub
Owner: Accounter Fullstack Team
Status: Active
Last Updated: 2024-11-30
Related: docs/archive/demo-staging-dataset-spec.md,
docs/archive/demo-staging-dataset-todo.md
Overview
The demo staging dataset system provides a use-case-driven approach to seeding rich, deterministic financial data for staging environments. Each use-case represents a complete financial scenario (e.g., "monthly expense with foreign currency", "shareholder dividend", "client payment + refund cycle") that can be deployed with a single command.
Key Features
- Deterministic UUIDs: Stable entity IDs across deploys via UUID v5 generation
- Modular Registry: Self-contained use-case fixtures with metadata
- Destructive Reset: Clean slate on each deploy (domain data only; schema preserved)
- Production Safeguards: Hard refusal to run in production environments
- Comprehensive Validation: Ledger balance checks, entity reconciliation, VAT verification
Architecture Components
Use-Case Registry → Seed Orchestrator → Validation Layer
↓ ↓ ↓
TypeScript modules scripts/seed-demo validate-demo
(fixtures + meta) (DB population) (integrity checks)
Quick Start
Prerequisites
- Docker Compose Postgres running locally
- Migrations applied:
yarn workspace @accounter/migrations migration:run - Environment variables configured (see
.env.example)
Local Seed & Validate
# Seed demo data (requires explicit flag)
ALLOW_DEMO_SEED=1 yarn seed:staging-demo
# Validate seeded data
yarn validate:demo
# Combined workflow (recommended)
ALLOW_DEMO_SEED=1 yarn seed:staging-demo && yarn validate:demo
Expected Output
Seed Script (successful run):
✅ Connected to database
🧹 Clearing existing demo data...
✅ Domain tables cleared
✅ Seeding countries...
✅ Seeding FIAT exchange rates...
✅ Seeding VAT defaults...
✅ Creating admin business context...
✅ Admin Business ID: e153a289-b7e7-5735-8379-e62e859c88ab
📦 Loading 3 use-cases...
📦 Monthly Expense (Foreign Currency) (monthly-expense-foreign-currency)
📦 Shareholder Dividend (Q4 2024) (shareholder-dividend)
📦 Client Payment with Refund (client-payment-with-refund)
✅ All use-cases seeded successfully
✅ Demo data seed complete
Validation Script (successful run):
✅ Demo data validation passed
Local Development (No Auth0)
Use this flow when you want to run the app locally without logging in through Auth0.
- Seed demo data and copy the printed demo user ids:
ALLOW_DEMO_SEED=1 yarn seed:staging-demo
The seed script prints both adminUserId and accountantUserId.
- Configure local environment variables:
# Bypass toggle (server + client)
ALLOW_DEV_AUTH=1
# Optional: attach real Auth0 id to seeded demo admin user
DEMO_AUTH0_USER_ID=
# Choose which seeded user to impersonate
DEV_AUTH_USER_ID=<adminUserId-or-accountantUserId>
- Switch roles by swapping
DEV_AUTH_USER_ID:
- Use
adminUserIdforbusiness_ownercontext. - Use
accountantUserIdforaccountantcontext.
Restart client and server after changing environment variables.
Adding a Use-Case
Follow these steps to create a new financial scenario fixture:
Step 1: Create Use-Case File
Create a new file in the appropriate category folder:
# Example: Adding a payroll use-case
touch packages/server/src/demo-fixtures/use-cases/payroll/monthly-salary-payment.ts
Step 2: Define Use-Case Spec
// packages/server/src/demo-fixtures/use-cases/payroll/monthly-salary-payment.ts
import { Currency, CountryCode } from '@shared/enums'
import { makeUUID } from '../../helpers/deterministic-uuid.js'
import type { UseCaseSpec } from '../../types.js'
export const monthlySalaryPayment: UseCaseSpec = {
id: 'monthly-salary-payment',
name: 'Monthly Salary Payment',
description: 'Employee salary payment with tax withholding and social security deductions',
category: 'payroll',
fixtures: {
businesses: [
{
id: makeUUID('business', 'employee-john-doe'),
name: 'John Doe (Employee)',
country: CountryCode.Isr,
canSettleWithReceipt: true
}
],
taxCategories: [
{
id: makeUUID('tax-category', 'salary-expenses'),
name: 'Salary Expenses'
},
{
id: makeUUID('tax-category', 'bank-account-ils'),
name: 'Bank Account (ILS)'
}
],
financialAccounts: [
{
accountNumber: 'PAYROLL-ACCOUNT-001',
type: 'BANK_ACCOUNT',
ownerId: '{{ADMIN_BUSINESS_ID}}',
taxCategoryMappings: [
{
currency: Currency.Ils,
taxCategoryId: makeUUID('tax-category', 'bank-account-ils')
}
]
}
],
charges: [
{
id: makeUUID('charge', 'salary-payment-2024-11'),
ownerId: '{{ADMIN_BUSINESS_ID}}',
userDescription: 'November 2024 salary payment'
}
],
transactions: [
{
id: makeUUID('transaction', 'salary-payment-bank-transfer'),
chargeId: makeUUID('charge', 'salary-payment-2024-11'),
businessId: makeUUID('business', 'employee-john-doe'),
accountNumber: 'PAYROLL-ACCOUNT-001',
amount: '-15000', // 15,000 ILS expense
currency: Currency.Ils,
eventDate: '2024-11-30',
debitDate: '2024-11-30'
}
],
documents: [
{
id: makeUUID('document', 'salary-slip-2024-11'),
chargeId: makeUUID('charge', 'salary-payment-2024-11'),
creditorId: makeUUID('business', 'employee-john-doe'),
debtorId: '{{ADMIN_BUSINESS_ID}}',
serialNumber: 'PAY-2024-11-001',
type: 'RECEIPT',
date: '2024-11-30',
totalAmount: '15000',
currencyCode: Currency.Ils
}
]
},
metadata: {
author: 'your-name',
createdAt: '2024-11-30',
updatedAt: '2024-11-30'
},
expectations: {
ledgerRecordCount: 2, // Debit (salary expense) + Credit (bank account)
totalDebitILS: '15000.00',
totalCreditILS: '15000.00'
}
}
Step 3: Register Use-Case
Update the registry to include your new use-case:
// packages/server/src/demo-fixtures/use-cases/index.ts
import { monthlyExpenseForeignCurrency } from './expenses/monthly-expense-foreign-currency.js'
import { shareholderDividend } from './equity/shareholder-dividend.js'
import { clientPaymentWithRefund } from './income/client-payment-with-refund.js'
import { monthlySalaryPayment } from './payroll/monthly-salary-payment.js' // Add import
import type { UseCaseSpec } from '../types.js'
export const USE_CASE_REGISTRY: Record<string, UseCaseSpec[]> = {
expenses: [monthlyExpenseForeignCurrency],
equity: [shareholderDividend],
income: [clientPaymentWithRefund],
payroll: [monthlySalaryPayment] // Add to registry
}
export function getAllUseCases(): UseCaseSpec[] {
return Object.values(USE_CASE_REGISTRY).flat()
}
Step 4: Validate Locally
Test your new use-case before deploying:
# Run TypeScript compiler
yarn workspace @accounter/server tsc --noEmit
# Seed with your new use-case
ALLOW_DEMO_SEED=1 yarn seed:staging-demo
# Validate integrity
yarn validate:demo
Step 5: Commit & Deploy
git add packages/server/src/demo-fixtures/use-cases/
git commit -m "feat(demo): add monthly salary payment use-case"
git push
The next staging deploy will automatically include your use-case.
Deterministic UUID Best Practices
Naming Conventions
Format: Always use kebab-case for semantic names:
// ✅ Good
makeUUID('business', 'acme-consulting-llc')
makeUUID('charge', 'consulting-invoice-2024-11')
makeUUID('transaction', 'bank-transfer-usd-500')
// ❌ Bad
makeUUID('business', 'Acme Consulting LLC')
makeUUID('charge', 'consulting_invoice_2024_11')
makeUUID('transaction', 'BankTransfer500')
Namespace Guidelines
Entity Type as Namespace: Use the entity type as the namespace to prevent collisions:
// ✅ Good - Different entities, different namespaces
const businessId = makeUUID('business', 'acme-corp')
const chargeId = makeUUID('charge', 'acme-corp')
// These produce different UUIDs even with same name
// ❌ Bad - Risk of collision
const businessId = makeUUID('entities', 'acme-corp-business')
const chargeId = makeUUID('entities', 'acme-corp-charge')
Stability Rules
Never change a semantic name once deployed:
// ❌ BREAKING: Changing the name breaks external references
// Old version (deployed):
makeUUID('business', 'supplier-acme-llc')
// New version (breaks UUIDs):
makeUUID('business', 'supplier-acme-inc') // Different UUID!
// ✅ If entity changes, create a new version:
makeUUID('business', 'supplier-acme-llc-v2')
Uniqueness Verification
Combine entity type + descriptive name to ensure no collisions:
// ✅ Good - Unique combinations
makeUUID('business', 'us-supplier-acme-llc')
makeUUID('business', 'uk-supplier-acme-ltd')
makeUUID('charge', 'november-expense-2024')
makeUUID('charge', 'december-expense-2024')
// ⚠️ Be specific to avoid accidental duplicates
makeUUID('business', 'supplier-1') // Too generic
makeUUID('business', 'supplier-2') // Better: 'supplier-acme-llc'
Placeholder Usage
Use {{ADMIN_BUSINESS_ID}} for admin context references (resolved at seed time):
// ✅ Good - Placeholder for runtime resolution
charges: [
{
id: makeUUID('charge', 'my-charge'),
ownerId: '{{ADMIN_BUSINESS_ID}}', // Replaced during seed
userDescription: 'Test charge',
},
],
// ❌ Bad - Hardcoded UUID breaks determinism
ownerId: 'e153a289-b7e7-5735-8379-e62e859c88ab', // Don't hardcode
Troubleshooting
Seed Script Issues
Error: "Refusing to seed demo data in production environment"
Cause: NODE_ENV=production is set.
Solution: This is intentional protection. Never run seed script in production.
# For local/staging only
NODE_ENV=development ALLOW_DEMO_SEED=1 yarn seed:staging-demo
Error: "ALLOW_DEMO_SEED=1 required to run demo seed"
Cause: Safety flag not set.
Solution: Explicitly enable seeding:
ALLOW_DEMO_SEED=1 yarn seed:staging-demo
Error: "Foreign key violation on business_id"
Cause: Use-case references a business that doesn't exist in fixtures.
Solution: Ensure all referenced entities are defined in the use-case:
// ❌ Bad - Transaction references undefined business
transactions: [
{
businessId: makeUUID('business', 'some-vendor'), // Not in fixtures.businesses!
// ...
},
],
// ✅ Good - Business defined in fixtures
fixtures: {
businesses: [
{
id: makeUUID('business', 'some-vendor'),
name: 'Some Vendor LLC',
country: CountryCode.Usa,
},
],
transactions: [
{
businessId: makeUUID('business', 'some-vendor'), // Reference exists
// ...
},
],
},
Error: "Fixture insertion failed"
Cause: Validation failed or database constraint violation.
Solution: Check detailed error in stack trace. Common issues:
- Missing required fields: Ensure all NOT NULL columns have values
- Type mismatch: Use
Currency.Ilsnot'ILS'(enums required) - Invalid dates: Use ISO format
'2024-11-30'not'30/11/2024' - Amount format: Use strings for numeric amounts:
'-500'not-500
Validation Script Issues
Error: "Accounter Admin Business entity missing"
Cause: Admin business context not created during seed.
Solution: Check createAdminBusinessContext() succeeded:
# Re-run seed with verbose logging
ALLOW_DEMO_SEED=1 yarn seed:staging-demo 2>&1 | grep -i "admin"
# Should see:
# ✅ Creating admin business context...
# ✅ Admin Business ID: e153a289-...
Error: "Charge count mismatch: expected X, got Y"
Cause: Seed script didn't insert all use-case charges.
Solution:
- Check for seed errors (scroll up in terminal output)
- Verify all use-cases registered in
use-cases/index.ts - Re-run seed with clean DB:
# Reset DB (if needed)
yarn workspace @accounter/migrations db:reset
yarn workspace @accounter/migrations migration:run
# Re-seed
ALLOW_DEMO_SEED=1 yarn seed:staging-demo
Error: "Ledger unbalanced for charge X"
Cause: Ledger generation produced unbalanced entries (debits ≠ credits).
Solution: This indicates a bug in ledger generation logic, not the demo data. File an issue with:
- Use-case ID
- Expected vs actual debit/credit totals
- Ledger records JSON (query DB directly)
Import/TypeScript Issues
Error: "Cannot find module '@shared/enums'"
Cause: Missing or incorrect import path.
Solution: Ensure enums are imported correctly:
// ✅ Good
import { Currency, CountryCode } from '@shared/enums'
// ❌ Bad
import { Currency } from '../../../shared/enums' // Relative path breaks
Error: "Type 'string' is not assignable to type 'Currency'"
Cause: Using string literals instead of enum values.
Solution: Use enum values:
// ✅ Good
currency: Currency.Ils,
currencyCode: Currency.Usd,
// ❌ Bad
currency: 'ILS', // Type error
currencyCode: 'USD',
Database Connection Issues
Error: "ECONNREFUSED 127.0.0.1:5432"
Cause: Postgres not running or wrong connection settings.
Solution:
# Check Postgres running
docker ps | grep postgres
# Start if not running
docker-compose -f docker/docker-compose.dev.yml up -d
# Verify env vars
cat .env | grep POSTGRES
Render Build Command Example
Staging Environment Configuration
Build Command (set in Render dashboard):
yarn install \
&& yarn build \
&& yarn workspace @accounter/migrations migration:run \
&& ALLOW_DEMO_SEED=1 yarn seed:staging-demo \
&& yarn validate:demo
Breakdown:
yarn install- Install dependenciesyarn build- Compile TypeScriptyarn workspace @accounter/migrations migration:run- Apply schema migrationsALLOW_DEMO_SEED=1 yarn seed:staging-demo- Seed demo data (with safety flag)yarn validate:demo- Verify data integrity (fails deploy if validation errors)
Environment Variables
Set these in Render dashboard for staging environment:
# Database
POSTGRES_HOST=<render-internal-postgres-host>
POSTGRES_PORT=5432
POSTGRES_DB=accounter_staging
POSTGRES_USER=accounter_user
POSTGRES_PASSWORD=<secret-from-render>
POSTGRES_SSL=1
# Environment
NODE_ENV=staging
ALLOW_DEMO_SEED=1
# Auth (set manually in dashboard)
AUTHORIZED_USERS=demo@accounter.local,admin@accounter.local
Deployment Workflow
Git Push → Render Webhook → Build Command Execution:
1. Install
2. Build
3. Migrate
4. Seed ✅
5. Validate ✅
6. Deploy
If validation fails (exit code 1), Render will not deploy the build, preventing broken data from reaching staging.
Post-Deploy Verification
After successful deployment, verify via UI:
- Navigate to staging URL:
https://accounter-staging.onrender.com - Login with
AUTHORIZED_USERScredentials - Check dashboard for:
- 3+ charges visible (one per use-case)
- Transactions in multiple currencies (ILS, USD)
- Ledger entries balanced
- Spot-check foreign currency conversion (e.g., 500 USD → 1750 ILS @ 3.5 rate)
Additional Resources
- Specification:
docs/demo-staging-dataset-spec.md- Complete technical spec - Implementation Plan:
docs/demo-staging-dataset-prompt-plan.md- Step-by-step prompts - TODO Checklist:
docs/demo-staging-dataset-todo.md- Progress tracking - Fixture Loader:
packages/server/src/__tests__/helpers/fixture-loader.ts- DB insertion logic - Validators:
packages/server/src/demo-fixtures/validators/- Ledger validation rules
Support
For questions or issues:
- Check this guide's Troubleshooting section
- Review seed script logs for detailed error messages
- Inspect validation output for specific failures
- File an issue with reproducible steps if bug suspected
Last Updated: 2024-11-30
Maintained By: Accounter Fullstack Team