el-contador 1.2.8 → 1.2.10

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/README.md CHANGED
@@ -49,7 +49,7 @@ You do **not** need to install PostgreSQL on the host or create a database/user
49
49
 
50
50
  On a new server (VPS, dedicated, etc.):
51
51
 
52
- 1. Install Node.js (v18+), npm, and Docker (and Docker Compose).
52
+ 1. Install Node.js (v18+), npm, Docker, and **Docker Compose V2** (`docker compose`). The CLI requires the Compose plugin; legacy `docker-compose` (V1) is not supported.
53
53
  2. Create a directory and install the app as above (e.g. `npm init -y`, `npm install el-contador`, copy `.env.example` to `.env`, set `DB_PASSWORD` and `SESSION_SECRET`).
54
54
  3. Start with `npx el-contador` (or the `docker compose -f ...` command). The app listens on `ADMIN_PORT` (default 3080) on the host.
55
55
  4. To expose it on a **subdomain or domain** (e.g. `admin.example.com`), configure a **reverse proxy** (nginx, Caddy, or Apache) on that server. The package does not install or configure nginx for you; you add the proxy config yourself. See below.
@@ -117,13 +117,9 @@ After a successful update, the backend log will show `el-contador-server@X.Y.Z`
117
117
  - `el-contador down` or `el-contador stop` – stop containers.
118
118
  - `el-contador update` – run `npm update el-contador` then rebuild and start.
119
119
 
120
- If `npx el-contador` fails (e.g. "unknown shorthand flag: 'f'"), your host may have Docker but not the Compose plugin in the expected form. Install **docker-compose** (standalone) or run Compose manually from the same directory as `.env`:
120
+ If `npx el-contador` fails with **"docker compose (Compose V2) is required"**, install the Compose plugin: `sudo apt install docker-compose-plugin` (Debian/Ubuntu). Then run `npx el-contador` again.
121
121
 
122
- ```bash
123
- docker-compose --project-directory node_modules/el-contador -f node_modules/el-contador/docker-compose.yml --env-file .env up -d
124
- ```
125
-
126
- On Ubuntu: `sudo apt install docker-compose-plugin` (V2) or the standalone `docker-compose` package.
122
+ If you see **KeyError: 'ContainerConfig'** (Python traceback), you are using legacy `docker-compose` (V1). Install Docker Compose V2 as above; the CLI no longer falls back to V1.
127
123
 
128
124
  ## Publishing to npm (maintainers)
129
125
 
package/bin/cli.js CHANGED
@@ -16,22 +16,19 @@ function shQuote(s) {
16
16
  return "'" + String(s).replace(/'/g, "'\"'\"'") + "'";
17
17
  }
18
18
 
19
+ // Prefer Docker Compose V2 only. V1 (docker-compose) can raise KeyError: 'ContainerConfig'
20
+ // with newer Docker Engine when inspecting container volumes.
19
21
  function getDockerComposeCmd() {
20
- const r1 = spawnSync(
22
+ const r = spawnSync(
21
23
  process.platform === 'win32' ? 'cmd' : 'sh',
22
24
  [process.platform === 'win32' ? '/c' : '-c', 'docker compose version'],
23
25
  { stdio: 'ignore' }
24
26
  );
25
- if (r1.status === 0) return 'docker compose';
26
-
27
- const r2 = spawnSync(
28
- process.platform === 'win32' ? 'cmd' : 'sh',
29
- [process.platform === 'win32' ? '/c' : '-c', 'docker-compose version'],
30
- { stdio: 'ignore' }
31
- );
32
- if (r2.status === 0) return 'docker-compose';
27
+ if (r.status === 0) return 'docker compose';
33
28
 
34
- console.error('el-contador: need "docker compose" or "docker-compose". Install Docker and Docker Compose.');
29
+ console.error('el-contador: "docker compose" (Compose V2) is required.');
30
+ console.error('Install the plugin: apt install docker-compose-plugin (Debian/Ubuntu)');
31
+ console.error('Or use the Docker Desktop built-in Compose. Legacy "docker-compose" (V1) is not supported.');
35
32
  process.exit(1);
36
33
  }
37
34
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "el-contador",
3
- "version": "1.2.8",
3
+ "version": "1.2.10",
4
4
  "description": "Bookkeeping and expense management – run with Docker",
5
5
  "keywords": [
6
6
  "bookkeeping",
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "files": [
18
18
  "server/db",
19
+ "server/lib",
19
20
  "server/middleware",
20
21
  "server/routes",
21
22
  "server/scripts",
package/server/index.js CHANGED
@@ -1,5 +1,12 @@
1
1
  require('dotenv').config({ path: require('path').join(__dirname, '..', '.env') });
2
2
  const path = require('path');
3
+ const fs = require('fs');
4
+ // Fail fast with a clear message if server/lib is missing (e.g. incomplete deploy)
5
+ const libFiscal = path.join(__dirname, 'lib', 'fiscal.js');
6
+ if (!fs.existsSync(libFiscal)) {
7
+ console.error('Missing server/lib/fiscal.js. Deploy the full server directory including server/lib/.');
8
+ process.exit(1);
9
+ }
3
10
  const express = require('express');
4
11
  const session = require('express-session');
5
12
  const cookieParser = require('cookie-parser');
@@ -0,0 +1,113 @@
1
+ const { pool } = require('../db/pool');
2
+
3
+ /**
4
+ * Load fiscal config from invoice_settings. Returns { enabled, startMonth, startDay }.
5
+ * Default: natural year (enabled: false, startMonth: 1, startDay: 1).
6
+ */
7
+ async function getFiscalConfig() {
8
+ const r = await pool.query('SELECT data FROM invoice_settings WHERE id = 1');
9
+ const raw = r.rows[0]?.data || {};
10
+ const enabled = Boolean(raw.fiscalYearEnabled);
11
+ const startMonth = Math.min(12, Math.max(1, Number(raw.fiscalYearStartMonth) || 1));
12
+ let startDay = Math.min(31, Math.max(1, Number(raw.fiscalYearStartDay) || 1));
13
+ const maxDay = new Date(2024, startMonth, 0).getDate();
14
+ if (startDay > maxDay) startDay = maxDay;
15
+ return { enabled, startMonth, startDay };
16
+ }
17
+
18
+ function pad(n) {
19
+ return String(n).padStart(2, '0');
20
+ }
21
+
22
+ /**
23
+ * Return { startDate, endDate } (ISO YYYY-MM-DD) for the given fiscal/calendar year number.
24
+ * When custom fiscal: year 2025 = 2025-startMonth-startDay to day before 2026-startMonth-startDay.
25
+ */
26
+ function getFiscalYearBounds(yearNumber, config) {
27
+ const { enabled, startMonth, startDay } = config;
28
+ if (!enabled || (startMonth === 1 && startDay === 1)) {
29
+ return {
30
+ startDate: `${yearNumber}-01-01`,
31
+ endDate: `${yearNumber}-12-31`,
32
+ };
33
+ }
34
+ const startDate = `${yearNumber}-${pad(startMonth)}-${pad(startDay)}`;
35
+ const endYear = yearNumber + 1;
36
+ const endDate = new Date(endYear, startMonth - 1, startDay - 1);
37
+ const endDateStr = `${endYear}-${pad(endDate.getMonth() + 1)}-${pad(endDate.getDate())}`;
38
+ return { startDate, endDate: endDateStr };
39
+ }
40
+
41
+ /**
42
+ * Return { startDate, endDate } for the given quarter (1-4) of the fiscal/calendar year.
43
+ * Custom fiscal: Q1 = first 3 months of fiscal year, Q2 = next 3, etc.
44
+ */
45
+ function getFiscalQuarterBounds(yearNumber, quarter, config) {
46
+ const { enabled, startMonth, startDay } = config;
47
+ if (!enabled || (startMonth === 1 && startDay === 1)) {
48
+ const startMonthQ = (quarter - 1) * 3 + 1;
49
+ const startDate = `${yearNumber}-${pad(startMonthQ)}-01`;
50
+ const endMonthQ = quarter * 3;
51
+ const endDate = new Date(yearNumber, endMonthQ, 0);
52
+ return {
53
+ startDate,
54
+ endDate: `${yearNumber}-${pad(endMonthQ)}-${pad(endDate.getDate())}`,
55
+ };
56
+ }
57
+ const yearStart = getFiscalYearBounds(yearNumber, config).startDate;
58
+ const start = new Date(yearStart);
59
+ start.setMonth(start.getMonth() + (quarter - 1) * 3);
60
+ const end = new Date(start);
61
+ end.setMonth(end.getMonth() + 3);
62
+ end.setDate(end.getDate() - 1);
63
+ const startDate = `${start.getFullYear()}-${pad(start.getMonth() + 1)}-${pad(start.getDate())}`;
64
+ const endDate = `${end.getFullYear()}-${pad(end.getMonth() + 1)}-${pad(end.getDate())}`;
65
+ return { startDate, endDate };
66
+ }
67
+
68
+ /**
69
+ * Return { startDate, endDate } for the given month (1-12) of the fiscal/calendar year.
70
+ * Month 1 = first month of fiscal year.
71
+ */
72
+ function getFiscalMonthBounds(yearNumber, month, config) {
73
+ const { enabled, startMonth, startDay } = config;
74
+ if (!enabled || (startMonth === 1 && startDay === 1)) {
75
+ const startDate = `${yearNumber}-${pad(month)}-01`;
76
+ const endDate = new Date(yearNumber, month, 0);
77
+ return {
78
+ startDate,
79
+ endDate: `${yearNumber}-${pad(month)}-${pad(endDate.getDate())}`,
80
+ };
81
+ }
82
+ const yearStart = getFiscalYearBounds(yearNumber, config).startDate;
83
+ const start = new Date(yearStart);
84
+ start.setMonth(start.getMonth() + (month - 1));
85
+ const end = new Date(start);
86
+ end.setMonth(end.getMonth() + 1);
87
+ end.setDate(end.getDate() - 1);
88
+ const startDate = `${start.getFullYear()}-${pad(start.getMonth() + 1)}-${pad(start.getDate())}`;
89
+ const endDate = `${end.getFullYear()}-${pad(end.getMonth() + 1)}-${pad(end.getDate())}`;
90
+ return { startDate, endDate };
91
+ }
92
+
93
+ /**
94
+ * Which fiscal year number contains the given date? Returns the start-year of that fiscal year.
95
+ */
96
+ function getFiscalYearContainingDateSync(dateStr, config) {
97
+ const d = new Date(dateStr);
98
+ const y = d.getFullYear();
99
+ const m = d.getMonth() + 1;
100
+ const day = d.getDate();
101
+ const { enabled, startMonth, startDay } = config;
102
+ if (!enabled || (startMonth === 1 && startDay === 1)) return y;
103
+ if (m < startMonth || (m === startMonth && day < startDay)) return y - 1;
104
+ return y;
105
+ }
106
+
107
+ module.exports = {
108
+ getFiscalConfig,
109
+ getFiscalYearBounds,
110
+ getFiscalQuarterBounds,
111
+ getFiscalMonthBounds,
112
+ getFiscalYearContainingDateSync,
113
+ };
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "el-contador-server",
3
- "version": "1.2.8",
3
+ "version": "1.2.10",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "el-contador-server",
9
- "version": "1.2.8",
9
+ "version": "1.2.10",
10
10
  "dependencies": {
11
11
  "@google/genai": "^0.10.0",
12
12
  "bcrypt": "^5.1.1",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "el-contador-server",
3
- "version": "1.2.8",
3
+ "version": "1.2.10",
4
4
  "description": "El Contador backend API and auth",
5
5
  "main": "index.js",
6
6
  "scripts": {