@fullqueso/mcp-bc-gastos 1.1.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 +18 -0
- package/CHANGELOG.md +49 -0
- package/LICENSE +21 -0
- package/README.md +118 -0
- package/config/benchmarks.js +135 -0
- package/config/company-config.js +39 -0
- package/config/expense-accounts.js +199 -0
- package/config/income-accounts.js +40 -0
- package/lib/anomaly-detector.js +223 -0
- package/lib/bc-client.js +361 -0
- package/lib/expense-analyzer.js +144 -0
- package/lib/formatter.js +77 -0
- package/lib/ratio-calculator.js +114 -0
- package/lib/trend-analyzer.js +195 -0
- package/package.json +59 -0
- package/server.js +142 -0
- package/tools/account-transactions.js +125 -0
- package/tools/anomaly-detection.js +105 -0
- package/tools/efficiency-ratios.js +81 -0
- package/tools/expense-analysis.js +88 -0
- package/tools/expense-details.js +179 -0
- package/tools/store-comparison.js +179 -0
- package/tools/trends.js +84 -0
- package/utils/currency-converter.js +30 -0
- package/utils/date-helper.js +107 -0
- package/utils/logger.js +18 -0
package/.env.example
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Business Central OAuth 2.0
|
|
2
|
+
BC_TENANT_ID=your-azure-tenant-id
|
|
3
|
+
BC_CLIENT_ID=your-azure-app-client-id
|
|
4
|
+
BC_CLIENT_SECRET=your-azure-app-client-secret
|
|
5
|
+
BC_TOKEN_URL=https://login.microsoftonline.com/YOUR_TENANT_ID/oauth2/v2.0/token
|
|
6
|
+
BC_SCOPE=https://api.businesscentral.dynamics.com/.default
|
|
7
|
+
|
|
8
|
+
# Business Central API
|
|
9
|
+
BC_API_BASE=https://api.businesscentral.dynamics.com/v2.0
|
|
10
|
+
BC_ENVIRONMENT=production
|
|
11
|
+
|
|
12
|
+
# Company GUIDs (from Business Central > Companies)
|
|
13
|
+
BC_COMPANY_FQ01=guid-for-store-fq01
|
|
14
|
+
BC_COMPANY_FQ28=guid-for-store-fq28
|
|
15
|
+
BC_COMPANY_FQ88=guid-for-store-fq88
|
|
16
|
+
|
|
17
|
+
# Optional
|
|
18
|
+
LOG_LEVEL=info
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# CHANGELOG
|
|
2
|
+
|
|
3
|
+
## [1.1.0] - 2026-02-19
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Herramienta `get_expense_details` - Drill-down transaccional de gastos con filtros por categoría, cuenta, monto mínimo y límite configurable
|
|
7
|
+
- Herramienta `get_account_transactions` - Listado completo de transacciones por cuenta contable con running balance
|
|
8
|
+
- Vendor lookup automático en ambas herramientas nuevas: cada transacción incluye `vendor_name` y `vendor_number` vía cruce con Vendor Ledger Entries
|
|
9
|
+
- Métodos `getVendorLedgerEntries()`, `getVendors()` y `buildVendorMap()` en bc-client.js
|
|
10
|
+
- Método `getDetailedGLEntries()` con filtros flexibles (cuenta específica, rango, fechas, top)
|
|
11
|
+
|
|
12
|
+
### Technical
|
|
13
|
+
- `buildVendorMap()` es resiliente: retorna `{}` si los endpoints de vendor no están disponibles (try/catch con logger.warn)
|
|
14
|
+
- GL entries y vendor map se fetchean en paralelo con `Promise.all` en ambas herramientas
|
|
15
|
+
- Tests 6 y 7 agregados a test-tools.js (total: 7 tools testeadas)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## [1.0.0] - 2026-02-18
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- Herramienta `get_expense_analysis` - Análisis detallado de gastos por categoría
|
|
23
|
+
- Herramienta `get_efficiency_ratios` - Cálculo de ratios financieros
|
|
24
|
+
- Herramienta `compare_stores` - Comparación entre tiendas
|
|
25
|
+
- Herramienta `detect_anomalies` - Detección automática de anomalías
|
|
26
|
+
- Herramienta `get_trends` - Análisis de tendencias históricas
|
|
27
|
+
- Integración con Business Central API vía OAuth 2.0
|
|
28
|
+
- Mapeo completo del Chart of Accounts (60000-99999, 50000-59999)
|
|
29
|
+
- Conversión automática VES/USD
|
|
30
|
+
- Benchmarking contra rangos esperados
|
|
31
|
+
- Insights accionables automáticos
|
|
32
|
+
|
|
33
|
+
### Technical
|
|
34
|
+
- Cliente Business Central con OAuth 2.0
|
|
35
|
+
- ExpenseAnalyzer para análisis de gastos
|
|
36
|
+
- RatioCalculator para métricas financieras
|
|
37
|
+
- AnomalyDetector para alertas
|
|
38
|
+
- TrendAnalyzer para análisis histórico
|
|
39
|
+
- Formatter para outputs bien formateados
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## [0.1.0] - 2026-02-18
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
- Proyecto inicializado
|
|
47
|
+
- Documentación completa (CLAUDE.md, TODO.md, PLAN_SUMMARY.md)
|
|
48
|
+
- Estructura de proyecto definida
|
|
49
|
+
- Chart of Accounts mapeado
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Francisco Padilla / Full Queso
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# @fullqueso/mcp-bc-gastos
|
|
2
|
+
|
|
3
|
+
MCP server for analyzing operational expenses and financial ratios from Microsoft Business Central. Built for the Full Queso franchise (3 stores: FQ01 Chacao, FQ28 Marques, FQ88 Candelaria).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Expense Analysis** - Detailed breakdown by 10 categories with account-level detail and benchmarks
|
|
8
|
+
- **Efficiency Ratios** - Expense-to-income, payroll, rent, utilities, marketing, operating margin
|
|
9
|
+
- **Store Comparison** - Rankings, variances, and savings opportunities across stores
|
|
10
|
+
- **Anomaly Detection** - Automatic alerts by severity with root-cause analysis
|
|
11
|
+
- **Trend Analysis** - 6-month historical trends with growth rates and seasonality
|
|
12
|
+
- **Expense Details** - Transaction-level drill-down with vendor information
|
|
13
|
+
- **Account Transactions** - Per-account ledger with running balance and vendor lookup
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
### Claude Desktop
|
|
18
|
+
|
|
19
|
+
Add to your `claude_desktop_config.json`:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"mcpServers": {
|
|
24
|
+
"fullqueso-bc-gastos": {
|
|
25
|
+
"command": "npx",
|
|
26
|
+
"args": ["-y", "@fullqueso/mcp-bc-gastos"],
|
|
27
|
+
"env": {
|
|
28
|
+
"BC_TENANT_ID": "your-azure-tenant-id",
|
|
29
|
+
"BC_CLIENT_ID": "your-azure-app-client-id",
|
|
30
|
+
"BC_CLIENT_SECRET": "your-azure-app-client-secret",
|
|
31
|
+
"BC_TOKEN_URL": "https://login.microsoftonline.com/YOUR_TENANT/oauth2/v2.0/token",
|
|
32
|
+
"BC_SCOPE": "https://api.businesscentral.dynamics.com/.default",
|
|
33
|
+
"BC_API_BASE": "https://api.businesscentral.dynamics.com/v2.0",
|
|
34
|
+
"BC_ENVIRONMENT": "production",
|
|
35
|
+
"BC_COMPANY_FQ01": "company-guid-fq01",
|
|
36
|
+
"BC_COMPANY_FQ28": "company-guid-fq28",
|
|
37
|
+
"BC_COMPANY_FQ88": "company-guid-fq88"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Local Development
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
git clone https://github.com/Fullqueso/fullqueso-mcp-bc-gastos.git
|
|
48
|
+
cd fullqueso-mcp-bc-gastos
|
|
49
|
+
npm install
|
|
50
|
+
cp .env.example .env
|
|
51
|
+
# Edit .env with your credentials
|
|
52
|
+
npm start
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Tools
|
|
56
|
+
|
|
57
|
+
### get_expense_analysis
|
|
58
|
+
Detailed expense analysis by category with benchmark comparisons.
|
|
59
|
+
- Parameters: `stores`, `period`, `month`, `start_date`, `end_date`
|
|
60
|
+
|
|
61
|
+
### get_efficiency_ratios
|
|
62
|
+
Financial ratios: expense-to-income, payroll, rent, utilities, marketing, operating margin.
|
|
63
|
+
- Parameters: `stores`, `period`, `month`, `start_date`, `end_date`
|
|
64
|
+
|
|
65
|
+
### compare_stores
|
|
66
|
+
Compare all stores with efficiency rankings and savings opportunities.
|
|
67
|
+
- Parameters: `period`, `month`, `start_date`, `end_date`
|
|
68
|
+
|
|
69
|
+
### detect_anomalies
|
|
70
|
+
Detect expense anomalies with severity levels and recommended actions.
|
|
71
|
+
- Parameters: `stores`, `period`, `month`, `start_date`, `end_date`, `sensitivity`
|
|
72
|
+
|
|
73
|
+
### get_trends
|
|
74
|
+
Historical trend analysis (up to 6 months) with growth rates and ASCII charts.
|
|
75
|
+
- Parameters: `store`, `months`
|
|
76
|
+
|
|
77
|
+
### get_expense_details
|
|
78
|
+
Transaction-level drill-down with vendor lookup and filters.
|
|
79
|
+
- Parameters: `store`, `period`, `month`, `start_date`, `end_date`, `category`, `account_number`, `min_amount`, `limit`
|
|
80
|
+
|
|
81
|
+
### get_account_transactions
|
|
82
|
+
Per-account ledger view with running balance and vendor information.
|
|
83
|
+
- Parameters: `account_number`, `store`, `start_date`, `end_date`
|
|
84
|
+
|
|
85
|
+
## Environment Variables
|
|
86
|
+
|
|
87
|
+
| Variable | Required | Description |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| `BC_TENANT_ID` | Yes | Azure AD tenant ID |
|
|
90
|
+
| `BC_CLIENT_ID` | Yes | Azure AD app client ID |
|
|
91
|
+
| `BC_CLIENT_SECRET` | Yes | Azure AD app client secret |
|
|
92
|
+
| `BC_TOKEN_URL` | Yes | OAuth 2.0 token endpoint |
|
|
93
|
+
| `BC_SCOPE` | Yes | BC API scope |
|
|
94
|
+
| `BC_API_BASE` | Yes | BC API base URL |
|
|
95
|
+
| `BC_ENVIRONMENT` | Yes | BC environment (e.g., `production`) |
|
|
96
|
+
| `BC_COMPANY_FQ01` | Yes | Company GUID for store FQ01 |
|
|
97
|
+
| `BC_COMPANY_FQ28` | No | Company GUID for store FQ28 |
|
|
98
|
+
| `BC_COMPANY_FQ88` | No | Company GUID for store FQ88 |
|
|
99
|
+
| `LOG_LEVEL` | No | Log level: `debug`, `info`, `warn`, `error` (default: `info`) |
|
|
100
|
+
|
|
101
|
+
## Chart of Accounts
|
|
102
|
+
|
|
103
|
+
10 expense categories mapped to account ranges 60000-99999:
|
|
104
|
+
|
|
105
|
+
1. **Planta Fisica** (60000-60999) - Rent, utilities
|
|
106
|
+
2. **Alquiler Equipos** (61000-61999) - Equipment rental
|
|
107
|
+
3. **Logistica** (62000-62999) - Vehicles, delivery
|
|
108
|
+
4. **Marketing** (63000-63999) - Advertising, commissions
|
|
109
|
+
5. **Administrativos** (64000-64999) - Office, software
|
|
110
|
+
6. **Seguros** (65000-65999) - Insurance
|
|
111
|
+
7. **Bancarios** (67000-67999) - Banking fees, interest
|
|
112
|
+
8. **Servicios Contratados** (68000-68999) - Contracted services
|
|
113
|
+
9. **Nomina** (70000-74999) - Payroll, benefits
|
|
114
|
+
10. **Otros** (80000-99999) - Depreciation, other
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
MIT
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// Industry Benchmarks for Quick-Service Restaurant / Deli
|
|
2
|
+
// Ranges and optimal percentages (as % of total income)
|
|
3
|
+
|
|
4
|
+
export const BENCHMARKS = {
|
|
5
|
+
planta_fisica: {
|
|
6
|
+
name: 'Planta Física (Alquiler + Servicios)',
|
|
7
|
+
min: 7,
|
|
8
|
+
max: 12,
|
|
9
|
+
optimal: 8,
|
|
10
|
+
alert_high: 15,
|
|
11
|
+
unit: '% de ingresos',
|
|
12
|
+
},
|
|
13
|
+
alquiler_equipos: {
|
|
14
|
+
name: 'Alquiler de Equipos',
|
|
15
|
+
min: 1,
|
|
16
|
+
max: 3,
|
|
17
|
+
optimal: 2,
|
|
18
|
+
alert_high: 5,
|
|
19
|
+
unit: '% de ingresos',
|
|
20
|
+
},
|
|
21
|
+
logistica: {
|
|
22
|
+
name: 'Logística',
|
|
23
|
+
min: 2,
|
|
24
|
+
max: 5,
|
|
25
|
+
optimal: 3,
|
|
26
|
+
alert_high: 7,
|
|
27
|
+
unit: '% de ingresos',
|
|
28
|
+
},
|
|
29
|
+
marketing: {
|
|
30
|
+
name: 'Marketing',
|
|
31
|
+
min: 2,
|
|
32
|
+
max: 5,
|
|
33
|
+
optimal: 3,
|
|
34
|
+
alert_high: 8,
|
|
35
|
+
unit: '% de ingresos',
|
|
36
|
+
},
|
|
37
|
+
administrativos: {
|
|
38
|
+
name: 'Administrativos',
|
|
39
|
+
min: 1,
|
|
40
|
+
max: 3,
|
|
41
|
+
optimal: 2,
|
|
42
|
+
alert_high: 5,
|
|
43
|
+
unit: '% de ingresos',
|
|
44
|
+
},
|
|
45
|
+
seguros: {
|
|
46
|
+
name: 'Seguros',
|
|
47
|
+
min: 0.5,
|
|
48
|
+
max: 2,
|
|
49
|
+
optimal: 1,
|
|
50
|
+
alert_high: 3,
|
|
51
|
+
unit: '% de ingresos',
|
|
52
|
+
},
|
|
53
|
+
bancarios: {
|
|
54
|
+
name: 'Bancarios',
|
|
55
|
+
min: 1,
|
|
56
|
+
max: 3,
|
|
57
|
+
optimal: 2,
|
|
58
|
+
alert_high: 5,
|
|
59
|
+
unit: '% de ingresos',
|
|
60
|
+
},
|
|
61
|
+
servicios_contratados: {
|
|
62
|
+
name: 'Servicios Contratados',
|
|
63
|
+
min: 1,
|
|
64
|
+
max: 3,
|
|
65
|
+
optimal: 2,
|
|
66
|
+
alert_high: 5,
|
|
67
|
+
unit: '% de ingresos',
|
|
68
|
+
},
|
|
69
|
+
nomina: {
|
|
70
|
+
name: 'Nómina',
|
|
71
|
+
min: 25,
|
|
72
|
+
max: 35,
|
|
73
|
+
optimal: 28,
|
|
74
|
+
alert_high: 40,
|
|
75
|
+
unit: '% de ingresos',
|
|
76
|
+
},
|
|
77
|
+
otros: {
|
|
78
|
+
name: 'Otros Gastos',
|
|
79
|
+
min: 1,
|
|
80
|
+
max: 5,
|
|
81
|
+
optimal: 2,
|
|
82
|
+
alert_high: 8,
|
|
83
|
+
unit: '% de ingresos',
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// Operating Margin Benchmarks
|
|
88
|
+
export const MARGIN_BENCHMARKS = {
|
|
89
|
+
operating_margin: {
|
|
90
|
+
name: 'Margen Operativo',
|
|
91
|
+
min: 40,
|
|
92
|
+
max: 60,
|
|
93
|
+
optimal: 55,
|
|
94
|
+
alert_low: 35,
|
|
95
|
+
unit: '%',
|
|
96
|
+
},
|
|
97
|
+
expense_to_income: {
|
|
98
|
+
name: 'Gastos / Ingresos',
|
|
99
|
+
min: 40,
|
|
100
|
+
max: 60,
|
|
101
|
+
optimal: 45,
|
|
102
|
+
alert_high: 65,
|
|
103
|
+
unit: '%',
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export function evaluateBenchmark(categoryKey, percentOfIncome) {
|
|
108
|
+
const benchmark = BENCHMARKS[categoryKey];
|
|
109
|
+
if (!benchmark) return { status: 'unknown', message: 'Sin benchmark disponible' };
|
|
110
|
+
|
|
111
|
+
if (percentOfIncome <= benchmark.optimal) {
|
|
112
|
+
return { status: 'optimal', message: `Óptimo (≤${benchmark.optimal}%)`, icon: '✅' };
|
|
113
|
+
}
|
|
114
|
+
if (percentOfIncome <= benchmark.max) {
|
|
115
|
+
return { status: 'normal', message: `Normal (${benchmark.min}-${benchmark.max}%)`, icon: '✓' };
|
|
116
|
+
}
|
|
117
|
+
if (percentOfIncome <= benchmark.alert_high) {
|
|
118
|
+
return { status: 'warning', message: `Elevado (>${benchmark.max}%)`, icon: '⚠️' };
|
|
119
|
+
}
|
|
120
|
+
return { status: 'critical', message: `Crítico (>${benchmark.alert_high}%)`, icon: '🚨' };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function evaluateMargin(marginPct) {
|
|
124
|
+
const b = MARGIN_BENCHMARKS.operating_margin;
|
|
125
|
+
if (marginPct >= b.optimal) {
|
|
126
|
+
return { status: 'optimal', message: `Excelente (≥${b.optimal}%)`, icon: '✅' };
|
|
127
|
+
}
|
|
128
|
+
if (marginPct >= b.min) {
|
|
129
|
+
return { status: 'normal', message: `Saludable (${b.min}-${b.max}%)`, icon: '✓' };
|
|
130
|
+
}
|
|
131
|
+
if (marginPct >= b.alert_low) {
|
|
132
|
+
return { status: 'warning', message: `Bajo (<${b.min}%)`, icon: '⚠️' };
|
|
133
|
+
}
|
|
134
|
+
return { status: 'critical', message: `Crítico (<${b.alert_low}%)`, icon: '🚨' };
|
|
135
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Company Configuration - Full Queso Stores
|
|
2
|
+
// Uses lazy evaluation to ensure env vars are available after dotenv.config()
|
|
3
|
+
|
|
4
|
+
function getStores() {
|
|
5
|
+
return {
|
|
6
|
+
FQ01: {
|
|
7
|
+
companyId: process.env.BC_COMPANY_FQ01,
|
|
8
|
+
name: 'FQ01 Chacao',
|
|
9
|
+
shortName: 'Chacao',
|
|
10
|
+
location: 'Chacao, Caracas',
|
|
11
|
+
},
|
|
12
|
+
FQ28: {
|
|
13
|
+
companyId: process.env.BC_COMPANY_FQ28,
|
|
14
|
+
name: 'FQ28 Marqués',
|
|
15
|
+
shortName: 'Marqués',
|
|
16
|
+
location: 'El Marqués, Caracas',
|
|
17
|
+
},
|
|
18
|
+
FQ88: {
|
|
19
|
+
companyId: process.env.BC_COMPANY_FQ88,
|
|
20
|
+
name: 'FQ88 Candelaria',
|
|
21
|
+
shortName: 'Candelaria',
|
|
22
|
+
location: 'La Candelaria, Caracas',
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const ALL_STORE_CODES = ['FQ01', 'FQ28', 'FQ88'];
|
|
28
|
+
|
|
29
|
+
export function resolveStores(storeCodes) {
|
|
30
|
+
const stores = getStores();
|
|
31
|
+
if (!storeCodes || storeCodes.length === 0 || storeCodes.includes('all')) {
|
|
32
|
+
return Object.entries(stores).map(([code, info]) => ({ code, ...info }));
|
|
33
|
+
}
|
|
34
|
+
return storeCodes.map((code) => {
|
|
35
|
+
const info = stores[code];
|
|
36
|
+
if (!info) throw new Error(`Tienda desconocida: ${code}. Opciones: ${ALL_STORE_CODES.join(', ')}`);
|
|
37
|
+
return { code, ...info };
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// Expense Accounts - Chart of Accounts (60000-99999)
|
|
2
|
+
// 10 Categorías de Gastos Operacionales
|
|
3
|
+
|
|
4
|
+
export const EXPENSE_CATEGORIES = {
|
|
5
|
+
planta_fisica: {
|
|
6
|
+
name: 'Planta Física',
|
|
7
|
+
nameEn: 'Physical Plant',
|
|
8
|
+
icon: '🏢',
|
|
9
|
+
accountRange: { min: 60000, max: 60999 },
|
|
10
|
+
accounts: {
|
|
11
|
+
60100: { name: 'Alquiler Local', nameEn: 'Store Rent' },
|
|
12
|
+
60200: { name: 'Electricidad', nameEn: 'Electricity' },
|
|
13
|
+
60300: { name: 'Agua', nameEn: 'Water' },
|
|
14
|
+
60400: { name: 'Gas', nameEn: 'Gas' },
|
|
15
|
+
60500: { name: 'Internet/Telefonía', nameEn: 'Internet/Phone' },
|
|
16
|
+
60600: { name: 'Mantenimiento Local', nameEn: 'Store Maintenance' },
|
|
17
|
+
60700: { name: 'Limpieza', nameEn: 'Cleaning' },
|
|
18
|
+
60800: { name: 'Seguridad Física', nameEn: 'Physical Security' },
|
|
19
|
+
60900: { name: 'Otros Planta Física', nameEn: 'Other Plant Expenses' },
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
alquiler_equipos: {
|
|
23
|
+
name: 'Alquiler de Equipos',
|
|
24
|
+
nameEn: 'Equipment Rental',
|
|
25
|
+
icon: '🔧',
|
|
26
|
+
accountRange: { min: 61000, max: 61999 },
|
|
27
|
+
accounts: {
|
|
28
|
+
61100: { name: 'Alquiler Equipos Refrigeración', nameEn: 'Refrigeration Equipment Rental' },
|
|
29
|
+
61200: { name: 'Alquiler Equipos Cocina', nameEn: 'Kitchen Equipment Rental' },
|
|
30
|
+
61300: { name: 'Alquiler POS/Tecnología', nameEn: 'POS/Tech Equipment Rental' },
|
|
31
|
+
61400: { name: 'Leasing Vehicular', nameEn: 'Vehicle Leasing' },
|
|
32
|
+
61900: { name: 'Otros Alquiler Equipos', nameEn: 'Other Equipment Rental' },
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
logistica: {
|
|
36
|
+
name: 'Logística',
|
|
37
|
+
nameEn: 'Logistics',
|
|
38
|
+
icon: '🚚',
|
|
39
|
+
accountRange: { min: 62000, max: 62999 },
|
|
40
|
+
accounts: {
|
|
41
|
+
62100: { name: 'Combustible Vehículos', nameEn: 'Vehicle Fuel' },
|
|
42
|
+
62200: { name: 'Mantenimiento Vehículos', nameEn: 'Vehicle Maintenance' },
|
|
43
|
+
62300: { name: 'Peajes y Estacionamiento', nameEn: 'Tolls & Parking' },
|
|
44
|
+
62400: { name: 'Servicio Delivery Terceros', nameEn: 'Third-party Delivery' },
|
|
45
|
+
62500: { name: 'Embalaje y Empaque', nameEn: 'Packaging' },
|
|
46
|
+
62600: { name: 'Flete y Transporte', nameEn: 'Freight & Transport' },
|
|
47
|
+
62900: { name: 'Otros Logística', nameEn: 'Other Logistics' },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
marketing: {
|
|
51
|
+
name: 'Marketing',
|
|
52
|
+
nameEn: 'Marketing',
|
|
53
|
+
icon: '📣',
|
|
54
|
+
accountRange: { min: 63000, max: 63999 },
|
|
55
|
+
accounts: {
|
|
56
|
+
63100: { name: 'Publicidad Digital', nameEn: 'Digital Advertising' },
|
|
57
|
+
63200: { name: 'Publicidad Impresa', nameEn: 'Print Advertising' },
|
|
58
|
+
63300: { name: 'Redes Sociales', nameEn: 'Social Media' },
|
|
59
|
+
63400: { name: 'Promociones y Descuentos', nameEn: 'Promotions & Discounts' },
|
|
60
|
+
63500: { name: 'Eventos y Degustaciones', nameEn: 'Events & Tastings' },
|
|
61
|
+
63600: { name: 'Comisiones Delivery Apps', nameEn: 'Delivery App Commissions' },
|
|
62
|
+
63700: { name: 'Material POP', nameEn: 'POP Material' },
|
|
63
|
+
63900: { name: 'Otros Marketing', nameEn: 'Other Marketing' },
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
administrativos: {
|
|
67
|
+
name: 'Administrativos',
|
|
68
|
+
nameEn: 'Administrative',
|
|
69
|
+
icon: '📋',
|
|
70
|
+
accountRange: { min: 64000, max: 64999 },
|
|
71
|
+
accounts: {
|
|
72
|
+
64100: { name: 'Útiles de Oficina', nameEn: 'Office Supplies' },
|
|
73
|
+
64200: { name: 'Software y Suscripciones', nameEn: 'Software & Subscriptions' },
|
|
74
|
+
64300: { name: 'Honorarios Profesionales', nameEn: 'Professional Fees' },
|
|
75
|
+
64400: { name: 'Viáticos y Representación', nameEn: 'Travel & Representation' },
|
|
76
|
+
64500: { name: 'Capacitación Personal', nameEn: 'Staff Training' },
|
|
77
|
+
64600: { name: 'Correo y Mensajería', nameEn: 'Mail & Courier' },
|
|
78
|
+
64700: { name: 'Licencias y Permisos', nameEn: 'Licenses & Permits' },
|
|
79
|
+
64900: { name: 'Otros Administrativos', nameEn: 'Other Administrative' },
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
seguros: {
|
|
83
|
+
name: 'Seguros',
|
|
84
|
+
nameEn: 'Insurance',
|
|
85
|
+
icon: '🛡️',
|
|
86
|
+
accountRange: { min: 65000, max: 65999 },
|
|
87
|
+
accounts: {
|
|
88
|
+
65100: { name: 'Seguro Local', nameEn: 'Store Insurance' },
|
|
89
|
+
65200: { name: 'Seguro Vehículos', nameEn: 'Vehicle Insurance' },
|
|
90
|
+
65300: { name: 'Seguro Equipos', nameEn: 'Equipment Insurance' },
|
|
91
|
+
65400: { name: 'Seguro Responsabilidad Civil', nameEn: 'Liability Insurance' },
|
|
92
|
+
65900: { name: 'Otros Seguros', nameEn: 'Other Insurance' },
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
bancarios: {
|
|
96
|
+
name: 'Bancarios',
|
|
97
|
+
nameEn: 'Banking',
|
|
98
|
+
icon: '🏦',
|
|
99
|
+
accountRange: { min: 67000, max: 67999 },
|
|
100
|
+
accounts: {
|
|
101
|
+
67100: { name: 'Comisiones Bancarias', nameEn: 'Bank Fees' },
|
|
102
|
+
67200: { name: 'Comisiones Punto de Venta', nameEn: 'POS Fees' },
|
|
103
|
+
67300: { name: 'Intereses Préstamos', nameEn: 'Loan Interest' },
|
|
104
|
+
67400: { name: 'Comisiones Transferencias', nameEn: 'Transfer Fees' },
|
|
105
|
+
67500: { name: 'Mantenimiento Cuentas', nameEn: 'Account Maintenance' },
|
|
106
|
+
67900: { name: 'Otros Bancarios', nameEn: 'Other Banking' },
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
servicios_contratados: {
|
|
110
|
+
name: 'Servicios Contratados',
|
|
111
|
+
nameEn: 'Contracted Services',
|
|
112
|
+
icon: '🤝',
|
|
113
|
+
accountRange: { min: 68000, max: 68999 },
|
|
114
|
+
accounts: {
|
|
115
|
+
68100: { name: 'Contabilidad Externa', nameEn: 'External Accounting' },
|
|
116
|
+
68200: { name: 'Asesoría Legal', nameEn: 'Legal Advisory' },
|
|
117
|
+
68300: { name: 'Consultoría', nameEn: 'Consulting' },
|
|
118
|
+
68400: { name: 'Servicios IT', nameEn: 'IT Services' },
|
|
119
|
+
68500: { name: 'Control de Plagas', nameEn: 'Pest Control' },
|
|
120
|
+
68600: { name: 'Servicio Técnico Equipos', nameEn: 'Equipment Technical Service' },
|
|
121
|
+
68900: { name: 'Otros Servicios Contratados', nameEn: 'Other Contracted Services' },
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
nomina: {
|
|
125
|
+
name: 'Nómina',
|
|
126
|
+
nameEn: 'Payroll',
|
|
127
|
+
icon: '💼',
|
|
128
|
+
accountRange: { min: 70000, max: 74999 },
|
|
129
|
+
accounts: {
|
|
130
|
+
71100: { name: 'Sueldos y Salarios', nameEn: 'Salaries & Wages' },
|
|
131
|
+
71110: { name: 'Sueldos Personal Tienda', nameEn: 'Store Staff Salaries' },
|
|
132
|
+
71120: { name: 'Sueldos Personal Administrativo', nameEn: 'Admin Staff Salaries' },
|
|
133
|
+
71200: { name: 'Horas Extra', nameEn: 'Overtime' },
|
|
134
|
+
71300: { name: 'Bonificaciones', nameEn: 'Bonuses' },
|
|
135
|
+
72100: { name: 'Seguro Social (IVSS)', nameEn: 'Social Security (IVSS)' },
|
|
136
|
+
72200: { name: 'Política Habitacional (FAOV)', nameEn: 'Housing Policy (FAOV)' },
|
|
137
|
+
72300: { name: 'INCES', nameEn: 'INCES' },
|
|
138
|
+
73100: { name: 'Vacaciones', nameEn: 'Vacation Pay' },
|
|
139
|
+
73200: { name: 'Utilidades', nameEn: 'Profit Sharing' },
|
|
140
|
+
73300: { name: 'Prestaciones Sociales', nameEn: 'Social Benefits' },
|
|
141
|
+
74100: { name: 'Cestaticket/Alimentación', nameEn: 'Meal Benefits' },
|
|
142
|
+
74200: { name: 'Uniformes', nameEn: 'Uniforms' },
|
|
143
|
+
74300: { name: 'Seguro HCM', nameEn: 'Health Insurance (HCM)' },
|
|
144
|
+
74900: { name: 'Otros Nómina', nameEn: 'Other Payroll' },
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
otros: {
|
|
148
|
+
name: 'Otros Gastos',
|
|
149
|
+
nameEn: 'Other Expenses',
|
|
150
|
+
icon: '📦',
|
|
151
|
+
accountRange: { min: 80000, max: 99999 },
|
|
152
|
+
accounts: {
|
|
153
|
+
80100: { name: 'Depreciación Equipos', nameEn: 'Equipment Depreciation' },
|
|
154
|
+
80200: { name: 'Depreciación Mejoras Local', nameEn: 'Store Improvements Depreciation' },
|
|
155
|
+
80300: { name: 'Amortización Intangibles', nameEn: 'Intangible Amortization' },
|
|
156
|
+
85100: { name: 'Impuestos Municipales', nameEn: 'Municipal Taxes' },
|
|
157
|
+
85200: { name: 'Impuesto Grandes Transacciones', nameEn: 'Large Transactions Tax' },
|
|
158
|
+
90100: { name: 'Pérdidas por Merma', nameEn: 'Spoilage Losses' },
|
|
159
|
+
90200: { name: 'Gastos Extraordinarios', nameEn: 'Extraordinary Expenses' },
|
|
160
|
+
99900: { name: 'Otros Gastos No Clasificados', nameEn: 'Other Unclassified Expenses' },
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// Range for all expense accounts
|
|
166
|
+
export const EXPENSE_RANGE = { min: 60000, max: 99999 };
|
|
167
|
+
|
|
168
|
+
export function isExpenseAccount(accountNumber) {
|
|
169
|
+
const num = parseInt(accountNumber, 10);
|
|
170
|
+
return num >= EXPENSE_RANGE.min && num <= EXPENSE_RANGE.max;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function getExpenseCategory(accountNumber) {
|
|
174
|
+
const num = parseInt(accountNumber, 10);
|
|
175
|
+
for (const [key, cat] of Object.entries(EXPENSE_CATEGORIES)) {
|
|
176
|
+
if (num >= cat.accountRange.min && num <= cat.accountRange.max) {
|
|
177
|
+
return { key, ...cat };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function getAccountName(accountNumber) {
|
|
184
|
+
const num = parseInt(accountNumber, 10);
|
|
185
|
+
for (const cat of Object.values(EXPENSE_CATEGORIES)) {
|
|
186
|
+
if (cat.accounts[num]) {
|
|
187
|
+
return cat.accounts[num].name;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return `Cuenta ${accountNumber}`;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function getAllExpenseAccountNumbers() {
|
|
194
|
+
const numbers = [];
|
|
195
|
+
for (const cat of Object.values(EXPENSE_CATEGORIES)) {
|
|
196
|
+
numbers.push(...Object.keys(cat.accounts).map(Number));
|
|
197
|
+
}
|
|
198
|
+
return numbers;
|
|
199
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Revenue & Cost Accounts - Chart of Accounts
|
|
2
|
+
// Revenue: 40000-49999 (credit balance = income)
|
|
3
|
+
// COGS: 50000-59999 (debit balance = cost of goods sold)
|
|
4
|
+
|
|
5
|
+
export const REVENUE_ACCOUNTS = {
|
|
6
|
+
40110: { name: 'Ventas Productos (Pedidos)', nameEn: 'Product Sales (Orders)' },
|
|
7
|
+
40160: { name: 'Ventas Productos (Órdenes)', nameEn: 'Product Sales (Orders)' },
|
|
8
|
+
40310: { name: 'Ventas Facturación Directa', nameEn: 'Direct Invoice Sales' },
|
|
9
|
+
40450: { name: 'Ingresos Varios Pedidos', nameEn: 'Misc Order Revenue' },
|
|
10
|
+
40460: { name: 'Ingresos Varios Órdenes', nameEn: 'Misc Order Revenue' },
|
|
11
|
+
40520: { name: 'Ventas Diarias', nameEn: 'Daily Sales' },
|
|
12
|
+
40580: { name: 'Cobros y Pagos', nameEn: 'Collections & Payments' },
|
|
13
|
+
40920: { name: 'Ventas Efectivo', nameEn: 'Cash Sales' },
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const COGS_ACCOUNTS = {
|
|
17
|
+
50110: { name: 'Costo Directo Intercompañía', nameEn: 'Direct Cost Intercompany' },
|
|
18
|
+
50120: { name: 'Costo de Ventas', nameEn: 'Cost of Goods Sold' },
|
|
19
|
+
50190: { name: 'Costo Directo Producción', nameEn: 'Direct Production Cost' },
|
|
20
|
+
51401: { name: 'Compras Mercancía', nameEn: 'Merchandise Purchases' },
|
|
21
|
+
52010: { name: 'Costo Intercompañía', nameEn: 'Intercompany Cost' },
|
|
22
|
+
52040: { name: 'Otros Costos Compras', nameEn: 'Other Purchase Costs' },
|
|
23
|
+
56020: { name: 'Costos Proveedores', nameEn: 'Supplier Costs' },
|
|
24
|
+
57010: { name: 'Costos Varios', nameEn: 'Miscellaneous Costs' },
|
|
25
|
+
57020: { name: 'Costos Diversos', nameEn: 'Diverse Costs' },
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Ranges
|
|
29
|
+
export const REVENUE_RANGE = { min: 40000, max: 49999 };
|
|
30
|
+
export const COGS_RANGE = { min: 50000, max: 59999 };
|
|
31
|
+
|
|
32
|
+
export function isRevenueAccount(accountNumber) {
|
|
33
|
+
const num = parseInt(accountNumber, 10);
|
|
34
|
+
return num >= REVENUE_RANGE.min && num <= REVENUE_RANGE.max;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function isCOGSAccount(accountNumber) {
|
|
38
|
+
const num = parseInt(accountNumber, 10);
|
|
39
|
+
return num >= COGS_RANGE.min && num <= COGS_RANGE.max;
|
|
40
|
+
}
|