estela 0.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/README.md +138 -0
- package/dist/billing/amortize.d.ts +44 -0
- package/dist/billing/amortize.d.ts.map +1 -0
- package/dist/billing/amortize.js +83 -0
- package/dist/billing/amortize.js.map +1 -0
- package/dist/billing/amortize.test.d.ts +2 -0
- package/dist/billing/amortize.test.d.ts.map +1 -0
- package/dist/billing/amortize.test.js +121 -0
- package/dist/billing/amortize.test.js.map +1 -0
- package/dist/billing/format.test.d.ts +2 -0
- package/dist/billing/format.test.d.ts.map +1 -0
- package/dist/billing/format.test.js +110 -0
- package/dist/billing/format.test.js.map +1 -0
- package/dist/billing/invoice.d.ts +52 -0
- package/dist/billing/invoice.d.ts.map +1 -0
- package/dist/billing/invoice.js +148 -0
- package/dist/billing/invoice.js.map +1 -0
- package/dist/billing/invoice.test.d.ts +2 -0
- package/dist/billing/invoice.test.d.ts.map +1 -0
- package/dist/billing/invoice.test.js +200 -0
- package/dist/billing/invoice.test.js.map +1 -0
- package/dist/billing/sessionize.d.ts +104 -0
- package/dist/billing/sessionize.d.ts.map +1 -0
- package/dist/billing/sessionize.js +0 -0
- package/dist/billing/sessionize.js.map +1 -0
- package/dist/billing/sessionize.test.d.ts +2 -0
- package/dist/billing/sessionize.test.d.ts.map +1 -0
- package/dist/billing/sessionize.test.js +67 -0
- package/dist/billing/sessionize.test.js.map +1 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1091 -0
- package/dist/cli.js.map +1 -0
- package/dist/cloud/auth.d.ts +4 -0
- package/dist/cloud/auth.d.ts.map +1 -0
- package/dist/cloud/auth.js +98 -0
- package/dist/cloud/auth.js.map +1 -0
- package/dist/cloud/client.d.ts +12 -0
- package/dist/cloud/client.d.ts.map +1 -0
- package/dist/cloud/client.js +34 -0
- package/dist/cloud/client.js.map +1 -0
- package/dist/db/schema.d.ts +11 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +424 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/db/schema.test.d.ts +2 -0
- package/dist/db/schema.test.d.ts.map +1 -0
- package/dist/db/schema.test.js +335 -0
- package/dist/db/schema.test.js.map +1 -0
- package/dist/db/store.d.ts +113 -0
- package/dist/db/store.d.ts.map +1 -0
- package/dist/db/store.js +550 -0
- package/dist/db/store.js.map +1 -0
- package/dist/doctor.d.ts +24 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +229 -0
- package/dist/doctor.js.map +1 -0
- package/dist/export/csv.d.ts +9 -0
- package/dist/export/csv.d.ts.map +1 -0
- package/dist/export/csv.js +81 -0
- package/dist/export/csv.js.map +1 -0
- package/dist/export/invoice-pdf.d.ts +21 -0
- package/dist/export/invoice-pdf.d.ts.map +1 -0
- package/dist/export/invoice-pdf.js +135 -0
- package/dist/export/invoice-pdf.js.map +1 -0
- package/dist/export/panel.d.ts +78 -0
- package/dist/export/panel.d.ts.map +1 -0
- package/dist/export/panel.js +693 -0
- package/dist/export/panel.js.map +1 -0
- package/dist/export/panel.test.d.ts +2 -0
- package/dist/export/panel.test.d.ts.map +1 -0
- package/dist/export/panel.test.js +273 -0
- package/dist/export/panel.test.js.map +1 -0
- package/dist/export/pdf.d.ts +32 -0
- package/dist/export/pdf.d.ts.map +1 -0
- package/dist/export/pdf.js +229 -0
- package/dist/export/pdf.js.map +1 -0
- package/dist/export/pdf.test.d.ts +2 -0
- package/dist/export/pdf.test.d.ts.map +1 -0
- package/dist/export/pdf.test.js +151 -0
- package/dist/export/pdf.test.js.map +1 -0
- package/dist/export/share.d.ts +42 -0
- package/dist/export/share.d.ts.map +1 -0
- package/dist/export/share.js +181 -0
- package/dist/export/share.js.map +1 -0
- package/dist/export/share.test.d.ts +2 -0
- package/dist/export/share.test.d.ts.map +1 -0
- package/dist/export/share.test.js +116 -0
- package/dist/export/share.test.js.map +1 -0
- package/dist/metrics/index.d.ts +101 -0
- package/dist/metrics/index.d.ts.map +1 -0
- package/dist/metrics/index.js +221 -0
- package/dist/metrics/index.js.map +1 -0
- package/dist/metrics/metrics.test.d.ts +2 -0
- package/dist/metrics/metrics.test.d.ts.map +1 -0
- package/dist/metrics/metrics.test.js +162 -0
- package/dist/metrics/metrics.test.js.map +1 -0
- package/dist/metrics/people.d.ts +46 -0
- package/dist/metrics/people.d.ts.map +1 -0
- package/dist/metrics/people.js +126 -0
- package/dist/metrics/people.js.map +1 -0
- package/dist/metrics/people.test.d.ts +2 -0
- package/dist/metrics/people.test.d.ts.map +1 -0
- package/dist/metrics/people.test.js +84 -0
- package/dist/metrics/people.test.js.map +1 -0
- package/dist/metrics/team.d.ts +41 -0
- package/dist/metrics/team.d.ts.map +1 -0
- package/dist/metrics/team.js +0 -0
- package/dist/metrics/team.js.map +1 -0
- package/dist/metrics/team.test.d.ts +2 -0
- package/dist/metrics/team.test.d.ts.map +1 -0
- package/dist/metrics/team.test.js +90 -0
- package/dist/metrics/team.test.js.map +1 -0
- package/dist/pricing/catalog.d.ts +39 -0
- package/dist/pricing/catalog.d.ts.map +1 -0
- package/dist/pricing/catalog.js +59 -0
- package/dist/pricing/catalog.js.map +1 -0
- package/dist/pricing/cost.d.ts +25 -0
- package/dist/pricing/cost.d.ts.map +1 -0
- package/dist/pricing/cost.js +50 -0
- package/dist/pricing/cost.js.map +1 -0
- package/dist/pricing/cost.test.d.ts +2 -0
- package/dist/pricing/cost.test.d.ts.map +1 -0
- package/dist/pricing/cost.test.js +74 -0
- package/dist/pricing/cost.test.js.map +1 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +1141 -0
- package/dist/server.js.map +1 -0
- package/dist/setup.d.ts +70 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +223 -0
- package/dist/setup.js.map +1 -0
- package/dist/setup.test.d.ts +2 -0
- package/dist/setup.test.d.ts.map +1 -0
- package/dist/setup.test.js +131 -0
- package/dist/setup.test.js.map +1 -0
- package/dist/watchers/claude.d.ts +34 -0
- package/dist/watchers/claude.d.ts.map +1 -0
- package/dist/watchers/claude.js +235 -0
- package/dist/watchers/claude.js.map +1 -0
- package/dist/watchers/claude.test.d.ts +2 -0
- package/dist/watchers/claude.test.d.ts.map +1 -0
- package/dist/watchers/claude.test.js +180 -0
- package/dist/watchers/claude.test.js.map +1 -0
- package/dist/watchers/git.d.ts +42 -0
- package/dist/watchers/git.d.ts.map +1 -0
- package/dist/watchers/git.js +218 -0
- package/dist/watchers/git.js.map +1 -0
- package/dist/watchers/git.test.d.ts +2 -0
- package/dist/watchers/git.test.d.ts.map +1 -0
- package/dist/watchers/git.test.js +73 -0
- package/dist/watchers/git.test.js.map +1 -0
- package/dist/watchers/identity.d.ts +34 -0
- package/dist/watchers/identity.d.ts.map +1 -0
- package/dist/watchers/identity.js +85 -0
- package/dist/watchers/identity.js.map +1 -0
- package/dist/watchers/identity.test.d.ts +2 -0
- package/dist/watchers/identity.test.d.ts.map +1 -0
- package/dist/watchers/identity.test.js +48 -0
- package/dist/watchers/identity.test.js.map +1 -0
- package/dist/web-ui.test.d.ts +2 -0
- package/dist/web-ui.test.d.ts.map +1 -0
- package/dist/web-ui.test.js +312 -0
- package/dist/web-ui.test.js.map +1 -0
- package/node_modules/@estela/shared/dist/index.d.ts +3 -0
- package/node_modules/@estela/shared/dist/index.d.ts.map +1 -0
- package/node_modules/@estela/shared/dist/index.js +19 -0
- package/node_modules/@estela/shared/dist/index.js.map +1 -0
- package/node_modules/@estela/shared/dist/money.d.ts +71 -0
- package/node_modules/@estela/shared/dist/money.d.ts.map +1 -0
- package/node_modules/@estela/shared/dist/money.js +171 -0
- package/node_modules/@estela/shared/dist/money.js.map +1 -0
- package/node_modules/@estela/shared/dist/types.d.ts +365 -0
- package/node_modules/@estela/shared/dist/types.d.ts.map +1 -0
- package/node_modules/@estela/shared/dist/types.js +24 -0
- package/node_modules/@estela/shared/dist/types.js.map +1 -0
- package/node_modules/@estela/shared/package.json +5 -0
- package/package.json +41 -0
- package/web/app.css +749 -0
- package/web/app.js +1257 -0
- package/web/apple-touch-icon.png +0 -0
- package/web/favicon.png +0 -0
- package/web/index.html +132 -0
- package/web/lib.js +94 -0
- package/web/logo-mark.png +0 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const node_path_1 = require("node:path");
|
|
9
|
+
const node_test_1 = require("node:test");
|
|
10
|
+
/**
|
|
11
|
+
* Pruebas de la interfaz web sin navegador.
|
|
12
|
+
*
|
|
13
|
+
* Los fallos que llegaron a producción fueron de dos clases:
|
|
14
|
+
*
|
|
15
|
+
* 1. Lógica pura mal escrita — "2h 60m", el día de hoy calculado en UTC, el
|
|
16
|
+
* salto de dos días al navegar. Se prueban llamando a las funciones.
|
|
17
|
+
* 2. CSS y DOM — el atributo `hidden` que no ocultaba nada porque una clase
|
|
18
|
+
* con `display: flex` lo pisaba, y un `id` referenciado que no existía en
|
|
19
|
+
* el HTML. Se prueban leyendo los ficheros y comprobando invariantes.
|
|
20
|
+
*
|
|
21
|
+
* Nada de esto necesita un navegador ni una dependencia nueva, y cubre las dos
|
|
22
|
+
* familias de fallo que de verdad ocurrieron.
|
|
23
|
+
*/
|
|
24
|
+
const WEB = (0, node_path_1.join)(__dirname, "..", "..", "web");
|
|
25
|
+
const html = (0, node_fs_1.readFileSync)((0, node_path_1.join)(WEB, "index.html"), "utf8");
|
|
26
|
+
const css = (0, node_fs_1.readFileSync)((0, node_path_1.join)(WEB, "app.css"), "utf8");
|
|
27
|
+
const appJs = (0, node_fs_1.readFileSync)((0, node_path_1.join)(WEB, "app.js"), "utf8");
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
29
|
+
const lib = require((0, node_path_1.join)(WEB, "lib.js"));
|
|
30
|
+
// ── Duraciones ─────────────────────────────────────────────────────────
|
|
31
|
+
(0, node_test_1.test)("interfaz: nunca imprime 60 minutos", () => {
|
|
32
|
+
strict_1.default.equal(lib.fmtDuration(2 * 3600 + 59 * 60 + 50), "3h");
|
|
33
|
+
strict_1.default.equal(lib.fmtDuration(3599), "1h");
|
|
34
|
+
for (let s = 0; s < 12 * 3600; s += 13) {
|
|
35
|
+
strict_1.default.ok(!lib.fmtDuration(s).includes("60m"), `fmtDuration(${s})`);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
(0, node_test_1.test)("interfaz: duraciones normales", () => {
|
|
39
|
+
strict_1.default.equal(lib.fmtDuration(0), "0m");
|
|
40
|
+
strict_1.default.equal(lib.fmtDuration(3660), "1h 01m");
|
|
41
|
+
strict_1.default.equal(lib.fmtDuration(4 * 3600 + 16 * 60), "4h 16m");
|
|
42
|
+
});
|
|
43
|
+
// ── Fechas ─────────────────────────────────────────────────────────────
|
|
44
|
+
(0, node_test_1.test)("interfaz: hoy se calcula en local, no en Greenwich", () => {
|
|
45
|
+
// El fallo real: a las 23:31 en UTC-5, toISOString() daba el día siguiente,
|
|
46
|
+
// así que la pestaña "Hoy" llevaba a un día vacío y el día real salía
|
|
47
|
+
// etiquetado como "Ayer".
|
|
48
|
+
const tarde = new Date("2026-08-27T04:31:00Z"); // 23:31 del 26 en UTC-5
|
|
49
|
+
strict_1.default.equal(lib.localToday(0, tarde), lib.localDay(tarde));
|
|
50
|
+
if (tarde.getTimezoneOffset() > 0) {
|
|
51
|
+
strict_1.default.notEqual(lib.localToday(0, tarde), tarde.toISOString().slice(0, 10), "al oeste de Greenwich la fecha local difiere de la UTC a esa hora");
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
(0, node_test_1.test)("interfaz: navegar un día no salta dos", () => {
|
|
55
|
+
// Anclar a mediodía evita que a las 00:30 la aritmética en UTC salte dos días.
|
|
56
|
+
strict_1.default.equal(lib.shiftDay("2026-08-26", 1), "2026-08-27");
|
|
57
|
+
strict_1.default.equal(lib.shiftDay("2026-08-27", -1), "2026-08-26");
|
|
58
|
+
strict_1.default.equal(lib.shiftDay("2026-03-01", -1), "2026-02-28");
|
|
59
|
+
strict_1.default.equal(lib.shiftDay("2026-12-31", 1), "2027-01-01");
|
|
60
|
+
});
|
|
61
|
+
(0, node_test_1.test)("interfaz: ida y vuelta entre días siempre vuelve al mismo sitio", () => {
|
|
62
|
+
let day = "2026-01-01";
|
|
63
|
+
for (let i = 0; i < 400; i++) {
|
|
64
|
+
const next = lib.shiftDay(day, 1);
|
|
65
|
+
strict_1.default.equal(lib.shiftDay(next, -1), day, `falla en ${day}`);
|
|
66
|
+
day = next;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
(0, node_test_1.test)("interfaz: 'Hoy' y 'Ayer' se calculan con la misma fecha local", () => {
|
|
70
|
+
const now = new Date("2026-08-27T04:31:00Z");
|
|
71
|
+
strict_1.default.equal(lib.fmtDayTitle(lib.localToday(0, now), now), "Hoy");
|
|
72
|
+
strict_1.default.equal(lib.fmtDayTitle(lib.localToday(-1, now), now), "Ayer");
|
|
73
|
+
});
|
|
74
|
+
(0, node_test_1.test)("interfaz: el periodo 'todo' no acota fechas", () => {
|
|
75
|
+
strict_1.default.deepEqual(lib.periodRange("all"), { from: "", to: "" });
|
|
76
|
+
const mes = lib.periodRange("month", new Date("2026-08-27T12:00:00Z"));
|
|
77
|
+
strict_1.default.ok(mes.from.endsWith("-01"), "el mes empieza el día 1");
|
|
78
|
+
strict_1.default.ok(mes.from <= mes.to);
|
|
79
|
+
});
|
|
80
|
+
// ── Importes ───────────────────────────────────────────────────────────
|
|
81
|
+
(0, node_test_1.test)("interfaz: sin importe devuelve null, no cero", () => {
|
|
82
|
+
// Enseñar "0,00" donde no hay tarifa haría creer que el trabajo no vale nada.
|
|
83
|
+
strict_1.default.equal(lib.fmtMoney(null, "EUR"), null);
|
|
84
|
+
strict_1.default.equal(lib.fmtMoney(undefined, "EUR"), null);
|
|
85
|
+
strict_1.default.equal(lib.fmtMoney(0, "EUR"), "€0,00");
|
|
86
|
+
});
|
|
87
|
+
(0, node_test_1.test)("interfaz: una moneda sin símbolo se escribe con su código", () => {
|
|
88
|
+
strict_1.default.ok(lib.fmtMoney(1000, "COP").includes("COP"));
|
|
89
|
+
strict_1.default.equal(lib.fmtMoney(1300, "EUR"), "€13,00");
|
|
90
|
+
});
|
|
91
|
+
// ── Invariantes del HTML y el CSS ──────────────────────────────────────
|
|
92
|
+
(0, node_test_1.test)("interfaz: el atributo hidden oculta de verdad", () => {
|
|
93
|
+
// El fallo real: `[hidden] { display: none }` es una regla del navegador y
|
|
94
|
+
// cualquier `display` de autor la gana. Como .hero y .approve son flex, el
|
|
95
|
+
// atributo no hacía nada: el formulario salía abierto y un día vacío seguía
|
|
96
|
+
// enseñando las cifras del anterior.
|
|
97
|
+
strict_1.default.match(css, /\[hidden\]\s*\{[^}]*display:\s*none\s*!important/, "hace falta [hidden] { display: none !important }");
|
|
98
|
+
});
|
|
99
|
+
(0, node_test_1.test)("interfaz: todo id que usa el guion existe en el HTML", () => {
|
|
100
|
+
// Un $("#x") contra un id que se renombró devuelve null y revienta al usarlo.
|
|
101
|
+
const ids = new Set([...html.matchAll(/\bid="([^"]+)"/g)].map((m) => m[1]));
|
|
102
|
+
const usados = [...appJs.matchAll(/\$\("#([a-zA-Z0-9_-]+)"\)/g)].map((m) => m[1]);
|
|
103
|
+
// Los que se crean dinámicamente y no están en el HTML de partida.
|
|
104
|
+
const dinamicos = new Set(["add-toggle", "add-form", "add-cancel", "add-kind",
|
|
105
|
+
"add-minutes", "add-what", "add-project", "new-project", "np-repo", "np-name",
|
|
106
|
+
"np-rate", "np-client", "np-clientname", "np-currency", "approve-all", "rep-periods",
|
|
107
|
+
"team-more"]);
|
|
108
|
+
const faltan = [...new Set(usados)].filter((id) => !ids.has(id) && !dinamicos.has(id));
|
|
109
|
+
strict_1.default.deepEqual(faltan, [], `ids referenciados que no existen: ${faltan.join(", ")}`);
|
|
110
|
+
});
|
|
111
|
+
(0, node_test_1.test)("interfaz: ningún color vive solo dentro de un media query", () => {
|
|
112
|
+
// Un color declarado únicamente bajo `prefers-color-scheme` no aplica en el
|
|
113
|
+
// otro tema, y la página sale con el texto de un tema sobre el fondo del otro.
|
|
114
|
+
const dentro = css.slice(css.indexOf("@media (prefers-color-scheme: dark)"));
|
|
115
|
+
const bloque = dentro.slice(0, dentro.indexOf("\n}\n") + 3);
|
|
116
|
+
const reglas = [...bloque.matchAll(/^\s{2,}([a-z-]+)\s*\{/gm)].map((m) => m[1]);
|
|
117
|
+
strict_1.default.deepEqual(reglas, [], `el bloque oscuro solo debe redefinir variables, no reglas: ${reglas.join(", ")}`);
|
|
118
|
+
});
|
|
119
|
+
(0, node_test_1.test)("interfaz: no quedan fechas calculadas en UTC", () => {
|
|
120
|
+
// El patrón que causó tres fallos distintos: toISOString().slice(0,10) sin
|
|
121
|
+
// restar antes el desfase horario.
|
|
122
|
+
const sospechosas = [...appJs.matchAll(/toISOString\(\)\.slice\(0,\s*10\)/g)];
|
|
123
|
+
const conOffset = [...appJs.matchAll(/getTimezoneOffset\(\)[^;]*toISOString\(\)\.slice\(0,\s*10\)/g)];
|
|
124
|
+
strict_1.default.equal(sospechosas.length, conOffset.length, "toda conversión a YYYY-MM-DD debe restar el desfase horario antes");
|
|
125
|
+
});
|
|
126
|
+
(0, node_test_1.test)("interfaz: el HTML carga lib.js antes que app.js", () => {
|
|
127
|
+
// Al revés, app.js llamaría a funciones que aún no existen.
|
|
128
|
+
const lib = html.indexOf('src="/lib.js"');
|
|
129
|
+
const app = html.indexOf('src="/app.js"');
|
|
130
|
+
strict_1.default.ok(lib > 0 && app > 0, "los dos scripts deben estar enlazados");
|
|
131
|
+
strict_1.default.ok(lib < app, "lib.js va primero");
|
|
132
|
+
});
|
|
133
|
+
(0, node_test_1.test)("interfaz: el escapado neutraliza HTML en datos", () => {
|
|
134
|
+
// Un mensaje de commit con etiquetas no puede inyectarse en la página.
|
|
135
|
+
const salida = lib.esc('<img onerror="alert(1)"> & "x"');
|
|
136
|
+
strict_1.default.ok(!salida.includes("<img"));
|
|
137
|
+
strict_1.default.ok(salida.includes("<img"));
|
|
138
|
+
strict_1.default.ok(salida.includes("&"));
|
|
139
|
+
});
|
|
140
|
+
// ── La misma lógica bajo varios husos horarios ─────────────────────────
|
|
141
|
+
/**
|
|
142
|
+
* Recarga lib.js con otra zona horaria.
|
|
143
|
+
*
|
|
144
|
+
* Sin esto, los tests de fechas solo valen en el huso de quien los ejecuta: la
|
|
145
|
+
* versión defectuosa de `shiftDay` acierta por casualidad en UTC-5 y falla al
|
|
146
|
+
* este de Greenwich. Un test que depende del reloj de la máquina no prueba nada.
|
|
147
|
+
*/
|
|
148
|
+
function conZona(tz, fn) {
|
|
149
|
+
const previa = process.env["TZ"];
|
|
150
|
+
process.env["TZ"] = tz;
|
|
151
|
+
const ruta = require.resolve((0, node_path_1.join)(WEB, "lib.js"));
|
|
152
|
+
delete require.cache[ruta];
|
|
153
|
+
try {
|
|
154
|
+
return fn(require(ruta));
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
process.env["TZ"] = previa;
|
|
158
|
+
delete require.cache[ruta];
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const libMod = lib;
|
|
162
|
+
const ZONAS = [
|
|
163
|
+
"America/Guayaquil", // UTC-5, el tuyo
|
|
164
|
+
"Pacific/Honolulu", // UTC-10, el extremo oeste
|
|
165
|
+
"Europe/Madrid", // UTC+2
|
|
166
|
+
"Asia/Tokyo", // UTC+9
|
|
167
|
+
"Pacific/Kiritimati", // UTC+14, el extremo este
|
|
168
|
+
"Asia/Kathmandu", // UTC+5:45, desfase no entero
|
|
169
|
+
];
|
|
170
|
+
(0, node_test_1.test)("fechas: navegar un día funciona en cualquier huso", () => {
|
|
171
|
+
for (const tz of ZONAS) {
|
|
172
|
+
conZona(tz, (l) => {
|
|
173
|
+
strict_1.default.equal(l.shiftDay("2026-08-26", 1), "2026-08-27", `avanzar en ${tz}`);
|
|
174
|
+
strict_1.default.equal(l.shiftDay("2026-08-27", -1), "2026-08-26", `retroceder en ${tz}`);
|
|
175
|
+
strict_1.default.equal(l.shiftDay("2026-02-28", 1), "2026-03-01", `fin de mes en ${tz}`);
|
|
176
|
+
strict_1.default.equal(l.shiftDay("2026-01-01", -1), "2025-12-31", `cambio de año en ${tz}`);
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
(0, node_test_1.test)("fechas: ida y vuelta es estable en cualquier huso", () => {
|
|
181
|
+
for (const tz of ZONAS) {
|
|
182
|
+
conZona(tz, (l) => {
|
|
183
|
+
let day = "2026-03-01";
|
|
184
|
+
for (let i = 0; i < 120; i++) {
|
|
185
|
+
const next = l.shiftDay(day, 1);
|
|
186
|
+
strict_1.default.equal(l.shiftDay(next, -1), day, `${day} en ${tz}`);
|
|
187
|
+
day = next;
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
(0, node_test_1.test)("fechas: el día local nunca es el de Greenwich al este ni al oeste", () => {
|
|
193
|
+
// A las 23:31 en Guayaquil ya es el día siguiente en UTC; a las 00:30 en
|
|
194
|
+
// Tokio todavía es el día anterior. Ambos casos rompían la pestaña "Hoy".
|
|
195
|
+
const nocheGuayaquil = new Date("2026-08-27T04:31:00Z");
|
|
196
|
+
conZona("America/Guayaquil", (l) => {
|
|
197
|
+
strict_1.default.equal(l.localToday(0, nocheGuayaquil), "2026-08-26");
|
|
198
|
+
});
|
|
199
|
+
const madrugadaTokio = new Date("2026-08-26T15:30:00Z"); // 00:30 del 27 en Tokio
|
|
200
|
+
conZona("Asia/Tokyo", (l) => {
|
|
201
|
+
strict_1.default.equal(l.localToday(0, madrugadaTokio), "2026-08-27");
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
(0, node_test_1.test)("fechas: el rango del mes empieza el día 1 en cualquier huso", () => {
|
|
205
|
+
for (const tz of ZONAS) {
|
|
206
|
+
conZona(tz, (l) => {
|
|
207
|
+
const r = l.periodRange("month", new Date("2026-08-27T04:31:00Z"));
|
|
208
|
+
strict_1.default.ok(r.from.endsWith("-01"), `${tz}: ${r.from}`);
|
|
209
|
+
strict_1.default.ok(r.from <= r.to, `${tz}: ${r.from} > ${r.to}`);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
(0, node_test_1.test)("interfaz: 'sin tarifa' solo se dice cuando de verdad falta", () => {
|
|
214
|
+
// El fallo real: un día con trabajo en euros y en dólares dejaba el total
|
|
215
|
+
// nulo, y la pantalla decía "sin tarifa definida" aunque todas estuvieran
|
|
216
|
+
// puestas. Mandaba a revisar una configuración correcta.
|
|
217
|
+
//
|
|
218
|
+
// Se comprueba sobre el propio código: la etiqueta debe depender de si hay
|
|
219
|
+
// subtotales, no de que exista un total único.
|
|
220
|
+
strict_1.default.match(appJs, /const totals = day\.totals \|\| \[\]/, "el día debe usar subtotales por moneda");
|
|
221
|
+
strict_1.default.match(appJs, /day\.missingRate \? "sin tarifa definida"/, "la etiqueta de falta de tarifa debe venir del servidor, no deducirse de un total nulo");
|
|
222
|
+
strict_1.default.ok(!/money \? "por facturar" : "sin tarifa definida"/.test(appJs), "no puede deducirse la falta de tarifa de que el total sea nulo");
|
|
223
|
+
});
|
|
224
|
+
// ── Mantenerse al día ────────────────────────────────────────────────────────
|
|
225
|
+
(0, node_test_1.test)("cada pestaña se puede refrescar", () => {
|
|
226
|
+
// Si se añade una pestaña y no se enseña a reloadView() a recargarla, el
|
|
227
|
+
// botón de actualizar deja de hacer nada en esa vista y no lo dice. Falla
|
|
228
|
+
// en silencio, que es justo el fallo que este cambio venía a arreglar.
|
|
229
|
+
const tabs = [...html.matchAll(/data-view="([a-z]+)"/g)].map((m) => m[1]);
|
|
230
|
+
strict_1.default.ok(tabs.length >= 4, `solo se encontraron ${tabs.length} pestañas`);
|
|
231
|
+
const reload = appJs.slice(appJs.indexOf("function reloadView"));
|
|
232
|
+
const body = reload.slice(0, reload.indexOf("\n}"));
|
|
233
|
+
for (const tab of tabs) {
|
|
234
|
+
strict_1.default.ok(body.includes(`"${tab}"`), `reloadView() no recarga la pestaña "${tab}"`);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
(0, node_test_1.test)("el aviso de import fallido existe y se puede ocultar", () => {
|
|
238
|
+
// El fallo real: `hidden` no ocultaba nada porque una clase con display lo
|
|
239
|
+
// pisaba. Aquí el aviso nace oculto, así que la regla tiene que aguantar.
|
|
240
|
+
strict_1.default.match(html, /id="sync-warn"[^>]*hidden/, "el aviso debe empezar oculto");
|
|
241
|
+
strict_1.default.ok(!/\.sync-warn\s*\{[^}]*display:/.test(css), ".sync-warn no debe declarar display: pisaría a [hidden]");
|
|
242
|
+
});
|
|
243
|
+
(0, node_test_1.test)("el botón de actualizar existe y está referenciado", () => {
|
|
244
|
+
for (const id of ["sync", "sync-label", "sync-warn"]) {
|
|
245
|
+
strict_1.default.ok(html.includes(`id="${id}"`), `falta id="${id}" en el HTML`);
|
|
246
|
+
strict_1.default.ok(appJs.includes(`#${id}`), `app.js no usa #${id}`);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
(0, node_test_1.test)("volver a la pestaña actualiza", () => {
|
|
250
|
+
// Es la vía que no exige acordarse de nada, y por eso la que importa.
|
|
251
|
+
strict_1.default.ok(appJs.includes("visibilitychange"), "sin esto hay que pulsar el botón a mano cada vez");
|
|
252
|
+
});
|
|
253
|
+
(0, node_test_1.test)("la animación del botón respeta prefers-reduced-motion", () => {
|
|
254
|
+
const idx = css.indexOf("prefers-reduced-motion");
|
|
255
|
+
strict_1.default.ok(idx > 0, "falta la regla de movimiento reducido");
|
|
256
|
+
strict_1.default.ok(css.slice(idx).includes("sync-dot"), "el punto que parpadea debe pararse con movimiento reducido");
|
|
257
|
+
});
|
|
258
|
+
// ── Vista de equipo ──────────────────────────────────────────────────────────
|
|
259
|
+
(0, node_test_1.test)("los KPIs del equipo nunca se apilan en una columna", () => {
|
|
260
|
+
// Cuatro cifras apiladas dejan de compararse entre sí y vuelven a ser datos
|
|
261
|
+
// sueltos, que es justo el problema que esta pantalla venía a resolver.
|
|
262
|
+
const block = css.slice(css.indexOf(".kpis-4"));
|
|
263
|
+
strict_1.default.match(block, /grid-template-columns:\s*repeat\(4/);
|
|
264
|
+
const mobile = block.slice(block.indexOf("max-width"), block.indexOf("max-width") + 160);
|
|
265
|
+
strict_1.default.match(mobile, /repeat\(2/, "en móvil deben quedar dos columnas, no una");
|
|
266
|
+
});
|
|
267
|
+
(0, node_test_1.test)("los segmentos de la barra de composición son bloques", () => {
|
|
268
|
+
// Ya pasó dos veces: un <span> sin display:block renderiza a altura cero y
|
|
269
|
+
// la barra desaparece sin que nada falle.
|
|
270
|
+
const seg = css.slice(css.indexOf(".seg "), css.indexOf(".seg ") + 90);
|
|
271
|
+
strict_1.default.match(seg, /display:\s*block/);
|
|
272
|
+
});
|
|
273
|
+
(0, node_test_1.test)("la tabla ancha hace scroll dentro de su caja, no en la página", () => {
|
|
274
|
+
const wrap = css.slice(css.indexOf(".tbl-wrap"), css.indexOf(".tbl-wrap") + 120);
|
|
275
|
+
strict_1.default.match(wrap, /overflow-x:\s*auto/);
|
|
276
|
+
});
|
|
277
|
+
(0, node_test_1.test)("las cifras de la tabla se alinean para poder compararse", () => {
|
|
278
|
+
const num = css.slice(css.indexOf(".tbl .num"), css.indexOf(".tbl .num") + 130);
|
|
279
|
+
strict_1.default.match(num, /text-align:\s*right/);
|
|
280
|
+
strict_1.default.match(num, /tabular-nums/, "sin cifras de ancho fijo las columnas bailan");
|
|
281
|
+
});
|
|
282
|
+
(0, node_test_1.test)("medido y estimado se distinguen a la vista", () => {
|
|
283
|
+
// Presentar horas estimadas con el mismo aspecto que las medidas es lo que
|
|
284
|
+
// convertiría esta pantalla en una herramienta para señalar a alguien.
|
|
285
|
+
strict_1.default.ok(appJs.includes('"medido"') && appJs.includes('"estimado"'));
|
|
286
|
+
strict_1.default.ok(css.includes(".prec-ok"), "falta el estilo que separa ambas");
|
|
287
|
+
});
|
|
288
|
+
(0, node_test_1.test)("la tabla de personas no se ordena por horas en el cliente", () => {
|
|
289
|
+
// El orden lo fija el servidor por actividad reciente. Si la web reordenara
|
|
290
|
+
// por horas, volvería a ser un ranking de rendimiento.
|
|
291
|
+
const render = appJs.slice(appJs.indexOf("function renderTeam"));
|
|
292
|
+
const body = render.slice(0, render.indexOf("\n}"));
|
|
293
|
+
strict_1.default.ok(!/\.sort\(/.test(body), "renderTeam() no debe reordenar a las personas");
|
|
294
|
+
});
|
|
295
|
+
(0, node_test_1.test)("la pestaña Equipo está enganchada a la navegación", () => {
|
|
296
|
+
strict_1.default.ok(html.includes('data-view="team"'));
|
|
297
|
+
strict_1.default.ok(html.includes('id="view-team"'));
|
|
298
|
+
strict_1.default.ok(appJs.includes('name === "team"'), "showView() no la carga");
|
|
299
|
+
});
|
|
300
|
+
(0, node_test_1.test)("la vista de equipo avisa de que sus cifras no suman con los totales", () => {
|
|
301
|
+
// Los totales cuentan tu trabajo imputado; la tabla de personas incluye horas
|
|
302
|
+
// estimadas de gente que no usa Estela. Sin decirlo, el primer lector atento
|
|
303
|
+
// ve que no cuadra y deja de creerse el resto de la pantalla.
|
|
304
|
+
strict_1.default.match(appJs, /no entran<\/b> en los totales/);
|
|
305
|
+
});
|
|
306
|
+
(0, node_test_1.test)("la vista de equipo ensancha la página", () => {
|
|
307
|
+
// 720px es el ancho para leer un texto. Una tabla de seis columnas ahí no
|
|
308
|
+
// cabe, y era la queja concreta: que ocupe más pantalla.
|
|
309
|
+
strict_1.default.match(css, /main\.wide\s*\{[^}]*max-width:\s*11\d\dpx/);
|
|
310
|
+
strict_1.default.ok(appJs.includes('classList.toggle("wide"'), "nadie activa la clase");
|
|
311
|
+
});
|
|
312
|
+
//# sourceMappingURL=web-ui.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-ui.test.js","sourceRoot":"","sources":["../src/web-ui.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./money.js"), exports);
|
|
18
|
+
__exportStar(require("./types.js"), exports);
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6CAA2B;AAC3B,6CAA2B"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dinero en unidades menores enteras. Nunca coma flotante.
|
|
3
|
+
*
|
|
4
|
+
* Un `0.1 + 0.2 !== 0.3` en una factura es un cliente preguntando por qué el
|
|
5
|
+
* total no cuadra. Todo importe se guarda como entero:
|
|
6
|
+
* - Importes facturables -> céntimos (EUR/USD/GBP tienen 2 decimales).
|
|
7
|
+
* - Coste de IA -> micro-dólares (1e-6 USD).
|
|
8
|
+
*
|
|
9
|
+
* El coste de IA necesita micro-unidades porque una lectura de caché puede
|
|
10
|
+
* valer $0.0007: en céntimos se redondearía a cero y el total del mes se
|
|
11
|
+
* perdería entero.
|
|
12
|
+
*/
|
|
13
|
+
export type Currency = "EUR" | "USD" | "GBP" | "MXN" | "COP" | "ARS" | "CLP" | "PEN" | "BRL";
|
|
14
|
+
/** Importe facturable. `amount` son unidades menores enteras de `currency`. */
|
|
15
|
+
export interface Money {
|
|
16
|
+
readonly amount: number;
|
|
17
|
+
readonly currency: Currency;
|
|
18
|
+
}
|
|
19
|
+
/** Coste de IA. Siempre USD: es la moneda en la que factura el proveedor. */
|
|
20
|
+
export interface AiCost {
|
|
21
|
+
readonly microUsd: number;
|
|
22
|
+
}
|
|
23
|
+
export declare const ZERO_AI_COST: AiCost;
|
|
24
|
+
export declare function money(amount: number, currency: Currency): Money;
|
|
25
|
+
/** Convierte "13" o "13.50" (entrada humana) a unidades menores. */
|
|
26
|
+
export declare function parseMoney(input: string, currency: Currency): Money;
|
|
27
|
+
export declare function addMoney(a: Money, b: Money): Money;
|
|
28
|
+
export declare function sumMoney(items: readonly Money[], currency: Currency): Money;
|
|
29
|
+
export declare function addAiCost(a: AiCost, b: AiCost): AiCost;
|
|
30
|
+
export declare function sumAiCost(items: readonly AiCost[]): AiCost;
|
|
31
|
+
/**
|
|
32
|
+
* Convierte entre monedas con un tipo de cambio **explícito y fechado**.
|
|
33
|
+
*
|
|
34
|
+
* No hay conversión implícita en ninguna parte del sistema: una factura emitida
|
|
35
|
+
* en marzo debe seguir cuadrando en noviembre, así que el tipo usado se guarda
|
|
36
|
+
* junto a la factura y jamás se recalcula con el de hoy.
|
|
37
|
+
*
|
|
38
|
+
* @param rate unidades de `to` por 1 unidad de `from` (ej. 0.92 para USD->EUR)
|
|
39
|
+
*/
|
|
40
|
+
export declare function convertMoney(from: Money, to: Currency, rate: number): Money;
|
|
41
|
+
/** Coste de IA (micro-USD) a un importe facturable en la moneda indicada. */
|
|
42
|
+
export declare function aiCostToMoney(cost: AiCost, to: Currency, usdRate: number): Money;
|
|
43
|
+
/**
|
|
44
|
+
* Importe facturable de una duración a una tarifa por hora.
|
|
45
|
+
*
|
|
46
|
+
* Redondeo half-up sobre la unidad menor. El redondeo del *tiempo* (facturar a
|
|
47
|
+
* bloques de 15 min, por ejemplo) es una decisión distinta y ocurre antes, en
|
|
48
|
+
* `roundSeconds` — mezclar ambos redondeos es como se generan los descuadres.
|
|
49
|
+
*/
|
|
50
|
+
export declare function billableAmount(seconds: number, hourlyRate: Money): Money;
|
|
51
|
+
/**
|
|
52
|
+
* Redondea una duración al incremento de facturación pactado.
|
|
53
|
+
* `incrementMinutes = 0` factura al segundo exacto.
|
|
54
|
+
*/
|
|
55
|
+
export declare function roundSeconds(seconds: number, incrementMinutes: number): number;
|
|
56
|
+
export declare function formatMoney(m: Money, locale?: string): string;
|
|
57
|
+
/** El coste de IA se muestra con 2 decimales, o 4 cuando es menor de un céntimo. */
|
|
58
|
+
export declare function formatAiCost(cost: AiCost): string;
|
|
59
|
+
/**
|
|
60
|
+
* Fecha local en formato YYYY-MM-DD.
|
|
61
|
+
*
|
|
62
|
+
* `toISOString().slice(0,10)` da la fecha **UTC**, y eso parte la jornada en dos
|
|
63
|
+
* para cualquiera al oeste de Greenwich: en Ecuador (UTC−5) todo lo trabajado
|
|
64
|
+
* después de las 19:00 cae al día UTC siguiente. Una jornada de tarde-noche
|
|
65
|
+
* aparecería repartida entre dos días y el parte diario dejaría de cuadrar.
|
|
66
|
+
*
|
|
67
|
+
* El día de trabajo es el del reloj de quien trabaja, no el de Greenwich.
|
|
68
|
+
*/
|
|
69
|
+
export declare function localDate(date: Date): string;
|
|
70
|
+
export declare function formatDuration(seconds: number): string;
|
|
71
|
+
//# sourceMappingURL=money.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../src/money.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AAO7F,+EAA+E;AAC/E,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;CAC7B;AAED,6EAA6E;AAC7E,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,YAAY,EAAE,MAAwB,CAAC;AAEpD,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAK/D;AAED,oEAAoE;AACpE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAenE;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,GAAG,KAAK,CAGlD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,KAAK,EAAE,EAAE,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAS3E;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAE1D;AAQD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,KAAK,CAQ3E;AAED,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,CAEhF;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,GAAG,KAAK,CAGxE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAI9E;AAMD,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,SAAU,GAAG,MAAM,CAQ9D;AAED,oFAAoF;AACpF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIjD;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAG5C;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAQtD"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Dinero en unidades menores enteras. Nunca coma flotante.
|
|
4
|
+
*
|
|
5
|
+
* Un `0.1 + 0.2 !== 0.3` en una factura es un cliente preguntando por qué el
|
|
6
|
+
* total no cuadra. Todo importe se guarda como entero:
|
|
7
|
+
* - Importes facturables -> céntimos (EUR/USD/GBP tienen 2 decimales).
|
|
8
|
+
* - Coste de IA -> micro-dólares (1e-6 USD).
|
|
9
|
+
*
|
|
10
|
+
* El coste de IA necesita micro-unidades porque una lectura de caché puede
|
|
11
|
+
* valer $0.0007: en céntimos se redondearía a cero y el total del mes se
|
|
12
|
+
* perdería entero.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.ZERO_AI_COST = void 0;
|
|
16
|
+
exports.money = money;
|
|
17
|
+
exports.parseMoney = parseMoney;
|
|
18
|
+
exports.addMoney = addMoney;
|
|
19
|
+
exports.sumMoney = sumMoney;
|
|
20
|
+
exports.addAiCost = addAiCost;
|
|
21
|
+
exports.sumAiCost = sumAiCost;
|
|
22
|
+
exports.convertMoney = convertMoney;
|
|
23
|
+
exports.aiCostToMoney = aiCostToMoney;
|
|
24
|
+
exports.billableAmount = billableAmount;
|
|
25
|
+
exports.roundSeconds = roundSeconds;
|
|
26
|
+
exports.formatMoney = formatMoney;
|
|
27
|
+
exports.formatAiCost = formatAiCost;
|
|
28
|
+
exports.localDate = localDate;
|
|
29
|
+
exports.formatDuration = formatDuration;
|
|
30
|
+
/** Decimales de la unidad menor por moneda (ISO 4217). */
|
|
31
|
+
const MINOR_UNIT_DIGITS = {
|
|
32
|
+
EUR: 2, USD: 2, GBP: 2, MXN: 2, COP: 2, ARS: 2, CLP: 0, PEN: 2, BRL: 2,
|
|
33
|
+
};
|
|
34
|
+
exports.ZERO_AI_COST = { microUsd: 0 };
|
|
35
|
+
function money(amount, currency) {
|
|
36
|
+
if (!Number.isInteger(amount)) {
|
|
37
|
+
throw new TypeError(`Money.amount debe ser entero (unidades menores), recibido ${amount}`);
|
|
38
|
+
}
|
|
39
|
+
return { amount, currency };
|
|
40
|
+
}
|
|
41
|
+
/** Convierte "13" o "13.50" (entrada humana) a unidades menores. */
|
|
42
|
+
function parseMoney(input, currency) {
|
|
43
|
+
const trimmed = input.trim().replace(",", ".");
|
|
44
|
+
if (!/^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
45
|
+
throw new TypeError(`Importe no reconocido: "${input}"`);
|
|
46
|
+
}
|
|
47
|
+
const digits = MINOR_UNIT_DIGITS[currency];
|
|
48
|
+
const [whole = "0", frac = ""] = trimmed.split(".");
|
|
49
|
+
const negative = whole.startsWith("-");
|
|
50
|
+
const wholeDigits = negative ? whole.slice(1) : whole;
|
|
51
|
+
const padded = (frac + "0".repeat(digits)).slice(0, digits);
|
|
52
|
+
// Redondeo half-up sobre el primer decimal descartado.
|
|
53
|
+
const nextDigit = frac.length > digits ? Number(frac[digits]) : 0;
|
|
54
|
+
let minor = Number(wholeDigits) * 10 ** digits + Number(padded || "0");
|
|
55
|
+
if (nextDigit >= 5)
|
|
56
|
+
minor += 1;
|
|
57
|
+
return { amount: negative ? -minor : minor, currency };
|
|
58
|
+
}
|
|
59
|
+
function addMoney(a, b) {
|
|
60
|
+
assertSameCurrency(a, b);
|
|
61
|
+
return { amount: a.amount + b.amount, currency: a.currency };
|
|
62
|
+
}
|
|
63
|
+
function sumMoney(items, currency) {
|
|
64
|
+
let total = 0;
|
|
65
|
+
for (const m of items) {
|
|
66
|
+
if (m.currency !== currency) {
|
|
67
|
+
throw new TypeError(`No se pueden sumar ${m.currency} y ${currency} sin tipo de cambio explícito`);
|
|
68
|
+
}
|
|
69
|
+
total += m.amount;
|
|
70
|
+
}
|
|
71
|
+
return { amount: total, currency };
|
|
72
|
+
}
|
|
73
|
+
function addAiCost(a, b) {
|
|
74
|
+
return { microUsd: a.microUsd + b.microUsd };
|
|
75
|
+
}
|
|
76
|
+
function sumAiCost(items) {
|
|
77
|
+
return { microUsd: items.reduce((acc, c) => acc + c.microUsd, 0) };
|
|
78
|
+
}
|
|
79
|
+
function assertSameCurrency(a, b) {
|
|
80
|
+
if (a.currency !== b.currency) {
|
|
81
|
+
throw new TypeError(`Monedas distintas: ${a.currency} vs ${b.currency}. Usa convertMoney con un tipo de cambio explícito.`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Convierte entre monedas con un tipo de cambio **explícito y fechado**.
|
|
86
|
+
*
|
|
87
|
+
* No hay conversión implícita en ninguna parte del sistema: una factura emitida
|
|
88
|
+
* en marzo debe seguir cuadrando en noviembre, así que el tipo usado se guarda
|
|
89
|
+
* junto a la factura y jamás se recalcula con el de hoy.
|
|
90
|
+
*
|
|
91
|
+
* @param rate unidades de `to` por 1 unidad de `from` (ej. 0.92 para USD->EUR)
|
|
92
|
+
*/
|
|
93
|
+
function convertMoney(from, to, rate) {
|
|
94
|
+
if (!(rate > 0) || !Number.isFinite(rate)) {
|
|
95
|
+
throw new TypeError(`Tipo de cambio inválido: ${rate}`);
|
|
96
|
+
}
|
|
97
|
+
const fromDigits = MINOR_UNIT_DIGITS[from.currency];
|
|
98
|
+
const toDigits = MINOR_UNIT_DIGITS[to];
|
|
99
|
+
const major = from.amount / 10 ** fromDigits;
|
|
100
|
+
return { amount: Math.round(major * rate * 10 ** toDigits), currency: to };
|
|
101
|
+
}
|
|
102
|
+
/** Coste de IA (micro-USD) a un importe facturable en la moneda indicada. */
|
|
103
|
+
function aiCostToMoney(cost, to, usdRate) {
|
|
104
|
+
return convertMoney({ amount: Math.round(cost.microUsd / 10_000), currency: "USD" }, to, usdRate);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Importe facturable de una duración a una tarifa por hora.
|
|
108
|
+
*
|
|
109
|
+
* Redondeo half-up sobre la unidad menor. El redondeo del *tiempo* (facturar a
|
|
110
|
+
* bloques de 15 min, por ejemplo) es una decisión distinta y ocurre antes, en
|
|
111
|
+
* `roundSeconds` — mezclar ambos redondeos es como se generan los descuadres.
|
|
112
|
+
*/
|
|
113
|
+
function billableAmount(seconds, hourlyRate) {
|
|
114
|
+
if (seconds < 0)
|
|
115
|
+
throw new RangeError(`Duración negativa: ${seconds}`);
|
|
116
|
+
return { amount: Math.round((hourlyRate.amount * seconds) / 3600), currency: hourlyRate.currency };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Redondea una duración al incremento de facturación pactado.
|
|
120
|
+
* `incrementMinutes = 0` factura al segundo exacto.
|
|
121
|
+
*/
|
|
122
|
+
function roundSeconds(seconds, incrementMinutes) {
|
|
123
|
+
if (incrementMinutes <= 0)
|
|
124
|
+
return seconds;
|
|
125
|
+
const step = incrementMinutes * 60;
|
|
126
|
+
return Math.round(seconds / step) * step;
|
|
127
|
+
}
|
|
128
|
+
const SYMBOLS = {
|
|
129
|
+
EUR: "€", USD: "$", GBP: "£", MXN: "$", BRL: "R$",
|
|
130
|
+
};
|
|
131
|
+
function formatMoney(m, locale = "es-EC") {
|
|
132
|
+
const digits = MINOR_UNIT_DIGITS[m.currency];
|
|
133
|
+
const value = (m.amount / 10 ** digits).toLocaleString(locale, {
|
|
134
|
+
minimumFractionDigits: digits,
|
|
135
|
+
maximumFractionDigits: digits,
|
|
136
|
+
});
|
|
137
|
+
const symbol = SYMBOLS[m.currency];
|
|
138
|
+
return symbol ? `${symbol}${value}` : `${value} ${m.currency}`;
|
|
139
|
+
}
|
|
140
|
+
/** El coste de IA se muestra con 2 decimales, o 4 cuando es menor de un céntimo. */
|
|
141
|
+
function formatAiCost(cost) {
|
|
142
|
+
const usd = cost.microUsd / 1_000_000;
|
|
143
|
+
if (usd !== 0 && Math.abs(usd) < 0.01)
|
|
144
|
+
return `$${usd.toFixed(4)}`;
|
|
145
|
+
return `$${usd.toFixed(2)}`;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Fecha local en formato YYYY-MM-DD.
|
|
149
|
+
*
|
|
150
|
+
* `toISOString().slice(0,10)` da la fecha **UTC**, y eso parte la jornada en dos
|
|
151
|
+
* para cualquiera al oeste de Greenwich: en Ecuador (UTC−5) todo lo trabajado
|
|
152
|
+
* después de las 19:00 cae al día UTC siguiente. Una jornada de tarde-noche
|
|
153
|
+
* aparecería repartida entre dos días y el parte diario dejaría de cuadrar.
|
|
154
|
+
*
|
|
155
|
+
* El día de trabajo es el del reloj de quien trabaja, no el de Greenwich.
|
|
156
|
+
*/
|
|
157
|
+
function localDate(date) {
|
|
158
|
+
const local = new Date(date.getTime() - date.getTimezoneOffset() * 60_000);
|
|
159
|
+
return local.toISOString().slice(0, 10);
|
|
160
|
+
}
|
|
161
|
+
function formatDuration(seconds) {
|
|
162
|
+
// Redondear a minutos ANTES de separar las horas. Al revés, 2h 59m 50s da
|
|
163
|
+
// "2h 60m": los minutos redondean a 60 y las horas ya se calcularon aparte.
|
|
164
|
+
const totalMinutes = Math.round(seconds / 60);
|
|
165
|
+
const h = Math.floor(totalMinutes / 60);
|
|
166
|
+
const m = totalMinutes % 60;
|
|
167
|
+
if (h === 0)
|
|
168
|
+
return `${m}m`;
|
|
169
|
+
return m === 0 ? `${h}h` : `${h}h ${String(m).padStart(2, "0")}m`;
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=money.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money.js","sourceRoot":"","sources":["../src/money.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;AAsBH,sBAKC;AAGD,gCAeC;AAED,4BAGC;AAED,4BASC;AAED,8BAEC;AAED,8BAEC;AAiBD,oCAQC;AAGD,sCAEC;AASD,wCAGC;AAMD,oCAIC;AAMD,kCAQC;AAGD,oCAIC;AAYD,8BAGC;AAED,wCAQC;AAnKD,0DAA0D;AAC1D,MAAM,iBAAiB,GAA6B;IAClD,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC;CACvE,CAAC;AAaW,QAAA,YAAY,GAAW,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;AAEpD,SAAgB,KAAK,CAAC,MAAc,EAAE,QAAkB;IACtD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,SAAS,CAAC,6DAA6D,MAAM,EAAE,CAAC,CAAC;IAC7F,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAC9B,CAAC;AAED,oEAAoE;AACpE,SAAgB,UAAU,CAAC,KAAa,EAAE,QAAkB;IAC1D,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC/C,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,SAAS,CAAC,2BAA2B,KAAK,GAAG,CAAC,CAAC;IAC3D,CAAC;IACD,MAAM,MAAM,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,CAAC,KAAK,GAAG,GAAG,EAAE,IAAI,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,WAAW,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACtD,MAAM,MAAM,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IAC5D,uDAAuD;IACvD,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClE,IAAI,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC;IACvE,IAAI,SAAS,IAAI,CAAC;QAAE,KAAK,IAAI,CAAC,CAAC;IAC/B,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC;AACzD,CAAC;AAED,SAAgB,QAAQ,CAAC,CAAQ,EAAE,CAAQ;IACzC,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACzB,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;AAC/D,CAAC;AAED,SAAgB,QAAQ,CAAC,KAAuB,EAAE,QAAkB;IAClE,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC5B,MAAM,IAAI,SAAS,CAAC,sBAAsB,CAAC,CAAC,QAAQ,MAAM,QAAQ,+BAA+B,CAAC,CAAC;QACrG,CAAC;QACD,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC;IACpB,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AACrC,CAAC;AAED,SAAgB,SAAS,CAAC,CAAS,EAAE,CAAS;IAC5C,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC;AAC/C,CAAC;AAED,SAAgB,SAAS,CAAC,KAAwB;IAChD,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC;AACrE,CAAC;AAED,SAAS,kBAAkB,CAAC,CAAQ,EAAE,CAAQ;IAC5C,IAAI,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,SAAS,CAAC,sBAAsB,CAAC,CAAC,QAAQ,OAAO,CAAC,CAAC,QAAQ,qDAAqD,CAAC,CAAC;IAC9H,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,YAAY,CAAC,IAAW,EAAE,EAAY,EAAE,IAAY;IAClE,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,SAAS,CAAC,4BAA4B,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,EAAE,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,UAAU,CAAC;IAC7C,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,GAAG,EAAE,IAAI,QAAQ,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;AAC7E,CAAC;AAED,6EAA6E;AAC7E,SAAgB,aAAa,CAAC,IAAY,EAAE,EAAY,EAAE,OAAe;IACvE,OAAO,YAAY,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;AACpG,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,cAAc,CAAC,OAAe,EAAE,UAAiB;IAC/D,IAAI,OAAO,GAAG,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,sBAAsB,OAAO,EAAE,CAAC,CAAC;IACvE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,MAAM,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC;AACrG,CAAC;AAED;;;GAGG;AACH,SAAgB,YAAY,CAAC,OAAe,EAAE,gBAAwB;IACpE,IAAI,gBAAgB,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC;IAC1C,MAAM,IAAI,GAAG,gBAAgB,GAAG,EAAE,CAAC;IACnC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;AAC3C,CAAC;AAED,MAAM,OAAO,GAAsC;IACjD,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI;CAClD,CAAC;AAEF,SAAgB,WAAW,CAAC,CAAQ,EAAE,MAAM,GAAG,OAAO;IACpD,MAAM,MAAM,GAAG,iBAAiB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,EAAE,IAAI,MAAM,CAAC,CAAC,cAAc,CAAC,MAAM,EAAE;QAC7D,qBAAqB,EAAE,MAAM;QAC7B,qBAAqB,EAAE,MAAM;KAC9B,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACnC,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;AACjE,CAAC;AAED,oFAAoF;AACpF,SAAgB,YAAY,CAAC,IAAY;IACvC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;IACtC,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI;QAAE,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IACnE,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9B,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,SAAS,CAAC,IAAU;IAClC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,iBAAiB,EAAE,GAAG,MAAM,CAAC,CAAC;IAC3E,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC1C,CAAC;AAED,SAAgB,cAAc,CAAC,OAAe;IAC5C,0EAA0E;IAC1E,4EAA4E;IAC5E,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IAC9C,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,EAAE,CAAC,CAAC;IACxC,MAAM,CAAC,GAAG,YAAY,GAAG,EAAE,CAAC;IAC5B,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC,GAAG,CAAC;IAC5B,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC;AACpE,CAAC"}
|