@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
@@ -8,17 +8,41 @@ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
8
8
  import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
9
9
  import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
10
10
  import { emitCrudSideEffects, flushCrudSideEffects } from "@open-mercato/shared/lib/commands/helpers";
11
- import { StaffTimeEntry, StaffTeamMember, StaffTimeProject } from "../../../../data/entities.js";
12
- import { staffTimeEntryBulkSaveSchema } from "../../../../data/validators.js";
11
+ import { StaffTimeEntry, StaffTeamMember, StaffTimeProject, StaffTimeTask } from "../../../../data/entities.js";
12
+ import {
13
+ staffTimeEntryBulkSaveSchema
14
+ } from "../../../../data/validators.js";
15
+ import {
16
+ buildTimeEntryLockedError,
17
+ rebaseTimeEntryIntervalToDate,
18
+ reconcileTimeEntryInterval,
19
+ resolveTimeEntryBillable,
20
+ resolveTimeEntryNotesInput,
21
+ resolveTimeEntryProjectId,
22
+ resolveTimeEntrySettings,
23
+ roundedMinutesFor,
24
+ timeEntryTaskMatchesProject,
25
+ toStoredTimeEntryRateOverride
26
+ } from "../../../../commands/timesheets-entries.js";
13
27
  import { staffTimeEntryCrudEvents } from "../../../../lib/crud.js";
28
+ import { emitStaffEvent } from "../../../../events.js";
14
29
  import { invalidateStaffTimeEntryCache } from "../../../../lib/timesheets/timeEntryCacheInvalidation.js";
15
30
  import {
16
- resolveUserFeatures,
31
+ STAFF_TIME_TRACKING_RESOURCE_KINDS,
17
32
  runStaffMutationGuardAfterSuccess,
18
33
  runStaffMutationGuards
19
34
  } from "../../../guards.js";
35
+ import { runTimesheetInterceptors } from "../../_shared/withTimesheetInterceptors.js";
20
36
  import { createLogger } from "@open-mercato/shared/lib/logger";
21
37
  const logger = createLogger("staff");
38
+ function cellDateKey(value) {
39
+ if (!value) return "";
40
+ const date = value instanceof Date ? value : new Date(value);
41
+ return Number.isNaN(date.getTime()) ? String(value) : date.toISOString().slice(0, 10);
42
+ }
43
+ function bulkValidationError(errors) {
44
+ return NextResponse.json({ ok: false, errors }, { status: 422 });
45
+ }
22
46
  const metadata = {
23
47
  POST: { requireAuth: true, requireFeatures: ["staff.timesheets.manage_own"] }
24
48
  };
@@ -34,8 +58,15 @@ async function POST(req) {
34
58
  if (!tenantId || !organizationId) {
35
59
  throw new CrudHttpError(400, { error: translate("staff.errors.missingScope", "Missing tenant or organization scope.") });
36
60
  }
37
- const body = await readJsonSafe(req, {});
38
- const parsed = staffTimeEntryBulkSaveSchema.safeParse(body);
61
+ const interceptors = await runTimesheetInterceptors({
62
+ request: req,
63
+ method: "POST",
64
+ scope: { container, userId: auth.sub, tenantId, organizationId },
65
+ body: await readJsonSafe(req, {})
66
+ });
67
+ if (!interceptors.ok) return interceptors.response;
68
+ const { session } = interceptors;
69
+ const parsed = staffTimeEntryBulkSaveSchema.safeParse(session.body);
39
70
  if (!parsed.success) {
40
71
  const errors = parsed.error.issues.map((issue) => ({
41
72
  path: issue.path.join("."),
@@ -44,6 +75,14 @@ async function POST(req) {
44
75
  return NextResponse.json({ ok: false, errors }, { status: 422 });
45
76
  }
46
77
  const { entries } = parsed.data;
78
+ const taggedRows = entries.filter((entry) => entry.tagIds !== void 0);
79
+ if (taggedRows.length > 0) {
80
+ const message = translate(
81
+ "staff.timesheets.errors.bulkTagsUnsupported",
82
+ "Tags cannot be changed from the timesheet grid. Open the time entry to edit its tags."
83
+ );
84
+ return bulkValidationError(taggedRows.map(() => ({ path: "entries[].tagIds", message })));
85
+ }
47
86
  const em = container.resolve("em").fork();
48
87
  const scopeCtx = { tenantId, organizationId };
49
88
  const staffMember = await findOneWithDecryption(
@@ -57,31 +96,77 @@ async function POST(req) {
57
96
  throw new CrudHttpError(403, { error: translate("staff.timesheets.errors.noStaffMember", "No staff member linked to your account.") });
58
97
  }
59
98
  const staffMemberId = staffMember.id;
60
- const referencedProjectIds = [
99
+ const referencedTaskIds = [
61
100
  ...new Set(
62
- entries.map((e) => e.timeProjectId).filter((id) => typeof id === "string" && id.length > 0)
101
+ entries.map((entry) => entry.taskId).filter((id) => typeof id === "string" && id.length > 0)
63
102
  )
64
103
  ];
104
+ const tasksById = /* @__PURE__ */ new Map();
105
+ if (referencedTaskIds.length > 0) {
106
+ const validTasks = await em.find(StaffTimeTask, {
107
+ id: { $in: referencedTaskIds },
108
+ tenantId,
109
+ organizationId,
110
+ deletedAt: null
111
+ }, { fields: ["id", "timeProjectId"] });
112
+ for (const task of validTasks) tasksById.set(task.id, { id: task.id, timeProjectId: task.timeProjectId });
113
+ const invalidTaskIds = referencedTaskIds.filter((id) => !tasksById.has(id));
114
+ if (invalidTaskIds.length > 0) {
115
+ const message = translate("staff.timesheets.errors.taskNotFound", "Task not found or not accessible.");
116
+ return bulkValidationError(
117
+ invalidTaskIds.map((id) => ({ path: "entries[].taskId", message, value: id }))
118
+ );
119
+ }
120
+ }
121
+ const resolvedRows = [];
122
+ const missingProjectErrors = [];
123
+ const taskProjectMismatchErrors = [];
124
+ for (const entry of entries) {
125
+ const task = entry.taskId ? tasksById.get(entry.taskId) ?? null : null;
126
+ const timeProjectId = resolveTimeEntryProjectId(entry.timeProjectId, task);
127
+ if (!timeProjectId) {
128
+ missingProjectErrors.push({
129
+ path: "entries[].timeProjectId",
130
+ message: translate("staff.timesheets.errors.projectRequired", "Time project id is required.")
131
+ });
132
+ continue;
133
+ }
134
+ if (!timeEntryTaskMatchesProject(task, timeProjectId)) {
135
+ taskProjectMismatchErrors.push({
136
+ path: "entries[].taskId",
137
+ message: translate("staff.timesheets.errors.taskNotFound", "Task not found or not accessible."),
138
+ value: entry.taskId
139
+ });
140
+ continue;
141
+ }
142
+ resolvedRows.push({ entry, task, timeProjectId });
143
+ }
144
+ if (missingProjectErrors.length > 0 || taskProjectMismatchErrors.length > 0) {
145
+ return bulkValidationError([...missingProjectErrors, ...taskProjectMismatchErrors]);
146
+ }
147
+ const referencedProjectIds = [...new Set(resolvedRows.map((row) => row.timeProjectId))];
148
+ const projectsById = /* @__PURE__ */ new Map();
65
149
  if (referencedProjectIds.length > 0) {
66
150
  const validProjects = await em.find(StaffTimeProject, {
67
151
  id: { $in: referencedProjectIds },
68
152
  tenantId,
69
153
  organizationId,
70
154
  deletedAt: null
71
- }, { fields: ["id"] });
72
- const validIds = new Set(validProjects.map((p) => p.id));
73
- const invalidIds = referencedProjectIds.filter((id) => !validIds.has(id));
155
+ }, { fields: ["id", "currencyCode", "billableByDefault"] });
156
+ for (const project of validProjects) {
157
+ projectsById.set(project.id, {
158
+ currencyCode: project.currencyCode ?? null,
159
+ billableByDefault: project.billableByDefault ?? null
160
+ });
161
+ }
162
+ const invalidIds = referencedProjectIds.filter((id) => !projectsById.has(id));
74
163
  if (invalidIds.length > 0) {
75
- return NextResponse.json(
76
- {
77
- ok: false,
78
- errors: invalidIds.map((id) => ({
79
- path: "entries[].timeProjectId",
80
- message: translate("staff.timesheets.errors.projectNotFound", "Time project not found or not accessible."),
81
- value: id
82
- }))
83
- },
84
- { status: 422 }
164
+ const message = translate(
165
+ "staff.timesheets.errors.projectNotFound",
166
+ "Time project not found or not accessible."
167
+ );
168
+ return bulkValidationError(
169
+ invalidIds.map((id) => ({ path: "entries[].timeProjectId", message, value: id }))
85
170
  );
86
171
  }
87
172
  }
@@ -89,21 +174,21 @@ async function POST(req) {
89
174
  tenantId,
90
175
  organizationId,
91
176
  userId: auth.sub ?? "",
92
- resourceKind: "staff.timesheets.time_entry",
177
+ resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry,
93
178
  resourceId: staffMemberId,
94
179
  operation: "update",
95
180
  requestMethod: req.method,
96
181
  requestHeaders: req.headers,
97
182
  mutationPayload: parsed.data
98
183
  };
99
- const guardResult = await runStaffMutationGuards(container, guardInput, resolveUserFeatures(auth));
184
+ const guardResult = await runStaffMutationGuards(container, guardInput);
100
185
  if (!guardResult.ok) {
101
186
  return NextResponse.json(
102
187
  guardResult.errorBody ?? { error: "Operation blocked by guard" },
103
188
  { status: guardResult.errorStatus ?? 422 }
104
189
  );
105
190
  }
106
- const existingIds = entries.map((entry) => entry.id).filter((id) => typeof id === "string" && id.length > 0);
191
+ const existingIds = resolvedRows.map((row) => row.entry.id).filter((id) => typeof id === "string" && id.length > 0);
107
192
  if (existingIds.length > 0) {
108
193
  const resolvedExisting = await em.find(
109
194
  StaffTimeEntry,
@@ -129,6 +214,42 @@ async function POST(req) {
129
214
  );
130
215
  }
131
216
  }
217
+ const idlessRows = resolvedRows.filter((row) => !row.entry.id);
218
+ const idlessCells = idlessRows.map((row) => `${cellDateKey(row.entry.date)}|${row.timeProjectId}`);
219
+ const lockConditions = [];
220
+ if (existingIds.length > 0) lockConditions.push({ id: { $in: existingIds } });
221
+ if (idlessCells.length > 0) {
222
+ lockConditions.push({
223
+ date: { $in: idlessRows.map((row) => row.entry.date) },
224
+ timeProjectId: { $in: [...new Set(idlessRows.map((row) => row.timeProjectId))] }
225
+ });
226
+ }
227
+ if (lockConditions.length > 0) {
228
+ const existingIdSet = new Set(existingIds);
229
+ const idlessCellSet = new Set(idlessCells);
230
+ const lockedCandidates = await em.find(
231
+ StaffTimeEntry,
232
+ {
233
+ tenantId,
234
+ organizationId,
235
+ staffMemberId,
236
+ deletedAt: null,
237
+ lockedReportId: { $ne: null },
238
+ $or: lockConditions
239
+ },
240
+ { fields: ["id", "lockedReportId", "date", "timeProjectId"] }
241
+ );
242
+ const blocked = lockedCandidates.filter(
243
+ (row) => existingIdSet.has(row.id) || idlessCellSet.has(`${cellDateKey(row.date)}|${row.timeProjectId ?? ""}`)
244
+ );
245
+ if (blocked.length > 0) {
246
+ throw buildTimeEntryLockedError(
247
+ blocked.map((row) => ({ id: row.id, lockedReportId: row.lockedReportId })),
248
+ translate
249
+ );
250
+ }
251
+ }
252
+ const settings = await resolveTimeEntrySettings(container, tenantId);
132
253
  const { counts, pendingChanges } = await em.transactional(async (trx) => {
133
254
  let created = 0;
134
255
  let updated = 0;
@@ -142,7 +263,9 @@ async function POST(req) {
142
263
  scopeCtx
143
264
  ) : [];
144
265
  const existingMap = new Map(existingEntries.map((entry) => [entry.id, entry]));
145
- for (const entry of entries) {
266
+ for (const { entry, task, timeProjectId } of resolvedRows) {
267
+ const project = projectsById.get(timeProjectId) ?? null;
268
+ const notes = resolveTimeEntryNotesInput(entry);
146
269
  if (entry.id && existingMap.has(entry.id)) {
147
270
  const existing = existingMap.get(entry.id);
148
271
  if (entry.durationMinutes === 0) {
@@ -150,10 +273,37 @@ async function POST(req) {
150
273
  deleted++;
151
274
  changes.push({ action: "deleted", entity: existing });
152
275
  } else {
276
+ const interval = reconcileTimeEntryInterval({
277
+ stored: rebaseTimeEntryIntervalToDate({
278
+ stored: {
279
+ startedAt: existing.startedAt ?? null,
280
+ endedAt: existing.endedAt ?? null,
281
+ durationMinutes: existing.durationMinutes
282
+ },
283
+ fromDate: existing.date,
284
+ toDate: entry.date
285
+ }),
286
+ startedAt: void 0,
287
+ endedAt: void 0,
288
+ durationMinutes: entry.durationMinutes
289
+ });
290
+ const projectChanged = timeProjectId !== (existing.timeProjectId ?? null);
153
291
  existing.date = entry.date;
154
- existing.timeProjectId = entry.timeProjectId;
155
- existing.durationMinutes = entry.durationMinutes;
156
- existing.notes = entry.notes ?? existing.notes;
292
+ existing.timeProjectId = timeProjectId;
293
+ if (projectChanged) existing.rateCurrencyCode = project?.currencyCode ?? null;
294
+ existing.startedAt = interval.startedAt;
295
+ existing.endedAt = interval.endedAt;
296
+ existing.durationMinutes = interval.durationMinutes;
297
+ existing.roundedMinutes = roundedMinutesFor(interval.durationMinutes, settings, {
298
+ tenantId,
299
+ organizationId
300
+ });
301
+ if (entry.taskId !== void 0) existing.taskId = task?.id ?? null;
302
+ if (entry.isBillable !== void 0) existing.isBillable = entry.isBillable;
303
+ if (entry.rateOverrideAmount !== void 0) {
304
+ existing.rateOverrideAmount = toStoredTimeEntryRateOverride(entry.rateOverrideAmount);
305
+ }
306
+ if (notes !== void 0) existing.notes = notes;
157
307
  existing.updatedAt = /* @__PURE__ */ new Date();
158
308
  updated++;
159
309
  changes.push({ action: "updated", entity: existing });
@@ -165,9 +315,22 @@ async function POST(req) {
165
315
  organizationId,
166
316
  staffMemberId,
167
317
  date: entry.date,
168
- timeProjectId: entry.timeProjectId,
318
+ timeProjectId,
319
+ taskId: task?.id ?? null,
169
320
  durationMinutes: entry.durationMinutes,
170
- notes: entry.notes ?? null,
321
+ roundedMinutes: roundedMinutesFor(entry.durationMinutes, settings, {
322
+ tenantId,
323
+ organizationId
324
+ }),
325
+ notes: notes ?? null,
326
+ isBillable: resolveTimeEntryBillable({
327
+ requested: entry.isBillable,
328
+ project,
329
+ settings,
330
+ scope: { tenantId, organizationId }
331
+ }),
332
+ rateOverrideAmount: toStoredTimeEntryRateOverride(entry.rateOverrideAmount),
333
+ rateCurrencyCode: project?.currencyCode ?? null,
171
334
  source: "manual",
172
335
  createdAt: now,
173
336
  updatedAt: now
@@ -214,14 +377,23 @@ async function POST(req) {
214
377
  tenantId,
215
378
  organizationId,
216
379
  userId: auth.sub ?? "",
217
- resourceKind: "staff.timesheets.time_entry",
380
+ resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry,
218
381
  resourceId: staffMemberId,
219
382
  operation: "update",
220
383
  requestMethod: req.method,
221
384
  requestHeaders: req.headers
222
385
  });
223
386
  }
224
- return NextResponse.json({ ok: true, ...counts }, { status: 200 });
387
+ void emitStaffEvent("staff.timesheets.time_entry.bulk_updated", {
388
+ tenantId,
389
+ organizationId,
390
+ staffMemberId,
391
+ ...counts,
392
+ entryIds: pendingChanges.map((change) => change.entity.id)
393
+ }, { persistent: true }).catch((err) => {
394
+ logger.error("staff.timesheets emit time_entry.bulk_updated failed", { err });
395
+ });
396
+ return session.respond(200, { ok: true, ...counts });
225
397
  } catch (err) {
226
398
  if (err instanceof CrudHttpError) {
227
399
  return NextResponse.json(err.body, { status: err.status });
@@ -240,7 +412,7 @@ const openApi = {
240
412
  methods: {
241
413
  POST: {
242
414
  summary: "Bulk save time entries",
243
- description: "Creates, updates, or soft-deletes multiple time entries in a single request. Entries with durationMinutes=0 and an existing id are soft-deleted.",
415
+ description: "Creates, updates, or soft-deletes multiple time entries in a single request. Entries with durationMinutes=0 and an existing id are soft-deleted. A row may name a taskId and let timeProjectId follow from it; task, description/notes precedence, isBillable defaulting, the rate override and the project currency snapshot follow the single-entry path exactly. tagIds is rejected with 422 \u2014 tags are edited from the time entry itself so their audit trail and undo stay intact. The whole batch is rejected with 409 time_entry_locked when any row targets \u2014 by id, or by landing on its (project, date) cell \u2014 an entry frozen in a closed report.",
244
416
  requestBody: {
245
417
  contentType: "application/json",
246
418
  schema: staffTimeEntryBulkSaveSchema
@@ -264,6 +436,17 @@ const openApi = {
264
436
  errors: z.array(z.object({ path: z.string(), message: z.string() }))
265
437
  })
266
438
  },
439
+ {
440
+ status: 409,
441
+ description: "One or more rows target a time entry locked in a closed report; nothing was saved",
442
+ schema: z.object({
443
+ code: z.literal("time_entry_locked"),
444
+ error: z.string(),
445
+ lockedReportId: z.string().uuid().nullable(),
446
+ lockedEntryIds: z.array(z.string().uuid()),
447
+ lockedReportIds: z.array(z.string().uuid())
448
+ })
449
+ },
267
450
  { status: 401, description: "Unauthorized", schema: z.object({ error: z.string() }) },
268
451
  { status: 403, description: "Forbidden", schema: z.object({ error: z.string() }) }
269
452
  ]
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/staff/api/timesheets/time-entries/bulk/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { emitCrudSideEffects, flushCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { StaffTimeEntry, StaffTeamMember, StaffTimeProject } from '../../../../data/entities'\nimport { staffTimeEntryBulkSaveSchema } from '../../../../data/validators'\nimport { staffTimeEntryCrudEvents } from '../../../../lib/crud'\nimport { invalidateStaffTimeEntryCache } from '../../../../lib/timesheets/timeEntryCacheInvalidation'\nimport {\n resolveUserFeatures,\n runStaffMutationGuardAfterSuccess,\n runStaffMutationGuards,\n} from '../../../guards'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('staff')\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n}\n\nexport async function POST(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = scope?.tenantId ?? auth.tenantId ?? null\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!tenantId || !organizationId) {\n throw new CrudHttpError(400, { error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.') })\n }\n\n const body = await readJsonSafe(req, {})\n const parsed = staffTimeEntryBulkSaveSchema.safeParse(body)\n if (!parsed.success) {\n const errors = parsed.error.issues.map((issue) => ({\n path: issue.path.join('.'),\n message: issue.message,\n }))\n return NextResponse.json({ ok: false, errors }, { status: 422 })\n }\n\n const { entries } = parsed.data\n\n const em = (container.resolve('em') as EntityManager).fork()\n const scopeCtx = { tenantId, organizationId }\n\n const staffMember = await findOneWithDecryption(\n em,\n StaffTeamMember,\n { userId: auth.sub, tenantId, organizationId, deletedAt: null },\n {},\n scopeCtx,\n )\n if (!staffMember) {\n throw new CrudHttpError(403, { error: translate('staff.timesheets.errors.noStaffMember', 'No staff member linked to your account.') })\n }\n const staffMemberId = staffMember.id\n\n // Validate that all referenced timeProjectIds exist and are in-scope\n const referencedProjectIds = [\n ...new Set(\n entries\n .map((e) => e.timeProjectId)\n .filter((id): id is string => typeof id === 'string' && id.length > 0),\n ),\n ]\n if (referencedProjectIds.length > 0) {\n const validProjects = await em.find(StaffTimeProject, {\n id: { $in: referencedProjectIds },\n tenantId,\n organizationId,\n deletedAt: null,\n }, { fields: ['id'] })\n const validIds = new Set(validProjects.map((p) => p.id))\n const invalidIds = referencedProjectIds.filter((id) => !validIds.has(id))\n if (invalidIds.length > 0) {\n return NextResponse.json(\n {\n ok: false,\n errors: invalidIds.map((id) => ({\n path: 'entries[].timeProjectId',\n message: translate('staff.timesheets.errors.projectNotFound', 'Time project not found or not accessible.'),\n value: id,\n })),\n },\n { status: 422 },\n )\n }\n }\n\n const guardInput = {\n tenantId,\n organizationId,\n userId: auth.sub ?? '',\n resourceKind: 'staff.timesheets.time_entry',\n resourceId: staffMemberId,\n operation: 'update' as const,\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: parsed.data as unknown as Record<string, unknown>,\n }\n const guardResult = await runStaffMutationGuards(container, guardInput, resolveUserFeatures(auth))\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n const existingIds = entries\n .map((entry) => entry.id)\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n\n // Validate referenced entry IDs upfront: a stale or foreign UUID would\n // otherwise fall through to the create branch in the loop below and insert\n // a duplicate row with that ID-less new identity. Reject as 422 instead.\n if (existingIds.length > 0) {\n const resolvedExisting = await em.find(\n StaffTimeEntry,\n { id: { $in: existingIds }, tenantId, organizationId, staffMemberId, deletedAt: null },\n { fields: ['id'] },\n )\n const resolvedIdSet = new Set(resolvedExisting.map((entry) => entry.id))\n const invalidIds = existingIds.filter((id) => !resolvedIdSet.has(id))\n if (invalidIds.length > 0) {\n return NextResponse.json(\n {\n ok: false,\n errors: invalidIds.map((id) => ({\n path: 'entries[].id',\n message: translate(\n 'staff.timesheets.errors.entryNotFound',\n 'Time entry not found, deleted, or not owned by you.',\n ),\n value: id,\n })),\n },\n { status: 422 },\n )\n }\n }\n\n type PendingChange = {\n action: 'created' | 'updated' | 'deleted'\n entity: StaffTimeEntry\n }\n\n const { counts, pendingChanges } = await em.transactional(async (trx) => {\n let created = 0\n let updated = 0\n let deleted = 0\n const changes: PendingChange[] = []\n\n const existingEntries = existingIds.length > 0\n ? await findWithDecryption(\n trx,\n StaffTimeEntry,\n { id: { $in: existingIds }, tenantId, organizationId, staffMemberId, deletedAt: null },\n {},\n scopeCtx,\n )\n : []\n\n const existingMap = new Map(existingEntries.map((entry) => [entry.id, entry]))\n\n for (const entry of entries) {\n if (entry.id && existingMap.has(entry.id)) {\n const existing = existingMap.get(entry.id)!\n if (entry.durationMinutes === 0) {\n existing.deletedAt = new Date()\n deleted++\n changes.push({ action: 'deleted', entity: existing })\n } else {\n existing.date = entry.date\n existing.timeProjectId = entry.timeProjectId\n existing.durationMinutes = entry.durationMinutes\n existing.notes = entry.notes ?? existing.notes\n existing.updatedAt = new Date()\n updated++\n changes.push({ action: 'updated', entity: existing })\n }\n } else {\n const now = new Date()\n const newEntry = trx.create(StaffTimeEntry, {\n tenantId,\n organizationId,\n staffMemberId,\n date: entry.date,\n timeProjectId: entry.timeProjectId,\n durationMinutes: entry.durationMinutes,\n notes: entry.notes ?? null,\n source: 'manual',\n createdAt: now,\n updatedAt: now,\n })\n created++\n changes.push({ action: 'created', entity: newEntry })\n }\n }\n\n await trx.flush()\n return { counts: { created, updated, deleted }, pendingChanges: changes }\n })\n\n const dataEngine = container.resolve<DataEngine>('dataEngine')\n for (const change of pendingChanges) {\n await emitCrudSideEffects({\n dataEngine,\n action: change.action,\n entity: change.entity,\n identifiers: {\n id: change.entity.id,\n organizationId: change.entity.organizationId,\n tenantId: change.entity.tenantId,\n },\n events: staffTimeEntryCrudEvents,\n })\n }\n await flushCrudSideEffects(dataEngine)\n\n const invalidatedRecordIds = new Set<string>()\n for (const change of pendingChanges) {\n if (invalidatedRecordIds.has(change.entity.id)) continue\n invalidatedRecordIds.add(change.entity.id)\n await invalidateStaffTimeEntryCache(\n container,\n {\n id: change.entity.id,\n organizationId: change.entity.organizationId,\n tenantId: change.entity.tenantId,\n },\n tenantId,\n `bulk:${change.action}`,\n )\n }\n\n if (guardResult.afterSuccessCallbacks.length) {\n await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId,\n organizationId,\n userId: auth.sub ?? '',\n resourceKind: 'staff.timesheets.time_entry',\n resourceId: staffMemberId,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n }\n\n return NextResponse.json({ ok: true, ...counts }, { status: 200 })\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.timesheets.time-entries.bulk failed', { err })\n return NextResponse.json(\n { error: translate('staff.timesheets.errors.bulkSave', 'Failed to bulk save time entries.') },\n { status: 400 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Bulk save time entries',\n methods: {\n POST: {\n summary: 'Bulk save time entries',\n description: 'Creates, updates, or soft-deletes multiple time entries in a single request. Entries with durationMinutes=0 and an existing id are soft-deleted.',\n requestBody: {\n contentType: 'application/json',\n schema: staffTimeEntryBulkSaveSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Bulk save completed',\n schema: z.object({\n ok: z.literal(true),\n created: z.number(),\n updated: z.number(),\n deleted: z.number(),\n }),\n },\n {\n status: 422,\n description: 'Validation error',\n schema: z.object({\n ok: z.literal(false),\n errors: z.array(z.object({ path: z.string(), message: z.string() })),\n }),\n },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 403, description: 'Forbidden', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport { emitCrudSideEffects, flushCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { StaffTimeEntry, StaffTeamMember, StaffTimeProject, StaffTimeTask } from '../../../../data/entities'\nimport {\n staffTimeEntryBulkSaveSchema,\n type StaffTimeEntryBulkSaveInput,\n} from '../../../../data/validators'\nimport {\n buildTimeEntryLockedError,\n rebaseTimeEntryIntervalToDate,\n reconcileTimeEntryInterval,\n resolveTimeEntryBillable,\n resolveTimeEntryNotesInput,\n resolveTimeEntryProjectId,\n resolveTimeEntrySettings,\n roundedMinutesFor,\n timeEntryTaskMatchesProject,\n toStoredTimeEntryRateOverride,\n type LockedTimeEntryRef,\n} from '../../../../commands/timesheets-entries'\nimport { staffTimeEntryCrudEvents } from '../../../../lib/crud'\nimport { emitStaffEvent } from '../../../../events'\nimport { invalidateStaffTimeEntryCache } from '../../../../lib/timesheets/timeEntryCacheInvalidation'\nimport {\n STAFF_TIME_TRACKING_RESOURCE_KINDS,\n runStaffMutationGuardAfterSuccess,\n runStaffMutationGuards,\n} from '../../../guards'\nimport { runTimesheetInterceptors } from '../../_shared/withTimesheetInterceptors'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('staff')\n\n/**\n * A grid cell is addressed by the day the work started, never by the timestamp,\n * so both sides of the comparison collapse to `YYYY-MM-DD` (D-8).\n */\nfunction cellDateKey(value: Date | string | null | undefined): string {\n if (!value) return ''\n const date = value instanceof Date ? value : new Date(value)\n return Number.isNaN(date.getTime()) ? String(value) : date.toISOString().slice(0, 10)\n}\n\ntype BulkRowError = { path: string; message: string; value?: unknown }\n\n/**\n * The batch answers with a per-row error list rather than a thrown 422: one grid\n * save spans a week of cells, so the client needs to know WHICH row it must fix.\n * The single-entry command raises the same refusals as `CrudHttpError` field\n * errors \u2014 same rule, different reporting shape.\n */\nfunction bulkValidationError(errors: BulkRowError[]): NextResponse {\n return NextResponse.json({ ok: false, errors }, { status: 422 })\n}\n\n/**\n * A row as the write loop needs it: the payload plus the two things that must be\n * settled before the lock gate can address a cell \u2014 the task it names (validated\n * in scope) and the project the entry actually lands on, which a task-only row\n * inherits from its task exactly as the single-entry path does.\n */\ntype BulkEntryInput = StaffTimeEntryBulkSaveInput['entries'][number]\n\ntype ResolvedBulkRow = {\n entry: BulkEntryInput\n task: { id: string; timeProjectId: string } | null\n timeProjectId: string\n}\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n}\n\nexport async function POST(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = scope?.tenantId ?? auth.tenantId ?? null\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!tenantId || !organizationId) {\n throw new CrudHttpError(400, { error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.') })\n }\n\n const interceptors = await runTimesheetInterceptors({\n request: req,\n method: 'POST',\n scope: { container, userId: auth.sub, tenantId, organizationId },\n body: await readJsonSafe<Record<string, unknown>>(req, {}),\n })\n if (!interceptors.ok) return interceptors.response\n const { session } = interceptors\n\n const parsed = staffTimeEntryBulkSaveSchema.safeParse(session.body)\n if (!parsed.success) {\n const errors = parsed.error.issues.map((issue) => ({\n path: issue.path.join('.'),\n message: issue.message,\n }))\n return NextResponse.json({ ok: false, errors }, { status: 422 })\n }\n\n const { entries } = parsed.data\n\n // Tags are REFUSED here rather than accepted and dropped.\n //\n // The single-entry path assigns tags by dispatching the tag commands, which\n // fork their own EntityManager so they can carry their own audit entry, undo\n // and lock check. Inside this route's `em.transactional` that fork would not\n // see the rows the transaction has not committed yet, and dispatching AFTER\n // the commit would answer `{ ok: true, created: n }` for a batch whose tags\n // silently failed \u2014 a response carrying only counts cannot say which cell\n // lost them, which is the very silent-success class this route is being\n // fixed for. So the row is rejected and the user is pointed at the entry\n // dialog, which writes tags through the commands with all of their\n // guarantees intact.\n const taggedRows = entries.filter((entry) => entry.tagIds !== undefined)\n if (taggedRows.length > 0) {\n const message = translate(\n 'staff.timesheets.errors.bulkTagsUnsupported',\n 'Tags cannot be changed from the timesheet grid. Open the time entry to edit its tags.',\n )\n return bulkValidationError(taggedRows.map(() => ({ path: 'entries[].tagIds', message })))\n }\n\n const em = (container.resolve('em') as EntityManager).fork()\n const scopeCtx = { tenantId, organizationId }\n\n const staffMember = await findOneWithDecryption(\n em,\n StaffTeamMember,\n { userId: auth.sub, tenantId, organizationId, deletedAt: null },\n {},\n scopeCtx,\n )\n if (!staffMember) {\n throw new CrudHttpError(403, { error: translate('staff.timesheets.errors.noStaffMember', 'No staff member linked to your account.') })\n }\n const staffMemberId = staffMember.id\n\n // Validate that all referenced taskIds exist and are in-scope, in one query\n // for the whole batch. Same rule the single-entry path applies through\n // `requireTaskInScope`: a dangling task reference is refused rather than\n // stored, because the task rollups aggregate entries by `task_id` and a stale\n // or foreign UUID would silently move hours onto another project's card.\n const referencedTaskIds = [\n ...new Set(\n entries\n .map((entry) => entry.taskId)\n .filter((id): id is string => typeof id === 'string' && id.length > 0),\n ),\n ]\n const tasksById = new Map<string, { id: string; timeProjectId: string }>()\n if (referencedTaskIds.length > 0) {\n const validTasks = await em.find(StaffTimeTask, {\n id: { $in: referencedTaskIds },\n tenantId,\n organizationId,\n deletedAt: null,\n }, { fields: ['id', 'timeProjectId'] })\n for (const task of validTasks) tasksById.set(task.id, { id: task.id, timeProjectId: task.timeProjectId })\n const invalidTaskIds = referencedTaskIds.filter((id) => !tasksById.has(id))\n if (invalidTaskIds.length > 0) {\n const message = translate('staff.timesheets.errors.taskNotFound', 'Task not found or not accessible.')\n return bulkValidationError(\n invalidTaskIds.map((id) => ({ path: 'entries[].taskId', message, value: id })),\n )\n }\n }\n\n // Every row is pinned to the project it will actually land on BEFORE the lock\n // gate runs, because the gate addresses a cell by (project, date) \u2014 a task row\n // that inherited its project must be checked against that inherited cell, not\n // against a blank one.\n const resolvedRows: ResolvedBulkRow[] = []\n const missingProjectErrors: BulkRowError[] = []\n const taskProjectMismatchErrors: BulkRowError[] = []\n for (const entry of entries) {\n const task = entry.taskId ? tasksById.get(entry.taskId) ?? null : null\n const timeProjectId = resolveTimeEntryProjectId(entry.timeProjectId, task)\n if (!timeProjectId) {\n missingProjectErrors.push({\n path: 'entries[].timeProjectId',\n message: translate('staff.timesheets.errors.projectRequired', 'Time project id is required.'),\n })\n continue\n }\n if (!timeEntryTaskMatchesProject(task, timeProjectId)) {\n taskProjectMismatchErrors.push({\n path: 'entries[].taskId',\n message: translate('staff.timesheets.errors.taskNotFound', 'Task not found or not accessible.'),\n value: entry.taskId,\n })\n continue\n }\n resolvedRows.push({ entry, task, timeProjectId })\n }\n if (missingProjectErrors.length > 0 || taskProjectMismatchErrors.length > 0) {\n return bulkValidationError([...missingProjectErrors, ...taskProjectMismatchErrors])\n }\n\n // Validate that all referenced timeProjectIds exist and are in-scope. The\n // project rows are kept, not just their ids: a created entry snapshots the\n // project's currency and inherits its billable default (D-3), so one query\n // serves validation and both defaults.\n const referencedProjectIds = [...new Set(resolvedRows.map((row) => row.timeProjectId))]\n const projectsById = new Map<string, { currencyCode?: string | null; billableByDefault?: boolean | null }>()\n if (referencedProjectIds.length > 0) {\n const validProjects = await em.find(StaffTimeProject, {\n id: { $in: referencedProjectIds },\n tenantId,\n organizationId,\n deletedAt: null,\n }, { fields: ['id', 'currencyCode', 'billableByDefault'] })\n for (const project of validProjects) {\n projectsById.set(project.id, {\n currencyCode: project.currencyCode ?? null,\n billableByDefault: project.billableByDefault ?? null,\n })\n }\n const invalidIds = referencedProjectIds.filter((id) => !projectsById.has(id))\n if (invalidIds.length > 0) {\n const message = translate(\n 'staff.timesheets.errors.projectNotFound',\n 'Time project not found or not accessible.',\n )\n return bulkValidationError(\n invalidIds.map((id) => ({ path: 'entries[].timeProjectId', message, value: id })),\n )\n }\n }\n\n const guardInput = {\n tenantId,\n organizationId,\n userId: auth.sub ?? '',\n resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry,\n resourceId: staffMemberId,\n operation: 'update' as const,\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: parsed.data as unknown as Record<string, unknown>,\n }\n const guardResult = await runStaffMutationGuards(container, guardInput)\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n const existingIds = resolvedRows\n .map((row) => row.entry.id)\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n\n // Validate referenced entry IDs upfront: a stale or foreign UUID would\n // otherwise fall through to the create branch in the loop below and insert\n // a duplicate row with that ID-less new identity. Reject as 422 instead.\n if (existingIds.length > 0) {\n const resolvedExisting = await em.find(\n StaffTimeEntry,\n { id: { $in: existingIds }, tenantId, organizationId, staffMemberId, deletedAt: null },\n { fields: ['id'] },\n )\n const resolvedIdSet = new Set(resolvedExisting.map((entry) => entry.id))\n const invalidIds = existingIds.filter((id) => !resolvedIdSet.has(id))\n if (invalidIds.length > 0) {\n return NextResponse.json(\n {\n ok: false,\n errors: invalidIds.map((id) => ({\n path: 'entries[].id',\n message: translate(\n 'staff.timesheets.errors.entryNotFound',\n 'Time entry not found, deleted, or not owned by you.',\n ),\n value: id,\n })),\n },\n { status: 422 },\n )\n }\n }\n\n // --- Lock gate (risk R3) -------------------------------------------------\n //\n // This is the endpoint the lock is most likely to leak through, because the\n // grid addresses a cell by (project, date) and only carries an id when the\n // client happened to have one loaded. So both shapes are checked:\n //\n // * a row naming an id that resolves to a frozen entry, and\n // * an id-less row landing on a (project, date) cell a frozen entry already\n // occupies \u2014 the grid renders one summed value per cell, so creating a\n // second row there silently restates hours a closed report has billed.\n //\n // The batch fails WHOLESALE, before the transaction opens. A grid save is one\n // user gesture spanning a week of cells and the response carries only counts,\n // so applying the unlocked rows would leave the client unable to tell which\n // cells landed \u2014 and the route already rejects the whole payload for an\n // unknown project or a stale entry id, so a partial 409 would be the odd one\n // out. The 409 names every offending id and the reports that froze them, so\n // the grid can mark exactly those cells read-only and the user retries the\n // rest after reloading (which is also what makes the lock badge appear).\n //\n // The cell is addressed by the RESOLVED project, so a task row that inherited\n // its project is checked against the cell it will really occupy.\n const idlessRows = resolvedRows.filter((row) => !row.entry.id)\n const idlessCells = idlessRows.map((row) => `${cellDateKey(row.entry.date)}|${row.timeProjectId}`)\n const lockConditions: Record<string, unknown>[] = []\n if (existingIds.length > 0) lockConditions.push({ id: { $in: existingIds } })\n if (idlessCells.length > 0) {\n lockConditions.push({\n date: { $in: idlessRows.map((row) => row.entry.date) },\n timeProjectId: { $in: [...new Set(idlessRows.map((row) => row.timeProjectId))] },\n })\n }\n if (lockConditions.length > 0) {\n const existingIdSet = new Set(existingIds)\n const idlessCellSet = new Set(idlessCells)\n const lockedCandidates = await em.find(\n StaffTimeEntry,\n {\n tenantId,\n organizationId,\n staffMemberId,\n deletedAt: null,\n lockedReportId: { $ne: null },\n $or: lockConditions,\n },\n { fields: ['id', 'lockedReportId', 'date', 'timeProjectId'] },\n )\n // The (date \u00D7 project) condition above is a cross product, so it can match a\n // locked entry on a cell the payload never touches \u2014 narrowed here.\n const blocked: LockedTimeEntryRef[] = lockedCandidates.filter(\n (row) =>\n existingIdSet.has(row.id) ||\n idlessCellSet.has(`${cellDateKey(row.date)}|${row.timeProjectId ?? ''}`),\n )\n if (blocked.length > 0) {\n throw buildTimeEntryLockedError(\n blocked.map((row) => ({ id: row.id, lockedReportId: row.lockedReportId })),\n translate,\n )\n }\n }\n\n type PendingChange = {\n action: 'created' | 'updated' | 'deleted'\n entity: StaffTimeEntry\n }\n\n // D-7 makes `rounded_minutes` the only input to cost, and this route writes\n // durations outside the entries command \u2014 so every row it touches is rounded\n // with the same tenant rule the command uses. The settings read is hoisted out\n // of the loop because it is tenant-scoped and identical for every row; the\n // billable default a created row falls back to comes from the same snapshot.\n const settings = await resolveTimeEntrySettings(container, tenantId)\n\n const { counts, pendingChanges } = await em.transactional(async (trx) => {\n let created = 0\n let updated = 0\n let deleted = 0\n const changes: PendingChange[] = []\n\n const existingEntries = existingIds.length > 0\n ? await findWithDecryption(\n trx,\n StaffTimeEntry,\n { id: { $in: existingIds }, tenantId, organizationId, staffMemberId, deletedAt: null },\n {},\n scopeCtx,\n )\n : []\n\n const existingMap = new Map(existingEntries.map((entry) => [entry.id, entry]))\n\n for (const { entry, task, timeProjectId } of resolvedRows) {\n const project = projectsById.get(timeProjectId) ?? null\n // Both branches read the five fields the schema has always accepted\n // through the SAME helpers the single-entry command uses, so a grid write\n // and a dialog write cannot settle them differently (#silent-no-op).\n const notes = resolveTimeEntryNotesInput(entry)\n if (entry.id && existingMap.has(entry.id)) {\n const existing = existingMap.get(entry.id)!\n if (entry.durationMinutes === 0) {\n existing.deletedAt = new Date()\n deleted++\n changes.push({ action: 'deleted', entity: existing })\n } else {\n // T4.10(a). A grid cell carries a duration and nothing else, so writing\n // it straight onto an entry that has clocks would store a two-hour\n // duration against a 09:00\u201310:00 span \u2014 exactly the contradiction the\n // single-entry path already refuses to store. The same reconciliation\n // is called here rather than re-derived: only-a-duration means\n // `started_at` anchors and `ended_at` shifts to match, a clock that\n // does not exist is never manufactured (so a cell whose entry has no\n // clocks keeps having none, and a running timer is not stopped), and a\n // re-dated row takes its clocks with it.\n const interval = reconcileTimeEntryInterval({\n stored: rebaseTimeEntryIntervalToDate({\n stored: {\n startedAt: existing.startedAt ?? null,\n endedAt: existing.endedAt ?? null,\n durationMinutes: existing.durationMinutes,\n },\n fromDate: existing.date,\n toDate: entry.date,\n }),\n startedAt: undefined,\n endedAt: undefined,\n durationMinutes: entry.durationMinutes,\n })\n const projectChanged = timeProjectId !== (existing.timeProjectId ?? null)\n existing.date = entry.date\n existing.timeProjectId = timeProjectId\n // Snapshotted, never joined at read time, so a later project currency\n // change cannot re-denominate money already reported (D-3) \u2014 and only\n // restated when the entry actually moves between projects, exactly as\n // the single-entry update command does it.\n if (projectChanged) existing.rateCurrencyCode = project?.currencyCode ?? null\n existing.startedAt = interval.startedAt\n existing.endedAt = interval.endedAt\n existing.durationMinutes = interval.durationMinutes\n // D-7 keeps `rounded_minutes` the only input to cost, so it is restated\n // from the effective duration the reconciliation settled on.\n existing.roundedMinutes = roundedMinutesFor(interval.durationMinutes, settings, {\n tenantId,\n organizationId,\n })\n if (entry.taskId !== undefined) existing.taskId = task?.id ?? null\n if (entry.isBillable !== undefined) existing.isBillable = entry.isBillable\n if (entry.rateOverrideAmount !== undefined) {\n existing.rateOverrideAmount = toStoredTimeEntryRateOverride(entry.rateOverrideAmount)\n }\n if (notes !== undefined) existing.notes = notes\n existing.updatedAt = new Date()\n updated++\n changes.push({ action: 'updated', entity: existing })\n }\n } else {\n const now = new Date()\n const newEntry = trx.create(StaffTimeEntry, {\n tenantId,\n organizationId,\n staffMemberId,\n date: entry.date,\n timeProjectId,\n taskId: task?.id ?? null,\n durationMinutes: entry.durationMinutes,\n roundedMinutes: roundedMinutesFor(entry.durationMinutes, settings, {\n tenantId,\n organizationId,\n }),\n notes: notes ?? null,\n isBillable: resolveTimeEntryBillable({\n requested: entry.isBillable,\n project,\n settings,\n scope: { tenantId, organizationId },\n }),\n rateOverrideAmount: toStoredTimeEntryRateOverride(entry.rateOverrideAmount),\n rateCurrencyCode: project?.currencyCode ?? null,\n source: 'manual',\n createdAt: now,\n updatedAt: now,\n })\n created++\n changes.push({ action: 'created', entity: newEntry })\n }\n }\n\n await trx.flush()\n return { counts: { created, updated, deleted }, pendingChanges: changes }\n })\n\n const dataEngine = container.resolve<DataEngine>('dataEngine')\n for (const change of pendingChanges) {\n await emitCrudSideEffects({\n dataEngine,\n action: change.action,\n entity: change.entity,\n identifiers: {\n id: change.entity.id,\n organizationId: change.entity.organizationId,\n tenantId: change.entity.tenantId,\n },\n events: staffTimeEntryCrudEvents,\n })\n }\n await flushCrudSideEffects(dataEngine)\n\n const invalidatedRecordIds = new Set<string>()\n for (const change of pendingChanges) {\n if (invalidatedRecordIds.has(change.entity.id)) continue\n invalidatedRecordIds.add(change.entity.id)\n await invalidateStaffTimeEntryCache(\n container,\n {\n id: change.entity.id,\n organizationId: change.entity.organizationId,\n tenantId: change.entity.tenantId,\n },\n tenantId,\n `bulk:${change.action}`,\n )\n }\n\n if (guardResult.afterSuccessCallbacks.length) {\n await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId,\n organizationId,\n userId: auth.sub ?? '',\n resourceKind: STAFF_TIME_TRACKING_RESOURCE_KINDS.timeEntry,\n resourceId: staffMemberId,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n }\n\n // One batch-level event beside the per-row CRUD events already flushed above.\n // It carries no `id`, so the entry-scoped subscribers that key off one record\n // skip it and only the batch-aware ones (payroll exports, audit mirrors) react.\n void emitStaffEvent('staff.timesheets.time_entry.bulk_updated', {\n tenantId,\n organizationId,\n staffMemberId,\n ...counts,\n entryIds: pendingChanges.map((change) => change.entity.id),\n }, { persistent: true }).catch((err) => {\n logger.error('staff.timesheets emit time_entry.bulk_updated failed', { err })\n })\n\n return session.respond(200, { ok: true, ...counts })\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.timesheets.time-entries.bulk failed', { err })\n return NextResponse.json(\n { error: translate('staff.timesheets.errors.bulkSave', 'Failed to bulk save time entries.') },\n { status: 400 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Bulk save time entries',\n methods: {\n POST: {\n summary: 'Bulk save time entries',\n description:\n 'Creates, updates, or soft-deletes multiple time entries in a single request. Entries with durationMinutes=0 and an existing id are soft-deleted. A row may name a taskId and let timeProjectId follow from it; task, description/notes precedence, isBillable defaulting, the rate override and the project currency snapshot follow the single-entry path exactly. tagIds is rejected with 422 \u2014 tags are edited from the time entry itself so their audit trail and undo stay intact. The whole batch is rejected with 409 time_entry_locked when any row targets \u2014 by id, or by landing on its (project, date) cell \u2014 an entry frozen in a closed report.',\n requestBody: {\n contentType: 'application/json',\n schema: staffTimeEntryBulkSaveSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Bulk save completed',\n schema: z.object({\n ok: z.literal(true),\n created: z.number(),\n updated: z.number(),\n deleted: z.number(),\n }),\n },\n {\n status: 422,\n description: 'Validation error',\n schema: z.object({\n ok: z.literal(false),\n errors: z.array(z.object({ path: z.string(), message: z.string() })),\n }),\n },\n {\n status: 409,\n description: 'One or more rows target a time entry locked in a closed report; nothing was saved',\n schema: z.object({\n code: z.literal('time_entry_locked'),\n error: z.string(),\n lockedReportId: z.string().uuid().nullable(),\n lockedEntryIds: z.array(z.string().uuid()),\n lockedReportIds: z.array(z.string().uuid()),\n }),\n },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 403, description: 'Forbidden', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }