@open-mercato/core 0.7.1-develop.7178.1.ab93fbaf4e → 0.7.1-develop.7180.1.9717fbbb43

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 (808) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/staff_time_entry/index.js +14 -0
  3. package/dist/generated/entities/staff_time_entry/index.js.map +2 -2
  4. package/dist/generated/entities/staff_time_entry_tag/index.js +15 -0
  5. package/dist/generated/entities/staff_time_entry_tag/index.js.map +7 -0
  6. package/dist/generated/entities/staff_time_project/index.js +16 -0
  7. package/dist/generated/entities/staff_time_project/index.js.map +2 -2
  8. package/dist/generated/entities/staff_time_report/index.js +57 -0
  9. package/dist/generated/entities/staff_time_report/index.js.map +7 -0
  10. package/dist/generated/entities/staff_time_report_entry/index.js +27 -0
  11. package/dist/generated/entities/staff_time_report_entry/index.js.map +7 -0
  12. package/dist/generated/entities/staff_time_report_event/index.js +21 -0
  13. package/dist/generated/entities/staff_time_report_event/index.js.map +7 -0
  14. package/dist/generated/entities/staff_time_report_project/index.js +15 -0
  15. package/dist/generated/entities/staff_time_report_project/index.js.map +7 -0
  16. package/dist/generated/entities/staff_time_tag/index.js +21 -0
  17. package/dist/generated/entities/staff_time_tag/index.js.map +7 -0
  18. package/dist/generated/entities/staff_time_task/index.js +37 -0
  19. package/dist/generated/entities/staff_time_task/index.js.map +7 -0
  20. package/dist/generated/entities/staff_time_task_comment/index.js +21 -0
  21. package/dist/generated/entities/staff_time_task_comment/index.js.map +7 -0
  22. package/dist/generated/entities/staff_time_task_status/index.js +29 -0
  23. package/dist/generated/entities/staff_time_task_status/index.js.map +7 -0
  24. package/dist/generated/entities/staff_time_task_tag/index.js +15 -0
  25. package/dist/generated/entities/staff_time_task_tag/index.js.map +7 -0
  26. package/dist/generated/entities.ids.generated.js +11 -1
  27. package/dist/generated/entities.ids.generated.js.map +2 -2
  28. package/dist/generated/entity-fields-registry.js +149 -0
  29. package/dist/generated/entity-fields-registry.js.map +2 -2
  30. package/dist/helpers/integration/timesheetFixtures.js +7 -0
  31. package/dist/helpers/integration/timesheetFixtures.js.map +2 -2
  32. package/dist/modules/auth/cli.js +16 -0
  33. package/dist/modules/auth/cli.js.map +2 -2
  34. package/dist/modules/auth/lib/backendChrome.js +1 -0
  35. package/dist/modules/auth/lib/backendChrome.js.map +2 -2
  36. package/dist/modules/customer_accounts/lib/customerRoleAcls.js +44 -0
  37. package/dist/modules/customer_accounts/lib/customerRoleAcls.js.map +7 -0
  38. package/dist/modules/customer_accounts/setup.js +1 -28
  39. package/dist/modules/customer_accounts/setup.js.map +2 -2
  40. package/dist/modules/data_sync/api/options.js +2 -0
  41. package/dist/modules/data_sync/api/options.js.map +2 -2
  42. package/dist/modules/data_sync/backend/data-sync/page.js +39 -17
  43. package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
  44. package/dist/modules/data_sync/lib/start-controls.js +42 -0
  45. package/dist/modules/data_sync/lib/start-controls.js.map +7 -0
  46. package/dist/modules/staff/acl.js +9 -1
  47. package/dist/modules/staff/acl.js.map +2 -2
  48. package/dist/modules/staff/ai-agents.js +142 -0
  49. package/dist/modules/staff/ai-agents.js.map +7 -0
  50. package/dist/modules/staff/ai-tools/time-tracking-pack.js +348 -0
  51. package/dist/modules/staff/ai-tools/time-tracking-pack.js.map +7 -0
  52. package/dist/modules/staff/ai-tools/types.js +54 -0
  53. package/dist/modules/staff/ai-tools/types.js.map +7 -0
  54. package/dist/modules/staff/ai-tools.js +8 -0
  55. package/dist/modules/staff/ai-tools.js.map +7 -0
  56. package/dist/modules/staff/analytics.js +84 -1
  57. package/dist/modules/staff/analytics.js.map +2 -2
  58. package/dist/modules/staff/api/guards.js +37 -3
  59. package/dist/modules/staff/api/guards.js.map +2 -2
  60. package/dist/modules/staff/api/leave-requests/accept/route.js +1 -3
  61. package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
  62. package/dist/modules/staff/api/leave-requests/reject/route.js +1 -3
  63. package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
  64. package/dist/modules/staff/api/portal/time-reports/[id]/route.js +131 -0
  65. package/dist/modules/staff/api/portal/time-reports/[id]/route.js.map +7 -0
  66. package/dist/modules/staff/api/portal/time-reports/route.js +179 -0
  67. package/dist/modules/staff/api/portal/time-reports/route.js.map +7 -0
  68. package/dist/modules/staff/api/team-members/self/route.js +1 -3
  69. package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
  70. package/dist/modules/staff/api/team-members/tags/assign/route.js +1 -3
  71. package/dist/modules/staff/api/team-members/tags/assign/route.js.map +2 -2
  72. package/dist/modules/staff/api/team-members/tags/unassign/route.js +1 -3
  73. package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
  74. package/dist/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.js +174 -0
  75. package/dist/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.js.map +7 -0
  76. package/dist/modules/staff/api/timesheets/access-requests/route.js +226 -0
  77. package/dist/modules/staff/api/timesheets/access-requests/route.js.map +7 -0
  78. package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js +15 -8
  79. package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js.map +2 -2
  80. package/dist/modules/staff/api/timesheets/my-projects/route.js +13 -1
  81. package/dist/modules/staff/api/timesheets/my-projects/route.js.map +2 -2
  82. package/dist/modules/staff/api/timesheets/my-work/myWorkAggregate.js +42 -0
  83. package/dist/modules/staff/api/timesheets/my-work/myWorkAggregate.js.map +7 -0
  84. package/dist/modules/staff/api/timesheets/my-work/route.js +383 -0
  85. package/dist/modules/staff/api/timesheets/my-work/route.js.map +7 -0
  86. package/dist/modules/staff/api/timesheets/projects/kpis/route.js +14 -2
  87. package/dist/modules/staff/api/timesheets/projects/kpis/route.js.map +2 -2
  88. package/dist/modules/staff/api/timesheets/reports/[id]/close/route.js +168 -0
  89. package/dist/modules/staff/api/timesheets/reports/[id]/close/route.js.map +7 -0
  90. package/dist/modules/staff/api/timesheets/reports/[id]/export/route.js +246 -0
  91. package/dist/modules/staff/api/timesheets/reports/[id]/export/route.js.map +7 -0
  92. package/dist/modules/staff/api/timesheets/reports/[id]/sheet/route.js +183 -0
  93. package/dist/modules/staff/api/timesheets/reports/[id]/sheet/route.js.map +7 -0
  94. package/dist/modules/staff/api/timesheets/reports/[id]/unlock/route.js +164 -0
  95. package/dist/modules/staff/api/timesheets/reports/[id]/unlock/route.js.map +7 -0
  96. package/dist/modules/staff/api/timesheets/reports/preview/route.js +360 -0
  97. package/dist/modules/staff/api/timesheets/reports/preview/route.js.map +7 -0
  98. package/dist/modules/staff/api/timesheets/reports/route.js +316 -0
  99. package/dist/modules/staff/api/timesheets/reports/route.js.map +7 -0
  100. package/dist/modules/staff/api/timesheets/reports/shared.js +63 -0
  101. package/dist/modules/staff/api/timesheets/reports/shared.js.map +7 -0
  102. package/dist/modules/staff/api/timesheets/settings/reapply-rounding/route.js +164 -0
  103. package/dist/modules/staff/api/timesheets/settings/reapply-rounding/route.js.map +7 -0
  104. package/dist/modules/staff/api/timesheets/settings/rounding-impact/route.js +168 -0
  105. package/dist/modules/staff/api/timesheets/settings/rounding-impact/route.js.map +7 -0
  106. package/dist/modules/staff/api/timesheets/settings/route.js +199 -0
  107. package/dist/modules/staff/api/timesheets/settings/route.js.map +7 -0
  108. package/dist/modules/staff/api/timesheets/tags/access.js +76 -0
  109. package/dist/modules/staff/api/timesheets/tags/access.js.map +7 -0
  110. package/dist/modules/staff/api/timesheets/tags/entry-assignments/route.js +235 -0
  111. package/dist/modules/staff/api/timesheets/tags/entry-assignments/route.js.map +7 -0
  112. package/dist/modules/staff/api/timesheets/tags/route.js +160 -0
  113. package/dist/modules/staff/api/timesheets/tags/route.js.map +7 -0
  114. package/dist/modules/staff/api/timesheets/tags/task-assignments/route.js +233 -0
  115. package/dist/modules/staff/api/timesheets/tags/task-assignments/route.js.map +7 -0
  116. package/dist/modules/staff/api/timesheets/task-statuses/route.js +188 -0
  117. package/dist/modules/staff/api/timesheets/task-statuses/route.js.map +7 -0
  118. package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js +399 -0
  119. package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js.map +7 -0
  120. package/dist/modules/staff/api/timesheets/tasks/[id]/status/route.js +210 -0
  121. package/dist/modules/staff/api/timesheets/tasks/[id]/status/route.js.map +7 -0
  122. package/dist/modules/staff/api/timesheets/tasks/route.js +333 -0
  123. package/dist/modules/staff/api/timesheets/tasks/route.js.map +7 -0
  124. package/dist/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.js +228 -0
  125. package/dist/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.js.map +7 -0
  126. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js +29 -10
  127. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js.map +2 -2
  128. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js +31 -18
  129. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js.map +2 -2
  130. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +21 -7
  131. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +2 -2
  132. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js +27 -7
  133. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js.map +2 -2
  134. package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js +215 -32
  135. package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js.map +2 -2
  136. package/dist/modules/staff/api/timesheets/time-entries/copy-day/route.js +378 -0
  137. package/dist/modules/staff/api/timesheets/time-entries/copy-day/route.js.map +7 -0
  138. package/dist/modules/staff/api/timesheets/time-entries/overlaps/route.js +340 -0
  139. package/dist/modules/staff/api/timesheets/time-entries/overlaps/route.js.map +7 -0
  140. package/dist/modules/staff/api/timesheets/time-entries/route.js +238 -26
  141. package/dist/modules/staff/api/timesheets/time-entries/route.js.map +2 -2
  142. package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js +23 -8
  143. package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js.map +2 -2
  144. package/dist/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.js +209 -0
  145. package/dist/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.js.map +7 -0
  146. package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js +59 -2
  147. package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js.map +2 -2
  148. package/dist/modules/staff/api/timesheets/time-projects/route.js +264 -22
  149. package/dist/modules/staff/api/timesheets/time-projects/route.js.map +2 -2
  150. package/dist/modules/staff/backend/staff/time-tracking/board/page.js +110 -0
  151. package/dist/modules/staff/backend/staff/time-tracking/board/page.js.map +7 -0
  152. package/dist/modules/staff/backend/staff/time-tracking/board/page.meta.js +18 -0
  153. package/dist/modules/staff/backend/staff/time-tracking/board/page.meta.js.map +7 -0
  154. package/dist/modules/staff/backend/staff/time-tracking/entries/page.js +981 -0
  155. package/dist/modules/staff/backend/staff/time-tracking/entries/page.js.map +7 -0
  156. package/dist/modules/staff/backend/staff/time-tracking/entries/page.meta.js +18 -0
  157. package/dist/modules/staff/backend/staff/time-tracking/entries/page.meta.js.map +7 -0
  158. package/dist/modules/staff/backend/staff/time-tracking/page.js +583 -0
  159. package/dist/modules/staff/backend/staff/time-tracking/page.js.map +7 -0
  160. package/dist/modules/staff/backend/staff/time-tracking/page.meta.js +15 -0
  161. package/dist/modules/staff/backend/staff/time-tracking/page.meta.js.map +7 -0
  162. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.js +65 -0
  163. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.js.map +7 -0
  164. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.meta.js +16 -0
  165. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.meta.js.map +7 -0
  166. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js +229 -0
  167. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js.map +7 -0
  168. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.meta.js +16 -0
  169. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.meta.js.map +7 -0
  170. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +1014 -0
  171. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +7 -0
  172. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.meta.js +16 -0
  173. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.meta.js.map +7 -0
  174. package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.js +97 -0
  175. package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.js.map +7 -0
  176. package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.meta.js +16 -0
  177. package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.meta.js.map +7 -0
  178. package/dist/modules/staff/backend/staff/time-tracking/projects/page.js +1040 -0
  179. package/dist/modules/staff/backend/staff/time-tracking/projects/page.js.map +7 -0
  180. package/dist/modules/staff/backend/staff/time-tracking/projects/page.meta.js +19 -0
  181. package/dist/modules/staff/backend/staff/time-tracking/projects/page.meta.js.map +7 -0
  182. package/dist/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.js +353 -0
  183. package/dist/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.js.map +7 -0
  184. package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.js +457 -0
  185. package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.js.map +7 -0
  186. package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.meta.js +16 -0
  187. package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.meta.js.map +7 -0
  188. package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.js +697 -0
  189. package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.js.map +7 -0
  190. package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.meta.js +16 -0
  191. package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.meta.js.map +7 -0
  192. package/dist/modules/staff/backend/staff/time-tracking/reports/page.js +174 -0
  193. package/dist/modules/staff/backend/staff/time-tracking/reports/page.js.map +7 -0
  194. package/dist/modules/staff/backend/staff/time-tracking/reports/page.meta.js +18 -0
  195. package/dist/modules/staff/backend/staff/time-tracking/reports/page.meta.js.map +7 -0
  196. package/dist/modules/staff/backend/staff/time-tracking/settings/page.js +546 -0
  197. package/dist/modules/staff/backend/staff/time-tracking/settings/page.js.map +7 -0
  198. package/dist/modules/staff/backend/staff/time-tracking/settings/page.meta.js +18 -0
  199. package/dist/modules/staff/backend/staff/time-tracking/settings/page.meta.js.map +7 -0
  200. package/dist/modules/staff/backend/staff/time-tracking/timesheet/GridView.js +710 -0
  201. package/dist/modules/staff/backend/staff/time-tracking/timesheet/GridView.js.map +7 -0
  202. package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.js +646 -0
  203. package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.js.map +7 -0
  204. package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.meta.js +18 -0
  205. package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.meta.js.map +7 -0
  206. package/dist/modules/staff/backend/staff/timesheets/page.js +4 -793
  207. package/dist/modules/staff/backend/staff/timesheets/page.js.map +2 -2
  208. package/dist/modules/staff/backend/staff/timesheets/page.meta.js +2 -7
  209. package/dist/modules/staff/backend/staff/timesheets/page.meta.js.map +2 -2
  210. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.js +5 -136
  211. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.js.map +2 -2
  212. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.js +1 -7
  213. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.js.map +2 -2
  214. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js +5 -445
  215. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js.map +3 -3
  216. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.js +1 -7
  217. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.js.map +2 -2
  218. package/dist/modules/staff/backend/staff/timesheets/projects/create/page.js +4 -76
  219. package/dist/modules/staff/backend/staff/timesheets/projects/create/page.js.map +2 -2
  220. package/dist/modules/staff/backend/staff/timesheets/projects/create/page.meta.js +1 -7
  221. package/dist/modules/staff/backend/staff/timesheets/projects/create/page.meta.js.map +2 -2
  222. package/dist/modules/staff/backend/staff/timesheets/projects/page.js +4 -613
  223. package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
  224. package/dist/modules/staff/backend/staff/timesheets/projects/page.meta.js +2 -17
  225. package/dist/modules/staff/backend/staff/timesheets/projects/page.meta.js.map +2 -2
  226. package/dist/modules/staff/ce.js +40 -0
  227. package/dist/modules/staff/ce.js.map +2 -2
  228. package/dist/modules/staff/cli.js +166 -1
  229. package/dist/modules/staff/cli.js.map +2 -2
  230. package/dist/modules/staff/commands/index.js +5 -0
  231. package/dist/modules/staff/commands/index.js.map +2 -2
  232. package/dist/modules/staff/commands/timesheets-entries.js +569 -21
  233. package/dist/modules/staff/commands/timesheets-entries.js.map +3 -3
  234. package/dist/modules/staff/commands/timesheets-projects.js +450 -7
  235. package/dist/modules/staff/commands/timesheets-projects.js.map +2 -2
  236. package/dist/modules/staff/commands/timesheets-reports.js +1036 -0
  237. package/dist/modules/staff/commands/timesheets-reports.js.map +7 -0
  238. package/dist/modules/staff/commands/timesheets-tags.js +1015 -0
  239. package/dist/modules/staff/commands/timesheets-tags.js.map +7 -0
  240. package/dist/modules/staff/commands/timesheets-task-comments.js +438 -0
  241. package/dist/modules/staff/commands/timesheets-task-comments.js.map +7 -0
  242. package/dist/modules/staff/commands/timesheets-task-statuses.js +723 -0
  243. package/dist/modules/staff/commands/timesheets-task-statuses.js.map +7 -0
  244. package/dist/modules/staff/commands/timesheets-tasks.js +834 -0
  245. package/dist/modules/staff/commands/timesheets-tasks.js.map +7 -0
  246. package/dist/modules/staff/data/enrichers.js +426 -12
  247. package/dist/modules/staff/data/enrichers.js.map +2 -2
  248. package/dist/modules/staff/data/entities.js +552 -3
  249. package/dist/modules/staff/data/entities.js.map +2 -2
  250. package/dist/modules/staff/data/extensions.js +41 -0
  251. package/dist/modules/staff/data/extensions.js.map +7 -0
  252. package/dist/modules/staff/data/validators.js +254 -9
  253. package/dist/modules/staff/data/validators.js.map +2 -2
  254. package/dist/modules/staff/di.js +42 -1
  255. package/dist/modules/staff/di.js.map +2 -2
  256. package/dist/modules/staff/events.js +84 -6
  257. package/dist/modules/staff/events.js.map +2 -2
  258. package/dist/modules/staff/events.payloads.js +158 -0
  259. package/dist/modules/staff/events.payloads.js.map +7 -0
  260. package/dist/modules/staff/extension-points.js +261 -0
  261. package/dist/modules/staff/extension-points.js.map +7 -0
  262. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.js +138 -0
  263. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.js.map +7 -0
  264. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.meta.js +12 -0
  265. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.meta.js.map +7 -0
  266. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.js +133 -0
  267. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.js.map +7 -0
  268. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.meta.js +19 -0
  269. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.meta.js.map +7 -0
  270. package/dist/modules/staff/lib/crud.js +11 -1
  271. package/dist/modules/staff/lib/crud.js.map +2 -2
  272. package/dist/modules/staff/lib/time-tracking/access.js +92 -0
  273. package/dist/modules/staff/lib/time-tracking/access.js.map +7 -0
  274. package/dist/modules/staff/lib/time-tracking/billability.js +39 -0
  275. package/dist/modules/staff/lib/time-tracking/billability.js.map +7 -0
  276. package/dist/modules/staff/lib/time-tracking/capacity.js +68 -0
  277. package/dist/modules/staff/lib/time-tracking/capacity.js.map +7 -0
  278. package/dist/modules/staff/lib/time-tracking/componentContracts.js +15 -0
  279. package/dist/modules/staff/lib/time-tracking/componentContracts.js.map +7 -0
  280. package/dist/modules/staff/lib/time-tracking/cost.js +82 -0
  281. package/dist/modules/staff/lib/time-tracking/cost.js.map +7 -0
  282. package/dist/modules/staff/lib/time-tracking/duration.js +55 -0
  283. package/dist/modules/staff/lib/time-tracking/duration.js.map +7 -0
  284. package/dist/modules/staff/lib/time-tracking/featureAccess.js +41 -0
  285. package/dist/modules/staff/lib/time-tracking/featureAccess.js.map +7 -0
  286. package/dist/modules/staff/lib/time-tracking/interval.js +72 -0
  287. package/dist/modules/staff/lib/time-tracking/interval.js.map +7 -0
  288. package/dist/modules/staff/lib/time-tracking/migrateProjectCodes.js +88 -0
  289. package/dist/modules/staff/lib/time-tracking/migrateProjectCodes.js.map +7 -0
  290. package/dist/modules/staff/lib/time-tracking/moneyVisibility.js +10 -0
  291. package/dist/modules/staff/lib/time-tracking/moneyVisibility.js.map +7 -0
  292. package/dist/modules/staff/lib/time-tracking/overlap.js +87 -0
  293. package/dist/modules/staff/lib/time-tracking/overlap.js.map +7 -0
  294. package/dist/modules/staff/lib/time-tracking/portalRecipients.js +27 -0
  295. package/dist/modules/staff/lib/time-tracking/portalRecipients.js.map +7 -0
  296. package/dist/modules/staff/lib/time-tracking/portalReports.js +16 -0
  297. package/dist/modules/staff/lib/time-tracking/portalReports.js.map +7 -0
  298. package/dist/modules/staff/lib/time-tracking/projectCode.js +135 -0
  299. package/dist/modules/staff/lib/time-tracking/projectCode.js.map +7 -0
  300. package/dist/modules/staff/lib/time-tracking/reapplyRounding.js +187 -0
  301. package/dist/modules/staff/lib/time-tracking/reapplyRounding.js.map +7 -0
  302. package/dist/modules/staff/lib/time-tracking/recalculationRunner.js +80 -0
  303. package/dist/modules/staff/lib/time-tracking/recalculationRunner.js.map +7 -0
  304. package/dist/modules/staff/lib/time-tracking/recalculations.js +56 -0
  305. package/dist/modules/staff/lib/time-tracking/recalculations.js.map +7 -0
  306. package/dist/modules/staff/lib/time-tracking/registries/invoke.js +63 -0
  307. package/dist/modules/staff/lib/time-tracking/registries/invoke.js.map +7 -0
  308. package/dist/modules/staff/lib/time-tracking/registries/registry.js +62 -0
  309. package/dist/modules/staff/lib/time-tracking/registries/registry.js.map +7 -0
  310. package/dist/modules/staff/lib/time-tracking/registries/scope.js +21 -0
  311. package/dist/modules/staff/lib/time-tracking/registries/scope.js.map +7 -0
  312. package/dist/modules/staff/lib/time-tracking/rollup.js +34 -0
  313. package/dist/modules/staff/lib/time-tracking/rollup.js.map +7 -0
  314. package/dist/modules/staff/lib/time-tracking/rounding.js +66 -0
  315. package/dist/modules/staff/lib/time-tracking/rounding.js.map +7 -0
  316. package/dist/modules/staff/lib/time-tracking/roundingImpact.js +39 -0
  317. package/dist/modules/staff/lib/time-tracking/roundingImpact.js.map +7 -0
  318. package/dist/modules/staff/lib/time-tracking/settingKeys.js +214 -0
  319. package/dist/modules/staff/lib/time-tracking/settingKeys.js.map +7 -0
  320. package/dist/modules/staff/lib/time-tracking/settings.js +87 -0
  321. package/dist/modules/staff/lib/time-tracking/settings.js.map +7 -0
  322. package/dist/modules/staff/lib/time-tracking/sqlInClause.js +9 -0
  323. package/dist/modules/staff/lib/time-tracking/sqlInClause.js.map +7 -0
  324. package/dist/modules/staff/lib/time-tracking/timeEntrySources.js +48 -0
  325. package/dist/modules/staff/lib/time-tracking/timeEntrySources.js.map +7 -0
  326. package/dist/modules/staff/lib/time-tracking-ui/BoardFilterChips.js +283 -0
  327. package/dist/modules/staff/lib/time-tracking-ui/BoardFilterChips.js.map +7 -0
  328. package/dist/modules/staff/lib/time-tracking-ui/CustomerPicker.js +324 -0
  329. package/dist/modules/staff/lib/time-tracking-ui/CustomerPicker.js.map +7 -0
  330. package/dist/modules/staff/lib/time-tracking-ui/DurationInput.js +121 -0
  331. package/dist/modules/staff/lib/time-tracking-ui/DurationInput.js.map +7 -0
  332. package/dist/modules/staff/lib/time-tracking-ui/EntryDetailsFields.js +188 -0
  333. package/dist/modules/staff/lib/time-tracking-ui/EntryDetailsFields.js.map +7 -0
  334. package/dist/modules/staff/lib/time-tracking-ui/EntryFilterBar.js +89 -0
  335. package/dist/modules/staff/lib/time-tracking-ui/EntryFilterBar.js.map +7 -0
  336. package/dist/modules/staff/lib/time-tracking-ui/KanbanBoard.js +855 -0
  337. package/dist/modules/staff/lib/time-tracking-ui/KanbanBoard.js.map +7 -0
  338. package/dist/modules/staff/lib/time-tracking-ui/KanbanCard.js +360 -0
  339. package/dist/modules/staff/lib/time-tracking-ui/KanbanCard.js.map +7 -0
  340. package/dist/modules/staff/lib/time-tracking-ui/KanbanColumn.js +259 -0
  341. package/dist/modules/staff/lib/time-tracking-ui/KanbanColumn.js.map +7 -0
  342. package/dist/modules/staff/lib/time-tracking-ui/NewTaskDialog.js +238 -0
  343. package/dist/modules/staff/lib/time-tracking-ui/NewTaskDialog.js.map +7 -0
  344. package/dist/modules/staff/lib/time-tracking-ui/NoProjectAccess.js +130 -0
  345. package/dist/modules/staff/lib/time-tracking-ui/NoProjectAccess.js.map +7 -0
  346. package/dist/modules/staff/lib/time-tracking-ui/PeriodSelector.js +98 -0
  347. package/dist/modules/staff/lib/time-tracking-ui/PeriodSelector.js.map +7 -0
  348. package/dist/modules/staff/lib/time-tracking-ui/PhasePlaceholder.js +27 -0
  349. package/dist/modules/staff/lib/time-tracking-ui/PhasePlaceholder.js.map +7 -0
  350. package/dist/modules/staff/lib/time-tracking-ui/ProjectCodeField.js +166 -0
  351. package/dist/modules/staff/lib/time-tracking-ui/ProjectCodeField.js.map +7 -0
  352. package/dist/modules/staff/lib/time-tracking-ui/ProjectCurrencyField.js +295 -0
  353. package/dist/modules/staff/lib/time-tracking-ui/ProjectCurrencyField.js.map +7 -0
  354. package/dist/modules/staff/lib/time-tracking-ui/ProjectFormSections.js +212 -0
  355. package/dist/modules/staff/lib/time-tracking-ui/ProjectFormSections.js.map +7 -0
  356. package/dist/modules/staff/lib/time-tracking-ui/ProjectTeamDrawer.js +729 -0
  357. package/dist/modules/staff/lib/time-tracking-ui/ProjectTeamDrawer.js.map +7 -0
  358. package/dist/modules/staff/lib/time-tracking-ui/ReportSheet.js +217 -0
  359. package/dist/modules/staff/lib/time-tracking-ui/ReportSheet.js.map +7 -0
  360. package/dist/modules/staff/lib/time-tracking-ui/TagPicker.js +158 -0
  361. package/dist/modules/staff/lib/time-tracking-ui/TagPicker.js.map +7 -0
  362. package/dist/modules/staff/lib/time-tracking-ui/TaskBoardScreen.js +224 -0
  363. package/dist/modules/staff/lib/time-tracking-ui/TaskBoardScreen.js.map +7 -0
  364. package/dist/modules/staff/lib/time-tracking-ui/TaskCommentThread.js +93 -0
  365. package/dist/modules/staff/lib/time-tracking-ui/TaskCommentThread.js.map +7 -0
  366. package/dist/modules/staff/lib/time-tracking-ui/TaskDrawer.js +1196 -0
  367. package/dist/modules/staff/lib/time-tracking-ui/TaskDrawer.js.map +7 -0
  368. package/dist/modules/staff/lib/time-tracking-ui/TaskListView.js +176 -0
  369. package/dist/modules/staff/lib/time-tracking-ui/TaskListView.js.map +7 -0
  370. package/dist/modules/staff/lib/time-tracking-ui/TaskPicker.js +307 -0
  371. package/dist/modules/staff/lib/time-tracking-ui/TaskPicker.js.map +7 -0
  372. package/dist/modules/staff/lib/time-tracking-ui/TaskQuickLog.js +284 -0
  373. package/dist/modules/staff/lib/time-tracking-ui/TaskQuickLog.js.map +7 -0
  374. package/dist/modules/staff/lib/time-tracking-ui/TimeEntriesSummaryFooter.js +73 -0
  375. package/dist/modules/staff/lib/time-tracking-ui/TimeEntriesSummaryFooter.js.map +7 -0
  376. package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDialog.js +1499 -0
  377. package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDialog.js.map +7 -0
  378. package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDurationCell.js +95 -0
  379. package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDurationCell.js.map +7 -0
  380. package/dist/modules/staff/lib/time-tracking-ui/TimesheetCalendar.js +188 -0
  381. package/dist/modules/staff/lib/time-tracking-ui/TimesheetCalendar.js.map +7 -0
  382. package/dist/modules/staff/lib/time-tracking-ui/TimesheetPeriodFooter.js +61 -0
  383. package/dist/modules/staff/lib/time-tracking-ui/TimesheetPeriodFooter.js.map +7 -0
  384. package/dist/modules/staff/lib/time-tracking-ui/TimesheetViewSwitch.js +45 -0
  385. package/dist/modules/staff/lib/time-tracking-ui/TimesheetViewSwitch.js.map +7 -0
  386. package/dist/modules/staff/lib/time-tracking-ui/boardDirectory.js +176 -0
  387. package/dist/modules/staff/lib/time-tracking-ui/boardDirectory.js.map +7 -0
  388. package/dist/modules/staff/lib/time-tracking-ui/boardFilters.js +82 -0
  389. package/dist/modules/staff/lib/time-tracking-ui/boardFilters.js.map +7 -0
  390. package/dist/modules/staff/lib/time-tracking-ui/boardSummary.js +5 -0
  391. package/dist/modules/staff/lib/time-tracking-ui/boardSummary.js.map +7 -0
  392. package/dist/modules/staff/lib/time-tracking-ui/kanbanBoardData.js +152 -0
  393. package/dist/modules/staff/lib/time-tracking-ui/kanbanBoardData.js.map +7 -0
  394. package/dist/modules/staff/lib/time-tracking-ui/projectTeamAssignment.js +92 -0
  395. package/dist/modules/staff/lib/time-tracking-ui/projectTeamAssignment.js.map +7 -0
  396. package/dist/modules/staff/lib/time-tracking-ui/taskDrawerData.js +111 -0
  397. package/dist/modules/staff/lib/time-tracking-ui/taskDrawerData.js.map +7 -0
  398. package/dist/modules/staff/lib/time-tracking-ui/timeEntryDialogState.js +243 -0
  399. package/dist/modules/staff/lib/time-tracking-ui/timeEntryDialogState.js.map +7 -0
  400. package/dist/modules/staff/lib/time-tracking-ui/timeEntryListData.js +151 -0
  401. package/dist/modules/staff/lib/time-tracking-ui/timeEntryListData.js.map +7 -0
  402. package/dist/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.js +105 -0
  403. package/dist/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.js.map +7 -0
  404. package/dist/modules/staff/lib/time-tracking-ui/timesheetData.js +132 -0
  405. package/dist/modules/staff/lib/time-tracking-ui/timesheetData.js.map +7 -0
  406. package/dist/modules/staff/lib/time-tracking-ui/timesheetPeriod.js +205 -0
  407. package/dist/modules/staff/lib/time-tracking-ui/timesheetPeriod.js.map +7 -0
  408. package/dist/modules/staff/lib/time-tracking-ui/timesheetTargets.js +53 -0
  409. package/dist/modules/staff/lib/time-tracking-ui/timesheetTargets.js.map +7 -0
  410. package/dist/modules/staff/lib/time-tracking-ui/useBoardFilters.js +77 -0
  411. package/dist/modules/staff/lib/time-tracking-ui/useBoardFilters.js.map +7 -0
  412. package/dist/modules/staff/lib/time-tracking-ui/useTimesheetPreferences.js +151 -0
  413. package/dist/modules/staff/lib/time-tracking-ui/useTimesheetPreferences.js.map +7 -0
  414. package/dist/modules/staff/lib/timeTrackingSeeds.js +809 -0
  415. package/dist/modules/staff/lib/timeTrackingSeeds.js.map +7 -0
  416. package/dist/modules/staff/lib/timesheets/timeEntryCacheInvalidation.js +2 -3
  417. package/dist/modules/staff/lib/timesheets/timeEntryCacheInvalidation.js.map +2 -2
  418. package/dist/modules/staff/lib/timesheets/timeEntryDecoration.js +152 -0
  419. package/dist/modules/staff/lib/timesheets/timeEntryDecoration.js.map +7 -0
  420. package/dist/modules/staff/lib/timesheets-projects/budgetBurn.js +36 -0
  421. package/dist/modules/staff/lib/timesheets-projects/budgetBurn.js.map +7 -0
  422. package/dist/modules/staff/lib/timesheets-projects/budgetThreshold.js +66 -0
  423. package/dist/modules/staff/lib/timesheets-projects/budgetThreshold.js.map +7 -0
  424. package/dist/modules/staff/lib/timesheets-projects/budgetThresholdState.js +78 -0
  425. package/dist/modules/staff/lib/timesheets-projects/budgetThresholdState.js.map +7 -0
  426. package/dist/modules/staff/lib/timesheets-projects/computeProjectFinancials.js +81 -0
  427. package/dist/modules/staff/lib/timesheets-projects/computeProjectFinancials.js.map +7 -0
  428. package/dist/modules/staff/lib/timesheets-projects/reportSelection.js +44 -0
  429. package/dist/modules/staff/lib/timesheets-projects/reportSelection.js.map +7 -0
  430. package/dist/modules/staff/lib/timesheets-projects-ui/AssignMembersDialog.js +169 -0
  431. package/dist/modules/staff/lib/timesheets-projects-ui/AssignMembersDialog.js.map +7 -0
  432. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectBudgetCell.js +22 -0
  433. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectBudgetCell.js.map +7 -0
  434. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectCard.js +51 -1
  435. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectCard.js.map +2 -2
  436. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectsBulkActionsBar.js +70 -0
  437. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectsBulkActionsBar.js.map +7 -0
  438. package/dist/modules/staff/lib/timesheets-reports/buildReportSheet.js +63 -0
  439. package/dist/modules/staff/lib/timesheets-reports/buildReportSheet.js.map +7 -0
  440. package/dist/modules/staff/lib/timesheets-reports/loadReportData.js +170 -0
  441. package/dist/modules/staff/lib/timesheets-reports/loadReportData.js.map +7 -0
  442. package/dist/modules/staff/lib/timesheets-reports/pdf.js +205 -0
  443. package/dist/modules/staff/lib/timesheets-reports/pdf.js.map +7 -0
  444. package/dist/modules/staff/lib/timesheets-reports/reportApprovalPolicies.js +68 -0
  445. package/dist/modules/staff/lib/timesheets-reports/reportApprovalPolicies.js.map +7 -0
  446. package/dist/modules/staff/lib/timesheets-reports/reportConfigData.js +135 -0
  447. package/dist/modules/staff/lib/timesheets-reports/reportConfigData.js.map +7 -0
  448. package/dist/modules/staff/lib/timesheets-reports/reportConfigState.js +48 -0
  449. package/dist/modules/staff/lib/timesheets-reports/reportConfigState.js.map +7 -0
  450. package/dist/modules/staff/lib/timesheets-reports/reportExport.js +262 -0
  451. package/dist/modules/staff/lib/timesheets-reports/reportExport.js.map +7 -0
  452. package/dist/modules/staff/lib/timesheets-reports/reportExportFormats.js +34 -0
  453. package/dist/modules/staff/lib/timesheets-reports/reportExportFormats.js.map +7 -0
  454. package/dist/modules/staff/lib/timesheets-reports/reportGroupings.js +79 -0
  455. package/dist/modules/staff/lib/timesheets-reports/reportGroupings.js.map +7 -0
  456. package/dist/modules/staff/lib/timesheets-reports/reportLabels.js +11 -0
  457. package/dist/modules/staff/lib/timesheets-reports/reportLabels.js.map +7 -0
  458. package/dist/modules/staff/lib/timesheets-reports/reportReference.js +83 -0
  459. package/dist/modules/staff/lib/timesheets-reports/reportReference.js.map +7 -0
  460. package/dist/modules/staff/lib/timesheets-reports/reportRows.js +42 -0
  461. package/dist/modules/staff/lib/timesheets-reports/reportRows.js.map +7 -0
  462. package/dist/modules/staff/lib/timesheets-reports/reportSheetData.js +107 -0
  463. package/dist/modules/staff/lib/timesheets-reports/reportSheetData.js.map +7 -0
  464. package/dist/modules/staff/lib/timesheets-reports/reportTotals.js +289 -0
  465. package/dist/modules/staff/lib/timesheets-reports/reportTotals.js.map +7 -0
  466. package/dist/modules/staff/lib/timesheets-reports/xlsx.js +145 -0
  467. package/dist/modules/staff/lib/timesheets-reports/xlsx.js.map +7 -0
  468. package/dist/modules/staff/lib/timesheets-tasks/computeTaskRollups.js +116 -0
  469. package/dist/modules/staff/lib/timesheets-tasks/computeTaskRollups.js.map +7 -0
  470. package/dist/modules/staff/lib/timesheets-tasks/defaultStatusTemplate.js +50 -0
  471. package/dist/modules/staff/lib/timesheets-tasks/defaultStatusTemplate.js.map +7 -0
  472. package/dist/modules/staff/lib/timesheets-tasks/seedProjectStatuses.js +27 -0
  473. package/dist/modules/staff/lib/timesheets-tasks/seedProjectStatuses.js.map +7 -0
  474. package/dist/modules/staff/lib/timesheets-tasks/statusPositions.js +65 -0
  475. package/dist/modules/staff/lib/timesheets-tasks/statusPositions.js.map +7 -0
  476. package/dist/modules/staff/lib/timesheets-tasks/statusSlug.js +31 -0
  477. package/dist/modules/staff/lib/timesheets-tasks/statusSlug.js.map +7 -0
  478. package/dist/modules/staff/lib/timesheets-tasks/taskHierarchy.js +31 -0
  479. package/dist/modules/staff/lib/timesheets-tasks/taskHierarchy.js.map +7 -0
  480. package/dist/modules/staff/lib/timesheets-tasks/taskHoursTotals.js +17 -0
  481. package/dist/modules/staff/lib/timesheets-tasks/taskHoursTotals.js.map +7 -0
  482. package/dist/modules/staff/lib/timesheets-tasks/taskReference.js +51 -0
  483. package/dist/modules/staff/lib/timesheets-tasks/taskReference.js.map +7 -0
  484. package/dist/modules/staff/lib/timesheets-ui/CreateProjectDialog.js +11 -4
  485. package/dist/modules/staff/lib/timesheets-ui/CreateProjectDialog.js.map +2 -2
  486. package/dist/modules/staff/lib/timesheets-ui/ListView.js +369 -111
  487. package/dist/modules/staff/lib/timesheets-ui/ListView.js.map +2 -2
  488. package/dist/modules/staff/lib/timesheets-ui/TimerBar.js +47 -2
  489. package/dist/modules/staff/lib/timesheets-ui/TimerBar.js.map +2 -2
  490. package/dist/modules/staff/migrations/Migration20260813073131_staff.js +79 -0
  491. package/dist/modules/staff/migrations/Migration20260813073131_staff.js.map +7 -0
  492. package/dist/modules/staff/migrations/Migration20260824143357_staff.js +15 -0
  493. package/dist/modules/staff/migrations/Migration20260824143357_staff.js.map +7 -0
  494. package/dist/modules/staff/notifications.client.js +16 -0
  495. package/dist/modules/staff/notifications.client.js.map +7 -0
  496. package/dist/modules/staff/notifications.handlers.js +80 -0
  497. package/dist/modules/staff/notifications.handlers.js.map +7 -0
  498. package/dist/modules/staff/notifications.js +168 -0
  499. package/dist/modules/staff/notifications.js.map +2 -2
  500. package/dist/modules/staff/search.js +159 -1
  501. package/dist/modules/staff/search.js.map +2 -2
  502. package/dist/modules/staff/setup.js +20 -1
  503. package/dist/modules/staff/setup.js.map +2 -2
  504. package/dist/modules/staff/subscribers/time-project-access-requested-notification.js +68 -0
  505. package/dist/modules/staff/subscribers/time-project-access-requested-notification.js.map +7 -0
  506. package/dist/modules/staff/subscribers/time-project-budget-threshold-notification.js +149 -0
  507. package/dist/modules/staff/subscribers/time-project-budget-threshold-notification.js.map +7 -0
  508. package/dist/modules/staff/subscribers/time-report-approved-notification.js +78 -0
  509. package/dist/modules/staff/subscribers/time-report-approved-notification.js.map +7 -0
  510. package/dist/modules/staff/subscribers/time-report-portal-broadcast.js +78 -0
  511. package/dist/modules/staff/subscribers/time-report-portal-broadcast.js.map +7 -0
  512. package/dist/modules/staff/translations.js +4 -1
  513. package/dist/modules/staff/translations.js.map +2 -2
  514. package/dist/modules/staff/widgets/components.js +7 -0
  515. package/dist/modules/staff/widgets/components.js.map +7 -0
  516. package/dist/modules/staff/widgets/injection/timer-sidebar-indicator/widget.js +5 -4
  517. package/dist/modules/staff/widgets/injection/timer-sidebar-indicator/widget.js.map +2 -2
  518. package/dist/modules/staff/widgets/notifications/TimeProjectAccessRequestedRenderer.js +135 -0
  519. package/dist/modules/staff/widgets/notifications/TimeProjectAccessRequestedRenderer.js.map +7 -0
  520. package/dist/modules/staff/widgets/notifications/index.js +5 -0
  521. package/dist/modules/staff/widgets/notifications/index.js.map +7 -0
  522. package/dist/modules/staff/workers/timesheets-reapply-rounding.js +43 -0
  523. package/dist/modules/staff/workers/timesheets-reapply-rounding.js.map +7 -0
  524. package/generated/entities/staff_time_entry/index.ts +7 -0
  525. package/generated/entities/staff_time_entry_tag/index.ts +6 -0
  526. package/generated/entities/staff_time_project/index.ts +8 -0
  527. package/generated/entities/staff_time_report/index.ts +27 -0
  528. package/generated/entities/staff_time_report_entry/index.ts +12 -0
  529. package/generated/entities/staff_time_report_event/index.ts +9 -0
  530. package/generated/entities/staff_time_report_project/index.ts +6 -0
  531. package/generated/entities/staff_time_tag/index.ts +9 -0
  532. package/generated/entities/staff_time_task/index.ts +17 -0
  533. package/generated/entities/staff_time_task_comment/index.ts +9 -0
  534. package/generated/entities/staff_time_task_status/index.ts +13 -0
  535. package/generated/entities/staff_time_task_tag/index.ts +6 -0
  536. package/generated/entities.ids.generated.ts +11 -1
  537. package/generated/entity-fields-registry.ts +149 -0
  538. package/package.json +7 -7
  539. package/src/helpers/integration/timesheetFixtures.ts +31 -1
  540. package/src/modules/auth/README.md +1 -1
  541. package/src/modules/auth/cli.ts +35 -1
  542. package/src/modules/auth/i18n/de.json +7 -0
  543. package/src/modules/auth/i18n/en.json +7 -0
  544. package/src/modules/auth/i18n/es.json +7 -0
  545. package/src/modules/auth/i18n/ko.json +7 -0
  546. package/src/modules/auth/i18n/pl.json +7 -0
  547. package/src/modules/auth/lib/backendChrome.tsx +1 -0
  548. package/src/modules/customer_accounts/lib/customerRoleAcls.ts +79 -0
  549. package/src/modules/customer_accounts/setup.ts +1 -45
  550. package/src/modules/data_sync/AGENTS.md +34 -0
  551. package/src/modules/data_sync/api/options.ts +2 -0
  552. package/src/modules/data_sync/backend/data-sync/page.tsx +82 -33
  553. package/src/modules/data_sync/i18n/de.json +1 -0
  554. package/src/modules/data_sync/i18n/en.json +1 -0
  555. package/src/modules/data_sync/i18n/es.json +1 -0
  556. package/src/modules/data_sync/i18n/ko.json +1 -0
  557. package/src/modules/data_sync/i18n/pl.json +1 -0
  558. package/src/modules/data_sync/lib/adapter.ts +45 -0
  559. package/src/modules/data_sync/lib/start-controls.ts +89 -0
  560. package/src/modules/staff/AGENTS.md +769 -1
  561. package/src/modules/staff/acl.ts +9 -0
  562. package/src/modules/staff/ai-agents.ts +189 -0
  563. package/src/modules/staff/ai-tools/time-tracking-pack.ts +494 -0
  564. package/src/modules/staff/ai-tools/types.ts +82 -0
  565. package/src/modules/staff/ai-tools.ts +13 -0
  566. package/src/modules/staff/analytics.ts +113 -0
  567. package/src/modules/staff/api/guards.ts +85 -3
  568. package/src/modules/staff/api/leave-requests/accept/route.ts +0 -2
  569. package/src/modules/staff/api/leave-requests/reject/route.ts +0 -2
  570. package/src/modules/staff/api/portal/time-reports/[id]/route.ts +182 -0
  571. package/src/modules/staff/api/portal/time-reports/route.ts +270 -0
  572. package/src/modules/staff/api/team-members/self/route.ts +0 -2
  573. package/src/modules/staff/api/team-members/tags/assign/route.ts +0 -2
  574. package/src/modules/staff/api/team-members/tags/unassign/route.ts +0 -2
  575. package/src/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.ts +292 -0
  576. package/src/modules/staff/api/timesheets/access-requests/route.ts +299 -0
  577. package/src/modules/staff/api/timesheets/my-projects/[projectId]/route.ts +15 -7
  578. package/src/modules/staff/api/timesheets/my-projects/route.ts +14 -1
  579. package/src/modules/staff/api/timesheets/my-work/myWorkAggregate.ts +90 -0
  580. package/src/modules/staff/api/timesheets/my-work/route.ts +475 -0
  581. package/src/modules/staff/api/timesheets/projects/kpis/route.ts +15 -2
  582. package/src/modules/staff/api/timesheets/reports/[id]/close/route.ts +203 -0
  583. package/src/modules/staff/api/timesheets/reports/[id]/export/route.ts +315 -0
  584. package/src/modules/staff/api/timesheets/reports/[id]/sheet/route.ts +220 -0
  585. package/src/modules/staff/api/timesheets/reports/[id]/unlock/route.ts +208 -0
  586. package/src/modules/staff/api/timesheets/reports/preview/route.ts +449 -0
  587. package/src/modules/staff/api/timesheets/reports/route.ts +386 -0
  588. package/src/modules/staff/api/timesheets/reports/shared.ts +121 -0
  589. package/src/modules/staff/api/timesheets/settings/reapply-rounding/route.ts +205 -0
  590. package/src/modules/staff/api/timesheets/settings/rounding-impact/route.ts +226 -0
  591. package/src/modules/staff/api/timesheets/settings/route.ts +242 -0
  592. package/src/modules/staff/api/timesheets/tags/access.ts +134 -0
  593. package/src/modules/staff/api/timesheets/tags/entry-assignments/route.ts +290 -0
  594. package/src/modules/staff/api/timesheets/tags/route.ts +177 -0
  595. package/src/modules/staff/api/timesheets/tags/task-assignments/route.ts +294 -0
  596. package/src/modules/staff/api/timesheets/task-statuses/route.ts +232 -0
  597. package/src/modules/staff/api/timesheets/tasks/[id]/comments/route.ts +532 -0
  598. package/src/modules/staff/api/timesheets/tasks/[id]/status/route.ts +271 -0
  599. package/src/modules/staff/api/timesheets/tasks/route.ts +443 -0
  600. package/src/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.ts +306 -0
  601. package/src/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.ts +32 -10
  602. package/src/modules/staff/api/timesheets/time-entries/[id]/segments/route.ts +32 -17
  603. package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +27 -6
  604. package/src/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.ts +36 -6
  605. package/src/modules/staff/api/timesheets/time-entries/bulk/route.ts +331 -34
  606. package/src/modules/staff/api/timesheets/time-entries/copy-day/route.ts +533 -0
  607. package/src/modules/staff/api/timesheets/time-entries/overlaps/route.ts +457 -0
  608. package/src/modules/staff/api/timesheets/time-entries/route.ts +378 -24
  609. package/src/modules/staff/api/timesheets/time-entries/start-timer/route.ts +31 -9
  610. package/src/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.ts +246 -0
  611. package/src/modules/staff/api/timesheets/time-projects/[id]/employees/route.ts +62 -2
  612. package/src/modules/staff/api/timesheets/time-projects/route.ts +397 -22
  613. package/src/modules/staff/backend/staff/time-tracking/board/page.meta.ts +14 -0
  614. package/src/modules/staff/backend/staff/time-tracking/board/page.tsx +151 -0
  615. package/src/modules/staff/backend/staff/time-tracking/entries/page.meta.ts +14 -0
  616. package/src/modules/staff/backend/staff/time-tracking/entries/page.tsx +1229 -0
  617. package/src/modules/staff/backend/staff/time-tracking/page.meta.ts +11 -0
  618. package/src/modules/staff/backend/staff/time-tracking/page.tsx +823 -0
  619. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.meta.ts +12 -0
  620. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.tsx +81 -0
  621. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.meta.ts +12 -0
  622. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.tsx +275 -0
  623. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.meta.ts +12 -0
  624. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +1420 -0
  625. package/src/modules/staff/backend/staff/time-tracking/projects/create/page.meta.ts +12 -0
  626. package/src/modules/staff/backend/staff/time-tracking/projects/create/page.tsx +109 -0
  627. package/src/modules/staff/backend/staff/time-tracking/projects/page.meta.ts +15 -0
  628. package/src/modules/staff/backend/staff/time-tracking/projects/page.tsx +1254 -0
  629. package/src/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.ts +466 -0
  630. package/src/modules/staff/backend/staff/time-tracking/reports/[id]/page.meta.ts +12 -0
  631. package/src/modules/staff/backend/staff/time-tracking/reports/[id]/page.tsx +632 -0
  632. package/src/modules/staff/backend/staff/time-tracking/reports/create/page.meta.ts +12 -0
  633. package/src/modules/staff/backend/staff/time-tracking/reports/create/page.tsx +867 -0
  634. package/src/modules/staff/backend/staff/time-tracking/reports/page.meta.ts +14 -0
  635. package/src/modules/staff/backend/staff/time-tracking/reports/page.tsx +246 -0
  636. package/src/modules/staff/backend/staff/time-tracking/settings/page.meta.ts +14 -0
  637. package/src/modules/staff/backend/staff/time-tracking/settings/page.tsx +701 -0
  638. package/src/modules/staff/backend/staff/time-tracking/timesheet/GridView.tsx +953 -0
  639. package/src/modules/staff/backend/staff/time-tracking/timesheet/page.meta.ts +14 -0
  640. package/src/modules/staff/backend/staff/time-tracking/timesheet/page.tsx +800 -0
  641. package/src/modules/staff/backend/staff/timesheets/page.meta.ts +6 -6
  642. package/src/modules/staff/backend/staff/timesheets/page.tsx +12 -1010
  643. package/src/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.ts +0 -6
  644. package/src/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.tsx +4 -158
  645. package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.ts +0 -6
  646. package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.tsx +4 -609
  647. package/src/modules/staff/backend/staff/timesheets/projects/create/page.meta.ts +0 -6
  648. package/src/modules/staff/backend/staff/timesheets/projects/create/page.tsx +3 -89
  649. package/src/modules/staff/backend/staff/timesheets/projects/page.meta.ts +2 -19
  650. package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +3 -778
  651. package/src/modules/staff/ce.ts +63 -0
  652. package/src/modules/staff/cli.ts +207 -1
  653. package/src/modules/staff/commands/index.ts +5 -0
  654. package/src/modules/staff/commands/timesheets-entries.ts +1006 -28
  655. package/src/modules/staff/commands/timesheets-projects.ts +593 -7
  656. package/src/modules/staff/commands/timesheets-reports.ts +1329 -0
  657. package/src/modules/staff/commands/timesheets-tags.ts +1315 -0
  658. package/src/modules/staff/commands/timesheets-task-comments.ts +583 -0
  659. package/src/modules/staff/commands/timesheets-task-statuses.ts +889 -0
  660. package/src/modules/staff/commands/timesheets-tasks.ts +1121 -0
  661. package/src/modules/staff/data/enrichers.ts +675 -10
  662. package/src/modules/staff/data/entities.ts +494 -2
  663. package/src/modules/staff/data/extensions.ts +68 -0
  664. package/src/modules/staff/data/validators.ts +348 -3
  665. package/src/modules/staff/di.ts +96 -0
  666. package/src/modules/staff/events.payloads.ts +268 -0
  667. package/src/modules/staff/events.ts +87 -5
  668. package/src/modules/staff/extension-points.ts +308 -0
  669. package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.meta.ts +10 -0
  670. package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.tsx +225 -0
  671. package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/page.meta.ts +17 -0
  672. package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/page.tsx +182 -0
  673. package/src/modules/staff/i18n/de.json +899 -0
  674. package/src/modules/staff/i18n/en.json +899 -0
  675. package/src/modules/staff/i18n/es.json +899 -0
  676. package/src/modules/staff/i18n/ko.json +899 -0
  677. package/src/modules/staff/i18n/pl.json +899 -0
  678. package/src/modules/staff/lib/crud.ts +14 -3
  679. package/src/modules/staff/lib/time-tracking/access.ts +155 -0
  680. package/src/modules/staff/lib/time-tracking/billability.ts +68 -0
  681. package/src/modules/staff/lib/time-tracking/capacity.ts +124 -0
  682. package/src/modules/staff/lib/time-tracking/componentContracts.ts +26 -0
  683. package/src/modules/staff/lib/time-tracking/cost.ts +144 -0
  684. package/src/modules/staff/lib/time-tracking/duration.ts +68 -0
  685. package/src/modules/staff/lib/time-tracking/featureAccess.ts +115 -0
  686. package/src/modules/staff/lib/time-tracking/interval.ts +90 -0
  687. package/src/modules/staff/lib/time-tracking/migrateProjectCodes.ts +161 -0
  688. package/src/modules/staff/lib/time-tracking/moneyVisibility.ts +37 -0
  689. package/src/modules/staff/lib/time-tracking/overlap.ts +159 -0
  690. package/src/modules/staff/lib/time-tracking/portalRecipients.ts +76 -0
  691. package/src/modules/staff/lib/time-tracking/portalReports.ts +31 -0
  692. package/src/modules/staff/lib/time-tracking/projectCode.ts +221 -0
  693. package/src/modules/staff/lib/time-tracking/reapplyRounding.ts +313 -0
  694. package/src/modules/staff/lib/time-tracking/recalculationRunner.ts +118 -0
  695. package/src/modules/staff/lib/time-tracking/recalculations.ts +135 -0
  696. package/src/modules/staff/lib/time-tracking/registries/invoke.ts +122 -0
  697. package/src/modules/staff/lib/time-tracking/registries/registry.ts +124 -0
  698. package/src/modules/staff/lib/time-tracking/registries/scope.ts +56 -0
  699. package/src/modules/staff/lib/time-tracking/rollup.ts +59 -0
  700. package/src/modules/staff/lib/time-tracking/rounding.ts +126 -0
  701. package/src/modules/staff/lib/time-tracking/roundingImpact.ts +75 -0
  702. package/src/modules/staff/lib/time-tracking/settingKeys.ts +274 -0
  703. package/src/modules/staff/lib/time-tracking/settings.ts +168 -0
  704. package/src/modules/staff/lib/time-tracking/sqlInClause.ts +22 -0
  705. package/src/modules/staff/lib/time-tracking/timeEntrySources.ts +85 -0
  706. package/src/modules/staff/lib/time-tracking-ui/BoardFilterChips.tsx +360 -0
  707. package/src/modules/staff/lib/time-tracking-ui/CustomerPicker.tsx +427 -0
  708. package/src/modules/staff/lib/time-tracking-ui/DurationInput.tsx +192 -0
  709. package/src/modules/staff/lib/time-tracking-ui/EntryDetailsFields.tsx +224 -0
  710. package/src/modules/staff/lib/time-tracking-ui/EntryFilterBar.tsx +144 -0
  711. package/src/modules/staff/lib/time-tracking-ui/KanbanBoard.tsx +1065 -0
  712. package/src/modules/staff/lib/time-tracking-ui/KanbanCard.tsx +435 -0
  713. package/src/modules/staff/lib/time-tracking-ui/KanbanColumn.tsx +316 -0
  714. package/src/modules/staff/lib/time-tracking-ui/NewTaskDialog.tsx +291 -0
  715. package/src/modules/staff/lib/time-tracking-ui/NoProjectAccess.tsx +162 -0
  716. package/src/modules/staff/lib/time-tracking-ui/PeriodSelector.tsx +149 -0
  717. package/src/modules/staff/lib/time-tracking-ui/PhasePlaceholder.tsx +31 -0
  718. package/src/modules/staff/lib/time-tracking-ui/ProjectCodeField.tsx +230 -0
  719. package/src/modules/staff/lib/time-tracking-ui/ProjectCurrencyField.tsx +432 -0
  720. package/src/modules/staff/lib/time-tracking-ui/ProjectFormSections.tsx +323 -0
  721. package/src/modules/staff/lib/time-tracking-ui/ProjectTeamDrawer.tsx +930 -0
  722. package/src/modules/staff/lib/time-tracking-ui/ReportSheet.tsx +357 -0
  723. package/src/modules/staff/lib/time-tracking-ui/TagPicker.tsx +195 -0
  724. package/src/modules/staff/lib/time-tracking-ui/TaskBoardScreen.tsx +243 -0
  725. package/src/modules/staff/lib/time-tracking-ui/TaskCommentThread.tsx +137 -0
  726. package/src/modules/staff/lib/time-tracking-ui/TaskDrawer.tsx +1405 -0
  727. package/src/modules/staff/lib/time-tracking-ui/TaskListView.tsx +260 -0
  728. package/src/modules/staff/lib/time-tracking-ui/TaskPicker.tsx +435 -0
  729. package/src/modules/staff/lib/time-tracking-ui/TaskQuickLog.tsx +296 -0
  730. package/src/modules/staff/lib/time-tracking-ui/TimeEntriesSummaryFooter.tsx +97 -0
  731. package/src/modules/staff/lib/time-tracking-ui/TimeEntryDialog.tsx +1929 -0
  732. package/src/modules/staff/lib/time-tracking-ui/TimeEntryDurationCell.tsx +126 -0
  733. package/src/modules/staff/lib/time-tracking-ui/TimesheetCalendar.tsx +265 -0
  734. package/src/modules/staff/lib/time-tracking-ui/TimesheetPeriodFooter.tsx +78 -0
  735. package/src/modules/staff/lib/time-tracking-ui/TimesheetViewSwitch.tsx +70 -0
  736. package/src/modules/staff/lib/time-tracking-ui/boardDirectory.ts +231 -0
  737. package/src/modules/staff/lib/time-tracking-ui/boardFilters.ts +128 -0
  738. package/src/modules/staff/lib/time-tracking-ui/boardSummary.ts +20 -0
  739. package/src/modules/staff/lib/time-tracking-ui/kanbanBoardData.ts +228 -0
  740. package/src/modules/staff/lib/time-tracking-ui/projectTeamAssignment.ts +143 -0
  741. package/src/modules/staff/lib/time-tracking-ui/taskDrawerData.ts +194 -0
  742. package/src/modules/staff/lib/time-tracking-ui/timeEntryDialogState.ts +400 -0
  743. package/src/modules/staff/lib/time-tracking-ui/timeEntryListData.ts +271 -0
  744. package/src/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.ts +168 -0
  745. package/src/modules/staff/lib/time-tracking-ui/timesheetData.ts +225 -0
  746. package/src/modules/staff/lib/time-tracking-ui/timesheetPeriod.ts +278 -0
  747. package/src/modules/staff/lib/time-tracking-ui/timesheetTargets.ts +93 -0
  748. package/src/modules/staff/lib/time-tracking-ui/useBoardFilters.ts +107 -0
  749. package/src/modules/staff/lib/time-tracking-ui/useTimesheetPreferences.ts +245 -0
  750. package/src/modules/staff/lib/timeTrackingSeeds.ts +1037 -0
  751. package/src/modules/staff/lib/timesheets/timeEntryCacheInvalidation.ts +11 -10
  752. package/src/modules/staff/lib/timesheets/timeEntryDecoration.ts +256 -0
  753. package/src/modules/staff/lib/timesheets-projects/budgetBurn.ts +60 -0
  754. package/src/modules/staff/lib/timesheets-projects/budgetThreshold.ts +135 -0
  755. package/src/modules/staff/lib/timesheets-projects/budgetThresholdState.ts +137 -0
  756. package/src/modules/staff/lib/timesheets-projects/computeProjectFinancials.ts +142 -0
  757. package/src/modules/staff/lib/timesheets-projects/reportSelection.ts +82 -0
  758. package/src/modules/staff/lib/timesheets-projects-ui/AssignMembersDialog.tsx +237 -0
  759. package/src/modules/staff/lib/timesheets-projects-ui/ProjectBudgetCell.tsx +33 -0
  760. package/src/modules/staff/lib/timesheets-projects-ui/ProjectCard.tsx +56 -1
  761. package/src/modules/staff/lib/timesheets-projects-ui/ProjectsBulkActionsBar.tsx +90 -0
  762. package/src/modules/staff/lib/timesheets-reports/buildReportSheet.ts +125 -0
  763. package/src/modules/staff/lib/timesheets-reports/loadReportData.ts +251 -0
  764. package/src/modules/staff/lib/timesheets-reports/pdf.ts +263 -0
  765. package/src/modules/staff/lib/timesheets-reports/reportApprovalPolicies.ts +137 -0
  766. package/src/modules/staff/lib/timesheets-reports/reportConfigData.ts +247 -0
  767. package/src/modules/staff/lib/timesheets-reports/reportConfigState.ts +89 -0
  768. package/src/modules/staff/lib/timesheets-reports/reportExport.ts +373 -0
  769. package/src/modules/staff/lib/timesheets-reports/reportExportFormats.ts +71 -0
  770. package/src/modules/staff/lib/timesheets-reports/reportGroupings.ts +153 -0
  771. package/src/modules/staff/lib/timesheets-reports/reportLabels.ts +23 -0
  772. package/src/modules/staff/lib/timesheets-reports/reportReference.ts +146 -0
  773. package/src/modules/staff/lib/timesheets-reports/reportRows.ts +89 -0
  774. package/src/modules/staff/lib/timesheets-reports/reportSheetData.ts +196 -0
  775. package/src/modules/staff/lib/timesheets-reports/reportTotals.ts +544 -0
  776. package/src/modules/staff/lib/timesheets-reports/xlsx.ts +207 -0
  777. package/src/modules/staff/lib/timesheets-tasks/computeTaskRollups.ts +238 -0
  778. package/src/modules/staff/lib/timesheets-tasks/defaultStatusTemplate.ts +90 -0
  779. package/src/modules/staff/lib/timesheets-tasks/seedProjectStatuses.ts +43 -0
  780. package/src/modules/staff/lib/timesheets-tasks/statusPositions.ts +132 -0
  781. package/src/modules/staff/lib/timesheets-tasks/statusSlug.ts +43 -0
  782. package/src/modules/staff/lib/timesheets-tasks/taskHierarchy.ts +99 -0
  783. package/src/modules/staff/lib/timesheets-tasks/taskHoursTotals.ts +42 -0
  784. package/src/modules/staff/lib/timesheets-tasks/taskReference.ts +108 -0
  785. package/src/modules/staff/lib/timesheets-ui/CreateProjectDialog.tsx +18 -4
  786. package/src/modules/staff/lib/timesheets-ui/ListView.tsx +504 -149
  787. package/src/modules/staff/lib/timesheets-ui/TimerBar.tsx +50 -2
  788. package/src/modules/staff/migrations/.snapshot-open-mercato.json +3244 -228
  789. package/src/modules/staff/migrations/Migration20260813073131_staff.ts +94 -0
  790. package/src/modules/staff/migrations/Migration20260824143357_staff.ts +17 -0
  791. package/src/modules/staff/notifications.client.ts +16 -0
  792. package/src/modules/staff/notifications.handlers.ts +88 -0
  793. package/src/modules/staff/notifications.ts +163 -0
  794. package/src/modules/staff/search.ts +191 -1
  795. package/src/modules/staff/setup.ts +22 -0
  796. package/src/modules/staff/subscribers/time-project-access-requested-notification.ts +95 -0
  797. package/src/modules/staff/subscribers/time-project-budget-threshold-notification.ts +226 -0
  798. package/src/modules/staff/subscribers/time-report-approved-notification.ts +116 -0
  799. package/src/modules/staff/subscribers/time-report-portal-broadcast.ts +120 -0
  800. package/src/modules/staff/translations.ts +17 -0
  801. package/src/modules/staff/widgets/components.ts +34 -0
  802. package/src/modules/staff/widgets/injection/timer-sidebar-indicator/widget.tsx +6 -5
  803. package/src/modules/staff/widgets/notifications/TimeProjectAccessRequestedRenderer.tsx +138 -0
  804. package/src/modules/staff/widgets/notifications/index.ts +1 -0
  805. package/src/modules/staff/workers/timesheets-reapply-rounding.ts +59 -0
  806. package/dist/modules/staff/backend/staff/timesheets/projects/projectFormConfig.js +0 -123
  807. package/dist/modules/staff/backend/staff/timesheets/projects/projectFormConfig.js.map +0 -7
  808. package/src/modules/staff/backend/staff/timesheets/projects/projectFormConfig.ts +0 -138
@@ -0,0 +1,289 @@
1
+ import { applicableRate, entryAmount, round2, sumAmounts } from "../time-tracking/cost.js";
2
+ import {
3
+ getReportGrouping,
4
+ isBuiltInReportGrouping,
5
+ REPORT_GROUPING_REGISTRY_ID,
6
+ DEFAULT_REPORT_GROUPING
7
+ } from "./reportGroupings.js";
8
+ import { runStrategy } from "../time-tracking/registries/invoke.js";
9
+ function finiteOrNull(value) {
10
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
11
+ }
12
+ function effectiveMinutes(entry) {
13
+ const rounded = finiteOrNull(entry.roundedMinutes);
14
+ if (rounded !== null) return rounded;
15
+ const raw = finiteOrNull(entry.durationMinutes);
16
+ return raw ?? 0;
17
+ }
18
+ function resolveEntryValues(entry, project) {
19
+ const frozen = entry.frozen;
20
+ if (frozen) {
21
+ return {
22
+ entryId: entry.id,
23
+ minutes: finiteOrNull(frozen.roundedMinutes) ?? 0,
24
+ rawMinutes: finiteOrNull(frozen.rawMinutes) ?? 0,
25
+ rate: finiteOrNull(frozen.rateAmount),
26
+ amount: frozen.isBillable ? finiteOrNull(frozen.amount) : null,
27
+ isBillable: frozen.isBillable,
28
+ hasOverride: finiteOrNull(entry.rateOverrideAmount) !== null,
29
+ isFrozen: true
30
+ };
31
+ }
32
+ const minutes = effectiveMinutes(entry);
33
+ const override = finiteOrNull(entry.rateOverrideAmount);
34
+ const rate = applicableRate({ rateOverrideAmount: override }, project ?? null);
35
+ return {
36
+ entryId: entry.id,
37
+ minutes,
38
+ rawMinutes: finiteOrNull(entry.durationMinutes) ?? 0,
39
+ rate: entry.isBillable ? rate : null,
40
+ amount: entryAmount(
41
+ { isBillable: entry.isBillable, roundedMinutes: minutes, rateOverrideAmount: override },
42
+ project ?? null
43
+ ),
44
+ isBillable: entry.isBillable,
45
+ hasOverride: override !== null,
46
+ isFrozen: false
47
+ };
48
+ }
49
+ function isAlreadyReported(entry, currentReportId) {
50
+ const frozen = entry.frozen;
51
+ if (!frozen) return false;
52
+ if (currentReportId && frozen.reportId === currentReportId) return false;
53
+ return true;
54
+ }
55
+ function distinctRate(values) {
56
+ let rate = null;
57
+ let seen = false;
58
+ for (const value of values) {
59
+ if (!value.isBillable) continue;
60
+ if (!seen) {
61
+ rate = value.rate;
62
+ seen = true;
63
+ continue;
64
+ }
65
+ if (value.rate !== rate) return null;
66
+ }
67
+ return rate;
68
+ }
69
+ function makeBucket(key, label) {
70
+ return { key, label, values: [], children: /* @__PURE__ */ new Map() };
71
+ }
72
+ function bucketToLine(bucket) {
73
+ const childLines = Array.from(bucket.children.values()).map(bucketToLine);
74
+ const allValues = collectValues(bucket);
75
+ return {
76
+ key: bucket.key,
77
+ label: bucket.label,
78
+ minutes: allValues.reduce((total, value) => total + value.minutes, 0),
79
+ rate: distinctRate(allValues),
80
+ amount: sumAmounts(allValues.map((value) => value.amount)),
81
+ entryCount: allValues.length,
82
+ hasOverride: allValues.some((value) => value.hasOverride),
83
+ children: childLines
84
+ };
85
+ }
86
+ function collectValues(bucket) {
87
+ const values = [...bucket.values];
88
+ for (const child of bucket.children.values()) values.push(...collectValues(child));
89
+ return values;
90
+ }
91
+ function resolveGroupingStrategy(grouping) {
92
+ const strategy = getReportGrouping(grouping) ?? getReportGrouping(DEFAULT_REPORT_GROUPING);
93
+ if (!strategy) throw new Error("[internal] no report grouping strategy is registered");
94
+ return strategy;
95
+ }
96
+ function guardGrouping(strategy) {
97
+ if (isBuiltInReportGrouping(strategy.id)) return strategy;
98
+ const fallback = getReportGrouping(DEFAULT_REPORT_GROUPING);
99
+ if (!fallback) throw new Error("[internal] no report grouping strategy is registered");
100
+ const guard = (run, recover) => runStrategy(REPORT_GROUPING_REGISTRY_ID, strategy.id, run, recover);
101
+ return {
102
+ ...strategy,
103
+ groupOf: (entry) => guard(() => strategy.groupOf(entry), () => fallback.groupOf(entry)),
104
+ labelOf: (key, ctx) => guard(() => strategy.labelOf(key, ctx), () => fallback.labelOf(key, ctx)),
105
+ sort: (left, right) => guard(() => strategy.sort(left, right), () => fallback.sort(left, right))
106
+ };
107
+ }
108
+ function computeReportTotals(input) {
109
+ const { entries, projects, directory, options, labels } = input;
110
+ const grouping = guardGrouping(resolveGroupingStrategy(options.grouping));
111
+ const labelContext = { directory, fallbacks: labels };
112
+ const lineKeyFor = (entry) => grouping.groupOf(entry);
113
+ const labelFor = (key) => grouping.labelOf(key, labelContext);
114
+ const currentReportId = input.currentReportId ?? null;
115
+ const projectById = new Map(projects.map((project) => [project.id, project]));
116
+ const projectOrder = projects.map((project) => project.id);
117
+ const alreadyReportedBySource = /* @__PURE__ */ new Map();
118
+ let alreadyReportedCount = 0;
119
+ let alreadyReportedMinutes = 0;
120
+ const billableBuckets = /* @__PURE__ */ new Map();
121
+ const nonbillableValues = [];
122
+ const nonbillableBuckets = /* @__PURE__ */ new Map();
123
+ const projectValues = /* @__PURE__ */ new Map();
124
+ for (const entry of entries) {
125
+ const skipped = isAlreadyReported(entry, currentReportId) && !options.includeAlreadyReported;
126
+ if (skipped) {
127
+ const frozen = entry.frozen;
128
+ const minutes = finiteOrNull(frozen?.roundedMinutes ?? null) ?? effectiveMinutes(entry);
129
+ alreadyReportedCount += 1;
130
+ alreadyReportedMinutes += minutes;
131
+ if (frozen) {
132
+ const existing = alreadyReportedBySource.get(frozen.reportId);
133
+ if (existing) {
134
+ existing.entryCount += 1;
135
+ existing.minutes += minutes;
136
+ } else {
137
+ alreadyReportedBySource.set(frozen.reportId, {
138
+ reportId: frozen.reportId,
139
+ reference: frozen.reference,
140
+ title: frozen.title,
141
+ entryCount: 1,
142
+ minutes
143
+ });
144
+ }
145
+ }
146
+ continue;
147
+ }
148
+ const project = projectById.get(entry.timeProjectId) ?? null;
149
+ const values = resolveEntryValues(entry, project);
150
+ if (!values.isBillable) {
151
+ if (options.nonbillableMode === "exclude") continue;
152
+ nonbillableValues.push(values);
153
+ const { key: key2 } = lineKeyFor(entry);
154
+ const label = labelFor(key2);
155
+ let bucket2 = nonbillableBuckets.get(key2);
156
+ if (!bucket2) {
157
+ bucket2 = makeBucket(key2, label);
158
+ nonbillableBuckets.set(key2, bucket2);
159
+ }
160
+ bucket2.values.push(values);
161
+ continue;
162
+ }
163
+ const perProject = projectValues.get(entry.timeProjectId) ?? [];
164
+ perProject.push(values);
165
+ projectValues.set(entry.timeProjectId, perProject);
166
+ let lines = billableBuckets.get(entry.timeProjectId);
167
+ if (!lines) {
168
+ lines = /* @__PURE__ */ new Map();
169
+ billableBuckets.set(entry.timeProjectId, lines);
170
+ }
171
+ const { key, parentKey } = lineKeyFor(entry);
172
+ if (parentKey) {
173
+ let parent = lines.get(parentKey);
174
+ if (!parent) {
175
+ parent = makeBucket(parentKey, labelFor(parentKey));
176
+ lines.set(parentKey, parent);
177
+ }
178
+ let child = parent.children.get(key);
179
+ if (!child) {
180
+ child = makeBucket(key, labelFor(key));
181
+ parent.children.set(key, child);
182
+ }
183
+ child.values.push(values);
184
+ continue;
185
+ }
186
+ let bucket = lines.get(key);
187
+ if (!bucket) {
188
+ bucket = makeBucket(key, labelFor(key));
189
+ lines.set(key, bucket);
190
+ }
191
+ bucket.values.push(values);
192
+ }
193
+ const groups = [];
194
+ for (const projectId of projectOrder) {
195
+ const values = projectValues.get(projectId);
196
+ if (!values || values.length === 0) continue;
197
+ const project = projectById.get(projectId);
198
+ const lines = Array.from((billableBuckets.get(projectId) ?? /* @__PURE__ */ new Map()).values()).map(
199
+ bucketToLine
200
+ );
201
+ lines.sort(grouping.sort);
202
+ groups.push({
203
+ key: projectId,
204
+ kind: "project",
205
+ label: project?.name ?? projectId,
206
+ rate: finiteOrNull(project?.hourlyRate ?? null),
207
+ minutes: values.reduce((total, value) => total + value.minutes, 0),
208
+ // Exact sum of already-rounded entry amounts — never a re-derivation from
209
+ // the group's minutes and rate, which would disagree the moment a line
210
+ // carries an override.
211
+ amount: sumAmounts(values.map((value) => value.amount)),
212
+ entryCount: values.length,
213
+ lines
214
+ });
215
+ }
216
+ if (nonbillableValues.length > 0) {
217
+ const lines = Array.from(nonbillableBuckets.values()).map(bucketToLine);
218
+ lines.sort(grouping.sort);
219
+ groups.push({
220
+ key: "__nonbillable__",
221
+ kind: "nonbillable",
222
+ label: labels.nonbillableGroup,
223
+ rate: null,
224
+ minutes: nonbillableValues.reduce((total, value) => total + value.minutes, 0),
225
+ amount: 0,
226
+ entryCount: nonbillableValues.length,
227
+ lines
228
+ });
229
+ }
230
+ const billableValues = Array.from(projectValues.values()).flat();
231
+ return {
232
+ groups,
233
+ billableMinutes: billableValues.reduce((total, value) => total + value.minutes, 0),
234
+ nonbillableMinutes: nonbillableValues.reduce((total, value) => total + value.minutes, 0),
235
+ entryCount: billableValues.length + nonbillableValues.length,
236
+ // The grand total is a sum over ENTRIES, so regrouping cannot move it.
237
+ totalAmount: sumAmounts(billableValues.map((value) => value.amount)),
238
+ alreadyReportedCount,
239
+ alreadyReportedMinutes,
240
+ alreadyReportedIn: Array.from(alreadyReportedBySource.values())
241
+ };
242
+ }
243
+ function selectIncludedEntries(entries, options, currentReportId = null) {
244
+ const included = [];
245
+ for (const entry of entries) {
246
+ if (isAlreadyReported(entry, currentReportId) && !options.includeAlreadyReported) continue;
247
+ const billable = entry.frozen ? entry.frozen.isBillable : entry.isBillable;
248
+ if (!billable && options.nonbillableMode === "exclude") continue;
249
+ included.push(entry);
250
+ }
251
+ return included;
252
+ }
253
+ function resolveReportCurrency(projects) {
254
+ const byCurrency = /* @__PURE__ */ new Map();
255
+ for (const project of projects) {
256
+ const code = typeof project.currencyCode === "string" ? project.currencyCode.trim().toUpperCase() : "";
257
+ if (!code) continue;
258
+ const bucket = byCurrency.get(code);
259
+ if (bucket) bucket.push(project);
260
+ else byCurrency.set(code, [project]);
261
+ }
262
+ if (byCurrency.size > 1) {
263
+ return {
264
+ ok: false,
265
+ currencies: Array.from(byCurrency.keys()).sort((a, b) => a < b ? -1 : a > b ? 1 : 0),
266
+ offenders: Array.from(byCurrency.values()).flat()
267
+ };
268
+ }
269
+ const [only] = Array.from(byCurrency.keys());
270
+ return { ok: true, currencyCode: only ?? null };
271
+ }
272
+ function formatReportMinutes(minutes) {
273
+ const safe = Number.isFinite(minutes) ? Math.max(0, Math.round(minutes)) : 0;
274
+ const hours = Math.floor(safe / 60);
275
+ const rest = safe % 60;
276
+ return `${hours}:${String(rest).padStart(2, "0")}`;
277
+ }
278
+ export {
279
+ computeReportTotals,
280
+ effectiveMinutes,
281
+ formatReportMinutes,
282
+ isAlreadyReported,
283
+ resolveEntryValues,
284
+ resolveReportCurrency,
285
+ round2,
286
+ selectIncludedEntries,
287
+ sumAmounts
288
+ };
289
+ //# sourceMappingURL=reportTotals.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/timesheets-reports/reportTotals.ts"],
4
+ "sourcesContent": ["/**\n * The money of a customer report \u2014 screens 13 and 14, decisions D-2, D-5 and D-7.\n *\n * Three rules are load-bearing here and everything else in this file exists to\n * keep them true.\n *\n * **D-7 \u2014 round at the entry, then sum upward.** `entryAmount` from\n * `lib/time-tracking/cost.ts` is the ONLY place an amount is produced, and every\n * level above it (line, group, grand total) is an exact integer-cent sum of\n * already-rounded values via `sumAmounts`. Two properties fall out, and both are\n * asserted by this module's tests:\n *\n * - a client adding up the printed lines reaches our total, because the total\n * is literally that addition;\n * - the grand total does not move when the grouping changes from task to\n * person to day, because it is a sum over ENTRIES and never over lines, so\n * redrawing the line boundaries cannot redistribute a rounding remainder.\n *\n * **D-2 \u2014 a task line is an inclusive rollup.** A child task's time folds into\n * its parent's line and is expandable underneath, so a client-facing sheet stays\n * at the level they contracted for. The line aggregates ENTRIES, never other\n * lines (risk R10), so a parent line is never a sum of a child line plus itself.\n *\n * **D-5 \u2014 an hour frozen in a closed report is excluded by default.** Excluded\n * entries are not silently dropped: they are counted, their minutes are summed\n * and the reports that froze them are named, so the config screen can show what\n * is being skipped. When the opt-in is ticked they re-enter at their FROZEN\n * values \u2014 the amount the client was already billed \u2014 not at whatever today's\n * rounding rule and today's project rate would produce.\n */\n\nimport { applicableRate, entryAmount, round2, sumAmounts } from '../time-tracking/cost'\nimport {\n getReportGrouping,\n isBuiltInReportGrouping,\n REPORT_GROUPING_REGISTRY_ID,\n DEFAULT_REPORT_GROUPING,\n UNASSIGNED_LINE_KEY,\n type ReportGrouping,\n type ReportGroupingLabelContext,\n type ReportGroupingStrategy,\n} from './reportGroupings'\nimport { runStrategy } from '../time-tracking/registries/invoke'\n\nexport type { ReportGrouping }\nexport type ReportNonBillableMode = 'separate' | 'exclude'\n\n/** The freeze record of an entry already closed into an earlier report. */\nexport type FrozenEntryValues = {\n reportId: string\n reference: string | null\n title: string | null\n rawMinutes: number\n roundedMinutes: number\n rateAmount: number | null\n currencyCode: string | null\n amount: number | null\n isBillable: boolean\n}\n\nexport type ReportInputEntry = {\n id: string\n timeProjectId: string\n /** The task the entry is logged against, or null for project-level time. */\n taskId: string | null\n /** Resolved top-level ancestor of `taskId` (D-2); equals `taskId` for a root task. */\n rootTaskId: string | null\n staffMemberId: string | null\n /** `yyyy-mm-dd`. */\n date: string\n durationMinutes: number\n roundedMinutes: number | null\n isBillable: boolean\n rateOverrideAmount: number | null\n description: string | null\n /** Present when this entry is already frozen in a closed report (D-5). */\n frozen: FrozenEntryValues | null\n}\n\nexport type ReportInputProject = {\n id: string\n name: string\n hourlyRate: number | null\n currencyCode: string | null\n}\n\nexport type ReportDirectory = {\n taskLabelById: Readonly<Record<string, string>>\n personLabelById: Readonly<Record<string, string>>\n}\n\nexport type ReportTotalsOptions = {\n grouping: ReportGrouping\n nonbillableMode: ReportNonBillableMode\n includeAlreadyReported: boolean\n}\n\n/** What one entry actually contributes, after D-5 and D-7 have been applied. */\nexport type ResolvedEntryValues = {\n entryId: string\n minutes: number\n rawMinutes: number\n rate: number | null\n amount: number | null\n isBillable: boolean\n hasOverride: boolean\n isFrozen: boolean\n}\n\nexport type ReportLine = {\n key: string\n label: string\n minutes: number\n /** The rate every entry on this line agreed on, or null when they disagree. */\n rate: number | null\n amount: number\n entryCount: number\n hasOverride: boolean\n /** Child-task rollup detail (D-2); empty for person and day groupings. */\n children: ReportLine[]\n}\n\nexport type ReportGroup = {\n key: string\n kind: 'project' | 'nonbillable'\n label: string\n rate: number | null\n minutes: number\n amount: number\n entryCount: number\n lines: ReportLine[]\n}\n\nexport type AlreadyReportedSource = {\n reportId: string\n reference: string | null\n title: string | null\n entryCount: number\n minutes: number\n}\n\nexport type ReportTotals = {\n groups: ReportGroup[]\n billableMinutes: number\n nonbillableMinutes: number\n entryCount: number\n totalAmount: number\n /** Entries skipped because they are frozen elsewhere (D-5). Zero when opted in. */\n alreadyReportedCount: number\n alreadyReportedMinutes: number\n alreadyReportedIn: AlreadyReportedSource[]\n}\n\n\nfunction finiteOrNull(value: number | null | undefined): number | null {\n return typeof value === 'number' && Number.isFinite(value) ? value : null\n}\n\n/**\n * The minutes an entry bills at. `rounded_minutes` is written by every entry\n * write path (T4.2), but an entry created before that column existed carries\n * null \u2014 falling back to the raw duration bills the truth rather than zero.\n */\nexport function effectiveMinutes(entry: Pick<ReportInputEntry, 'durationMinutes' | 'roundedMinutes'>): number {\n const rounded = finiteOrNull(entry.roundedMinutes)\n if (rounded !== null) return rounded\n const raw = finiteOrNull(entry.durationMinutes)\n return raw ?? 0\n}\n\n/**\n * D-7 in one place: the only per-entry money computation in the report path.\n * A frozen entry restates what it was billed at; a live entry is costed from\n * its rounded minutes and its applicable rate.\n */\nexport function resolveEntryValues(\n entry: ReportInputEntry,\n project: ReportInputProject | null | undefined,\n): ResolvedEntryValues {\n const frozen = entry.frozen\n if (frozen) {\n return {\n entryId: entry.id,\n minutes: finiteOrNull(frozen.roundedMinutes) ?? 0,\n rawMinutes: finiteOrNull(frozen.rawMinutes) ?? 0,\n rate: finiteOrNull(frozen.rateAmount),\n amount: frozen.isBillable ? finiteOrNull(frozen.amount) : null,\n isBillable: frozen.isBillable,\n hasOverride: finiteOrNull(entry.rateOverrideAmount) !== null,\n isFrozen: true,\n }\n }\n\n const minutes = effectiveMinutes(entry)\n const override = finiteOrNull(entry.rateOverrideAmount)\n // EP-33: the override \u2192 project-rate chain is the rate registry's built-in, so\n // the report path asks it rather than restating the chain here.\n const rate = applicableRate({ rateOverrideAmount: override }, project ?? null)\n return {\n entryId: entry.id,\n minutes,\n rawMinutes: finiteOrNull(entry.durationMinutes) ?? 0,\n rate: entry.isBillable ? rate : null,\n amount: entryAmount(\n { isBillable: entry.isBillable, roundedMinutes: minutes, rateOverrideAmount: override },\n project ?? null,\n ),\n isBillable: entry.isBillable,\n hasOverride: override !== null,\n isFrozen: false,\n }\n}\n\n/**\n * D-5's gate. An entry counts as already reported when it carries a freeze\n * record from a report other than the one being computed.\n */\nexport function isAlreadyReported(entry: ReportInputEntry, currentReportId: string | null): boolean {\n const frozen = entry.frozen\n if (!frozen) return false\n if (currentReportId && frozen.reportId === currentReportId) return false\n return true\n}\n\nfunction distinctRate(values: readonly ResolvedEntryValues[]): number | null {\n let rate: number | null = null\n let seen = false\n for (const value of values) {\n if (!value.isBillable) continue\n if (!seen) {\n rate = value.rate\n seen = true\n continue\n }\n if (value.rate !== rate) return null\n }\n return rate\n}\n\ntype LineBucket = {\n key: string\n label: string\n values: ResolvedEntryValues[]\n children: Map<string, LineBucket>\n}\n\nfunction makeBucket(key: string, label: string): LineBucket {\n return { key, label, values: [], children: new Map() }\n}\n\nfunction bucketToLine(bucket: LineBucket): ReportLine {\n const childLines = Array.from(bucket.children.values()).map(bucketToLine)\n // The parent line aggregates its own ENTRIES plus every child's entries \u2014 never\n // the child lines' amounts (risk R10). Because `sumAmounts` works in integer\n // cents, aggregating entries and aggregating already-summed children agree\n // exactly; entries are used so the rule is visible in the code.\n const allValues = collectValues(bucket)\n return {\n key: bucket.key,\n label: bucket.label,\n minutes: allValues.reduce((total, value) => total + value.minutes, 0),\n rate: distinctRate(allValues),\n amount: sumAmounts(allValues.map((value) => value.amount)),\n entryCount: allValues.length,\n hasOverride: allValues.some((value) => value.hasOverride),\n children: childLines,\n }\n}\n\nfunction collectValues(bucket: LineBucket): ResolvedEntryValues[] {\n const values = [...bucket.values]\n for (const child of bucket.children.values()) values.push(...collectValues(child))\n return values\n}\n\n/**\n * Resolving the strategy once per computation keeps a stored id whose module has\n * been removed from silently producing a different sheet on every call.\n */\nfunction resolveGroupingStrategy(grouping: ReportGrouping): ReportGroupingStrategy {\n const strategy = getReportGrouping(grouping) ?? getReportGrouping(DEFAULT_REPORT_GROUPING)\n if (!strategy) throw new Error('[internal] no report grouping strategy is registered')\n return strategy\n}\n\n/**\n * A grouping contributes three functions that run once per entry, once per line and\n * O(n log n) times per project, all in the middle of rendering a client-facing\n * sheet. A thrower in any of them used to abort the whole report; it now degrades to\n * the module default for that one call, so the sheet still renders \u2014 with the\n * grouping the module shipped rather than none at all. The default itself is exempt:\n * there is nothing left to fall back to, and it cannot be a contribution.\n */\nfunction guardGrouping(strategy: ReportGroupingStrategy): ReportGroupingStrategy {\n if (isBuiltInReportGrouping(strategy.id)) return strategy\n const fallback = getReportGrouping(DEFAULT_REPORT_GROUPING)\n if (!fallback) throw new Error('[internal] no report grouping strategy is registered')\n const guard = <TResult>(run: () => TResult, recover: () => TResult): TResult =>\n runStrategy(REPORT_GROUPING_REGISTRY_ID, strategy.id, run, recover)\n return {\n ...strategy,\n groupOf: (entry) => guard(() => strategy.groupOf(entry), () => fallback.groupOf(entry)),\n labelOf: (key, ctx) => guard(() => strategy.labelOf(key, ctx), () => fallback.labelOf(key, ctx)),\n sort: (left, right) => guard(() => strategy.sort(left, right), () => fallback.sort(left, right)),\n }\n}\n\nexport type ComputeReportTotalsInput = {\n entries: readonly ReportInputEntry[]\n projects: readonly ReportInputProject[]\n directory: ReportDirectory\n options: ReportTotalsOptions\n /** Excludes this report's OWN freeze records from the already-reported gate. */\n currentReportId?: string | null\n labels: {\n unassignedTask: string\n unassignedPerson: string\n nonbillableGroup: string\n }\n}\n\nexport function computeReportTotals(input: ComputeReportTotalsInput): ReportTotals {\n const { entries, projects, directory, options, labels } = input\n const grouping = guardGrouping(resolveGroupingStrategy(options.grouping))\n const labelContext: ReportGroupingLabelContext = { directory, fallbacks: labels }\n const lineKeyFor = (entry: ReportInputEntry) => grouping.groupOf(entry)\n const labelFor = (key: string) => grouping.labelOf(key, labelContext)\n const currentReportId = input.currentReportId ?? null\n const projectById = new Map(projects.map((project) => [project.id, project]))\n const projectOrder = projects.map((project) => project.id)\n\n const alreadyReportedBySource = new Map<string, AlreadyReportedSource>()\n let alreadyReportedCount = 0\n let alreadyReportedMinutes = 0\n\n const billableBuckets = new Map<string, Map<string, LineBucket>>()\n const nonbillableValues: ResolvedEntryValues[] = []\n const nonbillableBuckets = new Map<string, LineBucket>()\n const projectValues = new Map<string, ResolvedEntryValues[]>()\n\n for (const entry of entries) {\n const skipped = isAlreadyReported(entry, currentReportId) && !options.includeAlreadyReported\n if (skipped) {\n const frozen = entry.frozen\n const minutes = finiteOrNull(frozen?.roundedMinutes ?? null) ?? effectiveMinutes(entry)\n alreadyReportedCount += 1\n alreadyReportedMinutes += minutes\n if (frozen) {\n const existing = alreadyReportedBySource.get(frozen.reportId)\n if (existing) {\n existing.entryCount += 1\n existing.minutes += minutes\n } else {\n alreadyReportedBySource.set(frozen.reportId, {\n reportId: frozen.reportId,\n reference: frozen.reference,\n title: frozen.title,\n entryCount: 1,\n minutes,\n })\n }\n }\n continue\n }\n\n const project = projectById.get(entry.timeProjectId) ?? null\n const values = resolveEntryValues(entry, project)\n\n if (!values.isBillable) {\n // `exclude` drops non-billable time entirely; `separate` shows it in its own\n // group at zero so the client sees the full effort with an unambiguous total.\n if (options.nonbillableMode === 'exclude') continue\n nonbillableValues.push(values)\n const { key } = lineKeyFor(entry)\n const label = labelFor(key)\n let bucket = nonbillableBuckets.get(key)\n if (!bucket) {\n bucket = makeBucket(key, label)\n nonbillableBuckets.set(key, bucket)\n }\n bucket.values.push(values)\n continue\n }\n\n const perProject = projectValues.get(entry.timeProjectId) ?? []\n perProject.push(values)\n projectValues.set(entry.timeProjectId, perProject)\n\n let lines = billableBuckets.get(entry.timeProjectId)\n if (!lines) {\n lines = new Map<string, LineBucket>()\n billableBuckets.set(entry.timeProjectId, lines)\n }\n\n const { key, parentKey } = lineKeyFor(entry)\n if (parentKey) {\n let parent = lines.get(parentKey)\n if (!parent) {\n parent = makeBucket(parentKey, labelFor(parentKey))\n lines.set(parentKey, parent)\n }\n let child = parent.children.get(key)\n if (!child) {\n child = makeBucket(key, labelFor(key))\n parent.children.set(key, child)\n }\n child.values.push(values)\n continue\n }\n\n let bucket = lines.get(key)\n if (!bucket) {\n bucket = makeBucket(key, labelFor(key))\n lines.set(key, bucket)\n }\n bucket.values.push(values)\n }\n\n const groups: ReportGroup[] = []\n for (const projectId of projectOrder) {\n const values = projectValues.get(projectId)\n if (!values || values.length === 0) continue\n const project = projectById.get(projectId)\n const lines = Array.from((billableBuckets.get(projectId) ?? new Map<string, LineBucket>()).values()).map(\n bucketToLine,\n )\n lines.sort(grouping.sort)\n groups.push({\n key: projectId,\n kind: 'project',\n label: project?.name ?? projectId,\n rate: finiteOrNull(project?.hourlyRate ?? null),\n minutes: values.reduce((total, value) => total + value.minutes, 0),\n // Exact sum of already-rounded entry amounts \u2014 never a re-derivation from\n // the group's minutes and rate, which would disagree the moment a line\n // carries an override.\n amount: sumAmounts(values.map((value) => value.amount)),\n entryCount: values.length,\n lines,\n })\n }\n\n if (nonbillableValues.length > 0) {\n const lines = Array.from(nonbillableBuckets.values()).map(bucketToLine)\n lines.sort(grouping.sort)\n groups.push({\n key: '__nonbillable__',\n kind: 'nonbillable',\n label: labels.nonbillableGroup,\n rate: null,\n minutes: nonbillableValues.reduce((total, value) => total + value.minutes, 0),\n amount: 0,\n entryCount: nonbillableValues.length,\n lines,\n })\n }\n\n const billableValues = Array.from(projectValues.values()).flat()\n\n return {\n groups,\n billableMinutes: billableValues.reduce((total, value) => total + value.minutes, 0),\n nonbillableMinutes: nonbillableValues.reduce((total, value) => total + value.minutes, 0),\n entryCount: billableValues.length + nonbillableValues.length,\n // The grand total is a sum over ENTRIES, so regrouping cannot move it.\n totalAmount: sumAmounts(billableValues.map((value) => value.amount)),\n alreadyReportedCount,\n alreadyReportedMinutes,\n alreadyReportedIn: Array.from(alreadyReportedBySource.values()),\n }\n}\n\n/**\n * The entries a report actually covers, after D-5 and the non-billable mode have\n * been applied. Close writes one freeze record per entry this returns, so the\n * set that is frozen is by construction the same set the sheet printed \u2014 the two\n * cannot drift.\n */\nexport function selectIncludedEntries(\n entries: readonly ReportInputEntry[],\n options: ReportTotalsOptions,\n currentReportId: string | null = null,\n): ReportInputEntry[] {\n const included: ReportInputEntry[] = []\n for (const entry of entries) {\n if (isAlreadyReported(entry, currentReportId) && !options.includeAlreadyReported) continue\n // A frozen entry's billable flag is the frozen one, matching what\n // `resolveEntryValues` reads, so the two never disagree about which group a\n // re-included entry belongs to.\n const billable = entry.frozen ? entry.frozen.isBillable : entry.isBillable\n if (!billable && options.nonbillableMode === 'exclude') continue\n included.push(entry)\n }\n return included\n}\n\nexport type CurrencyConflictProject = {\n id: string\n name: string\n currencyCode: string | null\n}\n\nexport type ReportCurrencyResolution =\n | { ok: true; currencyCode: string | null }\n | { ok: false; currencies: string[]; offenders: CurrencyConflictProject[] }\n\n/**\n * Risk R2: a report never sums across currencies. Currency codes are stored\n * upper-cased at the validator boundary (watch item W6), so this comparison is a\n * plain equality rather than a case-insensitive one; the normalization is\n * repeated here anyway because a row written before W6 closed would otherwise\n * read as a second currency.\n */\nexport function resolveReportCurrency(\n projects: readonly CurrencyConflictProject[],\n): ReportCurrencyResolution {\n const byCurrency = new Map<string, CurrencyConflictProject[]>()\n for (const project of projects) {\n const code = typeof project.currencyCode === 'string' ? project.currencyCode.trim().toUpperCase() : ''\n if (!code) continue\n const bucket = byCurrency.get(code)\n if (bucket) bucket.push(project)\n else byCurrency.set(code, [project])\n }\n if (byCurrency.size > 1) {\n return {\n ok: false,\n currencies: Array.from(byCurrency.keys()).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)),\n offenders: Array.from(byCurrency.values()).flat(),\n }\n }\n const [only] = Array.from(byCurrency.keys())\n return { ok: true, currencyCode: only ?? null }\n}\n\n/** Formats `385` as `6:25`, the `t-mono` duration the report sheet prints. */\nexport function formatReportMinutes(minutes: number): string {\n const safe = Number.isFinite(minutes) ? Math.max(0, Math.round(minutes)) : 0\n const hours = Math.floor(safe / 60)\n const rest = safe % 60\n return `${hours}:${String(rest).padStart(2, '0')}`\n}\n\nexport { round2, sumAmounts }\n"],
5
+ "mappings": "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",
6
+ "names": ["key", "bucket"]
7
+ }
@@ -0,0 +1,145 @@
1
+ import { deflateRawSync } from "node:zlib";
2
+ const CRC_TABLE = (() => {
3
+ const table = new Int32Array(256);
4
+ for (let index = 0; index < 256; index += 1) {
5
+ let value = index;
6
+ for (let bit = 0; bit < 8; bit += 1) {
7
+ value = value & 1 ? 3988292384 ^ value >>> 1 : value >>> 1;
8
+ }
9
+ table[index] = value;
10
+ }
11
+ return table;
12
+ })();
13
+ function crc32(data) {
14
+ let crc = -1;
15
+ for (let index = 0; index < data.length; index += 1) {
16
+ crc = crc >>> 8 ^ CRC_TABLE[(crc ^ data[index]) & 255];
17
+ }
18
+ return (crc ^ -1) >>> 0;
19
+ }
20
+ function escapeXml(value) {
21
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;").replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F]/g, "");
22
+ }
23
+ function columnName(index) {
24
+ let name = "";
25
+ let remaining = index + 1;
26
+ while (remaining > 0) {
27
+ const rest = (remaining - 1) % 26;
28
+ name = String.fromCharCode(65 + rest) + name;
29
+ remaining = Math.floor((remaining - 1) / 26);
30
+ }
31
+ return name;
32
+ }
33
+ function cellXml(value, reference) {
34
+ if (value === null || value === void 0 || value === "") return "";
35
+ if (typeof value === "number" && Number.isFinite(value)) {
36
+ return `<c r="${reference}"><v>${value}</v></c>`;
37
+ }
38
+ return `<c r="${reference}" t="inlineStr"><is><t xml:space="preserve">${escapeXml(String(value))}</t></is></c>`;
39
+ }
40
+ function buildSheetXml(sheet) {
41
+ const rows = sheet.rows.map((row, rowIndex) => {
42
+ const cells = row.map((value, columnIndex) => cellXml(value, `${columnName(columnIndex)}${rowIndex + 1}`)).join("");
43
+ return `<row r="${rowIndex + 1}">${cells}</row>`;
44
+ }).join("");
45
+ return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData>${rows}</sheetData></worksheet>`;
46
+ }
47
+ function zipEntries(entries) {
48
+ const locals = [];
49
+ const centrals = [];
50
+ let offset = 0;
51
+ for (const entry of entries) {
52
+ const nameBuffer = Buffer.from(entry.name, "utf8");
53
+ const compressed = deflateRawSync(entry.data);
54
+ const crc = crc32(entry.data);
55
+ const local = Buffer.alloc(30 + nameBuffer.length);
56
+ local.writeUInt32LE(67324752, 0);
57
+ local.writeUInt16LE(20, 4);
58
+ local.writeUInt16LE(2048, 6);
59
+ local.writeUInt16LE(8, 8);
60
+ local.writeUInt16LE(0, 10);
61
+ local.writeUInt16LE(0, 12);
62
+ local.writeUInt32LE(crc, 14);
63
+ local.writeUInt32LE(compressed.length, 18);
64
+ local.writeUInt32LE(entry.data.length, 22);
65
+ local.writeUInt16LE(nameBuffer.length, 26);
66
+ local.writeUInt16LE(0, 28);
67
+ nameBuffer.copy(local, 30);
68
+ const central = Buffer.alloc(46 + nameBuffer.length);
69
+ central.writeUInt32LE(33639248, 0);
70
+ central.writeUInt16LE(20, 4);
71
+ central.writeUInt16LE(20, 6);
72
+ central.writeUInt16LE(2048, 8);
73
+ central.writeUInt16LE(8, 10);
74
+ central.writeUInt16LE(0, 12);
75
+ central.writeUInt16LE(0, 14);
76
+ central.writeUInt32LE(crc, 16);
77
+ central.writeUInt32LE(compressed.length, 20);
78
+ central.writeUInt32LE(entry.data.length, 24);
79
+ central.writeUInt16LE(nameBuffer.length, 28);
80
+ central.writeUInt16LE(0, 30);
81
+ central.writeUInt16LE(0, 32);
82
+ central.writeUInt16LE(0, 34);
83
+ central.writeUInt16LE(0, 36);
84
+ central.writeUInt32LE(0, 38);
85
+ central.writeUInt32LE(offset, 42);
86
+ nameBuffer.copy(central, 46);
87
+ locals.push(local, compressed);
88
+ centrals.push(central);
89
+ offset += local.length + compressed.length;
90
+ }
91
+ const centralDirectory = Buffer.concat(centrals);
92
+ const end = Buffer.alloc(22);
93
+ end.writeUInt32LE(101010256, 0);
94
+ end.writeUInt16LE(0, 4);
95
+ end.writeUInt16LE(0, 6);
96
+ end.writeUInt16LE(entries.length, 8);
97
+ end.writeUInt16LE(entries.length, 10);
98
+ end.writeUInt32LE(centralDirectory.length, 12);
99
+ end.writeUInt32LE(offset, 16);
100
+ end.writeUInt16LE(0, 20);
101
+ return Buffer.concat([...locals, centralDirectory, end]);
102
+ }
103
+ const XLSX_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
104
+ function buildXlsx(sheet) {
105
+ const safeName = escapeXml(sheet.name.slice(0, 31) || "Sheet1");
106
+ const parts = [
107
+ {
108
+ name: "[Content_Types].xml",
109
+ data: Buffer.from(
110
+ '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/></Types>',
111
+ "utf8"
112
+ )
113
+ },
114
+ {
115
+ name: "_rels/.rels",
116
+ data: Buffer.from(
117
+ '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>',
118
+ "utf8"
119
+ )
120
+ },
121
+ {
122
+ name: "xl/workbook.xml",
123
+ data: Buffer.from(
124
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheets><sheet name="${safeName}" sheetId="1" r:id="rId1"/></sheets></workbook>`,
125
+ "utf8"
126
+ )
127
+ },
128
+ {
129
+ name: "xl/_rels/workbook.xml.rels",
130
+ data: Buffer.from(
131
+ '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/></Relationships>',
132
+ "utf8"
133
+ )
134
+ },
135
+ { name: "xl/worksheets/sheet1.xml", data: Buffer.from(buildSheetXml(sheet), "utf8") }
136
+ ];
137
+ return zipEntries(parts);
138
+ }
139
+ export {
140
+ XLSX_CONTENT_TYPE,
141
+ buildSheetXml,
142
+ buildXlsx,
143
+ columnName
144
+ };
145
+ //# sourceMappingURL=xlsx.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/timesheets-reports/xlsx.ts"],
4
+ "sourcesContent": ["/**\n * A minimal, dependency-free single-sheet XLSX writer.\n *\n * US-G4 asks for CSV *and* XLSX because back-offices differ: CSV is what an\n * import script wants, XLSX is what an accountant opens. The repository has no\n * spreadsheet library and adding a production dependency is an \"ask first\"\n * change, so this writes the format directly \u2014 an XLSX file is a ZIP of five\n * small XML parts, and Node ships both DEFLATE (`node:zlib`) and everything else\n * needed.\n *\n * Numbers are written as numbers (`<v>`), not as text, so a column of amounts\n * sums in Excel rather than needing a re-type. Strings go in as inline strings\n * rather than through a shared-strings table: it costs a few bytes on repeated\n * values and removes an entire index that could go out of sync.\n */\n\nimport { deflateRawSync } from 'node:zlib'\n\nexport type XlsxCell = string | number | null | undefined\n\nexport type XlsxSheet = {\n name: string\n rows: XlsxCell[][]\n}\n\nconst CRC_TABLE = (() => {\n const table = new Int32Array(256)\n for (let index = 0; index < 256; index += 1) {\n let value = index\n for (let bit = 0; bit < 8; bit += 1) {\n value = value & 1 ? 0xedb88320 ^ (value >>> 1) : value >>> 1\n }\n table[index] = value\n }\n return table\n})()\n\nfunction crc32(data: Buffer): number {\n let crc = -1\n for (let index = 0; index < data.length; index += 1) {\n crc = (crc >>> 8) ^ CRC_TABLE[(crc ^ data[index]) & 0xff]\n }\n return (crc ^ -1) >>> 0\n}\n\nfunction escapeXml(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&apos;')\n // Excel refuses a file containing raw control characters.\n .replace(/[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F]/g, '')\n}\n\n/** `0 -> A`, `26 -> AA`. Spreadsheet columns are bijective base-26. */\nexport function columnName(index: number): string {\n let name = ''\n let remaining = index + 1\n while (remaining > 0) {\n const rest = (remaining - 1) % 26\n name = String.fromCharCode(65 + rest) + name\n remaining = Math.floor((remaining - 1) / 26)\n }\n return name\n}\n\nfunction cellXml(value: XlsxCell, reference: string): string {\n if (value === null || value === undefined || value === '') return ''\n if (typeof value === 'number' && Number.isFinite(value)) {\n return `<c r=\"${reference}\"><v>${value}</v></c>`\n }\n return `<c r=\"${reference}\" t=\"inlineStr\"><is><t xml:space=\"preserve\">${escapeXml(String(value))}</t></is></c>`\n}\n\nexport function buildSheetXml(sheet: XlsxSheet): string {\n const rows = sheet.rows\n .map((row, rowIndex) => {\n const cells = row.map((value, columnIndex) => cellXml(value, `${columnName(columnIndex)}${rowIndex + 1}`)).join('')\n return `<row r=\"${rowIndex + 1}\">${cells}</row>`\n })\n .join('')\n return `<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"><sheetData>${rows}</sheetData></worksheet>`\n}\n\ntype ZipEntry = { name: string; data: Buffer }\n\nfunction zipEntries(entries: readonly ZipEntry[]): Buffer {\n const locals: Buffer[] = []\n const centrals: Buffer[] = []\n let offset = 0\n\n for (const entry of entries) {\n const nameBuffer = Buffer.from(entry.name, 'utf8')\n const compressed = deflateRawSync(entry.data)\n const crc = crc32(entry.data)\n\n const local = Buffer.alloc(30 + nameBuffer.length)\n local.writeUInt32LE(0x04034b50, 0)\n local.writeUInt16LE(20, 4)\n // Bit 11 marks the filename as UTF-8; all names here are ASCII, but the flag\n // costs nothing and keeps the file honest if one ever is not.\n local.writeUInt16LE(0x0800, 6)\n local.writeUInt16LE(8, 8)\n local.writeUInt16LE(0, 10)\n local.writeUInt16LE(0, 12)\n local.writeUInt32LE(crc, 14)\n local.writeUInt32LE(compressed.length, 18)\n local.writeUInt32LE(entry.data.length, 22)\n local.writeUInt16LE(nameBuffer.length, 26)\n local.writeUInt16LE(0, 28)\n nameBuffer.copy(local, 30)\n\n const central = Buffer.alloc(46 + nameBuffer.length)\n central.writeUInt32LE(0x02014b50, 0)\n central.writeUInt16LE(20, 4)\n central.writeUInt16LE(20, 6)\n central.writeUInt16LE(0x0800, 8)\n central.writeUInt16LE(8, 10)\n central.writeUInt16LE(0, 12)\n central.writeUInt16LE(0, 14)\n central.writeUInt32LE(crc, 16)\n central.writeUInt32LE(compressed.length, 20)\n central.writeUInt32LE(entry.data.length, 24)\n central.writeUInt16LE(nameBuffer.length, 28)\n central.writeUInt16LE(0, 30)\n central.writeUInt16LE(0, 32)\n central.writeUInt16LE(0, 34)\n central.writeUInt16LE(0, 36)\n central.writeUInt32LE(0, 38)\n central.writeUInt32LE(offset, 42)\n nameBuffer.copy(central, 46)\n\n locals.push(local, compressed)\n centrals.push(central)\n offset += local.length + compressed.length\n }\n\n const centralDirectory = Buffer.concat(centrals)\n const end = Buffer.alloc(22)\n end.writeUInt32LE(0x06054b50, 0)\n end.writeUInt16LE(0, 4)\n end.writeUInt16LE(0, 6)\n end.writeUInt16LE(entries.length, 8)\n end.writeUInt16LE(entries.length, 10)\n end.writeUInt32LE(centralDirectory.length, 12)\n end.writeUInt32LE(offset, 16)\n end.writeUInt16LE(0, 20)\n\n return Buffer.concat([...locals, centralDirectory, end])\n}\n\nexport const XLSX_CONTENT_TYPE =\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'\n\nexport function buildXlsx(sheet: XlsxSheet): Buffer {\n const safeName = escapeXml(sheet.name.slice(0, 31) || 'Sheet1')\n const parts: ZipEntry[] = [\n {\n name: '[Content_Types].xml',\n data: Buffer.from(\n '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>' +\n '<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">' +\n '<Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/>' +\n '<Default Extension=\"xml\" ContentType=\"application/xml\"/>' +\n '<Override PartName=\"/xl/workbook.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml\"/>' +\n '<Override PartName=\"/xl/worksheets/sheet1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/>' +\n '</Types>',\n 'utf8',\n ),\n },\n {\n name: '_rels/.rels',\n data: Buffer.from(\n '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>' +\n '<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">' +\n '<Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument\" Target=\"xl/workbook.xml\"/>' +\n '</Relationships>',\n 'utf8',\n ),\n },\n {\n name: 'xl/workbook.xml',\n data: Buffer.from(\n '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>' +\n '<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">' +\n `<sheets><sheet name=\"${safeName}\" sheetId=\"1\" r:id=\"rId1\"/></sheets>` +\n '</workbook>',\n 'utf8',\n ),\n },\n {\n name: 'xl/_rels/workbook.xml.rels',\n data: Buffer.from(\n '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>' +\n '<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">' +\n '<Relationship Id=\"rId1\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet\" Target=\"worksheets/sheet1.xml\"/>' +\n '</Relationships>',\n 'utf8',\n ),\n },\n { name: 'xl/worksheets/sheet1.xml', data: Buffer.from(buildSheetXml(sheet), 'utf8') },\n ]\n\n return zipEntries(parts)\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,116 @@
1
+ import { loggedMinutes } from "../time-tracking/rollup.js";
2
+ const EMPTY_TASK_ROLLUP = {
3
+ ownMinutes: 0,
4
+ loggedMinutes: 0,
5
+ childCount: 0,
6
+ doneChildCount: 0
7
+ };
8
+ function toMinutes(value) {
9
+ if (value === null || value === void 0) return 0;
10
+ const parsed = typeof value === "number" ? value : Number(value);
11
+ return Number.isFinite(parsed) ? parsed : 0;
12
+ }
13
+ function allowedProjectIds(input) {
14
+ const pageIds = new Set(input.taskIds);
15
+ const allowed = /* @__PURE__ */ new Set();
16
+ for (const task of input.tasks) {
17
+ if (!pageIds.has(task.id) || task.deletedAt) continue;
18
+ if (task.timeProjectId) allowed.add(task.timeProjectId);
19
+ }
20
+ return allowed;
21
+ }
22
+ function countDoneChildren(taskId, allTasks, doneByTaskId) {
23
+ let done = 0;
24
+ for (const candidate of allTasks) {
25
+ if (candidate.parentTaskId !== taskId || candidate.id === taskId || candidate.deletedAt) continue;
26
+ if (doneByTaskId.get(candidate.id) === true) done += 1;
27
+ }
28
+ return done;
29
+ }
30
+ function summarizeTaskRollups(input) {
31
+ const allowed = allowedProjectIds(input);
32
+ const taskById = /* @__PURE__ */ new Map();
33
+ const doneByTaskId = /* @__PURE__ */ new Map();
34
+ for (const task of input.tasks) {
35
+ if (taskById.has(task.id)) continue;
36
+ taskById.set(task.id, { id: task.id, parentTaskId: task.parentTaskId ?? null, deletedAt: task.deletedAt ?? null });
37
+ doneByTaskId.set(task.id, task.isDone === true);
38
+ }
39
+ const allTasks = [...taskById.values()];
40
+ const entriesByTaskId = /* @__PURE__ */ new Map();
41
+ for (const group of input.entryGroups) {
42
+ if (group.timeProjectId && !allowed.has(group.timeProjectId)) continue;
43
+ const bucket = entriesByTaskId.get(group.taskId) ?? [];
44
+ bucket.push({ durationMinutes: toMinutes(group.minutes), deletedAt: group.deletedAt ?? null });
45
+ entriesByTaskId.set(group.taskId, bucket);
46
+ }
47
+ const result = /* @__PURE__ */ new Map();
48
+ for (const taskId of input.taskIds) {
49
+ const task = taskById.get(taskId) ?? { id: taskId, parentTaskId: null };
50
+ result.set(taskId, {
51
+ ...loggedMinutes(task, allTasks, entriesByTaskId),
52
+ doneChildCount: countDoneChildren(taskId, allTasks, doneByTaskId)
53
+ });
54
+ }
55
+ return result;
56
+ }
57
+ function toDoneFlag(value) {
58
+ if (typeof value === "boolean") return value;
59
+ return value === "t" || value === "true";
60
+ }
61
+ async function computeTaskRollups(scope) {
62
+ const taskIds = [...new Set(scope.taskIds)].filter((id) => typeof id === "string" && id.length > 0);
63
+ if (taskIds.length === 0) return /* @__PURE__ */ new Map();
64
+ const placeholders = taskIds.map(() => "?").join(", ");
65
+ const params = [scope.tenantId, scope.organizationId, ...taskIds, ...taskIds];
66
+ const sql = `
67
+ SELECT
68
+ t.id AS task_id,
69
+ t.parent_task_id,
70
+ t.time_project_id AS task_project_id,
71
+ COALESCE(s.is_done, false) AS task_is_done,
72
+ e.time_project_id AS entry_project_id,
73
+ COALESCE(SUM(e.duration_minutes), 0)::bigint AS minutes
74
+ FROM staff_time_tasks t
75
+ LEFT JOIN staff_time_task_statuses s
76
+ ON s.id = t.task_status_id
77
+ AND s.tenant_id = t.tenant_id
78
+ AND s.organization_id = t.organization_id
79
+ AND s.deleted_at IS NULL
80
+ LEFT JOIN staff_time_entries e
81
+ ON e.task_id = t.id
82
+ AND e.tenant_id = t.tenant_id
83
+ AND e.organization_id = t.organization_id
84
+ AND e.deleted_at IS NULL
85
+ WHERE t.tenant_id = ?
86
+ AND t.organization_id = ?
87
+ AND t.deleted_at IS NULL
88
+ AND (t.id IN (${placeholders}) OR t.parent_task_id IN (${placeholders}))
89
+ GROUP BY 1, 2, 3, 4, 5
90
+ `;
91
+ const rows = await scope.em.getConnection().execute(sql, params);
92
+ const tasks = [];
93
+ const entryGroups = [];
94
+ for (const row of rows) {
95
+ tasks.push({
96
+ id: row.task_id,
97
+ parentTaskId: row.parent_task_id,
98
+ timeProjectId: row.task_project_id,
99
+ isDone: toDoneFlag(row.task_is_done),
100
+ deletedAt: null
101
+ });
102
+ entryGroups.push({
103
+ taskId: row.task_id,
104
+ timeProjectId: row.entry_project_id,
105
+ minutes: toMinutes(row.minutes),
106
+ deletedAt: null
107
+ });
108
+ }
109
+ return summarizeTaskRollups({ taskIds, tasks, entryGroups });
110
+ }
111
+ export {
112
+ EMPTY_TASK_ROLLUP,
113
+ computeTaskRollups,
114
+ summarizeTaskRollups
115
+ };
116
+ //# sourceMappingURL=computeTaskRollups.js.map