9 KiB
Phase 4 Implementation & Integration Design Document
Variable Remuneration, Compensation, and Commissions System - Hoteles Estelar
This document outlines the detailed design decisions, schema mappings, API endpoints, UI layouts, and security/idempotency validations planned for Phase 4: Data Import & Integrations.
1. Architectural Strategy & Logic Flows
1.1. Excel Parser & Data Validation Flow
To prevent manual data-entry errors, the system allows authorized users (Administrators, Analysts, and Commercial Leaders) to upload Excel/CSV sheets with sales results.
Excel Spreadsheet Format Specifications
Two pre-populated spreadsheets are available under the public directory:
- Template spreadsheet: import_sales_template.xlsx — Clean column layout with a single dummy row reference. This template is made directly downloadable through the program's UI.
- Test spreadsheet: import_sales_test.xlsx — Contains mixed valid and invalid rows to verify frontend/backend validation error rendering.
The spreadsheet contains the following mandatory headers:
Colaborador: String representing the unique collaborator username (e.g.colaborador_mde).Periodo: String matching the target period inYYYY-MMformat (e.g.2026-06).Hotel: String representing the unique hotel code (e.g.EST-MDE).Monto: Decimal/Numeric positive value of total sales.Cantidad: Integer positive value of total sales count.Id_Transaccion: String representing the external transaction tracking ID (e.g.TX-EST-MDE-001).
The server-side parsing will:
- Parse the uploaded
.xlsxor.csvfile buffer using thexlsxlibrary. - Resolve row-level records and perform Atomic Validation on all rows before write transactions.
- If any row contains errors, abort the entire operation and return a structured JSON report identifying the exact rows, columns, values, error codes, and translation metadata.
1.2. Multilingual "Code + Metadata" Response Pattern
To support dynamic localized UI translations, the API does not return pre-translated text strings. Instead, it enforces the Code + Metadata pattern:
- Success Responses: Return a strict static success code alongside numeric/string variables in a metadata payload.
- Error Responses: Return strict error codes (
USER_NOT_FOUND,INVALID_PERIOD_FORMAT, etc.) with their respective parameters. The client-side application translates these keys locally using translation tables.
1.3. Two-Tier Idempotency Control (API & DB Layer)
To guarantee that duplicate uploads do not lead to duplicate commission calculations:
- API Header Check: The
POST /api/sales/importendpoint requires a client-generatedIdempotency-Keyheader. - Key Check:
- The backend checks if any existing sales results contain an idempotency key matching the pattern
${idempotency_key}-*. - True: The import is recognized as a duplicate. The server returns the cached success details of the previous import, preventing re-execution.
- False: Processing continues.
- The backend checks if any existing sales results contain an idempotency key matching the pattern
- Row-level uniqueness: Each inserted row is saved with a unique database constraint
idempotencyKey = ${idempotency_key}-${row_index}, guaranteeing database-level data integrity under concurrent scenarios.
graph TD
A[Upload request with Idempotency-Key] --> B{Key already processed?}
B -->|Yes| C[Return cached/existing import summary]
B -->|No| D[Parse Excel/CSV Buffer]
D --> E{Any validation error?}
E -->|Yes| F[Abort and return detailed error list]
E -->|No| G{N8N_WEBHOOK_URL set?}
G -->|Yes| H[Forward rows to n8n Webhook & return 202 Accepted]
G -->|No / Test| I[Prisma Transaction: Save rows with idempotency key + Audit Log]
I --> J[Return 201 Created]
1.4. n8n Integration & Callback Validation
For automatic integrations (US-COM-005), the workflow follows a Thick Client (Next.js), Thin Coordinator (n8n) architecture:
- Zero DB Connections & Zero Code Blocks: n8n does not connect directly to the database or run heavy processing code. Instead, n8n invokes Next.js validation and logic endpoints, using n8n strictly for pipeline orchestration and third-party notifications.
- Encapsulated LLM/AI & Model Fallback: All LLM-based checks (semantic anomaly checks) are executed directly inside n8n using native nodes. The workflow uses a Primary Chat Model node (DeepSeek) and a Fallback Chat Model node (Google Gemini) hooked to a LangChain LLM Chain node with failover enabled.
- Dynamic Workspace Credential Resolution: During the bootstrap and E2E test runs, the system queries
GET /api/v1/credentialsfrom n8n, searches for active credentials matching the DeepSeek and Gemini types (deepSeekApiandgooglePalmApi), and dynamically injects their IDs and names into the workflow JSON before deployment. - Webhook Security: Next.js exposes a public-facing but secured callback endpoint
/api/sales/batch-save. - Signature Verification: Webhooks from n8n must include the
x-n8n-signatureheader. The server verifies this token againstN8N_WEBHOOK_SECRETbefore executing database updates. - Stalwart SMTP Server: All outbound notifications (such as system error reports or settlement anomalies alerts) are sent by n8n using native SMTP/Email nodes pointing to the Stalwart mail server.
- Async Status Polling: The frontend displays a progress UI. If routed through n8n, it polls
GET /api/sales/import/status/[key]to check database status and update progress.
2. API Specifications
2.1. POST /api/sales/import (Upload Sales Sheet)
- Role Restriction:
admin|analyst|commercial_leader - Headers:
Idempotency-Key: Required string
- Request Body:
multipart/form-datawithfilefield containing the spreadsheet. - Response:
201 Created(Direct import mode):{ "success": true, "code": "IMPORT_SUCCESSFUL", "metadata": { "count": 45, "totalAmount": 1563000.00 } }202 Accepted(n8n mode):{ "success": true, "code": "IMPORT_ACCEPTED", "metadata": { "idempotencyKey": "unique-client-key-123", "status": "PROCESSING" } }400 Bad Request(Validation errors):{ "success": false, "error": { "code": "IMPORT_VALIDATION_FAILED", "details": [ { "row": 3, "column": "Colaborador", "value": "colaborador_inexistente", "code": "USER_NOT_FOUND", "metadata": { "username": "colaborador_inexistente" } }, { "row": 4, "column": "Periodo", "value": "2026/06", "code": "INVALID_PERIOD_FORMAT", "metadata": { "expected": "YYYY-MM" } } ] } }
2.2. POST /api/sales/batch-save (n8n Webhook Callback)
- Security Check: Header
x-n8n-signature === process.env.N8N_WEBHOOK_SECRET - Request Body:
{ "idempotencyKey": "unique-client-key-123", "uploaderId": 1, "sales": [ { "username": "colaborador_mde", "hotelCode": "EST-MDE", "period": "2026-06", "amount": 15000.00, "salesCount": 3 } ] } - Response:
201 Createdor400 Bad Request.
2.3. GET /api/sales/import/status/[key] (Poll Status)
- Response:
{ "idempotencyKey": "unique-client-key-123", "status": "SUCCESS" // PROCESSING | SUCCESS | FAILED }
3. UI Design Specifications
We will create a premium drag-and-drop loading interface at /sales/import:
- Interactive Drop Zone: Elegant border animation on dragover. Shows file metadata upon dropping.
- Template Download Link: Sleek, visible button to download the pre-formatted Excel template directly.
- Real-time Progress Indicator: Fluid CSS progress bar tracking parse/upload state.
- Inconsistency Panel: If the API returns validation errors, displays them in a sleek, scrollable log table with warning icons.
- Permissions Enforcement: Renders standard unauthorized page if role is not permitted.
4. Headless Puppeteer Verification Plan
We will create a new test script prisma/test-phase4-ui.js verifying:
- Role Enforcement:
colaborador_mdeis blocked from accessing the page and endpoint. - Missing Header Validation: Upload fails if
Idempotency-Keyis missing. - File Validation: Uploading an Excel file with negative values or fake users displays validation errors.
- Successful Direct Import: Uploading a valid Excel file imports rows, saves them, and shows success.
- Idempotency Prevention: Re-uploading with the same key returns the success message without modifying/inserting additional database records.
- n8n Webhook Callback Integrity: POSTing to
/api/sales/batch-savewith an invalid signature is blocked (401), while a valid signature is processed.