@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,80 @@
1
+ import { createLogger } from "@open-mercato/shared/lib/logger";
2
+ import {
3
+ emptyRecalculationSummary,
4
+ resolveTimeTrackingRecalculations
5
+ } from "./recalculations.js";
6
+ const logger = createLogger("staff").child({ component: "time-tracking/recalculation-runner" });
7
+ function progressContextFor(scope) {
8
+ return {
9
+ tenantId: scope.tenantId,
10
+ organizationId: scope.organizationIds?.length === 1 ? scope.organizationIds[0] : null,
11
+ userId: scope.userId ?? null
12
+ };
13
+ }
14
+ function accumulate(target, addend) {
15
+ target.totalCount += addend.totalCount;
16
+ target.processedCount += addend.processedCount;
17
+ target.updatedCount += addend.updatedCount;
18
+ target.unchangedCount += addend.unchangedCount;
19
+ target.skippedCount += addend.skippedCount;
20
+ target.cancelled = target.cancelled || addend.cancelled;
21
+ }
22
+ async function runTimeTrackingRecalculations(params) {
23
+ const { container, progressJobId, scope } = params;
24
+ const hooks = resolveTimeTrackingRecalculations(params.hookIds);
25
+ const progressService = container.resolve("progressService");
26
+ const progressContext = progressContextFor(scope);
27
+ await progressService.startJob(progressJobId, progressContext);
28
+ const summary = { ...emptyRecalculationSummary(), hooks: [] };
29
+ let knownTotal = 0;
30
+ let processed = 0;
31
+ try {
32
+ for (const hook of hooks) {
33
+ const hookSummary = await hook.run({
34
+ container,
35
+ scope,
36
+ report: {
37
+ setTotal: async (total) => {
38
+ knownTotal += total;
39
+ await progressService.updateProgress(
40
+ progressJobId,
41
+ { totalCount: knownTotal, processedCount: Math.min(processed, knownTotal) },
42
+ progressContext
43
+ );
44
+ },
45
+ advance: async (count) => {
46
+ processed += count;
47
+ await progressService.updateProgress(
48
+ progressJobId,
49
+ { totalCount: knownTotal, processedCount: Math.min(processed, knownTotal) },
50
+ progressContext
51
+ );
52
+ },
53
+ isCancellationRequested: () => progressService.isCancellationRequested(
54
+ progressJobId,
55
+ scope.tenantId,
56
+ progressContext.organizationId
57
+ )
58
+ }
59
+ });
60
+ summary.hooks.push({ id: hook.id, ...hookSummary });
61
+ accumulate(summary, hookSummary);
62
+ if (hookSummary.cancelled) break;
63
+ }
64
+ } catch (err) {
65
+ logger.error("staff.timesheets recalculation failed", { progressJobId, ...summary, err });
66
+ throw err;
67
+ }
68
+ if (summary.cancelled) {
69
+ logger.info("staff.timesheets recalculation cancelled", { progressJobId, ...summary });
70
+ await progressService.markCancelled(progressJobId, progressContext);
71
+ return summary;
72
+ }
73
+ await progressService.completeJob(progressJobId, { resultSummary: { ...summary } }, progressContext);
74
+ logger.info("staff.timesheets recalculation finished", { progressJobId, ...summary });
75
+ return summary;
76
+ }
77
+ export {
78
+ runTimeTrackingRecalculations
79
+ };
80
+ //# sourceMappingURL=recalculationRunner.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/time-tracking/recalculationRunner.ts"],
4
+ "sourcesContent": ["/**\n * EP-51 \u2014 the job shell every registered recalculation runs inside.\n *\n * It owns the `ProgressJob` (start, totals, cancel, complete) and hands each hook\n * a reporter scoped to that job, so N hooks share one progress bar instead of\n * fighting over it. Hooks run **sequentially** and in registry order: they write\n * the same rows, and two of them racing over `staff_time_entries` is the kind of\n * bug that shows up as a wrong invoice a month later.\n *\n * A hook that throws does not silently vanish \u2014 the runner logs it and rethrows,\n * because a half-finished restatement the operator believes succeeded is worse\n * than a loud failure. Marking the `ProgressJob` failed is the worker's job: it\n * already does that for every exception that reaches it, and doing it in both\n * places would write the failure twice.\n */\n\nimport type { AwilixContainer } from 'awilix'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport type { ProgressService, ProgressServiceContext } from '../../../progress/lib/progressService'\nimport {\n emptyRecalculationSummary,\n resolveTimeTrackingRecalculations,\n type TimeTrackingRecalculationScope,\n type TimeTrackingRecalculationSummary,\n} from './recalculations'\n\nconst logger = createLogger('staff').child({ component: 'time-tracking/recalculation-runner' })\n\nexport type TimeTrackingRecalculationRunSummary = TimeTrackingRecalculationSummary & {\n hooks: Array<{ id: string } & TimeTrackingRecalculationSummary>\n}\n\nfunction progressContextFor(scope: TimeTrackingRecalculationScope): ProgressServiceContext {\n return {\n tenantId: scope.tenantId,\n organizationId: scope.organizationIds?.length === 1 ? scope.organizationIds[0] : null,\n userId: scope.userId ?? null,\n }\n}\n\nfunction accumulate(\n target: TimeTrackingRecalculationSummary,\n addend: TimeTrackingRecalculationSummary,\n): void {\n target.totalCount += addend.totalCount\n target.processedCount += addend.processedCount\n target.updatedCount += addend.updatedCount\n target.unchangedCount += addend.unchangedCount\n target.skippedCount += addend.skippedCount\n target.cancelled = target.cancelled || addend.cancelled\n}\n\nexport async function runTimeTrackingRecalculations(params: {\n container: AwilixContainer\n progressJobId: string\n scope: TimeTrackingRecalculationScope\n hookIds?: readonly string[] | null\n}): Promise<TimeTrackingRecalculationRunSummary> {\n const { container, progressJobId, scope } = params\n const hooks = resolveTimeTrackingRecalculations(params.hookIds)\n const progressService = container.resolve('progressService') as ProgressService\n const progressContext = progressContextFor(scope)\n\n await progressService.startJob(progressJobId, progressContext)\n\n const summary: TimeTrackingRecalculationRunSummary = { ...emptyRecalculationSummary(), hooks: [] }\n let knownTotal = 0\n let processed = 0\n\n try {\n for (const hook of hooks) {\n const hookSummary = await hook.run({\n container,\n scope,\n report: {\n setTotal: async (total) => {\n knownTotal += total\n await progressService.updateProgress(\n progressJobId,\n { totalCount: knownTotal, processedCount: Math.min(processed, knownTotal) },\n progressContext,\n )\n },\n advance: async (count) => {\n processed += count\n await progressService.updateProgress(\n progressJobId,\n { totalCount: knownTotal, processedCount: Math.min(processed, knownTotal) },\n progressContext,\n )\n },\n isCancellationRequested: () =>\n progressService.isCancellationRequested(\n progressJobId,\n scope.tenantId,\n progressContext.organizationId,\n ),\n },\n })\n summary.hooks.push({ id: hook.id, ...hookSummary })\n accumulate(summary, hookSummary)\n if (hookSummary.cancelled) break\n }\n } catch (err) {\n logger.error('staff.timesheets recalculation failed', { progressJobId, ...summary, err })\n throw err\n }\n\n if (summary.cancelled) {\n logger.info('staff.timesheets recalculation cancelled', { progressJobId, ...summary })\n await progressService.markCancelled(progressJobId, progressContext)\n return summary\n }\n\n await progressService.completeJob(progressJobId, { resultSummary: { ...summary } }, progressContext)\n logger.info('staff.timesheets recalculation finished', { progressJobId, ...summary })\n return summary\n}\n"],
5
+ "mappings": "AAiBA,SAAS,oBAAoB;AAE7B;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAEP,MAAM,SAAS,aAAa,OAAO,EAAE,MAAM,EAAE,WAAW,qCAAqC,CAAC;AAM9F,SAAS,mBAAmB,OAA+D;AACzF,SAAO;AAAA,IACL,UAAU,MAAM;AAAA,IAChB,gBAAgB,MAAM,iBAAiB,WAAW,IAAI,MAAM,gBAAgB,CAAC,IAAI;AAAA,IACjF,QAAQ,MAAM,UAAU;AAAA,EAC1B;AACF;AAEA,SAAS,WACP,QACA,QACM;AACN,SAAO,cAAc,OAAO;AAC5B,SAAO,kBAAkB,OAAO;AAChC,SAAO,gBAAgB,OAAO;AAC9B,SAAO,kBAAkB,OAAO;AAChC,SAAO,gBAAgB,OAAO;AAC9B,SAAO,YAAY,OAAO,aAAa,OAAO;AAChD;AAEA,eAAsB,8BAA8B,QAKH;AAC/C,QAAM,EAAE,WAAW,eAAe,MAAM,IAAI;AAC5C,QAAM,QAAQ,kCAAkC,OAAO,OAAO;AAC9D,QAAM,kBAAkB,UAAU,QAAQ,iBAAiB;AAC3D,QAAM,kBAAkB,mBAAmB,KAAK;AAEhD,QAAM,gBAAgB,SAAS,eAAe,eAAe;AAE7D,QAAM,UAA+C,EAAE,GAAG,0BAA0B,GAAG,OAAO,CAAC,EAAE;AACjG,MAAI,aAAa;AACjB,MAAI,YAAY;AAEhB,MAAI;AACF,eAAW,QAAQ,OAAO;AACxB,YAAM,cAAc,MAAM,KAAK,IAAI;AAAA,QACjC;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,UACN,UAAU,OAAO,UAAU;AACzB,0BAAc;AACd,kBAAM,gBAAgB;AAAA,cACpB;AAAA,cACA,EAAE,YAAY,YAAY,gBAAgB,KAAK,IAAI,WAAW,UAAU,EAAE;AAAA,cAC1E;AAAA,YACF;AAAA,UACF;AAAA,UACA,SAAS,OAAO,UAAU;AACxB,yBAAa;AACb,kBAAM,gBAAgB;AAAA,cACpB;AAAA,cACA,EAAE,YAAY,YAAY,gBAAgB,KAAK,IAAI,WAAW,UAAU,EAAE;AAAA,cAC1E;AAAA,YACF;AAAA,UACF;AAAA,UACA,yBAAyB,MACvB,gBAAgB;AAAA,YACd;AAAA,YACA,MAAM;AAAA,YACN,gBAAgB;AAAA,UAClB;AAAA,QACJ;AAAA,MACF,CAAC;AACD,cAAQ,MAAM,KAAK,EAAE,IAAI,KAAK,IAAI,GAAG,YAAY,CAAC;AAClD,iBAAW,SAAS,WAAW;AAC/B,UAAI,YAAY,UAAW;AAAA,IAC7B;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,yCAAyC,EAAE,eAAe,GAAG,SAAS,IAAI,CAAC;AACxF,UAAM;AAAA,EACR;AAEA,MAAI,QAAQ,WAAW;AACrB,WAAO,KAAK,4CAA4C,EAAE,eAAe,GAAG,QAAQ,CAAC;AACrF,UAAM,gBAAgB,cAAc,eAAe,eAAe;AAClE,WAAO;AAAA,EACT;AAEA,QAAM,gBAAgB,YAAY,eAAe,EAAE,eAAe,EAAE,GAAG,QAAQ,EAAE,GAAG,eAAe;AACnG,SAAO,KAAK,2CAA2C,EAAE,eAAe,GAAG,QAAQ,CAAC;AACpF,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,56 @@
1
+ import { extensionPoints } from "@open-mercato/core/modules/staff/extension-points";
2
+ import { createStrategyRegistry } from "./registries/registry.js";
3
+ const TIME_TRACKING_RECALCULATION_REGISTRY_ID = extensionPoints.hosts.recalculationRegistry.spotId;
4
+ const BUILT_IN_RECALCULATION_ID = "staff.time_tracking.recalculation.rounding";
5
+ const registry = createStrategyRegistry(
6
+ TIME_TRACKING_RECALCULATION_REGISTRY_ID
7
+ );
8
+ function registerTimeTrackingRecalculation(recalculation) {
9
+ if (typeof recalculation?.run !== "function") {
10
+ throw new Error("[internal] a time-tracking recalculation must declare a run() function");
11
+ }
12
+ return registry.register(recalculation);
13
+ }
14
+ function listTimeTrackingRecalculations() {
15
+ return registry.list();
16
+ }
17
+ function timeTrackingRecalculationIds() {
18
+ return registry.ids();
19
+ }
20
+ function getTimeTrackingRecalculation(id) {
21
+ return registry.get(id);
22
+ }
23
+ function emptyRecalculationSummary() {
24
+ return {
25
+ totalCount: 0,
26
+ processedCount: 0,
27
+ updatedCount: 0,
28
+ unchangedCount: 0,
29
+ skippedCount: 0,
30
+ cancelled: false
31
+ };
32
+ }
33
+ function resolveTimeTrackingRecalculations(ids) {
34
+ if (!ids || ids.length === 0) {
35
+ const builtIn = registry.get(BUILT_IN_RECALCULATION_ID);
36
+ return builtIn ? [builtIn] : [];
37
+ }
38
+ const resolved = [];
39
+ for (const id of ids) {
40
+ const entry = registry.get(id);
41
+ if (!entry) throw new Error(`[internal] unknown time-tracking recalculation id: ${id}`);
42
+ if (!resolved.some((candidate) => candidate.id === entry.id)) resolved.push(entry);
43
+ }
44
+ return resolved;
45
+ }
46
+ export {
47
+ BUILT_IN_RECALCULATION_ID,
48
+ TIME_TRACKING_RECALCULATION_REGISTRY_ID,
49
+ emptyRecalculationSummary,
50
+ getTimeTrackingRecalculation,
51
+ listTimeTrackingRecalculations,
52
+ registerTimeTrackingRecalculation,
53
+ resolveTimeTrackingRecalculations,
54
+ timeTrackingRecalculationIds
55
+ };
56
+ //# sourceMappingURL=recalculations.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/time-tracking/recalculations.ts"],
4
+ "sourcesContent": ["/**\n * EP-51 \u2014 the recalculation registry.\n *\n * A recalculation is a tenant-wide restatement of a derived time-tracking value:\n * the module ships one, the retro-rounding pass behind\n * `POST /api/staff/timesheets/settings/reapply-rounding`, and a third party that\n * plugs in its own rate resolver (EP-33) or billability chain (EP-34) needs the\n * same thing \u2014 a way to re-apply the new rule to the rows that already exist,\n * with the progress bar, the cancel button and the queue worker the module\n * already has.\n *\n * The contract is deliberately narrow. A hook receives a scope and a reporter and\n * answers a count summary; it does NOT own the `ProgressJob`, because several\n * hooks share one job when the CLI runs them together, and a hook that started or\n * completed the job would fight the next one.\n *\n * **`run` is a write over a tenant's history.** Two rules that are not optional:\n * every query must carry `tenantId` (and honour `organizationIds` when it is not\n * `null`), and a locked entry \u2014 one frozen into a closed report \u2014 must never be\n * touched. The built-in excludes locked entries twice, in its own candidate query\n * and again in the command it drives.\n */\n\nimport type { AwilixContainer } from 'awilix'\nimport { extensionPoints } from '@open-mercato/core/modules/staff/extension-points'\nimport { createStrategyRegistry, BUILT_IN_STRATEGY_PRIORITY } from './registries/registry'\n\nexport const TIME_TRACKING_RECALCULATION_REGISTRY_ID =\n extensionPoints.hosts.recalculationRegistry.spotId\n\nexport const BUILT_IN_RECALCULATION_ID = 'staff.time_tracking.recalculation.rounding'\n\nexport type TimeTrackingRecalculationScope = {\n tenantId: string\n /** Organizations the caller may act in; `null` means every organization of the tenant. */\n organizationIds: string[] | null\n userId?: string | null\n}\n\n/**\n * The slice of the progress job a hook is allowed to touch. `advance` is additive\n * so two hooks sharing one job cannot rewind each other's count.\n */\nexport type TimeTrackingRecalculationReporter = {\n setTotal(total: number): Promise<void>\n advance(processed: number): Promise<void>\n isCancellationRequested(): Promise<boolean>\n}\n\nexport type TimeTrackingRecalculationContext = {\n container: AwilixContainer\n scope: TimeTrackingRecalculationScope\n report: TimeTrackingRecalculationReporter\n}\n\nexport type TimeTrackingRecalculationSummary = {\n totalCount: number\n processedCount: number\n updatedCount: number\n unchangedCount: number\n skippedCount: number\n cancelled: boolean\n}\n\nexport type TimeTrackingRecalculation = {\n id: string\n labelKey: string\n priority?: number\n run(ctx: TimeTrackingRecalculationContext): Promise<TimeTrackingRecalculationSummary>\n}\n\nconst registry = createStrategyRegistry<TimeTrackingRecalculation>(\n TIME_TRACKING_RECALCULATION_REGISTRY_ID,\n)\n\nexport function registerTimeTrackingRecalculation(\n recalculation: TimeTrackingRecalculation,\n): () => void {\n if (typeof recalculation?.run !== 'function') {\n throw new Error('[internal] a time-tracking recalculation must declare a run() function')\n }\n return registry.register(recalculation)\n}\n\nexport function listTimeTrackingRecalculations(): TimeTrackingRecalculation[] {\n return registry.list()\n}\n\nexport function timeTrackingRecalculationIds(): string[] {\n return registry.ids()\n}\n\nexport function getTimeTrackingRecalculation(\n id: string | null | undefined,\n): TimeTrackingRecalculation | null {\n return registry.get(id)\n}\n\nexport function emptyRecalculationSummary(): TimeTrackingRecalculationSummary {\n return {\n totalCount: 0,\n processedCount: 0,\n updatedCount: 0,\n unchangedCount: 0,\n skippedCount: 0,\n cancelled: false,\n }\n}\n\n/**\n * Resolves the hooks a job should run.\n *\n * An empty or absent request resolves to the built-in alone \u2014 which is what keeps\n * the settings route byte-identical: it enqueues no hook ids, so a contribution\n * cannot attach itself to the retro-rounding button a tenant pressed. Running\n * more than one is an explicit act, and the CLI is where it is made.\n *\n * An unknown id is an error rather than a silent skip: a backfill that quietly\n * did nothing is worse than one that refused to start.\n */\nexport function resolveTimeTrackingRecalculations(\n ids?: readonly string[] | null,\n): TimeTrackingRecalculation[] {\n if (!ids || ids.length === 0) {\n const builtIn = registry.get(BUILT_IN_RECALCULATION_ID)\n return builtIn ? [builtIn] : []\n }\n const resolved: TimeTrackingRecalculation[] = []\n for (const id of ids) {\n const entry = registry.get(id)\n if (!entry) throw new Error(`[internal] unknown time-tracking recalculation id: ${id}`)\n if (!resolved.some((candidate) => candidate.id === entry.id)) resolved.push(entry)\n }\n return resolved\n}\n"],
5
+ "mappings": "AAwBA,SAAS,uBAAuB;AAChC,SAAS,8BAA0D;AAE5D,MAAM,0CACX,gBAAgB,MAAM,sBAAsB;AAEvC,MAAM,4BAA4B;AAyCzC,MAAM,WAAW;AAAA,EACf;AACF;AAEO,SAAS,kCACd,eACY;AACZ,MAAI,OAAO,eAAe,QAAQ,YAAY;AAC5C,UAAM,IAAI,MAAM,wEAAwE;AAAA,EAC1F;AACA,SAAO,SAAS,SAAS,aAAa;AACxC;AAEO,SAAS,iCAA8D;AAC5E,SAAO,SAAS,KAAK;AACvB;AAEO,SAAS,+BAAyC;AACvD,SAAO,SAAS,IAAI;AACtB;AAEO,SAAS,6BACd,IACkC;AAClC,SAAO,SAAS,IAAI,EAAE;AACxB;AAEO,SAAS,4BAA8D;AAC5E,SAAO;AAAA,IACL,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,WAAW;AAAA,EACb;AACF;AAaO,SAAS,kCACd,KAC6B;AAC7B,MAAI,CAAC,OAAO,IAAI,WAAW,GAAG;AAC5B,UAAM,UAAU,SAAS,IAAI,yBAAyB;AACtD,WAAO,UAAU,CAAC,OAAO,IAAI,CAAC;AAAA,EAChC;AACA,QAAM,WAAwC,CAAC;AAC/C,aAAW,MAAM,KAAK;AACpB,UAAM,QAAQ,SAAS,IAAI,EAAE;AAC7B,QAAI,CAAC,MAAO,OAAM,IAAI,MAAM,sDAAsD,EAAE,EAAE;AACtF,QAAI,CAAC,SAAS,KAAK,CAAC,cAAc,UAAU,OAAO,MAAM,EAAE,EAAG,UAAS,KAAK,KAAK;AAAA,EACnF;AACA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,63 @@
1
+ import { createLogger } from "@open-mercato/shared/lib/logger";
2
+ const logger = createLogger("staff").child({ component: "time-tracking/registries" });
3
+ function runStrategy(registryId, strategyId, run, fallback) {
4
+ try {
5
+ return run();
6
+ } catch (err) {
7
+ logger.error("a time-tracking strategy threw; falling back to the built-in", {
8
+ registryId,
9
+ strategyId,
10
+ err
11
+ });
12
+ return fallback();
13
+ }
14
+ }
15
+ function tryStrategy(registryId, strategyId, run) {
16
+ try {
17
+ return run();
18
+ } catch (err) {
19
+ logger.error("a time-tracking strategy threw; skipping this candidate", {
20
+ registryId,
21
+ strategyId,
22
+ err
23
+ });
24
+ return void 0;
25
+ }
26
+ }
27
+ const MAX_STORED_MINUTES = 2147483647;
28
+ function clampToStoredMinutes(registryId, strategyId, value, fallback) {
29
+ if (typeof value !== "number" || !Number.isFinite(value)) {
30
+ logger.error("a time-tracking strategy answered a non-finite duration; falling back to the built-in", {
31
+ registryId,
32
+ strategyId,
33
+ value: String(value)
34
+ });
35
+ return fallback();
36
+ }
37
+ if (value < 0) {
38
+ logger.error("a time-tracking strategy answered a negative duration; falling back to the built-in", {
39
+ registryId,
40
+ strategyId,
41
+ value
42
+ });
43
+ return fallback();
44
+ }
45
+ const rounded = Math.round(value);
46
+ if (rounded > MAX_STORED_MINUTES) {
47
+ logger.error("a time-tracking strategy answered more minutes than the column can hold; clamping", {
48
+ registryId,
49
+ strategyId,
50
+ value,
51
+ max: MAX_STORED_MINUTES
52
+ });
53
+ return MAX_STORED_MINUTES;
54
+ }
55
+ return rounded;
56
+ }
57
+ export {
58
+ MAX_STORED_MINUTES,
59
+ clampToStoredMinutes,
60
+ runStrategy,
61
+ tryStrategy
62
+ };
63
+ //# sourceMappingURL=invoke.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/staff/lib/time-tracking/registries/invoke.ts"],
4
+ "sourcesContent": ["/**\n * Calling a contributed strategy safely.\n *\n * Every registry in `EP-32\u2026EP-41` hands third-party code a function the module then\n * calls in the middle of a write path or a report render. None of those call sites\n * originally wrapped the call, so one contributed `round`, `sort` or `serialize`\n * that threw took the surrounding operation down \u2014 for the whole process, not just\n * the tenant that installed the contribution, because the registries are plain\n * module-scope maps shared by every request the process serves.\n *\n * The rule these helpers encode: **a broken contribution degrades to the module's\n * own behaviour, loudly.** It never propagates, and it never silently wins either \u2014\n * every fallback logs the registry and the strategy id, because a strategy that\n * quietly stopped applying is the failure mode that takes weeks to notice.\n *\n * Which helper to reach for:\n *\n * - `runStrategy` \u2014 single-winner registries, where there is exactly one right\n * answer and the built-in is it when the contribution cannot produce one.\n * - `tryStrategy` \u2014 chain registries, where the next candidate is asked anyway, so\n * a thrower is skipped rather than replaced.\n */\n\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('staff').child({ component: 'time-tracking/registries' })\n\nexport function runStrategy<TResult>(\n registryId: string,\n strategyId: string,\n run: () => TResult,\n fallback: () => TResult,\n): TResult {\n try {\n return run()\n } catch (err) {\n logger.error('a time-tracking strategy threw; falling back to the built-in', {\n registryId,\n strategyId,\n err,\n })\n return fallback()\n }\n}\n\n/** `undefined` means \"this candidate could not answer\"; ask the next one. */\nexport function tryStrategy<TResult>(\n registryId: string,\n strategyId: string,\n run: () => TResult,\n): TResult | undefined {\n try {\n return run()\n } catch (err) {\n logger.error('a time-tracking strategy threw; skipping this candidate', {\n registryId,\n strategyId,\n err,\n })\n return undefined\n }\n}\n\n/** The largest value `staff_time_entries.rounded_minutes` \u2014 a PostgreSQL `integer` \u2014 can hold. */\nexport const MAX_STORED_MINUTES = 2_147_483_647\n\n/**\n * Clamps a strategy's answer to something the column it lands in can hold.\n *\n * `staff_time_entries.rounded_minutes` is an `integer` and the sole input to every\n * amount the suite computes, so a `NaN`, a fraction or a negative from a\n * contributed strategy is not a cosmetic problem: the write either fails or stores\n * garbage, and on read `entryAmount` substitutes `0`, which bills the entry at\n * nothing without saying so. The shipped default `unitMinutes: 0` makes this the\n * FIRST thing a plausible contribution hits \u2014 `Math.floor(raw / ctx.settings.unitMinutes)`\n * is division by zero.\n *\n * Three answers are refused rather than stored, and each one logs, because the rule\n * this file encodes is that a degraded contribution degrades *loudly*:\n *\n * - **Not a finite number** \u2014 nothing to clamp toward; take the built-in.\n * - **Negative** \u2014 a duration that ran backwards is not a value to clamp to zero.\n * Storing `0` is exactly the silent zero-bill the paragraph above describes, so a\n * negative takes the built-in arm alongside `NaN`.\n * - **Larger than the column** \u2014 the INSERT would fail with an out-of-range error\n * and 500 the write, which is the propagation these helpers exist to prevent.\n * Clamped down to the column's ceiling so the write still lands.\n */\nexport function clampToStoredMinutes(\n registryId: string,\n strategyId: string,\n value: unknown,\n fallback: () => number,\n): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n logger.error('a time-tracking strategy answered a non-finite duration; falling back to the built-in', {\n registryId,\n strategyId,\n value: String(value),\n })\n return fallback()\n }\n if (value < 0) {\n logger.error('a time-tracking strategy answered a negative duration; falling back to the built-in', {\n registryId,\n strategyId,\n value,\n })\n return fallback()\n }\n const rounded = Math.round(value)\n if (rounded > MAX_STORED_MINUTES) {\n logger.error('a time-tracking strategy answered more minutes than the column can hold; clamping', {\n registryId,\n strategyId,\n value,\n max: MAX_STORED_MINUTES,\n })\n return MAX_STORED_MINUTES\n }\n return rounded\n}\n"],
5
+ "mappings": "AAuBA,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,OAAO,EAAE,MAAM,EAAE,WAAW,2BAA2B,CAAC;AAE7E,SAAS,YACd,YACA,YACA,KACA,UACS;AACT,MAAI;AACF,WAAO,IAAI;AAAA,EACb,SAAS,KAAK;AACZ,WAAO,MAAM,gEAAgE;AAAA,MAC3E;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,WAAO,SAAS;AAAA,EAClB;AACF;AAGO,SAAS,YACd,YACA,YACA,KACqB;AACrB,MAAI;AACF,WAAO,IAAI;AAAA,EACb,SAAS,KAAK;AACZ,WAAO,MAAM,2DAA2D;AAAA,MACtE;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AACF;AAGO,MAAM,qBAAqB;AAwB3B,SAAS,qBACd,YACA,YACA,OACA,UACQ;AACR,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,GAAG;AACxD,WAAO,MAAM,yFAAyF;AAAA,MACpG;AAAA,MACA;AAAA,MACA,OAAO,OAAO,KAAK;AAAA,IACrB,CAAC;AACD,WAAO,SAAS;AAAA,EAClB;AACA,MAAI,QAAQ,GAAG;AACb,WAAO,MAAM,uFAAuF;AAAA,MAClG;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,WAAO,SAAS;AAAA,EAClB;AACA,QAAM,UAAU,KAAK,MAAM,KAAK;AAChC,MAAI,UAAU,oBAAoB;AAChC,WAAO,MAAM,qFAAqF;AAAA,MAChG;AAAA,MACA;AAAA,MACA;AAAA,MACA,KAAK;AAAA,IACP,CAAC;AACD,WAAO;AAAA,EACT;AACA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,62 @@
1
+ const BUILT_IN_STRATEGY_PRIORITY = -1e3;
2
+ function orderSlots(left, right) {
3
+ const leftPriority = left.entry.priority ?? 0;
4
+ const rightPriority = right.entry.priority ?? 0;
5
+ if (leftPriority !== rightPriority) return rightPriority - leftPriority;
6
+ return left.sequence - right.sequence;
7
+ }
8
+ function createStrategyRegistry(registryId) {
9
+ const builtInSlots = /* @__PURE__ */ new Map();
10
+ const contributedSlots = /* @__PURE__ */ new Map();
11
+ let sequence = 0;
12
+ const readId = (entry) => {
13
+ const id = typeof entry?.id === "string" ? entry.id.trim() : "";
14
+ if (!id) throw new Error(`[internal] ${registryId} requires a non-empty strategy id`);
15
+ return id;
16
+ };
17
+ const registerBuiltIn = (entry) => {
18
+ const id = readId(entry);
19
+ if (builtInSlots.has(id)) {
20
+ throw new Error(`[internal] ${registryId} already has a built-in registered as ${id}`);
21
+ }
22
+ sequence += 1;
23
+ const slot = { entry: { ...entry, id }, sequence };
24
+ builtInSlots.set(id, slot);
25
+ return slot.entry;
26
+ };
27
+ const register = (entry) => {
28
+ const id = readId(entry);
29
+ if (builtInSlots.has(id)) {
30
+ throw new Error(
31
+ `[internal] ${id} is a built-in ${registryId} strategy and cannot be replaced; register your own id`
32
+ );
33
+ }
34
+ sequence += 1;
35
+ const slot = { entry: { ...entry, id }, sequence };
36
+ contributedSlots.set(id, slot);
37
+ return () => {
38
+ if (contributedSlots.get(id) === slot) contributedSlots.delete(id);
39
+ };
40
+ };
41
+ const list = () => [...contributedSlots.values(), ...builtInSlots.values()].sort(orderSlots).map((slot) => slot.entry);
42
+ const get = (id) => {
43
+ if (typeof id !== "string") return null;
44
+ const trimmed = id.trim();
45
+ return contributedSlots.get(trimmed)?.entry ?? builtInSlots.get(trimmed)?.entry ?? null;
46
+ };
47
+ return {
48
+ registryId,
49
+ registerBuiltIn,
50
+ register,
51
+ get,
52
+ has: (id) => get(id) !== null,
53
+ isBuiltIn: (id) => typeof id === "string" && builtInSlots.has(id.trim()),
54
+ list,
55
+ ids: () => list().map((entry) => entry.id)
56
+ };
57
+ }
58
+ export {
59
+ BUILT_IN_STRATEGY_PRIORITY,
60
+ createStrategyRegistry
61
+ };
62
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/staff/lib/time-tracking/registries/registry.ts"],
4
+ "sourcesContent": ["/**\n * The one registry shape every time-tracking strategy registry is built from\n * (EP-32\u2026EP-41 of `.ai/specs/2026-08-24-time-tracking-umes-extension-points.md`).\n *\n * It follows the house provider idiom \u2014 `registerPaymentProvider` /\n * `registerShippingProvider` in `modules/sales/lib/providers/registry.ts`: a\n * module-level `Map`, a `register` that returns its own disposer, and plain\n * lookup helpers. That makes every registry reachable from the browser bundle as\n * well as the server, which matters because four of these strategies are\n * consulted by client previews that cannot resolve DI.\n *\n * Ordering is the only thing added on top of the sales idiom, and it is what\n * keeps \"no contribution means no behaviour change\" true: entries are served in\n * descending `priority`, ties broken by registration order, and every built-in\n * registers at `BUILT_IN_STRATEGY_PRIORITY` \u2014 far below the default of `0` \u2014 so\n * a built-in is always the last candidate considered.\n *\n * **A built-in is not just a low-priority entry, it is a separate slot.** The\n * fail-closed rule in `scope.ts` lands an unscoped call on \"the built-in\", and the\n * whole point of that rule is that the built-in is the same pure code the module\n * shipped before the registries existed. With one map keyed by id and last-writer-\n * wins semantics, a contribution registering under a published built-in id BECAME\n * the built-in: it ran on the unscoped path the gate exists to keep byte-identical,\n * and its disposer then deleted the real built-in permanently, after which every\n * resolver that requires one throws. So `registerBuiltIn` writes to a map\n * `register` cannot reach, `register` refuses a built-in id outright, and no\n * disposer can remove a built-in.\n */\n\nexport const BUILT_IN_STRATEGY_PRIORITY = -1000\n\nexport type RegisteredStrategy = {\n id: string\n priority?: number\n}\n\nexport type StrategyRegistry<TEntry extends RegisteredStrategy> = {\n registryId: string\n /**\n * Registers the module's own default. Not part of the contribution surface \u2014\n * module-load code only. The returned entry is the identity every resolver\n * should compare against rather than re-looking the id up.\n */\n registerBuiltIn(entry: TEntry): TEntry\n /** Contributes a strategy. Throws on a built-in id. Returns its own disposer. */\n register(entry: TEntry): () => void\n get(id: string | null | undefined): TEntry | null\n has(id: string | null | undefined): boolean\n isBuiltIn(id: string | null | undefined): boolean\n list(): TEntry[]\n ids(): string[]\n}\n\ntype Slot<TEntry extends RegisteredStrategy> = {\n entry: TEntry\n sequence: number\n}\n\nfunction orderSlots<TEntry extends RegisteredStrategy>(left: Slot<TEntry>, right: Slot<TEntry>): number {\n const leftPriority = left.entry.priority ?? 0\n const rightPriority = right.entry.priority ?? 0\n if (leftPriority !== rightPriority) return rightPriority - leftPriority\n return left.sequence - right.sequence\n}\n\nexport function createStrategyRegistry<TEntry extends RegisteredStrategy>(\n registryId: string,\n): StrategyRegistry<TEntry> {\n const builtInSlots = new Map<string, Slot<TEntry>>()\n const contributedSlots = new Map<string, Slot<TEntry>>()\n let sequence = 0\n\n const readId = (entry: TEntry): string => {\n const id = typeof entry?.id === 'string' ? entry.id.trim() : ''\n if (!id) throw new Error(`[internal] ${registryId} requires a non-empty strategy id`)\n return id\n }\n\n const registerBuiltIn = (entry: TEntry): TEntry => {\n const id = readId(entry)\n if (builtInSlots.has(id)) {\n throw new Error(`[internal] ${registryId} already has a built-in registered as ${id}`)\n }\n sequence += 1\n const slot: Slot<TEntry> = { entry: { ...entry, id }, sequence }\n builtInSlots.set(id, slot)\n return slot.entry\n }\n\n const register = (entry: TEntry): (() => void) => {\n const id = readId(entry)\n if (builtInSlots.has(id)) {\n throw new Error(\n `[internal] ${id} is a built-in ${registryId} strategy and cannot be replaced; register your own id`,\n )\n }\n sequence += 1\n const slot: Slot<TEntry> = { entry: { ...entry, id }, sequence }\n contributedSlots.set(id, slot)\n return () => {\n if (contributedSlots.get(id) === slot) contributedSlots.delete(id)\n }\n }\n\n const list = (): TEntry[] =>\n [...contributedSlots.values(), ...builtInSlots.values()].sort(orderSlots).map((slot) => slot.entry)\n\n const get = (id: string | null | undefined): TEntry | null => {\n if (typeof id !== 'string') return null\n const trimmed = id.trim()\n return contributedSlots.get(trimmed)?.entry ?? builtInSlots.get(trimmed)?.entry ?? null\n }\n\n return {\n registryId,\n registerBuiltIn,\n register,\n get,\n has: (id) => get(id) !== null,\n isBuiltIn: (id) => typeof id === 'string' && builtInSlots.has(id.trim()),\n list,\n ids: () => list().map((entry) => entry.id),\n }\n}\n"],
5
+ "mappings": "AA6BO,MAAM,6BAA6B;AA6B1C,SAAS,WAA8C,MAAoB,OAA6B;AACtG,QAAM,eAAe,KAAK,MAAM,YAAY;AAC5C,QAAM,gBAAgB,MAAM,MAAM,YAAY;AAC9C,MAAI,iBAAiB,cAAe,QAAO,gBAAgB;AAC3D,SAAO,KAAK,WAAW,MAAM;AAC/B;AAEO,SAAS,uBACd,YAC0B;AAC1B,QAAM,eAAe,oBAAI,IAA0B;AACnD,QAAM,mBAAmB,oBAAI,IAA0B;AACvD,MAAI,WAAW;AAEf,QAAM,SAAS,CAAC,UAA0B;AACxC,UAAM,KAAK,OAAO,OAAO,OAAO,WAAW,MAAM,GAAG,KAAK,IAAI;AAC7D,QAAI,CAAC,GAAI,OAAM,IAAI,MAAM,cAAc,UAAU,mCAAmC;AACpF,WAAO;AAAA,EACT;AAEA,QAAM,kBAAkB,CAAC,UAA0B;AACjD,UAAM,KAAK,OAAO,KAAK;AACvB,QAAI,aAAa,IAAI,EAAE,GAAG;AACxB,YAAM,IAAI,MAAM,cAAc,UAAU,yCAAyC,EAAE,EAAE;AAAA,IACvF;AACA,gBAAY;AACZ,UAAM,OAAqB,EAAE,OAAO,EAAE,GAAG,OAAO,GAAG,GAAG,SAAS;AAC/D,iBAAa,IAAI,IAAI,IAAI;AACzB,WAAO,KAAK;AAAA,EACd;AAEA,QAAM,WAAW,CAAC,UAAgC;AAChD,UAAM,KAAK,OAAO,KAAK;AACvB,QAAI,aAAa,IAAI,EAAE,GAAG;AACxB,YAAM,IAAI;AAAA,QACR,cAAc,EAAE,kBAAkB,UAAU;AAAA,MAC9C;AAAA,IACF;AACA,gBAAY;AACZ,UAAM,OAAqB,EAAE,OAAO,EAAE,GAAG,OAAO,GAAG,GAAG,SAAS;AAC/D,qBAAiB,IAAI,IAAI,IAAI;AAC7B,WAAO,MAAM;AACX,UAAI,iBAAiB,IAAI,EAAE,MAAM,KAAM,kBAAiB,OAAO,EAAE;AAAA,IACnE;AAAA,EACF;AAEA,QAAM,OAAO,MACX,CAAC,GAAG,iBAAiB,OAAO,GAAG,GAAG,aAAa,OAAO,CAAC,EAAE,KAAK,UAAU,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK;AAEpG,QAAM,MAAM,CAAC,OAAiD;AAC5D,QAAI,OAAO,OAAO,SAAU,QAAO;AACnC,UAAM,UAAU,GAAG,KAAK;AACxB,WAAO,iBAAiB,IAAI,OAAO,GAAG,SAAS,aAAa,IAAI,OAAO,GAAG,SAAS;AAAA,EACrF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,KAAK,CAAC,OAAO,IAAI,EAAE,MAAM;AAAA,IACzB,WAAW,CAAC,OAAO,OAAO,OAAO,YAAY,aAAa,IAAI,GAAG,KAAK,CAAC;AAAA,IACvE;AAAA,IACA,KAAK,MAAM,KAAK,EAAE,IAAI,CAAC,UAAU,MAAM,EAAE;AAAA,EAC3C;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,21 @@
1
+ function isNonEmpty(value) {
2
+ return typeof value === "string" && value.trim().length > 0;
3
+ }
4
+ function readResolverScope(ctx) {
5
+ if (!ctx) return null;
6
+ if (!isNonEmpty(ctx.tenantId) || !isNonEmpty(ctx.organizationId)) return null;
7
+ return { tenantId: ctx.tenantId, organizationId: ctx.organizationId };
8
+ }
9
+ function hasResolverScope(ctx) {
10
+ return readResolverScope(ctx) !== null;
11
+ }
12
+ function selectScopedStrategy(candidates, builtIn, ctx) {
13
+ if (!hasResolverScope(ctx)) return builtIn;
14
+ return candidates[0] ?? builtIn;
15
+ }
16
+ export {
17
+ hasResolverScope,
18
+ readResolverScope,
19
+ selectScopedStrategy
20
+ };
21
+ //# sourceMappingURL=scope.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/staff/lib/time-tracking/registries/scope.ts"],
4
+ "sourcesContent": ["/**\n * The scope contract every time-tracking strategy resolver receives, and the\n * fail-closed rule the spec's cross-cutting requirements attach to it: a\n * contributed strategy runs only when the caller supplied BOTH a tenant and an\n * organization, matching `resolveProjectAccess` in `../access.ts`.\n *\n * A built-in never needs the scope \u2014 it is the same pure arithmetic the module\n * shipped before the registries existed \u2014 so failing closed lands on the\n * built-in rather than on an error. That is what makes a client preview\n * (`TimeEntryDialog`, `timeTrackingSettingsForm`, `ProjectCodeField`), which has\n * no tenant id to hand, keep producing exactly the number it produced before.\n */\n\nexport type TimeTrackingResolverScope = {\n tenantId: string\n organizationId: string\n}\n\nexport type ScopedResolverContext = Partial<TimeTrackingResolverScope>\n\nfunction isNonEmpty(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0\n}\n\nexport function readResolverScope(\n ctx: ScopedResolverContext | null | undefined,\n): TimeTrackingResolverScope | null {\n if (!ctx) return null\n if (!isNonEmpty(ctx.tenantId) || !isNonEmpty(ctx.organizationId)) return null\n return { tenantId: ctx.tenantId, organizationId: ctx.organizationId }\n}\n\nexport function hasResolverScope(ctx: ScopedResolverContext | null | undefined): boolean {\n return readResolverScope(ctx) !== null\n}\n\n/**\n * Picks the strategy a scoped call site should run: the highest-priority\n * contribution when the scope is complete, the built-in otherwise. `candidates`\n * arrives already ordered by the registry.\n *\n * The built-in is passed **by reference**, not looked up by id among the\n * candidates. An id is a string anyone can register; identity is not. Resolving\n * the fail-closed default by id meant a contribution that named itself after the\n * built-in was served on the unscoped path the rule exists to keep byte-identical\n * \u2014 `registry.ts` now refuses that registration, and this makes the second half of\n * the same guarantee structural rather than conventional.\n */\nexport function selectScopedStrategy<TEntry extends { id: string }>(\n candidates: readonly TEntry[],\n builtIn: TEntry,\n ctx: ScopedResolverContext | null | undefined,\n): TEntry {\n if (!hasResolverScope(ctx)) return builtIn\n return candidates[0] ?? builtIn\n}\n"],
5
+ "mappings": "AAoBA,SAAS,WAAW,OAAiC;AACnD,SAAO,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS;AAC5D;AAEO,SAAS,kBACd,KACkC;AAClC,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,CAAC,WAAW,IAAI,QAAQ,KAAK,CAAC,WAAW,IAAI,cAAc,EAAG,QAAO;AACzE,SAAO,EAAE,UAAU,IAAI,UAAU,gBAAgB,IAAI,eAAe;AACtE;AAEO,SAAS,iBAAiB,KAAwD;AACvF,SAAO,kBAAkB,GAAG,MAAM;AACpC;AAcO,SAAS,qBACd,YACA,SACA,KACQ;AACR,MAAI,CAAC,iBAAiB,GAAG,EAAG,QAAO;AACnC,SAAO,WAAW,CAAC,KAAK;AAC1B;",
6
+ "names": []
7
+ }
@@ -0,0 +1,34 @@
1
+ function readEntries(source, taskId) {
2
+ if (!source) return [];
3
+ if (source instanceof Map) return source.get(taskId) ?? [];
4
+ return source[taskId] ?? [];
5
+ }
6
+ function sumEntryMinutes(entries) {
7
+ let total = 0;
8
+ for (const entry of entries) {
9
+ if (!entry || entry.deletedAt) continue;
10
+ const minutes = entry.durationMinutes;
11
+ if (minutes === null || minutes === void 0 || !Number.isFinite(minutes)) continue;
12
+ total += minutes;
13
+ }
14
+ return total;
15
+ }
16
+ function loggedMinutes(task, allTasks, entriesByTaskId) {
17
+ const ownMinutes = sumEntryMinutes(readEntries(entriesByTaskId, task.id));
18
+ const children = (allTasks ?? []).filter(
19
+ (candidate) => candidate.parentTaskId === task.id && candidate.id !== task.id && !candidate.deletedAt
20
+ );
21
+ let childMinutes = 0;
22
+ for (const child of children) {
23
+ childMinutes += sumEntryMinutes(readEntries(entriesByTaskId, child.id));
24
+ }
25
+ return {
26
+ ownMinutes,
27
+ loggedMinutes: ownMinutes + childMinutes,
28
+ childCount: children.length
29
+ };
30
+ }
31
+ export {
32
+ loggedMinutes
33
+ };
34
+ //# sourceMappingURL=rollup.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/time-tracking/rollup.ts"],
4
+ "sourcesContent": ["export type RollupTask = {\n id: string\n parentTaskId?: string | null\n deletedAt?: Date | string | null\n}\n\nexport type RollupEntry = {\n durationMinutes?: number | null\n deletedAt?: Date | string | null\n}\n\nexport type EntriesByTaskId =\n | ReadonlyMap<string, readonly RollupEntry[]>\n | Record<string, readonly RollupEntry[] | undefined>\n\nexport type TaskRollup = {\n ownMinutes: number\n loggedMinutes: number\n childCount: number\n}\n\nfunction readEntries(source: EntriesByTaskId | null | undefined, taskId: string): readonly RollupEntry[] {\n if (!source) return []\n if (source instanceof Map) return source.get(taskId) ?? []\n return (source as Record<string, readonly RollupEntry[] | undefined>)[taskId] ?? []\n}\n\nfunction sumEntryMinutes(entries: readonly RollupEntry[]): number {\n let total = 0\n for (const entry of entries) {\n if (!entry || entry.deletedAt) continue\n const minutes = entry.durationMinutes\n if (minutes === null || minutes === undefined || !Number.isFinite(minutes)) continue\n total += minutes\n }\n return total\n}\n\nexport function loggedMinutes(\n task: RollupTask,\n allTasks: readonly RollupTask[],\n entriesByTaskId: EntriesByTaskId,\n): TaskRollup {\n const ownMinutes = sumEntryMinutes(readEntries(entriesByTaskId, task.id))\n const children = (allTasks ?? []).filter(\n (candidate) => candidate.parentTaskId === task.id && candidate.id !== task.id && !candidate.deletedAt,\n )\n\n let childMinutes = 0\n for (const child of children) {\n childMinutes += sumEntryMinutes(readEntries(entriesByTaskId, child.id))\n }\n\n return {\n ownMinutes,\n loggedMinutes: ownMinutes + childMinutes,\n childCount: children.length,\n }\n}\n"],
5
+ "mappings": "AAqBA,SAAS,YAAY,QAA4C,QAAwC;AACvG,MAAI,CAAC,OAAQ,QAAO,CAAC;AACrB,MAAI,kBAAkB,IAAK,QAAO,OAAO,IAAI,MAAM,KAAK,CAAC;AACzD,SAAQ,OAA8D,MAAM,KAAK,CAAC;AACpF;AAEA,SAAS,gBAAgB,SAAyC;AAChE,MAAI,QAAQ;AACZ,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,SAAS,MAAM,UAAW;AAC/B,UAAM,UAAU,MAAM;AACtB,QAAI,YAAY,QAAQ,YAAY,UAAa,CAAC,OAAO,SAAS,OAAO,EAAG;AAC5E,aAAS;AAAA,EACX;AACA,SAAO;AACT;AAEO,SAAS,cACd,MACA,UACA,iBACY;AACZ,QAAM,aAAa,gBAAgB,YAAY,iBAAiB,KAAK,EAAE,CAAC;AACxE,QAAM,YAAY,YAAY,CAAC,GAAG;AAAA,IAChC,CAAC,cAAc,UAAU,iBAAiB,KAAK,MAAM,UAAU,OAAO,KAAK,MAAM,CAAC,UAAU;AAAA,EAC9F;AAEA,MAAI,eAAe;AACnB,aAAW,SAAS,UAAU;AAC5B,oBAAgB,gBAAgB,YAAY,iBAAiB,MAAM,EAAE,CAAC;AAAA,EACxE;AAEA,SAAO;AAAA,IACL;AAAA,IACA,eAAe,aAAa;AAAA,IAC5B,YAAY,SAAS;AAAA,EACvB;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,66 @@
1
+ import { extensionPoints } from "@open-mercato/core/modules/staff/extension-points";
2
+ import { createStrategyRegistry, BUILT_IN_STRATEGY_PRIORITY } from "./registries/registry.js";
3
+ import { selectScopedStrategy } from "./registries/scope.js";
4
+ import { clampToStoredMinutes, runStrategy } from "./registries/invoke.js";
5
+ const DEFAULT_ROUNDING_SETTINGS = {
6
+ unitMinutes: 0,
7
+ direction: "up"
8
+ };
9
+ const TIME_ROUNDING_REGISTRY_ID = extensionPoints.hosts.timeRoundingRegistry.spotId;
10
+ const BUILT_IN_TIME_ROUNDING_STRATEGY_ID = "staff.time_tracking.rounding.unit";
11
+ const registry = createStrategyRegistry(TIME_ROUNDING_REGISTRY_ID);
12
+ function registerTimeRoundingStrategy(strategy) {
13
+ return registry.register(strategy);
14
+ }
15
+ function listTimeRoundingStrategies() {
16
+ return registry.list();
17
+ }
18
+ function getTimeRoundingStrategy(id) {
19
+ return registry.get(id);
20
+ }
21
+ function roundToUnit(raw, settings) {
22
+ if (!Number.isFinite(raw)) return 0;
23
+ const unit = settings?.unitMinutes ?? 0;
24
+ if (!unit) return Math.round(raw);
25
+ const sign = raw < 0 ? -1 : 1;
26
+ const magnitude = Math.abs(raw) / unit;
27
+ const units = settings.direction === "nearest" ? Math.round(magnitude) : Math.ceil(magnitude);
28
+ return sign * units * unit;
29
+ }
30
+ const builtInRoundingStrategy = registry.registerBuiltIn({
31
+ id: BUILT_IN_TIME_ROUNDING_STRATEGY_ID,
32
+ labelKey: "staff.time_tracking.rounding.strategy.unit",
33
+ priority: BUILT_IN_STRATEGY_PRIORITY,
34
+ round: (rawMinutes, ctx) => roundToUnit(rawMinutes, ctx.settings)
35
+ });
36
+ function resolveTimeRoundingStrategy(ctx) {
37
+ return selectScopedStrategy(registry.list(), builtInRoundingStrategy, ctx);
38
+ }
39
+ function roundMinutes(raw, settings, ctx) {
40
+ const strategy = resolveTimeRoundingStrategy(ctx);
41
+ const builtIn = () => roundToUnit(raw, settings);
42
+ if (strategy === builtInRoundingStrategy) return builtIn();
43
+ let threw = false;
44
+ const answer = runStrategy(
45
+ TIME_ROUNDING_REGISTRY_ID,
46
+ strategy.id,
47
+ () => strategy.round(raw, { ...ctx ?? {}, settings }),
48
+ () => {
49
+ threw = true;
50
+ return Number.NaN;
51
+ }
52
+ );
53
+ if (threw) return builtIn();
54
+ return clampToStoredMinutes(TIME_ROUNDING_REGISTRY_ID, strategy.id, answer, builtIn);
55
+ }
56
+ export {
57
+ BUILT_IN_TIME_ROUNDING_STRATEGY_ID,
58
+ DEFAULT_ROUNDING_SETTINGS,
59
+ TIME_ROUNDING_REGISTRY_ID,
60
+ getTimeRoundingStrategy,
61
+ listTimeRoundingStrategies,
62
+ registerTimeRoundingStrategy,
63
+ resolveTimeRoundingStrategy,
64
+ roundMinutes
65
+ };
66
+ //# sourceMappingURL=rounding.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/time-tracking/rounding.ts"],
4
+ "sourcesContent": ["/**\n * EP-32 \u2014 the rounding strategy registry.\n *\n * `roundMinutes` is still the single rounding entry point every call site uses,\n * and with no contribution it runs the built-in `staff.time_tracking.rounding.unit`\n * strategy, which is the same `up`/`nearest` \u00D7 `0|5|10|15` arithmetic this file\n * has always held. A contribution outranks it only when the call site supplies a\n * complete tenant + organization scope (`registries/scope.ts`), so an unscoped\n * client preview keeps producing the built-in's number.\n */\n\nimport { extensionPoints } from '@open-mercato/core/modules/staff/extension-points'\nimport { createStrategyRegistry, BUILT_IN_STRATEGY_PRIORITY } from './registries/registry'\nimport { selectScopedStrategy, type ScopedResolverContext } from './registries/scope'\nimport { clampToStoredMinutes, runStrategy } from './registries/invoke'\n\nexport type RoundingUnitMinutes = 0 | 5 | 10 | 15\n\nexport type RoundingDirection = 'up' | 'nearest'\n\nexport type RoundingSettings = {\n unitMinutes: RoundingUnitMinutes\n direction: RoundingDirection\n}\n\nexport const DEFAULT_ROUNDING_SETTINGS: RoundingSettings = {\n unitMinutes: 0,\n direction: 'up',\n}\n\nexport type TimeRoundingContext = ScopedResolverContext & {\n settings: RoundingSettings\n /** Present on write paths; absent on the settings-screen and dialog previews. */\n timeProjectId?: string | null\n taskId?: string | null\n staffMemberId?: string | null\n date?: string | null\n}\n\nexport type TimeRoundingStrategy = {\n id: string\n labelKey: string\n priority?: number\n round(rawMinutes: number, ctx: TimeRoundingContext): number\n}\n\nexport const TIME_ROUNDING_REGISTRY_ID = extensionPoints.hosts.timeRoundingRegistry.spotId\n\nexport const BUILT_IN_TIME_ROUNDING_STRATEGY_ID = 'staff.time_tracking.rounding.unit'\n\nconst registry = createStrategyRegistry<TimeRoundingStrategy>(TIME_ROUNDING_REGISTRY_ID)\n\nexport function registerTimeRoundingStrategy(strategy: TimeRoundingStrategy): () => void {\n return registry.register(strategy)\n}\n\nexport function listTimeRoundingStrategies(): TimeRoundingStrategy[] {\n return registry.list()\n}\n\nexport function getTimeRoundingStrategy(id: string | null | undefined): TimeRoundingStrategy | null {\n return registry.get(id)\n}\n\nfunction roundToUnit(raw: number, settings: RoundingSettings): number {\n if (!Number.isFinite(raw)) return 0\n const unit = settings?.unitMinutes ?? 0\n if (!unit) return Math.round(raw)\n\n const sign = raw < 0 ? -1 : 1\n const magnitude = Math.abs(raw) / unit\n const units = settings.direction === 'nearest' ? Math.round(magnitude) : Math.ceil(magnitude)\n return sign * units * unit\n}\n\nconst builtInRoundingStrategy: TimeRoundingStrategy = registry.registerBuiltIn({\n id: BUILT_IN_TIME_ROUNDING_STRATEGY_ID,\n labelKey: 'staff.time_tracking.rounding.strategy.unit',\n priority: BUILT_IN_STRATEGY_PRIORITY,\n round: (rawMinutes, ctx) => roundToUnit(rawMinutes, ctx.settings),\n})\n\nexport function resolveTimeRoundingStrategy(\n ctx?: ScopedResolverContext | null,\n): TimeRoundingStrategy {\n return selectScopedStrategy(registry.list(), builtInRoundingStrategy, ctx)\n}\n\n/**\n * The single rounding entry point, and the boundary a contributed strategy's answer\n * has to survive.\n *\n * The result is written straight into `staff_time_entries.rounded_minutes` \u2014 an\n * `integer` column, and the only input to every amount the suite computes \u2014 so an\n * unusable answer is clamped back to the built-in's rather than stored, and one\n * larger than the column can hold is capped at its ceiling rather than 500ing the\n * write. The built-in is exempt from the clamp on purpose: it is the arithmetic the\n * module shipped, it cannot produce a non-finite or fractional value, and re-shaping\n * its output would be the behaviour change the registries exist to avoid.\n *\n * A strategy that *threw* has already been logged and replaced by `runStrategy`, so\n * it returns the built-in directly instead of routing its `NaN` through the clamp,\n * which would log the same failure a second time under the wrong description.\n */\nexport function roundMinutes(\n raw: number,\n settings: RoundingSettings,\n ctx?: ScopedResolverContext | null,\n): number {\n const strategy = resolveTimeRoundingStrategy(ctx)\n const builtIn = () => roundToUnit(raw, settings)\n if (strategy === builtInRoundingStrategy) return builtIn()\n\n let threw = false\n const answer = runStrategy(\n TIME_ROUNDING_REGISTRY_ID,\n strategy.id,\n () => strategy.round(raw, { ...(ctx ?? {}), settings }),\n () => {\n threw = true\n return Number.NaN\n },\n )\n if (threw) return builtIn()\n return clampToStoredMinutes(TIME_ROUNDING_REGISTRY_ID, strategy.id, answer, builtIn)\n}\n"],
5
+ "mappings": "AAWA,SAAS,uBAAuB;AAChC,SAAS,wBAAwB,kCAAkC;AACnE,SAAS,4BAAwD;AACjE,SAAS,sBAAsB,mBAAmB;AAW3C,MAAM,4BAA8C;AAAA,EACzD,aAAa;AAAA,EACb,WAAW;AACb;AAkBO,MAAM,4BAA4B,gBAAgB,MAAM,qBAAqB;AAE7E,MAAM,qCAAqC;AAElD,MAAM,WAAW,uBAA6C,yBAAyB;AAEhF,SAAS,6BAA6B,UAA4C;AACvF,SAAO,SAAS,SAAS,QAAQ;AACnC;AAEO,SAAS,6BAAqD;AACnE,SAAO,SAAS,KAAK;AACvB;AAEO,SAAS,wBAAwB,IAA4D;AAClG,SAAO,SAAS,IAAI,EAAE;AACxB;AAEA,SAAS,YAAY,KAAa,UAAoC;AACpE,MAAI,CAAC,OAAO,SAAS,GAAG,EAAG,QAAO;AAClC,QAAM,OAAO,UAAU,eAAe;AACtC,MAAI,CAAC,KAAM,QAAO,KAAK,MAAM,GAAG;AAEhC,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAM,YAAY,KAAK,IAAI,GAAG,IAAI;AAClC,QAAM,QAAQ,SAAS,cAAc,YAAY,KAAK,MAAM,SAAS,IAAI,KAAK,KAAK,SAAS;AAC5F,SAAO,OAAO,QAAQ;AACxB;AAEA,MAAM,0BAAgD,SAAS,gBAAgB;AAAA,EAC7E,IAAI;AAAA,EACJ,UAAU;AAAA,EACV,UAAU;AAAA,EACV,OAAO,CAAC,YAAY,QAAQ,YAAY,YAAY,IAAI,QAAQ;AAClE,CAAC;AAEM,SAAS,4BACd,KACsB;AACtB,SAAO,qBAAqB,SAAS,KAAK,GAAG,yBAAyB,GAAG;AAC3E;AAkBO,SAAS,aACd,KACA,UACA,KACQ;AACR,QAAM,WAAW,4BAA4B,GAAG;AAChD,QAAM,UAAU,MAAM,YAAY,KAAK,QAAQ;AAC/C,MAAI,aAAa,wBAAyB,QAAO,QAAQ;AAEzD,MAAI,QAAQ;AACZ,QAAM,SAAS;AAAA,IACb;AAAA,IACA,SAAS;AAAA,IACT,MAAM,SAAS,MAAM,KAAK,EAAE,GAAI,OAAO,CAAC,GAAI,SAAS,CAAC;AAAA,IACtD,MAAM;AACJ,cAAQ;AACR,aAAO,OAAO;AAAA,IAChB;AAAA,EACF;AACA,MAAI,MAAO,QAAO,QAAQ;AAC1B,SAAO,qBAAqB,2BAA2B,SAAS,IAAI,QAAQ,OAAO;AACrF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,39 @@
1
+ import { roundMinutes } from "./rounding.js";
2
+ const EMPTY_ROUNDING_IMPACT = {
3
+ entryCount: 0,
4
+ rawMinutes: 0,
5
+ roundedMinutes: 0,
6
+ deltaMinutes: 0
7
+ };
8
+ function toCount(value) {
9
+ const parsed = typeof value === "number" ? value : Number(value);
10
+ return Number.isFinite(parsed) && parsed > 0 ? Math.trunc(parsed) : 0;
11
+ }
12
+ function toMinutes(value) {
13
+ const parsed = typeof value === "number" ? value : Number(value);
14
+ return Number.isFinite(parsed) ? Math.trunc(parsed) : 0;
15
+ }
16
+ function projectRoundingImpact(buckets, settings) {
17
+ let entryCount = 0;
18
+ let rawMinutes = 0;
19
+ let roundedMinutes = 0;
20
+ for (const bucket of buckets) {
21
+ const count = toCount(bucket.entryCount);
22
+ if (count === 0) continue;
23
+ const duration = toMinutes(bucket.durationMinutes);
24
+ entryCount += count;
25
+ rawMinutes += duration * count;
26
+ roundedMinutes += roundMinutes(duration, settings) * count;
27
+ }
28
+ return {
29
+ entryCount,
30
+ rawMinutes,
31
+ roundedMinutes,
32
+ deltaMinutes: roundedMinutes - rawMinutes
33
+ };
34
+ }
35
+ export {
36
+ EMPTY_ROUNDING_IMPACT,
37
+ projectRoundingImpact
38
+ };
39
+ //# sourceMappingURL=roundingImpact.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/time-tracking/roundingImpact.ts"],
4
+ "sourcesContent": ["/**\n * Rounding impact projection (T7.2) \u2014 the warning card on screen 16.\n *\n * Changing the rounding rule changes what clients are billed, and the effect is\n * invisible until an invoice. The preview answers the only question that matters\n * before saving: at the candidate rule, what would the last 90 days have billed?\n *\n * Two properties this file exists to guarantee:\n *\n * 1. **It rounds through `roundMinutes`, never a second implementation.** A\n * preview that disagreed with the write path would be worse than no preview.\n * 2. **It rounds per entry, not per sum** (D-7). That is why the projection is\n * driven by `(durationMinutes, entryCount)` buckets rather than a SQL\n * `SUM(duration_minutes)`: the database groups identical durations, and each\n * distinct duration is rounded exactly once and multiplied by its count. The\n * result is arithmetically identical to rounding every row, at a fraction of\n * the rows.\n */\n\nimport { roundMinutes, type RoundingSettings } from './rounding'\n\nexport type DurationBucket = {\n durationMinutes: number\n entryCount: number\n}\n\nexport type RoundingImpact = {\n entryCount: number\n rawMinutes: number\n roundedMinutes: number\n /** Positive when the rule bills more than the raw time; negative when it bills less. */\n deltaMinutes: number\n}\n\nexport const EMPTY_ROUNDING_IMPACT: RoundingImpact = {\n entryCount: 0,\n rawMinutes: 0,\n roundedMinutes: 0,\n deltaMinutes: 0,\n}\n\nfunction toCount(value: unknown): number {\n const parsed = typeof value === 'number' ? value : Number(value)\n return Number.isFinite(parsed) && parsed > 0 ? Math.trunc(parsed) : 0\n}\n\nfunction toMinutes(value: unknown): number {\n const parsed = typeof value === 'number' ? value : Number(value)\n return Number.isFinite(parsed) ? Math.trunc(parsed) : 0\n}\n\nexport function projectRoundingImpact(\n buckets: readonly DurationBucket[],\n settings: RoundingSettings,\n): RoundingImpact {\n let entryCount = 0\n let rawMinutes = 0\n let roundedMinutes = 0\n\n for (const bucket of buckets) {\n const count = toCount(bucket.entryCount)\n if (count === 0) continue\n const duration = toMinutes(bucket.durationMinutes)\n entryCount += count\n rawMinutes += duration * count\n roundedMinutes += roundMinutes(duration, settings) * count\n }\n\n return {\n entryCount,\n rawMinutes,\n roundedMinutes,\n deltaMinutes: roundedMinutes - rawMinutes,\n }\n}\n"],
5
+ "mappings": "AAmBA,SAAS,oBAA2C;AAe7C,MAAM,wBAAwC;AAAA,EACnD,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,cAAc;AAChB;AAEA,SAAS,QAAQ,OAAwB;AACvC,QAAM,SAAS,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK;AAC/D,SAAO,OAAO,SAAS,MAAM,KAAK,SAAS,IAAI,KAAK,MAAM,MAAM,IAAI;AACtE;AAEA,SAAS,UAAU,OAAwB;AACzC,QAAM,SAAS,OAAO,UAAU,WAAW,QAAQ,OAAO,KAAK;AAC/D,SAAO,OAAO,SAAS,MAAM,IAAI,KAAK,MAAM,MAAM,IAAI;AACxD;AAEO,SAAS,sBACd,SACA,UACgB;AAChB,MAAI,aAAa;AACjB,MAAI,aAAa;AACjB,MAAI,iBAAiB;AAErB,aAAW,UAAU,SAAS;AAC5B,UAAM,QAAQ,QAAQ,OAAO,UAAU;AACvC,QAAI,UAAU,EAAG;AACjB,UAAM,WAAW,UAAU,OAAO,eAAe;AACjD,kBAAc;AACd,kBAAc,WAAW;AACzB,sBAAkB,aAAa,UAAU,QAAQ,IAAI;AAAA,EACvD;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc,iBAAiB;AAAA,EACjC;AACF;",
6
+ "names": []
7
+ }