@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,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/time-tracking/moneyVisibility.ts"],
4
+ "sourcesContent": ["import { resolveFeatureAccess } from './featureAccess'\n\nexport const RATES_FEATURE = 'staff.timesheets.rates.view'\n\ntype ContainerLike = { resolve: (name: string) => unknown }\n\n/**\n * Whether the caller may see rates, costs and amounts.\n *\n * One decision, made once, in one direction \u2014 because the same feature was\n * previously answered by five hand-rolled gates with two opposite failure\n * defaults. Three report surfaces (preview, export, sheet) read\n * `grantedFeatures === null || authorizeFeatures(...)`, so an unreadable grant\n * set *opened* the money fields; `my-work` and the entries list returned `false`\n * in the same situation. None of the three report routes declares\n * `staff.timesheets.rates.view` in its metadata \u2014 they require only\n * `reports.view` \u2014 so nothing else stood between a plain report viewer and the\n * customer's hourly rate when RBAC was unavailable.\n *\n * Failing closed is the only defensible default here: hiding a rate from someone\n * entitled to it is a support ticket, showing it to someone who is not is a\n * disclosure.\n *\n * The lookup itself belongs to `resolveFeatureAccess`, the module's single RBAC\n * authority: it asks `userHasAllFeatures` (the call that carries `isSuperAdmin`\n * internally), denies on every failure path, and logs the failure rather than\n * swallowing it. This function stays as the *name* of the question, so a route\n * reads `canSeeMoney` instead of a bare feature string \u2014 but it re-derives\n * nothing and cannot drift from the rest of the module.\n */\nexport async function resolveMoneyVisibility(\n container: ContainerLike,\n userId: string | null,\n scope: { tenantId: string | null; organizationId: string | null },\n): Promise<boolean> {\n return (await resolveFeatureAccess(container, userId, [RATES_FEATURE], scope)).allowed\n}\n"],
5
+ "mappings": "AAAA,SAAS,4BAA4B;AAE9B,MAAM,gBAAgB;AA4B7B,eAAsB,uBACpB,WACA,QACA,OACkB;AAClB,UAAQ,MAAM,qBAAqB,WAAW,QAAQ,CAAC,aAAa,GAAG,KAAK,GAAG;AACjF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,87 @@
1
+ import { MINUTES_PER_DAY, deriveInterval, parseClock } from "./interval.js";
2
+ import { extensionPoints } from "@open-mercato/core/modules/staff/extension-points";
3
+ import { createStrategyRegistry, BUILT_IN_STRATEGY_PRIORITY } from "./registries/registry.js";
4
+ import { tryStrategy } from "./registries/invoke.js";
5
+ import { hasResolverScope } from "./registries/scope.js";
6
+ const DATE_ONLY_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
7
+ const MS_PER_DAY = 864e5;
8
+ function toEpochDay(date) {
9
+ if (typeof date !== "string") return null;
10
+ const match = DATE_ONLY_PATTERN.exec(date.trim());
11
+ if (!match) return null;
12
+ const utc = Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3]));
13
+ if (!Number.isFinite(utc)) return null;
14
+ return Math.round(utc / MS_PER_DAY);
15
+ }
16
+ function toAbsoluteSpan(span) {
17
+ const epochDay = toEpochDay(span?.date);
18
+ if (epochDay === null) return null;
19
+ const derived = deriveInterval({
20
+ start: span.start,
21
+ end: span.end,
22
+ durationMinutes: span.durationMinutes
23
+ });
24
+ const startOfDay = parseClock(derived.start);
25
+ if (startOfDay === null) return null;
26
+ const duration = derived.durationMinutes;
27
+ if (duration === null || duration <= 0) return null;
28
+ const start = epochDay * MINUTES_PER_DAY + startOfDay;
29
+ return { start, end: start + duration };
30
+ }
31
+ function findOverlaps(candidate, existing, opts) {
32
+ const candidateSpan = toAbsoluteSpan(candidate);
33
+ if (!candidateSpan) return [];
34
+ const excludeId = opts?.excludeId;
35
+ const overlaps = [];
36
+ for (const other of existing ?? []) {
37
+ if (!other) continue;
38
+ if (excludeId !== void 0 && other.id === excludeId) continue;
39
+ const otherSpan = toAbsoluteSpan(other);
40
+ if (!otherSpan) continue;
41
+ if (candidateSpan.start >= otherSpan.end || otherSpan.start >= candidateSpan.end) continue;
42
+ overlaps.push({
43
+ id: other.id ?? null,
44
+ entry: other,
45
+ overlapMinutes: Math.min(candidateSpan.end, otherSpan.end) - Math.max(candidateSpan.start, otherSpan.start)
46
+ });
47
+ }
48
+ return overlaps;
49
+ }
50
+ const OVERLAP_POLICY_REGISTRY_ID = extensionPoints.hosts.overlapPolicyRegistry.spotId;
51
+ const BUILT_IN_OVERLAP_POLICY_ID = "staff.time_tracking.overlap.warn_when_enabled";
52
+ const overlapRegistry = createStrategyRegistry(OVERLAP_POLICY_REGISTRY_ID);
53
+ function registerOverlapPolicy(policy) {
54
+ return overlapRegistry.register(policy);
55
+ }
56
+ function listOverlapPolicies() {
57
+ return overlapRegistry.list();
58
+ }
59
+ function getOverlapPolicy(id) {
60
+ return overlapRegistry.get(id);
61
+ }
62
+ overlapRegistry.registerBuiltIn({
63
+ id: BUILT_IN_OVERLAP_POLICY_ID,
64
+ priority: BUILT_IN_STRATEGY_PRIORITY,
65
+ evaluate: (spans, ctx) => ctx.warningsEnabled && spans.length > 0 ? "warn" : "allow"
66
+ });
67
+ const DECISION_SEVERITY = { allow: 0, warn: 1, block: 2 };
68
+ function evaluateOverlapPolicies(spans, ctx) {
69
+ const scoped = hasResolverScope(ctx);
70
+ let decision = "allow";
71
+ for (const policy of overlapRegistry.list()) {
72
+ if (!scoped && policy.id !== BUILT_IN_OVERLAP_POLICY_ID) continue;
73
+ const answer = tryStrategy(OVERLAP_POLICY_REGISTRY_ID, policy.id, () => policy.evaluate(spans, ctx));
74
+ if (answer && DECISION_SEVERITY[answer] > DECISION_SEVERITY[decision]) decision = answer;
75
+ }
76
+ return decision;
77
+ }
78
+ export {
79
+ BUILT_IN_OVERLAP_POLICY_ID,
80
+ OVERLAP_POLICY_REGISTRY_ID,
81
+ evaluateOverlapPolicies,
82
+ findOverlaps,
83
+ getOverlapPolicy,
84
+ listOverlapPolicies,
85
+ registerOverlapPolicy
86
+ };
87
+ //# sourceMappingURL=overlap.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/time-tracking/overlap.ts"],
4
+ "sourcesContent": ["/**\n * EP-38 \u2014 the overlap policy provider.\n *\n * `findOverlaps` is unchanged: it still answers the arithmetic question \"which\n * of these spans intersect the candidate\". What is new is the policy layer on\n * top of it, which answers the product question \"and what should happen\".\n *\n * The built-in `staff.time_tracking.overlap.warn_when_enabled` reproduces\n * today's behaviour exactly, including the setting's off state: overlapping time\n * is legitimate in consulting work, so the answer is `warn` when the\n * `warnings.overlap` tenant setting is on and at least one span intersects, and\n * `allow` otherwise. It never returns `block`.\n *\n * Contributed policies can only ESCALATE \u2014 the combined answer is the most\n * severe of every policy's, `block` > `warn` > `allow` \u2014 so a payroll-compliance\n * module can harden the rule but nothing can silently suppress a warning the\n * tenant asked for.\n */\n\nimport { MINUTES_PER_DAY, deriveInterval, parseClock } from './interval'\nimport { extensionPoints } from '@open-mercato/core/modules/staff/extension-points'\nimport { createStrategyRegistry, BUILT_IN_STRATEGY_PRIORITY } from './registries/registry'\nimport { tryStrategy } from './registries/invoke'\nimport { hasResolverScope, type ScopedResolverContext } from './registries/scope'\n\nexport type OverlapSpan = {\n id?: string | null\n date: string\n start?: string | null\n end?: string | null\n durationMinutes?: number | null\n}\n\nexport type Overlap = {\n id: string | null\n entry: OverlapSpan\n overlapMinutes: number\n}\n\nexport type FindOverlapsOptions = {\n excludeId?: string\n}\n\nconst DATE_ONLY_PATTERN = /^(\\d{4})-(\\d{2})-(\\d{2})$/\n\nconst MS_PER_DAY = 86400000\n\nfunction toEpochDay(date: string | null | undefined): number | null {\n if (typeof date !== 'string') return null\n const match = DATE_ONLY_PATTERN.exec(date.trim())\n if (!match) return null\n const utc = Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3]))\n if (!Number.isFinite(utc)) return null\n return Math.round(utc / MS_PER_DAY)\n}\n\nfunction toAbsoluteSpan(span: OverlapSpan): { start: number; end: number } | null {\n const epochDay = toEpochDay(span?.date)\n if (epochDay === null) return null\n const derived = deriveInterval({\n start: span.start,\n end: span.end,\n durationMinutes: span.durationMinutes,\n })\n const startOfDay = parseClock(derived.start)\n if (startOfDay === null) return null\n const duration = derived.durationMinutes\n if (duration === null || duration <= 0) return null\n const start = epochDay * MINUTES_PER_DAY + startOfDay\n return { start, end: start + duration }\n}\n\nexport function findOverlaps(\n candidate: OverlapSpan,\n existing: readonly OverlapSpan[],\n opts?: FindOverlapsOptions,\n): Overlap[] {\n const candidateSpan = toAbsoluteSpan(candidate)\n if (!candidateSpan) return []\n\n const excludeId = opts?.excludeId\n const overlaps: Overlap[] = []\n\n for (const other of existing ?? []) {\n if (!other) continue\n if (excludeId !== undefined && other.id === excludeId) continue\n const otherSpan = toAbsoluteSpan(other)\n if (!otherSpan) continue\n if (candidateSpan.start >= otherSpan.end || otherSpan.start >= candidateSpan.end) continue\n overlaps.push({\n id: other.id ?? null,\n entry: other,\n overlapMinutes:\n Math.min(candidateSpan.end, otherSpan.end) - Math.max(candidateSpan.start, otherSpan.start),\n })\n }\n\n return overlaps\n}\n\nexport type OverlapDecision = 'allow' | 'warn' | 'block'\n\nexport type OverlapPolicyContext = ScopedResolverContext & {\n candidate: OverlapSpan\n /** The tenant's `warnings.overlap` setting; the built-in reads only this. */\n warningsEnabled: boolean\n staffMemberId?: string | null\n timeProjectId?: string | null\n excludeId?: string | null\n}\n\nexport type OverlapPolicy = {\n id: string\n priority?: number\n evaluate(spans: readonly Overlap[], ctx: OverlapPolicyContext): OverlapDecision\n}\n\nexport const OVERLAP_POLICY_REGISTRY_ID = extensionPoints.hosts.overlapPolicyRegistry.spotId\n\nexport const BUILT_IN_OVERLAP_POLICY_ID = 'staff.time_tracking.overlap.warn_when_enabled'\n\nconst overlapRegistry = createStrategyRegistry<OverlapPolicy>(OVERLAP_POLICY_REGISTRY_ID)\n\nexport function registerOverlapPolicy(policy: OverlapPolicy): () => void {\n return overlapRegistry.register(policy)\n}\n\nexport function listOverlapPolicies(): OverlapPolicy[] {\n return overlapRegistry.list()\n}\n\nexport function getOverlapPolicy(id: string | null | undefined): OverlapPolicy | null {\n return overlapRegistry.get(id)\n}\n\noverlapRegistry.registerBuiltIn({\n id: BUILT_IN_OVERLAP_POLICY_ID,\n priority: BUILT_IN_STRATEGY_PRIORITY,\n evaluate: (spans, ctx) => (ctx.warningsEnabled && spans.length > 0 ? 'warn' : 'allow'),\n})\n\nconst DECISION_SEVERITY: Record<OverlapDecision, number> = { allow: 0, warn: 1, block: 2 }\n\nexport function evaluateOverlapPolicies(\n spans: readonly Overlap[],\n ctx: OverlapPolicyContext,\n): OverlapDecision {\n const scoped = hasResolverScope(ctx)\n let decision: OverlapDecision = 'allow'\n for (const policy of overlapRegistry.list()) {\n if (!scoped && policy.id !== BUILT_IN_OVERLAP_POLICY_ID) continue\n // A policy can only ESCALATE, so a thrower is skipped: it cannot express a\n // warning, and letting it abort the evaluation would suppress the warnings the\n // remaining policies \u2014 including the built-in \u2014 do express.\n const answer = tryStrategy(OVERLAP_POLICY_REGISTRY_ID, policy.id, () => policy.evaluate(spans, ctx))\n if (answer && DECISION_SEVERITY[answer] > DECISION_SEVERITY[decision]) decision = answer\n }\n return decision\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,27 @@
1
+ import { createLogger } from "@open-mercato/shared/lib/logger";
2
+ const logger = createLogger("staff").child({ component: "time-tracking/portal-recipients" });
3
+ const MAX_PORTAL_RECIPIENTS = 100;
4
+ function isNonEmpty(value) {
5
+ return typeof value === "string" && value.trim().length > 0;
6
+ }
7
+ async function resolvePortalRecipientUserIds(em, scope) {
8
+ if (!isNonEmpty(scope.tenantId) || !isNonEmpty(scope.organizationId) || !isNonEmpty(scope.customerId)) {
9
+ return [];
10
+ }
11
+ try {
12
+ const db = em.getKysely();
13
+ const rows = await db.selectFrom("customer_users").select("id").where("tenant_id", "=", scope.tenantId).where("organization_id", "=", scope.organizationId).where("customer_entity_id", "=", scope.customerId).where("is_active", "=", true).where("deleted_at", "is", null).limit(MAX_PORTAL_RECIPIENTS).execute();
14
+ return rows.map((row) => row.id).filter(isNonEmpty);
15
+ } catch (err) {
16
+ logger.warn("Could not resolve portal recipients for a time-tracking customer", {
17
+ customerId: scope.customerId,
18
+ err
19
+ });
20
+ return [];
21
+ }
22
+ }
23
+ export {
24
+ MAX_PORTAL_RECIPIENTS,
25
+ resolvePortalRecipientUserIds
26
+ };
27
+ //# sourceMappingURL=portalRecipients.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/time-tracking/portalRecipients.ts"],
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('staff').child({ component: 'time-tracking/portal-recipients' })\n\n/**\n * EP-50. The customer-user side of the portal contract, expressed as table and\n * column **names** rather than imports.\n *\n * Staff is slated for extraction into `@open-mercato/staff`, so it may not take a\n * static dependency on `customer_accounts`; `requires` in `index.ts` stays\n * `['planner', 'resources']`. A Kysely query against the string\n * `'customer_users'` reads the mapping without an import edge, exactly the way\n * `warranty_claims/commands/claims.ts` does \u2014 and if the table is absent because\n * the module is not installed, the query throws and this returns an empty list,\n * which every caller treats as \"no portal audience\".\n *\n * `customer_users.customer_entity_id` is the FK into `customers:customer_entity`,\n * which is the same id `staff_time_reports.customer_id` holds (EP-44 declares that\n * link). That equality is the whole ownership model: a portal user reaches a\n * report when, and only when, their own `customer_entity_id` matches the report's\n * `customer_id` inside the same tenant and organization.\n */\n\ntype CustomerUsersDb = {\n customer_users: {\n id: string\n tenant_id: string | null\n organization_id: string | null\n customer_entity_id: string | null\n is_active: boolean\n deleted_at: Date | null\n }\n}\n\nexport type PortalRecipientScope = {\n tenantId: string\n organizationId: string\n customerId: string\n}\n\n/** A broadcast fan-out, not a mailing list \u2014 a customer with more portal users than this gets a truncated audience rather than an unbounded query. */\nexport const MAX_PORTAL_RECIPIENTS = 100\n\nfunction isNonEmpty(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0\n}\n\nexport async function resolvePortalRecipientUserIds(\n em: EntityManager,\n scope: PortalRecipientScope,\n): Promise<string[]> {\n if (!isNonEmpty(scope.tenantId) || !isNonEmpty(scope.organizationId) || !isNonEmpty(scope.customerId)) {\n return []\n }\n try {\n const db = em.getKysely<CustomerUsersDb>()\n const rows = await db\n .selectFrom('customer_users')\n .select('id')\n .where('tenant_id', '=', scope.tenantId)\n .where('organization_id', '=', scope.organizationId)\n .where('customer_entity_id', '=', scope.customerId)\n .where('is_active', '=', true)\n .where('deleted_at', 'is', null)\n .limit(MAX_PORTAL_RECIPIENTS)\n .execute()\n return rows.map((row) => row.id).filter(isNonEmpty)\n } catch (err) {\n logger.warn('Could not resolve portal recipients for a time-tracking customer', {\n customerId: scope.customerId,\n err,\n })\n return []\n }\n}\n"],
5
+ "mappings": "AACA,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,OAAO,EAAE,MAAM,EAAE,WAAW,kCAAkC,CAAC;AAuCpF,MAAM,wBAAwB;AAErC,SAAS,WAAW,OAAiC;AACnD,SAAO,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS;AAC5D;AAEA,eAAsB,8BACpB,IACA,OACmB;AACnB,MAAI,CAAC,WAAW,MAAM,QAAQ,KAAK,CAAC,WAAW,MAAM,cAAc,KAAK,CAAC,WAAW,MAAM,UAAU,GAAG;AACrG,WAAO,CAAC;AAAA,EACV;AACA,MAAI;AACF,UAAM,KAAK,GAAG,UAA2B;AACzC,UAAM,OAAO,MAAM,GAChB,WAAW,gBAAgB,EAC3B,OAAO,IAAI,EACX,MAAM,aAAa,KAAK,MAAM,QAAQ,EACtC,MAAM,mBAAmB,KAAK,MAAM,cAAc,EAClD,MAAM,sBAAsB,KAAK,MAAM,UAAU,EACjD,MAAM,aAAa,KAAK,IAAI,EAC5B,MAAM,cAAc,MAAM,IAAI,EAC9B,MAAM,qBAAqB,EAC3B,QAAQ;AACX,WAAO,KAAK,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,OAAO,UAAU;AAAA,EACpD,SAAS,KAAK;AACZ,WAAO,KAAK,oEAAoE;AAAA,MAC9E,YAAY,MAAM;AAAA,MAClB;AAAA,IACF,CAAC;AACD,WAAO,CAAC;AAAA,EACV;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,16 @@
1
+ const PORTAL_TIME_REPORTS_VIEW_FEATURE = "portal.time_reports.view";
2
+ function portalOwnedReportClause(scope) {
3
+ return {
4
+ sql: `tenant_id = ? AND organization_id = ? AND customer_id = ? AND status = 'closed' AND deleted_at IS NULL`,
5
+ params: [scope.tenantId, scope.organizationId, scope.customerId]
6
+ };
7
+ }
8
+ const PORTAL_TIME_REPORT_BEFORE_SPOT_ID = "portal:staff.time_report:before";
9
+ const PORTAL_TIME_REPORT_AFTER_SPOT_ID = "portal:staff.time_report:after";
10
+ export {
11
+ PORTAL_TIME_REPORTS_VIEW_FEATURE,
12
+ PORTAL_TIME_REPORT_AFTER_SPOT_ID,
13
+ PORTAL_TIME_REPORT_BEFORE_SPOT_ID,
14
+ portalOwnedReportClause
15
+ };
16
+ //# sourceMappingURL=portalReports.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/time-tracking/portalReports.ts"],
4
+ "sourcesContent": ["/**\n * EP-50. The customer-portal contract for time reports, in one place so the list\n * route, the detail route and the tests cannot drift from each other.\n *\n * `portal.*` is the customer RBAC namespace (`CustomerRbacService`), disjoint from\n * the staff `staff.timesheets.*` namespace. It is granted through\n * `setup.defaultCustomerRoleFeatures`, not `acl.ts` \u2014 `acl.ts` declares staff\n * features only.\n */\n\nexport const PORTAL_TIME_REPORTS_VIEW_FEATURE = 'portal.time_reports.view'\n\n/**\n * The four predicates every portal read of `staff_time_reports` must carry, as a\n * SQL fragment with its parameters in order. Written once because a portal report\n * read that forgets one of them is a cross-customer disclosure, and three call\n * sites re-typing the same WHERE clause is how one of them eventually forgets.\n */\nexport function portalOwnedReportClause(scope: {\n tenantId: string\n organizationId: string\n customerId: string\n}): { sql: string; params: string[] } {\n return {\n sql: `tenant_id = ? AND organization_id = ? AND customer_id = ? AND status = 'closed' AND deleted_at IS NULL`,\n params: [scope.tenantId, scope.organizationId, scope.customerId],\n }\n}\n\nexport const PORTAL_TIME_REPORT_BEFORE_SPOT_ID = 'portal:staff.time_report:before'\nexport const PORTAL_TIME_REPORT_AFTER_SPOT_ID = 'portal:staff.time_report:after'\n"],
5
+ "mappings": "AAUO,MAAM,mCAAmC;AAQzC,SAAS,wBAAwB,OAIF;AACpC,SAAO;AAAA,IACL,KAAK;AAAA,IACL,QAAQ,CAAC,MAAM,UAAU,MAAM,gBAAgB,MAAM,UAAU;AAAA,EACjE;AACF;AAEO,MAAM,oCAAoC;AAC1C,MAAM,mCAAmC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,135 @@
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 { runStrategy } from "./registries/invoke.js";
5
+ const PROJECT_CODE_MAX_LENGTH = 20;
6
+ const PROJECT_CODE_TARGET_LENGTH = 3;
7
+ const PROJECT_CODE_FALLBACK = "PRJ";
8
+ const WORD_BOUNDARY_BUDGET = PROJECT_CODE_MAX_LENGTH - 1;
9
+ const COMBINING_MARK_START = 768;
10
+ const COMBINING_MARK_END = 879;
11
+ const ASCII_END = 127;
12
+ const SPECIAL_CHARACTERS = {
13
+ "\u0142": "l",
14
+ "\u0141": "L",
15
+ "\u0111": "d",
16
+ "\u0110": "D",
17
+ "\xF0": "d",
18
+ "\xD0": "D",
19
+ "\xF8": "o",
20
+ "\xD8": "O",
21
+ "\xE6": "ae",
22
+ "\xC6": "AE",
23
+ "\u0153": "oe",
24
+ "\u0152": "OE",
25
+ "\xDF": "ss",
26
+ "\xFE": "th",
27
+ "\xDE": "TH"
28
+ };
29
+ function transliterate(value) {
30
+ let result = "";
31
+ for (const character of value.normalize("NFD")) {
32
+ const code = character.codePointAt(0) ?? 0;
33
+ if (code >= COMBINING_MARK_START && code <= COMBINING_MARK_END) continue;
34
+ if (code <= ASCII_END) {
35
+ result += character;
36
+ continue;
37
+ }
38
+ result += SPECIAL_CHARACTERS[character] ?? character;
39
+ }
40
+ return result;
41
+ }
42
+ function slugifyProjectName(name) {
43
+ return transliterate(typeof name === "string" ? name : "").toUpperCase().replace(/[^A-Z0-9]+/g, "-").replace(/^-+|-+$/g, "");
44
+ }
45
+ function truncateAtWordBoundary(slug, budget) {
46
+ if (budget <= 0) return "";
47
+ if (slug.length <= budget) return slug;
48
+ let result = "";
49
+ for (const word of slug.split("-")) {
50
+ const next = result ? `${result}-${word}` : word;
51
+ if (next.length > budget) break;
52
+ result = next;
53
+ }
54
+ if (!result) result = slug.slice(0, budget);
55
+ return result.replace(/-+$/g, "");
56
+ }
57
+ function deriveProjectCodeBase(name) {
58
+ const slug = slugifyProjectName(name);
59
+ if (!slug) return PROJECT_CODE_FALLBACK;
60
+ const words = slug.split("-").filter((word) => word.length > 0);
61
+ if (words.length === 0) return PROJECT_CODE_FALLBACK;
62
+ const initials = words.map((word) => word[0]).join("");
63
+ const base = words.length >= PROJECT_CODE_TARGET_LENGTH ? initials.slice(0, PROJECT_CODE_TARGET_LENGTH) : words.join("").slice(0, PROJECT_CODE_TARGET_LENGTH);
64
+ return base || PROJECT_CODE_FALLBACK;
65
+ }
66
+ function deriveInitialsCode(name, taken) {
67
+ const base = deriveProjectCodeBase(name);
68
+ const reserved = new Set(Array.from(taken ?? []).map((value) => String(value).toUpperCase()));
69
+ if (!reserved.has(base)) return base;
70
+ for (let counter = 2; counter < 1e4; counter += 1) {
71
+ const candidate = `${base}${counter}`;
72
+ if (candidate.length > PROJECT_CODE_MAX_LENGTH) break;
73
+ if (!reserved.has(candidate)) return candidate;
74
+ }
75
+ const longBase = truncateAtWordBoundary(slugifyProjectName(name), WORD_BOUNDARY_BUDGET) || PROJECT_CODE_FALLBACK;
76
+ if (!reserved.has(longBase)) return longBase;
77
+ for (let counter = 2; counter < 1e4; counter += 1) {
78
+ const suffix = `-${counter}`;
79
+ const stem = truncateAtWordBoundary(longBase, PROJECT_CODE_MAX_LENGTH - suffix.length) || PROJECT_CODE_FALLBACK;
80
+ const candidate = `${stem}${suffix}`;
81
+ if (!reserved.has(candidate)) return candidate;
82
+ }
83
+ return longBase;
84
+ }
85
+ const PROJECT_CODE_GENERATOR_REGISTRY_ID = extensionPoints.hosts.projectCodeGeneratorRegistry.spotId;
86
+ const BUILT_IN_PROJECT_CODE_GENERATOR_ID = "staff.time_tracking.project_code.initials";
87
+ const generatorRegistry = createStrategyRegistry(PROJECT_CODE_GENERATOR_REGISTRY_ID);
88
+ function registerProjectCodeGenerator(generator) {
89
+ return generatorRegistry.register(generator);
90
+ }
91
+ function listProjectCodeGenerators() {
92
+ return generatorRegistry.list();
93
+ }
94
+ function getProjectCodeGenerator(id) {
95
+ return generatorRegistry.get(id);
96
+ }
97
+ const builtInProjectCodeGenerator = generatorRegistry.registerBuiltIn({
98
+ id: BUILT_IN_PROJECT_CODE_GENERATOR_ID,
99
+ priority: BUILT_IN_STRATEGY_PRIORITY,
100
+ generate: (name, taken) => deriveInitialsCode(name, taken)
101
+ });
102
+ function resolveProjectCodeGenerator(ctx) {
103
+ return selectScopedStrategy(generatorRegistry.list(), builtInProjectCodeGenerator, ctx);
104
+ }
105
+ function deriveProjectCode(name, taken = /* @__PURE__ */ new Set(), ctx) {
106
+ const generator = resolveProjectCodeGenerator(ctx);
107
+ const reserved = taken ?? /* @__PURE__ */ new Set();
108
+ const builtIn = () => deriveInitialsCode(name, reserved);
109
+ if (generator === builtInProjectCodeGenerator) return builtIn();
110
+ const answer = runStrategy(
111
+ PROJECT_CODE_GENERATOR_REGISTRY_ID,
112
+ generator.id,
113
+ () => generator.generate(name, reserved, ctx ?? {}),
114
+ () => null
115
+ );
116
+ if (typeof answer !== "string") return builtIn();
117
+ const trimmed = answer.trim();
118
+ if (!trimmed || trimmed.length > PROJECT_CODE_MAX_LENGTH) return builtIn();
119
+ return trimmed;
120
+ }
121
+ export {
122
+ BUILT_IN_PROJECT_CODE_GENERATOR_ID,
123
+ PROJECT_CODE_FALLBACK,
124
+ PROJECT_CODE_GENERATOR_REGISTRY_ID,
125
+ PROJECT_CODE_MAX_LENGTH,
126
+ PROJECT_CODE_TARGET_LENGTH,
127
+ deriveProjectCode,
128
+ deriveProjectCodeBase,
129
+ getProjectCodeGenerator,
130
+ listProjectCodeGenerators,
131
+ registerProjectCodeGenerator,
132
+ resolveProjectCodeGenerator,
133
+ slugifyProjectName
134
+ };
135
+ //# sourceMappingURL=projectCode.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/time-tracking/projectCode.ts"],
4
+ "sourcesContent": ["/**\n * EP-39 \u2014 the project code generator provider.\n *\n * `deriveProjectCode` keeps its signature and its meaning; the three-letter\n * initials rule plus the collision counter now live in the built-in generator\n * `staff.time_tracking.project_code.initials`, which `deriveProjectCode`\n * delegates to. A contributed generator outranks it only when the caller passes\n * a complete tenant + organization scope.\n *\n * Two runtime call sites exist and both are client-side \u2014\n * `lib/time-tracking-ui/ProjectCodeField.tsx` derives the suggestion as the user\n * types a project name and again when the field is reset. Neither has a tenant\n * id to hand, so both resolve the built-in today.\n * `lib/time-tracking/migrateProjectCodes.ts` uses `deriveProjectCodeBase`, not\n * this function.\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 { runStrategy } from './registries/invoke'\n\nexport const PROJECT_CODE_MAX_LENGTH = 20\n\n/**\n * The length a derived code aims for. Task references quote it on every card,\n * drawer title and picker row (`ADS-21`), so a code that reads like a sentence\n * makes the reference unusable as the thing people say to each other.\n *\n * It is a target, not a ceiling: a collision extends the code rather than\n * mangling it, and a hand-typed code is still allowed up to\n * `PROJECT_CODE_MAX_LENGTH`, because the person naming a project knows better\n * than the transliterator does.\n */\nexport const PROJECT_CODE_TARGET_LENGTH = 3\n\nexport const PROJECT_CODE_FALLBACK = 'PRJ'\n\nconst WORD_BOUNDARY_BUDGET = PROJECT_CODE_MAX_LENGTH - 1\n\nconst COMBINING_MARK_START = 0x300\nconst COMBINING_MARK_END = 0x36f\nconst ASCII_END = 0x7f\n\nconst SPECIAL_CHARACTERS: Record<string, string> = {\n '\u0142': 'l',\n '\u0141': 'L',\n '\u0111': 'd',\n '\u0110': 'D',\n '\u00F0': 'd',\n '\u00D0': 'D',\n '\u00F8': 'o',\n '\u00D8': 'O',\n '\u00E6': 'ae',\n '\u00C6': 'AE',\n '\u0153': 'oe',\n '\u0152': 'OE',\n '\u00DF': 'ss',\n '\u00FE': 'th',\n '\u00DE': 'TH',\n}\n\nfunction transliterate(value: string): string {\n let result = ''\n for (const character of value.normalize('NFD')) {\n const code = character.codePointAt(0) ?? 0\n if (code >= COMBINING_MARK_START && code <= COMBINING_MARK_END) continue\n if (code <= ASCII_END) {\n result += character\n continue\n }\n result += SPECIAL_CHARACTERS[character] ?? character\n }\n return result\n}\n\nexport function slugifyProjectName(name: string): string {\n return transliterate(typeof name === 'string' ? name : '')\n .toUpperCase()\n .replace(/[^A-Z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n}\n\nfunction truncateAtWordBoundary(slug: string, budget: number): string {\n if (budget <= 0) return ''\n if (slug.length <= budget) return slug\n let result = ''\n for (const word of slug.split('-')) {\n const next = result ? `${result}-${word}` : word\n if (next.length > budget) break\n result = next\n }\n if (!result) result = slug.slice(0, budget)\n return result.replace(/-+$/g, '')\n}\n\n/**\n * The three letters a name reduces to.\n *\n * A multi-word name gives its initials \u2014 `Ergo Hestia Korpo` reads as `EHK`,\n * which a person recognises, where the first three characters (`ERG`) do not.\n * A single word gives its first three letters, so `Apollo` is `APO` and a name\n * already at or under three characters (`HBH`) is left exactly as it is.\n */\nexport function deriveProjectCodeBase(name: string): string {\n const slug = slugifyProjectName(name)\n if (!slug) return PROJECT_CODE_FALLBACK\n const words = slug.split('-').filter((word) => word.length > 0)\n if (words.length === 0) return PROJECT_CODE_FALLBACK\n\n const initials = words.map((word) => word[0]).join('')\n const base = words.length >= PROJECT_CODE_TARGET_LENGTH\n ? initials.slice(0, PROJECT_CODE_TARGET_LENGTH)\n : words.join('').slice(0, PROJECT_CODE_TARGET_LENGTH)\n\n return base || PROJECT_CODE_FALLBACK\n}\n\n/**\n * Three letters is roughly 17,500 combinations and derived codes cluster hard \u2014\n * `Apollo`, `Aponia` and `Apex` all want `APO` \u2014 so a collision is the normal\n * case rather than the exception, and the rule for resolving one has to be\n * boring and predictable. The counter extends the code (`APO` \u2192 `APO2`) instead\n * of substituting a letter, because a fourth character is still readable while\n * `APQ` is a different project as far as anybody reading it is concerned.\n */\nfunction deriveInitialsCode(name: string, taken: Set<string>): string {\n const base = deriveProjectCodeBase(name)\n const reserved = new Set(Array.from(taken ?? []).map((value) => String(value).toUpperCase()))\n if (!reserved.has(base)) return base\n\n for (let counter = 2; counter < 10000; counter += 1) {\n const candidate = `${base}${counter}`\n if (candidate.length > PROJECT_CODE_MAX_LENGTH) break\n if (!reserved.has(candidate)) return candidate\n }\n\n // Every short candidate is taken. Fall back to the long form rather than\n // failing the save \u2014 a save that refuses to happen is worse than a long code.\n const longBase =\n truncateAtWordBoundary(slugifyProjectName(name), WORD_BOUNDARY_BUDGET) || PROJECT_CODE_FALLBACK\n if (!reserved.has(longBase)) return longBase\n for (let counter = 2; counter < 10000; counter += 1) {\n const suffix = `-${counter}`\n const stem =\n truncateAtWordBoundary(longBase, PROJECT_CODE_MAX_LENGTH - suffix.length) || PROJECT_CODE_FALLBACK\n const candidate = `${stem}${suffix}`\n if (!reserved.has(candidate)) return candidate\n }\n return longBase\n}\n\nexport type ProjectCodeContext = ScopedResolverContext & {\n customerId?: string | null\n timeProjectId?: string | null\n}\n\nexport type ProjectCodeGenerator = {\n id: string\n priority?: number\n generate(name: string, taken: Set<string>, ctx: ProjectCodeContext): string\n}\n\nexport const PROJECT_CODE_GENERATOR_REGISTRY_ID = extensionPoints.hosts.projectCodeGeneratorRegistry.spotId\n\nexport const BUILT_IN_PROJECT_CODE_GENERATOR_ID = 'staff.time_tracking.project_code.initials'\n\nconst generatorRegistry = createStrategyRegistry<ProjectCodeGenerator>(PROJECT_CODE_GENERATOR_REGISTRY_ID)\n\nexport function registerProjectCodeGenerator(generator: ProjectCodeGenerator): () => void {\n return generatorRegistry.register(generator)\n}\n\nexport function listProjectCodeGenerators(): ProjectCodeGenerator[] {\n return generatorRegistry.list()\n}\n\nexport function getProjectCodeGenerator(id: string | null | undefined): ProjectCodeGenerator | null {\n return generatorRegistry.get(id)\n}\n\nconst builtInProjectCodeGenerator: ProjectCodeGenerator = generatorRegistry.registerBuiltIn({\n id: BUILT_IN_PROJECT_CODE_GENERATOR_ID,\n priority: BUILT_IN_STRATEGY_PRIORITY,\n generate: (name, taken) => deriveInitialsCode(name, taken),\n})\n\nexport function resolveProjectCodeGenerator(\n ctx?: ScopedResolverContext | null,\n): ProjectCodeGenerator {\n return selectScopedStrategy(generatorRegistry.list(), builtInProjectCodeGenerator, ctx)\n}\n\n/**\n * The code lands in a column with a length limit and is quoted on every task\n * reference, so a generator that throws or answers with a blank, an over-long or a\n * non-string value falls back to the module's own initials rule. A save that refuses\n * to happen, or a project whose reference is unreadable, is worse than a code the\n * contributor did not choose.\n */\nexport function deriveProjectCode(\n name: string,\n taken: Set<string> = new Set(),\n ctx?: ProjectCodeContext | null,\n): string {\n const generator = resolveProjectCodeGenerator(ctx)\n const reserved = taken ?? new Set<string>()\n const builtIn = () => deriveInitialsCode(name, reserved)\n if (generator === builtInProjectCodeGenerator) return builtIn()\n\n const answer = runStrategy(\n PROJECT_CODE_GENERATOR_REGISTRY_ID,\n generator.id,\n () => generator.generate(name, reserved, ctx ?? {}) as unknown,\n () => null,\n )\n if (typeof answer !== 'string') return builtIn()\n const trimmed = answer.trim()\n if (!trimmed || trimmed.length > PROJECT_CODE_MAX_LENGTH) return builtIn()\n return trimmed\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,187 @@
1
+ import { createModuleQueue } from "@open-mercato/queue";
2
+ import { createLogger } from "@open-mercato/shared/lib/logger";
3
+ import { buildSqlInClause } from "./sqlInClause.js";
4
+ import {
5
+ BUILT_IN_RECALCULATION_ID,
6
+ registerTimeTrackingRecalculation
7
+ } from "./recalculations.js";
8
+ import { BUILT_IN_STRATEGY_PRIORITY } from "./registries/registry.js";
9
+ import {
10
+ staffTimeEntryCommandIds
11
+ } from "../../commands/timesheets-entries.js";
12
+ const logger = createLogger("staff").child({ component: "time-tracking/reapply-rounding" });
13
+ const STAFF_TIME_REAPPLY_ROUNDING_QUEUE = "staff-time-reapply-rounding";
14
+ const STAFF_TIME_REAPPLY_ROUNDING_JOB_TYPE = "staff.timesheets.reapply_rounding";
15
+ const REAPPLY_ROUNDING_BATCH_SIZE = 200;
16
+ const queues = /* @__PURE__ */ new Map();
17
+ function getStaffQueue(queueName) {
18
+ const existing = queues.get(queueName);
19
+ if (existing) return existing;
20
+ const concurrency = Math.max(1, Number.parseInt(process.env.STAFF_QUEUE_CONCURRENCY ?? "1", 10) || 1);
21
+ const created = createModuleQueue(queueName, { concurrency });
22
+ queues.set(queueName, created);
23
+ return created;
24
+ }
25
+ function buildCommandContext(container, tenantId, organizationId) {
26
+ return {
27
+ container,
28
+ auth: null,
29
+ // The job runs without an end user; the enqueuing route is the authorization
30
+ // gate (`staff.timesheets.settings.manage`), so the worker declares itself a
31
+ // system actor and passes the scope explicitly rather than inheriting one.
32
+ systemActor: true,
33
+ bulkImport: { skipEvents: true, skipNotifications: true },
34
+ organizationScope: {
35
+ selectedId: organizationId,
36
+ filterIds: [organizationId],
37
+ allowedIds: [organizationId],
38
+ tenantId
39
+ },
40
+ selectedOrganizationId: organizationId,
41
+ organizationIds: [organizationId]
42
+ };
43
+ }
44
+ function organizationClause(organizationIds) {
45
+ if (!organizationIds) return { sql: "", params: [] };
46
+ const clause = buildSqlInClause("organization_id", organizationIds);
47
+ return { sql: ` AND ${clause.sql}`, params: clause.params };
48
+ }
49
+ async function countReapplyRoundingCandidates(em, scope) {
50
+ const org = organizationClause(scope.organizationIds);
51
+ const rows = await em.getConnection().execute(
52
+ `
53
+ SELECT COUNT(*)::bigint AS candidate_count
54
+ FROM staff_time_entries
55
+ WHERE tenant_id = ?
56
+ AND deleted_at IS NULL
57
+ AND locked_report_id IS NULL${org.sql}
58
+ `,
59
+ [scope.tenantId, ...org.params]
60
+ );
61
+ const parsed = Number(rows?.[0]?.candidate_count ?? 0);
62
+ return Number.isFinite(parsed) ? parsed : 0;
63
+ }
64
+ async function loadCandidatePage(em, scope, cursor, limit) {
65
+ const org = organizationClause(scope.organizationIds);
66
+ const cursorClause = cursor ? " AND id > ?" : "";
67
+ const cursorParams = cursor ? [cursor] : [];
68
+ return await em.getConnection().execute(
69
+ `
70
+ SELECT id, organization_id
71
+ FROM staff_time_entries
72
+ WHERE tenant_id = ?
73
+ AND deleted_at IS NULL
74
+ AND locked_report_id IS NULL${org.sql}${cursorClause}
75
+ ORDER BY id ASC
76
+ LIMIT ?
77
+ `,
78
+ [scope.tenantId, ...org.params, ...cursorParams, limit]
79
+ );
80
+ }
81
+ function groupByOrganization(rows) {
82
+ const grouped = /* @__PURE__ */ new Map();
83
+ for (const row of rows) {
84
+ const bucket = grouped.get(row.organization_id) ?? [];
85
+ bucket.push(row.id);
86
+ grouped.set(row.organization_id, bucket);
87
+ }
88
+ return grouped;
89
+ }
90
+ async function reapplyRoundingBatches(params) {
91
+ const { container, scope, report } = params;
92
+ const em = container.resolve("em").fork();
93
+ const commandBus = container.resolve("commandBus");
94
+ const totalCount = await countReapplyRoundingCandidates(em, scope);
95
+ const summary = {
96
+ totalCount,
97
+ processedCount: 0,
98
+ updatedCount: 0,
99
+ unchangedCount: 0,
100
+ skippedCount: 0,
101
+ cancelled: false
102
+ };
103
+ if (totalCount === 0) return summary;
104
+ await report.setTotal(totalCount);
105
+ let cursor = null;
106
+ for (; ; ) {
107
+ const page = await loadCandidatePage(em, scope, cursor, REAPPLY_ROUNDING_BATCH_SIZE);
108
+ if (page.length === 0) break;
109
+ cursor = page[page.length - 1].id;
110
+ let pageProcessed = 0;
111
+ for (const [organizationId, entryIds] of groupByOrganization(page)) {
112
+ const executed = await commandBus.execute(staffTimeEntryCommandIds.reapplyRounding, {
113
+ input: { tenantId: scope.tenantId, organizationId, entryIds },
114
+ ctx: buildCommandContext(container, scope.tenantId, organizationId)
115
+ });
116
+ const result = executed?.result;
117
+ summary.updatedCount += result?.updatedCount ?? 0;
118
+ summary.unchangedCount += result?.unchangedCount ?? 0;
119
+ summary.skippedCount += result?.skippedCount ?? 0;
120
+ summary.processedCount += entryIds.length;
121
+ pageProcessed += entryIds.length;
122
+ }
123
+ await report.advance(pageProcessed);
124
+ if (await report.isCancellationRequested()) {
125
+ summary.cancelled = true;
126
+ return summary;
127
+ }
128
+ }
129
+ return summary;
130
+ }
131
+ async function reapplyRoundingWithProgress(params) {
132
+ const { container, progressJobId, scope } = params;
133
+ const progressService = container.resolve("progressService");
134
+ const progressContext = {
135
+ tenantId: scope.tenantId,
136
+ organizationId: scope.organizationIds?.length === 1 ? scope.organizationIds[0] : null,
137
+ userId: scope.userId ?? null
138
+ };
139
+ await progressService.startJob(progressJobId, progressContext);
140
+ let totalCount = 0;
141
+ let processedCount = 0;
142
+ const summary = await reapplyRoundingBatches({
143
+ container,
144
+ scope,
145
+ report: {
146
+ setTotal: async (total) => {
147
+ totalCount = total;
148
+ await progressService.updateProgress(progressJobId, { totalCount, processedCount: 0 }, progressContext);
149
+ },
150
+ advance: async (processed) => {
151
+ processedCount += processed;
152
+ await progressService.updateProgress(
153
+ progressJobId,
154
+ { totalCount, processedCount: Math.min(processedCount, totalCount) },
155
+ progressContext
156
+ );
157
+ },
158
+ isCancellationRequested: () => progressService.isCancellationRequested(progressJobId, scope.tenantId, progressContext.organizationId)
159
+ }
160
+ });
161
+ if (summary.cancelled) {
162
+ logger.info("staff.timesheets reapply-rounding cancelled", { progressJobId, ...summary });
163
+ await progressService.markCancelled(progressJobId, progressContext);
164
+ return summary;
165
+ }
166
+ await progressService.completeJob(progressJobId, { resultSummary: { ...summary } }, progressContext);
167
+ if (summary.totalCount > 0) {
168
+ logger.info("staff.timesheets reapply-rounding finished", { progressJobId, ...summary });
169
+ }
170
+ return summary;
171
+ }
172
+ registerTimeTrackingRecalculation({
173
+ id: BUILT_IN_RECALCULATION_ID,
174
+ labelKey: "staff.time_tracking.settings.retro.jobName",
175
+ priority: BUILT_IN_STRATEGY_PRIORITY,
176
+ run: ({ container, scope, report }) => reapplyRoundingBatches({ container, scope, report })
177
+ });
178
+ export {
179
+ REAPPLY_ROUNDING_BATCH_SIZE,
180
+ STAFF_TIME_REAPPLY_ROUNDING_JOB_TYPE,
181
+ STAFF_TIME_REAPPLY_ROUNDING_QUEUE,
182
+ countReapplyRoundingCandidates,
183
+ getStaffQueue,
184
+ reapplyRoundingBatches,
185
+ reapplyRoundingWithProgress
186
+ };
187
+ //# sourceMappingURL=reapplyRounding.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/time-tracking/reapplyRounding.ts"],
4
+ "sourcesContent": ["/**\n * Retroactive rounding, the long-running half (T7.3 / screen 16 note 3).\n *\n * \"Apply the new rounding rule to entries that already exist\" is a tenant-wide\n * rewrite of a billing-relevant column, so it is a `ProgressJob` driven by a queue\n * worker rather than a request that has to finish before a response \u2014 and, just as\n * importantly, an action somebody deliberately takes rather than a passive toggle\n * that rewrites history when the settings form is saved.\n *\n * What this file owns:\n *\n * * **Candidate selection.** Only entries that are not deleted and not locked into\n * a closed report are ever selected. The command re-asserts the same condition in\n * its own WHERE clause, so a locked entry is excluded twice, independently.\n * * **Batching and progress.** Ids are paged with a stable `id > cursor` cursor and\n * handed to the command in batches, grouped by organization because a command\n * call is scoped to exactly one. Progress is reported per batch, and a cancel\n * request is honoured between batches.\n * * **Side-effect suppression.** Batches run with `bulkImport.skipEvents` /\n * `skipNotifications`: the query index still refreshes per entry, but a\n * thousand-entry restatement does not emit a thousand `time_entry.updated`\n * events (which would, among other things, re-evaluate every project budget\n * threshold a thousand times).\n */\n\nimport type { AwilixContainer } from 'awilix'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CommandBus, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { createModuleQueue, type Queue } from '@open-mercato/queue'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { buildSqlInClause } from './sqlInClause'\nimport {\n BUILT_IN_RECALCULATION_ID,\n registerTimeTrackingRecalculation,\n} from './recalculations'\nimport { BUILT_IN_STRATEGY_PRIORITY } from './registries/registry'\nimport type { ProgressService, ProgressServiceContext } from '../../../progress/lib/progressService'\nimport {\n staffTimeEntryCommandIds,\n type StaffTimeEntryReapplyRoundingResult,\n} from '../../commands/timesheets-entries'\n\nconst logger = createLogger('staff').child({ component: 'time-tracking/reapply-rounding' })\n\nexport const STAFF_TIME_REAPPLY_ROUNDING_QUEUE = 'staff-time-reapply-rounding'\nexport const STAFF_TIME_REAPPLY_ROUNDING_JOB_TYPE = 'staff.timesheets.reapply_rounding'\n\n/** One command call per batch; the command itself caps the id list at 500. */\nexport const REAPPLY_ROUNDING_BATCH_SIZE = 200\n\nexport type ReapplyRoundingScope = {\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\nexport type ReapplyRoundingJobPayload = {\n progressJobId: string\n scope: ReapplyRoundingScope\n /**\n * EP-51. The recalculation hooks this job should run, in the order given.\n * Absent \u2014 which is every job the settings route enqueues \u2014 means the built-in\n * rounding hook alone, so the retro-rounding button keeps doing exactly what it\n * did before the registry existed.\n */\n hookIds?: string[] | null\n}\n\nexport type ReapplyRoundingSummary = {\n totalCount: number\n processedCount: number\n updatedCount: number\n unchangedCount: number\n skippedCount: number\n cancelled: boolean\n}\n\nconst queues = new Map<string, Queue<Record<string, unknown>>>()\n\nexport function getStaffQueue(queueName: string): Queue<Record<string, unknown>> {\n const existing = queues.get(queueName)\n if (existing) return existing\n const concurrency = Math.max(1, Number.parseInt(process.env.STAFF_QUEUE_CONCURRENCY ?? '1', 10) || 1)\n const created = createModuleQueue<Record<string, unknown>>(queueName, { concurrency })\n queues.set(queueName, created)\n return created\n}\n\ntype CandidateRow = {\n id: string\n organization_id: string\n}\n\ntype CountRow = {\n candidate_count: string | number | null\n}\n\nfunction buildCommandContext(\n container: AwilixContainer,\n tenantId: string,\n organizationId: string,\n): CommandRuntimeContext {\n return {\n container,\n auth: null,\n // The job runs without an end user; the enqueuing route is the authorization\n // gate (`staff.timesheets.settings.manage`), so the worker declares itself a\n // system actor and passes the scope explicitly rather than inheriting one.\n systemActor: true,\n bulkImport: { skipEvents: true, skipNotifications: true },\n organizationScope: {\n selectedId: organizationId,\n filterIds: [organizationId],\n allowedIds: [organizationId],\n tenantId,\n },\n selectedOrganizationId: organizationId,\n organizationIds: [organizationId],\n }\n}\n\nfunction organizationClause(organizationIds: string[] | null): { sql: string; params: unknown[] } {\n if (!organizationIds) return { sql: '', params: [] }\n const clause = buildSqlInClause('organization_id', organizationIds)\n return { sql: ` AND ${clause.sql}`, params: clause.params }\n}\n\nexport async function countReapplyRoundingCandidates(\n em: EntityManager,\n scope: ReapplyRoundingScope,\n): Promise<number> {\n const org = organizationClause(scope.organizationIds)\n const rows = (await em.getConnection().execute(\n `\n SELECT COUNT(*)::bigint AS candidate_count\n FROM staff_time_entries\n WHERE tenant_id = ?\n AND deleted_at IS NULL\n AND locked_report_id IS NULL${org.sql}\n `,\n [scope.tenantId, ...org.params],\n )) as CountRow[]\n const parsed = Number(rows?.[0]?.candidate_count ?? 0)\n return Number.isFinite(parsed) ? parsed : 0\n}\n\nasync function loadCandidatePage(\n em: EntityManager,\n scope: ReapplyRoundingScope,\n cursor: string | null,\n limit: number,\n): Promise<CandidateRow[]> {\n const org = organizationClause(scope.organizationIds)\n const cursorClause = cursor ? ' AND id > ?' : ''\n const cursorParams = cursor ? [cursor] : []\n return (await em.getConnection().execute(\n `\n SELECT id, organization_id\n FROM staff_time_entries\n WHERE tenant_id = ?\n AND deleted_at IS NULL\n AND locked_report_id IS NULL${org.sql}${cursorClause}\n ORDER BY id ASC\n LIMIT ?\n `,\n [scope.tenantId, ...org.params, ...cursorParams, limit],\n )) as CandidateRow[]\n}\n\nfunction groupByOrganization(rows: readonly CandidateRow[]): Map<string, string[]> {\n const grouped = new Map<string, string[]>()\n for (const row of rows) {\n const bucket = grouped.get(row.organization_id) ?? []\n bucket.push(row.id)\n grouped.set(row.organization_id, bucket)\n }\n return grouped\n}\n\n/**\n * The restatement itself, with the `ProgressJob` lifecycle lifted out (EP-51).\n *\n * `reapplyRoundingWithProgress` below owns start/complete/cancel and is unchanged\n * for its callers; the recalculation runner owns the same lifecycle when several\n * hooks share one job. Both drive this function, so there is exactly one copy of\n * the candidate query, the batching and the locked-entry exclusion.\n */\nexport async function reapplyRoundingBatches(params: {\n container: AwilixContainer\n scope: ReapplyRoundingScope\n report: {\n setTotal(total: number): Promise<void>\n advance(processed: number): Promise<void>\n isCancellationRequested(): Promise<boolean>\n }\n}): Promise<ReapplyRoundingSummary> {\n const { container, scope, report } = params\n const em = (container.resolve('em') as EntityManager).fork()\n const commandBus = container.resolve('commandBus') as CommandBus\n\n const totalCount = await countReapplyRoundingCandidates(em, scope)\n const summary: ReapplyRoundingSummary = {\n totalCount,\n processedCount: 0,\n updatedCount: 0,\n unchangedCount: 0,\n skippedCount: 0,\n cancelled: false,\n }\n\n if (totalCount === 0) return summary\n\n await report.setTotal(totalCount)\n\n let cursor: string | null = null\n for (;;) {\n const page: CandidateRow[] = await loadCandidatePage(em, scope, cursor, REAPPLY_ROUNDING_BATCH_SIZE)\n if (page.length === 0) break\n cursor = page[page.length - 1].id\n\n let pageProcessed = 0\n for (const [organizationId, entryIds] of groupByOrganization(page)) {\n const executed = await commandBus.execute<\n { tenantId: string; organizationId: string; entryIds: string[] },\n StaffTimeEntryReapplyRoundingResult\n >(staffTimeEntryCommandIds.reapplyRounding, {\n input: { tenantId: scope.tenantId, organizationId, entryIds },\n ctx: buildCommandContext(container, scope.tenantId, organizationId),\n })\n const result = executed?.result\n summary.updatedCount += result?.updatedCount ?? 0\n summary.unchangedCount += result?.unchangedCount ?? 0\n summary.skippedCount += result?.skippedCount ?? 0\n summary.processedCount += entryIds.length\n pageProcessed += entryIds.length\n }\n\n await report.advance(pageProcessed)\n\n if (await report.isCancellationRequested()) {\n summary.cancelled = true\n return summary\n }\n }\n\n return summary\n}\n\nexport async function reapplyRoundingWithProgress(params: {\n container: AwilixContainer\n progressJobId: string\n scope: ReapplyRoundingScope\n}): Promise<ReapplyRoundingSummary> {\n const { container, progressJobId, scope } = params\n const progressService = container.resolve('progressService') as ProgressService\n const progressContext: ProgressServiceContext = {\n tenantId: scope.tenantId,\n organizationId: scope.organizationIds?.length === 1 ? scope.organizationIds[0] : null,\n userId: scope.userId ?? null,\n }\n\n await progressService.startJob(progressJobId, progressContext)\n\n let totalCount = 0\n let processedCount = 0\n const summary = await reapplyRoundingBatches({\n container,\n scope,\n report: {\n setTotal: async (total) => {\n totalCount = total\n await progressService.updateProgress(progressJobId, { totalCount, processedCount: 0 }, progressContext)\n },\n advance: async (processed) => {\n processedCount += processed\n await progressService.updateProgress(\n progressJobId,\n { totalCount, processedCount: Math.min(processedCount, totalCount) },\n progressContext,\n )\n },\n isCancellationRequested: () =>\n progressService.isCancellationRequested(progressJobId, scope.tenantId, progressContext.organizationId),\n },\n })\n\n if (summary.cancelled) {\n logger.info('staff.timesheets reapply-rounding cancelled', { progressJobId, ...summary })\n await progressService.markCancelled(progressJobId, progressContext)\n return summary\n }\n\n await progressService.completeJob(progressJobId, { resultSummary: { ...summary } }, progressContext)\n if (summary.totalCount > 0) {\n logger.info('staff.timesheets reapply-rounding finished', { progressJobId, ...summary })\n }\n return summary\n}\n\n/**\n * EP-51. The retro-rounding pass registers itself as the built-in recalculation,\n * so `registerTimeTrackingRecalculation` has a working reference implementation\n * and the worker has one code path for the shipped hook and a contributed one.\n * `BUILT_IN_STRATEGY_PRIORITY` keeps it last in the registry's order, the same as\n * every other built-in in this module.\n */\nregisterTimeTrackingRecalculation({\n id: BUILT_IN_RECALCULATION_ID,\n labelKey: 'staff.time_tracking.settings.retro.jobName',\n priority: BUILT_IN_STRATEGY_PRIORITY,\n run: ({ container, scope, report }) => reapplyRoundingBatches({ container, scope, report }),\n})\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }