estela 0.1.40 → 0.2.1

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.
Files changed (293) hide show
  1. package/README.md +46 -2
  2. package/dist/billing/assign.d.ts +45 -0
  3. package/dist/billing/assign.js +159 -0
  4. package/dist/bin.js +23 -1
  5. package/dist/cli.js +220 -57
  6. package/dist/db/store.d.ts +2 -0
  7. package/dist/db/store.js +24 -1
  8. package/dist/demo.js +1 -1
  9. package/dist/i18n/en.js +39 -0
  10. package/dist/metrics/quota.d.ts +44 -4
  11. package/dist/metrics/quota.js +60 -14
  12. package/dist/project-of.d.ts +52 -0
  13. package/dist/project-of.js +124 -0
  14. package/dist/quota.d.ts +10 -1
  15. package/dist/quota.js +14 -20
  16. package/dist/server.js +10 -27
  17. package/dist/statusline-cache.d.ts +35 -0
  18. package/dist/statusline-cache.js +57 -0
  19. package/dist/statusline-install.d.ts +52 -0
  20. package/dist/statusline-install.js +115 -0
  21. package/dist/statusline-run.d.ts +8 -0
  22. package/dist/statusline-run.js +256 -0
  23. package/dist/statusline.d.ts +71 -0
  24. package/dist/statusline.js +152 -0
  25. package/dist/watchers/claude.d.ts +11 -1
  26. package/dist/watchers/claude.js +32 -15
  27. package/package.json +3 -1
  28. package/dist/billing/amortize.d.ts.map +0 -1
  29. package/dist/billing/amortize.js.map +0 -1
  30. package/dist/billing/amortize.test.d.ts +0 -2
  31. package/dist/billing/amortize.test.d.ts.map +0 -1
  32. package/dist/billing/amortize.test.js +0 -121
  33. package/dist/billing/amortize.test.js.map +0 -1
  34. package/dist/billing/format.test.d.ts +0 -2
  35. package/dist/billing/format.test.d.ts.map +0 -1
  36. package/dist/billing/format.test.js +0 -110
  37. package/dist/billing/format.test.js.map +0 -1
  38. package/dist/billing/invoice.d.ts.map +0 -1
  39. package/dist/billing/invoice.js.map +0 -1
  40. package/dist/billing/invoice.test.d.ts +0 -2
  41. package/dist/billing/invoice.test.d.ts.map +0 -1
  42. package/dist/billing/invoice.test.js +0 -200
  43. package/dist/billing/invoice.test.js.map +0 -1
  44. package/dist/billing/localize.d.ts.map +0 -1
  45. package/dist/billing/localize.js.map +0 -1
  46. package/dist/billing/localize.test.d.ts +0 -2
  47. package/dist/billing/localize.test.d.ts.map +0 -1
  48. package/dist/billing/localize.test.js +0 -144
  49. package/dist/billing/localize.test.js.map +0 -1
  50. package/dist/billing/sessionize.d.ts.map +0 -1
  51. package/dist/billing/sessionize.js.map +0 -1
  52. package/dist/billing/sessionize.test.d.ts +0 -2
  53. package/dist/billing/sessionize.test.d.ts.map +0 -1
  54. package/dist/billing/sessionize.test.js +0 -187
  55. package/dist/billing/sessionize.test.js.map +0 -1
  56. package/dist/billing.d.ts.map +0 -1
  57. package/dist/billing.js.map +0 -1
  58. package/dist/bin.d.ts.map +0 -1
  59. package/dist/bin.js.map +0 -1
  60. package/dist/cli-badge.test.d.ts +0 -2
  61. package/dist/cli-badge.test.d.ts.map +0 -1
  62. package/dist/cli-badge.test.js +0 -118
  63. package/dist/cli-badge.test.js.map +0 -1
  64. package/dist/cli-idioma.test.d.ts +0 -2
  65. package/dist/cli-idioma.test.d.ts.map +0 -1
  66. package/dist/cli-idioma.test.js +0 -190
  67. package/dist/cli-idioma.test.js.map +0 -1
  68. package/dist/cli-quota.test.d.ts +0 -2
  69. package/dist/cli-quota.test.d.ts.map +0 -1
  70. package/dist/cli-quota.test.js +0 -216
  71. package/dist/cli-quota.test.js.map +0 -1
  72. package/dist/cli.d.ts.map +0 -1
  73. package/dist/cli.js.map +0 -1
  74. package/dist/cloud/auth.d.ts.map +0 -1
  75. package/dist/cloud/auth.js.map +0 -1
  76. package/dist/cloud/client.d.ts.map +0 -1
  77. package/dist/cloud/client.js.map +0 -1
  78. package/dist/cloud/errors.test.d.ts +0 -2
  79. package/dist/cloud/errors.test.d.ts.map +0 -1
  80. package/dist/cloud/errors.test.js +0 -34
  81. package/dist/cloud/errors.test.js.map +0 -1
  82. package/dist/db/schema.d.ts.map +0 -1
  83. package/dist/db/schema.js.map +0 -1
  84. package/dist/db/schema.test.d.ts +0 -2
  85. package/dist/db/schema.test.d.ts.map +0 -1
  86. package/dist/db/schema.test.js +0 -825
  87. package/dist/db/schema.test.js.map +0 -1
  88. package/dist/db/store.d.ts.map +0 -1
  89. package/dist/db/store.js.map +0 -1
  90. package/dist/demo.d.ts.map +0 -1
  91. package/dist/demo.js.map +0 -1
  92. package/dist/demo.test.d.ts +0 -2
  93. package/dist/demo.test.d.ts.map +0 -1
  94. package/dist/demo.test.js +0 -148
  95. package/dist/demo.test.js.map +0 -1
  96. package/dist/doctor.d.ts.map +0 -1
  97. package/dist/doctor.js.map +0 -1
  98. package/dist/doctor.test.d.ts +0 -2
  99. package/dist/doctor.test.d.ts.map +0 -1
  100. package/dist/doctor.test.js +0 -167
  101. package/dist/doctor.test.js.map +0 -1
  102. package/dist/export/badge.d.ts.map +0 -1
  103. package/dist/export/badge.js.map +0 -1
  104. package/dist/export/badge.test.d.ts +0 -2
  105. package/dist/export/badge.test.d.ts.map +0 -1
  106. package/dist/export/badge.test.js +0 -32
  107. package/dist/export/badge.test.js.map +0 -1
  108. package/dist/export/clockify.d.ts.map +0 -1
  109. package/dist/export/clockify.js.map +0 -1
  110. package/dist/export/clockify.test.d.ts +0 -2
  111. package/dist/export/clockify.test.d.ts.map +0 -1
  112. package/dist/export/clockify.test.js +0 -51
  113. package/dist/export/clockify.test.js.map +0 -1
  114. package/dist/export/csv.d.ts.map +0 -1
  115. package/dist/export/csv.js.map +0 -1
  116. package/dist/export/dates.d.ts.map +0 -1
  117. package/dist/export/dates.js.map +0 -1
  118. package/dist/export/idioma.test.d.ts +0 -2
  119. package/dist/export/idioma.test.d.ts.map +0 -1
  120. package/dist/export/idioma.test.js +0 -255
  121. package/dist/export/idioma.test.js.map +0 -1
  122. package/dist/export/invoice-pdf.d.ts.map +0 -1
  123. package/dist/export/invoice-pdf.js.map +0 -1
  124. package/dist/export/panel.d.ts.map +0 -1
  125. package/dist/export/panel.js.map +0 -1
  126. package/dist/export/panel.test.d.ts +0 -2
  127. package/dist/export/panel.test.d.ts.map +0 -1
  128. package/dist/export/panel.test.js +0 -386
  129. package/dist/export/panel.test.js.map +0 -1
  130. package/dist/export/pdf.d.ts.map +0 -1
  131. package/dist/export/pdf.js.map +0 -1
  132. package/dist/export/pdf.test.d.ts +0 -2
  133. package/dist/export/pdf.test.d.ts.map +0 -1
  134. package/dist/export/pdf.test.js +0 -172
  135. package/dist/export/pdf.test.js.map +0 -1
  136. package/dist/export/pixelfont.d.ts.map +0 -1
  137. package/dist/export/pixelfont.js.map +0 -1
  138. package/dist/export/png.d.ts.map +0 -1
  139. package/dist/export/png.js.map +0 -1
  140. package/dist/export/receipt.d.ts.map +0 -1
  141. package/dist/export/receipt.js.map +0 -1
  142. package/dist/export/receipt.test.d.ts +0 -2
  143. package/dist/export/receipt.test.d.ts.map +0 -1
  144. package/dist/export/receipt.test.js +0 -207
  145. package/dist/export/receipt.test.js.map +0 -1
  146. package/dist/export/share.d.ts.map +0 -1
  147. package/dist/export/share.js.map +0 -1
  148. package/dist/export/share.test.d.ts +0 -2
  149. package/dist/export/share.test.d.ts.map +0 -1
  150. package/dist/export/share.test.js +0 -116
  151. package/dist/export/share.test.js.map +0 -1
  152. package/dist/i18n/en.d.ts.map +0 -1
  153. package/dist/i18n/en.js.map +0 -1
  154. package/dist/i18n/i18n.test.d.ts +0 -2
  155. package/dist/i18n/i18n.test.d.ts.map +0 -1
  156. package/dist/i18n/i18n.test.js +0 -226
  157. package/dist/i18n/i18n.test.js.map +0 -1
  158. package/dist/i18n/index.d.ts.map +0 -1
  159. package/dist/i18n/index.js.map +0 -1
  160. package/dist/i18n/labels.d.ts.map +0 -1
  161. package/dist/i18n/labels.js.map +0 -1
  162. package/dist/metrics/index.d.ts.map +0 -1
  163. package/dist/metrics/index.js.map +0 -1
  164. package/dist/metrics/metrics.test.d.ts +0 -2
  165. package/dist/metrics/metrics.test.d.ts.map +0 -1
  166. package/dist/metrics/metrics.test.js +0 -162
  167. package/dist/metrics/metrics.test.js.map +0 -1
  168. package/dist/metrics/people.d.ts.map +0 -1
  169. package/dist/metrics/people.js.map +0 -1
  170. package/dist/metrics/people.test.d.ts +0 -2
  171. package/dist/metrics/people.test.d.ts.map +0 -1
  172. package/dist/metrics/people.test.js +0 -92
  173. package/dist/metrics/people.test.js.map +0 -1
  174. package/dist/metrics/quota.d.ts.map +0 -1
  175. package/dist/metrics/quota.js.map +0 -1
  176. package/dist/metrics/quota.test.d.ts +0 -2
  177. package/dist/metrics/quota.test.d.ts.map +0 -1
  178. package/dist/metrics/quota.test.js +0 -262
  179. package/dist/metrics/quota.test.js.map +0 -1
  180. package/dist/metrics/team.d.ts.map +0 -1
  181. package/dist/metrics/team.js.map +0 -1
  182. package/dist/metrics/team.test.d.ts +0 -2
  183. package/dist/metrics/team.test.d.ts.map +0 -1
  184. package/dist/metrics/team.test.js +0 -90
  185. package/dist/metrics/team.test.js.map +0 -1
  186. package/dist/node-version.d.ts.map +0 -1
  187. package/dist/node-version.js.map +0 -1
  188. package/dist/node-version.test.d.ts +0 -2
  189. package/dist/node-version.test.d.ts.map +0 -1
  190. package/dist/node-version.test.js +0 -53
  191. package/dist/node-version.test.js.map +0 -1
  192. package/dist/paths.d.ts.map +0 -1
  193. package/dist/paths.js.map +0 -1
  194. package/dist/paths.test.d.ts +0 -2
  195. package/dist/paths.test.d.ts.map +0 -1
  196. package/dist/paths.test.js +0 -32
  197. package/dist/paths.test.js.map +0 -1
  198. package/dist/pricing/catalog.d.ts.map +0 -1
  199. package/dist/pricing/catalog.js.map +0 -1
  200. package/dist/pricing/cost.d.ts.map +0 -1
  201. package/dist/pricing/cost.js.map +0 -1
  202. package/dist/pricing/cost.test.d.ts +0 -2
  203. package/dist/pricing/cost.test.d.ts.map +0 -1
  204. package/dist/pricing/cost.test.js +0 -100
  205. package/dist/pricing/cost.test.js.map +0 -1
  206. package/dist/publish.d.ts.map +0 -1
  207. package/dist/publish.js.map +0 -1
  208. package/dist/publish.test.d.ts +0 -2
  209. package/dist/publish.test.d.ts.map +0 -1
  210. package/dist/publish.test.js +0 -113
  211. package/dist/publish.test.js.map +0 -1
  212. package/dist/quota.d.ts.map +0 -1
  213. package/dist/quota.js.map +0 -1
  214. package/dist/server.d.ts.map +0 -1
  215. package/dist/server.js.map +0 -1
  216. package/dist/server.test.d.ts +0 -2
  217. package/dist/server.test.d.ts.map +0 -1
  218. package/dist/server.test.js +0 -402
  219. package/dist/server.test.js.map +0 -1
  220. package/dist/setup.d.ts.map +0 -1
  221. package/dist/setup.js.map +0 -1
  222. package/dist/setup.test.d.ts +0 -2
  223. package/dist/setup.test.d.ts.map +0 -1
  224. package/dist/setup.test.js +0 -144
  225. package/dist/setup.test.js.map +0 -1
  226. package/dist/sync/team.d.ts.map +0 -1
  227. package/dist/sync/team.js.map +0 -1
  228. package/dist/sync/team.test.d.ts +0 -2
  229. package/dist/sync/team.test.d.ts.map +0 -1
  230. package/dist/sync/team.test.js +0 -102
  231. package/dist/sync/team.test.js.map +0 -1
  232. package/dist/sync.d.ts.map +0 -1
  233. package/dist/sync.js.map +0 -1
  234. package/dist/sync.test.d.ts +0 -2
  235. package/dist/sync.test.d.ts.map +0 -1
  236. package/dist/sync.test.js +0 -151
  237. package/dist/sync.test.js.map +0 -1
  238. package/dist/team.d.ts.map +0 -1
  239. package/dist/team.js.map +0 -1
  240. package/dist/team.test.d.ts +0 -2
  241. package/dist/team.test.d.ts.map +0 -1
  242. package/dist/team.test.js +0 -163
  243. package/dist/team.test.js.map +0 -1
  244. package/dist/updateCheck.d.ts.map +0 -1
  245. package/dist/updateCheck.js.map +0 -1
  246. package/dist/updateCheck.test.d.ts +0 -2
  247. package/dist/updateCheck.test.d.ts.map +0 -1
  248. package/dist/updateCheck.test.js +0 -27
  249. package/dist/updateCheck.test.js.map +0 -1
  250. package/dist/watchers/agents.d.ts.map +0 -1
  251. package/dist/watchers/agents.js.map +0 -1
  252. package/dist/watchers/agents.test.d.ts +0 -2
  253. package/dist/watchers/agents.test.d.ts.map +0 -1
  254. package/dist/watchers/agents.test.js +0 -96
  255. package/dist/watchers/agents.test.js.map +0 -1
  256. package/dist/watchers/claude.d.ts.map +0 -1
  257. package/dist/watchers/claude.js.map +0 -1
  258. package/dist/watchers/claude.test.d.ts +0 -2
  259. package/dist/watchers/claude.test.d.ts.map +0 -1
  260. package/dist/watchers/claude.test.js +0 -180
  261. package/dist/watchers/claude.test.js.map +0 -1
  262. package/dist/watchers/codex.d.ts.map +0 -1
  263. package/dist/watchers/codex.js.map +0 -1
  264. package/dist/watchers/codex.test.d.ts +0 -2
  265. package/dist/watchers/codex.test.d.ts.map +0 -1
  266. package/dist/watchers/codex.test.js +0 -207
  267. package/dist/watchers/codex.test.js.map +0 -1
  268. package/dist/watchers/copilot.d.ts.map +0 -1
  269. package/dist/watchers/copilot.js.map +0 -1
  270. package/dist/watchers/copilot.test.d.ts +0 -2
  271. package/dist/watchers/copilot.test.d.ts.map +0 -1
  272. package/dist/watchers/copilot.test.js +0 -98
  273. package/dist/watchers/copilot.test.js.map +0 -1
  274. package/dist/watchers/git.d.ts.map +0 -1
  275. package/dist/watchers/git.js.map +0 -1
  276. package/dist/watchers/git.test.d.ts +0 -2
  277. package/dist/watchers/git.test.d.ts.map +0 -1
  278. package/dist/watchers/git.test.js +0 -75
  279. package/dist/watchers/git.test.js.map +0 -1
  280. package/dist/watchers/identity.d.ts.map +0 -1
  281. package/dist/watchers/identity.js.map +0 -1
  282. package/dist/watchers/identity.test.d.ts +0 -2
  283. package/dist/watchers/identity.test.d.ts.map +0 -1
  284. package/dist/watchers/identity.test.js +0 -48
  285. package/dist/watchers/identity.test.js.map +0 -1
  286. package/dist/web-ui.test.d.ts +0 -2
  287. package/dist/web-ui.test.d.ts.map +0 -1
  288. package/dist/web-ui.test.js +0 -414
  289. package/dist/web-ui.test.js.map +0 -1
  290. package/dist/workitems.test.d.ts +0 -2
  291. package/dist/workitems.test.d.ts.map +0 -1
  292. package/dist/workitems.test.js +0 -59
  293. package/dist/workitems.test.js.map +0 -1
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.scanClaudeCode = scanClaudeCode;
4
+ exports.parseTranscriptLine = parseTranscriptLine;
4
5
  exports.resolveScratchpads = resolveScratchpads;
6
+ exports.isScratchpad = isScratchpad;
5
7
  const node_fs_1 = require("node:fs");
6
8
  const promises_1 = require("node:fs/promises");
7
9
  const node_os_1 = require("node:os");
@@ -38,27 +40,19 @@ async function scanClaudeCode(options = {}) {
38
40
  let malformedRecords = 0;
39
41
  for (const file of files) {
40
42
  for await (const line of readLines(file)) {
41
- if (!line.trim())
43
+ const parsed = parseTranscriptLine(line);
44
+ if (parsed === null)
42
45
  continue;
43
46
  recordsSeen++;
44
- let raw;
45
- try {
46
- raw = JSON.parse(line);
47
- }
48
- catch {
49
- // Una línea a medio escribir mientras la sesión está viva es normal.
50
- malformedRecords++;
51
- continue;
52
- }
53
- if (raw.type !== "assistant") {
47
+ if (parsed === "other") {
54
48
  unknownRecords++;
55
49
  continue;
56
50
  }
57
- const turn = toTurn(raw);
58
- if (!turn) {
51
+ if (parsed === "malformed") {
59
52
  malformedRecords++;
60
53
  continue;
61
54
  }
55
+ const turn = parsed;
62
56
  if (seenTurnIds.has(turn.turnId)) {
63
57
  duplicatesDropped++;
64
58
  continue;
@@ -95,6 +89,27 @@ async function scanClaudeCode(options = {}) {
95
89
  },
96
90
  };
97
91
  }
92
+ /**
93
+ * Una línea del transcript: un turno, `"other"` si es de otro tipo (maquinaria
94
+ * interna que se ignora), `"malformed"` si no se entiende, o null si está en
95
+ * blanco. Es lo que usan tanto el escaneo completo como `estela statusline`,
96
+ * que lee solo las líneas nuevas: los dos tienen que entender lo mismo.
97
+ */
98
+ function parseTranscriptLine(line) {
99
+ if (!line.trim())
100
+ return null;
101
+ let raw;
102
+ try {
103
+ raw = JSON.parse(line);
104
+ }
105
+ catch {
106
+ // Una línea a medio escribir mientras la sesión está viva es normal.
107
+ return "malformed";
108
+ }
109
+ if (raw.type !== "assistant")
110
+ return "other";
111
+ return toTurn(raw) ?? "malformed";
112
+ }
98
113
  function toTurn(raw) {
99
114
  const id = raw.message?.id;
100
115
  const model = raw.message?.model;
@@ -163,8 +178,10 @@ function num(value) {
163
178
  * es generar ese mismo identificador para cada repositorio conocido y ver cuál
164
179
  * coincide.
165
180
  */
166
- function resolveScratchpads(turns) {
167
- const realPaths = new Set();
181
+ function resolveScratchpads(turns,
182
+ /** Repositorios ya conocidos, para cuando el lote no trae la ruta real (la status line lee pocas líneas). */
183
+ knownRepos = []) {
184
+ const realPaths = new Set(knownRepos.filter((p) => !isScratchpad(p)));
168
185
  for (const turn of turns) {
169
186
  if (turn.repoPath && !isScratchpad(turn.repoPath))
170
187
  realPaths.add(turn.repoPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "estela",
3
- "version": "0.1.40",
3
+ "version": "0.2.1",
4
4
  "description": "Time tracking you never fill in: rebuilds your billable hours from Claude Code, Codex and GitHub Copilot sessions and your Git history, and splits what the AI cost per project.",
5
5
  "keywords": [
6
6
  "time-tracking",
@@ -36,6 +36,8 @@
36
36
  "homepage": "https://getestela.dev",
37
37
  "files": [
38
38
  "dist",
39
+ "!dist/**/*.test.*",
40
+ "!dist/**/*.map",
39
41
  "web",
40
42
  "README.md"
41
43
  ],
@@ -1 +0,0 @@
1
- {"version":3,"file":"amortize.d.ts","sourceRoot":"","sources":["../../src/billing/amortize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAIlF;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,8BAA8B;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE,SAAS,cAAc,EAAE,EAC/B,aAAa,EAAE,SAAS,YAAY,EAAE,EACtC,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GACxC,cAAc,EAAE,CAiClB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,aAAa,EAAE,SAAS,YAAY,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAkB/F;AAED,0CAA0C;AAC1C,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE1C;AAED,gEAAgE;AAChE,wBAAgB,eAAe,CAC7B,MAAM,EAAE,SAAS,cAAc,EAAE,EACjC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,SAAS,MAAM,EAAE,GACxB,KAAK,GAAG,IAAI,CAMd"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"amortize.js","sourceRoot":"","sources":["../../src/billing/amortize.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=amortize.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"amortize.test.d.ts","sourceRoot":"","sources":["../../src/billing/amortize.test.ts"],"names":[],"mappings":""}
@@ -1,121 +0,0 @@
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_test_1 = require("node:test");
8
- const shared_1 = require("@estela/shared");
9
- const amortize_js_1 = require("./amortize.js");
10
- const CLAUDE_MAX = {
11
- id: "claude-max", name: "Claude Max",
12
- monthlyFee: (0, shared_1.parseMoney)("200", "USD"),
13
- effectiveFrom: new Date("2025-01-01T00:00:00Z"), effectiveTo: null,
14
- };
15
- (0, node_test_1.test)("reparte la cuota en proporción al consumo", () => {
16
- const shares = (0, amortize_js_1.amortize)([
17
- { projectId: "a", month: "2026-08", consumption: { microUsd: 75_000_000 } }, // 75%
18
- { projectId: "b", month: "2026-08", consumption: { microUsd: 25_000_000 } }, // 25%
19
- ], [CLAUDE_MAX]);
20
- const a = shares.find((s) => s.projectId === "a");
21
- const b = shares.find((s) => s.projectId === "b");
22
- strict_1.default.equal(a.share, 0.75);
23
- strict_1.default.equal((0, shared_1.formatMoney)(a.amount), "$150,00");
24
- strict_1.default.equal((0, shared_1.formatMoney)(b.amount), "$50,00");
25
- strict_1.default.equal(a.amount.amount + b.amount.amount, 20_000, "el reparto suma la cuota entera");
26
- });
27
- (0, node_test_1.test)("lo no facturable también consume cuota, y por eso entra en el denominador", () => {
28
- // Si tus experimentos se comen la mitad, al cliente solo le toca la otra mitad.
29
- const shares = (0, amortize_js_1.amortize)([
30
- { projectId: "cliente", month: "2026-08", consumption: { microUsd: 50_000_000 } },
31
- { projectId: "experimentos", month: "2026-08", consumption: { microUsd: 50_000_000 } },
32
- ], [CLAUDE_MAX]);
33
- const cliente = shares.find((s) => s.projectId === "cliente");
34
- strict_1.default.equal((0, shared_1.formatMoney)(cliente.amount), "$100,00");
35
- strict_1.default.equal(cliente.share, 0.5);
36
- });
37
- (0, node_test_1.test)("el trabajo sin asignar a proyecto también diluye la cuota", () => {
38
- // El fallo que esto previene: con un solo proyecto configurado y el 90% del
39
- // consumo en repos sin registrar, ese proyecto cargaba con la cuota entera.
40
- const shares = (0, amortize_js_1.amortize)([{ projectId: "nebula", month: "2026-08", consumption: { microUsd: 10_000_000 } }], [CLAUDE_MAX], new Map([["2026-08", { microUsd: 100_000_000 }]])); // total real de la máquina
41
- strict_1.default.equal(shares[0].share, 0.1);
42
- strict_1.default.equal((0, shared_1.formatMoney)(shares[0].amount), "$20,00");
43
- strict_1.default.equal(shares[0].monthTotal.microUsd, 100_000_000, "el denominador es el consumo real de la máquina, no la suma de proyectos");
44
- });
45
- (0, node_test_1.test)("un total incoherente nunca hace que las partes pasen del 100%", () => {
46
- const shares = (0, amortize_js_1.amortize)([{ projectId: "a", month: "2026-08", consumption: { microUsd: 50_000_000 } }], [CLAUDE_MAX], new Map([["2026-08", { microUsd: 10_000_000 }]])); // menor que lo asignado
47
- strict_1.default.equal(shares[0].share, 1, "se limita al 100%, no al 500%");
48
- strict_1.default.equal((0, shared_1.formatMoney)(shares[0].amount), "$200,00");
49
- });
50
- (0, node_test_1.test)("el reparto es por mes, no acumulado", () => {
51
- const shares = (0, amortize_js_1.amortize)([
52
- { projectId: "a", month: "2026-07", consumption: { microUsd: 10_000_000 } },
53
- { projectId: "a", month: "2026-08", consumption: { microUsd: 30_000_000 } },
54
- { projectId: "b", month: "2026-08", consumption: { microUsd: 10_000_000 } },
55
- ], [CLAUDE_MAX]);
56
- const julio = shares.find((s) => s.projectId === "a" && s.month === "2026-07");
57
- const agosto = shares.find((s) => s.projectId === "a" && s.month === "2026-08");
58
- strict_1.default.equal((0, shared_1.formatMoney)(julio.amount), "$200,00", "único proyecto en julio: cuota entera");
59
- strict_1.default.equal((0, shared_1.formatMoney)(agosto.amount), "$150,00", "75% de agosto");
60
- });
61
- (0, node_test_1.test)("varias suscripciones a la vez se suman", () => {
62
- const cursor = {
63
- id: "cursor", name: "Cursor", monthlyFee: (0, shared_1.parseMoney)("20", "USD"),
64
- effectiveFrom: new Date("2025-01-01T00:00:00Z"), effectiveTo: null,
65
- };
66
- strict_1.default.equal((0, shared_1.formatMoney)((0, amortize_js_1.feeForMonth)([CLAUDE_MAX, cursor], "2026-08")), "$220,00");
67
- });
68
- (0, node_test_1.test)("una suscripción no vigente no cuenta", () => {
69
- const antigua = {
70
- ...CLAUDE_MAX, id: "vieja",
71
- effectiveTo: new Date("2026-01-01T00:00:00Z"),
72
- };
73
- strict_1.default.equal((0, amortize_js_1.feeForMonth)([antigua], "2026-08"), null);
74
- strict_1.default.equal((0, shared_1.formatMoney)((0, amortize_js_1.feeForMonth)([antigua], "2025-06")), "$200,00");
75
- });
76
- (0, node_test_1.test)("suscripciones en monedas distintas se rechazan en vez de convertirse", () => {
77
- const enEuros = {
78
- id: "x", name: "X", monthlyFee: (0, shared_1.money)(2000, "EUR"),
79
- effectiveFrom: new Date("2025-01-01T00:00:00Z"), effectiveTo: null,
80
- };
81
- strict_1.default.throws(() => (0, amortize_js_1.feeForMonth)([CLAUDE_MAX, enEuros], "2026-08"), /monedas distintas/);
82
- });
83
- (0, node_test_1.test)("sin suscripción, el reparto sale a cero en vez de inventar un coste", () => {
84
- const shares = (0, amortize_js_1.amortize)([{ projectId: "a", month: "2026-08", consumption: { microUsd: 8_016_655 } }], []);
85
- strict_1.default.equal(shares[0].amount.amount, 0);
86
- strict_1.default.equal(shares[0].consumption.microUsd, 8_016_655, "el consumo sí se conserva");
87
- });
88
- (0, node_test_1.test)("consumo cero no divide por cero", () => {
89
- const shares = (0, amortize_js_1.amortize)([{ projectId: "a", month: "2026-08", consumption: { microUsd: 0 } }], [CLAUDE_MAX]);
90
- strict_1.default.equal(shares[0].share, 0);
91
- strict_1.default.equal(shares[0].amount.amount, 0);
92
- });
93
- (0, node_test_1.test)("suma de un proyecto a lo largo de varios meses", () => {
94
- const shares = (0, amortize_js_1.amortize)([
95
- { projectId: "a", month: "2026-07", consumption: { microUsd: 100_000_000 } },
96
- { projectId: "a", month: "2026-08", consumption: { microUsd: 100_000_000 } },
97
- ], [CLAUDE_MAX]);
98
- const total = (0, amortize_js_1.shareForProject)(shares, "a", ["2026-07", "2026-08"]);
99
- strict_1.default.equal((0, shared_1.formatMoney)(total), "$400,00");
100
- const soloJulio = (0, amortize_js_1.shareForProject)(shares, "a", ["2026-07"]);
101
- strict_1.default.equal((0, shared_1.formatMoney)(soloJulio), "$200,00");
102
- });
103
- (0, node_test_1.test)("monthOf usa UTC de forma consistente", () => {
104
- strict_1.default.equal((0, amortize_js_1.monthOf)(new Date("2026-08-26T23:30:00Z")), "2026-08");
105
- strict_1.default.equal((0, amortize_js_1.monthOf)(new Date("2026-09-01T00:00:00Z")), "2026-09");
106
- });
107
- (0, node_test_1.test)("el reparto de la cuota se congela en el informe", () => {
108
- // El fallo que esto previene: el reparto es un valor derivado que cambia al
109
- // entrar más consumo del mismo mes. Sin congelarlo, reimprimir un informe de
110
- // agosto en diciembre daría otra cifra, y un documento que cambia solo no es
111
- // un documento.
112
- const enAgosto = (0, amortize_js_1.amortize)([{ projectId: "a", month: "2026-08", consumption: { microUsd: 50_000_000 } }], [CLAUDE_MAX], new Map([["2026-08", { microUsd: 100_000_000 }]]));
113
- const congelado = (0, amortize_js_1.shareForProject)(enAgosto, "a", ["2026-08"]);
114
- strict_1.default.equal((0, shared_1.formatMoney)(congelado), "$100,00");
115
- // En diciembre se conoce el doble de consumo de ese mismo mes.
116
- const enDiciembre = (0, amortize_js_1.amortize)([{ projectId: "a", month: "2026-08", consumption: { microUsd: 50_000_000 } }], [CLAUDE_MAX], new Map([["2026-08", { microUsd: 200_000_000 }]]));
117
- const recalculado = (0, amortize_js_1.shareForProject)(enDiciembre, "a", ["2026-08"]);
118
- strict_1.default.equal((0, shared_1.formatMoney)(recalculado), "$50,00");
119
- strict_1.default.notEqual(congelado.amount, recalculado.amount, "el recálculo cambia: por eso hay que guardar el de su día");
120
- });
121
- //# sourceMappingURL=amortize.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"amortize.test.js","sourceRoot":"","sources":["../../src/billing/amortize.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=format.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"format.test.d.ts","sourceRoot":"","sources":["../../src/billing/format.test.ts"],"names":[],"mappings":""}
@@ -1,110 +0,0 @@
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_test_1 = require("node:test");
8
- const shared_1 = require("@estela/shared");
9
- const sessionize_js_1 = require("./sessionize.js");
10
- // ── Duraciones ─────────────────────────────────────────────────────────
11
- (0, node_test_1.test)("nunca imprime 60 minutos", () => {
12
- // El fallo que esto previene: redondear los minutos después de haber
13
- // separado las horas. 2h 59m 50s salía como "2h 60m", y en una factura.
14
- strict_1.default.equal((0, shared_1.formatDuration)(2 * 3600 + 59 * 60 + 50), "3h");
15
- strict_1.default.equal((0, shared_1.formatDuration)(3599), "1h");
16
- strict_1.default.equal((0, shared_1.formatDuration)(59 * 60 + 55), "1h");
17
- });
18
- (0, node_test_1.test)("duraciones normales", () => {
19
- strict_1.default.equal((0, shared_1.formatDuration)(0), "0m");
20
- strict_1.default.equal((0, shared_1.formatDuration)(90), "2m");
21
- strict_1.default.equal((0, shared_1.formatDuration)(3600), "1h");
22
- strict_1.default.equal((0, shared_1.formatDuration)(3660), "1h 01m");
23
- strict_1.default.equal((0, shared_1.formatDuration)(4 * 3600 + 16 * 60), "4h 16m");
24
- });
25
- (0, node_test_1.test)("ninguna duración razonable produce 60m", () => {
26
- for (let s = 0; s < 12 * 3600; s += 7) {
27
- const out = (0, shared_1.formatDuration)(s);
28
- strict_1.default.ok(!out.includes("60m"), `formatDuration(${s}) = "${out}"`);
29
- }
30
- });
31
- // ── Fecha local ────────────────────────────────────────────────────────
32
- (0, node_test_1.test)("el día de trabajo es el del reloj de quien trabaja", () => {
33
- // En UTC-5, las 22:00 del día 25 son las 03:00 UTC del 26. Usar la fecha UTC
34
- // partiría la jornada de tarde-noche entre dos días.
35
- const tarde = new Date("2026-08-26T03:19:00Z"); // 22:19 del 25 en Ecuador
36
- const esperado = new Date(tarde.getTime() - tarde.getTimezoneOffset() * 60_000)
37
- .toISOString().slice(0, 10);
38
- strict_1.default.equal((0, shared_1.localDate)(tarde), esperado);
39
- if (tarde.getTimezoneOffset() > 0) {
40
- strict_1.default.notEqual((0, shared_1.localDate)(tarde), tarde.toISOString().slice(0, 10), "al oeste de Greenwich, la fecha local y la UTC difieren en esta hora");
41
- }
42
- });
43
- // ── Agrupación ─────────────────────────────────────────────────────────
44
- function block(startIso, minutes, branch = "main") {
45
- const startedAt = new Date(startIso);
46
- return {
47
- startedAt,
48
- endedAt: new Date(startedAt.getTime() + minutes * 60_000),
49
- seconds: minutes * 60,
50
- repoPath: "/repo", branch, turnCount: 1,
51
- aiCost: { microUsd: 1_000_000 },
52
- models: ["claude-opus-5"], sessionIds: ["s"], commits: [], unpricedModels: [],
53
- };
54
- }
55
- (0, node_test_1.test)("suma el tiempo trabajado, no el lapso entre el primero y el último", () => {
56
- // Dos bloques de 30 min con tres horas de comida en medio son 1h, no 4h.
57
- // Facturar el hueco sería cobrarle de más al cliente.
58
- const merged = (0, sessionize_js_1.groupByBranchAndDay)([
59
- block("2026-08-26T14:00:00Z", 30),
60
- block("2026-08-26T21:00:00Z", 30),
61
- ]);
62
- strict_1.default.equal(merged.length, 1);
63
- strict_1.default.equal(merged[0].seconds, 3600, "1h de trabajo real");
64
- strict_1.default.equal((0, shared_1.formatDuration)(merged[0].seconds), "1h");
65
- });
66
- (0, node_test_1.test)("ramas distintas no se fusionan", () => {
67
- const merged = (0, sessionize_js_1.groupByBranchAndDay)([
68
- block("2026-08-26T14:00:00Z", 30, "main"),
69
- block("2026-08-26T15:00:00Z", 30, "feature/auth"),
70
- ]);
71
- strict_1.default.equal(merged.length, 2);
72
- });
73
- (0, node_test_1.test)("se acumulan commits, modelos y coste al fusionar", () => {
74
- const a = block("2026-08-26T14:00:00Z", 30);
75
- const b = block("2026-08-26T15:00:00Z", 30);
76
- const merged = (0, sessionize_js_1.groupByBranchAndDay)([a, b]);
77
- strict_1.default.equal(merged[0].aiCost.microUsd, 2_000_000);
78
- strict_1.default.equal(merged[0].turnCount, 2);
79
- });
80
- // ── Identificador determinista de una imputación ───────────────────────
81
- /** La misma fórmula que usan la CLI y el servidor para nombrar un bloque. */
82
- function entryId(projectId, startedAt, branch) {
83
- const day = (0, shared_1.localDate)(startedAt);
84
- const slug = (branch ?? "sin-rama").replace(/[^a-zA-Z0-9]+/g, "-");
85
- return `te_${projectId}_${day}_${slug}`;
86
- }
87
- (0, node_test_1.test)("el id usa la fecha local, no la UTC", () => {
88
- // El fallo que esto previene: la CLI construía el id con toISOString() (UTC)
89
- // y el servidor con localDate(). Para el mismo bloque salían dos ids, así que
90
- // en vez de actualizar una fila se creaba otra y el día se contaba doble.
91
- const tarde = new Date("2026-08-27T02:49:59Z"); // 21:49 del 26 en UTC-5
92
- const id = entryId("nebula", tarde, "feature/x");
93
- strict_1.default.ok(id.includes((0, shared_1.localDate)(tarde)), "el id lleva la fecha local");
94
- if (tarde.getTimezoneOffset() > 0) {
95
- strict_1.default.ok(!id.includes(tarde.toISOString().slice(0, 10)), "y no la UTC, que aquí es el día siguiente");
96
- }
97
- });
98
- (0, node_test_1.test)("el mismo bloque siempre produce el mismo id", () => {
99
- const at = new Date("2026-08-26T14:00:00Z");
100
- strict_1.default.equal(entryId("p", at, "main"), entryId("p", new Date(at), "main"));
101
- });
102
- (0, node_test_1.test)("el id coincide con el local_date de la fila", () => {
103
- // La invariante que hace posible detectar filas huérfanas: si un id lleva una
104
- // fecha distinta a su local_date, esa fila la creó una versión con el bug.
105
- for (const iso of ["2026-08-27T02:49:00Z", "2026-08-26T14:00:00Z", "2026-01-01T03:00:00Z"]) {
106
- const at = new Date(iso);
107
- strict_1.default.ok(entryId("p", at, "main").includes(`_${(0, shared_1.localDate)(at)}_`));
108
- }
109
- });
110
- //# sourceMappingURL=format.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"format.test.js","sourceRoot":"","sources":["../../src/billing/format.test.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"invoice.d.ts","sourceRoot":"","sources":["../../src/billing/invoice.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACF,MAAM,EAAE,OAAO,EAAe,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAC5E,MAAM,gBAAgB,CAAC;AAQxB;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC;IAC7B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,YAAa,SAAQ,KAAK;CAAG;AAE1C,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAwF9D;AAED,8EAA8E;AAC9E,wBAAgB,MAAM,CACpB,KAAK,EAAE,SAAS,UAAU,EAAE,EAC5B,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,IAAI,GACP,KAAK,GAAG,IAAI,CAOd;AAED,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAgDxF;AAED,oEAAoE;AACpE,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG;IAC7D,OAAO,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;CACjE,CAUA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"invoice.js","sourceRoot":"","sources":["../../src/billing/invoice.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=invoice.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"invoice.test.d.ts","sourceRoot":"","sources":["../../src/billing/invoice.test.ts"],"names":[],"mappings":""}
@@ -1,200 +0,0 @@
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_test_1 = require("node:test");
8
- const shared_1 = require("@estela/shared");
9
- const invoice_js_1 = require("./invoice.js");
10
- // El caso real: freelance, proyecto por horas, 13 EUR/hora pactados.
11
- const NEBULA = {
12
- id: "nebula", name: "Nebula", currency: "EUR", taxId: "B-12345678",
13
- };
14
- const PROYECTO = {
15
- id: "portal-ventas",
16
- clientId: "nebula",
17
- name: "Portal Ventas",
18
- repoPaths: ["/home/dev/proyectos/portal-ventas"],
19
- billable: true,
20
- roundingMinutes: 0,
21
- aiCostPolicy: "absorbed", kind: "client", closedAt: null,
22
- };
23
- const TARIFA = {
24
- projectId: "portal-ventas",
25
- hourlyRate: (0, shared_1.money)(1300, "EUR"), // 13,00 EUR/hora
26
- effectiveFrom: new Date("2025-01-01T00:00:00Z"),
27
- effectiveTo: null,
28
- };
29
- function entry(id, startIso, seconds, microUsd = 0) {
30
- const startedAt = new Date(startIso);
31
- return {
32
- id, projectId: "portal-ventas", startedAt,
33
- endedAt: new Date(startedAt.getTime() + seconds * 1000),
34
- seconds, description: `Bloque ${id}`, billable: true, invoiceId: null,
35
- aiCost: { microUsd }, agentSeconds: seconds, commitHashes: [], agents: ["claude-code"],
36
- source: "agent", kind: "development", branch: "main",
37
- };
38
- }
39
- // ---------------------------------------------------------------------------
40
- // Dinero
41
- // ---------------------------------------------------------------------------
42
- (0, node_test_1.test)("13 EUR/hora se guarda como 1300 céntimos, no como 13.0", () => {
43
- const rate = (0, shared_1.parseMoney)("13", "EUR");
44
- strict_1.default.equal(rate.amount, 1300);
45
- strict_1.default.equal(rate.currency, "EUR");
46
- strict_1.default.equal((0, shared_1.formatMoney)(rate), "€13,00");
47
- });
48
- (0, node_test_1.test)("acepta decimales y coma decimal", () => {
49
- strict_1.default.equal((0, shared_1.parseMoney)("13.50", "EUR").amount, 1350);
50
- strict_1.default.equal((0, shared_1.parseMoney)("13,50", "EUR").amount, 1350);
51
- strict_1.default.equal((0, shared_1.parseMoney)("0.01", "EUR").amount, 1);
52
- });
53
- (0, node_test_1.test)("una hora exacta a 13 EUR son 13 EUR", () => {
54
- strict_1.default.equal((0, shared_1.billableAmount)(3600, (0, shared_1.money)(1300, "EUR")).amount, 1300);
55
- });
56
- (0, node_test_1.test)("media hora a 13 EUR son 6,50 EUR sin error de coma flotante", () => {
57
- strict_1.default.equal((0, shared_1.billableAmount)(1800, (0, shared_1.money)(1300, "EUR")).amount, 650);
58
- });
59
- (0, node_test_1.test)("sumar diez mil bloques no acumula deriva", () => {
60
- let total = 0;
61
- for (let i = 0; i < 10_000; i++)
62
- total += (0, shared_1.billableAmount)(600, (0, shared_1.money)(1300, "EUR")).amount;
63
- // 10 min a 13 EUR/h = 216,666... céntimos -> 217 tras redondear
64
- strict_1.default.equal(total, 217 * 10_000);
65
- strict_1.default.ok(Number.isInteger(total));
66
- });
67
- (0, node_test_1.test)("no se pueden sumar monedas distintas sin tipo de cambio", () => {
68
- const { addMoney } = require("@estela/shared");
69
- strict_1.default.throws(() => addMoney((0, shared_1.money)(100, "EUR"), (0, shared_1.money)(100, "USD")), /Monedas distintas/);
70
- });
71
- (0, node_test_1.test)("el redondeo de tiempo es una decisión separada del de dinero", () => {
72
- // Half-up sobre el bloque: 50 min está más cerca de 45 que de 60.
73
- strict_1.default.equal((0, shared_1.roundSeconds)(3000, 15), 2700); // 50 min -> 45 min
74
- strict_1.default.equal((0, shared_1.roundSeconds)(3300, 15), 3600); // 55 min -> 1 h
75
- strict_1.default.equal((0, shared_1.roundSeconds)(3000, 0), 3000); // exacto
76
- strict_1.default.equal((0, shared_1.roundSeconds)(400, 15), 0); // 6,6 min -> 0 con bloques de 15
77
- });
78
- // ---------------------------------------------------------------------------
79
- // Tarifas con vigencia
80
- // ---------------------------------------------------------------------------
81
- (0, node_test_1.test)("subir la tarifa no reescribe lo ya trabajado", () => {
82
- const rates = [
83
- { ...TARIFA, effectiveTo: new Date("2026-06-01T00:00:00Z") },
84
- { projectId: "portal-ventas", hourlyRate: (0, shared_1.money)(1600, "EUR"),
85
- effectiveFrom: new Date("2026-06-01T00:00:00Z"), effectiveTo: null },
86
- ];
87
- strict_1.default.equal((0, invoice_js_1.rateAt)(rates, "portal-ventas", new Date("2026-03-15T10:00:00Z")).amount, 1300);
88
- strict_1.default.equal((0, invoice_js_1.rateAt)(rates, "portal-ventas", new Date("2026-08-15T10:00:00Z")).amount, 1600);
89
- });
90
- // ---------------------------------------------------------------------------
91
- // Emisión
92
- // ---------------------------------------------------------------------------
93
- (0, node_test_1.test)("factura al corte: incluye lo anterior y excluye lo posterior", () => {
94
- const entries = [
95
- entry("a", "2026-08-10T09:00:00Z", 3600),
96
- entry("b", "2026-08-15T09:00:00Z", 5400), // 1,5 h
97
- entry("c", "2026-08-28T09:00:00Z", 3600), // después del corte
98
- ];
99
- const invoice = (0, invoice_js_1.issueInvoice)({
100
- client: NEBULA, project: PROYECTO, rates: [TARIFA], entries,
101
- cutoffAt: new Date("2026-08-25T23:59:59Z"), number: "F-2026-001",
102
- });
103
- strict_1.default.equal(invoice.lines.length, 2);
104
- strict_1.default.equal(invoice.totalSeconds, 9000); // 2,5 h
105
- strict_1.default.equal(invoice.subtotal.amount, 3250); // 2,5 * 13 = 32,50 EUR
106
- strict_1.default.equal((0, shared_1.formatMoney)(invoice.total), "€32,50");
107
- strict_1.default.equal(invoice.currency, "EUR");
108
- });
109
- (0, node_test_1.test)("el coste de IA se registra aunque no se facture", () => {
110
- const invoice = (0, invoice_js_1.issueInvoice)({
111
- client: NEBULA, project: PROYECTO, rates: [TARIFA],
112
- entries: [entry("a", "2026-08-10T09:00:00Z", 3600, 8_016_665)],
113
- cutoffAt: new Date("2026-08-25T23:59:59Z"), number: "F-2026-002",
114
- });
115
- strict_1.default.equal(invoice.aiCost.microUsd, 8_016_665);
116
- strict_1.default.equal(invoice.aiCostBilled, null, "con política 'absorbed' no se repercute");
117
- strict_1.default.equal(invoice.total.amount, 1300, "el total es solo la hora facturada");
118
- });
119
- (0, node_test_1.test)("repercutir IA en EUR exige tipo de cambio explícito", () => {
120
- const passthrough = { ...PROYECTO, aiCostPolicy: "passthrough" };
121
- const input = {
122
- client: NEBULA, project: passthrough, rates: [TARIFA],
123
- entries: [entry("a", "2026-08-10T09:00:00Z", 3600, 10_000_000)], // $10
124
- cutoffAt: new Date("2026-08-25T23:59:59Z"), number: "F-2026-003",
125
- };
126
- strict_1.default.throws(() => (0, invoice_js_1.issueInvoice)(input), /tipo de cambio/);
127
- const invoice = (0, invoice_js_1.issueInvoice)({ ...input, usdFxRate: 0.92 });
128
- strict_1.default.equal(invoice.usdFxRate, 0.92, "el tipo queda grabado en la factura");
129
- strict_1.default.equal(invoice.aiCostBilled.amount, 920); // $10 -> 9,20 EUR
130
- strict_1.default.equal(invoice.total.amount, 1300 + 920);
131
- });
132
- (0, node_test_1.test)("una tarifa en otra moneda que la del cliente se rechaza en vez de convertirse", () => {
133
- const rateUsd = { ...TARIFA, hourlyRate: (0, shared_1.money)(1300, "USD") };
134
- strict_1.default.throws(() => (0, invoice_js_1.issueInvoice)({
135
- client: NEBULA, project: PROYECTO, rates: [rateUsd],
136
- entries: [entry("a", "2026-08-10T09:00:00Z", 3600)],
137
- cutoffAt: new Date("2026-08-25T23:59:59Z"), number: "F-2026-004",
138
- }), /USD.*EUR|EUR.*USD/);
139
- });
140
- (0, node_test_1.test)("distintos freelancers, distintas monedas", () => {
141
- const usa = { id: "acme", name: "Acme", currency: "USD" };
142
- const proyecto = { ...PROYECTO, id: "acme-app", clientId: "acme" };
143
- const rate = {
144
- projectId: "acme-app", hourlyRate: (0, shared_1.parseMoney)("85", "USD"),
145
- effectiveFrom: new Date("2025-01-01T00:00:00Z"), effectiveTo: null,
146
- };
147
- const invoice = (0, invoice_js_1.issueInvoice)({
148
- client: usa, project: proyecto, rates: [rate],
149
- entries: [{ ...entry("a", "2026-08-10T09:00:00Z", 7200), projectId: "acme-app" }],
150
- cutoffAt: new Date("2026-08-25T23:59:59Z"), number: "F-2026-005",
151
- });
152
- strict_1.default.equal(invoice.currency, "USD");
153
- strict_1.default.equal(invoice.subtotal.amount, 17_000); // 2 h * $85
154
- strict_1.default.equal((0, shared_1.formatMoney)(invoice.subtotal), "$170,00");
155
- });
156
- (0, node_test_1.test)("sin tarifa definida, error claro en vez de factura a cero", () => {
157
- strict_1.default.throws(() => (0, invoice_js_1.issueInvoice)({
158
- client: NEBULA, project: PROYECTO, rates: [],
159
- entries: [entry("a", "2026-08-10T09:00:00Z", 3600)],
160
- cutoffAt: new Date("2026-08-25T23:59:59Z"), number: "F-2026-006",
161
- }), /Sin tarifa vigente/);
162
- });
163
- (0, node_test_1.test)("lo ya facturado no se vuelve a facturar", () => {
164
- const entries = [
165
- { ...entry("a", "2026-08-10T09:00:00Z", 3600), invoiceId: "inv_F-2026-001" },
166
- entry("b", "2026-08-11T09:00:00Z", 3600),
167
- ];
168
- const invoice = (0, invoice_js_1.issueInvoice)({
169
- client: NEBULA, project: PROYECTO, rates: [TARIFA], entries,
170
- cutoffAt: new Date("2026-08-25T23:59:59Z"), number: "F-2026-007",
171
- });
172
- strict_1.default.equal(invoice.lines.length, 1);
173
- strict_1.default.equal(invoice.subtotal.amount, 1300);
174
- });
175
- (0, node_test_1.test)("margen real tras descontar la IA", () => {
176
- const invoice = (0, invoice_js_1.issueInvoice)({
177
- client: NEBULA, project: PROYECTO, rates: [TARIFA],
178
- entries: [entry("a", "2026-08-10T09:00:00Z", 36_000, 8_016_665)], // 10 h, $8,02 de IA
179
- cutoffAt: new Date("2026-08-25T23:59:59Z"), number: "F-2026-008",
180
- });
181
- const m = (0, invoice_js_1.marginOf)(invoice, 0.92);
182
- strict_1.default.equal(m.revenue.amount, 13_000); // 10 h * 13 EUR = 130 EUR
183
- strict_1.default.equal(m.aiCost.amount, 738); // $8,02 * 0,92 = 7,38 EUR
184
- strict_1.default.equal(m.margin.amount, 13_000 - 738);
185
- strict_1.default.ok(m.marginPct > 94 && m.marginPct < 95);
186
- });
187
- (0, node_test_1.test)("un tipo de cambio nunca se recalcula solo", () => {
188
- const en_marzo = (0, shared_1.convertMoney)((0, shared_1.money)(1000, "USD"), "EUR", 0.92);
189
- const en_noviembre = (0, shared_1.convertMoney)((0, shared_1.money)(1000, "USD"), "EUR", 0.88);
190
- strict_1.default.equal(en_marzo.amount, 920);
191
- strict_1.default.equal(en_noviembre.amount, 880);
192
- // La factura guarda su tipo, así que la de marzo sigue diciendo 920.
193
- });
194
- (0, node_test_1.test)("sin horas pendientes, error explicativo", () => {
195
- strict_1.default.throws(() => (0, invoice_js_1.issueInvoice)({
196
- client: NEBULA, project: PROYECTO, rates: [TARIFA], entries: [],
197
- cutoffAt: new Date("2026-08-25T23:59:59Z"), number: "F-2026-009",
198
- }), invoice_js_1.InvoiceError);
199
- });
200
- //# sourceMappingURL=invoice.test.js.map