@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
@@ -1,3 +1,4 @@
1
+ import { z } from 'zod'
1
2
  import type { CommandHandler } from '@open-mercato/shared/lib/commands'
2
3
  import { registerCommand } from '@open-mercato/shared/lib/commands'
3
4
  import type { EntityManager } from '@mikro-orm/postgresql'
@@ -5,10 +6,23 @@ import { UniqueConstraintViolationException } from '@mikro-orm/core'
5
6
  import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
6
7
  import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
7
8
  import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
8
- import { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects } from '@open-mercato/shared/lib/commands/helpers'
9
+ import { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects, setCustomFieldsIfAny } from '@open-mercato/shared/lib/commands/helpers'
10
+ import { extractCustomFieldValuesFromPayload } from '@open-mercato/shared/lib/crud/custom-fields'
11
+ import { E } from '#generated/entities.ids.generated'
12
+ import { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'
9
13
  import { makeCreateRedo } from '@open-mercato/shared/lib/commands/redo'
10
- import type { CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'
11
- import { StaffTeamMember, StaffTimeProject, StaffTimeProjectMember, type StaffTimeProjectStatus, type StaffTimeProjectMemberStatus } from '../data/entities'
14
+ import type { CrudEventsConfig, CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'
15
+ import { CustomerEntity } from '@open-mercato/core/modules/customers/data/entities'
16
+ import {
17
+ StaffTeamMember,
18
+ StaffTimeEntry,
19
+ StaffTimeProject,
20
+ StaffTimeProjectMember,
21
+ StaffTimeReport,
22
+ type StaffTimeProjectStatus,
23
+ type StaffTimeProjectBudgetKind,
24
+ type StaffTimeProjectMemberStatus,
25
+ } from '../data/entities'
12
26
 
13
27
  const timeProjectCrudIndexer: CrudIndexerConfig<StaffTimeProject> = {
14
28
  entityType: 'staff:staff_time_project',
@@ -19,6 +33,7 @@ const timeProjectMemberCrudIndexer: CrudIndexerConfig<StaffTimeProjectMember> =
19
33
  import {
20
34
  staffTimeProjectCreateSchema,
21
35
  staffTimeProjectUpdateSchema,
36
+ staffTimeProjectChangeCurrencySchema,
22
37
  staffTimeProjectMemberAssignSchema,
23
38
  staffTimeProjectMemberUpdateSchema,
24
39
  type StaffTimeProjectCreateInput,
@@ -27,6 +42,8 @@ import {
27
42
  type StaffTimeProjectMemberUpdateInput,
28
43
  } from '../data/validators'
29
44
  import { staffTimeProjectCrudEvents, staffTimeProjectMemberCrudEvents } from '../lib/crud'
45
+ import { DEFAULT_BUDGET_WARN_AT_PERCENT } from '../lib/timesheets-projects/budgetBurn'
46
+ import { seedProjectTaskStatuses } from '../lib/timesheets-tasks/seedProjectStatuses'
30
47
  import {
31
48
  applyScopeToWhere,
32
49
  commandActorScope,
@@ -51,12 +68,66 @@ function isUniqueViolation(error: unknown): boolean {
51
68
  return typeof message === 'string' && message.toLowerCase().includes('duplicate key')
52
69
  }
53
70
 
71
+ function readSnapshotText(value: unknown): string | null {
72
+ if (typeof value !== 'string') return null
73
+ const trimmed = value.trim()
74
+ return trimmed.length > 0 ? trimmed : null
75
+ }
76
+
77
+ /**
78
+ * D-9 keeps a project's customer as an FK id plus a denormalized snapshot, and
79
+ * denormalizing belongs to the module that owns the write: a caller that sends
80
+ * `customerId` alone — the CRUD route, an import, an integration — must still
81
+ * produce a project the portfolio grid can name, instead of one that has a
82
+ * customer in the database and advertises none in the UI. A snapshot the caller
83
+ * supplied still wins verbatim; the project form already carries the picked
84
+ * customer and must not be second-guessed.
85
+ *
86
+ * The lookup is a scoped read by id — never an ORM relation to the customers
87
+ * module — and a customers module that is absent, or a customer that is gone,
88
+ * degrades to `null` rather than failing the write.
89
+ */
90
+ async function resolveCustomerSnapshot(
91
+ em: EntityManager,
92
+ scope: { tenantId: string; organizationId: string },
93
+ customerId: string | null | undefined,
94
+ ): Promise<Record<string, unknown> | null> {
95
+ if (!customerId) return null
96
+ let customer: CustomerEntity | null = null
97
+ try {
98
+ customer = await findOneWithDecryption(
99
+ em,
100
+ CustomerEntity,
101
+ {
102
+ id: customerId,
103
+ tenantId: scope.tenantId,
104
+ organizationId: scope.organizationId,
105
+ deletedAt: null,
106
+ },
107
+ undefined,
108
+ { tenantId: scope.tenantId, organizationId: scope.organizationId },
109
+ )
110
+ } catch {
111
+ return null
112
+ }
113
+ if (!customer) return null
114
+ const email = readSnapshotText(customer.primaryEmail)
115
+ const name = readSnapshotText(customer.displayName) ?? email
116
+ if (!name) return null
117
+ const snapshot: Record<string, unknown> = { name }
118
+ const kind = readSnapshotText(customer.kind)
119
+ if (kind) snapshot.kind = kind
120
+ if (email) snapshot.email = email
121
+ return snapshot
122
+ }
123
+
54
124
  type TimeProjectSnapshot = {
55
125
  id: string
56
126
  tenantId: string
57
127
  organizationId: string
58
128
  name: string
59
129
  customerId: string | null
130
+ customerSnapshot: Record<string, unknown> | null
60
131
  code: string
61
132
  description: string | null
62
133
  projectType: string | null
@@ -65,6 +136,12 @@ type TimeProjectSnapshot = {
65
136
  ownerUserId: string | null
66
137
  costCenter: string | null
67
138
  startDate: string | null
139
+ hourlyRate: string | null
140
+ currencyCode: string | null
141
+ billableByDefault: boolean
142
+ budgetKind: string
143
+ budgetValue: string | null
144
+ budgetWarnAtPercent: number
68
145
  deletedAt: string | null
69
146
  }
70
147
 
@@ -107,6 +184,7 @@ async function loadTimeProjectSnapshot(em: EntityManager, id: string, scope?: St
107
184
  organizationId: project.organizationId,
108
185
  name: project.name,
109
186
  customerId: project.customerId ?? null,
187
+ customerSnapshot: project.customerSnapshot ?? null,
110
188
  code: project.code,
111
189
  description: project.description ?? null,
112
190
  projectType: project.projectType ?? null,
@@ -115,6 +193,12 @@ async function loadTimeProjectSnapshot(em: EntityManager, id: string, scope?: St
115
193
  ownerUserId: project.ownerUserId ?? null,
116
194
  costCenter: project.costCenter ?? null,
117
195
  startDate: project.startDate instanceof Date ? project.startDate.toISOString().split('T')[0] : (project.startDate ?? null),
196
+ hourlyRate: project.hourlyRate ?? null,
197
+ currencyCode: project.currencyCode ?? null,
198
+ billableByDefault: project.billableByDefault ?? true,
199
+ budgetKind: project.budgetKind ?? 'none',
200
+ budgetValue: project.budgetValue ?? null,
201
+ budgetWarnAtPercent: project.budgetWarnAtPercent ?? DEFAULT_BUDGET_WARN_AT_PERCENT,
118
202
  deletedAt: project.deletedAt ? project.deletedAt.toISOString() : null,
119
203
  }
120
204
  }
@@ -150,6 +234,7 @@ function timeProjectSeedFromSnapshot(snapshot: TimeProjectSnapshot): Record<stri
150
234
  organizationId: snapshot.organizationId,
151
235
  name: snapshot.name,
152
236
  customerId: snapshot.customerId ?? null,
237
+ customerSnapshot: snapshot.customerSnapshot ?? null,
153
238
  code: snapshot.code,
154
239
  description: snapshot.description ?? null,
155
240
  projectType: snapshot.projectType ?? null,
@@ -158,6 +243,12 @@ function timeProjectSeedFromSnapshot(snapshot: TimeProjectSnapshot): Record<stri
158
243
  ownerUserId: snapshot.ownerUserId ?? null,
159
244
  costCenter: snapshot.costCenter ?? null,
160
245
  startDate: snapshot.startDate ? new Date(snapshot.startDate) : null,
246
+ hourlyRate: snapshot.hourlyRate ?? null,
247
+ currencyCode: snapshot.currencyCode ?? null,
248
+ billableByDefault: snapshot.billableByDefault ?? true,
249
+ budgetKind: (snapshot.budgetKind ?? 'none') as StaffTimeProjectBudgetKind,
250
+ budgetValue: snapshot.budgetValue ?? null,
251
+ budgetWarnAtPercent: snapshot.budgetWarnAtPercent ?? DEFAULT_BUDGET_WARN_AT_PERCENT,
161
252
  createdAt: new Date(),
162
253
  updatedAt: new Date(),
163
254
  deletedAt: null,
@@ -191,12 +282,20 @@ const createTimeProjectCommand: CommandHandler<StaffTimeProjectCreateInput, { ti
191
282
  commandInputScope(ctx, parsed.tenantId, parsed.organizationId)
192
283
 
193
284
  const em = (ctx.container.resolve('em') as EntityManager).fork()
285
+ const customerSnapshot =
286
+ parsed.customerSnapshot
287
+ ?? (await resolveCustomerSnapshot(
288
+ em,
289
+ { tenantId: parsed.tenantId, organizationId: parsed.organizationId },
290
+ parsed.customerId,
291
+ ))
194
292
  const now = new Date()
195
293
  const project = em.create(StaffTimeProject, {
196
294
  tenantId: parsed.tenantId,
197
295
  organizationId: parsed.organizationId,
198
296
  name: parsed.name,
199
- customerId: parsed.customerId ?? null,
297
+ customerId: parsed.customerId,
298
+ customerSnapshot,
200
299
  code: parsed.code,
201
300
  description: parsed.description ?? null,
202
301
  projectType: parsed.projectType ?? null,
@@ -205,16 +304,41 @@ const createTimeProjectCommand: CommandHandler<StaffTimeProjectCreateInput, { ti
205
304
  ownerUserId: parsed.ownerUserId ?? null,
206
305
  costCenter: parsed.costCenter ?? null,
207
306
  startDate: parsed.startDate ?? null,
307
+ hourlyRate: parsed.hourlyRate ?? null,
308
+ // D-3: creation is the one moment the currency may be chosen freely.
309
+ currencyCode: parsed.currencyCode ?? null,
310
+ billableByDefault: parsed.billableByDefault ?? true,
311
+ budgetKind: parsed.budgetKind ?? 'none',
312
+ budgetValue: parsed.budgetValue ?? null,
313
+ budgetWarnAtPercent: parsed.budgetWarnAtPercent ?? DEFAULT_BUDGET_WARN_AT_PERCENT,
208
314
  createdAt: now,
209
315
  updatedAt: now,
210
316
  deletedAt: null,
211
317
  })
212
- em.persist(project)
318
+ const { translate } = await resolveTranslations()
213
319
  try {
214
- await em.flush()
320
+ // D-1: the project and its Kanban columns are one transaction. The template
321
+ // can only be seeded once the insert has handed back the project's id, so
322
+ // the two writes are separate phases — but a rollback takes both, and a
323
+ // project therefore never exists with an empty board.
324
+ await withAtomicFlush(
325
+ em,
326
+ [
327
+ () => {
328
+ em.persist(project)
329
+ },
330
+ () => {
331
+ seedProjectTaskStatuses(
332
+ em,
333
+ { tenantId: project.tenantId, organizationId: project.organizationId, timeProjectId: project.id },
334
+ translate,
335
+ )
336
+ },
337
+ ],
338
+ { transaction: true, label: 'staff.timesheets.time_projects.create' },
339
+ )
215
340
  } catch (err) {
216
341
  if (isUniqueViolation(err)) {
217
- const { translate } = await resolveTranslations()
218
342
  throw new CrudHttpError(409, {
219
343
  error: translate('staff.timesheets.errors.projectCodeDuplicate', 'A project with this code already exists.'),
220
344
  fieldErrors: { code: translate('staff.timesheets.errors.projectCodeDuplicate', 'A project with this code already exists.') },
@@ -223,6 +347,17 @@ const createTimeProjectCommand: CommandHandler<StaffTimeProjectCreateInput, { ti
223
347
  throw err
224
348
  }
225
349
 
350
+ await setCustomFieldsIfAny({
351
+ dataEngine: ctx.container.resolve('dataEngine'),
352
+ entityId: E.staff.staff_time_project,
353
+ recordId: project.id,
354
+ tenantId: project.tenantId,
355
+ organizationId: project.organizationId,
356
+ values: extractCustomFieldValuesFromPayload(
357
+ (rawInput && typeof rawInput === 'object' ? rawInput : {}) as Record<string, unknown>,
358
+ ),
359
+ })
360
+
226
361
  await emitCrudSideEffects({
227
362
  dataEngine: ctx.container.resolve('dataEngine'),
228
363
  action: 'created',
@@ -321,8 +456,33 @@ const updateTimeProjectCommand: CommandHandler<StaffTimeProjectUpdateInput, { ti
321
456
  ensureTenantScope(ctx, project.tenantId)
322
457
  ensureOrganizationScope(ctx, project.organizationId)
323
458
 
459
+ // The FK and its snapshot move together. A payload that re-points the
460
+ // customer without carrying a snapshot — or that clears a stale one while
461
+ // keeping the customer — has the snapshot re-derived here, so the grid never
462
+ // reads a project with a customer as unassigned. The read runs before every
463
+ // scalar mutation below, so it cannot reset the unit of work.
464
+ const customerTouched = parsed.customerId !== undefined || parsed.customerSnapshot !== undefined
465
+ const nextCustomerId = parsed.customerId !== undefined ? parsed.customerId ?? null : project.customerId ?? null
466
+ const customerUnchanged = nextCustomerId === (project.customerId ?? null)
467
+ let nextCustomerSnapshot: Record<string, unknown> | null | undefined
468
+ if (customerTouched) {
469
+ nextCustomerSnapshot =
470
+ parsed.customerSnapshot
471
+ ?? (await resolveCustomerSnapshot(
472
+ em,
473
+ { tenantId: project.tenantId, organizationId: project.organizationId },
474
+ nextCustomerId,
475
+ ))
476
+ // A customer that stays put keeps the snapshot it already had when the
477
+ // lookup comes back empty (customer removed, customers module absent);
478
+ // only a re-pointed or cleared customer drops it, because holding the
479
+ // previous customer's details on a different FK would misattribute them.
480
+ ?? (customerUnchanged && nextCustomerId ? project.customerSnapshot ?? null : null)
481
+ }
482
+
324
483
  if (parsed.name !== undefined) project.name = parsed.name
325
484
  if (parsed.customerId !== undefined) project.customerId = parsed.customerId ?? null
485
+ if (nextCustomerSnapshot !== undefined) project.customerSnapshot = nextCustomerSnapshot
326
486
  if (parsed.code !== undefined) project.code = parsed.code
327
487
  if (parsed.description !== undefined) project.description = parsed.description ?? null
328
488
  if (parsed.projectType !== undefined) project.projectType = parsed.projectType ?? null
@@ -331,6 +491,16 @@ const updateTimeProjectCommand: CommandHandler<StaffTimeProjectUpdateInput, { ti
331
491
  if (parsed.ownerUserId !== undefined) project.ownerUserId = parsed.ownerUserId ?? null
332
492
  if (parsed.costCenter !== undefined) project.costCenter = parsed.costCenter ?? null
333
493
  if (parsed.startDate !== undefined) project.startDate = parsed.startDate ?? null
494
+ if (parsed.hourlyRate !== undefined) project.hourlyRate = parsed.hourlyRate ?? null
495
+ if (parsed.billableByDefault !== undefined) project.billableByDefault = parsed.billableByDefault
496
+ if (parsed.budgetKind !== undefined) project.budgetKind = parsed.budgetKind
497
+ if (parsed.budgetValue !== undefined) project.budgetValue = parsed.budgetValue ?? null
498
+ if (parsed.budgetWarnAtPercent !== undefined) project.budgetWarnAtPercent = parsed.budgetWarnAtPercent
499
+ // `currency_code` is intentionally absent here and from the update schema:
500
+ // D-3 gives that column to `staff.timesheets.time_projects.change_currency`,
501
+ // which owns the acknowledgement and the locked-entry refusal. A caller that
502
+ // smuggles `currencyCode` into a PUT has it stripped by the schema, so there
503
+ // is no key to assign from.
334
504
  project.updatedAt = new Date()
335
505
  try {
336
506
  await em.flush()
@@ -345,6 +515,17 @@ const updateTimeProjectCommand: CommandHandler<StaffTimeProjectUpdateInput, { ti
345
515
  throw err
346
516
  }
347
517
 
518
+ await setCustomFieldsIfAny({
519
+ dataEngine: ctx.container.resolve('dataEngine'),
520
+ entityId: E.staff.staff_time_project,
521
+ recordId: project.id,
522
+ tenantId: project.tenantId,
523
+ organizationId: project.organizationId,
524
+ values: extractCustomFieldValuesFromPayload(
525
+ (rawInput && typeof rawInput === 'object' ? rawInput : {}) as Record<string, unknown>,
526
+ ),
527
+ })
528
+
348
529
  await emitCrudSideEffects({
349
530
  dataEngine: ctx.container.resolve('dataEngine'),
350
531
  action: 'updated',
@@ -377,8 +558,19 @@ const updateTimeProjectCommand: CommandHandler<StaffTimeProjectUpdateInput, { ti
377
558
  'ownerUserId',
378
559
  'costCenter',
379
560
  'startDate',
561
+ 'hourlyRate',
562
+ 'billableByDefault',
563
+ 'budgetKind',
564
+ 'budgetValue',
565
+ 'budgetWarnAtPercent',
380
566
  'deletedAt',
381
567
  ])
568
+ // `buildChanges` compares by identity, and the two snapshots are loaded through
569
+ // separate forks, so the denormalized customer snapshot needs a value compare
570
+ // or every edit would report it as changed.
571
+ if (JSON.stringify(before.customerSnapshot ?? null) !== JSON.stringify(after.customerSnapshot ?? null)) {
572
+ changes.customerSnapshot = { from: before.customerSnapshot ?? null, to: after.customerSnapshot ?? null }
573
+ }
382
574
  const { translate } = await resolveTranslations()
383
575
  return {
384
576
  actionLabel: translate('staff.audit.timesheets.time_projects.update', 'Update time project'),
@@ -406,6 +598,7 @@ const updateTimeProjectCommand: CommandHandler<StaffTimeProjectUpdateInput, { ti
406
598
  if (!project) return
407
599
  project.name = before.name
408
600
  project.customerId = before.customerId ?? null
601
+ project.customerSnapshot = before.customerSnapshot ?? null
409
602
  project.code = before.code
410
603
  project.description = before.description ?? null
411
604
  project.projectType = before.projectType ?? null
@@ -414,6 +607,13 @@ const updateTimeProjectCommand: CommandHandler<StaffTimeProjectUpdateInput, { ti
414
607
  project.ownerUserId = before.ownerUserId ?? null
415
608
  project.costCenter = before.costCenter ?? null
416
609
  project.startDate = before.startDate ? new Date(before.startDate) : null
610
+ project.hourlyRate = before.hourlyRate ?? null
611
+ project.billableByDefault = before.billableByDefault ?? true
612
+ project.budgetKind = (before.budgetKind ?? 'none') as StaffTimeProjectBudgetKind
613
+ project.budgetValue = before.budgetValue ?? null
614
+ project.budgetWarnAtPercent = before.budgetWarnAtPercent ?? DEFAULT_BUDGET_WARN_AT_PERCENT
615
+ // Undo of a plain update restores what a plain update could change; the currency
616
+ // stays where change_currency left it (D-3).
417
617
  project.deletedAt = before.deletedAt ? new Date(before.deletedAt) : null
418
618
  project.updatedAt = new Date()
419
619
  await em.flush()
@@ -508,6 +708,7 @@ const deleteTimeProjectCommand: CommandHandler<{ id?: string }, { timeProjectId:
508
708
  organizationId: before.organizationId,
509
709
  name: before.name,
510
710
  customerId: before.customerId ?? null,
711
+ customerSnapshot: before.customerSnapshot ?? null,
511
712
  code: before.code,
512
713
  description: before.description ?? null,
513
714
  projectType: before.projectType ?? null,
@@ -515,6 +716,12 @@ const deleteTimeProjectCommand: CommandHandler<{ id?: string }, { timeProjectId:
515
716
  ownerUserId: before.ownerUserId ?? null,
516
717
  costCenter: before.costCenter ?? null,
517
718
  startDate: before.startDate ? new Date(before.startDate) : null,
719
+ hourlyRate: before.hourlyRate ?? null,
720
+ currencyCode: before.currencyCode ?? null,
721
+ billableByDefault: before.billableByDefault ?? true,
722
+ budgetKind: (before.budgetKind ?? 'none') as StaffTimeProjectBudgetKind,
723
+ budgetValue: before.budgetValue ?? null,
724
+ budgetWarnAtPercent: before.budgetWarnAtPercent ?? DEFAULT_BUDGET_WARN_AT_PERCENT,
518
725
  deletedAt: null,
519
726
  createdAt: new Date(),
520
727
  updatedAt: new Date(),
@@ -523,6 +730,7 @@ const deleteTimeProjectCommand: CommandHandler<{ id?: string }, { timeProjectId:
523
730
  } else {
524
731
  project.name = before.name
525
732
  project.customerId = before.customerId ?? null
733
+ project.customerSnapshot = before.customerSnapshot ?? null
526
734
  project.code = before.code
527
735
  project.description = before.description ?? null
528
736
  project.projectType = before.projectType ?? null
@@ -530,6 +738,12 @@ const deleteTimeProjectCommand: CommandHandler<{ id?: string }, { timeProjectId:
530
738
  project.ownerUserId = before.ownerUserId ?? null
531
739
  project.costCenter = before.costCenter ?? null
532
740
  project.startDate = before.startDate ? new Date(before.startDate) : null
741
+ project.hourlyRate = before.hourlyRate ?? null
742
+ project.currencyCode = before.currencyCode ?? null
743
+ project.billableByDefault = before.billableByDefault ?? true
744
+ project.budgetKind = (before.budgetKind ?? 'none') as StaffTimeProjectBudgetKind
745
+ project.budgetValue = before.budgetValue ?? null
746
+ project.budgetWarnAtPercent = before.budgetWarnAtPercent ?? DEFAULT_BUDGET_WARN_AT_PERCENT
533
747
  project.deletedAt = null
534
748
  project.updatedAt = new Date()
535
749
  }
@@ -550,6 +764,249 @@ const deleteTimeProjectCommand: CommandHandler<{ id?: string }, { timeProjectId:
550
764
  },
551
765
  }
552
766
 
767
+ /**
768
+ * D-3: a project's currency is read-only once hours have been logged against it,
769
+ * and the deliberate change action below relabels the project WITHOUT converting
770
+ * any historical amount. It never touches an entry's `rate_currency_code` or its
771
+ * stored cost, so a closed report keeps billing what it billed. That is also why
772
+ * the change is refused outright while any entry of the project sits frozen in a
773
+ * closed report: relabelling then would make the project disagree with the
774
+ * `frozen_currency_code` those report lines were issued under.
775
+ */
776
+ export const staffTimeProjectChangeCurrencyCommandId = 'staff.timesheets.time_projects.change_currency'
777
+
778
+ export const PROJECT_HAS_LOCKED_ENTRIES_CODE = 'project_has_locked_entries'
779
+
780
+ const changeTimeProjectCurrencyCommandSchema = staffTimeProjectChangeCurrencySchema.extend({
781
+ id: z.string().uuid(),
782
+ })
783
+
784
+ export type StaffTimeProjectChangeCurrencyCommandInput = z.infer<typeof changeTimeProjectCurrencyCommandSchema>
785
+
786
+ export type StaffTimeProjectChangeCurrencyResult = {
787
+ timeProjectId: string
788
+ currencyCode: string
789
+ previousCurrencyCode: string | null
790
+ }
791
+
792
+ type BlockingLockedReport = {
793
+ id: string
794
+ reference: string | null
795
+ title: string | null
796
+ }
797
+
798
+ type LockedEntryBlock = {
799
+ lockedEntryCount: number
800
+ reports: BlockingLockedReport[]
801
+ }
802
+
803
+ async function findLockedEntryBlock(
804
+ em: EntityManager,
805
+ project: StaffTimeProject,
806
+ ): Promise<LockedEntryBlock> {
807
+ const scope = { tenantId: project.tenantId, organizationId: project.organizationId }
808
+ const lockedEntries = await em.find(
809
+ StaffTimeEntry,
810
+ { ...scope, timeProjectId: project.id, lockedReportId: { $ne: null }, deletedAt: null },
811
+ { fields: ['id', 'lockedReportId'] },
812
+ )
813
+ if (lockedEntries.length === 0) return { lockedEntryCount: 0, reports: [] }
814
+
815
+ const reportIds = Array.from(
816
+ new Set(lockedEntries.map((entry) => entry.lockedReportId).filter((value): value is string => typeof value === 'string')),
817
+ )
818
+ const reports = reportIds.length
819
+ ? await em.find(
820
+ StaffTimeReport,
821
+ { ...scope, id: { $in: reportIds } },
822
+ { fields: ['id', 'reference', 'title'] },
823
+ )
824
+ : []
825
+ const known = new Map(reports.map((report) => [report.id, report]))
826
+
827
+ return {
828
+ lockedEntryCount: lockedEntries.length,
829
+ reports: reportIds.map((id) => ({
830
+ id,
831
+ reference: known.get(id)?.reference ?? null,
832
+ title: known.get(id)?.title ?? null,
833
+ })),
834
+ }
835
+ }
836
+
837
+ async function assertNoLockedEntries(em: EntityManager, project: StaffTimeProject): Promise<void> {
838
+ const block = await findLockedEntryBlock(em, project)
839
+ if (block.lockedEntryCount === 0) return
840
+ const { translate } = await resolveTranslations()
841
+ throw new CrudHttpError(409, {
842
+ code: PROJECT_HAS_LOCKED_ENTRIES_CODE,
843
+ error: translate(
844
+ 'staff.timesheets.errors.projectCurrencyLocked',
845
+ 'This project has time entries frozen in a closed report. Unlock those reports before changing the currency.',
846
+ ),
847
+ lockedEntryCount: block.lockedEntryCount,
848
+ lockedReports: block.reports,
849
+ })
850
+ }
851
+
852
+ /**
853
+ * Same `staff.timesheets.time_project.updated` event id every project write emits,
854
+ * with the currency transition carried in the payload so a subscriber can tell a
855
+ * relabel apart from an ordinary field edit without re-reading the project.
856
+ */
857
+ function timeProjectCurrencyChangeEvents(
858
+ previousCurrencyCode: string | null,
859
+ currencyCode: string,
860
+ ): CrudEventsConfig<StaffTimeProject> {
861
+ return {
862
+ ...staffTimeProjectCrudEvents,
863
+ buildPayload: (emitCtx) => ({
864
+ id: emitCtx.identifiers.id,
865
+ organizationId: emitCtx.identifiers.organizationId,
866
+ tenantId: emitCtx.identifiers.tenantId,
867
+ changedField: 'currencyCode',
868
+ previousCurrencyCode,
869
+ currencyCode,
870
+ converted: false,
871
+ }),
872
+ }
873
+ }
874
+
875
+ const changeTimeProjectCurrencyCommand: CommandHandler<
876
+ StaffTimeProjectChangeCurrencyCommandInput,
877
+ StaffTimeProjectChangeCurrencyResult
878
+ > = {
879
+ id: staffTimeProjectChangeCurrencyCommandId,
880
+ async prepare(rawInput, ctx) {
881
+ const parsed = changeTimeProjectCurrencyCommandSchema.parse(rawInput)
882
+ const em = ctx.container.resolve('em') as EntityManager
883
+ const snapshot = await loadTimeProjectSnapshot(em, parsed.id, staffSnapshotScopeFromContext(ctx))
884
+ if (!snapshot) return {}
885
+ return { before: snapshot }
886
+ },
887
+ async execute(rawInput, ctx) {
888
+ const parsed = changeTimeProjectCurrencyCommandSchema.parse(rawInput)
889
+ const em = (ctx.container.resolve('em') as EntityManager).fork()
890
+ const scope = commandActorScope(ctx)
891
+ const project = await findOneWithDecryption(
892
+ em,
893
+ StaffTimeProject,
894
+ applyScopeToWhere<StaffTimeProject>({ id: parsed.id, deletedAt: null }, scope),
895
+ undefined,
896
+ scopeForDecryption(scope),
897
+ )
898
+ if (!project) {
899
+ const { translate } = await resolveTranslations()
900
+ throw new CrudHttpError(404, {
901
+ error: translate('staff.timesheets.errors.projectNotFound', 'Time project not found or not accessible.'),
902
+ })
903
+ }
904
+ ensureTenantScope(ctx, project.tenantId)
905
+ ensureOrganizationScope(ctx, project.organizationId)
906
+
907
+ const previousCurrencyCode = project.currencyCode ?? null
908
+
909
+ // The lock probe and the relabel are separate phases of one transaction: the
910
+ // probe queries `staff_time_entries` and must therefore never share a phase
911
+ // with the scalar mutation, and the shared transaction keeps a report closing
912
+ // concurrently from slipping between the check and the write.
913
+ await withAtomicFlush(
914
+ em,
915
+ [
916
+ () => assertNoLockedEntries(em, project),
917
+ () => {
918
+ project.currencyCode = parsed.currencyCode
919
+ project.updatedAt = new Date()
920
+ },
921
+ ],
922
+ { transaction: true, label: staffTimeProjectChangeCurrencyCommandId },
923
+ )
924
+
925
+ await emitCrudSideEffects({
926
+ dataEngine: ctx.container.resolve('dataEngine'),
927
+ action: 'updated',
928
+ entity: project,
929
+ identifiers: {
930
+ id: project.id,
931
+ organizationId: project.organizationId,
932
+ tenantId: project.tenantId,
933
+ },
934
+ events: timeProjectCurrencyChangeEvents(previousCurrencyCode, parsed.currencyCode),
935
+ indexer: timeProjectCrudIndexer,
936
+ })
937
+
938
+ return {
939
+ timeProjectId: project.id,
940
+ currencyCode: parsed.currencyCode,
941
+ previousCurrencyCode,
942
+ }
943
+ },
944
+ buildLog: async ({ snapshots, ctx }) => {
945
+ const before = snapshots.before as TimeProjectSnapshot | undefined
946
+ if (!before) return null
947
+ const em = (ctx.container.resolve('em') as EntityManager).fork()
948
+ const after = await loadTimeProjectSnapshot(em, before.id, staffSnapshotScopeFromSnapshot(before))
949
+ if (!after) return null
950
+ const changes = buildChanges(
951
+ before as unknown as Record<string, unknown>,
952
+ after as unknown as Record<string, unknown>,
953
+ ['currencyCode'],
954
+ )
955
+ const { translate } = await resolveTranslations()
956
+ return {
957
+ actionLabel: translate('staff.audit.timesheets.time_projects.change_currency', 'Change time project currency'),
958
+ resourceKind: 'staff.timesheets.time_project',
959
+ resourceId: before.id,
960
+ tenantId: before.tenantId,
961
+ organizationId: before.organizationId,
962
+ snapshotBefore: before,
963
+ snapshotAfter: after,
964
+ changes,
965
+ payload: {
966
+ undo: {
967
+ before,
968
+ after,
969
+ } satisfies TimeProjectUndoPayload,
970
+ },
971
+ }
972
+ },
973
+ undo: async ({ logEntry, ctx }) => {
974
+ const payload = extractUndoPayload<TimeProjectUndoPayload>(logEntry)
975
+ const before = payload?.before
976
+ if (!before) return
977
+ const em = (ctx.container.resolve('em') as EntityManager).fork()
978
+ const project = await em.findOne(StaffTimeProject, scopedStaffSnapshotWhere(before.id, staffSnapshotScopeFromSnapshot(before)))
979
+ if (!project) return
980
+
981
+ // Undo relabels back, so it has to honour the same invariant: entries may have
982
+ // been frozen into a closed report since the change was made.
983
+ await withAtomicFlush(
984
+ em,
985
+ [
986
+ () => assertNoLockedEntries(em, project),
987
+ () => {
988
+ project.currencyCode = before.currencyCode ?? null
989
+ project.updatedAt = new Date()
990
+ },
991
+ ],
992
+ { transaction: true, label: `${staffTimeProjectChangeCurrencyCommandId}.undo` },
993
+ )
994
+
995
+ await emitCrudUndoSideEffects({
996
+ dataEngine: ctx.container.resolve('dataEngine'),
997
+ action: 'updated',
998
+ entity: project,
999
+ identifiers: {
1000
+ id: project.id,
1001
+ organizationId: project.organizationId,
1002
+ tenantId: project.tenantId,
1003
+ },
1004
+ events: staffTimeProjectCrudEvents,
1005
+ indexer: timeProjectCrudIndexer,
1006
+ })
1007
+ },
1008
+ }
1009
+
553
1010
  const assignTimeProjectMemberCommand: CommandHandler<StaffTimeProjectMemberAssignInput, { timeProjectMemberId: string }> = {
554
1011
  id: 'staff.timesheets.time_project_members.assign',
555
1012
  async execute(rawInput, ctx) {
@@ -675,6 +1132,133 @@ const assignTimeProjectMemberCommand: CommandHandler<StaffTimeProjectMemberAssig
675
1132
  }),
676
1133
  }
677
1134
 
1135
+ /**
1136
+ * D-12 turned `assigned_end_date` into a field a Team Leader edits, so a
1137
+ * membership row needs an update path of its own. Without one the project team
1138
+ * drawer had to express a re-dating as "assign a replacement row, drop the old
1139
+ * one", and the audit trail then read as an unassign plus an assign — a history
1140
+ * that misstates what happened on the record an invoice is defended with.
1141
+ *
1142
+ * `timeProjectId` is optional in the schema but always supplied by the route
1143
+ * from the URL segment: the membership must belong to the project being edited,
1144
+ * so a body carrying a foreign membership id cannot reach another project's row.
1145
+ */
1146
+ const updateTimeProjectMemberCommandSchema = staffTimeProjectMemberUpdateSchema.extend({
1147
+ timeProjectId: z.string().uuid().optional(),
1148
+ })
1149
+
1150
+ export type StaffTimeProjectMemberUpdateCommandInput = StaffTimeProjectMemberUpdateInput & {
1151
+ timeProjectId?: string
1152
+ }
1153
+
1154
+ const updateTimeProjectMemberCommand: CommandHandler<StaffTimeProjectMemberUpdateCommandInput, { timeProjectMemberId: string }> = {
1155
+ id: 'staff.timesheets.time_project_members.update',
1156
+ async prepare(rawInput, ctx) {
1157
+ const parsed = updateTimeProjectMemberCommandSchema.parse(rawInput)
1158
+ const em = ctx.container.resolve('em') as EntityManager
1159
+ const snapshot = await loadTimeProjectMemberSnapshot(em, parsed.id, staffSnapshotScopeFromContext(ctx))
1160
+ if (!snapshot) return {}
1161
+ return { before: snapshot }
1162
+ },
1163
+ async execute(rawInput, ctx) {
1164
+ const parsed = updateTimeProjectMemberCommandSchema.parse(rawInput)
1165
+ const em = (ctx.container.resolve('em') as EntityManager).fork()
1166
+ const scope = commandActorScope(ctx)
1167
+ const member = await findOneWithDecryption(
1168
+ em,
1169
+ StaffTimeProjectMember,
1170
+ applyScopeToWhere<StaffTimeProjectMember>(
1171
+ {
1172
+ id: parsed.id,
1173
+ deletedAt: null,
1174
+ ...(parsed.timeProjectId ? { timeProjectId: parsed.timeProjectId } : {}),
1175
+ },
1176
+ scope,
1177
+ ),
1178
+ undefined,
1179
+ scopeForDecryption(scope),
1180
+ )
1181
+ if (!member) {
1182
+ const { translate } = await resolveTranslations()
1183
+ throw new CrudHttpError(404, {
1184
+ error: translate('staff.timesheets.errors.memberNotFound', 'Time project member not found or not accessible.'),
1185
+ })
1186
+ }
1187
+ ensureTenantScope(ctx, member.tenantId)
1188
+ ensureOrganizationScope(ctx, member.organizationId)
1189
+
1190
+ if (parsed.role !== undefined) member.role = parsed.role ?? null
1191
+ if (parsed.status !== undefined) member.status = parsed.status
1192
+ if (parsed.assignedEndDate !== undefined) member.assignedEndDate = parsed.assignedEndDate ?? null
1193
+ member.updatedAt = new Date()
1194
+ await em.flush()
1195
+
1196
+ await emitCrudSideEffects({
1197
+ dataEngine: ctx.container.resolve('dataEngine'),
1198
+ action: 'updated',
1199
+ entity: member,
1200
+ identifiers: { id: member.id, organizationId: member.organizationId, tenantId: member.tenantId },
1201
+ events: staffTimeProjectMemberCrudEvents,
1202
+ indexer: timeProjectMemberCrudIndexer,
1203
+ })
1204
+
1205
+ return { timeProjectMemberId: member.id }
1206
+ },
1207
+ buildLog: async ({ snapshots, ctx }) => {
1208
+ const before = snapshots.before as TimeProjectMemberSnapshot | undefined
1209
+ if (!before) return null
1210
+ const em = (ctx.container.resolve('em') as EntityManager).fork()
1211
+ const after = await loadTimeProjectMemberSnapshot(em, before.id, staffSnapshotScopeFromSnapshot(before))
1212
+ if (!after) return null
1213
+ const changes = buildChanges(before as unknown as Record<string, unknown>, after as unknown as Record<string, unknown>, [
1214
+ 'role',
1215
+ 'status',
1216
+ 'assignedEndDate',
1217
+ ])
1218
+ const { translate } = await resolveTranslations()
1219
+ return {
1220
+ actionLabel: translate('staff.audit.timesheets.time_project_members.update', 'Update time project member'),
1221
+ resourceKind: 'staff.timesheets.time_project_member',
1222
+ resourceId: before.id,
1223
+ tenantId: before.tenantId,
1224
+ organizationId: before.organizationId,
1225
+ snapshotBefore: before,
1226
+ snapshotAfter: after,
1227
+ changes,
1228
+ payload: {
1229
+ undo: {
1230
+ before,
1231
+ after,
1232
+ } satisfies TimeProjectMemberUndoPayload,
1233
+ },
1234
+ }
1235
+ },
1236
+ undo: async ({ logEntry, ctx }) => {
1237
+ const payload = extractUndoPayload<TimeProjectMemberUndoPayload>(logEntry)
1238
+ const before = payload?.before
1239
+ if (!before) return
1240
+ const em = (ctx.container.resolve('em') as EntityManager).fork()
1241
+ const member = await em.findOne(StaffTimeProjectMember, scopedStaffSnapshotWhere(before.id, staffSnapshotScopeFromSnapshot(before)))
1242
+ if (!member) return
1243
+ // Restores exactly what this command can change; the assignment window's
1244
+ // start and the row's identity belong to assign/unassign.
1245
+ member.role = before.role ?? null
1246
+ member.status = (before.status ?? 'active') as StaffTimeProjectMemberStatus
1247
+ member.assignedEndDate = before.assignedEndDate ? new Date(before.assignedEndDate) : null
1248
+ member.updatedAt = new Date()
1249
+ await em.flush()
1250
+
1251
+ await emitCrudUndoSideEffects({
1252
+ dataEngine: ctx.container.resolve('dataEngine'),
1253
+ action: 'updated',
1254
+ entity: member,
1255
+ identifiers: { id: member.id, organizationId: member.organizationId, tenantId: member.tenantId },
1256
+ events: staffTimeProjectMemberCrudEvents,
1257
+ indexer: timeProjectMemberCrudIndexer,
1258
+ })
1259
+ },
1260
+ }
1261
+
678
1262
  const unassignTimeProjectMemberCommand: CommandHandler<{ id?: string }, { timeProjectMemberId: string }> = {
679
1263
  id: 'staff.timesheets.time_project_members.unassign',
680
1264
  async prepare(input, ctx) {
@@ -783,5 +1367,7 @@ const unassignTimeProjectMemberCommand: CommandHandler<{ id?: string }, { timePr
783
1367
  registerCommand(createTimeProjectCommand)
784
1368
  registerCommand(updateTimeProjectCommand)
785
1369
  registerCommand(deleteTimeProjectCommand)
1370
+ registerCommand(changeTimeProjectCurrencyCommand)
786
1371
  registerCommand(assignTimeProjectMemberCommand)
1372
+ registerCommand(updateTimeProjectMemberCommand)
787
1373
  registerCommand(unassignTimeProjectMemberCommand)