docs: add Phase 5 Implementation and Settlement Design Document

This commit is contained in:
Luis Gabriel Ramos Robles 2026-06-12 13:36:19 +00:00
parent acd139cf0d
commit 24d54f8bed

View file

@ -0,0 +1,123 @@
# Phase 5 Implementation & Settlement Design Document
**Variable Remuneration, Compensation, and Commissions System - Hoteles Estelar**
---
This document outlines the detailed architectural decisions, logical workflows, API specifications, UI mockups, and E2E verification plans for **Phase 5: Settlement Engine & Approvals**.
## 1. Architectural Strategy & Logic Flows
### 1.1. Core Settlement Calculation Engine (US-COM-006)
The system calculates commissions automatically based on defined rules. For a target period (`YYYY-MM`), the calculation steps are:
1. **Fetch Active Plans**: Resolve the active version of each `CompensationPlan` for target collaborators.
2. **Fetch Goals**: Resolve the goal amount for target users from the `Goal` table for the matching period.
3. **Fetch Sales Results**: Retrieve and sum the total `amount` and `salesCount` of `SalesResult` records generated by each collaborator in the period.
4. **Determine Achievement**:
$$\text{Achievement \%} = \frac{\text{Sum of Sales Amount}}{\text{Goal Amount}}$$
5. **Apply Rules**:
- Compare the Achievement % against boundaries in the plan's `CalculationRule` list.
- For `TIER` rules: Payout is calculated as:
$$\text{Commission} = \text{Sum of Sales Amount} \times \text{Rule Rate}$$
- For `BONUS` rules: Payout is the fixed `payoutAmount` configured.
6. **Enforce Cap Limit**: If the sum of calculated payouts exceeds the plan's `maxCap`, the payout is capped at `maxCap`.
### 1.2. Retroactive Adjustments & PMS Clawbacks
Hotel booking modifications, refunds, or cancellations frequently occur after past commissions have been paid. The engine processes retroactive adjustments as follows:
```mermaid
graph TD
A[Start Calculation for Month M] --> B[Retrieve Month M-1 and M-2 Sales Results]
B --> C[Fetch Original Settlements for Month M-1 and M-2]
C --> D{Any sales discrepancy found?}
D -->|No| E[Continue with normal month M calculation]
D -->|Yes| F[Recalculate past commission with live sales data]
F --> G[Calculate Delta: Corrected Payout - Previously Paid Payout]
G --> H[Create Pending Adjustment Settlement record in DB linked to Original ID]
H --> I[Add Delta to Month M Total Payout: Total = Commission + Bonus + Sum(Deltas)]
```
- If $\Delta \text{Payout} < 0$, it is treated as a **Clawback** (reduction).
- If $\Delta \text{Payout} > 0$, it is treated as an **Adjustment Credit** (addition).
- A self-referencing relationship is stored in the `Settlement` table via `originalSettlementId` to preserve the historical audit trail.
### 1.3. Webhook Branching & Environment Isolation
To prevent test calculation suites from polluting production records, the n8n workflows implement a strict routing architecture:
1. **Conditional Branching**: The n8n webhook endpoint evaluates the request URI path.
2. **Test/Staging calls**: Webhooks routed via `/webhook-test/calculate-settlements` route to `http://app-dev:3001` (running development stack, connected to `TEST_DATABASE_URL`) and validate signatures using `N8N_WEBHOOK_SECRET_DEV`.
3. **Production calls**: Webhooks routed via `/webhook/calculate-settlements` route to `http://app-prod:3000` and validate signatures using `N8N_WEBHOOK_SECRET`.
### 1.4. SOC 2 Log Redaction
All Next.js API routes run console outputs through a filtering proxy that redacts base salaries, passwords, and custom compensation parameters before writing to standard streams:
- Intercepts logs on settlement mutations.
- Replaces values for keys: `password`, `passwordHash`, `baseSalary`, `bankAccount`, and `salary` with `[REDACTED]`.
---
## 2. API Specifications
### 2.1. `POST /api/settlements/calculate` (Process Settlements)
- **Role Restriction**: `admin` | `analyst`
- **Request Body**:
```json
{
"period": "2026-06",
"simulateOnly": false
}
```
- **Response**:
- `201 Created` (Direct commit mode):
```json
{
"success": true,
"code": "SETTLEMENTS_CALCULATED",
"metadata": {
"count": 14,
"totalCommission": 85000.00,
"totalAdjustment": -3500.00
}
}
```
### 2.2. `POST /api/settlements/[id]/approve` (Approve Payout)
- **Role Restriction**: `commercial_leader`
- **Logic**: Enforces RLS. A leader can only approve settlements belonging to collaborators of hotels in their region. Sets status to `APPROVED`, logs `approvedBy`, and triggers a callback notification.
- **Response**: `200 OK`.
### 2.3. `POST /api/settlements/[id]/reject` (Reject Payout)
- **Role Restriction**: `commercial_leader`
- **Request Body**:
```json
{
"reason": "Venta reportada del colaborador no coincide con el cierre del hotel"
}
```
- **Logic**: Sets status to `REJECTED`, stores the mandatory reason in the database, and sets the state to allow correction and recalculation.
- **Response**: `200 OK`.
---
## 3. UI Design Specifications
### 3.1. Simulation Dashboard (`/sales/simulation`)
Provides analysts with a dry-run environment:
1. **Period Selector**: Dropdown to select the target payout month.
2. **Comparison Matrix**: Grid displaying:
- Collaborator | Plan Version | Goal | Confirmed Sales | Achievement % | Proposed Commission | Retroactive Adjustments | Total Payout (Simulated)
3. **Dry-Run Mode Toggle**: Execute calculations without writing data or triggering final callbacks.
### 3.2. Approvals Panel (`/settlements/approvals`)
Allows leaders to sign off on payouts:
1. **Segregated List**: Displays pending settlements, restricted to the leader's region.
2. **Reason Modal**: Appears when selecting "Rechazar", requiring a text entry.
3. **Status Badges**: Shows simulated vs finalized payout progress.
---
## 4. Headless Puppeteer Verification Plan
We will create a verification script `prisma/test-phase5-ui.js` that tests:
1. **Engine Accuracy**: Seed sales/goals, run calculation, verify output against expected mathematical tiers.
2. **Clawback Delta Logic**: Update a past month's sale amount, run the calculation again, and verify that the current month's proposed settlement contains a negative adjustment equal to the commission delta.
3. **Leader Isolation**: Confirm `lider_ctg` is blocked from approving a settlement for a collaborator in Antioquia (`EST-MDE`).
4. **Rejection Constraint**: Ensure a rejection request fails (400) if the reason is omitted.