# Design: add-toldo-poc-capacitor ## Architecture Overview This project follows a clean, layered architecture to ensure testability, maintainability, and ease of evolution. ``` ┌─────────────────────────────────────────────────────────────┐ │ apps/toldo-app │ │ (Vite + Capacitor UI) │ └─────────────────────────┬───────────────────────────────────┘ │ depends on ┌─────────────────────────▼───────────────────────────────────┐ │ packages/ui │ │ (Reusable, small UI components) │ └─────────────────────────┬───────────────────────────────────┘ │ depends on ┌─────────────────────────▼───────────────────────────────────┐ │ packages/services │ │ (Application use cases/orchestration) │ └─────────────┬───────────────────────────┬───────────────────┘ │ depends on │ depends on ┌─────────────▼───────────┐ ┌───────────▼───────────────────┐ │ packages/data │ │ packages/core │ │ (Dexie, repositories) │ │ (Domain, entities, rules) │ └─────────────────────────┘ └───────────────────────────────┘ ``` ### Dependency Rules (Critical) - **core/**: NO dependencies on data/, services/, ui/, apps/, @capacitor/*, or any framework. Pure TypeScript only. - **data/**: Depends only on core/. Implements repository interfaces defined in core/ or data/contracts. - **services/**: Depends on core/ and data/. Contains application logic/use cases. No direct UI dependencies. - **ui/**: Depends on core/ and services/ via interfaces/contracts. No direct Dexie or Capacitor imports. - **apps/toldo-app/**: Assembles everything, configures DI/composition root, handles Capacitor plugins (mockable), UI composition only here. ## SOLID Principles - **Single Responsibility (SRP)**: Each class/function/module has one reason to change. Components are small and focused. - **Open/Closed (OCP)**: Open for extension, closed for modification. Use interfaces and composition. - **Liskov Substitution (LSP)**: Subtypes must be substitutable for base types. - **Interface Segregation (ISP)**: Many specific interfaces over one general interface. Repository interfaces are granular. - **Dependency Inversion (DIP)**: Depend on abstractions, not concretions. core defines interfaces, data implements them. ## Domain Model ### Core Entities ```typescript // Producer interface Productor { id: string; nombre: string; activo: boolean; createdAt: Date; } // Product - different per producer even if same name interface Producto { id: string; productorId: string; nombrePresentacion: string; // e.g., "Café molido 250gr Santa Ana" precioUnitario: number; activo: boolean; createdAt: Date; } // Initial inventory / movements interface MovimientoInventario { id: string; fecha: Date; tipo: 'INGRESO_INICIAL' | 'INGRESO_SEMANA' | 'AJUSTE' | 'VENTA'; productoId: string; cantidad: number; motivo?: string; // required for AJUSTE recibidoPor?: string; referenciaId?: string; // links to venta/ajuste createdAt: Date; } // Sale interface Venta { id: string; fecha: Date; lineas: VentaLinea[]; subtotal: number; total: number; createdAt: Date; } interface VentaLinea { id: string; ventaId: string; productoId: string; cantidad: number; precioUnitario: number; subtotal: number; formaPago: 'EFECTIVO' | 'QR'; } // Expense interface Gasto { id: string; fecha: Date; concepto: string; monto: number; formaPago?: 'EFECTIVO' | 'QR'; createdAt: Date; } // Adjustment with reason interface Ajuste { id: string; fecha: Date; productoId: string; cantidadAnterior: number; cantidadNueva: number; diferencia: number; motivo: string; // REQUIRED modificadoPor?: string; createdAt: Date; } // Cash reconciliation interface CuadreCaja { id: string; fecha: Date; baseInicial: number; totalVentasEfectivo: number; totalVentasQR: number; totalGastosEfectivo: number; totalGastosQR: number; saldoEsperadoEfectivo: number; saldoRealEfectivo?: number; descuadreEfectivo?: number; totalQRRegistrado?: number; totalQRBanco?: number; descuadreQR?: number; totalVentas: number; observaciones?: string; createdAt: Date; } ``` ### Critical Rules 1. **Products unique by producer**: `producto` is uniquely identified by `(productorId, nombrePresentacion)`. Same name from different producers = different products. 2. **Adjustments require reason**: All adjustments must have a non-empty `motivo`. Adjustments are tracked separately and marked as changed in reports. 3. **Inventory reconciliation**: Initial inventory vs real differences must be recorded. End-of-day inventory should match initial inventory minus sold products. 4. **Cash vs QR separation**: Track cash and QR separately in sales, expenses, and reconciliation. Compare QR registered vs bank. 5. **Traceability**: All modifications (adjustments to inventory/sales/cash discrepancies/QR discrepancies) must be logged with reason. 6. **Movements**: Track inventory movements both on market day and during the week (week restocks). ## Data Layer (Dexie) ### Schema Versioning - Database schema versioned with Dexie migrations - Export/import JSON has `schemaVersion`, `appVersion`, `exportedAt` - Import validates versions and migrates or rejects with clear message ### Tables ```typescript db.version(1).stores({ productores: 'id, nombre, activo', productos: 'id, productorId, nombrePresentacion, [productorId+nombrePresentacion]', movimientosInventario: 'id, fecha, tipo, productoId', ventas: 'id, fecha', ventaLineas: 'id, ventaId, productoId, formaPago', gastos: 'id, fecha', ajustes: 'id, fecha, productoId', cuadres: 'id, fecha' }); ``` ### Repository Pattern All data access goes through repository interfaces in core/contracts or data/contracts. Implementations in packages/data using Dexie. ## Testing Strategy | Layer | Tool | Approach | |-------|------|----------| | Core (domain/rules) | Vitest | **TDD mandatory** - write tests first, pure unit tests, no mocks needed | | Data (repos) | Vitest + fake-indexeddb | Integration tests with in-memory IndexedDB | | Services (use cases) | Vitest | Unit tests with mocked repositories | | UI components | Vitest + @testing-library/* | Test behavior, not implementation; user interactions | | Capacitor plugins | vi.mock | Always mock @capacitor/* in unit tests | ## Build & Distribution - **Dev**: `vite` dev server - **Web build**: `vite build` outputs to `dist/`, servable by static HTTP server - **Capacitor sync**: `npx cap sync android` copies web assets and updates native project - **APK**: Build via Android Studio or `./gradlew assembleDebug` in android/ folder - **Offline**: All assets bundled in APK, IndexedDB persists data, no network required ## Quality Gates - TypeScript strict mode enabled - All core logic tested (TDD) - Components small and focused (< ~150 lines when possible) - Linting passes - Type checking passes - Tests pass - Build succeeds - SOLID principles followed (validated by architecture skill)