@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
@@ -2,18 +2,62 @@ import type { EntityManager } from '@mikro-orm/postgresql'
2
2
  import type { AwilixContainer } from 'awilix'
3
3
  import type { ResponseEnricher, EnricherContext } from '@open-mercato/shared/lib/crud/response-enricher'
4
4
  import type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
5
- import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
6
- import { StaffTeamMember } from './entities'
5
+ import { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
6
+ import { CustomerEntity } from '@open-mercato/core/modules/customers/data/entities'
7
+ import {
8
+ StaffTeamMember,
9
+ StaffTimeProject,
10
+ StaffTimeReport,
11
+ StaffTimeReportEntry,
12
+ StaffTimeReportEvent,
13
+ StaffTimeTask,
14
+ StaffTimeTaskStatus,
15
+ } from './entities'
16
+ import { decorateTimeEntryRows } from '../lib/timesheets/timeEntryDecoration'
7
17
  import { computeProjectHoursTrend } from '../lib/timesheets-projects/computeProjectHoursTrend'
18
+ import { computeProjectFinancials } from '../lib/timesheets-projects/computeProjectFinancials'
19
+ import type { ProjectBudgetKind } from '../lib/timesheets-projects/budgetBurn'
8
20
  import {
9
21
  listProjectMembersPreview,
10
22
  type MemberPreview,
11
23
  } from '../lib/timesheets-projects/listProjectMembersPreview'
24
+ import { computeTaskRollups, EMPTY_TASK_ROLLUP } from '../lib/timesheets-tasks/computeTaskRollups'
12
25
 
13
26
  const MANAGE_FEATURE = 'staff.timesheets.projects.manage'
27
+ const RATES_FEATURE = 'staff.timesheets.rates.view'
28
+
29
+ /**
30
+ * The CRUD factory looks an enricher up by the `enrichers.entityId` its route
31
+ * declares — the colon form, `staff:staff_time_entry`. The query engine looks the
32
+ * same enricher up by the query entity id with `:` replaced by `.`
33
+ * (`entityIdToEventEntity`, `query-extension-runner.ts`), so an enricher that only
34
+ * declares the colon form never participates in a query-engine pipeline no matter
35
+ * what its `queryEngine` config says.
36
+ *
37
+ * `withQueryEngineSurface` publishes a second registry entry for the same enricher
38
+ * under the dot form, carrying the query-engine opt-in. The two entries never both
39
+ * match one lookup — the API surface asks for the colon form and the query-engine
40
+ * surface asks for the dot form — so nothing runs twice.
41
+ */
42
+ function withQueryEngineSurface<TRecord, TEnriched>(
43
+ enricher: ResponseEnricher<TRecord, TEnriched>,
44
+ ): ResponseEnricher<TRecord, TEnriched> {
45
+ return {
46
+ ...enricher,
47
+ id: `${enricher.id}.query-engine`,
48
+ targetEntity: enricher.targetEntity.replace(/:/g, '.'),
49
+ queryEngine: { enabled: true },
50
+ }
51
+ }
14
52
 
15
53
  type EntityRecord = Record<string, unknown> & { id: string }
16
54
 
55
+ type ProjectBudget = {
56
+ kind: ProjectBudgetKind
57
+ value: number | null
58
+ warnAtPercent: number | null
59
+ }
60
+
17
61
  type StaffEnrichment = {
18
62
  _staff: {
19
63
  hoursWeek: number
@@ -21,6 +65,12 @@ type StaffEnrichment = {
21
65
  myRole: string | null
22
66
  members?: MemberPreview[]
23
67
  memberCount?: number
68
+ customerName?: string | null
69
+ totalMinutes?: number
70
+ billableMinutes?: number
71
+ budget?: ProjectBudget
72
+ hourlyRate?: number | null
73
+ cost?: number | null
24
74
  }
25
75
  }
26
76
 
@@ -37,11 +87,10 @@ type InternalContext = EnricherContext & {
37
87
  container: AwilixContainer
38
88
  }
39
89
 
40
- async function callerHasManage(ctx: InternalContext): Promise<boolean> {
41
- if (ctx.userFeatures?.includes(MANAGE_FEATURE)) return true
90
+ async function callerHasFeature(ctx: InternalContext, feature: string): Promise<boolean> {
42
91
  try {
43
92
  const rbac = ctx.container.resolve('rbacService') as RbacService
44
- return await rbac.userHasAllFeatures(ctx.userId, [MANAGE_FEATURE], {
93
+ return await rbac.userHasAllFeatures(ctx.userId, [feature], {
45
94
  tenantId: ctx.tenantId,
46
95
  organizationId: ctx.organizationId,
47
96
  })
@@ -50,6 +99,55 @@ async function callerHasManage(ctx: InternalContext): Promise<boolean> {
50
99
  }
51
100
  }
52
101
 
102
+ function toNullableNumber(value: string | number | null | undefined): number | null {
103
+ if (value === null || value === undefined) return null
104
+ const parsed = typeof value === 'number' ? value : Number(value)
105
+ return Number.isFinite(parsed) ? parsed : null
106
+ }
107
+
108
+ function resolveCustomerName(snapshot: Record<string, unknown> | null | undefined): string | null {
109
+ if (!snapshot || typeof snapshot !== 'object') return null
110
+ for (const key of ['displayName', 'name', 'companyName', 'label', 'email']) {
111
+ const value = snapshot[key]
112
+ if (typeof value === 'string' && value.trim().length > 0) return value.trim()
113
+ }
114
+ return null
115
+ }
116
+
117
+ /**
118
+ * Second line of defence behind the write-side denormalization: a row written
119
+ * before the commands derived the snapshot — or by any path that bypassed them —
120
+ * still carries the FK, so the name is read live for exactly those projects,
121
+ * in one scoped query for the page rather than one per row. A customers module
122
+ * that is absent degrades to no name, never to a failed list.
123
+ */
124
+ async function loadCustomerNames(
125
+ em: EntityManager,
126
+ tenantId: string,
127
+ organizationId: string,
128
+ customerIds: readonly string[],
129
+ ): Promise<Map<string, string>> {
130
+ const names = new Map<string, string>()
131
+ const ids = [...new Set(customerIds)]
132
+ if (ids.length === 0) return names
133
+ try {
134
+ const customers = await findWithDecryption(
135
+ em,
136
+ CustomerEntity,
137
+ { id: { $in: ids }, tenantId, organizationId, deletedAt: null },
138
+ undefined,
139
+ { tenantId, organizationId },
140
+ )
141
+ for (const customer of customers) {
142
+ const name = customer.displayName?.trim() || customer.primaryEmail?.trim()
143
+ if (name) names.set(customer.id, name)
144
+ }
145
+ } catch {
146
+ return names
147
+ }
148
+ return names
149
+ }
150
+
53
151
  async function resolveCallerStaffMemberId(ctx: InternalContext): Promise<string | null> {
54
152
  const member = await findOneWithDecryption(
55
153
  ctx.em.fork(),
@@ -87,18 +185,36 @@ const portfolioEnricher: ResponseEnricher<EntityRecord, StaffEnrichment> = {
87
185
  const ctx = context as InternalContext
88
186
  const projectIds = records.map((r) => r.id)
89
187
 
90
- const [callerStaffMemberId, hasManage] = await Promise.all([
188
+ const [callerStaffMemberId, hasManage, hasRates] = await Promise.all([
91
189
  resolveCallerStaffMemberId(ctx),
92
- callerHasManage(ctx),
190
+ callerHasFeature(ctx, MANAGE_FEATURE),
191
+ callerHasFeature(ctx, RATES_FEATURE),
93
192
  ])
193
+ const ownEntriesOnly = hasManage ? null : callerStaffMemberId
194
+
195
+ const projects = await ctx.em.fork().find(StaffTimeProject, {
196
+ id: { $in: projectIds },
197
+ tenantId: ctx.tenantId,
198
+ organizationId: ctx.organizationId,
199
+ deletedAt: null,
200
+ })
201
+ const projectById = new Map(projects.map((project) => [project.id, project]))
202
+ const hourlyRateByProjectId = new Map(
203
+ projects.map((project) => [project.id, toNullableNumber(project.hourlyRate)]),
204
+ )
205
+
206
+ const customerIdsMissingSnapshot = projects
207
+ .filter((project) => !resolveCustomerName(project.customerSnapshot))
208
+ .map((project) => project.customerId)
209
+ .filter((customerId): customerId is string => typeof customerId === 'string' && customerId.length > 0)
94
210
 
95
- const [trendMap, membersMap] = await Promise.all([
211
+ const [trendMap, membersMap, financialsMap, customerNameById] = await Promise.all([
96
212
  computeProjectHoursTrend({
97
213
  em: ctx.em,
98
214
  tenantId: ctx.tenantId,
99
215
  organizationId: ctx.organizationId,
100
216
  projectIds,
101
- staffMemberId: hasManage ? null : callerStaffMemberId,
217
+ staffMemberId: ownEntriesOnly,
102
218
  }),
103
219
  listProjectMembersPreview({
104
220
  em: ctx.em,
@@ -107,6 +223,15 @@ const portfolioEnricher: ResponseEnricher<EntityRecord, StaffEnrichment> = {
107
223
  projectIds,
108
224
  callerStaffMemberId,
109
225
  }),
226
+ computeProjectFinancials({
227
+ em: ctx.em.fork(),
228
+ tenantId: ctx.tenantId,
229
+ organizationId: ctx.organizationId,
230
+ projectIds,
231
+ hourlyRateByProjectId,
232
+ staffMemberId: ownEntriesOnly,
233
+ }),
234
+ loadCustomerNames(ctx.em.fork(), ctx.tenantId, ctx.organizationId, customerIdsMissingSnapshot),
110
235
  ])
111
236
 
112
237
  return records.map((record) => {
@@ -115,20 +240,560 @@ const portfolioEnricher: ResponseEnricher<EntityRecord, StaffEnrichment> = {
115
240
  hoursTrend: [0, 0, 0, 0, 0, 0, 0],
116
241
  }
117
242
  const members = membersMap.get(record.id)
243
+ const financials = financialsMap.get(record.id)
244
+ const project = projectById.get(record.id)
118
245
  const enrichment: StaffEnrichment['_staff'] = {
119
246
  hoursWeek: trend.hoursWeek,
120
247
  hoursTrend: trend.hoursTrend,
121
248
  myRole: members?.myRole ?? null,
249
+ customerName:
250
+ resolveCustomerName(project?.customerSnapshot)
251
+ ?? (project?.customerId ? customerNameById.get(project.customerId) ?? null : null),
252
+ totalMinutes: financials?.totalMinutes ?? 0,
253
+ billableMinutes: financials?.billableMinutes ?? 0,
254
+ budget: {
255
+ kind: project?.budgetKind ?? 'none',
256
+ value: toNullableNumber(project?.budgetValue),
257
+ warnAtPercent: project?.budgetWarnAtPercent ?? null,
258
+ },
122
259
  }
123
260
  if (hasManage && members) {
124
261
  enrichment.members = members.preview
125
262
  enrichment.memberCount = members.total
126
263
  }
264
+ if (hasRates) {
265
+ enrichment.hourlyRate = toNullableNumber(project?.hourlyRate)
266
+ enrichment.cost = financials?.cost ?? null
267
+ }
127
268
  return { ...record, _staff: enrichment }
128
269
  })
129
270
  },
130
271
  }
131
272
 
132
- export const enrichers: ResponseEnricher[] = [portfolioEnricher]
273
+ /**
274
+ * `ownMinutes` is this task's own entries; `loggedMinutes` is the inclusive rollup
275
+ * (own + every child's), which is what every hours display shows (D-2).
276
+ *
277
+ * The two names are the guard against risk R10: a caller that sums parent and child
278
+ * `loggedMinutes` double-counts, because the parent's figure already contains the
279
+ * child's. `ownMinutes` is the field to sum when a total spans both levels.
280
+ *
281
+ * These land at the top level rather than under the usual `_staff` namespace.
282
+ * The namespace exists so one module's enrichment cannot collide with another's
283
+ * fields — here staff enriches its own entity, and the spec fixes these exact names as
284
+ * the task contract. Publishing the same number twice, under two paths, is precisely
285
+ * the ambiguity D-2 removes.
286
+ */
287
+ type TaskRollupEnrichment = {
288
+ ownMinutes: number
289
+ loggedMinutes: number
290
+ childCount: number
291
+ /**
292
+ * How many of those children sit in a done column (D-2). Published beside
293
+ * `childCount` so `3/5` is readable from the row itself — deriving it costs a
294
+ * page of child rows per opened task, for a number the same aggregate knows.
295
+ */
296
+ doneChildCount: number
297
+ }
298
+
299
+ const taskRollupEnricher: ResponseEnricher<EntityRecord, TaskRollupEnrichment> = {
300
+ id: 'staff.timesheets-tasks-rollup',
301
+ targetEntity: 'staff:staff_time_task',
302
+ // ACL is enforced by the route (`requireFeatures: ['staff.timesheets.tasks.view']`),
303
+ // which also intersects the page with the caller's project access; the aggregate
304
+ // narrows entries to the projects of that already-filtered page.
305
+ priority: 10,
306
+ timeout: 3000,
307
+ critical: false,
308
+ fallback: { ...EMPTY_TASK_ROLLUP },
309
+ // Minutes come from `staff_time_entries`, a table the task list cache never
310
+ // invalidates on, so the rollup must be recomputed on every hit.
311
+ cacheableOnListHit: false,
312
+
313
+ async enrichOne(record, context) {
314
+ const enriched = await this.enrichMany!([record], context)
315
+ return enriched[0]
316
+ },
317
+
318
+ async enrichMany(records, context) {
319
+ if (records.length === 0) return records as (EntityRecord & TaskRollupEnrichment)[]
320
+
321
+ const ctx = context as InternalContext
322
+ const rollups = await computeTaskRollups({
323
+ em: ctx.em.fork(),
324
+ tenantId: ctx.tenantId,
325
+ organizationId: ctx.organizationId,
326
+ taskIds: records.map((record) => record.id).filter((id): id is string => typeof id === 'string'),
327
+ })
328
+
329
+ return records.map((record) => ({ ...record, ...(rollups.get(record.id) ?? EMPTY_TASK_ROLLUP) }))
330
+ },
331
+ }
332
+
333
+ /**
334
+ * The tags a task carries, as ids and as renderable chips.
335
+ *
336
+ * `tagIds` is the contract the board and the drawer read: the board resolves the
337
+ * labels once per page through `/api/staff/timesheets/tags?ids=`, so it needs
338
+ * nothing but the ids, while a surface with no such lookup can render `tags`
339
+ * directly. Both come from the same row, so publishing the second costs nothing.
340
+ *
341
+ * Without this, a tag assigned to a task is invisible to every reader — the
342
+ * assignment route only writes — so the drawer could add a tag it could never
343
+ * show or remove.
344
+ */
345
+ type TaskTagSummary = {
346
+ id: string
347
+ slug: string
348
+ label: string
349
+ color: string | null
350
+ }
351
+
352
+ type TaskTagEnrichment = {
353
+ tagIds: string[]
354
+ tags: TaskTagSummary[]
355
+ }
356
+
357
+ const EMPTY_TASK_TAGS: TaskTagEnrichment = { tagIds: [], tags: [] }
358
+
359
+ type TaskTagRow = {
360
+ task_id: string
361
+ tag_id: string
362
+ slug: string | null
363
+ label: string | null
364
+ color: string | null
365
+ }
366
+
367
+ /**
368
+ * One join per page, never one per card: the assignment table is filtered by the
369
+ * page's already-access-filtered task ids, and the tag row travels with it rather
370
+ * than costing a second lookup.
371
+ */
372
+ async function loadTaskTags(
373
+ em: EntityManager,
374
+ taskIds: readonly string[],
375
+ tenantId: string,
376
+ organizationId: string,
377
+ ): Promise<Map<string, TaskTagEnrichment>> {
378
+ const byTaskId = new Map<string, TaskTagEnrichment>()
379
+ const ids = [...new Set(taskIds)].filter((id) => typeof id === 'string' && id.length > 0)
380
+ if (ids.length === 0) return byTaskId
381
+
382
+ const placeholders = ids.map(() => '?').join(', ')
383
+ const sql = `
384
+ SELECT
385
+ tt.task_id,
386
+ tt.tag_id,
387
+ tg.slug,
388
+ tg.label,
389
+ tg.color
390
+ FROM staff_time_task_tags tt
391
+ JOIN staff_time_tags tg
392
+ ON tg.id = tt.tag_id
393
+ AND tg.tenant_id = tt.tenant_id
394
+ AND tg.organization_id = tt.organization_id
395
+ AND tg.deleted_at IS NULL
396
+ WHERE tt.tenant_id = ?
397
+ AND tt.organization_id = ?
398
+ AND tt.task_id IN (${placeholders})
399
+ ORDER BY tg.label ASC, tg.id ASC
400
+ `
401
+
402
+ const rows = (await em.getConnection().execute(sql, [tenantId, organizationId, ...ids])) as TaskTagRow[]
403
+ for (const row of rows) {
404
+ if (typeof row.task_id !== 'string' || typeof row.tag_id !== 'string') continue
405
+ const bucket = byTaskId.get(row.task_id) ?? { tagIds: [], tags: [] }
406
+ if (bucket.tagIds.includes(row.tag_id)) continue
407
+ bucket.tagIds.push(row.tag_id)
408
+ bucket.tags.push({
409
+ id: row.tag_id,
410
+ slug: row.slug ?? '',
411
+ label: row.label ?? row.tag_id,
412
+ color: row.color ?? null,
413
+ })
414
+ byTaskId.set(row.task_id, bucket)
415
+ }
416
+ return byTaskId
417
+ }
418
+
419
+ const taskTagEnricher: ResponseEnricher<EntityRecord, TaskTagEnrichment> = {
420
+ id: 'staff.timesheets-tasks-tags',
421
+ targetEntity: 'staff:staff_time_task',
422
+ // ACL is enforced by the route (`requireFeatures: ['staff.timesheets.tasks.view']`),
423
+ // which also intersects the page with `resolveProjectAccess`; the join is keyed by
424
+ // that already-filtered page, so a tag can only be read through a task the caller
425
+ // may already see.
426
+ priority: 10,
427
+ timeout: 3000,
428
+ critical: false,
429
+ fallback: { ...EMPTY_TASK_TAGS },
430
+ // Assignments live in `staff_time_task_tags`, a table the task list cache never
431
+ // invalidates on, so the chips must be re-read on every hit.
432
+ cacheableOnListHit: false,
433
+
434
+ async enrichOne(record, context) {
435
+ const enriched = await this.enrichMany!([record], context)
436
+ return enriched[0]
437
+ },
438
+
439
+ async enrichMany(records, context) {
440
+ if (records.length === 0) return records as (EntityRecord & TaskTagEnrichment)[]
441
+
442
+ const ctx = context as InternalContext
443
+ if (!ctx.tenantId || !ctx.organizationId) {
444
+ return records.map((record) => ({ ...record, tagIds: [], tags: [] }))
445
+ }
446
+
447
+ const tagsByTaskId = await loadTaskTags(
448
+ ctx.em.fork(),
449
+ records.map((record) => record.id).filter((id): id is string => typeof id === 'string'),
450
+ ctx.tenantId,
451
+ ctx.organizationId,
452
+ )
453
+
454
+ return records.map((record) => {
455
+ const assigned = tagsByTaskId.get(record.id)
456
+ return { ...record, tagIds: assigned?.tagIds ?? [], tags: assigned?.tags ?? [] }
457
+ })
458
+ },
459
+ }
460
+
461
+ /**
462
+ * The consulting-suite fields on a time-entry row: the `description` alias, the
463
+ * rounded minutes, the lock state, the assigned tags, and — only for a caller
464
+ * holding `staff.timesheets.rates.view` — `cost`, `currencyCode` and the stored
465
+ * rate override, which the route's projection deliberately does not select.
466
+ *
467
+ * This was a route-private `hooks.afterList` until EP-14. As a declared enricher a
468
+ * third-party enricher for the same entity composes with it and can read what it
469
+ * added, and the CRUD list cache stores the pre-enrichment rows instead of one
470
+ * caller's decorated copy.
471
+ *
472
+ * **`critical: false` with no `fallback`, and the money gate does not depend on
473
+ * either.** A non-critical enricher that throws or times out leaves the items as
474
+ * the route produced them, which is a page of entries without the decoration —
475
+ * degraded, but never a page of entries WITH money the caller may not see, because
476
+ * every money key on the row is one this enricher put there. A `fallback` is
477
+ * deliberately absent: it merges one fixed object into every row, and there is no
478
+ * value of `description`, `roundedMinutes` or `tags` that is correct for all of
479
+ * them. Omitting the decoration is honest; inventing it is not.
480
+ */
481
+ type TimeEntryEnrichment = Record<string, unknown>
482
+
483
+ const timeEntryEnricher: ResponseEnricher<EntityRecord, TimeEntryEnrichment> = {
484
+ id: 'staff.timesheets-time-entries',
485
+ targetEntity: 'staff:staff_time_entry',
486
+ // ACL is enforced by the route (`requireFeatures: ['staff.timesheets.view']`),
487
+ // which also intersects the page with `resolveProjectAccess`. The money fields
488
+ // below are gated per caller on `staff.timesheets.rates.view`.
489
+ priority: 10,
490
+ timeout: 3000,
491
+ critical: false,
492
+ // Tags live in `staff_time_entry_tags` and rates in `staff_time_projects` —
493
+ // neither invalidates the entry list cache, and the money keys are per-caller,
494
+ // so the decoration must be recomputed on every hit.
495
+ cacheableOnListHit: false,
496
+
497
+ async enrichOne(record, context) {
498
+ const enriched = await this.enrichMany!([record], context)
499
+ return enriched[0]
500
+ },
501
+
502
+ async enrichMany(records, context) {
503
+ if (records.length === 0) return records as (EntityRecord & TimeEntryEnrichment)[]
504
+
505
+ const ctx = context as InternalContext
506
+ // Copies, so the enricher stays additive with respect to its input while the
507
+ // shared decoration keeps writing in place.
508
+ const rows = records.map((record) => ({ ...record }))
509
+ await decorateTimeEntryRows(rows, {
510
+ em: ctx.em,
511
+ tenantId: ctx.tenantId,
512
+ organizationId: ctx.organizationId,
513
+ canSeeRates: await callerHasFeature(ctx, RATES_FEATURE),
514
+ })
515
+ return rows as (EntityRecord & TimeEntryEnrichment)[]
516
+ },
517
+ }
518
+
519
+ /**
520
+ * The context a task row needs to render outside its own board: the project it
521
+ * belongs to, the column it sits in and who it is assigned to. Without it every
522
+ * task table pays one lookup per row for three names the same page already knows.
523
+ *
524
+ * `hourlyRate` / `currencyCode` are the PROJECT's rate, which is money, so they
525
+ * exist only for a caller holding `staff.timesheets.rates.view`.
526
+ */
527
+ type TaskContextEnrichment = {
528
+ _staff: {
529
+ projectName: string | null
530
+ projectCode: string | null
531
+ projectColor: string | null
532
+ statusName: string | null
533
+ statusIsDone: boolean | null
534
+ assigneeName: string | null
535
+ hourlyRate?: number | null
536
+ currencyCode?: string | null
537
+ }
538
+ }
539
+
540
+ const EMPTY_TASK_CONTEXT: TaskContextEnrichment = {
541
+ _staff: {
542
+ projectName: null,
543
+ projectCode: null,
544
+ projectColor: null,
545
+ statusName: null,
546
+ statusIsDone: null,
547
+ assigneeName: null,
548
+ },
549
+ }
550
+
551
+ const taskContextEnricher: ResponseEnricher<EntityRecord, TaskContextEnrichment> = {
552
+ id: 'staff.timesheets-tasks-context',
553
+ targetEntity: 'staff:staff_time_task',
554
+ // ACL is enforced by the route (`requireFeatures: ['staff.timesheets.tasks.view']`),
555
+ // which intersects the page with `resolveProjectAccess`; every lookup below is keyed
556
+ // by that already-filtered page and re-scoped to the caller tenant and organization.
557
+ priority: 5,
558
+ timeout: 3000,
559
+ critical: false,
560
+ fallback: { ...EMPTY_TASK_CONTEXT },
561
+ cacheableOnListHit: false,
562
+
563
+ async enrichOne(record, context) {
564
+ const enriched = await this.enrichMany!([record], context)
565
+ return enriched[0]
566
+ },
567
+
568
+ async enrichMany(records, context) {
569
+ if (records.length === 0) return records as (EntityRecord & TaskContextEnrichment)[]
570
+
571
+ const ctx = context as InternalContext
572
+ if (!ctx.tenantId || !ctx.organizationId) {
573
+ return records.map((record) => ({ ...record, ...EMPTY_TASK_CONTEXT }))
574
+ }
575
+
576
+ const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
577
+ const taskIds = records.map((record) => record.id).filter((id): id is string => typeof id === 'string')
578
+ const tasks = await ctx.em.fork().find(StaffTimeTask, { id: { $in: taskIds }, ...scope, deletedAt: null })
579
+ const taskById = new Map(tasks.map((task) => [task.id, task]))
580
+
581
+ const projectIds = [...new Set(tasks.map((task) => task.timeProjectId).filter(Boolean))]
582
+ const statusIds = [...new Set(tasks.map((task) => task.taskStatusId).filter(Boolean))]
583
+ const assigneeIds = [
584
+ ...new Set(
585
+ tasks
586
+ .map((task) => task.assigneeStaffMemberId)
587
+ .filter((id): id is string => typeof id === 'string' && id.length > 0),
588
+ ),
589
+ ]
590
+
591
+ const hasRates = await callerHasFeature(ctx, RATES_FEATURE)
592
+
593
+ const [projects, statuses, assignees] = await Promise.all([
594
+ projectIds.length
595
+ ? ctx.em.fork().find(StaffTimeProject, { id: { $in: projectIds }, ...scope, deletedAt: null })
596
+ : Promise.resolve([]),
597
+ statusIds.length
598
+ ? ctx.em.fork().find(StaffTimeTaskStatus, { id: { $in: statusIds }, ...scope, deletedAt: null })
599
+ : Promise.resolve([]),
600
+ assigneeIds.length
601
+ ? findWithDecryption(
602
+ ctx.em.fork(),
603
+ StaffTeamMember,
604
+ { id: { $in: assigneeIds }, ...scope, deletedAt: null },
605
+ undefined,
606
+ scope,
607
+ )
608
+ : Promise.resolve([]),
609
+ ])
610
+
611
+ const projectById = new Map(projects.map((project) => [project.id, project]))
612
+ const statusById = new Map(statuses.map((status) => [status.id, status]))
613
+ const assigneeById = new Map(assignees.map((member) => [member.id, member]))
614
+
615
+ return records.map((record) => {
616
+ const task = taskById.get(record.id)
617
+ if (!task) return { ...record, ...EMPTY_TASK_CONTEXT }
618
+ const project = projectById.get(task.timeProjectId)
619
+ const status = statusById.get(task.taskStatusId)
620
+ const assignee = task.assigneeStaffMemberId ? assigneeById.get(task.assigneeStaffMemberId) : undefined
621
+ const enrichment: TaskContextEnrichment['_staff'] = {
622
+ projectName: project?.name ?? null,
623
+ projectCode: project?.code ?? null,
624
+ projectColor: project?.color ?? null,
625
+ statusName: status?.name ?? null,
626
+ statusIsDone: status ? status.isDone : null,
627
+ assigneeName: assignee?.displayName ?? null,
628
+ }
629
+ if (hasRates) {
630
+ enrichment.hourlyRate = toNullableNumber(project?.hourlyRate)
631
+ enrichment.currencyCode = project?.currencyCode ?? null
632
+ }
633
+ return { ...record, _staff: enrichment }
634
+ })
635
+ },
636
+ }
637
+
638
+ /**
639
+ * What a report row cannot say about itself: whether it is frozen, how many entries
640
+ * the freeze holds, and whether anyone has taken a file out of it.
641
+ *
642
+ * `lockedEntryCount` counts the frozen `staff_time_report_entries` rows, which is the
643
+ * number a close actually wrote — the report's own columns carry only minute and
644
+ * amount totals. The export history comes from the append-only
645
+ * `staff_time_report_events` audit (screen 14 note 5: an export never locks), so
646
+ * "last exported" is answerable without opening the report.
647
+ *
648
+ * `totalAmount` is money and exists only for a caller holding
649
+ * `staff.timesheets.rates.view`; the minute totals are not money and stay for everyone.
650
+ */
651
+ type ReportEnrichment = {
652
+ _staff: {
653
+ isClosed: boolean
654
+ closedAt: string | null
655
+ billableMinutes: number
656
+ nonbillableMinutes: number
657
+ totalMinutes: number
658
+ lockedEntryCount: number
659
+ exportCount: number
660
+ lastExportedAt: string | null
661
+ lastExportFormat: string | null
662
+ totalAmount?: number | null
663
+ currencyCode?: string | null
664
+ }
665
+ }
666
+
667
+ const EMPTY_REPORT_ENRICHMENT: ReportEnrichment = {
668
+ _staff: {
669
+ isClosed: false,
670
+ closedAt: null,
671
+ billableMinutes: 0,
672
+ nonbillableMinutes: 0,
673
+ totalMinutes: 0,
674
+ lockedEntryCount: 0,
675
+ exportCount: 0,
676
+ lastExportedAt: null,
677
+ lastExportFormat: null,
678
+ },
679
+ }
680
+
681
+ type ReportExportSummary = { count: number; lastAt: Date | null; lastFormat: string | null }
682
+
683
+ function readExportFormat(metadata: Record<string, unknown> | null | undefined): string | null {
684
+ if (!metadata || typeof metadata !== 'object') return null
685
+ const format = metadata.format
686
+ return typeof format === 'string' && format.length > 0 ? format : null
687
+ }
688
+
689
+ const reportEnricher: ResponseEnricher<EntityRecord, ReportEnrichment> = {
690
+ id: 'staff.timesheets-reports',
691
+ targetEntity: 'staff:staff_time_report',
692
+ // ACL is enforced by the route (`requireFeatures: ['staff.timesheets.reports.view']`);
693
+ // `totalAmount` is gated per caller on `staff.timesheets.rates.view` below.
694
+ priority: 10,
695
+ timeout: 3000,
696
+ critical: false,
697
+ fallback: { ...EMPTY_REPORT_ENRICHMENT },
698
+ // The freeze and export counts live in tables the report list cache never
699
+ // invalidates on, so they are re-read on every hit.
700
+ cacheableOnListHit: false,
701
+
702
+ async enrichOne(record, context) {
703
+ const enriched = await this.enrichMany!([record], context)
704
+ return enriched[0]
705
+ },
706
+
707
+ async enrichMany(records, context) {
708
+ if (records.length === 0) return records as (EntityRecord & ReportEnrichment)[]
709
+
710
+ const ctx = context as InternalContext
711
+ if (!ctx.tenantId || !ctx.organizationId) {
712
+ return records.map((record) => ({ ...record, ...EMPTY_REPORT_ENRICHMENT }))
713
+ }
714
+
715
+ const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
716
+ const reportIds = records.map((record) => record.id).filter((id): id is string => typeof id === 'string')
717
+ if (reportIds.length === 0) {
718
+ return records.map((record) => ({ ...record, ...EMPTY_REPORT_ENRICHMENT }))
719
+ }
720
+
721
+ const [reports, frozenEntries, exportEvents, hasRates] = await Promise.all([
722
+ ctx.em.fork().find(StaffTimeReport, { id: { $in: reportIds }, ...scope, deletedAt: null }),
723
+ ctx.em.fork().find(
724
+ StaffTimeReportEntry,
725
+ { reportId: { $in: reportIds }, ...scope },
726
+ { fields: ['reportId'] },
727
+ ),
728
+ ctx.em.fork().find(
729
+ StaffTimeReportEvent,
730
+ { reportId: { $in: reportIds }, eventType: 'exported', ...scope },
731
+ { orderBy: { createdAt: 'desc' } },
732
+ ),
733
+ callerHasFeature(ctx, RATES_FEATURE),
734
+ ])
735
+
736
+ const reportById = new Map(reports.map((report) => [report.id, report]))
737
+ const frozenCountByReportId = new Map<string, number>()
738
+ for (const entry of frozenEntries) {
739
+ frozenCountByReportId.set(entry.reportId, (frozenCountByReportId.get(entry.reportId) ?? 0) + 1)
740
+ }
741
+ const exportsByReportId = new Map<string, ReportExportSummary>()
742
+ for (const event of exportEvents) {
743
+ const summary = exportsByReportId.get(event.reportId) ?? { count: 0, lastAt: null, lastFormat: null }
744
+ summary.count += 1
745
+ // The rows arrive newest first, so the first one seen for a report is the last export.
746
+ if (summary.lastAt === null) {
747
+ summary.lastAt = event.createdAt ?? null
748
+ summary.lastFormat = readExportFormat(event.metadata)
749
+ }
750
+ exportsByReportId.set(event.reportId, summary)
751
+ }
752
+
753
+ return records.map((record) => {
754
+ const report = reportById.get(record.id)
755
+ if (!report) return { ...record, ...EMPTY_REPORT_ENRICHMENT }
756
+ const exports = exportsByReportId.get(report.id)
757
+ const billableMinutes = report.totalBillableMinutes ?? 0
758
+ const nonbillableMinutes = report.totalNonbillableMinutes ?? 0
759
+ const enrichment: ReportEnrichment['_staff'] = {
760
+ isClosed: report.status === 'closed',
761
+ closedAt: report.closedAt ? report.closedAt.toISOString() : null,
762
+ billableMinutes,
763
+ nonbillableMinutes,
764
+ totalMinutes: billableMinutes + nonbillableMinutes,
765
+ lockedEntryCount: frozenCountByReportId.get(report.id) ?? 0,
766
+ exportCount: exports?.count ?? 0,
767
+ lastExportedAt: exports?.lastAt ? exports.lastAt.toISOString() : null,
768
+ lastExportFormat: exports?.lastFormat ?? null,
769
+ }
770
+ if (hasRates) {
771
+ enrichment.totalAmount = toNullableNumber(report.totalAmount)
772
+ enrichment.currencyCode = report.currencyCode ?? null
773
+ }
774
+ return { ...record, _staff: enrichment }
775
+ })
776
+ },
777
+ }
778
+
779
+ /**
780
+ * The API-response surface. Every one of these also participates in query-engine
781
+ * pipelines through the dot-form aliases below, so a dashboard, an export or an AI
782
+ * tool that passes `QueryOptions.extensions` sees the same enrichment the REST
783
+ * response carries.
784
+ */
785
+ const apiEnrichers: ResponseEnricher[] = [
786
+ portfolioEnricher,
787
+ taskRollupEnricher,
788
+ taskTagEnricher,
789
+ taskContextEnricher,
790
+ timeEntryEnricher,
791
+ reportEnricher,
792
+ ]
793
+
794
+ export const enrichers: ResponseEnricher[] = [
795
+ ...apiEnrichers,
796
+ ...apiEnrichers.map((enricher) => withQueryEngineSurface(enricher)),
797
+ ]
133
798
 
134
799
  export default enrichers