Development guide for a Swedish budget management app with PostgreSQL, React, Express, and Drizzle ORM. Includes transaction import, auto-categorization, and Swedish payday logic.
Development guidance for working with a full-stack Swedish budget management application built with PostgreSQL, React, Express, and Drizzle ORM.
This is a Swedish budget management application with multi-user household support, intelligent CSV/XLSX bank import, auto-categorization, and Swedish payday-based balance calculations (25th of month).
**Tech Stack:**
```bash
npm run dev # Start development server with tsx (port 5000)
npm run build # Build for production (Vite + esbuild)
npm run start # Start production server from dist/
npm run check # TypeScript type checking
npm run db:push # Push Drizzle schema changes to PostgreSQL
```
**MOST COMMON BUG**: New fields added to database/API but not displaying in UI after page refresh.
Data passes through multiple manual conversion points: DB → API → Components → UI. Missing ANY conversion point causes fields to vanish.
1. **Backend** - Verify `dbStorage.ts` uses `select()` not explicit columns:
```typescript
// ✅ CORRECT - All fields included
const result = await db.select().from(transactions)
// ❌ WRONG - Fields will be missing
const result = await db.select({
id: transactions.id,
newField: transactions.newField,
}).from(transactions)
```
2. **Frontend Data Mapping** - Search ENTIRE codebase for `.map(item => ({` patterns:
```bash
grep -rn "\.map.*=> ({" client/src/
```
Add new field to EVERY conversion:
```typescript
// ✅ CORRECT - Include ALL fields
const items = apiData.map(item => ({
id: item.id,
name: item.name,
newField: item.newField, // NEW FIELD - Add everywhere
}));
```
3. **Transaction Orchestrator** - Update ALL conversion points in `budgetOrchestrator.ts`:
- `forceReloadTransactions()` (~line 1960)
- `setTransactionsForMonth()` (~line 3260)
- ALL `.map(tx => ({` patterns
Add new field to every conversion:
```typescript
const converted: Transaction[] = transactions.map(tx => ({
id: tx.id,
linkedTransactionId: tx.linkedTransactionId,
newField: tx.newField, // NEW FIELD - Add here
correctedAmount: tx.correctedAmount,
}))
```
4. **Transaction Linking Functions** - Update ALL linking functions:
- `linkExpenseAndCoverage()`
- `coverCost()`
- `applyExpenseClaim()`
```typescript
updates: {
type: 'ExpenseClaim',
linkedTransactionId: positiveTxId,
newField: newValue, // NEW FIELD - Add to updates
isManuallyChanged: true
}
```
5. **Snake_case Conversion** - Add database compatibility in components:
```typescript
// In TransactionExpandableCard.tsx
let newField = propTransaction.newField || (propTransaction as any).new_field || null;
const converted = {
...propTransaction,
newField: newField, // Add converted field
}
```
6. **TypeScript Interfaces** - Update ALL relevant interfaces:
- `types/transaction.ts` - ImportedTransaction
- `types/budget.ts` - Transaction
- Add to type unions if needed
**Problem**: Payment transactions linked to family members via "Koppla Utbetalning" feature lost linkedPerson field after page refresh.
**Root Cause**: linkedPerson was missing from 5 critical data conversion points.
**Symptoms:**
**Required Fixes:**
1. `budgetOrchestrator.ts:1949` - Add to `forceReloadTransactions()`
2. `budgetOrchestrator.ts:3227` - Add to `setTransactionsForMonth()`
3. `TransactionExpandableCard.tsx:69,112` - Add snake_case conversion
4. `types/transaction.ts:35` - Add to ImportedTransaction interface
5. `types/budget.ts:27` - Add to Transaction interface
1. Add table schema in `shared/schema.ts` with UUID primary key
2. Create Zod insert/select schemas with validation
3. Implement CRUD in `server/dbStorage.ts` using `select()` (NOT explicit columns)
4. Add API routes in `server/routes.ts` with mock auth
5. Create TanStack Query hook in `client/src/hooks/`
6. **CRITICAL**: Search for ALL `.map(` functions and add new field everywhere
1. Create component in `client/src/pages/`
2. Add route to `client/src/App.tsx` Switch component
3. Add navigation in `client/src/components/AppSidebar.tsx`
1. File upload (CSV/XLSX) via `TransactionImportEnhanced.tsx`
2. Column mapping stored in `bankCsvMappings` per bank
3. Smart reconciliation prevents duplicates
4. Balance calculation using Swedish payday logic (25th)
5. Auto-categorization via UUID-based `categoryRules`
6. Persistence to PostgreSQL with audit trail
Mock auth middleware injects `dev-user-123` for all routes:
```bash
DATABASE_URL # PostgreSQL connection (required for production)
PORT # Server port (default: 5000)
NODE_ENV # development/production
```
1. **Always use UUIDs** - Never string-based entity lookups
2. **Orchestrator Pattern** - ALL mutations through `budgetOrchestrator.ts`
3. **Field Mapping** - Search for ALL `.map(` when adding new fields
4. **No Explicit Columns** - Use `db.select().from(table)` not `db.select({...})`
5. **Real Data Testing** - Test imports with actual Swedish bank CSV/XLSX files
6. **Payday Validation** - Verify 25th-of-month balance calculations
7. **TanStack Query** - Use for ALL server state management
8. **Mock Auth** - `dev-user-123` auto-injected in development
If a field appears in API but not UI:
1. Check backend uses `select()` without explicit columns
2. Run: `grep -rn "\.map.*=> ({" client/src/`
3. Add field to ALL conversion points found
4. Check `budgetOrchestrator.ts` has field in ALL transaction conversions
5. Add snake_case → camelCase conversion if needed
6. Verify TypeScript interfaces include field
7. Test with fresh page reload
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/swedish-budget-app-development-guide/raw