@fullqueso/mcp-bc-gastos 1.14.2 → 1.19.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/CHANGELOG.md +137 -0
- package/config/bank-gl-map.json +93 -0
- package/lib/bc-client.js +143 -0
- package/package.json +1 -1
- package/scripts/generate-pending-invoices-xlsx.py +378 -0
- package/scripts/generate-pending-sales-invoices-xlsx.py +372 -0
- package/scripts/report-pending-invoices.js +250 -0
- package/scripts/report-pending-sales-invoices.js +253 -0
- package/server.js +61 -0
- package/tools/auditoria/bank-ledger-entries.js +104 -0
- package/tools/auditoria/gl-account-entries.js +75 -0
- package/tools/auditoria/pm-receipts.js +182 -0
- package/tools/auditoria/reconcile-pos-sales.js +39 -16
- package/tools/cobranzas/customer-list.js +63 -0
- package/tools/inventario/index.js +4 -0
- package/tools/inventario/inventory-by-location.js +212 -0
- package/tools/inventario/inventory-change.js +386 -0
- package/tools/inventario/inventory-levels.js +214 -0
- package/tools/inventario/item-card.js +1 -50
- package/tools/inventario/item-cost-trend.js +185 -0
- package/tools/inventario/shared/cost-calculator.js +64 -0
- package/tools/ventas/index.js +3 -0
- package/tools/ventas/product-performance.js +182 -0
- package/tools/ventas/sales-analysis.js +211 -0
- package/tools/ventas/sales-store-comparison.js +192 -0
- package/utils/sales-aggregation.js +82 -0
- package/utils/sales-insights.js +167 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,142 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## [1.19.0] - 2026-04-13
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **`get_customer_list` tool (cobranzas)** — Lists customers with number, name, and RIF (taxRegistrationNumber) per store. Supports optional filter by customer number.
|
|
7
|
+
- **`config/bank-gl-map.json`** — Reference config documenting the full bank account suffix → GL account mapping per store (FQ01, FQ28, FQ88), including GL structure for Caja y Bancos (181xx–189xx).
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- **POS reconciliation: per-store bank GL mapping** — `BANK_GL_MAP` in `reconcile-pos-sales.js` now keyed by store (FQ01, FQ28) instead of flat suffix lookup. Adds cash register accounts (MN0030, ME0030) and full FQ28 bank set. `getBankGLAccount()` now accepts store parameter for correct GL resolution.
|
|
11
|
+
- Tool count: 44 → 45
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## [1.18.2] - 2026-04-13
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- **Inventory tools: exclude FQ-\* sale/combo categories** — `get_inventory_levels`, `get_inventory_change`, and `get_inventory_by_location` now filter out items with `itemCategoryCode` starting with `FQ-`, `FQ_`, or equal to `FQVENTA`. These are sales/combo products, not real inventory, and their BC `unitCost` can be contaminated (zero-inventory edge cases), producing inflated inventory valuations. Real inventory items (TERMINADO, INSUMO, etc.) keep BC's FIFO-based `unitCost` which is accurate.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- **Extracted `getCalculatedCosts` to shared module** — Moved from `item-card.js` to `tools/inventario/shared/cost-calculator.js` along with `isInventoryCategory()`. Avoids duplication across inventory tools.
|
|
22
|
+
- **Updated `calculated_unit_cost_method.md`** — Documented category filtering logic and clarified that calculated cost is for FQ-* items only; real inventory uses BC FIFO cost.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## [1.18.1] - 2026-04-13
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- `get_inventory_by_location` — Added `as_of_date` parameter for historical inventory reconstruction by location. Filters OData V4 item ledger entries with `Posting_Date <= as_of_date`. Also added `isInventoryCategory` filter to exclude FQ-* sale/combo items (aligns with `get_inventory_levels` behavior).
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## [1.18.0] - 2026-03-19
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
**Inventario domain — 2 new tools (inventory levels & change tracking):**
|
|
38
|
+
- `get_inventory_levels` — Inventory snapshot grouped by itemCategoryCode and inventoryPostingGroupCode (congelados/imported/local). Supports historical reconstruction via `as_of_date` (e.g., 1st of month post-EOM adjustment). Returns classification matrix with qty, cost value, and top items per group.
|
|
39
|
+
- `get_inventory_change` — WoW and MoM inventory change analysis. Reconstructs opening/closing inventory per period walking backwards from current balance. Movement breakdown by type (purchases, sales, adjustments, assembly). Trend summary per category and classification.
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
- Tool count: 42 → 44
|
|
43
|
+
- Inventario domain: 4 → 6 tools
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## [1.17.0] - 2026-03-18
|
|
48
|
+
|
|
49
|
+
### Highlights
|
|
50
|
+
- **Ventas domain consolidated** from `mcp-bc-analisis-ventas` — 3 new sales analysis tools
|
|
51
|
+
- Full Queso now has one unified BC MCP with **42 tools** across 8 domains
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
**New domain: Ventas (tools/ventas/) — 3 tools:**
|
|
56
|
+
- `get_sales_analysis` — Multi-dimensional sales analysis by product, category, customer, store, time period. Revenue from GL entries (USD), trend comparison vs previous period, actionable insights
|
|
57
|
+
- `get_product_performance` — Top/bottom product performers with growth trends, per-store breakdown, margin analysis, and promotion opportunities
|
|
58
|
+
- `compare_sales_by_store` — Sales comparison across FQ01/FQ28/FQ88: revenue, transactions, avg ticket, top products, daily patterns, cross-store insights
|
|
59
|
+
|
|
60
|
+
**bc-client.js — Sales API methods:**
|
|
61
|
+
- `getSalesInvoicesExpanded()` — Sales invoices with `$expand=salesInvoiceLines`
|
|
62
|
+
- `getSalesInvoicesWithLines()` — Flattened invoices + enriched lines
|
|
63
|
+
- `getItemsCatalog()` — Items master data for product enrichment
|
|
64
|
+
- `getSalesStoreData()` — Per-store sales orchestration (invoices + items + GL revenue + exchange rate enrichment)
|
|
65
|
+
- `getAllSalesStoreData()` — Multi-store sequential fetch with shared exchange rates
|
|
66
|
+
|
|
67
|
+
**Utilities:**
|
|
68
|
+
- `utils/sales-aggregation.js` — Sales-specific aggregation: by field, by date, summary calculation, growth, top/bottom N
|
|
69
|
+
- `utils/sales-insights.js` — Business insight generation: revenue trends, margin alerts, store gap analysis, product opportunities
|
|
70
|
+
|
|
71
|
+
### Notes
|
|
72
|
+
- `compare_sales_by_store` (sales) is separate from `compare_stores` (expenses) to avoid naming conflict
|
|
73
|
+
- Revenue uses GL entries (accounts 40000-49999) as authoritative USD source
|
|
74
|
+
- Line-level amounts used only for relative product/category breakdowns
|
|
75
|
+
- This consolidation deprecates the standalone `@fullqueso/mcp-bc-analisis-ventas` package
|
|
76
|
+
|
|
77
|
+
## [1.16.0] - 2026-03-16
|
|
78
|
+
|
|
79
|
+
### Highlights
|
|
80
|
+
- **Dynamic CLI reports** for pending invoice auditing — both Purchase and Sales
|
|
81
|
+
- Parametric scripts: any store (FQ01/FQ28/FQ88/all), any month range, auto-generated Excel
|
|
82
|
+
- 3-tab Excel output: Sin Draft, Con Draft, Pago Parcial — with subtotals by month and invoice numbers for audit
|
|
83
|
+
|
|
84
|
+
### Added
|
|
85
|
+
|
|
86
|
+
**Scripts — Posted Purchase Invoices Pendientes:**
|
|
87
|
+
- `scripts/report-pending-invoices.js` — Node.js CLI that fetches open vendor ledger entries + purchMultiPaymentHeaders/Lines, classifies invoices by payment status, and generates Excel via Python
|
|
88
|
+
- `scripts/generate-pending-invoices-xlsx.py` — Python (openpyxl) Excel generator with 3 tabs, month subtotals, color-coded tiers, and grand totals
|
|
89
|
+
|
|
90
|
+
**Scripts — Posted Sales Invoices Pendientes:**
|
|
91
|
+
- `scripts/report-pending-sales-invoices.js` — Same pattern for customer side: customerLedgerEntries + multiPaymentHeaders/Lines
|
|
92
|
+
- `scripts/generate-pending-sales-invoices-xlsx.py` — Sales-specific Excel generator (Cliente instead of Proveedor, Posted SI instead of Posted PI)
|
|
93
|
+
|
|
94
|
+
### Usage
|
|
95
|
+
```bash
|
|
96
|
+
# Purchase invoices
|
|
97
|
+
node scripts/report-pending-invoices.js --store FQ28 --from 2025-05 --to 2025-07
|
|
98
|
+
|
|
99
|
+
# Sales invoices
|
|
100
|
+
node scripts/report-pending-sales-invoices.js --store FQ01 --from 2025-12 --to 2026-01
|
|
101
|
+
|
|
102
|
+
# All stores, custom output path
|
|
103
|
+
node scripts/report-pending-invoices.js --store all --from 2026-03 --to 2026-03 --out ~/Desktop/report.xlsx
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Technical
|
|
107
|
+
- CLI args via `node:util/parseArgs`: `--store`, `--from`, `--to`, `--out`
|
|
108
|
+
- Parallel API calls: ledger + MP headers + exchange rates fetched concurrently
|
|
109
|
+
- Bulk MP line fetching with server-side $filter for ≤50 headers
|
|
110
|
+
- USD conversion via BCV exchange rate for VES payment lines
|
|
111
|
+
- JSON intermediate file (auto-cleaned) passed to Python for Excel generation
|
|
112
|
+
- Output defaults to `~/Downloads/{STORE}_{Type}_{period}_{date}.xlsx`
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## [1.15.0] - 2026-03-15
|
|
117
|
+
|
|
118
|
+
### Highlights
|
|
119
|
+
- **36 tools** across 7 domains — new `get_item_cost_trend` tool for inventory cost trending
|
|
120
|
+
- 2W vs 4W rolling average comparison to detect rising/falling ingredient costs
|
|
121
|
+
- Documented `calculated_unit_cost` methodology for cross-project use (MCP + Dashboard)
|
|
122
|
+
|
|
123
|
+
### Added
|
|
124
|
+
|
|
125
|
+
**Inventario — 1 new tool:**
|
|
126
|
+
- `get_item_cost_trend` — compares weighted avg inbound cost of last 2 weeks vs last 4 weeks. Detects if cost is trending up (>+5%), down (<-5%), or stable. Supports single item or bulk analysis by category. Includes stale cost detection (no inbound > 30 days).
|
|
127
|
+
|
|
128
|
+
**Documentation:**
|
|
129
|
+
- `docs/calculated_unit_cost_method.md` — complete methodology for calculated unit cost (Last 3 Inbound) and cost trending (2W vs 4W). Shared reference for mcp-fullqueso-bc-gastos and mcp-fullqueso-dashboard COGS table implementation.
|
|
130
|
+
|
|
131
|
+
### Technical
|
|
132
|
+
- New file: `tools/inventario/item-cost-trend.js`
|
|
133
|
+
- Configurable `period_days` parameter (default 28, recent = half)
|
|
134
|
+
- Results sorted by severity: cost spikes first, then stable items
|
|
135
|
+
- Batch API calls (5 concurrent) to avoid BC rate limiting
|
|
136
|
+
- Category filtering via cross-reference with items endpoint
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
3
140
|
## [1.14.0] - 2026-03-15
|
|
4
141
|
|
|
5
142
|
### Highlights
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0",
|
|
3
|
+
"last_updated": "2026-03-23",
|
|
4
|
+
"description": "Mapeo banco_code (BC Bank Account suffix) → cuenta GL (Chart of Accounts). Usado por POS reconciliation y journal entry suggestions. Cada tienda tiene su propio CoA con bancos distintos.",
|
|
5
|
+
"stores": {
|
|
6
|
+
"FQ01": {
|
|
7
|
+
"store_name": "FQ01 - Chacao Sambil",
|
|
8
|
+
"accounts": {
|
|
9
|
+
"MN0001": { "gl": "18210", "name": "Banesco 4421 Bs.", "currency": "VES", "category": "bancos_nacionales" },
|
|
10
|
+
"MN0002": { "gl": "18220", "name": "BDV 1925 Bs.", "currency": "VES", "category": "bancos_nacionales" },
|
|
11
|
+
"MN0003": { "gl": "18230", "name": "Bancamiga 7015 Bs.", "currency": "VES", "category": "bancos_nacionales" },
|
|
12
|
+
"MN0004": { "gl": "18240", "name": "BDV 5550 Bs.", "currency": "VES", "category": "bancos_nacionales" },
|
|
13
|
+
"MN0005": { "gl": "18250", "name": "Bancamiga 4523 Bs.", "currency": "VES", "category": "bancos_nacionales" },
|
|
14
|
+
"MN0006": { "gl": "18260", "name": "Bancrecer 2450 Bs.", "currency": "VES", "category": "bancos_nacionales" },
|
|
15
|
+
"MN0007": { "gl": "18270", "name": "Bancrecer 2467 Bs.", "currency": "VES", "category": "bancos_nacionales" },
|
|
16
|
+
"MN0008": { "gl": "18280", "name": "BDV 5187 Bs. (Pago Móvil)", "currency": "VES", "category": "bancos_nacionales" },
|
|
17
|
+
"MN0012": { "gl": "18290", "name": "UBII", "currency": "VES", "category": "bancos_nacionales" },
|
|
18
|
+
"MN0030": { "gl": "18130", "name": "Caja tienda ventas Bs.", "currency": "VES", "category": "caja" },
|
|
19
|
+
"ME0030": { "gl": "18140", "name": "Caja tienda ventas $", "currency": "USD", "category": "caja" },
|
|
20
|
+
"ME0006": { "gl": null, "name": "Zelle USD (Wells Fargo)", "currency": "USD", "category": "bancos_extranjeros", "_nota": "GL pendiente de confirmar" }
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"FQ28": {
|
|
24
|
+
"store_name": "FQ28 - Marqués El Unicentro",
|
|
25
|
+
"accounts": {
|
|
26
|
+
"MN0001": { "gl": "18210", "name": "BDV 8139 Bs.", "currency": "VES", "category": "bancos_nacionales" },
|
|
27
|
+
"MN0002": { "gl": "18220", "name": "Bancrecer 5474 Bs.", "currency": "VES", "category": "bancos_nacionales" },
|
|
28
|
+
"MN0028": { "gl": "18290", "name": "UBII 6249 Bs", "currency": "VES", "category": "bancos_nacionales" },
|
|
29
|
+
"MN0029": { "gl": "18291", "name": "UBII 6442 * Bs", "currency": "VES", "category": "bancos_nacionales" },
|
|
30
|
+
"MN0030": { "gl": "18130", "name": "Caja tienda ventas Bs.", "currency": "VES", "category": "caja" },
|
|
31
|
+
"MN0031": { "gl": "18160", "name": "Caja transitoria tienda ventas Bs.", "currency": "VES", "category": "caja" },
|
|
32
|
+
"MN0032": { "gl": "18292", "name": "UBII 4 FQ-28", "currency": "VES", "category": "bancos_nacionales" },
|
|
33
|
+
"MN0033": { "gl": "18295", "name": "UBII 7 FQ-28", "currency": "VES", "category": "bancos_nacionales" },
|
|
34
|
+
"MN0098": { "gl": "18298", "name": "Operaciones en Tránsito", "currency": "VES", "category": "bancos_nacionales" },
|
|
35
|
+
"ME0002": { "gl": "18310", "name": "Bancrecer $", "currency": "USD", "category": "bancos_nacionales_divisas" },
|
|
36
|
+
"ME0005": { "gl": "18410", "name": "Zelle Bofa $ *", "currency": "USD", "category": "bancos_extranjeros" },
|
|
37
|
+
"ME0030": { "gl": "18140", "name": "Caja tienda ventas $", "currency": "USD", "category": "caja" },
|
|
38
|
+
"ME0031": { "gl": "18170", "name": "Caja transitoria tienda ventas $", "currency": "USD", "category": "caja" },
|
|
39
|
+
"ME0035": { "gl": "18150", "name": "Banco Bóveda $", "currency": "USD", "category": "caja" }
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"FQ88": {
|
|
43
|
+
"store_name": "FQ88 - La Candelaria",
|
|
44
|
+
"_nota": "GL accounts pendientes de confirmar con CoA de FQ88",
|
|
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" },
|
|
53
|
+
"MN0030": { "gl": "18130", "name": "Caja tienda ventas Bs.", "currency": "VES", "category": "caja" },
|
|
54
|
+
"ME0030": { "gl": "18140", "name": "Caja tienda ventas $", "currency": "USD", "category": "caja" },
|
|
55
|
+
"ME0005": { "gl": null, "name": "Zelle USD", "currency": "USD", "category": "bancos_extranjeros" }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"gl_structure": {
|
|
60
|
+
"18100": "Caja (header)",
|
|
61
|
+
"18110": "Caja chica VEB",
|
|
62
|
+
"18120": "Caja chica $",
|
|
63
|
+
"18130": "Caja tienda ventas VEB",
|
|
64
|
+
"18140": "Caja tienda ventas $",
|
|
65
|
+
"18150": "Bóveda $",
|
|
66
|
+
"18160": "Caja tránsito tienda ventas VEB",
|
|
67
|
+
"18170": "Caja tránsito tienda ventas $",
|
|
68
|
+
"18199": "Total Caja",
|
|
69
|
+
"18200": "Bancos Nacionales Bolívares (header)",
|
|
70
|
+
"18210": "Banco principal Bs.",
|
|
71
|
+
"18220": "Banco secundario Bs.",
|
|
72
|
+
"18230": "Banco terciario Bs.",
|
|
73
|
+
"18240": "Banco cuarto Bs.",
|
|
74
|
+
"18250": "Banco quinto Bs.",
|
|
75
|
+
"18260": "Banco sexto Bs.",
|
|
76
|
+
"18270": "Banco séptimo Bs.",
|
|
77
|
+
"18280": "Banco octavo Bs.",
|
|
78
|
+
"18290": "UBII principal",
|
|
79
|
+
"18291": "UBII secundario",
|
|
80
|
+
"18292": "UBII terciario",
|
|
81
|
+
"18295": "UBII cuarto",
|
|
82
|
+
"18298": "Operaciones en Tránsito",
|
|
83
|
+
"18299": "Total Bancos Nacionales",
|
|
84
|
+
"18300": "Bancos Nacionales Divisas (header)",
|
|
85
|
+
"18310": "Banco divisas principal",
|
|
86
|
+
"18399": "Total Bancos Nacionales Divisas",
|
|
87
|
+
"18400": "Bancos Extranjeros Divisas (header)",
|
|
88
|
+
"18410": "Banco extranjero principal",
|
|
89
|
+
"18499": "Total Bancos Extranjeros Divisas",
|
|
90
|
+
"18998": "Total Bancos",
|
|
91
|
+
"18999": "Total Caja y Bancos"
|
|
92
|
+
}
|
|
93
|
+
}
|
package/lib/bc-client.js
CHANGED
|
@@ -530,6 +530,149 @@ export class BCClient {
|
|
|
530
530
|
return filters.top ? this.apiCall(url) : this.apiCallAllPages(url);
|
|
531
531
|
}
|
|
532
532
|
|
|
533
|
+
// ── Sales Analysis Methods ──────────────────────────────────────
|
|
534
|
+
|
|
535
|
+
async getSalesInvoicesExpanded(companyId, startDate, endDate) {
|
|
536
|
+
const url = this.buildApiUrl(companyId, 'salesInvoices', {
|
|
537
|
+
$filter: `postingDate ge ${startDate} and postingDate le ${endDate}`,
|
|
538
|
+
$orderby: 'postingDate desc',
|
|
539
|
+
$expand: 'salesInvoiceLines',
|
|
540
|
+
});
|
|
541
|
+
return this.apiCallAllPages(url);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
async getSalesInvoicesWithLines(companyId, startDate, endDate) {
|
|
545
|
+
const expandedInvoices = await this.getSalesInvoicesExpanded(companyId, startDate, endDate);
|
|
546
|
+
const invoices = [];
|
|
547
|
+
const lines = [];
|
|
548
|
+
|
|
549
|
+
for (const inv of expandedInvoices) {
|
|
550
|
+
const { salesInvoiceLines, ...invoiceData } = inv;
|
|
551
|
+
invoices.push(invoiceData);
|
|
552
|
+
|
|
553
|
+
for (const line of (salesInvoiceLines || [])) {
|
|
554
|
+
lines.push({
|
|
555
|
+
...line,
|
|
556
|
+
postingDate: inv.postingDate,
|
|
557
|
+
invoiceDate: inv.invoiceDate,
|
|
558
|
+
documentNo: inv.number,
|
|
559
|
+
customerName: inv.customerName,
|
|
560
|
+
amount: line.amountExcludingTax || line.netAmount || 0,
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
return { invoices, lines };
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
async getItemsCatalog(companyId) {
|
|
569
|
+
const url = this.buildApiUrl(companyId, 'items', {
|
|
570
|
+
$select: 'id,number,displayName,type,unitPrice,unitCost,itemCategoryCode',
|
|
571
|
+
});
|
|
572
|
+
return this.apiCallAllPages(url);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
async getSalesStoreData(storeCode, startDate, endDate, exchangeRates = null) {
|
|
576
|
+
const stores = resolveStores([storeCode]);
|
|
577
|
+
const store = stores[0];
|
|
578
|
+
|
|
579
|
+
logger.info(`Fetching sales data for ${store.name}: ${startDate} to ${endDate}`);
|
|
580
|
+
|
|
581
|
+
const salesData = await this.getSalesInvoicesWithLines(store.companyId, startDate, endDate);
|
|
582
|
+
const items = await this.getItemsCatalog(store.companyId);
|
|
583
|
+
|
|
584
|
+
// Revenue from GL (40000-49999) for authoritative USD amounts
|
|
585
|
+
const revenueEntries = await this.getRevenueEntries(store.companyId, startDate, endDate);
|
|
586
|
+
const glRevenue = { total_usd: 0, breakdown: {} };
|
|
587
|
+
for (const e of revenueEntries) {
|
|
588
|
+
const amount = (e.creditAmount || 0) - (e.debitAmount || 0);
|
|
589
|
+
glRevenue.total_usd += amount;
|
|
590
|
+
const acct = e.accountNumber;
|
|
591
|
+
glRevenue.breakdown[acct] = (glRevenue.breakdown[acct] || 0) + amount;
|
|
592
|
+
}
|
|
593
|
+
for (const acct of Object.keys(glRevenue.breakdown)) {
|
|
594
|
+
glRevenue.breakdown[acct] = Math.round(glRevenue.breakdown[acct] * 100) / 100;
|
|
595
|
+
}
|
|
596
|
+
glRevenue.total_usd = Math.round(glRevenue.total_usd * 100) / 100;
|
|
597
|
+
|
|
598
|
+
// Exchange rates
|
|
599
|
+
if (!exchangeRates) {
|
|
600
|
+
try {
|
|
601
|
+
exchangeRates = await this.getExchangeRates(storeCode);
|
|
602
|
+
} catch (err) {
|
|
603
|
+
logger.warn('Could not fetch exchange rates:', err.message);
|
|
604
|
+
exchangeRates = [];
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const periodRate = this.getExchangeRateForDate(exchangeRates, endDate);
|
|
609
|
+
|
|
610
|
+
// Build items lookup
|
|
611
|
+
const itemsMap = {};
|
|
612
|
+
for (const item of items) {
|
|
613
|
+
itemsMap[item.number] = item;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// Enrich lines with item data and converted costs
|
|
617
|
+
for (const line of salesData.lines) {
|
|
618
|
+
const itemNo = line.lineObjectNumber || line.itemId;
|
|
619
|
+
const item = itemsMap[itemNo];
|
|
620
|
+
if (item) {
|
|
621
|
+
line.itemCategoryCode = item.itemCategoryCode;
|
|
622
|
+
line.unitCostUSD = item.unitCost || 0;
|
|
623
|
+
line.displayName = item.displayName || line.description;
|
|
624
|
+
}
|
|
625
|
+
if (!line.displayName) {
|
|
626
|
+
line.displayName = line.description || 'Sin nombre';
|
|
627
|
+
}
|
|
628
|
+
line.itemNumber = line.lineObjectNumber || '';
|
|
629
|
+
line.unitCostUSD = line.unitCostUSD || 0;
|
|
630
|
+
|
|
631
|
+
if (exchangeRates.length > 0 && line.unitCostUSD) {
|
|
632
|
+
const rateForDate = this.getExchangeRateForDate(exchangeRates, line.postingDate || endDate);
|
|
633
|
+
const rate = rateForDate ? rateForDate.rate : (periodRate ? periodRate.rate : 0);
|
|
634
|
+
line.unitCostVES = line.unitCostUSD * rate;
|
|
635
|
+
line.exchangeRate = rate;
|
|
636
|
+
} else {
|
|
637
|
+
line.unitCostVES = 0;
|
|
638
|
+
line.exchangeRate = periodRate ? periodRate.rate : null;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
return {
|
|
643
|
+
invoices: salesData.invoices,
|
|
644
|
+
lines: salesData.lines,
|
|
645
|
+
items,
|
|
646
|
+
itemsMap,
|
|
647
|
+
exchangeRate: periodRate,
|
|
648
|
+
glRevenue,
|
|
649
|
+
storeName: store.name,
|
|
650
|
+
storeCode: store.code,
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
async getAllSalesStoreData(storeCodes, startDate, endDate) {
|
|
655
|
+
const stores = resolveStores(storeCodes);
|
|
656
|
+
const results = {};
|
|
657
|
+
|
|
658
|
+
// Fetch exchange rates once (shared across all companies in same tenant)
|
|
659
|
+
let exchangeRates = [];
|
|
660
|
+
try {
|
|
661
|
+
exchangeRates = await this.getExchangeRates(stores[0].code);
|
|
662
|
+
logger.info(`Exchange rates loaded: ${exchangeRates.length} entries`);
|
|
663
|
+
} catch (err) {
|
|
664
|
+
logger.warn('Could not fetch exchange rates:', err.message);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// Fetch stores sequentially to avoid BC API rate limiting
|
|
668
|
+
for (const store of stores) {
|
|
669
|
+
const data = await this.getSalesStoreData(store.code, startDate, endDate, exchangeRates);
|
|
670
|
+
results[store.code] = data;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
return results;
|
|
674
|
+
}
|
|
675
|
+
|
|
533
676
|
async getCompanies() {
|
|
534
677
|
const url = `${this.baseUrl}/${this.tenantId}/${this.environment}/api/v2.0/companies`;
|
|
535
678
|
return this.apiCall(url);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fullqueso/mcp-bc-gastos",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.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": {
|