@fullqueso/mcp-bc-gastos 1.26.0 → 1.28.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.
Files changed (61) hide show
  1. package/.env.example +1 -0
  2. package/CHANGELOG.md +31 -0
  3. package/README.md +3 -1
  4. package/config/bank-gl-map.json +12 -10
  5. package/config/company-config.js +8 -1
  6. package/lib/bc-client.js +32 -0
  7. package/package.json +1 -1
  8. package/tools/account-transactions.js +1 -1
  9. package/tools/anomaly-detection.js +1 -1
  10. package/tools/auditoria/bank-ledger-entries.js +1 -1
  11. package/tools/auditoria/bank-reconciliation-report.js +1 -1
  12. package/tools/auditoria/find-potential-matches.js +1 -1
  13. package/tools/auditoria/gl-account-entries.js +1 -1
  14. package/tools/auditoria/list-bank-accounts.js +1 -1
  15. package/tools/auditoria/pm-receipts.js +1 -1
  16. package/tools/auditoria/reconcile-pos-sales.js +1 -1
  17. package/tools/auditoria/reconciliation-status.js +1 -1
  18. package/tools/auditoria/suggest-journal-entries.js +1 -1
  19. package/tools/auditoria/unmatched-ledger-entries.js +1 -1
  20. package/tools/auditoria/unmatched-statement-lines.js +1 -1
  21. package/tools/cierre-mensual/generate-closing-journal.js +1 -1
  22. package/tools/cierre-mensual/get-match-results.js +1 -1
  23. package/tools/cierre-mensual/get-questionnaire.js +1 -1
  24. package/tools/cierre-mensual/reconcile-with-bc.js +1 -1
  25. package/tools/cierre-mensual/start-month-closing.js +1 -1
  26. package/tools/cierre-mensual/submit-answers.js +1 -1
  27. package/tools/cobranzas/collection-status.js +1 -1
  28. package/tools/cobranzas/customer-balances.js +1 -1
  29. package/tools/cobranzas/customer-ledger.js +1 -1
  30. package/tools/cobranzas/customer-list.js +1 -1
  31. package/tools/cobranzas/open-payables.js +1 -1
  32. package/tools/cobranzas/open-receivables.js +1 -1
  33. package/tools/cobranzas/vendor-ledger.js +1 -1
  34. package/tools/efficiency-ratios.js +1 -1
  35. package/tools/expense-analysis.js +1 -1
  36. package/tools/expense-details.js +1 -1
  37. package/tools/financials/cash-flow-html.js +41 -13
  38. package/tools/financials/cash-flow.js +24 -112
  39. package/tools/financials/fx-cash.js +227 -0
  40. package/tools/financials/index.js +3 -3
  41. package/tools/get-exchange-rate.js +1 -1
  42. package/tools/inventario/inventory-by-location.js +1 -1
  43. package/tools/inventario/inventory-change.js +1 -1
  44. package/tools/inventario/inventory-levels.js +1 -1
  45. package/tools/inventario/item-card.js +1 -1
  46. package/tools/inventario/item-cost-trend.js +1 -1
  47. package/tools/inventario/item-ledger-entries.js +1 -1
  48. package/tools/inventario/item-value-entries.js +1 -1
  49. package/tools/list-vendors.js +1 -1
  50. package/tools/multi-payment/draft-payables.js +1 -1
  51. package/tools/multi-payment/draft-receivables.js +1 -1
  52. package/tools/multi-payment/draft-summary.js +1 -1
  53. package/tools/payroll/employees.js +1 -1
  54. package/tools/payroll/payroll-documents.js +1 -1
  55. package/tools/payroll/payroll-lines.js +1 -1
  56. package/tools/reports/manager-report.js +1 -1
  57. package/tools/trends.js +1 -1
  58. package/tools/vendor-transactions.js +1 -1
  59. package/tools/ventas/item-sales-detail.js +1 -1
  60. package/tools/ventas/product-performance.js +1 -1
  61. package/tools/ventas/sales-analysis.js +1 -1
package/.env.example CHANGED
@@ -13,6 +13,7 @@ BC_ENVIRONMENT=production
13
13
  BC_COMPANY_FQ01=guid-for-store-fq01
14
14
  BC_COMPANY_FQ28=guid-for-store-fq28
15
15
  BC_COMPANY_FQ88=guid-for-store-fq88
16
+ BC_COMPANY_FQFR=guid-for-franquicias
16
17
 
17
18
  # Optional
18
19
  LOG_LEVEL=info
package/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## [1.28.0] — 2026-06-12
4
+
5
+ ### Added
6
+ - **Cuarta compañía: `FQFR` — Full Queso Franquicias, C.A.** El MCP solo exponía FQ01/FQ28/FQ88; ahora la compañía de Franquicias está disponible en todas las herramientas.
7
+ - **Datos BC** (descubiertos del endpoint `/companies`): `companyId` (GUID) = `30e63c27-6336-f011-9a4a-000d3afe29c3`; `name` OData V4 = `Full Queso Franquicias` (→ `companyName: 'Full%20Queso%20Franquicias'`).
8
+ - Nueva entrada `FQFR` en `config/company-config.js` (`getStores()`) y agregada a `ALL_STORE_CODES` → `"all"` ahora incluye Franquicias en consolidados/comparativas.
9
+ - Nueva env var **`BC_COMPANY_FQFR`** (opcional, como FQ28/FQ88) en `.env`, `.env.example` y README. La validación de `server.js` no cambia (solo FQ01 es obligatoria).
10
+ - **52 herramientas** tenían el `enum` de tiendas hardcodeado (`['FQ01','FQ28','FQ88'(,'all')]`) — esto rechazaba `FQFR` en la validación del schema MCP. Se agregó `'FQFR'` a los 52 enums (39 sin `'all'`, 13 con `'all'`).
11
+ - **Verificado contra BC real:** token OK; API v2.0 (GUID) → 200; OData V4 (nombre) → 200; `getGLEntries` ingresos 2025 de FQFR → 116 asientos; `resolveStores(['all'])` → `FQ01,FQ28,FQ88,FQFR`. `node --check` OK en todos los .js.
12
+
13
+ ### Nota operativa
14
+ - **Reiniciar Claude Desktop** tras el publish para que los tools recarguen con FQFR (G-002).
15
+
16
+ ---
17
+
18
+ ## [1.27.0] — 2026-06-05
19
+
20
+ ### Changed
21
+ - **`get_cash_flow` — efecto cambiario revaluado contra el SALDO REAL del banco + asiento sugerido (corrige v1.26.0).** FP detectó que el saldo VES del GL está inflado por **backlog de conciliación** (transacciones que el banco ya procesó pero BC no postea). Ej FQ88: GL cuenta 18250 = 12.44M VES vs saldo real del banco = 1.19M VES. Por eso el `fx_adjustment` de v1.26.0 (sobre GL, −$2,290) **sobrestimaba** la pérdida real. Ahora se revalúa el **saldo real** (`BankAccReconciliation.StatementEndingBalance`, en VES) y se aísla el backlog.
22
+ - **Matemática robusta:** por cuenta, `implied_rate = gl_ves/gl_usd` (tasa promedio histórica), `fx_real = stmt_ves × (1/rate_close − 1/implied_rate)` — escala por el saldo **real**, no el GL inflado. `recon_gap = gl_ves − stmt_ves` se reporta aparte (backlog a postear, NO es FX).
23
+ - **3 buckets de cuentas VES:** (1) banco real con statement → entra al total y al **asiento sugerido**; (2) banco sin statement → provisional, flag, fuera del total; (3) ajuste/tránsito (lista explícita en config: 18275, 18297) → solo informativo. Flag de tasa implícita atípica.
24
+ - **Asiento sugerido de diferencial cambiario** (READ-ONLY texto, cuadra DR=CR): débito Diferencial Cambiario / crédito cada banco VES por su `fx_real`. Es la hoja de trabajo del cierre.
25
+ - Nuevo módulo compartido `tools/financials/fx-cash.js` (computeCashPosition, buildFxJournal, consolidateCashPositions) — reutilizable por get_cash_flow y, a futuro, por el paso de cierre-mensual (Fase 3B). Nuevo `bcClient.getBankStatementBalances` (OData V4 `BankAccReconciliation`, elige la statement ≤ fin de período). Completado el mapeo banco→GL de FQ88 en `config/bank-gl-map.json` (desde fq-239-audit) + lista `adjustment_accounts_ves`.
26
+ - El bloque `cash_position` cambia de forma: `bank_accounts`/`pending_no_statement`/`informational` + `totals` (solo bucket 1) + `fx_journal` + `gl_basis` (referencia v1.26.0). Insight separa "brecha de conciliación" de "efecto cambiario sobre caja real". **El FCF sigue intacto** (el FX es memo de balance).
27
+ - **Verificado contra BC real (FQ88, mayo 2026):** GL libro $34,880 / FX sobre GL −$2,290 (v1.26.0, sobrestimado) → **caja real $5,884 / FX real −$500.89** (correcto); brecha de conciliación 14.36M VES (~$25,897, backlog). Asiento cuadra ($500.89). FCF intacto.
28
+
29
+ ### Pendiente (Fase 3B)
30
+ - `generate_fx_revaluation` en cierre-mensual: emitir el asiento de diferencial cambiario integrado al journal de cierre (reusa el módulo `fx-cash.js`).
31
+
32
+ ---
33
+
3
34
  ## [1.26.0] — 2026-06-05
4
35
 
5
36
  ### Added
package/README.md CHANGED
@@ -33,7 +33,8 @@ Add to your `claude_desktop_config.json`:
33
33
  "BC_ENVIRONMENT": "production",
34
34
  "BC_COMPANY_FQ01": "company-guid-fq01",
35
35
  "BC_COMPANY_FQ28": "company-guid-fq28",
36
- "BC_COMPANY_FQ88": "company-guid-fq88"
36
+ "BC_COMPANY_FQ88": "company-guid-fq88",
37
+ "BC_COMPANY_FQFR": "company-guid-franquicias"
37
38
  }
38
39
  }
39
40
  }
@@ -181,6 +182,7 @@ All APIs share the same OAuth 2.0 credentials (Azure AD client credentials flow)
181
182
  | `BC_COMPANY_FQ01` | Yes | Company GUID for store FQ01 |
182
183
  | `BC_COMPANY_FQ28` | No | Company GUID for store FQ28 |
183
184
  | `BC_COMPANY_FQ88` | No | Company GUID for store FQ88 |
185
+ | `BC_COMPANY_FQFR` | No | Company GUID for Franquicias (FQFR) |
184
186
  | `LOG_LEVEL` | No | `debug`, `info`, `warn`, `error` (default: `info`) |
185
187
 
186
188
  ## Chart of Accounts
@@ -41,18 +41,18 @@
41
41
  },
42
42
  "FQ88": {
43
43
  "store_name": "FQ88 - La Candelaria",
44
- "_nota": "GL accounts pendientes de confirmar con CoA de FQ88",
44
+ "_nota": "GL confirmados 2026-06-05 desde fq-239-audit/data/fq_bancos_all.json (consolidacion_por_banco).",
45
45
  "accounts": {
46
- "MN0001": { "gl": null, "name": "Bancamiga 0240 Bs.", "currency": "VES", "category": "bancos_nacionales" },
47
- "MN0002": { "gl": null, "name": "Bancamiga 9379 Bs. *", "currency": "VES", "category": "bancos_nacionales" },
48
- "MN0003": { "gl": null, "name": "BDV 9127 Bs.", "currency": "VES", "category": "bancos_nacionales" },
49
- "MN0004": { "gl": null, "name": "BDV 7191 Bs. *", "currency": "VES", "category": "bancos_nacionales" },
50
- "MN0005": { "gl": null, "name": "Bancrecer 2558 Bs.", "currency": "VES", "category": "bancos_nacionales" },
51
- "MN0007": { "gl": null, "name": "BDV Pago Movil 5145", "currency": "VES", "category": "bancos_nacionales" },
52
- "MN0028": { "gl": null, "name": "Ubii Bank", "currency": "VES", "category": "bancos_nacionales" },
46
+ "MN0001": { "gl": "18210", "name": "Bancamiga 0240 Bs.", "currency": "VES", "category": "bancos_nacionales" },
47
+ "MN0002": { "gl": "18220", "name": "Bancamiga 9379 Bs. *", "currency": "VES", "category": "bancos_nacionales" },
48
+ "MN0003": { "gl": "18230", "name": "BDV 9127 Bs.", "currency": "VES", "category": "bancos_nacionales" },
49
+ "MN0004": { "gl": "18240", "name": "BDV 7191 Bs. *", "currency": "VES", "category": "bancos_nacionales" },
50
+ "MN0005": { "gl": "18250", "name": "Bancrecer 2558 Bs.", "currency": "VES", "category": "bancos_nacionales" },
51
+ "MN0007": { "gl": "18270", "name": "BDV Pago Movil 5145", "currency": "VES", "category": "bancos_nacionales" },
52
+ "MN0028": { "gl": "18290", "name": "Ubii Bank", "currency": "VES", "category": "bancos_nacionales" },
53
53
  "MN0030": { "gl": "18130", "name": "Caja tienda ventas Bs.", "currency": "VES", "category": "caja" },
54
54
  "ME0030": { "gl": "18140", "name": "Caja tienda ventas $", "currency": "USD", "category": "caja" },
55
- "ME0005": { "gl": null, "name": "Zelle USD", "currency": "USD", "category": "bancos_extranjeros" }
55
+ "ME0005": { "gl": "18410", "name": "Zelle USD", "currency": "USD", "category": "bancos_extranjeros" }
56
56
  }
57
57
  }
58
58
  },
@@ -89,5 +89,7 @@
89
89
  "18499": "Total Bancos Extranjeros Divisas",
90
90
  "18998": "Total Bancos",
91
91
  "18999": "Total Caja y Bancos"
92
- }
92
+ },
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": ["18275", "18297"]
93
95
  }
@@ -24,10 +24,17 @@ function getStores() {
24
24
  shortName: 'Candelaria',
25
25
  location: 'La Candelaria, Caracas',
26
26
  },
27
+ FQFR: {
28
+ companyId: process.env.BC_COMPANY_FQFR,
29
+ companyName: 'Full%20Queso%20Franquicias',
30
+ name: 'FQFR Franquicias',
31
+ shortName: 'Franquicias',
32
+ location: 'Franquicias',
33
+ },
27
34
  };
28
35
  }
29
36
 
30
- export const ALL_STORE_CODES = ['FQ01', 'FQ28', 'FQ88'];
37
+ export const ALL_STORE_CODES = ['FQ01', 'FQ28', 'FQ88', 'FQFR'];
31
38
 
32
39
  export function resolveStores(storeCodes) {
33
40
  const stores = getStores();
package/lib/bc-client.js CHANGED
@@ -329,6 +329,38 @@ export class BCClient {
329
329
  return this.apiCallAllPages(url, maxPages);
330
330
  }
331
331
 
332
+ // Saldos REALES de banco (statement) por cuenta, vía BankAccReconciliation (OData V4).
333
+ // Por cada BankAccountNo elige la statement más reciente con StatementDate ≤ asOfDate
334
+ // (los bancos tienen varias statements semanales + cierre de mes). Devuelve un mapa
335
+ // { 'FQ88-MN0005': { ending: 1190029.52, statementDate, statementNo, stale } }.
336
+ async getBankStatementBalances(storeCode, asOfDate) {
337
+ const url = this.buildODataUrl(storeCode, 'BankAccReconciliation', {
338
+ $select: 'BankAccountNo,StatementNo,StatementDate,StatementEndingBalance',
339
+ });
340
+ const rows = await this.odataCallAllPages(url);
341
+ const list = rows.value || rows || [];
342
+ const ad = String(asOfDate);
343
+ const byAcct = {};
344
+ for (const r of list) {
345
+ const acct = r.BankAccountNo;
346
+ if (!acct) continue;
347
+ const date = r.StatementDate ? String(r.StatementDate) : '';
348
+ // Preferir statements ≤ asOfDate (la más reciente). Si no hay, usar la más antigua futura (stale).
349
+ const cur = byAcct[acct];
350
+ const within = date && date <= ad;
351
+ if (!cur) {
352
+ byAcct[acct] = { ending: r.StatementEndingBalance || 0, statementDate: date, statementNo: r.StatementNo, stale: !within };
353
+ } else if (within) {
354
+ // entre las ≤ asOfDate, quedarse con la fecha mayor
355
+ if (cur.stale || date > cur.statementDate) byAcct[acct] = { ending: r.StatementEndingBalance || 0, statementDate: date, statementNo: r.StatementNo, stale: false };
356
+ } else if (cur.stale && date < cur.statementDate) {
357
+ // todas futuras: quedarse con la más cercana (menor fecha futura)
358
+ byAcct[acct] = { ending: r.StatementEndingBalance || 0, statementDate: date, statementNo: r.StatementNo, stale: true };
359
+ }
360
+ }
361
+ return byAcct;
362
+ }
363
+
332
364
  // Fetch revenue entries (40000-49999) - credit balance = income
333
365
  async getRevenueEntries(companyId, startDate, endDate) {
334
366
  return this.getGLEntries(companyId, startDate, endDate, '40000', '49999');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fullqueso/mcp-bc-gastos",
3
- "version": "1.26.0",
3
+ "version": "1.28.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": {
@@ -15,7 +15,7 @@ export const accountTransactionsTool = {
15
15
  },
16
16
  store: {
17
17
  type: 'string',
18
- enum: ['FQ01', 'FQ28', 'FQ88'],
18
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
19
19
  description: 'Tienda a consultar.',
20
20
  },
21
21
  start_date: {
@@ -28,7 +28,7 @@ export const anomalyDetectionTool = {
28
28
  },
29
29
  stores: {
30
30
  type: 'array',
31
- items: { type: 'string', enum: ['FQ01', 'FQ28', 'FQ88', 'all'] },
31
+ items: { type: 'string', enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR', 'all'] },
32
32
  description: 'Tiendas a analizar.',
33
33
  default: ['all'],
34
34
  },
@@ -9,7 +9,7 @@ export const bankLedgerEntriesTool = {
9
9
  properties: {
10
10
  store: {
11
11
  type: 'string',
12
- enum: ['FQ01', 'FQ28', 'FQ88'],
12
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
13
13
  description: 'Tienda a consultar.',
14
14
  },
15
15
  bank_account: {
@@ -12,7 +12,7 @@ export const bankReconciliationReportTool = {
12
12
  properties: {
13
13
  store: {
14
14
  type: 'string',
15
- enum: ['FQ01', 'FQ28', 'FQ88'],
15
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
16
16
  description: 'Tienda a consultar.',
17
17
  },
18
18
  bank_account: {
@@ -10,7 +10,7 @@ export const findPotentialMatchesTool = {
10
10
  properties: {
11
11
  store: {
12
12
  type: 'string',
13
- enum: ['FQ01', 'FQ28', 'FQ88'],
13
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
14
14
  description: 'Tienda a consultar.',
15
15
  },
16
16
  bank_account: {
@@ -9,7 +9,7 @@ export const glAccountEntriesTool = {
9
9
  properties: {
10
10
  store: {
11
11
  type: 'string',
12
- enum: ['FQ01', 'FQ28', 'FQ88'],
12
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
13
13
  description: 'Tienda a consultar.',
14
14
  },
15
15
  gl_account: {
@@ -9,7 +9,7 @@ export const listBankAccountsTool = {
9
9
  properties: {
10
10
  store: {
11
11
  type: 'string',
12
- enum: ['FQ01', 'FQ28', 'FQ88'],
12
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
13
13
  description: 'Tienda a consultar.',
14
14
  },
15
15
  },
@@ -9,7 +9,7 @@ export const pmReceiptsTool = {
9
9
  properties: {
10
10
  store: {
11
11
  type: 'string',
12
- enum: ['FQ01', 'FQ28', 'FQ88'],
12
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
13
13
  description: 'Tienda a consultar.',
14
14
  },
15
15
  bank_account: {
@@ -414,7 +414,7 @@ export const reconcilePOSSalesTool = {
414
414
  properties: {
415
415
  store: {
416
416
  type: 'string',
417
- enum: ['FQ01', 'FQ28', 'FQ88'],
417
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
418
418
  description: 'Tienda a conciliar.',
419
419
  },
420
420
  start_date: {
@@ -10,7 +10,7 @@ export const reconciliationStatusTool = {
10
10
  properties: {
11
11
  store: {
12
12
  type: 'string',
13
- enum: ['FQ01', 'FQ28', 'FQ88'],
13
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
14
14
  description: 'Tienda a consultar.',
15
15
  },
16
16
  bank_account: {
@@ -12,7 +12,7 @@ export const suggestJournalEntriesTool = {
12
12
  properties: {
13
13
  store: {
14
14
  type: 'string',
15
- enum: ['FQ01', 'FQ28', 'FQ88'],
15
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
16
16
  description: 'Tienda a consultar.',
17
17
  },
18
18
  bank_account: {
@@ -10,7 +10,7 @@ export const unmatchedLedgerEntriesTool = {
10
10
  properties: {
11
11
  store: {
12
12
  type: 'string',
13
- enum: ['FQ01', 'FQ28', 'FQ88'],
13
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
14
14
  description: 'Tienda a consultar.',
15
15
  },
16
16
  bank_account: {
@@ -10,7 +10,7 @@ export const unmatchedStatementLinesTool = {
10
10
  properties: {
11
11
  store: {
12
12
  type: 'string',
13
- enum: ['FQ01', 'FQ28', 'FQ88'],
13
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
14
14
  description: 'Tienda a consultar.',
15
15
  },
16
16
  bank_account: {
@@ -19,7 +19,7 @@ export const generateClosingJournalTool = {
19
19
  inputSchema: {
20
20
  type: 'object',
21
21
  properties: {
22
- store: { type: 'string', enum: ['FQ01', 'FQ28', 'FQ88'] },
22
+ store: { type: 'string', enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'] },
23
23
  month: { type: 'string', description: 'YYYY-MM' },
24
24
  output_dir: { type: 'string', description: 'Directorio de salida (default: /tmp).' },
25
25
  allow_partial: {
@@ -23,7 +23,7 @@ export const getClosingMatchResultsTool = {
23
23
  inputSchema: {
24
24
  type: 'object',
25
25
  properties: {
26
- store: { type: 'string', enum: ['FQ01', 'FQ28', 'FQ88'] },
26
+ store: { type: 'string', enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'] },
27
27
  month: { type: 'string', description: 'YYYY-MM' },
28
28
  list: {
29
29
  type: 'string',
@@ -13,7 +13,7 @@ export const getClosingQuestionnaireTool = {
13
13
  inputSchema: {
14
14
  type: 'object',
15
15
  properties: {
16
- store: { type: 'string', enum: ['FQ01', 'FQ28', 'FQ88'] },
16
+ store: { type: 'string', enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'] },
17
17
  month: { type: 'string', description: 'YYYY-MM' },
18
18
  bucket: {
19
19
  type: 'string',
@@ -15,7 +15,7 @@ export const reconcileWithBcTool = {
15
15
  inputSchema: {
16
16
  type: 'object',
17
17
  properties: {
18
- store: { type: 'string', enum: ['FQ01', 'FQ28', 'FQ88'] },
18
+ store: { type: 'string', enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'] },
19
19
  month: { type: 'string', description: 'YYYY-MM' },
20
20
  },
21
21
  required: ['store', 'month'],
@@ -19,7 +19,7 @@ export const startMonthClosingTool = {
19
19
  inputSchema: {
20
20
  type: 'object',
21
21
  properties: {
22
- store: { type: 'string', enum: ['FQ01', 'FQ28', 'FQ88'] },
22
+ store: { type: 'string', enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'] },
23
23
  month: {
24
24
  type: 'string',
25
25
  description: 'Mes YYYY-MM (opcional). Si no se especifica, usa el mes más antiguo con statements abiertos.',
@@ -10,7 +10,7 @@ export const submitClosingAnswersTool = {
10
10
  inputSchema: {
11
11
  type: 'object',
12
12
  properties: {
13
- store: { type: 'string', enum: ['FQ01', 'FQ28', 'FQ88'] },
13
+ store: { type: 'string', enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'] },
14
14
  month: { type: 'string', description: 'YYYY-MM' },
15
15
  user: {
16
16
  type: 'string',
@@ -9,7 +9,7 @@ export const collectionStatusTool = {
9
9
  properties: {
10
10
  store: {
11
11
  type: 'string',
12
- enum: ['FQ01', 'FQ28', 'FQ88'],
12
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
13
13
  description: 'Tienda a consultar.',
14
14
  },
15
15
  start_date: {
@@ -9,7 +9,7 @@ export const customerBalancesTool = {
9
9
  properties: {
10
10
  store: {
11
11
  type: 'string',
12
- enum: ['FQ01', 'FQ28', 'FQ88'],
12
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
13
13
  description: 'Tienda a consultar.',
14
14
  },
15
15
  only_with_balance: {
@@ -9,7 +9,7 @@ export const customerLedgerTool = {
9
9
  properties: {
10
10
  store: {
11
11
  type: 'string',
12
- enum: ['FQ01', 'FQ28', 'FQ88'],
12
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
13
13
  description: 'Tienda a consultar.',
14
14
  },
15
15
  start_date: {
@@ -9,7 +9,7 @@ export const customerListTool = {
9
9
  properties: {
10
10
  store: {
11
11
  type: 'string',
12
- enum: ['FQ01', 'FQ28', 'FQ88'],
12
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
13
13
  description: 'Tienda a consultar.',
14
14
  },
15
15
  customer_number: {
@@ -9,7 +9,7 @@ export const openPayablesTool = {
9
9
  properties: {
10
10
  store: {
11
11
  type: 'string',
12
- enum: ['FQ01', 'FQ28', 'FQ88'],
12
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
13
13
  description: 'Tienda a consultar.',
14
14
  },
15
15
  as_of_date: {
@@ -9,7 +9,7 @@ export const openReceivablesTool = {
9
9
  properties: {
10
10
  store: {
11
11
  type: 'string',
12
- enum: ['FQ01', 'FQ28', 'FQ88'],
12
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
13
13
  description: 'Tienda a consultar.',
14
14
  },
15
15
  as_of_date: {
@@ -9,7 +9,7 @@ export const vendorLedgerTool = {
9
9
  properties: {
10
10
  store: {
11
11
  type: 'string',
12
- enum: ['FQ01', 'FQ28', 'FQ88'],
12
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
13
13
  description: 'Tienda a consultar.',
14
14
  },
15
15
  start_date: {
@@ -28,7 +28,7 @@ export const efficiencyRatiosTool = {
28
28
  },
29
29
  stores: {
30
30
  type: 'array',
31
- items: { type: 'string', enum: ['FQ01', 'FQ28', 'FQ88', 'all'] },
31
+ items: { type: 'string', enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR', 'all'] },
32
32
  description: 'Tiendas a analizar.',
33
33
  default: ['all'],
34
34
  },
@@ -29,7 +29,7 @@ export const expenseAnalysisTool = {
29
29
  },
30
30
  stores: {
31
31
  type: 'array',
32
- items: { type: 'string', enum: ['FQ01', 'FQ28', 'FQ88', 'all'] },
32
+ items: { type: 'string', enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR', 'all'] },
33
33
  description: 'Tiendas a analizar. Use ["all"] para todas.',
34
34
  default: ['all'],
35
35
  },
@@ -12,7 +12,7 @@ export const expenseDetailsTool = {
12
12
  properties: {
13
13
  store: {
14
14
  type: 'string',
15
- enum: ['FQ01', 'FQ28', 'FQ88'],
15
+ enum: ['FQ01', 'FQ28', 'FQ88', 'FQFR'],
16
16
  description: 'Tienda a consultar.',
17
17
  },
18
18
  period: {
@@ -271,24 +271,52 @@ function renderCapexCard(s) {
271
271
  </div>`;
272
272
  }
273
273
 
274
- // Posición de caja + efecto cambiario sobre la caja VES (no afecta el FCF).
274
+ // Posición de caja · conciliación · efecto cambiario (base saldo REAL del banco). NO afecta el FCF.
275
275
  function renderCashPositionCard(s) {
276
276
  const cp = s.cash_position;
277
- if (!cp || !cp.ves || !cp.ves.accounts.length) return '';
278
- const adj = cp.ves.fx_adjustment;
277
+ if (!cp || !cp.bank_accounts) return '';
278
+ const t = cp.totals;
279
279
  const dev = cp.month_devaluation_pct != null ? ` · devaluación mes ${cp.month_devaluation_pct}%` : '';
280
- const acctRows = cp.ves.accounts
281
- .map((a) => `<div class="pnl-row sub"><span class="pnl-label">${esc(a.account)} ${esc(a.name || '')}${a.store ? ` <span class="pnl-acct">${esc(a.store)}</span>` : ''}</span><span class="pnl-amt ${a.fx_adjustment < 0 ? 'val-red' : ''}">${signed(a.fx_adjustment)}</span></div>`)
282
- .join('');
280
+ const st = (store) => (store ? ` <span class="pnl-acct">${esc(store)}</span>` : '');
281
+
282
+ // Bucket 1 — bancos con statement: GL vs real, brecha, tasa implícita, fx_real
283
+ const bankRows = cp.bank_accounts.map((b) => {
284
+ const flag = b.flags && b.flags.length ? ` <span class="pnl-acct">⚠ ${esc(b.flags.join(','))}</span>` : '';
285
+ return `<div class="pnl-row sub">
286
+ <span class="pnl-label">${esc(b.account)} ${esc(b.name || '')}${st(b.store)}${flag}<br>
287
+ <span class="kpi-cell-sub">GL ${fmt(b.gl_ves)} → real ${fmt(b.stmt_ves)} VES · impl ${b.implied_rate || '—'} vs ${cp.rate_end}</span></span>
288
+ <span class="pnl-amt ${b.fx_real < 0 ? 'val-red' : 'val-green'}">${signed(b.fx_real)}</span>
289
+ </div>`;
290
+ }).join('');
291
+
292
+ // Bucket 3 — ajuste/tránsito (informativo)
293
+ const infoRows = (cp.informational || []).map((b) =>
294
+ `<div class="pnl-row sub"><span class="pnl-label">${esc(b.account)} ${esc(b.name || '')}${st(b.store)} <span class="pnl-acct">informativo</span></span><span class="pnl-amt">${signed(b.fx_theoretical)}</span></div>`
295
+ ).join('');
296
+ // Bucket 2 — sin statement (provisional)
297
+ const pendRows = (cp.pending_no_statement || []).map((b) =>
298
+ `<div class="pnl-row sub"><span class="pnl-label">${esc(b.account)} ${esc(b.name || '')}${st(b.store)} <span class="pnl-acct">sin statement</span></span><span class="pnl-amt">${signed(b.fx_provisional)}</span></div>`
299
+ ).join('');
300
+
301
+ // Asiento sugerido (solo bucket 1)
302
+ const j = cp.fx_journal;
303
+ const jRows = (j && j.rows || []).map((r) =>
304
+ `<div class="pnl-row sub"><span class="pnl-label">${esc(String(r.account))} <span class="pnl-acct">${esc(r.account_type)}</span></span><span class="pnl-amt">${r.debit_usd ? 'DR ' + signed(r.debit_usd) : 'CR ' + signed(r.credit_usd)}</span></div>`
305
+ ).join('');
306
+
307
+ const reconUsd = cp.rate_end ? t.recon_gap_ves / cp.rate_end : 0;
283
308
  return `<div class="card">
284
- <div class="card-hd"><span class="card-icon">&#x1F4B1;</span><span class="card-title">Posición de Caja · Efecto Cambiario (VES)</span></div>
309
+ <div class="card-hd"><span class="card-icon">&#x1F4B1;</span><span class="card-title">Posición de Caja · Conciliación · Efecto Cambiario (VES)</span></div>
285
310
  <div class="card-bd"><div class="pnl">
286
- <div class="pnl-row"><span class="pnl-label">Caja VES libro (USD)</span><span class="pnl-amt">${signed(cp.ves.book_usd)}</span></div>
287
- <div class="pnl-row"><span class="pnl-label">Caja VES real (a ${cp.rate_end} VES/USD)</span><span class="pnl-amt">${signed(cp.ves.real_usd)}</span></div>
288
- <div class="pnl-row gross"><span class="pnl-label">= Ajuste cambiario no contabilizado${dev}</span><span class="pnl-amt ${adj < 0 ? 'val-red' : 'val-green'}">${signed(adj)}</span></div>
289
- <div class="pnl-section-hd">Por cuenta VES</div>
290
- ${acctRows}
291
- <div class="pnl-row total"><span class="pnl-label">Caja total — libro ${signed(cp.total_cash_book_usd)} → real</span><span class="pnl-amt">${signed(cp.total_cash_real_usd)}</span></div>
311
+ <div class="pnl-row"><span class="pnl-label">Caja VES real (statement, a ${cp.rate_end} VES/USD)</span><span class="pnl-amt">${signed(t.ves_real_usd)}</span></div>
312
+ <div class="pnl-row"><span class="pnl-label">Brecha de conciliación (backlog a postear, NO es FX)</span><span class="pnl-amt">${fmt(t.recon_gap_ves)} VES (~${signed(reconUsd)})</span></div>
313
+ <div class="pnl-row gross"><span class="pnl-label">= Efecto cambiario sobre caja real${dev}</span><span class="pnl-amt ${t.fx_real < 0 ? 'val-red' : 'val-green'}">${signed(t.fx_real)}</span></div>
314
+ <div class="pnl-row sub"><span class="pnl-label">(ref. libro GL: ${signed(cp.gl_basis.ves_book_usd)} · FX s/GL ${signed(cp.gl_basis.fx_on_gl)})</span><span class="pnl-amt"></span></div>
315
+ <div class="pnl-section-hd">Bancos VES (saldo real vs libro)</div>
316
+ ${bankRows}
317
+ ${pendRows ? `<div class="pnl-section-hd">Sin statement (provisional)</div>${pendRows}` : ''}
318
+ ${infoRows ? `<div class="pnl-section-hd">Ajustes / Tránsito (informativo, fuera del total)</div>${infoRows}` : ''}
319
+ ${jRows ? `<div class="pnl-section-hd">Asiento sugerido — Diferencial Cambiario ${j.balanced ? '✓ cuadra' : '✗'}</div>${jRows}` : ''}
292
320
  </div></div>
293
321
  </div>`;
294
322
  }