@fullqueso/mcp-bc-gastos 1.28.0 → 1.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +7 -0
- package/CHANGELOG.md +127 -0
- package/README.md +219 -66
- package/config/bank-gl-map.json +4 -2
- package/config/income-accounts.js +29 -0
- package/lib/bc-client.js +37 -3
- package/package.json +1 -1
- package/scripts/generate-tools-doc.mjs +129 -0
- package/server.js +17 -2
- package/tools/financials/income-statement.js +265 -0
- package/tools/financials/index.js +1 -0
- package/tools/get-crm-rate.js +109 -0
- package/tools/multi-payment/index.js +1 -0
- package/tools/multi-payment/unposted-invoices.js +224 -0
- package/tools/payroll/cost-model.js +51 -0
- package/tools/payroll/employees.js +29 -5
- package/tools/payroll/payroll-documents.js +196 -12
- package/tools/payroll/payroll-lines.js +57 -33
- package/tools/ventas/sales-analysis.js +1 -1
- package/tools/ventas/sales-store-comparison.js +5 -1
- package/utils/sales-aggregation.js +35 -1
package/.env.example
CHANGED
|
@@ -17,3 +17,10 @@ BC_COMPANY_FQFR=guid-for-franquicias
|
|
|
17
17
|
|
|
18
18
|
# Optional
|
|
19
19
|
LOG_LEVEL=info
|
|
20
|
+
|
|
21
|
+
# Per-request BC fetch timeout (ms). A hung request is aborted + retried instead
|
|
22
|
+
# of blocking the MCP forever (BUG #1 fix). Default 90000. Raise for heavy reports.
|
|
23
|
+
BC_FETCH_TIMEOUT_MS=90000
|
|
24
|
+
|
|
25
|
+
# CRM rate endpoint (get_crm_rate) — opcional, default abajo
|
|
26
|
+
CRM_RATE_BASE=https://crm-rate.fullqueso.com/api/v1
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,132 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## [1.33.0] — 2026-07-11
|
|
4
|
+
|
|
5
|
+
### Added — `get_income_statement`: estado de resultados en formato Power BI (Level 1)
|
|
6
|
+
Nuevo tool en `tools/financials/income-statement.js`. READ-ONLY.
|
|
7
|
+
|
|
8
|
+
- **Qué expone:** el reporte "Income Statement by Month" de Power BI (G/L Account **Level 1**)
|
|
9
|
+
para una tienda y un mes, agregando los General Ledger entries por cuenta level-1 con la
|
|
10
|
+
convención de signos de Power BI (`amount = debitAmount − creditAmount` → ingresos negativos,
|
|
11
|
+
costos/gastos positivos, Total negativo = ganancia). Devuelve las 3 secciones (40001/50001/60001),
|
|
12
|
+
cuentas level-1, totales, Total general y un **`accounts_map`** listo para el generador Excel
|
|
13
|
+
del skill-fq-resultados-mensual (`data.json`). Con `level=2` incluye las cuentas de posteo
|
|
14
|
+
hijas con su nombre real de BC.
|
|
15
|
+
- **Por qué:** resuelve las fronteras que `get_financial_statements` (agrega por categoría) no
|
|
16
|
+
separa: nómina **71000** vs impuestos **74000**, y otros **74000** (80xxx) vs **90000** (9xxxx).
|
|
17
|
+
Trabaja desde los entries por cuenta, así que cuadra 1:1 con Power BI.
|
|
18
|
+
- **Estructura level-1** embebida (`COA_PBI`) — fuente: screenshots Power BI FQ28, CoA compartido
|
|
19
|
+
FQ01/FQ28/FQ88. Espejo de `skill-fq-resultados-mensual/references/income_statement_coa.json`.
|
|
20
|
+
- **Nuevo en bc-client:** `getChartOfAccountNames(companyId)` (entidad BC `accounts`, cache por
|
|
21
|
+
companyId) para etiquetar las cuentas hijas en level 2.
|
|
22
|
+
- **Test:** `tests/test-income-statement.js` (agregación pura con GL entries mock, sin BC):
|
|
23
|
+
valida signos, mapeo por rangos, fronteras 71000/74000 y 74000/90000, totales y level 2.
|
|
24
|
+
- La columna **Ajustado** (drafts como posteados) y la reclasificación de dividendos las agrega
|
|
25
|
+
el skill sobre el `posted` — el MCP entrega solo el posted objetivo del GL.
|
|
26
|
+
|
|
27
|
+
## [1.32.1] — 2026-07-04
|
|
28
|
+
|
|
29
|
+
### Docs — README "Tools" regenerado desde el código (22 stale → 57 reales)
|
|
30
|
+
- **`scripts/generate-tools-doc.mjs`**: importa dinámicamente todos los `*Tool` de
|
|
31
|
+
`tools/**/*.js` (incluye definiciones en `index.js`, dedup por `name`), agrupa por
|
|
32
|
+
categoría y reescribe la sección `## Tools (N)` del README. Verificado 57/57 contra
|
|
33
|
+
el switch de `server.js`. Uso: `node scripts/generate-tools-doc.mjs --write`
|
|
34
|
+
(sin flag = dry-run a stdout). La sección queda marcada como autogenerada.
|
|
35
|
+
|
|
36
|
+
## [1.32.0] — 2026-07-04
|
|
37
|
+
|
|
38
|
+
### Added — `get_unposted_invoices`: facturas sin postear para Cierre Parcial
|
|
39
|
+
Nuevo tool en el cluster de draft visibility (`tools/multi-payment/unposted-invoices.js`).
|
|
40
|
+
Verificado contra BC real (mayo 2026, 4 empresas). READ-ONLY.
|
|
41
|
+
|
|
42
|
+
- **Qué expone:** facturas de compra y venta con `status` `Draft`/`In Review` en
|
|
43
|
+
`api/v2.0` `purchaseInvoices`/`salesInvoices` — documentos que NO están en el GL.
|
|
44
|
+
Complementa a `get_draft_payables`/`receivables`/`summary`, que son Multi-Payments
|
|
45
|
+
(settlement) sobre facturas ya posteadas y por tanto no sirven como ajuste P&L.
|
|
46
|
+
- **Para qué:** ajuste de CIERRE PARCIAL del P&L en `skill-fq-resultados-mensual`
|
|
47
|
+
(compras draft → +costo/gasto; ventas draft no-IC → +ingreso). Cada tienda incluye
|
|
48
|
+
`pl_adjustment_hint` con los montos listos.
|
|
49
|
+
- **Params:** `store` (incl. `all`), `type` (purchases|sales|both), `start_date`/`end_date`
|
|
50
|
+
(filtro por `postingDate`), `include_in_review` (default true), `summary_only`.
|
|
51
|
+
- **Multi-moneda correcto:** totales SIEMPRE por moneda (`by_currency`,
|
|
52
|
+
`non_ic_by_currency`, consolidado por moneda) — nunca se suman VES con USD.
|
|
53
|
+
`LCY` = USD; VES se convierte aguas arriba con `get_exchange_rate`.
|
|
54
|
+
- **Ventas IC:** separa clientes `IC-*` (`intercompany`) del total ajustable.
|
|
55
|
+
- **Smoke test:** `tests/test-unposted-invoices.js` (store y mes por CLI).
|
|
56
|
+
- Hallazgo del estreno (mayo 2026): FQ01 11 compras sin postear (Bs 1,838,737.54),
|
|
57
|
+
FQ28 4 (Bs 1,210,031.04), FQFR 2 ($7,116.50), FQ01 1 venta IC (Bs 165,456.04).
|
|
58
|
+
|
|
59
|
+
## [1.31.0] — 2026-07-01
|
|
60
|
+
|
|
61
|
+
### Added — KPIs de nómina operativa: tienda vs eventos, activos vs pagados
|
|
62
|
+
Contratos existentes intactos (solo se agregan campos). Todo USD. Verificado contra BC real (mayo 2026, FQ01/FQ28/FQ88/FQFR).
|
|
63
|
+
|
|
64
|
+
- **`paid_employees` en `get_payroll_documents` (con `include_employee_count=true`).** Además de `summary.unique_employees`, ahora `summary.paid_employees`: lista agregada por empleado en el período con `{ employee_code, employee_name, cedula, cost_usd, lines, payroll_types:[...], classification }`. Permite cruzar activos vs pagados sin leer las líneas doc por doc.
|
|
65
|
+
- **Deduplicación por cédula.** `unique_employees` y `paid_employees` deduplican por `cedula` cuando existe (una persona puede tener un código SEMANAL y otro EVENTO → se consolidan costo y líneas en un registro). `summary.duplicate_codes: [{ cedula, codes:[...] }]` expone los casos multi-código para limpieza en BC. Si `cedula` es null se trata el código como persona única (no se fusiona por nombre). El consolidado (`store="all"`) re-deduplica también entre tiendas (misma cédula en dos tiendas = una persona).
|
|
66
|
+
- Resolución de cédula en 2 pasos: si un código tiene cédula en alguna línea, las líneas con cédula en blanco de ese código heredan la cédula (evita partir un código en dos personas). Verificado mayo 2026: 0 splits, 0 merges; 82 personas = 58 con cédula (1:1) + 24 códigos sin cédula en BC (data-quality, tratados como únicos).
|
|
67
|
+
- **Clasificación evento/operativo por empleado** (relevante FQ01): `classification` = `"evento"` (todos sus pagos EVENTO), `"operativo"` (ningún EVENTO — SEMANAL/VACACIONES/LIQUIDACION), `"mixto"` (tiene EVENTO y no-EVENTO). Evita marcar como "inactivo no declarado" a quien solo trabaja eventos.
|
|
68
|
+
- **Buckets de nómina tienda vs eventos** en `summary` (per-store y consolidado): `store_payroll` = Σ `cost_usd` de docs SEMANAL + VACACIONES + LIQUIDACION; `event_payroll` = Σ EVENTO. `total_cost_usd` se mantiene igual (gerencia queda solo ahí). Constantes `STORE_PAYROLL_TYPES`/`EVENT_PAYROLL_TYPES` en `tools/payroll/cost-model.js`.
|
|
69
|
+
|
|
70
|
+
### Added — Denominador de ventas correcto para el KPI (get_sales_analysis / compare_sales_by_store)
|
|
71
|
+
- **`revenue_store`, `revenue_events`, `revenue_intercompany`, `revenue_non_sales` + `revenue_total`** en `summary` (y por tienda en `compare_sales_by_store`), además del `total_revenue_usd` actual (sin cambios). Identidad: `revenue_total = revenue_store + revenue_intercompany + revenue_events + revenue_non_sales`.
|
|
72
|
+
- **`revenue_store = revenue_total − intercompañía − eventos − no-venta`.** Se excluye del denominador de tienda la venta intercompañía, la venta de eventos y el ingreso **no-venta** (FX gains + rebajas de compra) — este último por recomendación aceptada por FP.
|
|
73
|
+
- **Cuentas configurables (no inline), por tienda** en `config/income-accounts.js`, verificadas contra el CoA real:
|
|
74
|
+
- `INTERCOMPANY_REVENUE_ACCOUNTS = ['40310']` (tiendas operativas; FQ01/FQ88, FQ28=0).
|
|
75
|
+
- `EVENT_REVENUE_ACCOUNTS = ['40180','40700']` (solo FQ01 con movimiento).
|
|
76
|
+
- `NON_SALES_REVENUE_ACCOUNTS = ['40580','40540','40550']` (dif. cambiario + descuentos/rebajas de compra).
|
|
77
|
+
- **FQFR** usa `FQFR_INTERCOMPANY_REVENUE_ACCOUNTS = ['40420','40430','40660','40930']` (royalties/fees del master de franquicia, NO 40310) vía `getRevenueClassification(storeCode)` → sus buckets salen correctos aunque el KPI no aplique.
|
|
78
|
+
- Lógica en `splitRevenueBuckets(glRevenue, total, storeCode)` (`utils/sales-aggregation.js`); `calculateSummary` ahora recibe `storeCode` (los dos tools de ventas lo pasan por tienda).
|
|
79
|
+
- **KPI** (lo calcula el consumidor cruzando ambos tools): `nomina_tienda_pct = store_payroll / revenue_store`; `nomina_eventos_pct = event_payroll / revenue_events` (**null si `revenue_events = 0`** → solo FQ01). **FQFR excluido por completo** del KPI (`KPI_PAYROLL_STORES = ['FQ01','FQ28','FQ88']`).
|
|
80
|
+
- Verificado mayo 2026 (2 fases, `tests/verify-payroll-sales-kpi.js`):
|
|
81
|
+
- **Estricto** (− interco − eventos), coincide con la validación de FP: FQ01 8.36% tienda / 31.02% eventos (rev_store 86,987.43); FQ28 10.22% (77,407.51); FQ88 8.75% (150,122.32).
|
|
82
|
+
- **Refinado** (además − no-venta): FQ01 8.36% (86,940.37); FQ28 10.25% (77,146.08); FQ88 8.80% (149,322.16).
|
|
83
|
+
|
|
84
|
+
### Nota operativa
|
|
85
|
+
- **Reiniciar Claude Desktop** tras el publish para que los tools recarguen (G-002).
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## [1.30.0] — 2026-07-01
|
|
90
|
+
|
|
91
|
+
### Fixed
|
|
92
|
+
- **BUG #1 — `get_employees`/`get_payroll_documents` con `store="all"` podían colgar el MCP indefinidamente.** Causa raíz: `fetch()` no tenía timeout → una sola petición colgada nunca resolvía, y como el consolidado usaba `Promise.all`, una tienda colgada tumbaba todo el call (aparecía como "MCP no responde").
|
|
93
|
+
- **`lib/bc-client.js` — timeout por petición (AbortController).** Cada intento de `fetchWithRetry` aborta a los `BC_FETCH_TIMEOUT_MS` (default **90000**, env-tunable) y reintenta; al agotar reintentos lanza un error claro de timeout en vez de colgarse. Cubre token + todas las llamadas v2.0/OData/custom (afecta a las 54 tools; el default generoso no toca ninguna llamada legítima — todas las observadas <1s).
|
|
94
|
+
- **Consolidación multi-tienda resiliente.** `get_payroll_documents` y `get_employees` con `store="all"` ahora usan `Promise.allSettled`: una tienda que falle/timeoutee devuelve data parcial + un array `errors[]` por tienda, nunca cuelga ni bota el consolidado.
|
|
95
|
+
- Verificado: per-store discovery <500ms; el timeout aborta+reintenta (3x) y lanza error en ~6s con `BC_FETCH_TIMEOUT_MS=1`.
|
|
96
|
+
|
|
97
|
+
### Changed / Added — KPIs de nómina operativa
|
|
98
|
+
- **#2/#3 — Costo total en USD por documento (`total_cost_usd`) robusto a drafts sin distribuir.** Nuevo módulo `tools/payroll/cost-model.js` como **fuente única de verdad del costo**. Regla (verificada contra 448 docs reales): `total_cost_usd = totalGrossUSD (>0) → si no, bonos-equivalente USD → si no, neto (MAX, no suma)`. El campo `cost_basis` (`gross`|`bonuses`|`net`) hace auditable de dónde salió el número, y `net_distributed:false` marca los drafts cuyo neto aún no se reparte por método de pago (elimina el parche `max(neto,bonos)`).
|
|
99
|
+
- **Por qué la suma `net_usd + net_ves/tasa` estaba mal:** VACACIONES/LIQUIDACION contabilizan el MISMO monto en `totalNetUSD` **y** `totalNetVES` → esa suma **duplica** el costo. `totalGrossUSD` es el costo real y ya está en el header (antes no se seleccionaba).
|
|
100
|
+
- Expuestos en cada documento: `total_cost_usd`, `cost_basis`, `net_distributed`, `total_gross_usd`, `total_deductions_usd`. Agregado `total_cost_usd` a `summary` y `consolidated`.
|
|
101
|
+
- **#4 — Enums de `payroll_type` corregidos a los valores reales de BC + `exclude_managerial`.** El enum viejo `[SEMANAL|QUINCENAL|MENSUAL]` estaba mal (QUINCENAL/MENSUAL no existen).
|
|
102
|
+
- `get_payroll_documents`: `SEMANAL, GERENCIAL, GERENCIAL_CIERRE, EVENTO, VACACIONES, LIQUIDACION`. Nuevo flag `exclude_managerial` → excluye GERENCIAL + GERENCIAL_CIERRE server-side (nómina operativa en un solo call).
|
|
103
|
+
- `get_employees`: `SEMANAL, GERENCIAL, EVENTO`. Nuevo flag `exclude_managerial` → excluye `payrollType GERENCIAL` + `employeeType Management`.
|
|
104
|
+
- **#5 — `summary.by_type` ahora trae costo, no solo conteo.** De `{ TIPO: n }` a `{ TIPO: { count, lines, cost_usd } }` (per-store y consolidado).
|
|
105
|
+
- **#6 — Headcount único de empleados pagados en el periodo.** Nuevo flag `include_employee_count` en `get_payroll_documents` → `summary.unique_employees` (y `consolidated.unique_employees`). Lee las líneas de los docs filtrados en lotes de OR (no doc-por-doc). Distingue el headcount real de `total_employees_lines` (que duplica al mismo empleado por cada corrida semanal).
|
|
106
|
+
- **#7 — Costo por empleado en USD en `get_payroll_lines`.** Expuesto `total_ves_equivalent` (campo de la API antes sin usar) y `cost_usd` por línea = `totalVESEquivalent / tasa` (la tasa se lee del header en 1 llamada extra). `summary` trae `total_cost_usd`, `total_ves_equivalent`, `payroll_type`, `status`, `exchange_rate`.
|
|
107
|
+
- **Nota (L-001):** la API `fqPayrollLines` **no** expone días/horas trabajados ni FTE por línea → no se pueden calcular KPIs de productividad laboral sin ampliar la extensión de BC. Documentado, no fabricado.
|
|
108
|
+
|
|
109
|
+
### Nota operativa
|
|
110
|
+
- **Reiniciar Claude Desktop** tras el publish para que los tools recarguen (G-002).
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## [1.29.0] — 2026-06-21
|
|
115
|
+
|
|
116
|
+
### Added
|
|
117
|
+
- **`get_crm_rate` — tasa de cambio Bs/USD desde el CRM de Full Queso (tasa de referencia, hora Caracas).** Complementa a `get_exchange_rate` (que trae la tasa BCV contable vigente en BC): este tool consulta el endpoint del CRM `https://crm-rate.fullqueso.com` y expone tanto la tasa **USDT/Binance** (paralelo) como la **BCV** (oficial), histórica o del día.
|
|
118
|
+
- **Dos monedas con sinónimos:** `coin` acepta `USDT`/`BINANCE` (paralelo, mismo valor) y `VES`/`BCV` (oficial). La descripción dispara con "tasa USDT, tasa Binance, dólar Binance, tasa paralelo/paralela, tasa BCV, tasa oficial, tasa del día, tasa histórica".
|
|
119
|
+
- **Histórica vs del día automático:** con `date` (YYYY-MM-DD) → `GET /api/v1/history/price?coin={VES|USDT}&date=...`; sin `date` → `GET /api/v1/coin/{bcv|usdt}` (momento de la consulta).
|
|
120
|
+
- Maneja `400` (coin/date inválidos → error claro), `404` (sin registro → `price: null` + mensaje) y fallo de conexión. Precio redondeado a 2 decimales; respuesta incluye `queried_at_caracas` y `source`.
|
|
121
|
+
- Base URL configurable vía env **`CRM_RATE_BASE`** (default `https://crm-rate.fullqueso.com/api/v1`). El endpoint no requiere auth de BC → tool autónomo (ignora `bcClient`).
|
|
122
|
+
- Nuevo `tools/get-crm-rate.js`; registrado en `server.js` (import + lista de tools + switch).
|
|
123
|
+
- **Verificado contra el endpoint real (21-jun-2026):** USDT del día ≈798.35; BCV del día 612.43; histórica USDT 19-jun 805.40; histórica VES 19-jun **607.39** (coincide con el ejemplo del doc); 404 (1990) y coin inválido (DOGE) manejados. `node --check` OK.
|
|
124
|
+
|
|
125
|
+
### Nota operativa
|
|
126
|
+
- **Reiniciar Claude Desktop** tras el publish para que el tool recargue (G-002).
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
3
130
|
## [1.28.0] — 2026-06-12
|
|
4
131
|
|
|
5
132
|
### Added
|
package/README.md
CHANGED
|
@@ -52,111 +52,264 @@ cp .env.example .env
|
|
|
52
52
|
npm start
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
## Tools (
|
|
55
|
+
## Tools (58)
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
_Sección generada por `scripts/generate-tools-doc.mjs` — no editar a mano._
|
|
58
|
+
_Regenerar con: `node scripts/generate-tools-doc.mjs --write`_
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
Detailed expense analysis by category with account-level detail and benchmark comparisons.
|
|
61
|
-
- Params: `stores`, `period`, `month`, `start_date`, `end_date`
|
|
62
|
-
|
|
63
|
-
#### get_efficiency_ratios
|
|
64
|
-
Financial ratios: expense-to-income, payroll, rent, utilities, marketing, operating margin.
|
|
65
|
-
- Params: `stores`, `period`, `month`, `start_date`, `end_date`
|
|
60
|
+
### Expense Analysis & Core (11 tools)
|
|
66
61
|
|
|
67
62
|
#### compare_stores
|
|
68
|
-
|
|
63
|
+
Compara la eficiencia de gastos entre las tiendas de Full Queso (FQ01 Chacao, FQ28 Marqués, FQ88 Candelaria).
|
|
69
64
|
- Params: `period`, `month`, `start_date`, `end_date`
|
|
70
65
|
|
|
71
66
|
#### detect_anomalies
|
|
72
|
-
|
|
73
|
-
- Params: `
|
|
67
|
+
Detecta anomalías en los gastos operacionales de Full Queso: gastos por encima de benchmarks, incrementos inusuales vs periodo anterior, concentración excesiva en una cuenta, y alertas de margen.
|
|
68
|
+
- Params: `period`, `month`, `start_date`, `end_date`, `stores`, `sensitivity`
|
|
74
69
|
|
|
75
|
-
####
|
|
76
|
-
|
|
77
|
-
- Params: `store
|
|
70
|
+
#### get_account_transactions
|
|
71
|
+
Listado completo de transacciones para una cuenta contable específica con balance running y nombre de proveedor.
|
|
72
|
+
- Params: `account_number`*, `store`*, `start_date`*, `end_date`*
|
|
78
73
|
|
|
79
|
-
|
|
74
|
+
#### get_crm_rate
|
|
75
|
+
Obtiene la tasa de cambio Bs/USD desde el CRM de Full Queso (hora Caracas).
|
|
76
|
+
- Params: `coin`*, `date`
|
|
77
|
+
|
|
78
|
+
#### get_efficiency_ratios
|
|
79
|
+
Calcula ratios de eficiencia financiera de Full Queso: gastos/ingresos, nómina/ingresos, alquiler/ingresos, servicios/ingresos, marketing/ingresos y margen operativo.
|
|
80
|
+
- Params: `period`, `month`, `start_date`, `end_date`, `stores`
|
|
81
|
+
|
|
82
|
+
#### get_exchange_rate
|
|
83
|
+
Obtiene la tasa de cambio USD → VES desde Business Central.
|
|
84
|
+
- Params: `store`*, `date`, `start_date`, `end_date`
|
|
85
|
+
|
|
86
|
+
#### get_expense_analysis
|
|
87
|
+
Análisis detallado de gastos operacionales de Full Queso por categoría (nómina, alquiler, servicios, marketing, etc.) con números de cuenta específicos.
|
|
88
|
+
- Params: `period`, `month`, `start_date`, `end_date`, `stores`
|
|
80
89
|
|
|
81
90
|
#### get_expense_details
|
|
82
|
-
|
|
83
|
-
- Params: `store
|
|
91
|
+
Drill-down de transacciones individuales de gastos operacionales.
|
|
92
|
+
- Params: `store`*, `period`, `month`, `start_date`, `end_date`, `category`, `account_number`, `min_amount`, `vendor_search`, `limit`, `offset`
|
|
84
93
|
|
|
85
|
-
####
|
|
86
|
-
|
|
87
|
-
- Params: `
|
|
94
|
+
#### get_trends
|
|
95
|
+
Análisis de tendencias históricas de gastos e ingresos de Full Queso.
|
|
96
|
+
- Params: `months`, `store`
|
|
88
97
|
|
|
89
98
|
#### get_vendor_transactions
|
|
90
|
-
|
|
91
|
-
- Params: `store
|
|
99
|
+
Todas las transacciones de gastos operacionales de un proveedor específico.
|
|
100
|
+
- Params: `store`*, `vendor_search`*, `start_date`*, `end_date`*
|
|
92
101
|
|
|
93
102
|
#### list_vendors
|
|
94
|
-
|
|
95
|
-
- Params: `store
|
|
103
|
+
Lista todos los proveedores activos de una tienda en un periodo, con monto total pagado y número de transacciones.
|
|
104
|
+
- Params: `store`*, `start_date`, `end_date`
|
|
105
|
+
|
|
106
|
+
### Auditoría / POS Reconciliation (11 tools)
|
|
96
107
|
|
|
97
|
-
|
|
108
|
+
#### find_potential_matches
|
|
109
|
+
Para una línea no conciliada del banco, busca posibles correspondencias en las entradas contables de BC por monto, fecha y descripción.
|
|
110
|
+
- Params: `store`*, `bank_account`*, `statement_amount`*, `transaction_date`*, `description`, `date_tolerance_days`, `amount_tolerance_pct`
|
|
98
111
|
|
|
99
|
-
|
|
112
|
+
#### get_bank_ledger_entries
|
|
113
|
+
Todos los movimientos del libro de banco (abiertos y cerrados) para una cuenta bancaria.
|
|
114
|
+
- Params: `store`*, `bank_account`*, `date_from`*, `date_to`*, `open_only`
|
|
100
115
|
|
|
101
|
-
####
|
|
102
|
-
|
|
103
|
-
- Params: `store`
|
|
116
|
+
#### get_bank_reconciliation_report
|
|
117
|
+
Reporte consolidado de reconciliación bancaria: progreso de todos los statements abiertos, líneas no conciliadas (débitos y créditos por separado), y sugerencias de asientos contables para débitos.
|
|
118
|
+
- Params: `store`*, `bank_account`*, `statement_no`, `month`, `min_amount`, `include_suggestions`, `save_to_file`, `excel_output`
|
|
119
|
+
|
|
120
|
+
#### get_gl_account_entries
|
|
121
|
+
Movimientos del libro mayor (G/L) para una cuenta específica.
|
|
122
|
+
- Params: `store`*, `gl_account`*, `date_from`*, `date_to`*
|
|
123
|
+
|
|
124
|
+
#### get_pm_receipts
|
|
125
|
+
Recibos de Pago Móvil (PM) registrados en BC para una cuenta bancaria.
|
|
126
|
+
- Params: `store`*, `bank_account`*, `date_from`*, `date_to`*
|
|
104
127
|
|
|
105
128
|
#### get_reconciliation_status
|
|
106
|
-
|
|
107
|
-
- Params: `store
|
|
129
|
+
Resumen del estado de reconciliaciones bancarias abiertas: total líneas, conciliadas, pendientes y diferencia.
|
|
130
|
+
- Params: `store`*, `bank_account`
|
|
131
|
+
|
|
132
|
+
#### get_unmatched_ledger_entries
|
|
133
|
+
Entradas contables del banco en BC sin correspondencia en el estado de cuenta.
|
|
134
|
+
- Params: `store`*, `bank_account`*, `date_from`, `date_to`
|
|
108
135
|
|
|
109
136
|
#### get_unmatched_statement_lines
|
|
110
|
-
|
|
111
|
-
- Params: `store
|
|
137
|
+
Líneas del estado de cuenta bancario NO conciliadas con entradas en BC.
|
|
138
|
+
- Params: `store`*, `bank_account`*, `statement_no`, `min_amount`, `type_filter`
|
|
112
139
|
|
|
113
|
-
####
|
|
114
|
-
|
|
115
|
-
- Params: `store
|
|
140
|
+
#### list_bank_accounts
|
|
141
|
+
Lista las cuentas bancarias de una tienda Full Queso.
|
|
142
|
+
- Params: `store`*
|
|
116
143
|
|
|
117
|
-
####
|
|
118
|
-
|
|
119
|
-
- Params: `store
|
|
144
|
+
#### reconcile_pos_sales
|
|
145
|
+
Conciliación automática de ventas POS: cruza montos bancarios de BC (BankAccountLedgerEntries) con liquidaciones bancarias por número de lote, agrupa por liquidación bancaria (settlement batches), calcula comisiones reales.
|
|
146
|
+
- Params: `store`*, `start_date`*, `end_date`*, `bank_account`
|
|
120
147
|
|
|
121
148
|
#### suggest_journal_entries
|
|
122
|
-
|
|
123
|
-
- Params: `store
|
|
149
|
+
Para pagos del banco sin correspondencia en BC, sugiere asientos contables basándose en la descripción y patrones históricos.
|
|
150
|
+
- Params: `store`*, `bank_account`*, `statement_no`, `auto_categorize`
|
|
124
151
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
-
|
|
130
|
-
- **UBII**: cross-account matching (BC virtual account → Bancrecer/BDV deposits)
|
|
131
|
-
- Params: `store` (req), `start_date` (req), `end_date` (req), `bank_account`
|
|
152
|
+
### Cierre Mensual Bancario (6 tools)
|
|
153
|
+
|
|
154
|
+
#### generate_closing_journal
|
|
155
|
+
Genera el EXCEL del CIERRE MENSUAL bancario (mes completo, multi-banco).
|
|
156
|
+
- Params: `store`*, `month`*, `output_dir`, `allow_partial`, `strict`
|
|
132
157
|
|
|
133
|
-
|
|
158
|
+
#### get_closing_match_results
|
|
159
|
+
CIERRE MENSUAL bancario: vista paginada de las 4 listas con auto-clasificación.
|
|
160
|
+
- Params: `store`*, `month`*, `list`, `bank_account`, `category`, `source`, `confidence`, `min_abs_amount_ves`, `sort`, `limit`, `offset`
|
|
134
161
|
|
|
135
|
-
|
|
162
|
+
#### get_closing_questionnaire
|
|
163
|
+
CIERRE MENSUAL bancario: vista paginada/filtrada de las entradas del cuestionario del MES.
|
|
164
|
+
- Params: `store`*, `month`*, `bucket`, `counterparty_no`, `bank_account`, `status_filter`, `min_abs_amount_ves`, `min_abs_amount_usd`, `limit`, `offset`, `sort`, `pair_by_amount`, `pair_tolerance_pct`, `pair_max_days`, `description_regex`
|
|
165
|
+
|
|
166
|
+
#### reconcile_closing_with_bc
|
|
167
|
+
CIERRE MENSUAL bancario: refresca el estado contra BC para detectar drift desde la última corrida.
|
|
168
|
+
- Params: `store`*, `month`*
|
|
169
|
+
|
|
170
|
+
#### start_month_closing
|
|
171
|
+
CIERRE MENSUAL bancario (mes completo, multi-banco) en Business Central.
|
|
172
|
+
- Params: `store`*, `month`, `force_refresh`
|
|
173
|
+
|
|
174
|
+
#### submit_closing_answers
|
|
175
|
+
CIERRE MENSUAL bancario: registra respuestas/aprobaciones al cuestionario del MES.
|
|
176
|
+
- Params: `store`*, `month`*, `user`, `answers`*
|
|
177
|
+
|
|
178
|
+
### Cobranzas (AR / AP) (7 tools)
|
|
179
|
+
|
|
180
|
+
#### get_collection_status
|
|
181
|
+
Verificación rápida del estado de cobranza de un período específico.
|
|
182
|
+
- Params: `store`*, `start_date`*, `end_date`*
|
|
136
183
|
|
|
137
184
|
#### get_customer_balances
|
|
138
|
-
|
|
139
|
-
- Params: `store
|
|
185
|
+
Lista clientes con saldos pendientes, montos vencidos y estado de cobranza.
|
|
186
|
+
- Params: `store`*, `only_with_balance`, `customer_number`
|
|
140
187
|
|
|
141
188
|
#### get_customer_ledger
|
|
142
|
-
|
|
143
|
-
- Params: `store
|
|
189
|
+
Movimientos del libro mayor de clientes: facturas emitidas, pagos recibidos, notas de crédito.
|
|
190
|
+
- Params: `store`*, `start_date`*, `end_date`*, `customer_number`, `document_type`, `open_only`
|
|
144
191
|
|
|
145
|
-
####
|
|
146
|
-
|
|
147
|
-
- Params: `store
|
|
192
|
+
#### get_customer_list
|
|
193
|
+
Lista de clientes con número, nombre y RIF (taxRegistrationNumber).
|
|
194
|
+
- Params: `store`*, `customer_number`
|
|
148
195
|
|
|
149
|
-
####
|
|
150
|
-
|
|
151
|
-
- Params: `store`
|
|
196
|
+
#### get_open_payables
|
|
197
|
+
Resumen de todas las cuentas por pagar abiertas.
|
|
198
|
+
- Params: `store`*, `as_of_date`, `vendor_number`, `min_amount`
|
|
199
|
+
|
|
200
|
+
#### get_open_receivables
|
|
201
|
+
Resumen rápido de todas las cuentas por cobrar abiertas.
|
|
202
|
+
- Params: `store`*, `as_of_date`, `customer_number`, `min_amount`
|
|
152
203
|
|
|
153
204
|
#### get_vendor_ledger
|
|
154
|
-
|
|
155
|
-
- Params: `store
|
|
205
|
+
Movimientos del libro mayor de proveedores: facturas recibidas, pagos realizados, notas de crédito.
|
|
206
|
+
- Params: `store`*, `start_date`*, `end_date`*, `vendor_number`, `document_type`, `open_only`
|
|
156
207
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
208
|
+
### Financial Statements & Cash Flow (3 tools)
|
|
209
|
+
|
|
210
|
+
#### get_cash_flow
|
|
211
|
+
Free Cash Flow (FCF) por método indirecto para una o más tiendas Full Queso (FQ01, FQ28, FQ88).
|
|
212
|
+
- Params: `stores`, `period`, `month`, `start_date`, `end_date`, `compare_previous`, `include_cash_position`, `render_html`, `output_path`, `open_browser`, `inline_html`
|
|
213
|
+
|
|
214
|
+
#### get_financial_statements
|
|
215
|
+
Estado financiero completo (P&L / Profit & Loss) para una o más tiendas Full Queso (FQ01, FQ28, FQ88, FQFR) en un período.
|
|
216
|
+
- Params: `stores`, `period`, `month`, `start_date`, `end_date`, `render_html`, `compare_previous`, `output_path`, `open_browser`, `inline_html`
|
|
217
|
+
|
|
218
|
+
#### get_income_statement
|
|
219
|
+
Estado de resultados (Income Statement) en el formato EXACTO del reporte Power BI "Income Statement by Month" (G/L Account Level 1) para una tienda Full Queso (FQ01, FQ28, FQ88) y un mes.
|
|
220
|
+
- Params: `store`, `stores`, `period`, `month`, `start_date`, `end_date`, `level`
|
|
221
|
+
|
|
222
|
+
### Inventario (7 tools)
|
|
223
|
+
|
|
224
|
+
#### get_inventory_by_location
|
|
225
|
+
Inventario desglosado por ubicación (Location Code) dentro de una tienda.
|
|
226
|
+
- Params: `store`*, `location_code`, `item_category`, `classification`, `as_of_date`
|
|
227
|
+
|
|
228
|
+
#### get_inventory_change
|
|
229
|
+
Cambio de inventario WoW (semana) o MoM (mes).
|
|
230
|
+
- Params: `store`*, `period`*, `periods_back`, `item_category`, `classification`
|
|
231
|
+
|
|
232
|
+
#### get_inventory_levels
|
|
233
|
+
Niveles de inventario agrupados por itemCategoryCode y/o inventoryPostingGroupCode (congelados, importado, local).
|
|
234
|
+
- Params: `store`*, `item_category`, `classification`, `as_of_date`
|
|
235
|
+
|
|
236
|
+
#### get_item_card
|
|
237
|
+
Datos maestros de ítems de inventario: unitCost (BC), calculated_unit_cost (real desde últimas entradas), inventory qty, unitPrice, categoría.
|
|
238
|
+
- Params: `store`*, `item_number`, `item_search`, `item_category`
|
|
239
|
+
|
|
240
|
+
#### get_item_cost_analysis
|
|
241
|
+
Análisis de costo de un ítem — calcula costo promedio ponderado de entradas recientes (compras, ensamblaje, ajustes positivos), compara con el unitCost actual de BC, y muestra el historial de costos por mes.
|
|
242
|
+
- Params: `store`*, `item_number`*, `months`
|
|
243
|
+
|
|
244
|
+
#### get_item_cost_trend
|
|
245
|
+
Tendencia de costo de ítems: compara weighted avg inbound cost de últimas 2 semanas vs últimas 4 semanas.
|
|
246
|
+
- Params: `store`*, `item_number`, `item_category`, `period_days`
|
|
247
|
+
|
|
248
|
+
#### get_item_ledger_entries
|
|
249
|
+
Entradas del libro de artículos (Item Ledger Entries) — historial de movimientos de inventario con costo real por entrada.
|
|
250
|
+
- Params: `store`*, `item_number`*, `entry_type`, `start_date`, `end_date`, `top`
|
|
251
|
+
|
|
252
|
+
### Draft Visibility (Multi-Payments + facturas sin postear) (4 tools)
|
|
253
|
+
|
|
254
|
+
#### get_draft_payables
|
|
255
|
+
Muestra facturas de compra abiertas clasificadas en tres niveles: totalmente pendientes (sin documento de pago), con Purch.
|
|
256
|
+
- Params: `store`*, `status_filter`, `vendor_number`, `include_lines`, `report`, `summary_only`, `save_to_file`, `excel_output`
|
|
257
|
+
|
|
258
|
+
#### get_draft_receivables
|
|
259
|
+
Muestra facturas de venta abiertas clasificadas en tres niveles: totalmente pendientes (sin documento de cobro), con Multi-Payment en borrador (Open o Transferred), y el monto neto realmente sin cubrir.
|
|
260
|
+
- Params: `store`*, `status_filter`, `customer_number`, `include_lines`, `report`, `summary_only`, `save_to_file`, `excel_output`
|
|
261
|
+
|
|
262
|
+
#### get_draft_summary
|
|
263
|
+
Resumen ejecutivo consolidado de todos los Multi-Payments en borrador (Open y Transferred) para una o todas las tiendas.
|
|
264
|
+
- Params: `store`*
|
|
265
|
+
|
|
266
|
+
#### get_unposted_invoices
|
|
267
|
+
Facturas de compra y/o venta SIN POSTEAR en Business Central (status Draft o In Review).
|
|
268
|
+
- Params: `store`*, `type`, `start_date`, `end_date`, `include_in_review`, `summary_only`
|
|
269
|
+
|
|
270
|
+
### Nómina (3 tools)
|
|
271
|
+
|
|
272
|
+
#### get_employees
|
|
273
|
+
Lista empleados de una tienda con datos de nomina: tipo, status, salarios base, bonos predeterminados, fechas.
|
|
274
|
+
- Params: `store`*, `status`, `payroll_type`, `exclude_managerial`, `employee_search`, `employee_code`
|
|
275
|
+
|
|
276
|
+
#### get_payroll_documents
|
|
277
|
+
Lista documentos de nomina (headers) con filtros por periodo, tipo, status.
|
|
278
|
+
- Params: `store`*, `period_code`, `payroll_type`, `exclude_managerial`, `status`, `start_date`, `end_date`, `include_employee_count`, `summary_only`
|
|
279
|
+
|
|
280
|
+
#### get_payroll_lines
|
|
281
|
+
Detalle de nomina por empleado para un documento.
|
|
282
|
+
- Params: `store`*, `document_no`*, `employee_code`, `employee_search`
|
|
283
|
+
|
|
284
|
+
### Reports (2 tools)
|
|
285
|
+
|
|
286
|
+
#### generate_cxp_report
|
|
287
|
+
Genera reporte Excel de Cuentas por Pagar con 3 hojas: Sin Draft, Draft No Posteado, Pago Parcial + hoja Resumen con desglose por proveedor.
|
|
288
|
+
- Params: `store`*, `output_path`
|
|
289
|
+
|
|
290
|
+
#### generate_manager_report
|
|
291
|
+
Genera el Reporte Gerente HTML completo para una tienda FQ.
|
|
292
|
+
- Params: `store`*, `date`, `output_path`, `open_browser`, `payroll_days`
|
|
293
|
+
|
|
294
|
+
### Ventas (4 tools)
|
|
295
|
+
|
|
296
|
+
#### compare_sales_by_store
|
|
297
|
+
Comparación de rendimiento de VENTAS entre las tiendas de Full Queso (FQ01 Chacao, FQ28 Marqués, FQ88 Candelaria).
|
|
298
|
+
- Params: `period`, `month`, `start_date`, `end_date`, `metrics`
|
|
299
|
+
|
|
300
|
+
#### get_item_sales_detail
|
|
301
|
+
Detalle de ventas por ítem específico (1–50 SKUs) con granularidad día/semana/mes/total y desglose por tienda.
|
|
302
|
+
- Params: `items`*, `start_date`*, `end_date`*, `stores`, `granularity`, `include_zero_days`
|
|
303
|
+
|
|
304
|
+
#### get_product_performance
|
|
305
|
+
Análisis detallado de rendimiento de productos de Full Queso.
|
|
306
|
+
- Params: `period`, `month`, `start_date`, `end_date`, `stores`, `sort_by`, `top_n`
|
|
307
|
+
|
|
308
|
+
#### get_sales_analysis
|
|
309
|
+
Análisis multidimensional de ventas de Full Queso.
|
|
310
|
+
- Params: `period`, `month`, `start_date`, `end_date`, `stores`, `dimensions`, `metrics`
|
|
311
|
+
|
|
312
|
+
_Los params marcados con `*` son requeridos. Detalle completo en `docs/tool_*.md`._
|
|
160
313
|
|
|
161
314
|
## API Integrations
|
|
162
315
|
|
package/config/bank-gl-map.json
CHANGED
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"MN0004": { "gl": "18240", "name": "BDV 7191 Bs. *", "currency": "VES", "category": "bancos_nacionales" },
|
|
50
50
|
"MN0005": { "gl": "18250", "name": "Bancrecer 2558 Bs.", "currency": "VES", "category": "bancos_nacionales" },
|
|
51
51
|
"MN0007": { "gl": "18270", "name": "BDV Pago Movil 5145", "currency": "VES", "category": "bancos_nacionales" },
|
|
52
|
+
"MN0008": { "gl": "18275", "name": "Banesco 9547 Bs.", "currency": "VES", "category": "bancos_nacionales", "_nota": "Banco nuevo agregado 2026-07-10 (Puntos 20 y 21, BANESCO_9547_BS., banco 0134). gl 18275 removido de adjustment_accounts_ves el 2026-07-10 (FP): ahora es banco real, entra normal en revaluación/reconciliación." },
|
|
52
53
|
"MN0028": { "gl": "18290", "name": "Ubii Bank", "currency": "VES", "category": "bancos_nacionales" },
|
|
53
54
|
"MN0030": { "gl": "18130", "name": "Caja tienda ventas Bs.", "currency": "VES", "category": "caja" },
|
|
54
55
|
"ME0030": { "gl": "18140", "name": "Caja tienda ventas $", "currency": "USD", "category": "caja" },
|
|
@@ -74,6 +75,7 @@
|
|
|
74
75
|
"18250": "Banco quinto Bs.",
|
|
75
76
|
"18260": "Banco sexto Bs.",
|
|
76
77
|
"18270": "Banco séptimo Bs.",
|
|
78
|
+
"18275": "Banesco 9547 Bs. (FQ88)",
|
|
77
79
|
"18280": "Banco octavo Bs.",
|
|
78
80
|
"18290": "UBII principal",
|
|
79
81
|
"18291": "UBII secundario",
|
|
@@ -90,6 +92,6 @@
|
|
|
90
92
|
"18998": "Total Bancos",
|
|
91
93
|
"18999": "Total Caja y Bancos"
|
|
92
94
|
},
|
|
93
|
-
"_adjustment_accounts_nota": "Cuentas VES de ajuste/tránsito (NO son bancos reales, no tienen statement). Se tratan como informativas en la revaluación cambiaria (fuera del total de pérdida y del asiento sugerido). FP 2026-06-05.",
|
|
94
|
-
"adjustment_accounts_ves": ["
|
|
95
|
+
"_adjustment_accounts_nota": "Cuentas VES de ajuste/tránsito (NO son bancos reales, no tienen statement). Se tratan como informativas en la revaluación cambiaria (fuera del total de pérdida y del asiento sugerido). FP 2026-06-05. 18275 removido 2026-07-10: BC lo reasignó al banco real Banesco 9547 (FQ88-MN0008).",
|
|
96
|
+
"adjustment_accounts_ves": ["18297"]
|
|
95
97
|
}
|
|
@@ -25,6 +25,35 @@ export const COGS_ACCOUNTS = {
|
|
|
25
25
|
57020: { name: 'Costos Diversos', nameEn: 'Diverse Costs' },
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
+
// ── Revenue classification for the store-payroll KPI denominator ─────────────
|
|
29
|
+
// Verified against the real CoA (tests/discover-revenue-accounts.js, May 2026):
|
|
30
|
+
// 40310 "Ventas Materia Prima otras tiendas" = intercompany de tienda (FQ01/FQ88,
|
|
31
|
+
// FQ28=0); su costo par es 50110 (see LESSONS L-011).
|
|
32
|
+
// 40180 "Ventas Eventos - NEN" + 40700 "Ventas Eventos" = event sales (solo FQ01).
|
|
33
|
+
// 40580 "Ganancia por dif. cambiario" + 40540/40550 "Descuentos/Rebajas en Compras"
|
|
34
|
+
// = ingreso NO-venta → fuera del denominador de tienda (confirmado por FP).
|
|
35
|
+
// revenue_store = revenue_total − intercompany − events − non_sales.
|
|
36
|
+
export const INTERCOMPANY_REVENUE_ACCOUNTS = ['40310'];
|
|
37
|
+
export const EVENT_REVENUE_ACCOUNTS = ['40180', '40700'];
|
|
38
|
+
export const NON_SALES_REVENUE_ACCOUNTS = ['40580', '40540', '40550'];
|
|
39
|
+
|
|
40
|
+
// FQFR (master de franquicia): su intercompañía usa cuentas propias (royalties/fees),
|
|
41
|
+
// NO 40310, y no tiene ventas de evento de tienda. El KPI de nómina/ventas NO aplica a
|
|
42
|
+
// FQFR (ver KPI_PAYROLL_STORES) — esta clasificación es solo para que sus buckets de
|
|
43
|
+
// venta salgan correctos si alguien consulta ventas de FQFR.
|
|
44
|
+
export const FQFR_INTERCOMPANY_REVENUE_ACCOUNTS = ['40420', '40430', '40660', '40930'];
|
|
45
|
+
|
|
46
|
+
// Tiendas a las que aplica el KPI nómina/ventas (FQFR excluido por completo).
|
|
47
|
+
export const KPI_PAYROLL_STORES = ['FQ01', 'FQ28', 'FQ88'];
|
|
48
|
+
|
|
49
|
+
// Cuentas de clasificación de ingreso por tienda (interco/eventos/no-venta).
|
|
50
|
+
export function getRevenueClassification(storeCode) {
|
|
51
|
+
if (storeCode === 'FQFR') {
|
|
52
|
+
return { intercompany: FQFR_INTERCOMPANY_REVENUE_ACCOUNTS, events: [], nonSales: NON_SALES_REVENUE_ACCOUNTS };
|
|
53
|
+
}
|
|
54
|
+
return { intercompany: INTERCOMPANY_REVENUE_ACCOUNTS, events: EVENT_REVENUE_ACCOUNTS, nonSales: NON_SALES_REVENUE_ACCOUNTS };
|
|
55
|
+
}
|
|
56
|
+
|
|
28
57
|
// Income group labels for breakdown (by 100-prefix: 40110 → 40100)
|
|
29
58
|
export const INCOME_GROUP_LABELS = {
|
|
30
59
|
40100: '40100_ingreso_ventas',
|
package/lib/bc-client.js
CHANGED
|
@@ -18,6 +18,12 @@ export class BCClient {
|
|
|
18
18
|
this._inflightRequests = new Map();
|
|
19
19
|
this.STORE_DATA_TTL = 5 * 60 * 1000; // 5 minutes
|
|
20
20
|
this.EXCHANGE_RATE_TTL = 15 * 60 * 1000; // 15 minutes
|
|
21
|
+
|
|
22
|
+
// Per-request hard timeout (ms). Without this a single hung fetch() never
|
|
23
|
+
// rejects → the whole tool call (and, via Promise.all consolidations, the
|
|
24
|
+
// MCP process) hangs forever. Generous default (all observed BC calls are
|
|
25
|
+
// <1s); tunable via env for unusually heavy OData reports. See L-015.
|
|
26
|
+
this.FETCH_TIMEOUT_MS = Number(process.env.BC_FETCH_TIMEOUT_MS) || 90000;
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
/**
|
|
@@ -135,8 +141,12 @@ export class BCClient {
|
|
|
135
141
|
|
|
136
142
|
async fetchWithRetry(url, options, retries = 3) {
|
|
137
143
|
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
144
|
+
// Abort a hung request so it fails fast (and retries) instead of blocking
|
|
145
|
+
// forever. Each attempt gets its own controller/timer. See L-015.
|
|
146
|
+
const controller = new AbortController();
|
|
147
|
+
const timer = setTimeout(() => controller.abort(), this.FETCH_TIMEOUT_MS);
|
|
138
148
|
try {
|
|
139
|
-
const response = await fetch(url, options);
|
|
149
|
+
const response = await fetch(url, { ...options, signal: controller.signal });
|
|
140
150
|
if (response.status === 429 && attempt < retries) {
|
|
141
151
|
const retryAfter = response.headers.get('Retry-After');
|
|
142
152
|
const delay = retryAfter ? parseInt(retryAfter) * 1000 : attempt * 3000;
|
|
@@ -146,10 +156,20 @@ export class BCClient {
|
|
|
146
156
|
}
|
|
147
157
|
return response;
|
|
148
158
|
} catch (err) {
|
|
149
|
-
|
|
159
|
+
const timedOut = err.name === 'AbortError';
|
|
160
|
+
if (attempt === retries) {
|
|
161
|
+
throw timedOut
|
|
162
|
+
? new Error(`BC request timed out after ${this.FETCH_TIMEOUT_MS}ms (${retries} attempts): ${url}`)
|
|
163
|
+
: err;
|
|
164
|
+
}
|
|
150
165
|
const delay = attempt * 2000;
|
|
151
|
-
logger.warn(
|
|
166
|
+
logger.warn(
|
|
167
|
+
`Fetch ${timedOut ? `timed out (>${this.FETCH_TIMEOUT_MS}ms)` : 'failed'} ` +
|
|
168
|
+
`(attempt ${attempt}/${retries}), retrying in ${delay}ms...`
|
|
169
|
+
);
|
|
152
170
|
await new Promise((r) => setTimeout(r, delay));
|
|
171
|
+
} finally {
|
|
172
|
+
clearTimeout(timer);
|
|
153
173
|
}
|
|
154
174
|
}
|
|
155
175
|
}
|
|
@@ -316,6 +336,20 @@ export class BCClient {
|
|
|
316
336
|
return this.apiCallAllPages(url);
|
|
317
337
|
}
|
|
318
338
|
|
|
339
|
+
// Nombres del Chart of Accounts (entidad `accounts` de BC api/v2.0) → { number: displayName }.
|
|
340
|
+
// Cache por companyId (el CoA cambia rara vez). Usado por get_income_statement (level 2)
|
|
341
|
+
// para etiquetar las cuentas de posteo hijas con su nombre real de BC.
|
|
342
|
+
async getChartOfAccountNames(companyId) {
|
|
343
|
+
if (!this._coaNameCache) this._coaNameCache = {};
|
|
344
|
+
if (this._coaNameCache[companyId]) return this._coaNameCache[companyId];
|
|
345
|
+
const url = this.buildApiUrl(companyId, 'accounts', { $select: 'number,displayName' });
|
|
346
|
+
const rows = await this.apiCallAllPages(url);
|
|
347
|
+
const map = {};
|
|
348
|
+
for (const a of rows) if (a && a.number) map[String(a.number)] = a.displayName || '';
|
|
349
|
+
this._coaNameCache[companyId] = map;
|
|
350
|
+
return map;
|
|
351
|
+
}
|
|
352
|
+
|
|
319
353
|
// Saldos acumulados (a fecha) de cuentas con AMBAS monedas: debit/creditAmount = USD (LCY)
|
|
320
354
|
// y additionalCurrency*Amount = VES (moneda adicional). Para revaluación de caja VES.
|
|
321
355
|
// postingDate le asOfDate (acumulado desde el inicio = saldo a la fecha).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fullqueso/mcp-bc-gastos",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.33.0",
|
|
4
4
|
"description": "MCP server for Business Central operational expense analysis, bank reconciliation, POS reconciliation, accounts receivable/payable, multi-payment draft visibility, payroll, inventory cost analysis, and manager reports - Full Queso franchise stores",
|
|
5
5
|
"main": "server.js",
|
|
6
6
|
"bin": {
|