# Architecture and System Design **Variable Remuneration, Compensation, and Commissions System - Hoteles Estelar** --- ## 1. Technical Stack * **Frontend**: Next.js App Router (React + TypeScript). * **Styling**: Vanilla CSS utilizing **CSS Modules** (`*.module.css`) for component-level style isolation, paired with a global variables system (`globals.css`) defining the design tokens. * **Backend**: Next.js API Routes (Node.js runtime). * **ORM**: Prisma ORM (providing type-safe database queries and automated schema migrations). * **Database**: PostgreSQL (decoupled, configured via environment variables to run on any host/network). * **Workflow Engine**: **n8n** (external service triggered via API Webhooks). Runs the actual calculation steps, anomaly checks, AI-assisted audits, and notifications. This allows live monitoring of tasks and flexible AI model switching (e.g., swapping OpenAI/Anthropic/Ollama models within n8n nodes without rebuilding the Next.js codebase). * **Auditing**: Ready-to-use, community-proven **`@explita/prisma-audit-log`** Prisma Client Extension. It automatically intercepts database mutations (create, update, delete), tracks changes (old vs new state), handles sensitive field masking (e.g. passwords), and records logs into the `AuditLog` table. This approach is database-agnostic, requires no native OS dependencies, and runs purely inside the runtime. --- ## 2. Decoupled Network & Environment Independence The project is structured to run in any isolated Docker environment. All parameters are fed via environment variables to cleanly partition production and development configurations: ### 2.1. Environment Variables * **Core Variables (Both Env)**: * `DATABASE_URL`: Connection string for the active database (Production or Development). * `NEXT_PUBLIC_APP_URL`: The domain or local host path of the running application. * `NEXTAUTH_SECRET`: Secret key for JWT session validation. * **Development-Exclusive Test Variables**: * `TEST_DATABASE_URL`: Connection string to the secondary sandbox/test database. * `N8N_TEST_WEBHOOK_URL`: The n8n testing webhook entry point. Used by development services and test runners. * `N8N_WEBHOOK_SECRET`: Token to authorize and verify n8n webhook payload signatures locally. ### 2.2. Component Interaction: ```mermaid graph TD User([User Client]) -->|HTTPS| WebApp[Next.js App Router] subgraph Isolated Network WebApp -->|Prisma Client| DB[(PostgreSQL Main)] WebApp -.->|Prisma Client - Test Env| DBTest[(PostgreSQL Test)] WebApp -->|HTTP POST Webhook /webhook-test| n8n[n8n Workflow Engine] n8n -->|IF Webhook Path Match| DBTest n8n -->|Else| DB n8n -->|HTTP POST Callback| WebApp end ``` --- ## 3. Database Schema Design (Entity-Relationship Diagram) ```mermaid erDiagram REGIONS ||--o{ HOTELS : contains HOTELS ||--o{ USERS : houses USERS ||--o{ GOALS : achieves USERS ||--o{ SALES_RESULTS : generates USERS ||--o{ SETTLEMENTS : receives COMPENSATION_PLANS ||--o{ CALCULATION_RULES : dictates COMPENSATION_PLANS ||--o{ SETTLEMENTS : calculates USERS ||--o{ AUDIT_LOGS : executes USERS ||--o{ NOTIFICATIONS : receives REGIONS { int id PK string name string code } HOTELS { int id PK string name string code int region_id FK string status } USERS { int id PK string username string email string password_hash string role "admin | director | hotel_manager | commercial_leader | analyst | auditor | collaborator" int hotel_id FK string area string status "ACTIVE | INACTIVE" datetime created_at } COMPENSATION_PLANS { int id PK string name string code UK datetime validity_start datetime validity_end string type "PERCENTAGE | SCALE | CONDITIONAL | FIXED" string formula "JSON or string representation" decimal meta_amount decimal percentage_rate decimal max_cap string status "DRAFT | ACTIVE | INACTIVE" int version int created_by FK datetime created_at } CALCULATION_RULES { int id PK int plan_id FK string type "TIER | BONUS" decimal min_achievement "percentage" decimal max_achievement "percentage" decimal rate "multiplier or percentage" decimal payout_amount "fixed payment" } GOALS { int id PK string target_type "INDIVIDUAL | TEAM | HOTEL" int target_id "user_id, team_id, or hotel_id" string period "YYYY-MM" decimal amount datetime created_at } SALES_RESULTS { int id PK string source "EXCEL | API" int hotel_id FK int user_id FK "colaborador" string period "YYYY-MM" decimal amount int sales_count string status "PENDING | PROCESSED" string idempotency_key UK string transaction_id int uploaded_by FK datetime created_at } SETTLEMENTS { int id PK string period "YYYY-MM" int plan_id FK "References specific version of the plan" int user_id FK decimal sales_amount decimal goal_amount decimal achievement_percentage decimal calculated_commission decimal calculated_bonus decimal adjustment_amount "Clawback or adjustment delta" decimal total_payout "calculated_commission + calculated_bonus + adjustment_amount" string status "SIMULATED | PENDING | APPROVED | REJECTED" int approved_by FK datetime approved_at string rejection_reason int original_settlement_id FK "Self-references the settlement adjusted, if any" string adjustment_notes datetime created_at } AUDIT_LOGS { int id PK int user_id FK string action "CREATE | UPDATE | DELETE | APPROVE | REJECT | LOGIN" string target_table int target_id json previous_value json new_value string ip_address datetime created_at } NOTIFICATIONS { int id PK int user_id FK string title string message string status "UNREAD | READ" string type "EMAIL | PUSH" datetime sent_at } ``` --- ### 4. Workflows & n8n Integration Model To achieve high modularity, testability, and adaptability, the system implements a **Thick Client (Next.js), Thin Coordinator (n8n)** architecture: * **Zero Database Connections in n8n**: n8n must not connect directly to PostgreSQL. All data read/write mutations are handled by Next.js API endpoints. * **Minimal Code Blocks in n8n**: Business logic, math calculations, and transaction management are kept on the Next.js side where they are type-safe and fully covered by unit tests. * **All AI/LLM inside n8n**: All LLM queries and AI-assisted audits are processed using native n8n LangChain and AI Agent nodes, allowing prompt/model hot-swapping without redeploying the Next.js codebase. * **Stalwart Email Notifications**: Email notifications are dispatched directly by n8n using native SMTP nodes configured to use the company's Stalwart mail server. ### 4.1. Sales Data Import Workflow 1. **Trigger**: Next.js calls `POST /webhook/calculate-commissions` (or `/webhook-test/...` in dev) forwarding parsed Excel rows, uploader ID, and an idempotency key. 2. **n8n Processing**: - Queries `POST /api/n8n/validate-sales` to resolve database entities (collaborators, hotels) and retrieve historical standard deviation baselines. - Passes data to a native LangChain LLM Chain node (`@n8n/n8n-nodes-langchain.chainLlm`) connected to a **Primary Chat Model** (DeepSeek, `lmChatDeepSeek`) and a **Fallback Chat Model** (Google Gemini, `lmChatGoogleGemini`) to perform semantic anomaly validation and record anomaly flags. If the primary model fails or is rate-limited, n8n automatically falls back to the Google Gemini model. - **Dynamic Credential Mapping**: To support isolated project spaces, the bootstrap and test scripts query the n8n API (`GET /api/v1/credentials`) at deploy time, resolve the correct credential IDs for the DeepSeek and Gemini accounts in the workspace, and inject them into the workflow definition before creating or updating the workflow. 3. **Response**: n8n calls `POST /api/sales/batch-save` (authenticated via `x-n8n-signature`) to persist finalized results in the PostgreSQL database under admin privileges. ### 4.2. Settlement Calculation Workflow 1. **Trigger**: Next.js calls n8n to execute the calculation for period `YYYY-MM`. 2. **n8n Processing**: - Gathers plans, goals, and sales results from `/api/n8n/fetch-calculation-data?period=YYYY-MM`. - Offloads calculation processing to `/api/n8n/process-formula` to run the type-safe mathematical rules engine (tiers, bonuses, clawbacks). - Runs a native AI Agent node to audit calculated payouts for compliance (e.g. capping rules, negative payouts). - Dispatches email alerts using the Stalwart mail server if any extreme payouts require analyst reviews. 3. **Response**: Persists settlements via `POST /api/n8n/save-settlements` with static signature checks. ### 4.3. Test Webhook Branching & Environment Isolation in n8n To ensure complete isolation of production data, all n8n workflows follow a strict testing branch architecture: 1. **Webhook Entry Node**: n8n listens on two webhook path variants: - Production calls hit: `/webhook/calculate-commissions` or `/webhook/calculate-settlements` - Test suite calls hit: `/webhook-test/calculate-commissions` or `/webhook-test/calculate-settlements` 2. **Conditional Path Routing**: - An `IF` node immediately checks if the webhook request path contains `webhook-test`. - **True (Test Mode)**: The workflow sets its Next.js target host variables to the development instance (`https://special-hotel-dev.gaboggamer.online`), which uses `TEST_DATABASE_URL` (the secondary test sandbox database) and signing secret `N8N_WEBHOOK_SECRET_DEV`. - **False (Prod Mode)**: The workflow executes against the production instance, validating calls using `N8N_WEBHOOK_SECRET`. --- ## 5. Security & Access Control Model (RBAC) We define role-based access restrictions as follows: | Role | Access Level | Restrictions | | :--- | :--- | :--- | | **Administrador** | Full system write & read. | None. | | **Director Comercial** | Reads all dashboards & reports. Can configure metadata. | Cannot calculate or approve. | | **Gerente Hotel** | Reads data, sales, and settlements for their specific Hotel. | Restricted to `hotel_id`. | | **Líder Comercial** | Triggers simulations, views dashboards. Approves/Rejects settlements. | Restricted to their region/team. | | **Analista Financiero** | Reviews calculations. Exports consolidated PDF/Excel reports. | Cannot approve. | | **Consulta** | Read-only. | No mutations allowed. | | **Colaborador** | Consults own history & dashboard. | Restricted to `user_id`. | ### 5.1. PostgreSQL Row-Level Security (RLS) & Data Isolation To ensure absolute segregation of sensitive compensation data, the database implements **Row-Level Security (RLS)**. RLS is enforced at the database layer (or via Prisma client middleware setting transaction context parameters), guaranteeing security even if application queries omit filters. * **Tenant Isolation Rules**: * **Colaboradores**: Can only select rows from `SETTLEMENTS`, `SALES_RESULTS`, and `GOALS` where `user_id = current_setting('app.current_user_id')`. * **Gerentes**: Can only select rows where `hotel_id = current_setting('app.current_hotel_id')`. * **Líderes**: Can select rows within their assigned regions or teams (`region_id = current_setting('app.current_region_id')`). * **Administradores / Analistas**: RLS is bypassed to allow system-wide computations and consolidated reporting. * **Audit Trail Immutability**: * The `AUDIT_LOGS` table has RLS policies that prevent `UPDATE` or `DELETE` actions for all users, including administrators. It is strictly write-only (`INSERT` operations only). --- ## 6. Dual-Environment Docker Deployment Model We design the containerized architecture to run two distinct instances of the application concurrently from the same codebase, ensuring clean division. ### 6.1. Service Configurations * **Production Container (`app-prod`)**: * **Network Port**: Exposed on port `3000` (mapped via Caddy to `special-hotel.yourdomain.com`). * **Database**: Bound to the production `DATABASE_URL`. * **Logs**: Prefixed with `[PROD]` inside the container engine. * **Development Container (`app-dev`)**: * **Network Port**: Exposed on port `3001` (mapped via Caddy to `special-hotel-dev.yourdomain.com`). * **Database**: Bound to `TEST_DATABASE_URL` (acting as its main `DATABASE_URL` for test isolated migrations). * **Logs**: Prefixed with `[DEV]` for easy debugging contrast. ### 6.2. Graceful Dev Failure Model The development instance utilizes a validation startup hook. If any development-exclusive environment variables (like `TEST_DATABASE_URL`) are omitted: 1. The `app-dev` container logs a clear notification: `[DEV] Missing required development variables. Gracefully shutting down development service.` 2. The entrypoint script exits with **exit code 0**. 3. Docker or the compose orchestrator registers the container as cleanly stopped (not crashed). The production stack is completely unaffected, avoiding restart-loop penalties or deployment failures. --- ## 7. Internationalization (i18n) Architecture The internationalization architecture provides bilingual support (English and Spanish) across the application, separating client-side UI translations from AI audit translation flows. ### 7.1. Client-Side Translation Context * **Scaffolding**: Static JSON dictionaries map UI strings under `src/lib/i18n/dictionaries/`. * **State Management**: A React Context provider (`LocaleProvider`) coordinates the selected locale across the client components, utilizing local storage for persistence. * **Component Translation**: UI components call a lightweight `t(key)` translation hook, dynamically rendering headers, tables, validation warnings, and labels. ### 7.2. Bilingual AI Audit Translation Flow To maintain semantic accuracy and structured parsing within the n8n pipelines, AI audits execute in English, followed by a dedicated translation stage: ```mermaid graph LR Engine[Next.js API] -->|Calculated Data| n8n[n8n Workflow] n8n -->|Step 1: Audit in EN| LLM[LLM Node] LLM -->|Audit Notes in EN| Trans[LLM Translation Node] Trans -->|Translate to EN & ES| Output[Bilingual JSON Object] Output -->|Callback Save| NextDb[Next.js Database API] ``` * **Storage**: Localized AI outputs are transmitted as JSON objects: ```json { "en": "Audit warning: High commission payout.", "es": "Advertencia de auditoría: Pago de comisión alto." } ``` These are stored in the database as PostgreSQL `JSONB` fields (`flaggedReason` on `SalesResult`, and `aiAuditNotes` on `Settlement`). * **Rendering**: The frontend page renders the string corresponding to the user's active locale: `item.aiAuditNotes[locale]`.