@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.
- package/.env.example +1 -0
- package/CHANGELOG.md +31 -0
- package/README.md +3 -1
- package/config/bank-gl-map.json +12 -10
- package/config/company-config.js +8 -1
- package/lib/bc-client.js +32 -0
- package/package.json +1 -1
- package/tools/account-transactions.js +1 -1
- package/tools/anomaly-detection.js +1 -1
- package/tools/auditoria/bank-ledger-entries.js +1 -1
- package/tools/auditoria/bank-reconciliation-report.js +1 -1
- package/tools/auditoria/find-potential-matches.js +1 -1
- package/tools/auditoria/gl-account-entries.js +1 -1
- package/tools/auditoria/list-bank-accounts.js +1 -1
- package/tools/auditoria/pm-receipts.js +1 -1
- package/tools/auditoria/reconcile-pos-sales.js +1 -1
- package/tools/auditoria/reconciliation-status.js +1 -1
- package/tools/auditoria/suggest-journal-entries.js +1 -1
- package/tools/auditoria/unmatched-ledger-entries.js +1 -1
- package/tools/auditoria/unmatched-statement-lines.js +1 -1
- package/tools/cierre-mensual/generate-closing-journal.js +1 -1
- package/tools/cierre-mensual/get-match-results.js +1 -1
- package/tools/cierre-mensual/get-questionnaire.js +1 -1
- package/tools/cierre-mensual/reconcile-with-bc.js +1 -1
- package/tools/cierre-mensual/start-month-closing.js +1 -1
- package/tools/cierre-mensual/submit-answers.js +1 -1
- package/tools/cobranzas/collection-status.js +1 -1
- package/tools/cobranzas/customer-balances.js +1 -1
- package/tools/cobranzas/customer-ledger.js +1 -1
- package/tools/cobranzas/customer-list.js +1 -1
- package/tools/cobranzas/open-payables.js +1 -1
- package/tools/cobranzas/open-receivables.js +1 -1
- package/tools/cobranzas/vendor-ledger.js +1 -1
- package/tools/efficiency-ratios.js +1 -1
- package/tools/expense-analysis.js +1 -1
- package/tools/expense-details.js +1 -1
- package/tools/financials/cash-flow-html.js +41 -13
- package/tools/financials/cash-flow.js +24 -112
- package/tools/financials/fx-cash.js +227 -0
- package/tools/financials/index.js +3 -3
- package/tools/get-exchange-rate.js +1 -1
- package/tools/inventario/inventory-by-location.js +1 -1
- package/tools/inventario/inventory-change.js +1 -1
- package/tools/inventario/inventory-levels.js +1 -1
- package/tools/inventario/item-card.js +1 -1
- package/tools/inventario/item-cost-trend.js +1 -1
- package/tools/inventario/item-ledger-entries.js +1 -1
- package/tools/inventario/item-value-entries.js +1 -1
- package/tools/list-vendors.js +1 -1
- package/tools/multi-payment/draft-payables.js +1 -1
- package/tools/multi-payment/draft-receivables.js +1 -1
- package/tools/multi-payment/draft-summary.js +1 -1
- package/tools/payroll/employees.js +1 -1
- package/tools/payroll/payroll-documents.js +1 -1
- package/tools/payroll/payroll-lines.js +1 -1
- package/tools/reports/manager-report.js +1 -1
- package/tools/trends.js +1 -1
- package/tools/vendor-transactions.js +1 -1
- package/tools/ventas/item-sales-detail.js +1 -1
- package/tools/ventas/product-performance.js +1 -1
- package/tools/ventas/sales-analysis.js +1 -1
package/.env.example
CHANGED
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
|
package/config/bank-gl-map.json
CHANGED
|
@@ -41,18 +41,18 @@
|
|
|
41
41
|
},
|
|
42
42
|
"FQ88": {
|
|
43
43
|
"store_name": "FQ88 - La Candelaria",
|
|
44
|
-
"_nota": "GL
|
|
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":
|
|
47
|
-
"MN0002": { "gl":
|
|
48
|
-
"MN0003": { "gl":
|
|
49
|
-
"MN0004": { "gl":
|
|
50
|
-
"MN0005": { "gl":
|
|
51
|
-
"MN0007": { "gl":
|
|
52
|
-
"MN0028": { "gl":
|
|
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":
|
|
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
|
}
|
package/config/company-config.js
CHANGED
|
@@ -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.
|
|
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": {
|
|
@@ -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
|
},
|
|
@@ -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',
|
|
@@ -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
|
},
|
package/tools/expense-details.js
CHANGED
|
@@ -271,24 +271,52 @@ function renderCapexCard(s) {
|
|
|
271
271
|
</div>`;
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
-
// Posición de caja
|
|
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.
|
|
278
|
-
const
|
|
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
|
|
281
|
-
|
|
282
|
-
|
|
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">💱</span><span class="card-title">Posición de Caja · Efecto Cambiario (VES)</span></div>
|
|
309
|
+
<div class="card-hd"><span class="card-icon">💱</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
|
|
287
|
-
<div class="pnl-row"><span class="pnl-label">
|
|
288
|
-
<div class="pnl-row gross"><span class="pnl-label">=
|
|
289
|
-
<div class="pnl-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
}
|