Actualizar Honduras y dist validado para produccion
This commit is contained in:
@@ -27,6 +27,11 @@ VITE_N8N_EL_SALVADOR_WEBHOOK_URL="https://agenteit.digitalcompass.agency/webhook
|
||||
VITE_HISTORICOS_SV_URL="https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-sv-historicos"
|
||||
VITE_MARCAR_RESUELTO_SV_URL="https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-sv-marcar-resuelto"
|
||||
|
||||
# Honduras: cruce, históricos y resolución.
|
||||
VITE_N8N_HONDURAS_WEBHOOK_URL="https://agenteit.digitalcompass.agency/webhook/nominahn-bamboo-test"
|
||||
VITE_HISTORICOS_HN_URL="https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-hn-historicos"
|
||||
VITE_MARCAR_RESUELTO_HN_URL="https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-hn-marcar-resuelto"
|
||||
|
||||
# Endpoint opcional de n8n + Gemini para analizar el nombre del archivo de nómina.
|
||||
# La app ya incluye detección local como respaldo.
|
||||
VITE_ANALIZAR_NOMINA_URL="https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-analizar-nomina"
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
# Implementación — Cruce de Cuentas GLM · Honduras (HN)
|
||||
|
||||
## Alcance
|
||||
|
||||
Esta entrega agrega Honduras al portal centralizado sin retirar Guatemala (GT), Trinidad y Tobago (TT), Panamá (PA), El Salvador (SV) ni Bono 14 Guatemala.
|
||||
|
||||
Honduras queda configurado con:
|
||||
|
||||
- Código: `HN`
|
||||
- Nombre: `Honduras`
|
||||
- Moneda principal: `HNL`
|
||||
- Soporte de pagos en `USD` dentro del mismo archivo de nómina
|
||||
- Locale: `es-HN`
|
||||
- Bandera: `https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Bandera%20de%20Honduras.webp`
|
||||
- Responsable de correcciones BambooHR: Melody Chang Blandin — `mchang@gomezleemarketing.com`
|
||||
|
||||
## Archivos n8n
|
||||
|
||||
### Nuevos
|
||||
|
||||
1. `Portal de Verificación de Nómina - Honduras.json`
|
||||
- Webhook de producción: `/webhook/nominahn-bamboo-test`
|
||||
- Recibe un Excel de nómina y uno o varios CSV bancarios.
|
||||
- Procesa planes BAC AR94 (HNL) y AR95US (USD).
|
||||
- Genera/reutiliza el Google Sheet del período, registra el histórico en Supabase y envía el reporte.
|
||||
|
||||
2. `Portal de Verificación de Nómina Honduras - Históricos.json`
|
||||
- Webhook: `/webhook/cruce-cuentas-hn-historicos`
|
||||
- Consulta únicamente históricos `country = HN`.
|
||||
|
||||
3. `Portal de Verificación de Nómina Honduras - Envío de Reporte.json`
|
||||
- Webhook: `/webhook/cruce-cuentas-hn-marcar-resuelto`
|
||||
- Marca únicamente reportes HN como resueltos y envía la notificación correspondiente.
|
||||
|
||||
### Actualizaciones compartidas
|
||||
|
||||
4. `Portal de Verificación de Nómina - Analizar Nómina con Gemini GT TT PA SV HN.json`
|
||||
- Conserva el webhook existente `/webhook/cruce-cuentas-analizar-nomina`.
|
||||
- Agrega HN al validador, esquema, prompt y normalizador.
|
||||
- Reconoce nombres compactos de Honduras como `Nomina 7.1 2026 GLM HN.xlsx` y `Nomina 7.2 2026 GLM HN.xlsx`.
|
||||
- Calcula dinámicamente 1–15 y 16–último día real del mes, incluyendo febrero y años bisiestos.
|
||||
|
||||
5. `Portal de Verificación de Nómina - Reportar corrección BambooHR a RRHH - GT TT PA SV HN.json`
|
||||
- Conserva el webhook existente `/webhook/cruce-cuentas-bamboo-correccion`.
|
||||
- Agrega HN y envía las correcciones de Honduras exclusivamente a `mchang@gomezleemarketing.com`.
|
||||
|
||||
> Los dos workflows compartidos usan los mismos paths que los workflows actuales. No deben quedar dos versiones activas con el mismo webhook. Actualiza/reemplaza la versión existente o desactiva la anterior antes de publicar la nueva.
|
||||
|
||||
## Destinatarios de reportes generados de Honduras
|
||||
|
||||
El workflow principal usa estos seis destinatarios:
|
||||
|
||||
```javascript
|
||||
const to = [
|
||||
'iaracena@gomezleemarketing.com',
|
||||
'jgomez@gomezleemarketing.com',
|
||||
'mgomez@gomezleemarketing.com',
|
||||
'vparamo@gomezleemarketing.com',
|
||||
'ymadera@gomezleemarketing.com',
|
||||
'adminregional@gomezleemarketing.com',
|
||||
].join(',');
|
||||
```
|
||||
|
||||
## Nómina Honduras soportada
|
||||
|
||||
El workflow procesa las hojas reales observadas en junio/julio de 2026, con tolerancia a hojas opcionales ausentes:
|
||||
|
||||
- Colgate Base
|
||||
- Colgate Combustible
|
||||
- POP Helper Base
|
||||
- POP Helper Combustible
|
||||
- Motorola Base
|
||||
- Motorola Temporales
|
||||
- Motorola KAM Dolares
|
||||
- Nestle Base
|
||||
- Nestle Combustible
|
||||
- Whirlpool Base
|
||||
- Whirlpool Combustible
|
||||
- FF Regional WH Base
|
||||
- FF Regional WH Combustible
|
||||
- GLM Base Lempiras
|
||||
- GLM Combustible Lempiras
|
||||
- GLM Dolares Base
|
||||
- GLM Dolares Combustible
|
||||
|
||||
Las cuentas se preservan como texto y los montos se separan por moneda HNL/USD.
|
||||
|
||||
## Regla de redondeo
|
||||
|
||||
Para Honduras, una diferencia absoluta de hasta `0.05` entre nómina y banco se considera redondeo operativo y no una discrepancia. Los archivos reales mostraron diferencias de 0.01 y un caso USD de 0.03. Diferencias superiores continúan reportándose.
|
||||
|
||||
## Supabase
|
||||
|
||||
Ejecuta `SUPABASE_Honduras_READY.sql` antes de activar los workflows.
|
||||
|
||||
El script:
|
||||
|
||||
- Agrega `HN` a los constraints de aliases BambooHR, correcciones e históricos.
|
||||
- Agrega `HN` al constraint `paises` de `cruce_cuentas_usuarios_autorizados`.
|
||||
- Agrega HN a **todos los usuarios actuales**, preservando los demás permisos.
|
||||
- Actualiza `cruce_cuentas_bamboo_aliases_activos` para cinco países.
|
||||
- Actualiza `cruce_cuentas_get_historicos` para cinco países.
|
||||
- Actualiza `cruce_cuentas_get_banco_sin_bamboo_reporte` para cinco países.
|
||||
- Conserva la tabla histórica compartida `cruces_cuentas_gt_reportes`; el nombre es legado, la tabla es multi-país.
|
||||
|
||||
## App
|
||||
|
||||
Variables Honduras incorporadas:
|
||||
|
||||
```env
|
||||
VITE_N8N_HONDURAS_WEBHOOK_URL="https://agenteit.digitalcompass.agency/webhook/nominahn-bamboo-test"
|
||||
VITE_HISTORICOS_HN_URL="https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-hn-historicos"
|
||||
VITE_MARCAR_RESUELTO_HN_URL="https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-hn-marcar-resuelto"
|
||||
```
|
||||
|
||||
El selector de país incluye Honduras y el `dist` entregado ya contiene la configuración HN, los tres endpoints, la bandera y el destinatario de correcciones.
|
||||
|
||||
## Orden de implementación recomendado
|
||||
|
||||
1. Ejecutar `SUPABASE_Honduras_READY.sql` y verificar que todos los usuarios tengan `HN` en `paises`.
|
||||
2. Importar el workflow nuevo **Portal de Verificación de Nómina - Honduras** y revisar credenciales.
|
||||
3. Importar **Honduras - Históricos**.
|
||||
4. Importar **Honduras - Envío de Reporte / Marcar Resuelto**.
|
||||
5. Reemplazar/actualizar el workflow compartido de Gemini; no dejar activa la versión anterior con el mismo webhook.
|
||||
6. Reemplazar/actualizar el workflow compartido de correcciones BambooHR; no dejar activa la versión anterior con el mismo webhook.
|
||||
7. Publicar/activar los cinco workflows una vez revisadas las credenciales.
|
||||
8. Desplegar exactamente el contenido de `dist` del ZIP de aplicación bajo `/cruce-cuentas/`.
|
||||
9. Hacer una prueba controlada de una Q15 y una Q30 desde la aplicación.
|
||||
10. Verificar Históricos, “Marcar resuelto” y un caso de “Reportar a RRHH”.
|
||||
|
||||
## Estado de activación de los JSON
|
||||
|
||||
Los cinco JSON entregados tienen `active: false` intencionalmente para una importación segura. Actívalos únicamente después de confirmar credenciales y de evitar duplicidad de webhooks compartidos.
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+514
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,163 @@
|
||||
{
|
||||
"name": "Portal de Verificación de Nómina Honduras - Históricos",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"path": "cruce-cuentas-hn-historicos",
|
||||
"responseMode": "responseNode",
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2.1,
|
||||
"position": [
|
||||
304,
|
||||
272
|
||||
],
|
||||
"id": "2fe1cba5-882d-4054-afa7-d331d33220aa",
|
||||
"name": "Webhook Históricos HN",
|
||||
"webhookId": "48f69298-ea9a-4ff2-a051-1052bbed0c1d"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const items = $input.all();\n\nfunction formatDate(value) {\n if (!value) return '';\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return String(value).slice(0, 10);\n return date.toISOString().slice(0, 10);\n}\n\nfunction toNumber(value) {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\nfunction normalizeStatus(value, discrepancias) {\n const status = String(value || '').toLowerCase();\n\n if (status === 'resuelto') return 'resuelto';\n if (status === 'procesado' && toNumber(discrepancias) === 0) return 'procesado';\n\n return 'pendiente_revision';\n}\n\nfunction statusLabel(status) {\n if (status === 'resuelto') return 'RESUELTO';\n if (status === 'procesado') return 'PROCESADO';\n return 'PENDIENTE REVISIÓN';\n}\n\nconst reports = items.map((item) => {\n const row = item.json || {};\n\n const discrepancias = toNumber(row.discrepancias);\n const bancoSinNomina = toNumber(row.banco_sin_nomina);\n const nominaSinCuenta = toNumber(row.nomina_sin_cuenta);\n const bancoSinBamboo = toNumber(row.banco_sin_bamboo);\n const nominaSinBamboo = toNumber(row.nomina_sin_bamboo);\n const bambooSinNomina = toNumber(row.bamboo_sin_nomina);\n\n const totalPendientes =\n discrepancias +\n bancoSinNomina +\n nominaSinCuenta +\n bancoSinBamboo +\n nominaSinBamboo +\n bambooSinNomina;\n\n const estado = normalizeStatus(row.estado, totalPendientes);\n\n return {\n id: row.id,\n\n fecha: formatDate(row.period_end || row.ejecutado_en || row.created_at),\n fechaEjecucion: row.ejecutado_en || row.created_at,\n\n periodo: row.period_label || '',\n year: row.year,\n month: row.month,\n periodType: row.period_type,\n periodStart: row.period_start,\n periodEnd: row.period_end,\n\n archivoNomina: row.payroll_file_name || '',\n archivosBanco: row.bank_file_names || [],\n\n coincidencias: toNumber(row.coincidencias),\n discrepancias,\n bancoSinNomina,\n nominaSinCuenta,\n bancoSinBamboo,\n nominaSinBamboo,\n bambooSinNomina,\n totalPendientes,\n\n totalNomina: toNumber(row.total_nomina),\n totalBanco: toNumber(row.total_banco),\n diferenciaTotal: toNumber(row.diferencia_total),\n\n estado,\n estadoLabel: statusLabel(estado),\n\n reportUrl: row.report_url || '',\n spreadsheetId: row.spreadsheet_id || '',\n\n ejecutadoPorNombre: row.ejecutado_por_nombre || '',\n ejecutadoPorEmail: row.ejecutado_por_email || '',\n\n resueltoPorNombre: row.resuelto_por_nombre || '',\n resueltoPorEmail: row.resuelto_por_email || '',\n resueltoEn: row.resuelto_en || null,\n comentarioResolucion: row.comentario_resolucion || '',\n };\n});\n\nreturn [\n {\n json: {\n ok: true,\n stage: 'historicos_hn_listos',\n count: reports.length,\n reports,\n },\n },\n];"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
720,
|
||||
272
|
||||
],
|
||||
"id": "dcd21660-393d-4ce2-9092-e61fc909fe0a",
|
||||
"name": "Normalizar históricos para app"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "https://dbit.digitalcompass.agency/rest/v1/cruces_cuentas_gt_reportes?select=*&country=eq.HN&order=ejecutado_en.desc&limit=100",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "apikey",
|
||||
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q"
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.4,
|
||||
"position": [
|
||||
512,
|
||||
272
|
||||
],
|
||||
"id": "60a88bd9-4050-43d7-a88d-b415f90d1543",
|
||||
"name": "Consultar históricos Supabase"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ $json }}",
|
||||
"options": {
|
||||
"responseHeaders": {
|
||||
"entries": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Access-Control-Allow-Origin",
|
||||
"value": "*"
|
||||
},
|
||||
{
|
||||
"name": "Access-Control-Allow-Methods",
|
||||
"value": "GET,POST,OPTIONS"
|
||||
},
|
||||
{
|
||||
"name": "Access-Control-Allow-Headers",
|
||||
"value": "Content-Type, Authorization"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1.5,
|
||||
"position": [
|
||||
944,
|
||||
272
|
||||
],
|
||||
"id": "9f35b767-e0f2-40a5-8097-8bc623669b8d",
|
||||
"name": "Responder históricos app"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"content": "# 📚 HISTÓRICOS DE VERIFICACIÓN — HONDURAS\n\nEntrega a la aplicación el historial de reportes procesados para Honduras.\n\n- Consulta únicamente registros con `country = HN`.\n- Supabase continúa usando la tabla histórica compartida `cruces_cuentas_gt_reportes`; el nombre de la tabla es legado y funciona como tabla multi-país.\n- Devuelve la misma estructura de históricos utilizada por los demás países.\n- Este webhook sirve como respaldo; la app intenta primero la RPC paginada de Supabase.",
|
||||
"height": 496,
|
||||
"width": 1360,
|
||||
"color": 6
|
||||
},
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"id": "8e6b3835-6bd6-4f64-827e-24744543d76c",
|
||||
"name": "Sticky Note"
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"Consultar históricos Supabase": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Normalizar históricos para app",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Normalizar históricos para app": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Responder históricos app",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Webhook Históricos HN": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Consultar históricos Supabase",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1",
|
||||
"binaryMode": "separate"
|
||||
},
|
||||
"versionId": "c2ddfc50-7d6a-4750-9f9c-1e7e423b6380",
|
||||
"meta": {
|
||||
"instanceId": "b4b77b17af092830e794eef639ce2f6d7daccf7eddc075060b03b3b6545aac70"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
-- ============================================================
|
||||
-- CRUCE DE CUENTAS GLM · HABILITAR HONDURAS (HN)
|
||||
-- GT + TT + PA + SV + HN
|
||||
-- ============================================================
|
||||
-- Ejecutar en el SQL Editor del Supabase empresarial.
|
||||
-- Conserva datos existentes y puede ejecutarse nuevamente de forma segura.
|
||||
|
||||
begin;
|
||||
|
||||
-- 1) Constraints de país: aliases, correcciones, históricos y accesos.
|
||||
do $$
|
||||
declare r record;
|
||||
begin
|
||||
if to_regclass('public.cruce_cuentas_bamboo_aliases') is null then raise exception 'No existe public.cruce_cuentas_bamboo_aliases.'; end if;
|
||||
if to_regclass('public.cruce_cuentas_bamboo_correcciones') is null then raise exception 'No existe public.cruce_cuentas_bamboo_correcciones.'; end if;
|
||||
if to_regclass('public.cruces_cuentas_gt_reportes') is null then raise exception 'No existe public.cruces_cuentas_gt_reportes.'; end if;
|
||||
if to_regclass('public.cruce_cuentas_usuarios_autorizados') is null then raise exception 'No existe public.cruce_cuentas_usuarios_autorizados.'; end if;
|
||||
|
||||
for r in select conname from pg_constraint where conrelid='public.cruce_cuentas_bamboo_aliases'::regclass and contype='c' and pg_get_constraintdef(oid) ilike '%pais%' and pg_get_constraintdef(oid) ilike '%GT%' and pg_get_constraintdef(oid) not ilike '%HN%' loop
|
||||
execute format('alter table public.cruce_cuentas_bamboo_aliases drop constraint %I',r.conname);
|
||||
end loop;
|
||||
alter table public.cruce_cuentas_bamboo_aliases drop constraint if exists ck_cruce_cuentas_bamboo_aliases_pais;
|
||||
alter table public.cruce_cuentas_bamboo_aliases add constraint ck_cruce_cuentas_bamboo_aliases_pais check (upper(trim(pais)) in ('GT','TT','PA','SV','HN'));
|
||||
|
||||
for r in select conname from pg_constraint where conrelid='public.cruce_cuentas_bamboo_correcciones'::regclass and contype='c' and pg_get_constraintdef(oid) ilike '%pais%' and pg_get_constraintdef(oid) ilike '%GT%' and pg_get_constraintdef(oid) not ilike '%HN%' loop
|
||||
execute format('alter table public.cruce_cuentas_bamboo_correcciones drop constraint %I',r.conname);
|
||||
end loop;
|
||||
alter table public.cruce_cuentas_bamboo_correcciones drop constraint if exists ck_cruce_cuentas_bamboo_correcciones_pais;
|
||||
alter table public.cruce_cuentas_bamboo_correcciones add constraint ck_cruce_cuentas_bamboo_correcciones_pais check (upper(trim(pais)) in ('GT','TT','PA','SV','HN'));
|
||||
|
||||
for r in select conname from pg_constraint where conrelid='public.cruces_cuentas_gt_reportes'::regclass and contype='c' and pg_get_constraintdef(oid) ilike '%country%' and pg_get_constraintdef(oid) ilike '%GT%' and pg_get_constraintdef(oid) not ilike '%HN%' loop
|
||||
execute format('alter table public.cruces_cuentas_gt_reportes drop constraint %I',r.conname);
|
||||
end loop;
|
||||
alter table public.cruces_cuentas_gt_reportes drop constraint if exists ck_cruces_cuentas_reportes_country;
|
||||
alter table public.cruces_cuentas_gt_reportes add constraint ck_cruces_cuentas_reportes_country check (country is null or upper(trim(country)) in ('GT','TT','PA','SV','HN'));
|
||||
|
||||
-- La tabla de acceso usa text[] en paises.
|
||||
alter table public.cruce_cuentas_usuarios_autorizados drop constraint if exists cruce_cuentas_paises_validos_check;
|
||||
alter table public.cruce_cuentas_usuarios_autorizados add constraint cruce_cuentas_paises_validos_check check (paises is null or paises <@ array['GT','TT','PA','SV','HN']::text[]);
|
||||
end;
|
||||
$$;
|
||||
|
||||
-- 2) Dar HN a TODOS los usuarios autorizados actuales, preservando permisos.
|
||||
update public.cruce_cuentas_usuarios_autorizados
|
||||
set paises = case
|
||||
when paises is null then array['GT','TT','PA','SV','HN']::text[]
|
||||
when not ('HN'=any(paises)) then array_append(paises,'HN')
|
||||
else paises
|
||||
end,
|
||||
updated_at=now();
|
||||
|
||||
-- 3) Alias BambooHR activos: cinco países.
|
||||
create or replace function public.cruce_cuentas_bamboo_aliases_activos(p_pais text)
|
||||
returns jsonb language sql stable security definer set search_path=public as $$
|
||||
select jsonb_build_object('ok',true,'pais',upper(trim(coalesce(p_pais,''))),'aliases',coalesce(jsonb_agg(jsonb_build_object('id',a.id,'pais',a.pais,'nombre_origen',a.nombre_origen,'nombre_origen_normalizado',a.nombre_origen_normalizado,'cuenta_bancaria',a.cuenta_bancaria,'bamboo_employee_number',a.bamboo_employee_number,'bamboo_employee_id',a.bamboo_employee_id,'bamboo_nombre',a.bamboo_nombre,'motivo',a.motivo,'activo',a.activo) order by a.updated_at desc) filter (where a.id is not null),'[]'::jsonb))
|
||||
from public.cruce_cuentas_bamboo_aliases a where a.activo=true and a.pais=upper(trim(coalesce(p_pais,''))) and upper(trim(coalesce(p_pais,''))) in ('GT','TT','PA','SV','HN');
|
||||
$$;
|
||||
revoke all on function public.cruce_cuentas_bamboo_aliases_activos(text) from public,anon,authenticated;
|
||||
grant execute on function public.cruce_cuentas_bamboo_aliases_activos(text) to service_role;
|
||||
|
||||
-- 4) Históricos paginados: cinco países.
|
||||
create index if not exists idx_cruces_cuentas_reportes_country_created on public.cruces_cuentas_gt_reportes(country,created_at desc);
|
||||
create index if not exists idx_cruces_cuentas_reportes_country_status_created on public.cruces_cuentas_gt_reportes(country,estado,created_at desc);
|
||||
|
||||
create or replace function public.cruce_cuentas_get_historicos(p_country text,p_page integer default 1,p_page_size integer default 10,p_search text default null,p_status text default null)
|
||||
returns jsonb language plpgsql security definer set search_path=public as $$
|
||||
declare
|
||||
v_country text := upper(trim(coalesce(p_country,'')));
|
||||
v_page integer := greatest(coalesce(p_page,1),1);
|
||||
v_page_size integer := least(greatest(coalesce(p_page_size,10),1),100);
|
||||
v_offset integer;
|
||||
v_search text := nullif(trim(coalesce(p_search,'')),'');
|
||||
v_status text := nullif(trim(coalesce(p_status,'')),'');
|
||||
v_total bigint; v_filtered_total bigint; v_reports jsonb;
|
||||
begin
|
||||
if v_country not in ('GT','TT','PA','SV','HN') then raise exception 'País no permitido: %',v_country; end if;
|
||||
v_offset := (v_page-1)*v_page_size;
|
||||
select count(*) into v_total from public.cruces_cuentas_gt_reportes r where upper(coalesce(r.country,'GT'))=v_country;
|
||||
select count(*) into v_filtered_total from public.cruces_cuentas_gt_reportes r where upper(coalesce(r.country,'GT'))=v_country and (v_status is null or r.estado=v_status) and (v_search is null or coalesce(r.period_label,'') ilike '%'||v_search||'%' or coalesce(r.payroll_file_name,'') ilike '%'||v_search||'%' or coalesce(r.ejecutado_por_nombre,'') ilike '%'||v_search||'%' or coalesce(r.resuelto_por_nombre,'') ilike '%'||v_search||'%' or r.id::text ilike '%'||v_search||'%');
|
||||
select coalesce(jsonb_agg((to_jsonb(q)-'detalle_banco_sin_bamboo') order by q.created_at desc),'[]'::jsonb) into v_reports from (select r.* from public.cruces_cuentas_gt_reportes r where upper(coalesce(r.country,'GT'))=v_country and (v_status is null or r.estado=v_status) and (v_search is null or coalesce(r.period_label,'') ilike '%'||v_search||'%' or coalesce(r.payroll_file_name,'') ilike '%'||v_search||'%' or coalesce(r.ejecutado_por_nombre,'') ilike '%'||v_search||'%' or coalesce(r.resuelto_por_nombre,'') ilike '%'||v_search||'%' or r.id::text ilike '%'||v_search||'%') order by r.created_at desc limit v_page_size offset v_offset) q;
|
||||
return jsonb_build_object('ok',true,'country',v_country,'page',v_page,'page_size',v_page_size,'total',v_total,'filtered_total',v_filtered_total,'total_pages',greatest(ceil(v_filtered_total::numeric/v_page_size)::integer,1),'reports',v_reports);
|
||||
end;
|
||||
$$;
|
||||
revoke all on function public.cruce_cuentas_get_historicos(text,integer,integer,text,text) from public,anon;
|
||||
grant execute on function public.cruce_cuentas_get_historicos(text,integer,integer,text,text) to authenticated;
|
||||
|
||||
-- 5) Detalle Banco sin Bamboo de históricos.
|
||||
create or replace function public.cruce_cuentas_get_banco_sin_bamboo_reporte(p_report_id text,p_country text)
|
||||
returns jsonb language plpgsql stable security definer set search_path=public as $$
|
||||
declare
|
||||
v_country text := upper(trim(coalesce(p_country,'')));
|
||||
v_report public.cruces_cuentas_gt_reportes%rowtype;
|
||||
v_corrections jsonb := '[]'::jsonb;
|
||||
begin
|
||||
if auth.uid() is null then raise exception 'No autenticado.'; end if;
|
||||
if v_country not in ('GT','TT','PA','SV','HN') then raise exception 'País no permitido: %',v_country; end if;
|
||||
select r.* into v_report from public.cruces_cuentas_gt_reportes r where r.id::text=trim(coalesce(p_report_id,'')) and upper(coalesce(r.country,'GT'))=v_country limit 1;
|
||||
if not found then return jsonb_build_object('ok',false,'message','No se encontró el reporte solicitado.'); end if;
|
||||
select coalesce(jsonb_agg(jsonb_build_object('id',c.id,'pais',c.pais,'nombre_origen_principal',c.nombre_origen_principal,'nombres_origen',c.nombres_origen,'cuenta_bancaria',c.cuenta_bancaria,'bamboo_employee_number',c.bamboo_employee_number,'bamboo_nombre',c.bamboo_nombre,'estado',c.estado,'correo_enviado',c.correo_enviado,'correo_enviado_at',c.correo_enviado_at,'created_at',c.created_at) order by c.created_at desc),'[]'::jsonb) into v_corrections from public.cruce_cuentas_bamboo_correcciones c where c.pais=v_country and (c.execution_id=v_report.id::text or (nullif(trim(coalesce(c.execution_id,'')),'') is null and nullif(trim(coalesce(c.report_url,'')),'') is not null and c.report_url=v_report.report_url));
|
||||
return jsonb_build_object('ok',true,'report',jsonb_build_object('id',v_report.id,'country',v_report.country,'period_label',v_report.period_label,'period_start',v_report.period_start,'period_end',v_report.period_end,'report_url',v_report.report_url,'banco_sin_bamboo',v_report.banco_sin_bamboo),'cases',coalesce(v_report.detalle_banco_sin_bamboo,'[]'::jsonb),'corrections',v_corrections);
|
||||
end;
|
||||
$$;
|
||||
revoke all on function public.cruce_cuentas_get_banco_sin_bamboo_reporte(text,text) from public,anon;
|
||||
grant execute on function public.cruce_cuentas_get_banco_sin_bamboo_reporte(text,text) to authenticated;
|
||||
|
||||
commit;
|
||||
|
||||
-- VALIDACIÓN (solo lectura)
|
||||
select email,nombre,paises from public.cruce_cuentas_usuarios_autorizados order by email;
|
||||
select public.cruce_cuentas_bamboo_aliases_activos('HN');
|
||||
select public.cruce_cuentas_get_historicos('HN',1,10,null,null);
|
||||
select conrelid::regclass as tabla,conname,pg_get_constraintdef(oid) as definicion from pg_constraint where conrelid in ('public.cruce_cuentas_bamboo_aliases'::regclass,'public.cruce_cuentas_bamboo_correcciones'::regclass,'public.cruces_cuentas_gt_reportes'::regclass,'public.cruce_cuentas_usuarios_autorizados'::regclass) and contype='c' order by 1,2;
|
||||
@@ -0,0 +1,111 @@
|
||||
# Validación — Cruce de Cuentas GLM · Honduras
|
||||
|
||||
## Datos reales utilizados
|
||||
|
||||
Se validó la lógica contra cuatro períodos reales:
|
||||
|
||||
- Junio 2026 · Quincena 15
|
||||
- Junio 2026 · Quincena 30 / fin de mes
|
||||
- Julio 2026 · Quincena 15
|
||||
- Julio 2026 · Quincena 30 / fin de mes
|
||||
|
||||
Se utilizaron los Excel reales de nómina, los CSV BAC de planes AR94/AR95US y el reporte completo de BambooHR proporcionado para las pruebas.
|
||||
|
||||
## Normalización de nómina
|
||||
|
||||
| Período | Filas válidas | Total HNL | Total USD |
|
||||
|---|---:|---:|---:|
|
||||
| Junio Q15 | 266 | 2,111,731.15 | 0.00 |
|
||||
| Junio Q30 | 332 | 1,810,119.17 | 23,474.54 |
|
||||
| Julio Q15 | 264 | 1,450,958.81 | 0.00 |
|
||||
| Julio Q30 | 336 | 1,760,132.78 | 24,013.15 |
|
||||
|
||||
## Parser bancario
|
||||
|
||||
| Período | Transacciones | Total HNL | Total USD |
|
||||
|---|---:|---:|---:|
|
||||
| Junio Q15 | 266 | 2,128,362.38 | 0.00 |
|
||||
| Junio Q30 | 508 | 4,603,602.47 | 43,859.02 |
|
||||
| Julio Q15 | 264 | 1,450,959.26 | 0.00 |
|
||||
| Julio Q30 | 337 | 1,760,133.30 | 24,780.81 |
|
||||
|
||||
Los archivos bancarios de junio Q30 incluyen envíos del 23/06 y 30/06; ambos pertenecen a la segunda mitad del mes y por diseño se auditan dentro de Q30. Por eso existen pagos “Banco sin nómina” correspondientes a envíos adicionales del período.
|
||||
|
||||
## Resultado del reconciliador final
|
||||
|
||||
| Período | Coincidencias | Discrepancias | Banco sin nómina | Banco sin Bamboo | Diferencias nombre banco | Cuenta mal digitada |
|
||||
|---|---:|---:|---:|---:|---:|---:|
|
||||
| Junio Q15 | 143 | 122 | 0 | 5 | 1 | 1 |
|
||||
| Junio Q30 | 331 | 0 | 176 | 5 | 5 | 1 |
|
||||
| Julio Q15 | 263 | 0 | 0 | 4 | 1 | 1 |
|
||||
| Julio Q30 | 335 | 0 | 1 | 5 | 3 | 1 |
|
||||
|
||||
### Redondeos
|
||||
|
||||
La validación final aplica una tolerancia de hasta 0.05. Esto elimina falsos positivos de 0.01 observados repetidamente y el caso real de 0.03 USD, sin ocultar diferencias materiales. En Junio Q15, después de eliminar únicamente esos redondeos, las 122 discrepancias restantes empiezan en 58.18 HNL, por lo que siguen siendo hallazgos materiales.
|
||||
|
||||
### Cuenta mal digitada confirmada
|
||||
|
||||
El caso consistente en los períodos es **Brenda Baquedano**:
|
||||
|
||||
- Nómina: `745899511`
|
||||
- Banco: `745899611`
|
||||
|
||||
Durante una revisión preliminar se había señalado también a Harold Panchame en junio Q30. La revisión directa del Excel original mostró que la celda de nómina contiene `756669711.0`, que normaliza a `756669711`, exactamente igual al banco; por tanto el reconciliador final no lo clasifica como cuenta mal digitada.
|
||||
|
||||
### Diferencias de nombre banco
|
||||
|
||||
La regla final distingue truncamientos normales del archivo bancario de diferencias reales:
|
||||
|
||||
- `Oscar Rene Flores Alvarad` vs `OSCAR RENE FLORES ALVARADO` se considera truncamiento y no genera falso positivo.
|
||||
- `Erick Santaigo Herrera He` vs `ERICK SANTIAGO HERRERA HERRERA` sigue reportándose porque contiene una diferencia interna de escritura.
|
||||
- Los registros de `Suhan Gonzales` vs `SUHAN ELIZABETH GONZALEZ DUBON` siguen reportándose cuando aparecen.
|
||||
|
||||
### BambooHR
|
||||
|
||||
El normalizador identifica 456 registros de Honduras en el reporte completo utilizado para prueba. Se corrigió el caso ambiguo de `Suyapa Palma`: cuando el nombre corto coincide con un registro histórico y el nombre completo del cuentahabiente identifica de forma única a `Suyapa Melisa Palma Aguilar`, el workflow prioriza el nombre legal completo y evita un falso “Banco sin Bamboo”.
|
||||
|
||||
## Gemini / todas las quincenas y años
|
||||
|
||||
El fallback determinista y el flujo Gemini fueron probados con:
|
||||
|
||||
- `Nomina 6.1 2026 GLM HN.xlsx` → junio 1–15.
|
||||
- `Nomina 6.2 2026 GLM HN.xlsx` → junio 16–30.
|
||||
- `Nomina 7.1 2026 GLM HN.xlsx` → julio 1–15.
|
||||
- `Nomina 7.2 2026 GLM HN.xlsx` → julio 16–31.
|
||||
- Febrero 2027 → segunda quincena 16–28.
|
||||
- Febrero 2028 → segunda quincena 16–29 (bisiesto).
|
||||
- Diciembre 2031 → segunda quincena 16–31.
|
||||
|
||||
El último día se calcula en tiempo de ejecución; no está fijado a 30 y no depende de 2026.
|
||||
|
||||
## App / dist
|
||||
|
||||
Validaciones realizadas sobre el paquete final:
|
||||
|
||||
- `CountryCode` incluye `HN`.
|
||||
- Configuración HN usa HNL, `es-HN` y la URL exacta de la bandera proporcionada.
|
||||
- Webhook principal HN incorporado.
|
||||
- Endpoint de Históricos HN incorporado.
|
||||
- Endpoint de Marcar Resuelto HN incorporado.
|
||||
- Modal de corrección muestra Melody Chang Blandin.
|
||||
- El JavaScript compilado `dist/assets/index-pa.js` pasó `node --check`.
|
||||
- `index.html`, JavaScript, CSS y favicon fueron servidos localmente bajo `/cruce-cuentas/` y respondieron HTTP 200.
|
||||
- Los 16 archivos TypeScript/TSX de `src` pasaron una validación de sintaxis con TypeScript `transpileModule` sin errores.
|
||||
|
||||
### Nota sobre el build
|
||||
|
||||
El entorno aislado usado para preparar esta entrega no pudo completar una instalación nueva de dependencias desde npm. Por ello no se afirma que se haya ejecutado un `npm run build` fresco en esta sesión. El `dist` final parte del `dist` de El Salvador que el usuario ya había probado y desplegado, fue actualizado directamente con las mismas incorporaciones HN del código fuente y luego fue validado por sintaxis y servido estáticamente bajo la ruta real `/cruce-cuentas/`.
|
||||
|
||||
## n8n
|
||||
|
||||
Los cinco JSON finales:
|
||||
|
||||
- parsean como JSON;
|
||||
- están en `active: false`;
|
||||
- tienen nombres e IDs de nodo únicos dentro de cada workflow;
|
||||
- no contienen conexiones hacia nodos inexistentes;
|
||||
- conservan los paths esperados;
|
||||
- los nodos Code pasan validación de sintaxis JavaScript;
|
||||
- las correcciones HN se dirigen a `mchang@gomezleemarketing.com`;
|
||||
- el reporte general HN contiene los seis destinatarios definidos.
|
||||
Vendored
+1
-1
@@ -9,7 +9,7 @@
|
||||
<title>Portal de Cruce de Cuentas GLM</title>
|
||||
<link rel="icon" type="image/png" href="/cruce-cuentas/favicon.png" />
|
||||
<link rel="apple-touch-icon" href="/cruce-cuentas/favicon.png" />
|
||||
<script type="module" crossorigin src="/cruce-cuentas/assets/index-BfZQMoC1.js"></script>
|
||||
<script type="module" crossorigin src="/cruce-cuentas/assets/index-BmyDbce7.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/cruce-cuentas/assets/index-CLG5SAqX.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -67,6 +67,7 @@ export default function BambooCorrectionModal({
|
||||
if (country.code === 'TT') return 'Laurele Nicome-Jack';
|
||||
if (country.code === 'PA') return 'Delarys Peñalba';
|
||||
if (country.code === 'SV') return 'Carlos Antonio Malta Méndez';
|
||||
if (country.code === 'HN') return 'Melody Chang Blandin';
|
||||
return 'haguirre@gomezleemarketing.com';
|
||||
}, [country.code]);
|
||||
|
||||
|
||||
@@ -91,6 +91,13 @@ function getReconciliationUrl(countryCode: string) {
|
||||
);
|
||||
}
|
||||
|
||||
if (countryCode === 'HN') {
|
||||
return (
|
||||
(import.meta.env.VITE_N8N_HONDURAS_WEBHOOK_URL as string | undefined) ||
|
||||
'https://agenteit.digitalcompass.agency/webhook/nominahn-bamboo-test'
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
(import.meta.env.VITE_N8N_GUATEMALA_WEBHOOK_URL as string | undefined) ||
|
||||
(import.meta.env.VITE_N8N_WEBHOOK_URL as string | undefined) ||
|
||||
@@ -674,8 +681,19 @@ function detectPeriodFromPayrollFilename(fileName: string): Partial<DetectedPayr
|
||||
}
|
||||
}
|
||||
|
||||
// Honduras usa frecuentemente nombres compactos como "Nomina 7.1 2026 GLM HN"
|
||||
// y "Nomina 7.2 2026 GLM HN": el primer número es el mes y el segundo la quincena.
|
||||
const compactHnMatch = normalized.match(/\b(1[0-2]|[1-9])\s+(1|2)\s+(20\d{2})\b/);
|
||||
if (!month && compactHnMatch) {
|
||||
month = Number(compactHnMatch[1]);
|
||||
}
|
||||
|
||||
let periodType: PeriodType | undefined;
|
||||
if (/\b(?:1q|q1|quincena\s*15|primera\s+quincena)\b/.test(normalized)) {
|
||||
if (compactHnMatch?.[2] === '1') {
|
||||
periodType = 'quincena_15';
|
||||
} else if (compactHnMatch?.[2] === '2') {
|
||||
periodType = 'quincena_30';
|
||||
} else if (/\b(?:1q|q1|quincena\s*15|primera\s+quincena)\b/.test(normalized)) {
|
||||
periodType = 'quincena_15';
|
||||
} else if (/\b(?:2q|q2|quincena\s*30|segunda\s+quincena|fin\s+de\s+mes)\b/.test(normalized)) {
|
||||
periodType = 'quincena_30';
|
||||
@@ -795,6 +813,7 @@ export default function Dashboard({
|
||||
const defaultPeriod = useMemo(() => readStoredPeriod(country.code), [country.code]);
|
||||
const isPanama = country.code === 'PA';
|
||||
const isElSalvador = country.code === 'SV';
|
||||
const isHonduras = country.code === 'HN';
|
||||
const isMultiPayrollCountry = isPanama || isElSalvador;
|
||||
const [isProcessing, setIsProcessing] = useState(false);
|
||||
const [processingStep, setProcessingStep] = useState('');
|
||||
@@ -1648,7 +1667,8 @@ export default function Dashboard({
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{isMultiPayrollCountry && (uploadedPayrollFiles.length > 0 || uploadedBankFiles.length > 0) && (
|
||||
{(isMultiPayrollCountry || isHonduras) &&
|
||||
(uploadedPayroll !== null || uploadedPayrollFiles.length > 0 || uploadedBankFiles.length > 0) && (
|
||||
<div className="flex justify-end" style={{ marginBottom: '-20px' }}>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -47,6 +47,13 @@ function getHistoryUrl(countryCode: string) {
|
||||
);
|
||||
}
|
||||
|
||||
if (countryCode === 'HN') {
|
||||
return (
|
||||
(import.meta.env.VITE_HISTORICOS_HN_URL as string | undefined) ||
|
||||
'https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-hn-historicos'
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
(import.meta.env.VITE_HISTORICOS_GT_URL as string | undefined) ||
|
||||
'https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-gt-historicos'
|
||||
@@ -72,6 +79,13 @@ function getMarkResolvedUrl(countryCode: string) {
|
||||
);
|
||||
}
|
||||
|
||||
if (countryCode === 'HN') {
|
||||
return (
|
||||
(import.meta.env.VITE_MARCAR_RESUELTO_HN_URL as string | undefined) ||
|
||||
'https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-hn-marcar-resuelto'
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
(import.meta.env.VITE_MARCAR_RESUELTO_GT_URL as string | undefined) ||
|
||||
'https://agenteit.digitalcompass.agency/webhook/cruce-cuentas-gt-marcar-resuelto'
|
||||
@@ -157,7 +171,7 @@ function normalizeReports(input: unknown, country: CountryConfig): HistoricalRep
|
||||
|
||||
return {
|
||||
id: String(report.id ?? `reporte-${index + 1}`),
|
||||
country: (rawCountry === 'TT' ? 'TT' : rawCountry === 'PA' ? 'PA' : rawCountry === 'SV' ? 'SV' : 'GT') as HistoricalReport['country'],
|
||||
country: (rawCountry === 'TT' ? 'TT' : rawCountry === 'PA' ? 'PA' : rawCountry === 'SV' ? 'SV' : rawCountry === 'HN' ? 'HN' : 'GT') as HistoricalReport['country'],
|
||||
date: formatDate(report.fecha ?? report.period_end ?? rawExecutionDate, country.locale),
|
||||
executionDate: formatDateTime(rawExecutionDate, country.locale),
|
||||
period: String(report.periodo ?? report.period_label ?? report.period ?? 'Período no especificado'),
|
||||
@@ -471,7 +485,7 @@ export default function History({
|
||||
if (!isCurrentRequest()) return;
|
||||
|
||||
// Respaldo temporal: webhook histórico existente. También se envía el país
|
||||
// para impedir que se mezclen reportes de Guatemala, Trinidad y Tobago, Panamá y El Salvador.
|
||||
// para impedir que se mezclen reportes de Guatemala, Trinidad y Tobago, Panamá, El Salvador y Honduras.
|
||||
const historyUrl = getHistoryUrl(country.code);
|
||||
if (!historyUrl) {
|
||||
throw new Error(`No está configurado el origen histórico de ${country.name}.`);
|
||||
|
||||
@@ -33,6 +33,14 @@ export const COUNTRIES: CountryConfig[] = [
|
||||
locale: 'es-SV',
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
code: 'HN',
|
||||
name: 'Honduras',
|
||||
flag: 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/Bandera%20de%20Honduras.webp',
|
||||
currency: 'HNL',
|
||||
locale: 'es-HN',
|
||||
enabled: true,
|
||||
},
|
||||
];
|
||||
|
||||
export const DEFAULT_COUNTRY_CODE: CountryCode = 'GT';
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ export type ViewType = 'cruce' | 'historial';
|
||||
|
||||
export type ToastType = 'success' | 'error' | 'info';
|
||||
|
||||
export type CountryCode = 'GT' | 'TT' | 'PA' | 'SV';
|
||||
export type CountryCode = 'GT' | 'TT' | 'PA' | 'SV' | 'HN';
|
||||
|
||||
export interface CountryConfig {
|
||||
code: CountryCode;
|
||||
|
||||
Vendored
+6
@@ -5,12 +5,18 @@ interface ImportMetaEnv {
|
||||
readonly VITE_N8N_GUATEMALA_WEBHOOK_URL?: string;
|
||||
readonly VITE_N8N_TRINIDAD_WEBHOOK_URL?: string;
|
||||
readonly VITE_N8N_PANAMA_WEBHOOK_URL?: string;
|
||||
readonly VITE_N8N_EL_SALVADOR_WEBHOOK_URL?: string;
|
||||
readonly VITE_N8N_HONDURAS_WEBHOOK_URL?: string;
|
||||
readonly VITE_HISTORICOS_GT_URL?: string;
|
||||
readonly VITE_MARCAR_RESUELTO_GT_URL?: string;
|
||||
readonly VITE_HISTORICOS_TT_URL?: string;
|
||||
readonly VITE_MARCAR_RESUELTO_TT_URL?: string;
|
||||
readonly VITE_HISTORICOS_PA_URL?: string;
|
||||
readonly VITE_MARCAR_RESUELTO_PA_URL?: string;
|
||||
readonly VITE_HISTORICOS_SV_URL?: string;
|
||||
readonly VITE_MARCAR_RESUELTO_SV_URL?: string;
|
||||
readonly VITE_HISTORICOS_HN_URL?: string;
|
||||
readonly VITE_MARCAR_RESUELTO_HN_URL?: string;
|
||||
readonly VITE_ANALIZAR_NOMINA_URL?: string;
|
||||
readonly VITE_REPORTAR_BAMBOO_URL?: string;
|
||||
readonly VITE_REPORTAR_APP_URL?: string;
|
||||
|
||||
+279
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+514
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,163 @@
|
||||
{
|
||||
"name": "Portal de Verificación de Nómina Honduras - Históricos",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {
|
||||
"path": "cruce-cuentas-hn-historicos",
|
||||
"responseMode": "responseNode",
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.webhook",
|
||||
"typeVersion": 2.1,
|
||||
"position": [
|
||||
304,
|
||||
272
|
||||
],
|
||||
"id": "2fe1cba5-882d-4054-afa7-d331d33220aa",
|
||||
"name": "Webhook Históricos HN",
|
||||
"webhookId": "48f69298-ea9a-4ff2-a051-1052bbed0c1d"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"jsCode": "const items = $input.all();\n\nfunction formatDate(value) {\n if (!value) return '';\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return String(value).slice(0, 10);\n return date.toISOString().slice(0, 10);\n}\n\nfunction toNumber(value) {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : 0;\n}\n\nfunction normalizeStatus(value, discrepancias) {\n const status = String(value || '').toLowerCase();\n\n if (status === 'resuelto') return 'resuelto';\n if (status === 'procesado' && toNumber(discrepancias) === 0) return 'procesado';\n\n return 'pendiente_revision';\n}\n\nfunction statusLabel(status) {\n if (status === 'resuelto') return 'RESUELTO';\n if (status === 'procesado') return 'PROCESADO';\n return 'PENDIENTE REVISIÓN';\n}\n\nconst reports = items.map((item) => {\n const row = item.json || {};\n\n const discrepancias = toNumber(row.discrepancias);\n const bancoSinNomina = toNumber(row.banco_sin_nomina);\n const nominaSinCuenta = toNumber(row.nomina_sin_cuenta);\n const bancoSinBamboo = toNumber(row.banco_sin_bamboo);\n const nominaSinBamboo = toNumber(row.nomina_sin_bamboo);\n const bambooSinNomina = toNumber(row.bamboo_sin_nomina);\n\n const totalPendientes =\n discrepancias +\n bancoSinNomina +\n nominaSinCuenta +\n bancoSinBamboo +\n nominaSinBamboo +\n bambooSinNomina;\n\n const estado = normalizeStatus(row.estado, totalPendientes);\n\n return {\n id: row.id,\n\n fecha: formatDate(row.period_end || row.ejecutado_en || row.created_at),\n fechaEjecucion: row.ejecutado_en || row.created_at,\n\n periodo: row.period_label || '',\n year: row.year,\n month: row.month,\n periodType: row.period_type,\n periodStart: row.period_start,\n periodEnd: row.period_end,\n\n archivoNomina: row.payroll_file_name || '',\n archivosBanco: row.bank_file_names || [],\n\n coincidencias: toNumber(row.coincidencias),\n discrepancias,\n bancoSinNomina,\n nominaSinCuenta,\n bancoSinBamboo,\n nominaSinBamboo,\n bambooSinNomina,\n totalPendientes,\n\n totalNomina: toNumber(row.total_nomina),\n totalBanco: toNumber(row.total_banco),\n diferenciaTotal: toNumber(row.diferencia_total),\n\n estado,\n estadoLabel: statusLabel(estado),\n\n reportUrl: row.report_url || '',\n spreadsheetId: row.spreadsheet_id || '',\n\n ejecutadoPorNombre: row.ejecutado_por_nombre || '',\n ejecutadoPorEmail: row.ejecutado_por_email || '',\n\n resueltoPorNombre: row.resuelto_por_nombre || '',\n resueltoPorEmail: row.resuelto_por_email || '',\n resueltoEn: row.resuelto_en || null,\n comentarioResolucion: row.comentario_resolucion || '',\n };\n});\n\nreturn [\n {\n json: {\n ok: true,\n stage: 'historicos_hn_listos',\n count: reports.length,\n reports,\n },\n },\n];"
|
||||
},
|
||||
"type": "n8n-nodes-base.code",
|
||||
"typeVersion": 2,
|
||||
"position": [
|
||||
720,
|
||||
272
|
||||
],
|
||||
"id": "dcd21660-393d-4ce2-9092-e61fc909fe0a",
|
||||
"name": "Normalizar históricos para app"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"url": "https://dbit.digitalcompass.agency/rest/v1/cruces_cuentas_gt_reportes?select=*&country=eq.HN&order=ejecutado_en.desc&limit=100",
|
||||
"sendHeaders": true,
|
||||
"headerParameters": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "apikey",
|
||||
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q"
|
||||
},
|
||||
{
|
||||
"name": "Authorization",
|
||||
"value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAicm9sZSI6ICJzZXJ2aWNlX3JvbGUiLAogICAgImlzcyI6ICJzdXBhYmFzZS1kZW1vIiwKICAgICJpYXQiOiAxNjQxNzY5MjAwLAogICAgImV4cCI6IDE3OTk1MzU2MDAKfQ.DaYlNEoUrrEn2Ig7tqibS-PHK5vgusbcbo7X36XVt4Q"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"type": "n8n-nodes-base.httpRequest",
|
||||
"typeVersion": 4.4,
|
||||
"position": [
|
||||
512,
|
||||
272
|
||||
],
|
||||
"id": "60a88bd9-4050-43d7-a88d-b415f90d1543",
|
||||
"name": "Consultar históricos Supabase"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"respondWith": "json",
|
||||
"responseBody": "={{ $json }}",
|
||||
"options": {
|
||||
"responseHeaders": {
|
||||
"entries": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"name": "Access-Control-Allow-Origin",
|
||||
"value": "*"
|
||||
},
|
||||
{
|
||||
"name": "Access-Control-Allow-Methods",
|
||||
"value": "GET,POST,OPTIONS"
|
||||
},
|
||||
{
|
||||
"name": "Access-Control-Allow-Headers",
|
||||
"value": "Content-Type, Authorization"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "n8n-nodes-base.respondToWebhook",
|
||||
"typeVersion": 1.5,
|
||||
"position": [
|
||||
944,
|
||||
272
|
||||
],
|
||||
"id": "9f35b767-e0f2-40a5-8097-8bc623669b8d",
|
||||
"name": "Responder históricos app"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"content": "# 📚 HISTÓRICOS DE VERIFICACIÓN — HONDURAS\n\nEntrega a la aplicación el historial de reportes procesados para Honduras.\n\n- Consulta únicamente registros con `country = HN`.\n- Supabase continúa usando la tabla histórica compartida `cruces_cuentas_gt_reportes`; el nombre de la tabla es legado y funciona como tabla multi-país.\n- Devuelve la misma estructura de históricos utilizada por los demás países.\n- Este webhook sirve como respaldo; la app intenta primero la RPC paginada de Supabase.",
|
||||
"height": 496,
|
||||
"width": 1360,
|
||||
"color": 6
|
||||
},
|
||||
"type": "n8n-nodes-base.stickyNote",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"id": "8e6b3835-6bd6-4f64-827e-24744543d76c",
|
||||
"name": "Sticky Note"
|
||||
}
|
||||
],
|
||||
"pinData": {},
|
||||
"connections": {
|
||||
"Consultar históricos Supabase": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Normalizar históricos para app",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Normalizar históricos para app": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Responder históricos app",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Webhook Históricos HN": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Consultar históricos Supabase",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1",
|
||||
"binaryMode": "separate"
|
||||
},
|
||||
"versionId": "c2ddfc50-7d6a-4750-9f9c-1e7e423b6380",
|
||||
"meta": {
|
||||
"instanceId": "b4b77b17af092830e794eef639ce2f6d7daccf7eddc075060b03b3b6545aac70"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
Reference in New Issue
Block a user