@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 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.14.2",
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": {