@open-mercato/core 0.7.1-develop.7178.1.ab93fbaf4e → 0.7.1-develop.7180.1.9717fbbb43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (808) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/staff_time_entry/index.js +14 -0
  3. package/dist/generated/entities/staff_time_entry/index.js.map +2 -2
  4. package/dist/generated/entities/staff_time_entry_tag/index.js +15 -0
  5. package/dist/generated/entities/staff_time_entry_tag/index.js.map +7 -0
  6. package/dist/generated/entities/staff_time_project/index.js +16 -0
  7. package/dist/generated/entities/staff_time_project/index.js.map +2 -2
  8. package/dist/generated/entities/staff_time_report/index.js +57 -0
  9. package/dist/generated/entities/staff_time_report/index.js.map +7 -0
  10. package/dist/generated/entities/staff_time_report_entry/index.js +27 -0
  11. package/dist/generated/entities/staff_time_report_entry/index.js.map +7 -0
  12. package/dist/generated/entities/staff_time_report_event/index.js +21 -0
  13. package/dist/generated/entities/staff_time_report_event/index.js.map +7 -0
  14. package/dist/generated/entities/staff_time_report_project/index.js +15 -0
  15. package/dist/generated/entities/staff_time_report_project/index.js.map +7 -0
  16. package/dist/generated/entities/staff_time_tag/index.js +21 -0
  17. package/dist/generated/entities/staff_time_tag/index.js.map +7 -0
  18. package/dist/generated/entities/staff_time_task/index.js +37 -0
  19. package/dist/generated/entities/staff_time_task/index.js.map +7 -0
  20. package/dist/generated/entities/staff_time_task_comment/index.js +21 -0
  21. package/dist/generated/entities/staff_time_task_comment/index.js.map +7 -0
  22. package/dist/generated/entities/staff_time_task_status/index.js +29 -0
  23. package/dist/generated/entities/staff_time_task_status/index.js.map +7 -0
  24. package/dist/generated/entities/staff_time_task_tag/index.js +15 -0
  25. package/dist/generated/entities/staff_time_task_tag/index.js.map +7 -0
  26. package/dist/generated/entities.ids.generated.js +11 -1
  27. package/dist/generated/entities.ids.generated.js.map +2 -2
  28. package/dist/generated/entity-fields-registry.js +149 -0
  29. package/dist/generated/entity-fields-registry.js.map +2 -2
  30. package/dist/helpers/integration/timesheetFixtures.js +7 -0
  31. package/dist/helpers/integration/timesheetFixtures.js.map +2 -2
  32. package/dist/modules/auth/cli.js +16 -0
  33. package/dist/modules/auth/cli.js.map +2 -2
  34. package/dist/modules/auth/lib/backendChrome.js +1 -0
  35. package/dist/modules/auth/lib/backendChrome.js.map +2 -2
  36. package/dist/modules/customer_accounts/lib/customerRoleAcls.js +44 -0
  37. package/dist/modules/customer_accounts/lib/customerRoleAcls.js.map +7 -0
  38. package/dist/modules/customer_accounts/setup.js +1 -28
  39. package/dist/modules/customer_accounts/setup.js.map +2 -2
  40. package/dist/modules/data_sync/api/options.js +2 -0
  41. package/dist/modules/data_sync/api/options.js.map +2 -2
  42. package/dist/modules/data_sync/backend/data-sync/page.js +39 -17
  43. package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
  44. package/dist/modules/data_sync/lib/start-controls.js +42 -0
  45. package/dist/modules/data_sync/lib/start-controls.js.map +7 -0
  46. package/dist/modules/staff/acl.js +9 -1
  47. package/dist/modules/staff/acl.js.map +2 -2
  48. package/dist/modules/staff/ai-agents.js +142 -0
  49. package/dist/modules/staff/ai-agents.js.map +7 -0
  50. package/dist/modules/staff/ai-tools/time-tracking-pack.js +348 -0
  51. package/dist/modules/staff/ai-tools/time-tracking-pack.js.map +7 -0
  52. package/dist/modules/staff/ai-tools/types.js +54 -0
  53. package/dist/modules/staff/ai-tools/types.js.map +7 -0
  54. package/dist/modules/staff/ai-tools.js +8 -0
  55. package/dist/modules/staff/ai-tools.js.map +7 -0
  56. package/dist/modules/staff/analytics.js +84 -1
  57. package/dist/modules/staff/analytics.js.map +2 -2
  58. package/dist/modules/staff/api/guards.js +37 -3
  59. package/dist/modules/staff/api/guards.js.map +2 -2
  60. package/dist/modules/staff/api/leave-requests/accept/route.js +1 -3
  61. package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
  62. package/dist/modules/staff/api/leave-requests/reject/route.js +1 -3
  63. package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
  64. package/dist/modules/staff/api/portal/time-reports/[id]/route.js +131 -0
  65. package/dist/modules/staff/api/portal/time-reports/[id]/route.js.map +7 -0
  66. package/dist/modules/staff/api/portal/time-reports/route.js +179 -0
  67. package/dist/modules/staff/api/portal/time-reports/route.js.map +7 -0
  68. package/dist/modules/staff/api/team-members/self/route.js +1 -3
  69. package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
  70. package/dist/modules/staff/api/team-members/tags/assign/route.js +1 -3
  71. package/dist/modules/staff/api/team-members/tags/assign/route.js.map +2 -2
  72. package/dist/modules/staff/api/team-members/tags/unassign/route.js +1 -3
  73. package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
  74. package/dist/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.js +174 -0
  75. package/dist/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.js.map +7 -0
  76. package/dist/modules/staff/api/timesheets/access-requests/route.js +226 -0
  77. package/dist/modules/staff/api/timesheets/access-requests/route.js.map +7 -0
  78. package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js +15 -8
  79. package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js.map +2 -2
  80. package/dist/modules/staff/api/timesheets/my-projects/route.js +13 -1
  81. package/dist/modules/staff/api/timesheets/my-projects/route.js.map +2 -2
  82. package/dist/modules/staff/api/timesheets/my-work/myWorkAggregate.js +42 -0
  83. package/dist/modules/staff/api/timesheets/my-work/myWorkAggregate.js.map +7 -0
  84. package/dist/modules/staff/api/timesheets/my-work/route.js +383 -0
  85. package/dist/modules/staff/api/timesheets/my-work/route.js.map +7 -0
  86. package/dist/modules/staff/api/timesheets/projects/kpis/route.js +14 -2
  87. package/dist/modules/staff/api/timesheets/projects/kpis/route.js.map +2 -2
  88. package/dist/modules/staff/api/timesheets/reports/[id]/close/route.js +168 -0
  89. package/dist/modules/staff/api/timesheets/reports/[id]/close/route.js.map +7 -0
  90. package/dist/modules/staff/api/timesheets/reports/[id]/export/route.js +246 -0
  91. package/dist/modules/staff/api/timesheets/reports/[id]/export/route.js.map +7 -0
  92. package/dist/modules/staff/api/timesheets/reports/[id]/sheet/route.js +183 -0
  93. package/dist/modules/staff/api/timesheets/reports/[id]/sheet/route.js.map +7 -0
  94. package/dist/modules/staff/api/timesheets/reports/[id]/unlock/route.js +164 -0
  95. package/dist/modules/staff/api/timesheets/reports/[id]/unlock/route.js.map +7 -0
  96. package/dist/modules/staff/api/timesheets/reports/preview/route.js +360 -0
  97. package/dist/modules/staff/api/timesheets/reports/preview/route.js.map +7 -0
  98. package/dist/modules/staff/api/timesheets/reports/route.js +316 -0
  99. package/dist/modules/staff/api/timesheets/reports/route.js.map +7 -0
  100. package/dist/modules/staff/api/timesheets/reports/shared.js +63 -0
  101. package/dist/modules/staff/api/timesheets/reports/shared.js.map +7 -0
  102. package/dist/modules/staff/api/timesheets/settings/reapply-rounding/route.js +164 -0
  103. package/dist/modules/staff/api/timesheets/settings/reapply-rounding/route.js.map +7 -0
  104. package/dist/modules/staff/api/timesheets/settings/rounding-impact/route.js +168 -0
  105. package/dist/modules/staff/api/timesheets/settings/rounding-impact/route.js.map +7 -0
  106. package/dist/modules/staff/api/timesheets/settings/route.js +199 -0
  107. package/dist/modules/staff/api/timesheets/settings/route.js.map +7 -0
  108. package/dist/modules/staff/api/timesheets/tags/access.js +76 -0
  109. package/dist/modules/staff/api/timesheets/tags/access.js.map +7 -0
  110. package/dist/modules/staff/api/timesheets/tags/entry-assignments/route.js +235 -0
  111. package/dist/modules/staff/api/timesheets/tags/entry-assignments/route.js.map +7 -0
  112. package/dist/modules/staff/api/timesheets/tags/route.js +160 -0
  113. package/dist/modules/staff/api/timesheets/tags/route.js.map +7 -0
  114. package/dist/modules/staff/api/timesheets/tags/task-assignments/route.js +233 -0
  115. package/dist/modules/staff/api/timesheets/tags/task-assignments/route.js.map +7 -0
  116. package/dist/modules/staff/api/timesheets/task-statuses/route.js +188 -0
  117. package/dist/modules/staff/api/timesheets/task-statuses/route.js.map +7 -0
  118. package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js +399 -0
  119. package/dist/modules/staff/api/timesheets/tasks/[id]/comments/route.js.map +7 -0
  120. package/dist/modules/staff/api/timesheets/tasks/[id]/status/route.js +210 -0
  121. package/dist/modules/staff/api/timesheets/tasks/[id]/status/route.js.map +7 -0
  122. package/dist/modules/staff/api/timesheets/tasks/route.js +333 -0
  123. package/dist/modules/staff/api/timesheets/tasks/route.js.map +7 -0
  124. package/dist/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.js +228 -0
  125. package/dist/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.js.map +7 -0
  126. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js +29 -10
  127. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js.map +2 -2
  128. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js +31 -18
  129. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js.map +2 -2
  130. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +21 -7
  131. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +2 -2
  132. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js +27 -7
  133. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js.map +2 -2
  134. package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js +215 -32
  135. package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js.map +2 -2
  136. package/dist/modules/staff/api/timesheets/time-entries/copy-day/route.js +378 -0
  137. package/dist/modules/staff/api/timesheets/time-entries/copy-day/route.js.map +7 -0
  138. package/dist/modules/staff/api/timesheets/time-entries/overlaps/route.js +340 -0
  139. package/dist/modules/staff/api/timesheets/time-entries/overlaps/route.js.map +7 -0
  140. package/dist/modules/staff/api/timesheets/time-entries/route.js +238 -26
  141. package/dist/modules/staff/api/timesheets/time-entries/route.js.map +2 -2
  142. package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js +23 -8
  143. package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js.map +2 -2
  144. package/dist/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.js +209 -0
  145. package/dist/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.js.map +7 -0
  146. package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js +59 -2
  147. package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js.map +2 -2
  148. package/dist/modules/staff/api/timesheets/time-projects/route.js +264 -22
  149. package/dist/modules/staff/api/timesheets/time-projects/route.js.map +2 -2
  150. package/dist/modules/staff/backend/staff/time-tracking/board/page.js +110 -0
  151. package/dist/modules/staff/backend/staff/time-tracking/board/page.js.map +7 -0
  152. package/dist/modules/staff/backend/staff/time-tracking/board/page.meta.js +18 -0
  153. package/dist/modules/staff/backend/staff/time-tracking/board/page.meta.js.map +7 -0
  154. package/dist/modules/staff/backend/staff/time-tracking/entries/page.js +981 -0
  155. package/dist/modules/staff/backend/staff/time-tracking/entries/page.js.map +7 -0
  156. package/dist/modules/staff/backend/staff/time-tracking/entries/page.meta.js +18 -0
  157. package/dist/modules/staff/backend/staff/time-tracking/entries/page.meta.js.map +7 -0
  158. package/dist/modules/staff/backend/staff/time-tracking/page.js +583 -0
  159. package/dist/modules/staff/backend/staff/time-tracking/page.js.map +7 -0
  160. package/dist/modules/staff/backend/staff/time-tracking/page.meta.js +15 -0
  161. package/dist/modules/staff/backend/staff/time-tracking/page.meta.js.map +7 -0
  162. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.js +65 -0
  163. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.js.map +7 -0
  164. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.meta.js +16 -0
  165. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.meta.js.map +7 -0
  166. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js +229 -0
  167. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.js.map +7 -0
  168. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.meta.js +16 -0
  169. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.meta.js.map +7 -0
  170. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +1014 -0
  171. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +7 -0
  172. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.meta.js +16 -0
  173. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.meta.js.map +7 -0
  174. package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.js +97 -0
  175. package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.js.map +7 -0
  176. package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.meta.js +16 -0
  177. package/dist/modules/staff/backend/staff/time-tracking/projects/create/page.meta.js.map +7 -0
  178. package/dist/modules/staff/backend/staff/time-tracking/projects/page.js +1040 -0
  179. package/dist/modules/staff/backend/staff/time-tracking/projects/page.js.map +7 -0
  180. package/dist/modules/staff/backend/staff/time-tracking/projects/page.meta.js +19 -0
  181. package/dist/modules/staff/backend/staff/time-tracking/projects/page.meta.js.map +7 -0
  182. package/dist/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.js +353 -0
  183. package/dist/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.js.map +7 -0
  184. package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.js +457 -0
  185. package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.js.map +7 -0
  186. package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.meta.js +16 -0
  187. package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.meta.js.map +7 -0
  188. package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.js +697 -0
  189. package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.js.map +7 -0
  190. package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.meta.js +16 -0
  191. package/dist/modules/staff/backend/staff/time-tracking/reports/create/page.meta.js.map +7 -0
  192. package/dist/modules/staff/backend/staff/time-tracking/reports/page.js +174 -0
  193. package/dist/modules/staff/backend/staff/time-tracking/reports/page.js.map +7 -0
  194. package/dist/modules/staff/backend/staff/time-tracking/reports/page.meta.js +18 -0
  195. package/dist/modules/staff/backend/staff/time-tracking/reports/page.meta.js.map +7 -0
  196. package/dist/modules/staff/backend/staff/time-tracking/settings/page.js +546 -0
  197. package/dist/modules/staff/backend/staff/time-tracking/settings/page.js.map +7 -0
  198. package/dist/modules/staff/backend/staff/time-tracking/settings/page.meta.js +18 -0
  199. package/dist/modules/staff/backend/staff/time-tracking/settings/page.meta.js.map +7 -0
  200. package/dist/modules/staff/backend/staff/time-tracking/timesheet/GridView.js +710 -0
  201. package/dist/modules/staff/backend/staff/time-tracking/timesheet/GridView.js.map +7 -0
  202. package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.js +646 -0
  203. package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.js.map +7 -0
  204. package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.meta.js +18 -0
  205. package/dist/modules/staff/backend/staff/time-tracking/timesheet/page.meta.js.map +7 -0
  206. package/dist/modules/staff/backend/staff/timesheets/page.js +4 -793
  207. package/dist/modules/staff/backend/staff/timesheets/page.js.map +2 -2
  208. package/dist/modules/staff/backend/staff/timesheets/page.meta.js +2 -7
  209. package/dist/modules/staff/backend/staff/timesheets/page.meta.js.map +2 -2
  210. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.js +5 -136
  211. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.js.map +2 -2
  212. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.js +1 -7
  213. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.js.map +2 -2
  214. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js +5 -445
  215. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js.map +3 -3
  216. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.js +1 -7
  217. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.js.map +2 -2
  218. package/dist/modules/staff/backend/staff/timesheets/projects/create/page.js +4 -76
  219. package/dist/modules/staff/backend/staff/timesheets/projects/create/page.js.map +2 -2
  220. package/dist/modules/staff/backend/staff/timesheets/projects/create/page.meta.js +1 -7
  221. package/dist/modules/staff/backend/staff/timesheets/projects/create/page.meta.js.map +2 -2
  222. package/dist/modules/staff/backend/staff/timesheets/projects/page.js +4 -613
  223. package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
  224. package/dist/modules/staff/backend/staff/timesheets/projects/page.meta.js +2 -17
  225. package/dist/modules/staff/backend/staff/timesheets/projects/page.meta.js.map +2 -2
  226. package/dist/modules/staff/ce.js +40 -0
  227. package/dist/modules/staff/ce.js.map +2 -2
  228. package/dist/modules/staff/cli.js +166 -1
  229. package/dist/modules/staff/cli.js.map +2 -2
  230. package/dist/modules/staff/commands/index.js +5 -0
  231. package/dist/modules/staff/commands/index.js.map +2 -2
  232. package/dist/modules/staff/commands/timesheets-entries.js +569 -21
  233. package/dist/modules/staff/commands/timesheets-entries.js.map +3 -3
  234. package/dist/modules/staff/commands/timesheets-projects.js +450 -7
  235. package/dist/modules/staff/commands/timesheets-projects.js.map +2 -2
  236. package/dist/modules/staff/commands/timesheets-reports.js +1036 -0
  237. package/dist/modules/staff/commands/timesheets-reports.js.map +7 -0
  238. package/dist/modules/staff/commands/timesheets-tags.js +1015 -0
  239. package/dist/modules/staff/commands/timesheets-tags.js.map +7 -0
  240. package/dist/modules/staff/commands/timesheets-task-comments.js +438 -0
  241. package/dist/modules/staff/commands/timesheets-task-comments.js.map +7 -0
  242. package/dist/modules/staff/commands/timesheets-task-statuses.js +723 -0
  243. package/dist/modules/staff/commands/timesheets-task-statuses.js.map +7 -0
  244. package/dist/modules/staff/commands/timesheets-tasks.js +834 -0
  245. package/dist/modules/staff/commands/timesheets-tasks.js.map +7 -0
  246. package/dist/modules/staff/data/enrichers.js +426 -12
  247. package/dist/modules/staff/data/enrichers.js.map +2 -2
  248. package/dist/modules/staff/data/entities.js +552 -3
  249. package/dist/modules/staff/data/entities.js.map +2 -2
  250. package/dist/modules/staff/data/extensions.js +41 -0
  251. package/dist/modules/staff/data/extensions.js.map +7 -0
  252. package/dist/modules/staff/data/validators.js +254 -9
  253. package/dist/modules/staff/data/validators.js.map +2 -2
  254. package/dist/modules/staff/di.js +42 -1
  255. package/dist/modules/staff/di.js.map +2 -2
  256. package/dist/modules/staff/events.js +84 -6
  257. package/dist/modules/staff/events.js.map +2 -2
  258. package/dist/modules/staff/events.payloads.js +158 -0
  259. package/dist/modules/staff/events.payloads.js.map +7 -0
  260. package/dist/modules/staff/extension-points.js +261 -0
  261. package/dist/modules/staff/extension-points.js.map +7 -0
  262. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.js +138 -0
  263. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.js.map +7 -0
  264. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.meta.js +12 -0
  265. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.meta.js.map +7 -0
  266. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.js +133 -0
  267. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.js.map +7 -0
  268. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.meta.js +19 -0
  269. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.meta.js.map +7 -0
  270. package/dist/modules/staff/lib/crud.js +11 -1
  271. package/dist/modules/staff/lib/crud.js.map +2 -2
  272. package/dist/modules/staff/lib/time-tracking/access.js +92 -0
  273. package/dist/modules/staff/lib/time-tracking/access.js.map +7 -0
  274. package/dist/modules/staff/lib/time-tracking/billability.js +39 -0
  275. package/dist/modules/staff/lib/time-tracking/billability.js.map +7 -0
  276. package/dist/modules/staff/lib/time-tracking/capacity.js +68 -0
  277. package/dist/modules/staff/lib/time-tracking/capacity.js.map +7 -0
  278. package/dist/modules/staff/lib/time-tracking/componentContracts.js +15 -0
  279. package/dist/modules/staff/lib/time-tracking/componentContracts.js.map +7 -0
  280. package/dist/modules/staff/lib/time-tracking/cost.js +82 -0
  281. package/dist/modules/staff/lib/time-tracking/cost.js.map +7 -0
  282. package/dist/modules/staff/lib/time-tracking/duration.js +55 -0
  283. package/dist/modules/staff/lib/time-tracking/duration.js.map +7 -0
  284. package/dist/modules/staff/lib/time-tracking/featureAccess.js +41 -0
  285. package/dist/modules/staff/lib/time-tracking/featureAccess.js.map +7 -0
  286. package/dist/modules/staff/lib/time-tracking/interval.js +72 -0
  287. package/dist/modules/staff/lib/time-tracking/interval.js.map +7 -0
  288. package/dist/modules/staff/lib/time-tracking/migrateProjectCodes.js +88 -0
  289. package/dist/modules/staff/lib/time-tracking/migrateProjectCodes.js.map +7 -0
  290. package/dist/modules/staff/lib/time-tracking/moneyVisibility.js +10 -0
  291. package/dist/modules/staff/lib/time-tracking/moneyVisibility.js.map +7 -0
  292. package/dist/modules/staff/lib/time-tracking/overlap.js +87 -0
  293. package/dist/modules/staff/lib/time-tracking/overlap.js.map +7 -0
  294. package/dist/modules/staff/lib/time-tracking/portalRecipients.js +27 -0
  295. package/dist/modules/staff/lib/time-tracking/portalRecipients.js.map +7 -0
  296. package/dist/modules/staff/lib/time-tracking/portalReports.js +16 -0
  297. package/dist/modules/staff/lib/time-tracking/portalReports.js.map +7 -0
  298. package/dist/modules/staff/lib/time-tracking/projectCode.js +135 -0
  299. package/dist/modules/staff/lib/time-tracking/projectCode.js.map +7 -0
  300. package/dist/modules/staff/lib/time-tracking/reapplyRounding.js +187 -0
  301. package/dist/modules/staff/lib/time-tracking/reapplyRounding.js.map +7 -0
  302. package/dist/modules/staff/lib/time-tracking/recalculationRunner.js +80 -0
  303. package/dist/modules/staff/lib/time-tracking/recalculationRunner.js.map +7 -0
  304. package/dist/modules/staff/lib/time-tracking/recalculations.js +56 -0
  305. package/dist/modules/staff/lib/time-tracking/recalculations.js.map +7 -0
  306. package/dist/modules/staff/lib/time-tracking/registries/invoke.js +63 -0
  307. package/dist/modules/staff/lib/time-tracking/registries/invoke.js.map +7 -0
  308. package/dist/modules/staff/lib/time-tracking/registries/registry.js +62 -0
  309. package/dist/modules/staff/lib/time-tracking/registries/registry.js.map +7 -0
  310. package/dist/modules/staff/lib/time-tracking/registries/scope.js +21 -0
  311. package/dist/modules/staff/lib/time-tracking/registries/scope.js.map +7 -0
  312. package/dist/modules/staff/lib/time-tracking/rollup.js +34 -0
  313. package/dist/modules/staff/lib/time-tracking/rollup.js.map +7 -0
  314. package/dist/modules/staff/lib/time-tracking/rounding.js +66 -0
  315. package/dist/modules/staff/lib/time-tracking/rounding.js.map +7 -0
  316. package/dist/modules/staff/lib/time-tracking/roundingImpact.js +39 -0
  317. package/dist/modules/staff/lib/time-tracking/roundingImpact.js.map +7 -0
  318. package/dist/modules/staff/lib/time-tracking/settingKeys.js +214 -0
  319. package/dist/modules/staff/lib/time-tracking/settingKeys.js.map +7 -0
  320. package/dist/modules/staff/lib/time-tracking/settings.js +87 -0
  321. package/dist/modules/staff/lib/time-tracking/settings.js.map +7 -0
  322. package/dist/modules/staff/lib/time-tracking/sqlInClause.js +9 -0
  323. package/dist/modules/staff/lib/time-tracking/sqlInClause.js.map +7 -0
  324. package/dist/modules/staff/lib/time-tracking/timeEntrySources.js +48 -0
  325. package/dist/modules/staff/lib/time-tracking/timeEntrySources.js.map +7 -0
  326. package/dist/modules/staff/lib/time-tracking-ui/BoardFilterChips.js +283 -0
  327. package/dist/modules/staff/lib/time-tracking-ui/BoardFilterChips.js.map +7 -0
  328. package/dist/modules/staff/lib/time-tracking-ui/CustomerPicker.js +324 -0
  329. package/dist/modules/staff/lib/time-tracking-ui/CustomerPicker.js.map +7 -0
  330. package/dist/modules/staff/lib/time-tracking-ui/DurationInput.js +121 -0
  331. package/dist/modules/staff/lib/time-tracking-ui/DurationInput.js.map +7 -0
  332. package/dist/modules/staff/lib/time-tracking-ui/EntryDetailsFields.js +188 -0
  333. package/dist/modules/staff/lib/time-tracking-ui/EntryDetailsFields.js.map +7 -0
  334. package/dist/modules/staff/lib/time-tracking-ui/EntryFilterBar.js +89 -0
  335. package/dist/modules/staff/lib/time-tracking-ui/EntryFilterBar.js.map +7 -0
  336. package/dist/modules/staff/lib/time-tracking-ui/KanbanBoard.js +855 -0
  337. package/dist/modules/staff/lib/time-tracking-ui/KanbanBoard.js.map +7 -0
  338. package/dist/modules/staff/lib/time-tracking-ui/KanbanCard.js +360 -0
  339. package/dist/modules/staff/lib/time-tracking-ui/KanbanCard.js.map +7 -0
  340. package/dist/modules/staff/lib/time-tracking-ui/KanbanColumn.js +259 -0
  341. package/dist/modules/staff/lib/time-tracking-ui/KanbanColumn.js.map +7 -0
  342. package/dist/modules/staff/lib/time-tracking-ui/NewTaskDialog.js +238 -0
  343. package/dist/modules/staff/lib/time-tracking-ui/NewTaskDialog.js.map +7 -0
  344. package/dist/modules/staff/lib/time-tracking-ui/NoProjectAccess.js +130 -0
  345. package/dist/modules/staff/lib/time-tracking-ui/NoProjectAccess.js.map +7 -0
  346. package/dist/modules/staff/lib/time-tracking-ui/PeriodSelector.js +98 -0
  347. package/dist/modules/staff/lib/time-tracking-ui/PeriodSelector.js.map +7 -0
  348. package/dist/modules/staff/lib/time-tracking-ui/PhasePlaceholder.js +27 -0
  349. package/dist/modules/staff/lib/time-tracking-ui/PhasePlaceholder.js.map +7 -0
  350. package/dist/modules/staff/lib/time-tracking-ui/ProjectCodeField.js +166 -0
  351. package/dist/modules/staff/lib/time-tracking-ui/ProjectCodeField.js.map +7 -0
  352. package/dist/modules/staff/lib/time-tracking-ui/ProjectCurrencyField.js +295 -0
  353. package/dist/modules/staff/lib/time-tracking-ui/ProjectCurrencyField.js.map +7 -0
  354. package/dist/modules/staff/lib/time-tracking-ui/ProjectFormSections.js +212 -0
  355. package/dist/modules/staff/lib/time-tracking-ui/ProjectFormSections.js.map +7 -0
  356. package/dist/modules/staff/lib/time-tracking-ui/ProjectTeamDrawer.js +729 -0
  357. package/dist/modules/staff/lib/time-tracking-ui/ProjectTeamDrawer.js.map +7 -0
  358. package/dist/modules/staff/lib/time-tracking-ui/ReportSheet.js +217 -0
  359. package/dist/modules/staff/lib/time-tracking-ui/ReportSheet.js.map +7 -0
  360. package/dist/modules/staff/lib/time-tracking-ui/TagPicker.js +158 -0
  361. package/dist/modules/staff/lib/time-tracking-ui/TagPicker.js.map +7 -0
  362. package/dist/modules/staff/lib/time-tracking-ui/TaskBoardScreen.js +224 -0
  363. package/dist/modules/staff/lib/time-tracking-ui/TaskBoardScreen.js.map +7 -0
  364. package/dist/modules/staff/lib/time-tracking-ui/TaskCommentThread.js +93 -0
  365. package/dist/modules/staff/lib/time-tracking-ui/TaskCommentThread.js.map +7 -0
  366. package/dist/modules/staff/lib/time-tracking-ui/TaskDrawer.js +1196 -0
  367. package/dist/modules/staff/lib/time-tracking-ui/TaskDrawer.js.map +7 -0
  368. package/dist/modules/staff/lib/time-tracking-ui/TaskListView.js +176 -0
  369. package/dist/modules/staff/lib/time-tracking-ui/TaskListView.js.map +7 -0
  370. package/dist/modules/staff/lib/time-tracking-ui/TaskPicker.js +307 -0
  371. package/dist/modules/staff/lib/time-tracking-ui/TaskPicker.js.map +7 -0
  372. package/dist/modules/staff/lib/time-tracking-ui/TaskQuickLog.js +284 -0
  373. package/dist/modules/staff/lib/time-tracking-ui/TaskQuickLog.js.map +7 -0
  374. package/dist/modules/staff/lib/time-tracking-ui/TimeEntriesSummaryFooter.js +73 -0
  375. package/dist/modules/staff/lib/time-tracking-ui/TimeEntriesSummaryFooter.js.map +7 -0
  376. package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDialog.js +1499 -0
  377. package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDialog.js.map +7 -0
  378. package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDurationCell.js +95 -0
  379. package/dist/modules/staff/lib/time-tracking-ui/TimeEntryDurationCell.js.map +7 -0
  380. package/dist/modules/staff/lib/time-tracking-ui/TimesheetCalendar.js +188 -0
  381. package/dist/modules/staff/lib/time-tracking-ui/TimesheetCalendar.js.map +7 -0
  382. package/dist/modules/staff/lib/time-tracking-ui/TimesheetPeriodFooter.js +61 -0
  383. package/dist/modules/staff/lib/time-tracking-ui/TimesheetPeriodFooter.js.map +7 -0
  384. package/dist/modules/staff/lib/time-tracking-ui/TimesheetViewSwitch.js +45 -0
  385. package/dist/modules/staff/lib/time-tracking-ui/TimesheetViewSwitch.js.map +7 -0
  386. package/dist/modules/staff/lib/time-tracking-ui/boardDirectory.js +176 -0
  387. package/dist/modules/staff/lib/time-tracking-ui/boardDirectory.js.map +7 -0
  388. package/dist/modules/staff/lib/time-tracking-ui/boardFilters.js +82 -0
  389. package/dist/modules/staff/lib/time-tracking-ui/boardFilters.js.map +7 -0
  390. package/dist/modules/staff/lib/time-tracking-ui/boardSummary.js +5 -0
  391. package/dist/modules/staff/lib/time-tracking-ui/boardSummary.js.map +7 -0
  392. package/dist/modules/staff/lib/time-tracking-ui/kanbanBoardData.js +152 -0
  393. package/dist/modules/staff/lib/time-tracking-ui/kanbanBoardData.js.map +7 -0
  394. package/dist/modules/staff/lib/time-tracking-ui/projectTeamAssignment.js +92 -0
  395. package/dist/modules/staff/lib/time-tracking-ui/projectTeamAssignment.js.map +7 -0
  396. package/dist/modules/staff/lib/time-tracking-ui/taskDrawerData.js +111 -0
  397. package/dist/modules/staff/lib/time-tracking-ui/taskDrawerData.js.map +7 -0
  398. package/dist/modules/staff/lib/time-tracking-ui/timeEntryDialogState.js +243 -0
  399. package/dist/modules/staff/lib/time-tracking-ui/timeEntryDialogState.js.map +7 -0
  400. package/dist/modules/staff/lib/time-tracking-ui/timeEntryListData.js +151 -0
  401. package/dist/modules/staff/lib/time-tracking-ui/timeEntryListData.js.map +7 -0
  402. package/dist/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.js +105 -0
  403. package/dist/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.js.map +7 -0
  404. package/dist/modules/staff/lib/time-tracking-ui/timesheetData.js +132 -0
  405. package/dist/modules/staff/lib/time-tracking-ui/timesheetData.js.map +7 -0
  406. package/dist/modules/staff/lib/time-tracking-ui/timesheetPeriod.js +205 -0
  407. package/dist/modules/staff/lib/time-tracking-ui/timesheetPeriod.js.map +7 -0
  408. package/dist/modules/staff/lib/time-tracking-ui/timesheetTargets.js +53 -0
  409. package/dist/modules/staff/lib/time-tracking-ui/timesheetTargets.js.map +7 -0
  410. package/dist/modules/staff/lib/time-tracking-ui/useBoardFilters.js +77 -0
  411. package/dist/modules/staff/lib/time-tracking-ui/useBoardFilters.js.map +7 -0
  412. package/dist/modules/staff/lib/time-tracking-ui/useTimesheetPreferences.js +151 -0
  413. package/dist/modules/staff/lib/time-tracking-ui/useTimesheetPreferences.js.map +7 -0
  414. package/dist/modules/staff/lib/timeTrackingSeeds.js +809 -0
  415. package/dist/modules/staff/lib/timeTrackingSeeds.js.map +7 -0
  416. package/dist/modules/staff/lib/timesheets/timeEntryCacheInvalidation.js +2 -3
  417. package/dist/modules/staff/lib/timesheets/timeEntryCacheInvalidation.js.map +2 -2
  418. package/dist/modules/staff/lib/timesheets/timeEntryDecoration.js +152 -0
  419. package/dist/modules/staff/lib/timesheets/timeEntryDecoration.js.map +7 -0
  420. package/dist/modules/staff/lib/timesheets-projects/budgetBurn.js +36 -0
  421. package/dist/modules/staff/lib/timesheets-projects/budgetBurn.js.map +7 -0
  422. package/dist/modules/staff/lib/timesheets-projects/budgetThreshold.js +66 -0
  423. package/dist/modules/staff/lib/timesheets-projects/budgetThreshold.js.map +7 -0
  424. package/dist/modules/staff/lib/timesheets-projects/budgetThresholdState.js +78 -0
  425. package/dist/modules/staff/lib/timesheets-projects/budgetThresholdState.js.map +7 -0
  426. package/dist/modules/staff/lib/timesheets-projects/computeProjectFinancials.js +81 -0
  427. package/dist/modules/staff/lib/timesheets-projects/computeProjectFinancials.js.map +7 -0
  428. package/dist/modules/staff/lib/timesheets-projects/reportSelection.js +44 -0
  429. package/dist/modules/staff/lib/timesheets-projects/reportSelection.js.map +7 -0
  430. package/dist/modules/staff/lib/timesheets-projects-ui/AssignMembersDialog.js +169 -0
  431. package/dist/modules/staff/lib/timesheets-projects-ui/AssignMembersDialog.js.map +7 -0
  432. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectBudgetCell.js +22 -0
  433. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectBudgetCell.js.map +7 -0
  434. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectCard.js +51 -1
  435. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectCard.js.map +2 -2
  436. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectsBulkActionsBar.js +70 -0
  437. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectsBulkActionsBar.js.map +7 -0
  438. package/dist/modules/staff/lib/timesheets-reports/buildReportSheet.js +63 -0
  439. package/dist/modules/staff/lib/timesheets-reports/buildReportSheet.js.map +7 -0
  440. package/dist/modules/staff/lib/timesheets-reports/loadReportData.js +170 -0
  441. package/dist/modules/staff/lib/timesheets-reports/loadReportData.js.map +7 -0
  442. package/dist/modules/staff/lib/timesheets-reports/pdf.js +205 -0
  443. package/dist/modules/staff/lib/timesheets-reports/pdf.js.map +7 -0
  444. package/dist/modules/staff/lib/timesheets-reports/reportApprovalPolicies.js +68 -0
  445. package/dist/modules/staff/lib/timesheets-reports/reportApprovalPolicies.js.map +7 -0
  446. package/dist/modules/staff/lib/timesheets-reports/reportConfigData.js +135 -0
  447. package/dist/modules/staff/lib/timesheets-reports/reportConfigData.js.map +7 -0
  448. package/dist/modules/staff/lib/timesheets-reports/reportConfigState.js +48 -0
  449. package/dist/modules/staff/lib/timesheets-reports/reportConfigState.js.map +7 -0
  450. package/dist/modules/staff/lib/timesheets-reports/reportExport.js +262 -0
  451. package/dist/modules/staff/lib/timesheets-reports/reportExport.js.map +7 -0
  452. package/dist/modules/staff/lib/timesheets-reports/reportExportFormats.js +34 -0
  453. package/dist/modules/staff/lib/timesheets-reports/reportExportFormats.js.map +7 -0
  454. package/dist/modules/staff/lib/timesheets-reports/reportGroupings.js +79 -0
  455. package/dist/modules/staff/lib/timesheets-reports/reportGroupings.js.map +7 -0
  456. package/dist/modules/staff/lib/timesheets-reports/reportLabels.js +11 -0
  457. package/dist/modules/staff/lib/timesheets-reports/reportLabels.js.map +7 -0
  458. package/dist/modules/staff/lib/timesheets-reports/reportReference.js +83 -0
  459. package/dist/modules/staff/lib/timesheets-reports/reportReference.js.map +7 -0
  460. package/dist/modules/staff/lib/timesheets-reports/reportRows.js +42 -0
  461. package/dist/modules/staff/lib/timesheets-reports/reportRows.js.map +7 -0
  462. package/dist/modules/staff/lib/timesheets-reports/reportSheetData.js +107 -0
  463. package/dist/modules/staff/lib/timesheets-reports/reportSheetData.js.map +7 -0
  464. package/dist/modules/staff/lib/timesheets-reports/reportTotals.js +289 -0
  465. package/dist/modules/staff/lib/timesheets-reports/reportTotals.js.map +7 -0
  466. package/dist/modules/staff/lib/timesheets-reports/xlsx.js +145 -0
  467. package/dist/modules/staff/lib/timesheets-reports/xlsx.js.map +7 -0
  468. package/dist/modules/staff/lib/timesheets-tasks/computeTaskRollups.js +116 -0
  469. package/dist/modules/staff/lib/timesheets-tasks/computeTaskRollups.js.map +7 -0
  470. package/dist/modules/staff/lib/timesheets-tasks/defaultStatusTemplate.js +50 -0
  471. package/dist/modules/staff/lib/timesheets-tasks/defaultStatusTemplate.js.map +7 -0
  472. package/dist/modules/staff/lib/timesheets-tasks/seedProjectStatuses.js +27 -0
  473. package/dist/modules/staff/lib/timesheets-tasks/seedProjectStatuses.js.map +7 -0
  474. package/dist/modules/staff/lib/timesheets-tasks/statusPositions.js +65 -0
  475. package/dist/modules/staff/lib/timesheets-tasks/statusPositions.js.map +7 -0
  476. package/dist/modules/staff/lib/timesheets-tasks/statusSlug.js +31 -0
  477. package/dist/modules/staff/lib/timesheets-tasks/statusSlug.js.map +7 -0
  478. package/dist/modules/staff/lib/timesheets-tasks/taskHierarchy.js +31 -0
  479. package/dist/modules/staff/lib/timesheets-tasks/taskHierarchy.js.map +7 -0
  480. package/dist/modules/staff/lib/timesheets-tasks/taskHoursTotals.js +17 -0
  481. package/dist/modules/staff/lib/timesheets-tasks/taskHoursTotals.js.map +7 -0
  482. package/dist/modules/staff/lib/timesheets-tasks/taskReference.js +51 -0
  483. package/dist/modules/staff/lib/timesheets-tasks/taskReference.js.map +7 -0
  484. package/dist/modules/staff/lib/timesheets-ui/CreateProjectDialog.js +11 -4
  485. package/dist/modules/staff/lib/timesheets-ui/CreateProjectDialog.js.map +2 -2
  486. package/dist/modules/staff/lib/timesheets-ui/ListView.js +369 -111
  487. package/dist/modules/staff/lib/timesheets-ui/ListView.js.map +2 -2
  488. package/dist/modules/staff/lib/timesheets-ui/TimerBar.js +47 -2
  489. package/dist/modules/staff/lib/timesheets-ui/TimerBar.js.map +2 -2
  490. package/dist/modules/staff/migrations/Migration20260813073131_staff.js +79 -0
  491. package/dist/modules/staff/migrations/Migration20260813073131_staff.js.map +7 -0
  492. package/dist/modules/staff/migrations/Migration20260824143357_staff.js +15 -0
  493. package/dist/modules/staff/migrations/Migration20260824143357_staff.js.map +7 -0
  494. package/dist/modules/staff/notifications.client.js +16 -0
  495. package/dist/modules/staff/notifications.client.js.map +7 -0
  496. package/dist/modules/staff/notifications.handlers.js +80 -0
  497. package/dist/modules/staff/notifications.handlers.js.map +7 -0
  498. package/dist/modules/staff/notifications.js +168 -0
  499. package/dist/modules/staff/notifications.js.map +2 -2
  500. package/dist/modules/staff/search.js +159 -1
  501. package/dist/modules/staff/search.js.map +2 -2
  502. package/dist/modules/staff/setup.js +20 -1
  503. package/dist/modules/staff/setup.js.map +2 -2
  504. package/dist/modules/staff/subscribers/time-project-access-requested-notification.js +68 -0
  505. package/dist/modules/staff/subscribers/time-project-access-requested-notification.js.map +7 -0
  506. package/dist/modules/staff/subscribers/time-project-budget-threshold-notification.js +149 -0
  507. package/dist/modules/staff/subscribers/time-project-budget-threshold-notification.js.map +7 -0
  508. package/dist/modules/staff/subscribers/time-report-approved-notification.js +78 -0
  509. package/dist/modules/staff/subscribers/time-report-approved-notification.js.map +7 -0
  510. package/dist/modules/staff/subscribers/time-report-portal-broadcast.js +78 -0
  511. package/dist/modules/staff/subscribers/time-report-portal-broadcast.js.map +7 -0
  512. package/dist/modules/staff/translations.js +4 -1
  513. package/dist/modules/staff/translations.js.map +2 -2
  514. package/dist/modules/staff/widgets/components.js +7 -0
  515. package/dist/modules/staff/widgets/components.js.map +7 -0
  516. package/dist/modules/staff/widgets/injection/timer-sidebar-indicator/widget.js +5 -4
  517. package/dist/modules/staff/widgets/injection/timer-sidebar-indicator/widget.js.map +2 -2
  518. package/dist/modules/staff/widgets/notifications/TimeProjectAccessRequestedRenderer.js +135 -0
  519. package/dist/modules/staff/widgets/notifications/TimeProjectAccessRequestedRenderer.js.map +7 -0
  520. package/dist/modules/staff/widgets/notifications/index.js +5 -0
  521. package/dist/modules/staff/widgets/notifications/index.js.map +7 -0
  522. package/dist/modules/staff/workers/timesheets-reapply-rounding.js +43 -0
  523. package/dist/modules/staff/workers/timesheets-reapply-rounding.js.map +7 -0
  524. package/generated/entities/staff_time_entry/index.ts +7 -0
  525. package/generated/entities/staff_time_entry_tag/index.ts +6 -0
  526. package/generated/entities/staff_time_project/index.ts +8 -0
  527. package/generated/entities/staff_time_report/index.ts +27 -0
  528. package/generated/entities/staff_time_report_entry/index.ts +12 -0
  529. package/generated/entities/staff_time_report_event/index.ts +9 -0
  530. package/generated/entities/staff_time_report_project/index.ts +6 -0
  531. package/generated/entities/staff_time_tag/index.ts +9 -0
  532. package/generated/entities/staff_time_task/index.ts +17 -0
  533. package/generated/entities/staff_time_task_comment/index.ts +9 -0
  534. package/generated/entities/staff_time_task_status/index.ts +13 -0
  535. package/generated/entities/staff_time_task_tag/index.ts +6 -0
  536. package/generated/entities.ids.generated.ts +11 -1
  537. package/generated/entity-fields-registry.ts +149 -0
  538. package/package.json +7 -7
  539. package/src/helpers/integration/timesheetFixtures.ts +31 -1
  540. package/src/modules/auth/README.md +1 -1
  541. package/src/modules/auth/cli.ts +35 -1
  542. package/src/modules/auth/i18n/de.json +7 -0
  543. package/src/modules/auth/i18n/en.json +7 -0
  544. package/src/modules/auth/i18n/es.json +7 -0
  545. package/src/modules/auth/i18n/ko.json +7 -0
  546. package/src/modules/auth/i18n/pl.json +7 -0
  547. package/src/modules/auth/lib/backendChrome.tsx +1 -0
  548. package/src/modules/customer_accounts/lib/customerRoleAcls.ts +79 -0
  549. package/src/modules/customer_accounts/setup.ts +1 -45
  550. package/src/modules/data_sync/AGENTS.md +34 -0
  551. package/src/modules/data_sync/api/options.ts +2 -0
  552. package/src/modules/data_sync/backend/data-sync/page.tsx +82 -33
  553. package/src/modules/data_sync/i18n/de.json +1 -0
  554. package/src/modules/data_sync/i18n/en.json +1 -0
  555. package/src/modules/data_sync/i18n/es.json +1 -0
  556. package/src/modules/data_sync/i18n/ko.json +1 -0
  557. package/src/modules/data_sync/i18n/pl.json +1 -0
  558. package/src/modules/data_sync/lib/adapter.ts +45 -0
  559. package/src/modules/data_sync/lib/start-controls.ts +89 -0
  560. package/src/modules/staff/AGENTS.md +769 -1
  561. package/src/modules/staff/acl.ts +9 -0
  562. package/src/modules/staff/ai-agents.ts +189 -0
  563. package/src/modules/staff/ai-tools/time-tracking-pack.ts +494 -0
  564. package/src/modules/staff/ai-tools/types.ts +82 -0
  565. package/src/modules/staff/ai-tools.ts +13 -0
  566. package/src/modules/staff/analytics.ts +113 -0
  567. package/src/modules/staff/api/guards.ts +85 -3
  568. package/src/modules/staff/api/leave-requests/accept/route.ts +0 -2
  569. package/src/modules/staff/api/leave-requests/reject/route.ts +0 -2
  570. package/src/modules/staff/api/portal/time-reports/[id]/route.ts +182 -0
  571. package/src/modules/staff/api/portal/time-reports/route.ts +270 -0
  572. package/src/modules/staff/api/team-members/self/route.ts +0 -2
  573. package/src/modules/staff/api/team-members/tags/assign/route.ts +0 -2
  574. package/src/modules/staff/api/team-members/tags/unassign/route.ts +0 -2
  575. package/src/modules/staff/api/timesheets/_shared/withTimesheetInterceptors.ts +292 -0
  576. package/src/modules/staff/api/timesheets/access-requests/route.ts +299 -0
  577. package/src/modules/staff/api/timesheets/my-projects/[projectId]/route.ts +15 -7
  578. package/src/modules/staff/api/timesheets/my-projects/route.ts +14 -1
  579. package/src/modules/staff/api/timesheets/my-work/myWorkAggregate.ts +90 -0
  580. package/src/modules/staff/api/timesheets/my-work/route.ts +475 -0
  581. package/src/modules/staff/api/timesheets/projects/kpis/route.ts +15 -2
  582. package/src/modules/staff/api/timesheets/reports/[id]/close/route.ts +203 -0
  583. package/src/modules/staff/api/timesheets/reports/[id]/export/route.ts +315 -0
  584. package/src/modules/staff/api/timesheets/reports/[id]/sheet/route.ts +220 -0
  585. package/src/modules/staff/api/timesheets/reports/[id]/unlock/route.ts +208 -0
  586. package/src/modules/staff/api/timesheets/reports/preview/route.ts +449 -0
  587. package/src/modules/staff/api/timesheets/reports/route.ts +386 -0
  588. package/src/modules/staff/api/timesheets/reports/shared.ts +121 -0
  589. package/src/modules/staff/api/timesheets/settings/reapply-rounding/route.ts +205 -0
  590. package/src/modules/staff/api/timesheets/settings/rounding-impact/route.ts +226 -0
  591. package/src/modules/staff/api/timesheets/settings/route.ts +242 -0
  592. package/src/modules/staff/api/timesheets/tags/access.ts +134 -0
  593. package/src/modules/staff/api/timesheets/tags/entry-assignments/route.ts +290 -0
  594. package/src/modules/staff/api/timesheets/tags/route.ts +177 -0
  595. package/src/modules/staff/api/timesheets/tags/task-assignments/route.ts +294 -0
  596. package/src/modules/staff/api/timesheets/task-statuses/route.ts +232 -0
  597. package/src/modules/staff/api/timesheets/tasks/[id]/comments/route.ts +532 -0
  598. package/src/modules/staff/api/timesheets/tasks/[id]/status/route.ts +271 -0
  599. package/src/modules/staff/api/timesheets/tasks/route.ts +443 -0
  600. package/src/modules/staff/api/timesheets/time-entries/[id]/duplicate/route.ts +306 -0
  601. package/src/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.ts +32 -10
  602. package/src/modules/staff/api/timesheets/time-entries/[id]/segments/route.ts +32 -17
  603. package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +27 -6
  604. package/src/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.ts +36 -6
  605. package/src/modules/staff/api/timesheets/time-entries/bulk/route.ts +331 -34
  606. package/src/modules/staff/api/timesheets/time-entries/copy-day/route.ts +533 -0
  607. package/src/modules/staff/api/timesheets/time-entries/overlaps/route.ts +457 -0
  608. package/src/modules/staff/api/timesheets/time-entries/route.ts +378 -24
  609. package/src/modules/staff/api/timesheets/time-entries/start-timer/route.ts +31 -9
  610. package/src/modules/staff/api/timesheets/time-projects/[id]/change-currency/route.ts +246 -0
  611. package/src/modules/staff/api/timesheets/time-projects/[id]/employees/route.ts +62 -2
  612. package/src/modules/staff/api/timesheets/time-projects/route.ts +397 -22
  613. package/src/modules/staff/backend/staff/time-tracking/board/page.meta.ts +14 -0
  614. package/src/modules/staff/backend/staff/time-tracking/board/page.tsx +151 -0
  615. package/src/modules/staff/backend/staff/time-tracking/entries/page.meta.ts +14 -0
  616. package/src/modules/staff/backend/staff/time-tracking/entries/page.tsx +1229 -0
  617. package/src/modules/staff/backend/staff/time-tracking/page.meta.ts +11 -0
  618. package/src/modules/staff/backend/staff/time-tracking/page.tsx +823 -0
  619. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.meta.ts +12 -0
  620. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/board/page.tsx +81 -0
  621. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.meta.ts +12 -0
  622. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/edit/page.tsx +275 -0
  623. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.meta.ts +12 -0
  624. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +1420 -0
  625. package/src/modules/staff/backend/staff/time-tracking/projects/create/page.meta.ts +12 -0
  626. package/src/modules/staff/backend/staff/time-tracking/projects/create/page.tsx +109 -0
  627. package/src/modules/staff/backend/staff/time-tracking/projects/page.meta.ts +15 -0
  628. package/src/modules/staff/backend/staff/time-tracking/projects/page.tsx +1254 -0
  629. package/src/modules/staff/backend/staff/time-tracking/projects/projectFormConfig.ts +466 -0
  630. package/src/modules/staff/backend/staff/time-tracking/reports/[id]/page.meta.ts +12 -0
  631. package/src/modules/staff/backend/staff/time-tracking/reports/[id]/page.tsx +632 -0
  632. package/src/modules/staff/backend/staff/time-tracking/reports/create/page.meta.ts +12 -0
  633. package/src/modules/staff/backend/staff/time-tracking/reports/create/page.tsx +867 -0
  634. package/src/modules/staff/backend/staff/time-tracking/reports/page.meta.ts +14 -0
  635. package/src/modules/staff/backend/staff/time-tracking/reports/page.tsx +246 -0
  636. package/src/modules/staff/backend/staff/time-tracking/settings/page.meta.ts +14 -0
  637. package/src/modules/staff/backend/staff/time-tracking/settings/page.tsx +701 -0
  638. package/src/modules/staff/backend/staff/time-tracking/timesheet/GridView.tsx +953 -0
  639. package/src/modules/staff/backend/staff/time-tracking/timesheet/page.meta.ts +14 -0
  640. package/src/modules/staff/backend/staff/time-tracking/timesheet/page.tsx +800 -0
  641. package/src/modules/staff/backend/staff/timesheets/page.meta.ts +6 -6
  642. package/src/modules/staff/backend/staff/timesheets/page.tsx +12 -1010
  643. package/src/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.ts +0 -6
  644. package/src/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.tsx +4 -158
  645. package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.ts +0 -6
  646. package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.tsx +4 -609
  647. package/src/modules/staff/backend/staff/timesheets/projects/create/page.meta.ts +0 -6
  648. package/src/modules/staff/backend/staff/timesheets/projects/create/page.tsx +3 -89
  649. package/src/modules/staff/backend/staff/timesheets/projects/page.meta.ts +2 -19
  650. package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +3 -778
  651. package/src/modules/staff/ce.ts +63 -0
  652. package/src/modules/staff/cli.ts +207 -1
  653. package/src/modules/staff/commands/index.ts +5 -0
  654. package/src/modules/staff/commands/timesheets-entries.ts +1006 -28
  655. package/src/modules/staff/commands/timesheets-projects.ts +593 -7
  656. package/src/modules/staff/commands/timesheets-reports.ts +1329 -0
  657. package/src/modules/staff/commands/timesheets-tags.ts +1315 -0
  658. package/src/modules/staff/commands/timesheets-task-comments.ts +583 -0
  659. package/src/modules/staff/commands/timesheets-task-statuses.ts +889 -0
  660. package/src/modules/staff/commands/timesheets-tasks.ts +1121 -0
  661. package/src/modules/staff/data/enrichers.ts +675 -10
  662. package/src/modules/staff/data/entities.ts +494 -2
  663. package/src/modules/staff/data/extensions.ts +68 -0
  664. package/src/modules/staff/data/validators.ts +348 -3
  665. package/src/modules/staff/di.ts +96 -0
  666. package/src/modules/staff/events.payloads.ts +268 -0
  667. package/src/modules/staff/events.ts +87 -5
  668. package/src/modules/staff/extension-points.ts +308 -0
  669. package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.meta.ts +10 -0
  670. package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.tsx +225 -0
  671. package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/page.meta.ts +17 -0
  672. package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/page.tsx +182 -0
  673. package/src/modules/staff/i18n/de.json +899 -0
  674. package/src/modules/staff/i18n/en.json +899 -0
  675. package/src/modules/staff/i18n/es.json +899 -0
  676. package/src/modules/staff/i18n/ko.json +899 -0
  677. package/src/modules/staff/i18n/pl.json +899 -0
  678. package/src/modules/staff/lib/crud.ts +14 -3
  679. package/src/modules/staff/lib/time-tracking/access.ts +155 -0
  680. package/src/modules/staff/lib/time-tracking/billability.ts +68 -0
  681. package/src/modules/staff/lib/time-tracking/capacity.ts +124 -0
  682. package/src/modules/staff/lib/time-tracking/componentContracts.ts +26 -0
  683. package/src/modules/staff/lib/time-tracking/cost.ts +144 -0
  684. package/src/modules/staff/lib/time-tracking/duration.ts +68 -0
  685. package/src/modules/staff/lib/time-tracking/featureAccess.ts +115 -0
  686. package/src/modules/staff/lib/time-tracking/interval.ts +90 -0
  687. package/src/modules/staff/lib/time-tracking/migrateProjectCodes.ts +161 -0
  688. package/src/modules/staff/lib/time-tracking/moneyVisibility.ts +37 -0
  689. package/src/modules/staff/lib/time-tracking/overlap.ts +159 -0
  690. package/src/modules/staff/lib/time-tracking/portalRecipients.ts +76 -0
  691. package/src/modules/staff/lib/time-tracking/portalReports.ts +31 -0
  692. package/src/modules/staff/lib/time-tracking/projectCode.ts +221 -0
  693. package/src/modules/staff/lib/time-tracking/reapplyRounding.ts +313 -0
  694. package/src/modules/staff/lib/time-tracking/recalculationRunner.ts +118 -0
  695. package/src/modules/staff/lib/time-tracking/recalculations.ts +135 -0
  696. package/src/modules/staff/lib/time-tracking/registries/invoke.ts +122 -0
  697. package/src/modules/staff/lib/time-tracking/registries/registry.ts +124 -0
  698. package/src/modules/staff/lib/time-tracking/registries/scope.ts +56 -0
  699. package/src/modules/staff/lib/time-tracking/rollup.ts +59 -0
  700. package/src/modules/staff/lib/time-tracking/rounding.ts +126 -0
  701. package/src/modules/staff/lib/time-tracking/roundingImpact.ts +75 -0
  702. package/src/modules/staff/lib/time-tracking/settingKeys.ts +274 -0
  703. package/src/modules/staff/lib/time-tracking/settings.ts +168 -0
  704. package/src/modules/staff/lib/time-tracking/sqlInClause.ts +22 -0
  705. package/src/modules/staff/lib/time-tracking/timeEntrySources.ts +85 -0
  706. package/src/modules/staff/lib/time-tracking-ui/BoardFilterChips.tsx +360 -0
  707. package/src/modules/staff/lib/time-tracking-ui/CustomerPicker.tsx +427 -0
  708. package/src/modules/staff/lib/time-tracking-ui/DurationInput.tsx +192 -0
  709. package/src/modules/staff/lib/time-tracking-ui/EntryDetailsFields.tsx +224 -0
  710. package/src/modules/staff/lib/time-tracking-ui/EntryFilterBar.tsx +144 -0
  711. package/src/modules/staff/lib/time-tracking-ui/KanbanBoard.tsx +1065 -0
  712. package/src/modules/staff/lib/time-tracking-ui/KanbanCard.tsx +435 -0
  713. package/src/modules/staff/lib/time-tracking-ui/KanbanColumn.tsx +316 -0
  714. package/src/modules/staff/lib/time-tracking-ui/NewTaskDialog.tsx +291 -0
  715. package/src/modules/staff/lib/time-tracking-ui/NoProjectAccess.tsx +162 -0
  716. package/src/modules/staff/lib/time-tracking-ui/PeriodSelector.tsx +149 -0
  717. package/src/modules/staff/lib/time-tracking-ui/PhasePlaceholder.tsx +31 -0
  718. package/src/modules/staff/lib/time-tracking-ui/ProjectCodeField.tsx +230 -0
  719. package/src/modules/staff/lib/time-tracking-ui/ProjectCurrencyField.tsx +432 -0
  720. package/src/modules/staff/lib/time-tracking-ui/ProjectFormSections.tsx +323 -0
  721. package/src/modules/staff/lib/time-tracking-ui/ProjectTeamDrawer.tsx +930 -0
  722. package/src/modules/staff/lib/time-tracking-ui/ReportSheet.tsx +357 -0
  723. package/src/modules/staff/lib/time-tracking-ui/TagPicker.tsx +195 -0
  724. package/src/modules/staff/lib/time-tracking-ui/TaskBoardScreen.tsx +243 -0
  725. package/src/modules/staff/lib/time-tracking-ui/TaskCommentThread.tsx +137 -0
  726. package/src/modules/staff/lib/time-tracking-ui/TaskDrawer.tsx +1405 -0
  727. package/src/modules/staff/lib/time-tracking-ui/TaskListView.tsx +260 -0
  728. package/src/modules/staff/lib/time-tracking-ui/TaskPicker.tsx +435 -0
  729. package/src/modules/staff/lib/time-tracking-ui/TaskQuickLog.tsx +296 -0
  730. package/src/modules/staff/lib/time-tracking-ui/TimeEntriesSummaryFooter.tsx +97 -0
  731. package/src/modules/staff/lib/time-tracking-ui/TimeEntryDialog.tsx +1929 -0
  732. package/src/modules/staff/lib/time-tracking-ui/TimeEntryDurationCell.tsx +126 -0
  733. package/src/modules/staff/lib/time-tracking-ui/TimesheetCalendar.tsx +265 -0
  734. package/src/modules/staff/lib/time-tracking-ui/TimesheetPeriodFooter.tsx +78 -0
  735. package/src/modules/staff/lib/time-tracking-ui/TimesheetViewSwitch.tsx +70 -0
  736. package/src/modules/staff/lib/time-tracking-ui/boardDirectory.ts +231 -0
  737. package/src/modules/staff/lib/time-tracking-ui/boardFilters.ts +128 -0
  738. package/src/modules/staff/lib/time-tracking-ui/boardSummary.ts +20 -0
  739. package/src/modules/staff/lib/time-tracking-ui/kanbanBoardData.ts +228 -0
  740. package/src/modules/staff/lib/time-tracking-ui/projectTeamAssignment.ts +143 -0
  741. package/src/modules/staff/lib/time-tracking-ui/taskDrawerData.ts +194 -0
  742. package/src/modules/staff/lib/time-tracking-ui/timeEntryDialogState.ts +400 -0
  743. package/src/modules/staff/lib/time-tracking-ui/timeEntryListData.ts +271 -0
  744. package/src/modules/staff/lib/time-tracking-ui/timeTrackingSettingsForm.ts +168 -0
  745. package/src/modules/staff/lib/time-tracking-ui/timesheetData.ts +225 -0
  746. package/src/modules/staff/lib/time-tracking-ui/timesheetPeriod.ts +278 -0
  747. package/src/modules/staff/lib/time-tracking-ui/timesheetTargets.ts +93 -0
  748. package/src/modules/staff/lib/time-tracking-ui/useBoardFilters.ts +107 -0
  749. package/src/modules/staff/lib/time-tracking-ui/useTimesheetPreferences.ts +245 -0
  750. package/src/modules/staff/lib/timeTrackingSeeds.ts +1037 -0
  751. package/src/modules/staff/lib/timesheets/timeEntryCacheInvalidation.ts +11 -10
  752. package/src/modules/staff/lib/timesheets/timeEntryDecoration.ts +256 -0
  753. package/src/modules/staff/lib/timesheets-projects/budgetBurn.ts +60 -0
  754. package/src/modules/staff/lib/timesheets-projects/budgetThreshold.ts +135 -0
  755. package/src/modules/staff/lib/timesheets-projects/budgetThresholdState.ts +137 -0
  756. package/src/modules/staff/lib/timesheets-projects/computeProjectFinancials.ts +142 -0
  757. package/src/modules/staff/lib/timesheets-projects/reportSelection.ts +82 -0
  758. package/src/modules/staff/lib/timesheets-projects-ui/AssignMembersDialog.tsx +237 -0
  759. package/src/modules/staff/lib/timesheets-projects-ui/ProjectBudgetCell.tsx +33 -0
  760. package/src/modules/staff/lib/timesheets-projects-ui/ProjectCard.tsx +56 -1
  761. package/src/modules/staff/lib/timesheets-projects-ui/ProjectsBulkActionsBar.tsx +90 -0
  762. package/src/modules/staff/lib/timesheets-reports/buildReportSheet.ts +125 -0
  763. package/src/modules/staff/lib/timesheets-reports/loadReportData.ts +251 -0
  764. package/src/modules/staff/lib/timesheets-reports/pdf.ts +263 -0
  765. package/src/modules/staff/lib/timesheets-reports/reportApprovalPolicies.ts +137 -0
  766. package/src/modules/staff/lib/timesheets-reports/reportConfigData.ts +247 -0
  767. package/src/modules/staff/lib/timesheets-reports/reportConfigState.ts +89 -0
  768. package/src/modules/staff/lib/timesheets-reports/reportExport.ts +373 -0
  769. package/src/modules/staff/lib/timesheets-reports/reportExportFormats.ts +71 -0
  770. package/src/modules/staff/lib/timesheets-reports/reportGroupings.ts +153 -0
  771. package/src/modules/staff/lib/timesheets-reports/reportLabels.ts +23 -0
  772. package/src/modules/staff/lib/timesheets-reports/reportReference.ts +146 -0
  773. package/src/modules/staff/lib/timesheets-reports/reportRows.ts +89 -0
  774. package/src/modules/staff/lib/timesheets-reports/reportSheetData.ts +196 -0
  775. package/src/modules/staff/lib/timesheets-reports/reportTotals.ts +544 -0
  776. package/src/modules/staff/lib/timesheets-reports/xlsx.ts +207 -0
  777. package/src/modules/staff/lib/timesheets-tasks/computeTaskRollups.ts +238 -0
  778. package/src/modules/staff/lib/timesheets-tasks/defaultStatusTemplate.ts +90 -0
  779. package/src/modules/staff/lib/timesheets-tasks/seedProjectStatuses.ts +43 -0
  780. package/src/modules/staff/lib/timesheets-tasks/statusPositions.ts +132 -0
  781. package/src/modules/staff/lib/timesheets-tasks/statusSlug.ts +43 -0
  782. package/src/modules/staff/lib/timesheets-tasks/taskHierarchy.ts +99 -0
  783. package/src/modules/staff/lib/timesheets-tasks/taskHoursTotals.ts +42 -0
  784. package/src/modules/staff/lib/timesheets-tasks/taskReference.ts +108 -0
  785. package/src/modules/staff/lib/timesheets-ui/CreateProjectDialog.tsx +18 -4
  786. package/src/modules/staff/lib/timesheets-ui/ListView.tsx +504 -149
  787. package/src/modules/staff/lib/timesheets-ui/TimerBar.tsx +50 -2
  788. package/src/modules/staff/migrations/.snapshot-open-mercato.json +3244 -228
  789. package/src/modules/staff/migrations/Migration20260813073131_staff.ts +94 -0
  790. package/src/modules/staff/migrations/Migration20260824143357_staff.ts +17 -0
  791. package/src/modules/staff/notifications.client.ts +16 -0
  792. package/src/modules/staff/notifications.handlers.ts +88 -0
  793. package/src/modules/staff/notifications.ts +163 -0
  794. package/src/modules/staff/search.ts +191 -1
  795. package/src/modules/staff/setup.ts +22 -0
  796. package/src/modules/staff/subscribers/time-project-access-requested-notification.ts +95 -0
  797. package/src/modules/staff/subscribers/time-project-budget-threshold-notification.ts +226 -0
  798. package/src/modules/staff/subscribers/time-report-approved-notification.ts +116 -0
  799. package/src/modules/staff/subscribers/time-report-portal-broadcast.ts +120 -0
  800. package/src/modules/staff/translations.ts +17 -0
  801. package/src/modules/staff/widgets/components.ts +34 -0
  802. package/src/modules/staff/widgets/injection/timer-sidebar-indicator/widget.tsx +6 -5
  803. package/src/modules/staff/widgets/notifications/TimeProjectAccessRequestedRenderer.tsx +138 -0
  804. package/src/modules/staff/widgets/notifications/index.ts +1 -0
  805. package/src/modules/staff/workers/timesheets-reapply-rounding.ts +59 -0
  806. package/dist/modules/staff/backend/staff/timesheets/projects/projectFormConfig.js +0 -123
  807. package/dist/modules/staff/backend/staff/timesheets/projects/projectFormConfig.js.map +0 -7
  808. package/src/modules/staff/backend/staff/timesheets/projects/projectFormConfig.ts +0 -138
@@ -0,0 +1,63 @@
1
+ import { loadReportData } from "./loadReportData.js";
2
+ import { normalizeReportGrouping } from "./reportGroupings.js";
3
+ import {
4
+ computeReportTotals,
5
+ resolveReportCurrency
6
+ } from "./reportTotals.js";
7
+ function toDate(value, fallback) {
8
+ if (value instanceof Date && !Number.isNaN(value.getTime())) return value;
9
+ if (typeof value === "string") {
10
+ const parsed = new Date(value);
11
+ if (!Number.isNaN(parsed.getTime())) return parsed;
12
+ }
13
+ return fallback;
14
+ }
15
+ async function buildReportSheet(input) {
16
+ const { em, scope, report, labels } = input;
17
+ const isClosed = report.status === "closed";
18
+ const grouping = normalizeReportGrouping(input.grouping ?? report.grouping);
19
+ const nonbillableMode = report.nonbillableMode;
20
+ const data = await loadReportData({
21
+ em,
22
+ scope,
23
+ timeProjectIds: input.timeProjectIds,
24
+ periodFrom: toDate(report.periodFrom, /* @__PURE__ */ new Date(0)),
25
+ periodTo: toDate(report.periodTo, /* @__PURE__ */ new Date())
26
+ });
27
+ const entries = isClosed ? data.entries.filter((entry) => entry.frozen?.reportId === report.id) : data.entries;
28
+ const totals = computeReportTotals({
29
+ entries,
30
+ projects: data.projects,
31
+ directory: data.directory,
32
+ options: {
33
+ grouping,
34
+ nonbillableMode,
35
+ includeAlreadyReported: report.includeAlreadyReported ?? false
36
+ },
37
+ currentReportId: report.id,
38
+ labels
39
+ });
40
+ const currency = resolveReportCurrency(
41
+ data.projects.map((project) => ({
42
+ id: project.id,
43
+ name: project.name,
44
+ currencyCode: project.currencyCode
45
+ }))
46
+ );
47
+ return {
48
+ totals,
49
+ entries,
50
+ projects: data.projects,
51
+ directory: data.directory,
52
+ // A closed report keeps the currency it was closed with, even if a project
53
+ // has since been relabelled (risk R11).
54
+ currencyCode: isClosed ? report.currencyCode : currency.ok ? currency.currencyCode : report.currencyCode,
55
+ grouping,
56
+ nonbillableMode,
57
+ isClosed
58
+ };
59
+ }
60
+ export {
61
+ buildReportSheet
62
+ };
63
+ //# sourceMappingURL=buildReportSheet.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/timesheets-reports/buildReportSheet.ts"],
4
+ "sourcesContent": ["/**\n * The sheet of screen 14, built once and reused by the detail page, the exports\n * and the close command.\n *\n * The one rule that matters here is the difference between a draft and a closed\n * report:\n *\n * - a **draft** is computed live, so it moves when an entry is edited or the\n * rounding rule changes \u2014 by design, and screen 16 says so;\n * - a **closed** report renders exactly what it froze. Its lines come from the\n * `staff_time_report_entries` snapshots, so a later rounding change, a rate\n * change or an unlocked-and-edited entry cannot restate an invoice a client\n * already holds (risk R1).\n *\n * Both paths go through the same `computeReportTotals`, which is what keeps the\n * preview, the sheet and the freeze records arithmetically identical.\n */\n\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { StaffTimeReport } from '../../data/entities'\nimport { loadReportData, type ReportDataScope } from './loadReportData'\nimport { normalizeReportGrouping } from './reportGroupings'\nimport {\n computeReportTotals,\n resolveReportCurrency,\n type ReportDirectory,\n type ReportGrouping,\n type ReportInputEntry,\n type ReportInputProject,\n type ReportNonBillableMode,\n type ReportTotals,\n} from './reportTotals'\n\nexport type ReportSheetLabels = {\n unassignedTask: string\n unassignedPerson: string\n nonbillableGroup: string\n}\n\nexport type BuildReportSheetInput = {\n em: EntityManager\n scope: ReportDataScope\n report: StaffTimeReport\n timeProjectIds: readonly string[]\n labels: ReportSheetLabels\n /** Overrides the report's stored grouping, for an export that re-groups. */\n grouping?: ReportGrouping\n}\n\nexport type ReportSheet = {\n totals: ReportTotals\n entries: ReportInputEntry[]\n projects: ReportInputProject[]\n directory: ReportDirectory\n currencyCode: string | null\n grouping: ReportGrouping\n nonbillableMode: ReportNonBillableMode\n isClosed: boolean\n}\n\nfunction toDate(value: Date | string | null | undefined, fallback: Date): Date {\n if (value instanceof Date && !Number.isNaN(value.getTime())) return value\n if (typeof value === 'string') {\n const parsed = new Date(value)\n if (!Number.isNaN(parsed.getTime())) return parsed\n }\n return fallback\n}\n\nexport async function buildReportSheet(input: BuildReportSheetInput): Promise<ReportSheet> {\n const { em, scope, report, labels } = input\n const isClosed = report.status === 'closed'\n const grouping = normalizeReportGrouping(input.grouping ?? report.grouping)\n const nonbillableMode = report.nonbillableMode as ReportNonBillableMode\n\n const data = await loadReportData({\n em,\n scope,\n timeProjectIds: input.timeProjectIds,\n periodFrom: toDate(report.periodFrom, new Date(0)),\n periodTo: toDate(report.periodTo, new Date()),\n })\n\n // A closed report shows only what it froze. An entry logged into the period\n // AFTER the report closed is genuinely new work and belongs to the next\n // report \u2014 screen 15 note 2 is explicit that the lock covers entries, not\n // tasks, so logging more time on the same task must stay possible.\n const entries = isClosed\n ? data.entries.filter((entry) => entry.frozen?.reportId === report.id)\n : data.entries\n\n const totals = computeReportTotals({\n entries,\n projects: data.projects,\n directory: data.directory,\n options: {\n grouping,\n nonbillableMode,\n includeAlreadyReported: report.includeAlreadyReported ?? false,\n },\n currentReportId: report.id,\n labels,\n })\n\n const currency = resolveReportCurrency(\n data.projects.map((project) => ({\n id: project.id,\n name: project.name,\n currencyCode: project.currencyCode,\n })),\n )\n\n return {\n totals,\n entries,\n projects: data.projects,\n directory: data.directory,\n // A closed report keeps the currency it was closed with, even if a project\n // has since been relabelled (risk R11).\n currencyCode: isClosed ? report.currencyCode : currency.ok ? currency.currencyCode : report.currencyCode,\n grouping,\n nonbillableMode,\n isClosed,\n }\n}\n"],
5
+ "mappings": "AAoBA,SAAS,sBAA4C;AACrD,SAAS,+BAA+B;AACxC;AAAA,EACE;AAAA,EACA;AAAA,OAOK;AA6BP,SAAS,OAAO,OAAyC,UAAsB;AAC7E,MAAI,iBAAiB,QAAQ,CAAC,OAAO,MAAM,MAAM,QAAQ,CAAC,EAAG,QAAO;AACpE,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,SAAS,IAAI,KAAK,KAAK;AAC7B,QAAI,CAAC,OAAO,MAAM,OAAO,QAAQ,CAAC,EAAG,QAAO;AAAA,EAC9C;AACA,SAAO;AACT;AAEA,eAAsB,iBAAiB,OAAoD;AACzF,QAAM,EAAE,IAAI,OAAO,QAAQ,OAAO,IAAI;AACtC,QAAM,WAAW,OAAO,WAAW;AACnC,QAAM,WAAW,wBAAwB,MAAM,YAAY,OAAO,QAAQ;AAC1E,QAAM,kBAAkB,OAAO;AAE/B,QAAM,OAAO,MAAM,eAAe;AAAA,IAChC;AAAA,IACA;AAAA,IACA,gBAAgB,MAAM;AAAA,IACtB,YAAY,OAAO,OAAO,YAAY,oBAAI,KAAK,CAAC,CAAC;AAAA,IACjD,UAAU,OAAO,OAAO,UAAU,oBAAI,KAAK,CAAC;AAAA,EAC9C,CAAC;AAMD,QAAM,UAAU,WACZ,KAAK,QAAQ,OAAO,CAAC,UAAU,MAAM,QAAQ,aAAa,OAAO,EAAE,IACnE,KAAK;AAET,QAAM,SAAS,oBAAoB;AAAA,IACjC;AAAA,IACA,UAAU,KAAK;AAAA,IACf,WAAW,KAAK;AAAA,IAChB,SAAS;AAAA,MACP;AAAA,MACA;AAAA,MACA,wBAAwB,OAAO,0BAA0B;AAAA,IAC3D;AAAA,IACA,iBAAiB,OAAO;AAAA,IACxB;AAAA,EACF,CAAC;AAED,QAAM,WAAW;AAAA,IACf,KAAK,SAAS,IAAI,CAAC,aAAa;AAAA,MAC9B,IAAI,QAAQ;AAAA,MACZ,MAAM,QAAQ;AAAA,MACd,cAAc,QAAQ;AAAA,IACxB,EAAE;AAAA,EACJ;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,UAAU,KAAK;AAAA,IACf,WAAW,KAAK;AAAA;AAAA;AAAA,IAGhB,cAAc,WAAW,OAAO,eAAe,SAAS,KAAK,SAAS,eAAe,OAAO;AAAA,IAC5F;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,170 @@
1
+ import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
2
+ import {
3
+ StaffTeamMember,
4
+ StaffTimeEntry,
5
+ StaffTimeProject,
6
+ StaffTimeReport,
7
+ StaffTimeReportEntry,
8
+ StaffTimeTask
9
+ } from "../../data/entities.js";
10
+ function toNumberOrNull(value) {
11
+ if (value === null || value === void 0) return null;
12
+ const parsed = typeof value === "number" ? value : Number.parseFloat(String(value));
13
+ return Number.isFinite(parsed) ? parsed : null;
14
+ }
15
+ function toIsoDate(value) {
16
+ if (!value) return "";
17
+ if (value instanceof Date) {
18
+ if (Number.isNaN(value.getTime())) return "";
19
+ const year = value.getFullYear();
20
+ const month = String(value.getMonth() + 1).padStart(2, "0");
21
+ const day = String(value.getDate()).padStart(2, "0");
22
+ return `${year}-${month}-${day}`;
23
+ }
24
+ return String(value).slice(0, 10);
25
+ }
26
+ function resolveRootTaskId(taskId, parentById) {
27
+ if (!taskId) return null;
28
+ const parent = parentById.get(taskId);
29
+ if (!parent) return taskId;
30
+ return parent;
31
+ }
32
+ async function loadReportData(input) {
33
+ const { em, scope, periodFrom, periodTo } = input;
34
+ const projectIds = Array.from(new Set(input.timeProjectIds.filter((id) => typeof id === "string" && id.length > 0)));
35
+ if (projectIds.length === 0) {
36
+ return {
37
+ projects: [],
38
+ entries: [],
39
+ directory: { taskLabelById: {}, personLabelById: {} },
40
+ projectRows: []
41
+ };
42
+ }
43
+ const projectRows = await em.find(StaffTimeProject, {
44
+ id: { $in: projectIds },
45
+ tenantId: scope.tenantId,
46
+ organizationId: scope.organizationId,
47
+ deletedAt: null
48
+ });
49
+ const entryRows = await em.find(StaffTimeEntry, {
50
+ timeProjectId: { $in: projectIds },
51
+ tenantId: scope.tenantId,
52
+ organizationId: scope.organizationId,
53
+ deletedAt: null,
54
+ date: { $gte: periodFrom, $lte: periodTo }
55
+ });
56
+ const taskIds = Array.from(
57
+ new Set(entryRows.map((entry) => entry.taskId).filter((id) => typeof id === "string" && id.length > 0))
58
+ );
59
+ const taskRows = taskIds.length > 0 ? await em.find(StaffTimeTask, {
60
+ id: { $in: taskIds },
61
+ tenantId: scope.tenantId,
62
+ organizationId: scope.organizationId
63
+ }) : [];
64
+ const parentIds = Array.from(
65
+ new Set(
66
+ taskRows.map((task) => task.parentTaskId).filter((id) => typeof id === "string" && id.length > 0 && !taskIds.includes(id))
67
+ )
68
+ );
69
+ const parentRows = parentIds.length > 0 ? await em.find(StaffTimeTask, {
70
+ id: { $in: parentIds },
71
+ tenantId: scope.tenantId,
72
+ organizationId: scope.organizationId
73
+ }) : [];
74
+ const parentById = /* @__PURE__ */ new Map();
75
+ const taskLabelById = {};
76
+ for (const task of [...taskRows, ...parentRows]) {
77
+ parentById.set(task.id, task.parentTaskId ?? null);
78
+ taskLabelById[task.id] = task.title;
79
+ }
80
+ const staffMemberIds = Array.from(
81
+ new Set(
82
+ entryRows.map((entry) => entry.staffMemberId).filter((id) => typeof id === "string" && id.length > 0)
83
+ )
84
+ );
85
+ const memberRows = staffMemberIds.length > 0 ? await findWithDecryption(
86
+ em,
87
+ StaffTeamMember,
88
+ {
89
+ id: { $in: staffMemberIds },
90
+ tenantId: scope.tenantId,
91
+ organizationId: scope.organizationId
92
+ },
93
+ {},
94
+ scope
95
+ ) : [];
96
+ const personLabelById = {};
97
+ for (const member of memberRows) personLabelById[member.id] = member.displayName;
98
+ const frozenByEntryId = await loadFrozenValues(
99
+ em,
100
+ scope,
101
+ entryRows.map((entry) => entry.id)
102
+ );
103
+ const entries = entryRows.map((entry) => ({
104
+ id: entry.id,
105
+ timeProjectId: entry.timeProjectId ?? "",
106
+ taskId: entry.taskId ?? null,
107
+ rootTaskId: resolveRootTaskId(entry.taskId ?? null, parentById),
108
+ staffMemberId: entry.staffMemberId ?? null,
109
+ date: toIsoDate(entry.date),
110
+ durationMinutes: entry.durationMinutes ?? 0,
111
+ roundedMinutes: entry.roundedMinutes ?? null,
112
+ isBillable: entry.isBillable ?? true,
113
+ rateOverrideAmount: toNumberOrNull(entry.rateOverrideAmount),
114
+ description: entry.notes ?? null,
115
+ frozen: frozenByEntryId.get(entry.id) ?? null
116
+ }));
117
+ const projects = projectRows.map((project) => ({
118
+ id: project.id,
119
+ name: project.name,
120
+ hourlyRate: toNumberOrNull(project.hourlyRate),
121
+ currencyCode: project.currencyCode ?? null
122
+ }));
123
+ return {
124
+ projects,
125
+ entries,
126
+ directory: { taskLabelById, personLabelById },
127
+ projectRows
128
+ };
129
+ }
130
+ async function loadFrozenValues(em, scope, timeEntryIds) {
131
+ const ids = Array.from(new Set(timeEntryIds.filter((id) => typeof id === "string" && id.length > 0)));
132
+ const result = /* @__PURE__ */ new Map();
133
+ if (ids.length === 0) return result;
134
+ const rows = await em.find(StaffTimeReportEntry, {
135
+ timeEntryId: { $in: ids },
136
+ tenantId: scope.tenantId,
137
+ organizationId: scope.organizationId
138
+ });
139
+ if (rows.length === 0) return result;
140
+ const reportIds = Array.from(new Set(rows.map((row) => row.reportId)));
141
+ const reports = await em.find(StaffTimeReport, {
142
+ id: { $in: reportIds },
143
+ tenantId: scope.tenantId,
144
+ organizationId: scope.organizationId,
145
+ status: "closed",
146
+ deletedAt: null
147
+ });
148
+ const reportById = new Map(reports.map((report) => [report.id, report]));
149
+ for (const row of rows) {
150
+ const report = reportById.get(row.reportId);
151
+ if (!report) continue;
152
+ result.set(row.timeEntryId, {
153
+ reportId: report.id,
154
+ reference: report.reference ?? null,
155
+ title: report.title ?? null,
156
+ rawMinutes: row.frozenRawMinutes ?? 0,
157
+ roundedMinutes: row.frozenRoundedMinutes ?? 0,
158
+ rateAmount: toNumberOrNull(row.frozenRateAmount),
159
+ currencyCode: row.frozenCurrencyCode ?? null,
160
+ amount: toNumberOrNull(row.frozenAmount),
161
+ isBillable: row.frozenIsBillable ?? true
162
+ });
163
+ }
164
+ return result;
165
+ }
166
+ export {
167
+ loadFrozenValues,
168
+ loadReportData
169
+ };
170
+ //# sourceMappingURL=loadReportData.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/timesheets-reports/loadReportData.ts"],
4
+ "sourcesContent": ["/**\n * The one read that feeds every report surface: the live preview of screen 13,\n * the sheet of screen 14, the close command's freeze records and the exports.\n *\n * It is deliberately a single loader rather than one query per surface, because\n * a preview that counts different hours from the sheet it generates \u2014 or from\n * the snapshot the close writes \u2014 is the failure mode this whole phase exists to\n * prevent. Everything downstream consumes the same `ReportInputEntry[]`.\n *\n * Scoping is not optional here: every query carries `tenantId` and\n * `organizationId`, and the project ids are expected to have been narrowed by\n * `resolveProjectAccess` before they arrive.\n */\n\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport {\n StaffTeamMember,\n StaffTimeEntry,\n StaffTimeProject,\n StaffTimeReport,\n StaffTimeReportEntry,\n StaffTimeTask,\n} from '../../data/entities'\nimport type { FrozenEntryValues, ReportDirectory, ReportInputEntry, ReportInputProject } from './reportTotals'\n\nexport type ReportDataScope = {\n tenantId: string\n organizationId: string\n}\n\nexport type LoadReportDataInput = {\n em: EntityManager\n scope: ReportDataScope\n timeProjectIds: readonly string[]\n periodFrom: Date\n periodTo: Date\n}\n\nexport type ReportData = {\n projects: ReportInputProject[]\n entries: ReportInputEntry[]\n directory: ReportDirectory\n /** Raw project rows, kept so callers can assert currency without a second read. */\n projectRows: StaffTimeProject[]\n}\n\nfunction toNumberOrNull(value: unknown): number | null {\n if (value === null || value === undefined) return null\n const parsed = typeof value === 'number' ? value : Number.parseFloat(String(value))\n return Number.isFinite(parsed) ? parsed : null\n}\n\nfunction toIsoDate(value: Date | string | null | undefined): string {\n if (!value) return ''\n if (value instanceof Date) {\n if (Number.isNaN(value.getTime())) return ''\n const year = value.getFullYear()\n const month = String(value.getMonth() + 1).padStart(2, '0')\n const day = String(value.getDate()).padStart(2, '0')\n return `${year}-${month}-${day}`\n }\n return String(value).slice(0, 10)\n}\n\n/**\n * D-2 caps nesting at one level, so a task's root is either itself or its\n * parent. The fallback to the task's own id keeps a line visible even if a\n * parent row has since been hard-removed.\n */\nfunction resolveRootTaskId(taskId: string | null, parentById: ReadonlyMap<string, string | null>): string | null {\n if (!taskId) return null\n const parent = parentById.get(taskId)\n if (!parent) return taskId\n return parent\n}\n\nexport async function loadReportData(input: LoadReportDataInput): Promise<ReportData> {\n const { em, scope, periodFrom, periodTo } = input\n const projectIds = Array.from(new Set(input.timeProjectIds.filter((id) => typeof id === 'string' && id.length > 0)))\n if (projectIds.length === 0) {\n return {\n projects: [],\n entries: [],\n directory: { taskLabelById: {}, personLabelById: {} },\n projectRows: [],\n }\n }\n\n const projectRows = await em.find(StaffTimeProject, {\n id: { $in: projectIds },\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n\n const entryRows = await em.find(StaffTimeEntry, {\n timeProjectId: { $in: projectIds },\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n date: { $gte: periodFrom, $lte: periodTo },\n })\n\n const taskIds = Array.from(\n new Set(entryRows.map((entry) => entry.taskId).filter((id): id is string => typeof id === 'string' && id.length > 0)),\n )\n const taskRows =\n taskIds.length > 0\n ? await em.find(StaffTimeTask, {\n id: { $in: taskIds },\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n : []\n\n // A child task's parent is not necessarily among the entries' tasks, so the\n // parents are fetched in a second pass to give the rolled-up line its label.\n const parentIds = Array.from(\n new Set(\n taskRows\n .map((task) => task.parentTaskId)\n .filter((id): id is string => typeof id === 'string' && id.length > 0 && !taskIds.includes(id)),\n ),\n )\n const parentRows =\n parentIds.length > 0\n ? await em.find(StaffTimeTask, {\n id: { $in: parentIds },\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n : []\n\n const parentById = new Map<string, string | null>()\n const taskLabelById: Record<string, string> = {}\n for (const task of [...taskRows, ...parentRows]) {\n parentById.set(task.id, task.parentTaskId ?? null)\n taskLabelById[task.id] = task.title\n }\n\n const staffMemberIds = Array.from(\n new Set(\n entryRows\n .map((entry) => entry.staffMemberId)\n .filter((id): id is string => typeof id === 'string' && id.length > 0),\n ),\n )\n const memberRows =\n staffMemberIds.length > 0\n ? await findWithDecryption(\n em,\n StaffTeamMember,\n {\n id: { $in: staffMemberIds },\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n },\n {},\n scope,\n )\n : []\n const personLabelById: Record<string, string> = {}\n for (const member of memberRows) personLabelById[member.id] = member.displayName\n\n const frozenByEntryId = await loadFrozenValues(\n em,\n scope,\n entryRows.map((entry) => entry.id),\n )\n\n const entries: ReportInputEntry[] = entryRows.map((entry) => ({\n id: entry.id,\n timeProjectId: entry.timeProjectId ?? '',\n taskId: entry.taskId ?? null,\n rootTaskId: resolveRootTaskId(entry.taskId ?? null, parentById),\n staffMemberId: entry.staffMemberId ?? null,\n date: toIsoDate(entry.date),\n durationMinutes: entry.durationMinutes ?? 0,\n roundedMinutes: entry.roundedMinutes ?? null,\n isBillable: entry.isBillable ?? true,\n rateOverrideAmount: toNumberOrNull(entry.rateOverrideAmount),\n description: entry.notes ?? null,\n frozen: frozenByEntryId.get(entry.id) ?? null,\n }))\n\n const projects: ReportInputProject[] = projectRows.map((project) => ({\n id: project.id,\n name: project.name,\n hourlyRate: toNumberOrNull(project.hourlyRate),\n currencyCode: project.currencyCode ?? null,\n }))\n\n return {\n projects,\n entries,\n directory: { taskLabelById, personLabelById },\n projectRows,\n }\n}\n\n/**\n * D-5's indexed lookup. `staff_time_report_entries` is authoritative for \"has\n * this hour been billed already?\", and only rows belonging to a **closed**\n * report count: a freeze record left behind by a report that has since been\n * unlocked must not keep an hour out of the next invoice.\n */\nexport async function loadFrozenValues(\n em: EntityManager,\n scope: ReportDataScope,\n timeEntryIds: readonly string[],\n): Promise<Map<string, FrozenEntryValues>> {\n const ids = Array.from(new Set(timeEntryIds.filter((id) => typeof id === 'string' && id.length > 0)))\n const result = new Map<string, FrozenEntryValues>()\n if (ids.length === 0) return result\n\n const rows = await em.find(StaffTimeReportEntry, {\n timeEntryId: { $in: ids },\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n })\n if (rows.length === 0) return result\n\n const reportIds = Array.from(new Set(rows.map((row) => row.reportId)))\n const reports = await em.find(StaffTimeReport, {\n id: { $in: reportIds },\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n status: 'closed',\n deletedAt: null,\n })\n const reportById = new Map(reports.map((report) => [report.id, report]))\n\n for (const row of rows) {\n const report = reportById.get(row.reportId)\n if (!report) continue\n result.set(row.timeEntryId, {\n reportId: report.id,\n reference: report.reference ?? null,\n title: report.title ?? null,\n rawMinutes: row.frozenRawMinutes ?? 0,\n roundedMinutes: row.frozenRoundedMinutes ?? 0,\n rateAmount: toNumberOrNull(row.frozenRateAmount),\n currencyCode: row.frozenCurrencyCode ?? null,\n amount: toNumberOrNull(row.frozenAmount),\n isBillable: row.frozenIsBillable ?? true,\n })\n }\n\n return result\n}\n"],
5
+ "mappings": "AAeA,SAAS,0BAA0B;AACnC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAwBP,SAAS,eAAe,OAA+B;AACrD,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,QAAM,SAAS,OAAO,UAAU,WAAW,QAAQ,OAAO,WAAW,OAAO,KAAK,CAAC;AAClF,SAAO,OAAO,SAAS,MAAM,IAAI,SAAS;AAC5C;AAEA,SAAS,UAAU,OAAiD;AAClE,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,iBAAiB,MAAM;AACzB,QAAI,OAAO,MAAM,MAAM,QAAQ,CAAC,EAAG,QAAO;AAC1C,UAAM,OAAO,MAAM,YAAY;AAC/B,UAAM,QAAQ,OAAO,MAAM,SAAS,IAAI,CAAC,EAAE,SAAS,GAAG,GAAG;AAC1D,UAAM,MAAM,OAAO,MAAM,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG;AACnD,WAAO,GAAG,IAAI,IAAI,KAAK,IAAI,GAAG;AAAA,EAChC;AACA,SAAO,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AAClC;AAOA,SAAS,kBAAkB,QAAuB,YAA+D;AAC/G,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,SAAS,WAAW,IAAI,MAAM;AACpC,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO;AACT;AAEA,eAAsB,eAAe,OAAiD;AACpF,QAAM,EAAE,IAAI,OAAO,YAAY,SAAS,IAAI;AAC5C,QAAM,aAAa,MAAM,KAAK,IAAI,IAAI,MAAM,eAAe,OAAO,CAAC,OAAO,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC,CAAC,CAAC;AACnH,MAAI,WAAW,WAAW,GAAG;AAC3B,WAAO;AAAA,MACL,UAAU,CAAC;AAAA,MACX,SAAS,CAAC;AAAA,MACV,WAAW,EAAE,eAAe,CAAC,GAAG,iBAAiB,CAAC,EAAE;AAAA,MACpD,aAAa,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,cAAc,MAAM,GAAG,KAAK,kBAAkB;AAAA,IAClD,IAAI,EAAE,KAAK,WAAW;AAAA,IACtB,UAAU,MAAM;AAAA,IAChB,gBAAgB,MAAM;AAAA,IACtB,WAAW;AAAA,EACb,CAAC;AAED,QAAM,YAAY,MAAM,GAAG,KAAK,gBAAgB;AAAA,IAC9C,eAAe,EAAE,KAAK,WAAW;AAAA,IACjC,UAAU,MAAM;AAAA,IAChB,gBAAgB,MAAM;AAAA,IACtB,WAAW;AAAA,IACX,MAAM,EAAE,MAAM,YAAY,MAAM,SAAS;AAAA,EAC3C,CAAC;AAED,QAAM,UAAU,MAAM;AAAA,IACpB,IAAI,IAAI,UAAU,IAAI,CAAC,UAAU,MAAM,MAAM,EAAE,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC,CAAC;AAAA,EACtH;AACA,QAAM,WACJ,QAAQ,SAAS,IACb,MAAM,GAAG,KAAK,eAAe;AAAA,IAC3B,IAAI,EAAE,KAAK,QAAQ;AAAA,IACnB,UAAU,MAAM;AAAA,IAChB,gBAAgB,MAAM;AAAA,EACxB,CAAC,IACD,CAAC;AAIP,QAAM,YAAY,MAAM;AAAA,IACtB,IAAI;AAAA,MACF,SACG,IAAI,CAAC,SAAS,KAAK,YAAY,EAC/B,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,KAAK,CAAC,QAAQ,SAAS,EAAE,CAAC;AAAA,IAClG;AAAA,EACF;AACA,QAAM,aACJ,UAAU,SAAS,IACf,MAAM,GAAG,KAAK,eAAe;AAAA,IAC3B,IAAI,EAAE,KAAK,UAAU;AAAA,IACrB,UAAU,MAAM;AAAA,IAChB,gBAAgB,MAAM;AAAA,EACxB,CAAC,IACD,CAAC;AAEP,QAAM,aAAa,oBAAI,IAA2B;AAClD,QAAM,gBAAwC,CAAC;AAC/C,aAAW,QAAQ,CAAC,GAAG,UAAU,GAAG,UAAU,GAAG;AAC/C,eAAW,IAAI,KAAK,IAAI,KAAK,gBAAgB,IAAI;AACjD,kBAAc,KAAK,EAAE,IAAI,KAAK;AAAA,EAChC;AAEA,QAAM,iBAAiB,MAAM;AAAA,IAC3B,IAAI;AAAA,MACF,UACG,IAAI,CAAC,UAAU,MAAM,aAAa,EAClC,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC;AAAA,IACzE;AAAA,EACF;AACA,QAAM,aACJ,eAAe,SAAS,IACpB,MAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,MACE,IAAI,EAAE,KAAK,eAAe;AAAA,MAC1B,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,IACxB;AAAA,IACA,CAAC;AAAA,IACD;AAAA,EACF,IACA,CAAC;AACP,QAAM,kBAA0C,CAAC;AACjD,aAAW,UAAU,WAAY,iBAAgB,OAAO,EAAE,IAAI,OAAO;AAErE,QAAM,kBAAkB,MAAM;AAAA,IAC5B;AAAA,IACA;AAAA,IACA,UAAU,IAAI,CAAC,UAAU,MAAM,EAAE;AAAA,EACnC;AAEA,QAAM,UAA8B,UAAU,IAAI,CAAC,WAAW;AAAA,IAC5D,IAAI,MAAM;AAAA,IACV,eAAe,MAAM,iBAAiB;AAAA,IACtC,QAAQ,MAAM,UAAU;AAAA,IACxB,YAAY,kBAAkB,MAAM,UAAU,MAAM,UAAU;AAAA,IAC9D,eAAe,MAAM,iBAAiB;AAAA,IACtC,MAAM,UAAU,MAAM,IAAI;AAAA,IAC1B,iBAAiB,MAAM,mBAAmB;AAAA,IAC1C,gBAAgB,MAAM,kBAAkB;AAAA,IACxC,YAAY,MAAM,cAAc;AAAA,IAChC,oBAAoB,eAAe,MAAM,kBAAkB;AAAA,IAC3D,aAAa,MAAM,SAAS;AAAA,IAC5B,QAAQ,gBAAgB,IAAI,MAAM,EAAE,KAAK;AAAA,EAC3C,EAAE;AAEF,QAAM,WAAiC,YAAY,IAAI,CAAC,aAAa;AAAA,IACnE,IAAI,QAAQ;AAAA,IACZ,MAAM,QAAQ;AAAA,IACd,YAAY,eAAe,QAAQ,UAAU;AAAA,IAC7C,cAAc,QAAQ,gBAAgB;AAAA,EACxC,EAAE;AAEF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,WAAW,EAAE,eAAe,gBAAgB;AAAA,IAC5C;AAAA,EACF;AACF;AAQA,eAAsB,iBACpB,IACA,OACA,cACyC;AACzC,QAAM,MAAM,MAAM,KAAK,IAAI,IAAI,aAAa,OAAO,CAAC,OAAO,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC,CAAC,CAAC;AACpG,QAAM,SAAS,oBAAI,IAA+B;AAClD,MAAI,IAAI,WAAW,EAAG,QAAO;AAE7B,QAAM,OAAO,MAAM,GAAG,KAAK,sBAAsB;AAAA,IAC/C,aAAa,EAAE,KAAK,IAAI;AAAA,IACxB,UAAU,MAAM;AAAA,IAChB,gBAAgB,MAAM;AAAA,EACxB,CAAC;AACD,MAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,QAAM,YAAY,MAAM,KAAK,IAAI,IAAI,KAAK,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,CAAC;AACrE,QAAM,UAAU,MAAM,GAAG,KAAK,iBAAiB;AAAA,IAC7C,IAAI,EAAE,KAAK,UAAU;AAAA,IACrB,UAAU,MAAM;AAAA,IAChB,gBAAgB,MAAM;AAAA,IACtB,QAAQ;AAAA,IACR,WAAW;AAAA,EACb,CAAC;AACD,QAAM,aAAa,IAAI,IAAI,QAAQ,IAAI,CAAC,WAAW,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC;AAEvE,aAAW,OAAO,MAAM;AACtB,UAAM,SAAS,WAAW,IAAI,IAAI,QAAQ;AAC1C,QAAI,CAAC,OAAQ;AACb,WAAO,IAAI,IAAI,aAAa;AAAA,MAC1B,UAAU,OAAO;AAAA,MACjB,WAAW,OAAO,aAAa;AAAA,MAC/B,OAAO,OAAO,SAAS;AAAA,MACvB,YAAY,IAAI,oBAAoB;AAAA,MACpC,gBAAgB,IAAI,wBAAwB;AAAA,MAC5C,YAAY,eAAe,IAAI,gBAAgB;AAAA,MAC/C,cAAc,IAAI,sBAAsB;AAAA,MACxC,QAAQ,eAAe,IAAI,YAAY;AAAA,MACvC,YAAY,IAAI,oBAAoB;AAAA,IACtC,CAAC;AAAA,EACH;AAEA,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,205 @@
1
+ const PDF_CONTENT_TYPE = "application/pdf";
2
+ const PAGE_WIDTH = 595.28;
3
+ const PAGE_HEIGHT = 841.89;
4
+ const MARGIN_X = 42;
5
+ const MARGIN_TOP = 56;
6
+ const MARGIN_BOTTOM = 48;
7
+ const LINE_HEIGHT = 14;
8
+ const DEFAULT_SIZE = 9;
9
+ const DIFFERENCE_GLYPHS = [
10
+ ["\u0105", 1, "aogonek"],
11
+ ["\u0107", 2, "cacute"],
12
+ ["\u0119", 3, "eogonek"],
13
+ ["\u0142", 4, "lslash"],
14
+ ["\u0144", 5, "nacute"],
15
+ ["\u015B", 6, "sacute"],
16
+ ["\u017A", 7, "zacute"],
17
+ ["\u017C", 8, "zdotaccent"],
18
+ ["\u0104", 9, "Aogonek"],
19
+ ["\u0106", 10, "Cacute"],
20
+ ["\u0118", 11, "Eogonek"],
21
+ ["\u0141", 12, "Lslash"],
22
+ ["\u0143", 13, "Nacute"],
23
+ ["\u015A", 14, "Sacute"],
24
+ ["\u0179", 15, "Zacute"],
25
+ ["\u017B", 16, "Zdotaccent"]
26
+ ];
27
+ const WIN_ANSI_EXTRAS = {
28
+ "\xF3": 243,
29
+ "\xD3": 211,
30
+ "\xE9": 233,
31
+ "\xE8": 232,
32
+ "\xE4": 228,
33
+ "\xF6": 246,
34
+ "\xFC": 252,
35
+ "\xDF": 223,
36
+ "\xE1": 225,
37
+ "\xED": 237,
38
+ "\xF1": 241,
39
+ "\xFA": 250,
40
+ "\u2013": 150,
41
+ "\u2014": 151,
42
+ "\xB7": 183,
43
+ "\u20AC": 128,
44
+ "\u201E": 132,
45
+ "\u201D": 148,
46
+ "\u2019": 146,
47
+ "\u2018": 145,
48
+ "\u2026": 133,
49
+ "\xB0": 176,
50
+ "\xD7": 215
51
+ };
52
+ const ENCODING_MAP = (() => {
53
+ const map = /* @__PURE__ */ new Map();
54
+ for (let code = 32; code <= 126; code += 1) map.set(String.fromCharCode(code), code);
55
+ for (const [char, code] of Object.entries(WIN_ANSI_EXTRAS)) map.set(char, code);
56
+ for (const [char, code] of DIFFERENCE_GLYPHS) map.set(char, code);
57
+ return map;
58
+ })();
59
+ function encodePdfText(value) {
60
+ let out = "";
61
+ for (const char of value) {
62
+ const code = ENCODING_MAP.get(char) ?? 63;
63
+ const byte = String.fromCharCode(code);
64
+ if (byte === "(" || byte === ")" || byte === "\\") out += `\\${byte}`;
65
+ else out += byte;
66
+ }
67
+ return out;
68
+ }
69
+ const HELVETICA_WIDTHS = (() => {
70
+ const widths = {};
71
+ const groups = [
72
+ [278, " !I|jltfi.,:;'`"],
73
+ [355, '"'],
74
+ [556, "#$0123456789+<=>~"],
75
+ [889, "%"],
76
+ [667, "&BCDEHKNRSUVXY"],
77
+ [333, "()[]{}/\\*-r"],
78
+ [584, "^"],
79
+ [500, "?_"],
80
+ [1015, "@"],
81
+ [722, "ADGHOQ"],
82
+ [611, "FPZ"],
83
+ [944, "M"],
84
+ [778, "W"],
85
+ [556, "abcdeghknopqsu"],
86
+ [222, "i"],
87
+ [500, "yvz"],
88
+ [833, "m"],
89
+ [722, "w"],
90
+ [333, "JLT"]
91
+ ];
92
+ for (const [width, chars] of groups) {
93
+ for (const char of chars) widths[char] = width;
94
+ }
95
+ return widths;
96
+ })();
97
+ function measureText(text, size, bold) {
98
+ let units = 0;
99
+ for (const char of text) units += HELVETICA_WIDTHS[char] ?? 556;
100
+ const factor = bold ? 1.03 : 1;
101
+ return units / 1e3 * size * factor;
102
+ }
103
+ function buildPages(input) {
104
+ const pages = [];
105
+ let current = [];
106
+ let y = PAGE_HEIGHT - MARGIN_TOP;
107
+ const pushPage = () => {
108
+ if (current.length > 0) pages.push(current);
109
+ current = [];
110
+ y = PAGE_HEIGHT - MARGIN_TOP;
111
+ };
112
+ for (const line of input.lines) {
113
+ const height = line.kind === "space" ? line.height : LINE_HEIGHT;
114
+ if (y - height < MARGIN_BOTTOM) pushPage();
115
+ if (line.kind === "space") {
116
+ y -= line.height;
117
+ continue;
118
+ }
119
+ if (line.kind === "rule") {
120
+ current.push(
121
+ `0.8 0.8 0.8 RG 0.5 w ${MARGIN_X} ${(y - 4).toFixed(2)} m ${(PAGE_WIDTH - MARGIN_X).toFixed(2)} ${(y - 4).toFixed(2)} l S`
122
+ );
123
+ y -= LINE_HEIGHT;
124
+ continue;
125
+ }
126
+ for (const cell of line.cells) {
127
+ const size = cell.size ?? DEFAULT_SIZE;
128
+ const font = cell.bold ? "/F2" : "/F1";
129
+ const width = measureText(cell.text, size, cell.bold === true);
130
+ const x = cell.align === "right" ? cell.x - width : cell.x;
131
+ const colour = cell.muted ? "0.45 0.45 0.45 rg" : "0 0 0 rg";
132
+ current.push(
133
+ `BT ${colour} ${font} ${size} Tf 1 0 0 1 ${x.toFixed(2)} ${y.toFixed(2)} Tm (${encodePdfText(cell.text)}) Tj ET`
134
+ );
135
+ }
136
+ y -= LINE_HEIGHT;
137
+ }
138
+ pushPage();
139
+ return pages.length > 0 ? pages : [[]];
140
+ }
141
+ function buildPdf(input) {
142
+ const pages = buildPages(input);
143
+ const objects = [];
144
+ const differences = DIFFERENCE_GLYPHS.map(([, code, glyph]) => `${code} /${glyph}`).join(" ");
145
+ const pageObjectStart = 6;
146
+ const pageIds = pages.map((_, index) => pageObjectStart + index * 2);
147
+ objects[1] = "<< /Type /Catalog /Pages 2 0 R >>";
148
+ objects[2] = `<< /Type /Pages /Kids [${pageIds.map((id) => `${id} 0 R`).join(" ")}] /Count ${pages.length} >>`;
149
+ objects[3] = "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding 5 0 R >>";
150
+ objects[4] = "<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding 5 0 R >>";
151
+ objects[5] = `<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [${differences}] >>`;
152
+ pages.forEach((content, index) => {
153
+ const pageId = pageIds[index];
154
+ const contentId = pageId + 1;
155
+ const stream = content.join("\n");
156
+ objects[pageId] = `<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${PAGE_WIDTH} ${PAGE_HEIGHT}] /Resources << /Font << /F1 3 0 R /F2 4 0 R >> >> /Contents ${contentId} 0 R >>`;
157
+ objects[contentId] = `<< /Length ${Buffer.byteLength(stream, "latin1")} >>
158
+ stream
159
+ ${stream}
160
+ endstream`;
161
+ });
162
+ const chunks = [Buffer.from("%PDF-1.4\n", "latin1")];
163
+ let offset = chunks[0].length;
164
+ const offsets = [];
165
+ for (let id = 1; id < objects.length; id += 1) {
166
+ const body = objects[id];
167
+ if (body === void 0) continue;
168
+ offsets[id] = offset;
169
+ const chunk = Buffer.from(`${id} 0 obj
170
+ ${body}
171
+ endobj
172
+ `, "latin1");
173
+ chunks.push(chunk);
174
+ offset += chunk.length;
175
+ }
176
+ const maxId = objects.length;
177
+ const xrefOffset = offset;
178
+ const xrefRows = ["0000000000 65535 f \n"];
179
+ for (let id = 1; id < maxId; id += 1) {
180
+ const at = offsets[id];
181
+ xrefRows.push(at === void 0 ? "0000000000 65535 f \n" : `${String(at).padStart(10, "0")} 00000 n
182
+ `);
183
+ }
184
+ chunks.push(
185
+ Buffer.from(
186
+ `xref
187
+ 0 ${maxId}
188
+ ${xrefRows.join("")}trailer
189
+ << /Size ${maxId} /Root 1 0 R >>
190
+ startxref
191
+ ${xrefOffset}
192
+ %%EOF
193
+ `,
194
+ "latin1"
195
+ )
196
+ );
197
+ return Buffer.concat(chunks);
198
+ }
199
+ export {
200
+ PDF_CONTENT_TYPE,
201
+ buildPdf,
202
+ encodePdfText,
203
+ measureText
204
+ };
205
+ //# sourceMappingURL=pdf.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/timesheets-reports/pdf.ts"],
4
+ "sourcesContent": ["/**\n * A minimal, dependency-free PDF writer \u2014 just enough to print the report sheet\n * of screen 14 as the client-facing document US-G4 asks for.\n *\n * The repository has no PDF library and adding a production dependency is an\n * \"ask first\" change, so this emits PDF 1.4 directly: a catalog, a pages tree,\n * two base-14 fonts and one content stream per page. Nothing here is a general\n * PDF toolkit; it lays out left-aligned and right-aligned text runs on a fixed\n * grid, which is exactly what a table of hours and amounts needs.\n *\n * **Polish text is the reason for the encoding work.** The mockups are Polish\n * and a report is a client-facing document, so `\u0105 \u0107 \u0119 \u0142 \u0144 \u015B \u017A \u017C` must survive.\n * WinAnsiEncoding covers `\u00F3`/`\u00D3` and nothing else Polish, so the remaining\n * sixteen glyphs are mapped into unused low code points through a `/Differences`\n * array using their standard Adobe glyph names. A character with no mapping is\n * rendered as `?` rather than being dropped, so a missing glyph is visible\n * rather than silently changing a name.\n */\n\nexport type PdfTextAlign = 'left' | 'right'\n\nexport type PdfCell = {\n text: string\n x: number\n align?: PdfTextAlign\n bold?: boolean\n size?: number\n /** Grey for hints and secondary text; black otherwise. */\n muted?: boolean\n}\n\nexport type PdfLine =\n | { kind: 'cells'; cells: PdfCell[] }\n | { kind: 'rule' }\n | { kind: 'space'; height: number }\n\nexport type PdfDocumentInput = {\n title: string\n lines: PdfLine[]\n}\n\nexport const PDF_CONTENT_TYPE = 'application/pdf'\n\nconst PAGE_WIDTH = 595.28\nconst PAGE_HEIGHT = 841.89\nconst MARGIN_X = 42\nconst MARGIN_TOP = 56\nconst MARGIN_BOTTOM = 48\nconst LINE_HEIGHT = 14\nconst DEFAULT_SIZE = 9\n\n/**\n * Glyphs Polish needs that WinAnsiEncoding does not carry, parked in unused\n * low code points and declared through /Differences.\n */\nconst DIFFERENCE_GLYPHS: Array<[string, number, string]> = [\n ['\u0105', 1, 'aogonek'],\n ['\u0107', 2, 'cacute'],\n ['\u0119', 3, 'eogonek'],\n ['\u0142', 4, 'lslash'],\n ['\u0144', 5, 'nacute'],\n ['\u015B', 6, 'sacute'],\n ['\u017A', 7, 'zacute'],\n ['\u017C', 8, 'zdotaccent'],\n ['\u0104', 9, 'Aogonek'],\n ['\u0106', 10, 'Cacute'],\n ['\u0118', 11, 'Eogonek'],\n ['\u0141', 12, 'Lslash'],\n ['\u0143', 13, 'Nacute'],\n ['\u015A', 14, 'Sacute'],\n ['\u0179', 15, 'Zacute'],\n ['\u017B', 16, 'Zdotaccent'],\n]\n\n/** Characters WinAnsiEncoding already carries at a code above ASCII. */\nconst WIN_ANSI_EXTRAS: Record<string, number> = {\n '\u00F3': 0xf3,\n '\u00D3': 0xd3,\n '\u00E9': 0xe9,\n '\u00E8': 0xe8,\n '\u00E4': 0xe4,\n '\u00F6': 0xf6,\n '\u00FC': 0xfc,\n '\u00DF': 0xdf,\n '\u00E1': 0xe1,\n '\u00ED': 0xed,\n '\u00F1': 0xf1,\n '\u00FA': 0xfa,\n '\u2013': 0x96,\n '\u2014': 0x97,\n '\u00B7': 0xb7,\n '\u20AC': 0x80,\n '\u201E': 0x84,\n '\u201D': 0x94,\n '\u2019': 0x92,\n '\u2018': 0x91,\n '\u2026': 0x85,\n '\u00B0': 0xb0,\n '\u00D7': 0xd7,\n}\n\nconst ENCODING_MAP: Map<string, number> = (() => {\n const map = new Map<string, number>()\n for (let code = 32; code <= 126; code += 1) map.set(String.fromCharCode(code), code)\n for (const [char, code] of Object.entries(WIN_ANSI_EXTRAS)) map.set(char, code)\n for (const [char, code] of DIFFERENCE_GLYPHS) map.set(char, code)\n return map\n})()\n\nexport function encodePdfText(value: string): string {\n let out = ''\n for (const char of value) {\n const code = ENCODING_MAP.get(char) ?? 0x3f\n const byte = String.fromCharCode(code)\n if (byte === '(' || byte === ')' || byte === '\\\\') out += `\\\\${byte}`\n else out += byte\n }\n return out\n}\n\n/**\n * Helvetica advance widths (1/1000 em) for the printable ASCII range, used to\n * right-align the money and hours columns. Characters outside the table fall\n * back to the average width, which is close enough for the accented letters and\n * only ever shifts a right-aligned label by a hair.\n */\nconst HELVETICA_WIDTHS: Record<string, number> = (() => {\n const widths: Record<string, number> = {}\n const groups: Array<[number, string]> = [\n [278, ' !I|jltfi.,:;\\'`'],\n [355, '\"'],\n [556, '#$0123456789+<=>~'],\n [889, '%'],\n [667, '&BCDEHKNRSUVXY'],\n [333, '()[]{}/\\\\*-r'],\n [584, '^'],\n [500, '?_'],\n [1015, '@'],\n [722, 'ADGHOQ'],\n [611, 'FPZ'],\n [944, 'M'],\n [778, 'W'],\n [556, 'abcdeghknopqsu'],\n [222, 'i'],\n [500, 'yvz'],\n [833, 'm'],\n [722, 'w'],\n [333, 'JLT'],\n ]\n for (const [width, chars] of groups) {\n for (const char of chars) widths[char] = width\n }\n return widths\n})()\n\nexport function measureText(text: string, size: number, bold: boolean): number {\n let units = 0\n for (const char of text) units += HELVETICA_WIDTHS[char] ?? 556\n // Helvetica-Bold runs a touch wider; a flat factor is accurate enough for\n // right-aligning a column and avoids shipping a second width table.\n const factor = bold ? 1.03 : 1\n return (units / 1000) * size * factor\n}\n\ntype PageContent = string[]\n\nfunction buildPages(input: PdfDocumentInput): PageContent[] {\n const pages: PageContent[] = []\n let current: PageContent = []\n let y = PAGE_HEIGHT - MARGIN_TOP\n\n const pushPage = () => {\n if (current.length > 0) pages.push(current)\n current = []\n y = PAGE_HEIGHT - MARGIN_TOP\n }\n\n for (const line of input.lines) {\n const height = line.kind === 'space' ? line.height : LINE_HEIGHT\n if (y - height < MARGIN_BOTTOM) pushPage()\n\n if (line.kind === 'space') {\n y -= line.height\n continue\n }\n if (line.kind === 'rule') {\n current.push(\n `0.8 0.8 0.8 RG 0.5 w ${MARGIN_X} ${(y - 4).toFixed(2)} m ${(PAGE_WIDTH - MARGIN_X).toFixed(2)} ${(y - 4).toFixed(2)} l S`,\n )\n y -= LINE_HEIGHT\n continue\n }\n\n for (const cell of line.cells) {\n const size = cell.size ?? DEFAULT_SIZE\n const font = cell.bold ? '/F2' : '/F1'\n const width = measureText(cell.text, size, cell.bold === true)\n const x = cell.align === 'right' ? cell.x - width : cell.x\n const colour = cell.muted ? '0.45 0.45 0.45 rg' : '0 0 0 rg'\n current.push(\n `BT ${colour} ${font} ${size} Tf 1 0 0 1 ${x.toFixed(2)} ${y.toFixed(2)} Tm (${encodePdfText(cell.text)}) Tj ET`,\n )\n }\n y -= LINE_HEIGHT\n }\n\n pushPage()\n return pages.length > 0 ? pages : [[]]\n}\n\nexport function buildPdf(input: PdfDocumentInput): Buffer {\n const pages = buildPages(input)\n const objects: string[] = []\n\n const differences = DIFFERENCE_GLYPHS.map(([, code, glyph]) => `${code} /${glyph}`).join(' ')\n const pageObjectStart = 6\n const pageIds = pages.map((_, index) => pageObjectStart + index * 2)\n\n objects[1] = '<< /Type /Catalog /Pages 2 0 R >>'\n objects[2] = `<< /Type /Pages /Kids [${pageIds.map((id) => `${id} 0 R`).join(' ')}] /Count ${pages.length} >>`\n objects[3] = '<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica /Encoding 5 0 R >>'\n objects[4] = '<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold /Encoding 5 0 R >>'\n objects[5] = `<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [${differences}] >>`\n\n pages.forEach((content, index) => {\n const pageId = pageIds[index]\n const contentId = pageId + 1\n const stream = content.join('\\n')\n objects[pageId] =\n `<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${PAGE_WIDTH} ${PAGE_HEIGHT}] ` +\n `/Resources << /Font << /F1 3 0 R /F2 4 0 R >> >> /Contents ${contentId} 0 R >>`\n objects[contentId] = `<< /Length ${Buffer.byteLength(stream, 'latin1')} >>\\nstream\\n${stream}\\nendstream`\n })\n\n const chunks: Buffer[] = [Buffer.from('%PDF-1.4\\n', 'latin1')]\n let offset = chunks[0].length\n const offsets: number[] = []\n\n for (let id = 1; id < objects.length; id += 1) {\n const body = objects[id]\n if (body === undefined) continue\n offsets[id] = offset\n const chunk = Buffer.from(`${id} 0 obj\\n${body}\\nendobj\\n`, 'latin1')\n chunks.push(chunk)\n offset += chunk.length\n }\n\n const maxId = objects.length\n const xrefOffset = offset\n const xrefRows = ['0000000000 65535 f \\n']\n for (let id = 1; id < maxId; id += 1) {\n const at = offsets[id]\n xrefRows.push(at === undefined ? '0000000000 65535 f \\n' : `${String(at).padStart(10, '0')} 00000 n \\n`)\n }\n chunks.push(\n Buffer.from(\n `xref\\n0 ${maxId}\\n${xrefRows.join('')}trailer\\n<< /Size ${maxId} /Root 1 0 R >>\\nstartxref\\n${xrefOffset}\\n%%EOF\\n`,\n 'latin1',\n ),\n )\n\n return Buffer.concat(chunks)\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,68 @@
1
+ import { extensionPoints } from "@open-mercato/core/modules/staff/extension-points";
2
+ import { createStrategyRegistry, BUILT_IN_STRATEGY_PRIORITY } from "../time-tracking/registries/registry.js";
3
+ import { runStrategy } from "../time-tracking/registries/invoke.js";
4
+ import { hasResolverScope } from "../time-tracking/registries/scope.js";
5
+ const REPORT_APPROVAL_POLICY_REGISTRY_ID = extensionPoints.hosts.reportApprovalPolicyRegistry.spotId;
6
+ const BUILT_IN_REPORT_APPROVAL_POLICY_ID = "staff.time_tracking.report_approval.acl_only";
7
+ const registry = createStrategyRegistry(REPORT_APPROVAL_POLICY_REGISTRY_ID);
8
+ function registerReportApprovalPolicy(policy) {
9
+ return registry.register(policy);
10
+ }
11
+ function listReportApprovalPolicies() {
12
+ return registry.list();
13
+ }
14
+ function getReportApprovalPolicy(id) {
15
+ return registry.get(id);
16
+ }
17
+ registry.registerBuiltIn({
18
+ id: BUILT_IN_REPORT_APPROVAL_POLICY_ID,
19
+ priority: BUILT_IN_STRATEGY_PRIORITY
20
+ });
21
+ function applicablePolicies(ctx) {
22
+ const scoped = hasResolverScope(ctx);
23
+ return registry.list().filter((policy) => scoped || policy.id === BUILT_IN_REPORT_APPROVAL_POLICY_ID);
24
+ }
25
+ function refusalOnFailure() {
26
+ return { code: "approval_policy_unavailable", messageKey: "staff.errors.forbidden" };
27
+ }
28
+ function evaluatePolicyVerdict(ctx, read) {
29
+ for (const policy of applicablePolicies(ctx)) {
30
+ const verdict = runStrategy(
31
+ REPORT_APPROVAL_POLICY_REGISTRY_ID,
32
+ policy.id,
33
+ () => read(policy),
34
+ () => refusalOnFailure()
35
+ );
36
+ if (verdict) return verdict;
37
+ }
38
+ return null;
39
+ }
40
+ function evaluateReportClosePolicies(ctx) {
41
+ return evaluatePolicyVerdict(ctx, (policy) => policy.canClose?.(ctx));
42
+ }
43
+ function evaluateReportUnlockPolicies(ctx) {
44
+ return evaluatePolicyVerdict(ctx, (policy) => policy.canUnlock?.(ctx));
45
+ }
46
+ async function notifyReportClosed(ctx) {
47
+ const failures = [];
48
+ for (const policy of applicablePolicies(ctx)) {
49
+ if (!policy.onClosed) continue;
50
+ try {
51
+ await policy.onClosed(ctx);
52
+ } catch (error) {
53
+ failures.push({ policyId: policy.id, error });
54
+ }
55
+ }
56
+ return failures;
57
+ }
58
+ export {
59
+ BUILT_IN_REPORT_APPROVAL_POLICY_ID,
60
+ REPORT_APPROVAL_POLICY_REGISTRY_ID,
61
+ evaluateReportClosePolicies,
62
+ evaluateReportUnlockPolicies,
63
+ getReportApprovalPolicy,
64
+ listReportApprovalPolicies,
65
+ notifyReportClosed,
66
+ registerReportApprovalPolicy
67
+ };
68
+ //# sourceMappingURL=reportApprovalPolicies.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/staff/lib/timesheets-reports/reportApprovalPolicies.ts"],
4
+ "sourcesContent": ["/**\n * EP-41 \u2014 the report approval / lock policy provider.\n *\n * The policy layer sits ON TOP of the ACL check, it does not replace it. The\n * close and unlock routes still require `staff.timesheets.lock` and\n * `staff.timesheets.reports.unlock` respectively, unconditionally and before\n * anything here runs; a policy can only add a reason to refuse. That is enforced\n * structurally: `canClose` / `canUnlock` return a refusal or nothing, never a\n * grant, so there is no shape in which a policy can open a door the ACL closed.\n *\n * The built-in `staff.time_tracking.report_approval.acl_only` refuses nothing,\n * which is exactly the ACL-only gate the module shipped with.\n */\n\nimport { extensionPoints } from '@open-mercato/core/modules/staff/extension-points'\nimport { createStrategyRegistry, BUILT_IN_STRATEGY_PRIORITY } from '../time-tracking/registries/registry'\nimport { runStrategy } from '../time-tracking/registries/invoke'\nimport { hasResolverScope, type ScopedResolverContext } from '../time-tracking/registries/scope'\n\nexport type ReportApprovalContext = ScopedResolverContext & {\n reportId: string\n actorUserId: string | null\n /** The features the ACL gate already resolved for the actor. */\n actorFeatures: readonly string[]\n status: string\n reason?: string | null\n}\n\nexport type ReportApprovalRefusal = {\n /** Machine-readable, surfaced in the response body as `code`. */\n code: string\n /** i18n key the route translates; never a user-facing literal. */\n messageKey: string\n}\n\n/** `null` \u2014 and `undefined` \u2014 mean \"no objection\", never \"allowed\". */\nexport type ReportApprovalVerdict = ReportApprovalRefusal | null | undefined\n\nexport type ReportApprovalPolicy = {\n id: string\n priority?: number\n canClose?(ctx: ReportApprovalContext): ReportApprovalVerdict\n canUnlock?(ctx: ReportApprovalContext): ReportApprovalVerdict\n onClosed?(ctx: ReportApprovalContext): void | Promise<void>\n}\n\nexport const REPORT_APPROVAL_POLICY_REGISTRY_ID = extensionPoints.hosts.reportApprovalPolicyRegistry.spotId\n\nexport const BUILT_IN_REPORT_APPROVAL_POLICY_ID = 'staff.time_tracking.report_approval.acl_only'\n\nconst registry = createStrategyRegistry<ReportApprovalPolicy>(REPORT_APPROVAL_POLICY_REGISTRY_ID)\n\nexport function registerReportApprovalPolicy(policy: ReportApprovalPolicy): () => void {\n return registry.register(policy)\n}\n\nexport function listReportApprovalPolicies(): ReportApprovalPolicy[] {\n return registry.list()\n}\n\nexport function getReportApprovalPolicy(id: string | null | undefined): ReportApprovalPolicy | null {\n return registry.get(id)\n}\n\nregistry.registerBuiltIn({\n id: BUILT_IN_REPORT_APPROVAL_POLICY_ID,\n priority: BUILT_IN_STRATEGY_PRIORITY,\n})\n\nfunction applicablePolicies(ctx: ReportApprovalContext): ReportApprovalPolicy[] {\n const scoped = hasResolverScope(ctx)\n return registry\n .list()\n .filter((policy) => scoped || policy.id === BUILT_IN_REPORT_APPROVAL_POLICY_ID)\n}\n\n/**\n * These two are the only registry call sites that do NOT fall back to the built-in\n * when a policy throws, and the asymmetry is deliberate: they are gates, and the\n * built-in grants (it declares no `canClose`/`canUnlock` at all, because the ACL\n * check in the routes runs first and unconditionally). Falling back would turn a\n * broken policy into permission. A thrower refuses instead, which is the same answer\n * the policy would have given if it could not satisfy itself.\n */\nfunction refusalOnFailure(): ReportApprovalRefusal {\n return { code: 'approval_policy_unavailable', messageKey: 'staff.errors.forbidden' }\n}\n\nfunction evaluatePolicyVerdict(\n ctx: ReportApprovalContext,\n read: (policy: ReportApprovalPolicy) => ReportApprovalVerdict,\n): ReportApprovalRefusal | null {\n for (const policy of applicablePolicies(ctx)) {\n const verdict = runStrategy(\n REPORT_APPROVAL_POLICY_REGISTRY_ID,\n policy.id,\n () => read(policy),\n () => refusalOnFailure(),\n )\n if (verdict) return verdict\n }\n return null\n}\n\n/** The first refusal wins; every policy must agree before a close proceeds. */\nexport function evaluateReportClosePolicies(ctx: ReportApprovalContext): ReportApprovalRefusal | null {\n return evaluatePolicyVerdict(ctx, (policy) => policy.canClose?.(ctx))\n}\n\nexport function evaluateReportUnlockPolicies(ctx: ReportApprovalContext): ReportApprovalRefusal | null {\n return evaluatePolicyVerdict(ctx, (policy) => policy.canUnlock?.(ctx))\n}\n\nexport type ReportApprovalNotificationFailure = {\n policyId: string\n error: unknown\n}\n\n/**\n * Fired after a close has committed. A throwing hook cannot unwind a freeze that\n * already happened, so failures are collected and handed back for the caller to\n * log \u2014 the same trade the export route makes with its audit row.\n */\nexport async function notifyReportClosed(\n ctx: ReportApprovalContext,\n): Promise<ReportApprovalNotificationFailure[]> {\n const failures: ReportApprovalNotificationFailure[] = []\n for (const policy of applicablePolicies(ctx)) {\n if (!policy.onClosed) continue\n try {\n await policy.onClosed(ctx)\n } catch (error) {\n failures.push({ policyId: policy.id, error })\n }\n }\n return failures\n}\n"],
5
+ "mappings": "AAcA,SAAS,uBAAuB;AAChC,SAAS,wBAAwB,kCAAkC;AACnE,SAAS,mBAAmB;AAC5B,SAAS,wBAAoD;AA6BtD,MAAM,qCAAqC,gBAAgB,MAAM,6BAA6B;AAE9F,MAAM,qCAAqC;AAElD,MAAM,WAAW,uBAA6C,kCAAkC;AAEzF,SAAS,6BAA6B,QAA0C;AACrF,SAAO,SAAS,SAAS,MAAM;AACjC;AAEO,SAAS,6BAAqD;AACnE,SAAO,SAAS,KAAK;AACvB;AAEO,SAAS,wBAAwB,IAA4D;AAClG,SAAO,SAAS,IAAI,EAAE;AACxB;AAEA,SAAS,gBAAgB;AAAA,EACvB,IAAI;AAAA,EACJ,UAAU;AACZ,CAAC;AAED,SAAS,mBAAmB,KAAoD;AAC9E,QAAM,SAAS,iBAAiB,GAAG;AACnC,SAAO,SACJ,KAAK,EACL,OAAO,CAAC,WAAW,UAAU,OAAO,OAAO,kCAAkC;AAClF;AAUA,SAAS,mBAA0C;AACjD,SAAO,EAAE,MAAM,+BAA+B,YAAY,yBAAyB;AACrF;AAEA,SAAS,sBACP,KACA,MAC8B;AAC9B,aAAW,UAAU,mBAAmB,GAAG,GAAG;AAC5C,UAAM,UAAU;AAAA,MACd;AAAA,MACA,OAAO;AAAA,MACP,MAAM,KAAK,MAAM;AAAA,MACjB,MAAM,iBAAiB;AAAA,IACzB;AACA,QAAI,QAAS,QAAO;AAAA,EACtB;AACA,SAAO;AACT;AAGO,SAAS,4BAA4B,KAA0D;AACpG,SAAO,sBAAsB,KAAK,CAAC,WAAW,OAAO,WAAW,GAAG,CAAC;AACtE;AAEO,SAAS,6BAA6B,KAA0D;AACrG,SAAO,sBAAsB,KAAK,CAAC,WAAW,OAAO,YAAY,GAAG,CAAC;AACvE;AAYA,eAAsB,mBACpB,KAC8C;AAC9C,QAAM,WAAgD,CAAC;AACvD,aAAW,UAAU,mBAAmB,GAAG,GAAG;AAC5C,QAAI,CAAC,OAAO,SAAU;AACtB,QAAI;AACF,YAAM,OAAO,SAAS,GAAG;AAAA,IAC3B,SAAS,OAAO;AACd,eAAS,KAAK,EAAE,UAAU,OAAO,IAAI,MAAM,CAAC;AAAA,IAC9C;AAAA,EACF;AACA,SAAO;AACT;",
6
+ "names": []
7
+ }