# 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**: Audit logs are explicitly recorded within transactions inside the Next.js API endpoints (`tx.auditLog.create`) utilizing the authenticated user session details. To enforce financial integrity and SOC 2/GDPR compliance, the database layer implements strict **Row-Level Security (RLS) policies** on the `audit_logs` table that permit only `INSERT` operations (making the logs strictly append-only) and block all `UPDATE` or `DELETE` mutations for all users, including system administrators. Sensitive inputs (like password hashes or detailed bulk data records) are redacted/masked at the application layer before serialization.

---

## 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 (.env Layout)
* **Production Stack Variables (app-prod)**:
  * `DATABASE_URL_PROD`: Connection string for the production PostgreSQL database.
  * `NEXT_PUBLIC_APP_URL`: The production public domain (`https://hotels.gaboggamer.online`).
  * `NEXTAUTH_SECRET`: Secret token used to sign and verify session JWTs in production.
  * `N8N_WEBHOOK_URL`: Webhook endpoint for live production calculation workflows (`https://n8n.gaboggamer.online/webhook/calculate-commissions`).
  * `N8N_WEBHOOK_SECRET`: Signature token to verify n8n webhook payload authenticity.
  * `APP_PROD_INTERNAL_URL`: Internal container URL for prod mapping (`http://special-hotel-prod:3000`).
* **Development & Test Stack Variables (app-dev)**:
  * `DATABASE_URL_DEV`: Connection string for the development PostgreSQL database.
  * `TEST_DATABASE_URL`: Dedicated connection string for the isolated test/sandbox database.
  * `NEXT_PUBLIC_APP_URL_DEV`: Development URL (`http://localhost:3001`).
  * `NEXTAUTH_SECRET_DEV`: JWT signature token for development sessions.
  * `N8N_TEST_WEBHOOK_URL`: Sandbox webhook URL for development calculation testing (`https://n8n.gaboggamer.online/webhook-test/calculate-commissions`).
  * `N8N_WEBHOOK_SECRET_DEV`: Signature token to authorize development webhook payloads.
  * `APP_DEV_INTERNAL_URL`: Internal container URL for dev mapping (`http://special-hotel-dev:3000`).

### 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
        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
        boolean is_anomaly
        json flagged_reason
        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
        boolean ai_audited
        json ai_audit_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 & RLS)

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 / Auditor** | 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 database RLS policies (`audit_logs_insert_policy` and `audit_logs_select_policy`) that only permit `INSERT` operations and explicitly block `UPDATE` and `DELETE` actions for all users, including administrators. It is strictly write-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` / `special-hotel-prod`)**:
  * **Network Port**: Exposed on port `3000` (mapped via Caddy to `hotels.gaboggamer.online`).
  * **Database**: Bound to the production `DATABASE_URL_PROD`.
  * **Logs**: Prefixed with `[PROD]` inside the container engine.
* **Development Container (`app-dev` / `special-hotel-dev`)**:
  * **Network Port**: Exposed on port `3001` (mapped via Caddy to `localhost:3001` or developmental paths).
  * **Database**: Bound to `DATABASE_URL_DEV` (with integration testing executing queries on `TEST_DATABASE_URL`).
  * **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] Required development-exclusive variables (TEST_DATABASE_URL, N8N_TEST_WEBHOOK_URL) are missing.`
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 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.
* **Keyword Localization**: Core calculation states and types (such as `ACTIVE/ACTIVO`, `CLOSED/CERRADO`, `REVENUE/INGRESOS`, `NIGHTS/NOCHES`) are mapped at the dictionary level so views translate database enums to localized strings seamlessly.

### 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]`.
