@open-mercato/core 0.6.4-develop.4217.1.c9aa050183 → 0.6.4-develop.4239.1.4a264a5828

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 (408) hide show
  1. package/.turbo/turbo-build.log +2 -2
  2. package/dist/generated/entities/staff_time_entry/index.js +37 -0
  3. package/dist/generated/entities/staff_time_entry/index.js.map +7 -0
  4. package/dist/generated/entities/staff_time_entry_segment/index.js +23 -0
  5. package/dist/generated/entities/staff_time_entry_segment/index.js.map +7 -0
  6. package/dist/generated/entities/staff_time_project/index.js +35 -0
  7. package/dist/generated/entities/staff_time_project/index.js.map +7 -0
  8. package/dist/generated/entities/staff_time_project_member/index.js +29 -0
  9. package/dist/generated/entities/staff_time_project_member/index.js.map +7 -0
  10. package/dist/generated/entities.ids.generated.js +5 -1
  11. package/dist/generated/entities.ids.generated.js.map +2 -2
  12. package/dist/generated/entity-fields-registry.js +64 -0
  13. package/dist/generated/entity-fields-registry.js.map +2 -2
  14. package/dist/helpers/integration/timesheetFixtures.js +50 -0
  15. package/dist/helpers/integration/timesheetFixtures.js.map +7 -0
  16. package/dist/modules/attachments/api/library/[id]/route.js +20 -16
  17. package/dist/modules/attachments/api/library/[id]/route.js.map +2 -2
  18. package/dist/modules/attachments/api/route.js +18 -14
  19. package/dist/modules/attachments/api/route.js.map +2 -2
  20. package/dist/modules/auth/api/roles/acl/route.js +10 -4
  21. package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
  22. package/dist/modules/auth/api/sidebar/preferences/route.js +27 -20
  23. package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
  24. package/dist/modules/auth/api/users/acl/route.js +16 -11
  25. package/dist/modules/auth/api/users/acl/route.js.map +2 -2
  26. package/dist/modules/auth/commands/users.js +87 -71
  27. package/dist/modules/auth/commands/users.js.map +2 -2
  28. package/dist/modules/auth/services/sidebarPreferencesService.js +39 -30
  29. package/dist/modules/auth/services/sidebarPreferencesService.js.map +2 -2
  30. package/dist/modules/business_rules/backend/logs/[id]/page.js +24 -5
  31. package/dist/modules/business_rules/backend/logs/[id]/page.js.map +2 -2
  32. package/dist/modules/catalog/api/offers/route.js +15 -5
  33. package/dist/modules/catalog/api/offers/route.js.map +2 -2
  34. package/dist/modules/catalog/commands/categories.js +61 -12
  35. package/dist/modules/catalog/commands/categories.js.map +2 -2
  36. package/dist/modules/catalog/commands/products.js +79 -54
  37. package/dist/modules/catalog/commands/products.js.map +2 -2
  38. package/dist/modules/catalog/commands/variants.js +29 -16
  39. package/dist/modules/catalog/commands/variants.js.map +2 -2
  40. package/dist/modules/currencies/backend/currencies/[id]/page.js +19 -2
  41. package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
  42. package/dist/modules/currencies/commands/currencies.js +15 -8
  43. package/dist/modules/currencies/commands/currencies.js.map +2 -2
  44. package/dist/modules/customer_accounts/api/admin/users.js +27 -26
  45. package/dist/modules/customer_accounts/api/admin/users.js.map +2 -2
  46. package/dist/modules/customer_accounts/api/password/reset-confirm.js +5 -5
  47. package/dist/modules/customer_accounts/api/password/reset-confirm.js.map +2 -2
  48. package/dist/modules/customer_accounts/api/portal/users/[id]/roles.js +11 -10
  49. package/dist/modules/customer_accounts/api/portal/users/[id]/roles.js.map +2 -2
  50. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js +27 -7
  51. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js.map +2 -2
  52. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js +27 -7
  53. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js.map +2 -2
  54. package/dist/modules/customers/backend/customers/people/[id]/page.js +29 -8
  55. package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
  56. package/dist/modules/customers/commands/addresses.js +35 -21
  57. package/dist/modules/customers/commands/addresses.js.map +2 -2
  58. package/dist/modules/customers/commands/companies.js +163 -162
  59. package/dist/modules/customers/commands/companies.js.map +2 -2
  60. package/dist/modules/customers/commands/deals.js +3 -4
  61. package/dist/modules/customers/commands/deals.js.map +2 -2
  62. package/dist/modules/customers/commands/interactions.js +19 -22
  63. package/dist/modules/customers/commands/interactions.js.map +2 -2
  64. package/dist/modules/customers/commands/people.js +18 -15
  65. package/dist/modules/customers/commands/people.js.map +2 -2
  66. package/dist/modules/customers/commands/personCompanyLinks.js +105 -94
  67. package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
  68. package/dist/modules/customers/commands/pipeline-stages.js +30 -23
  69. package/dist/modules/customers/commands/pipeline-stages.js.map +2 -2
  70. package/dist/modules/customers/commands/pipelines.js +27 -20
  71. package/dist/modules/customers/commands/pipelines.js.map +2 -2
  72. package/dist/modules/customers/commands/tags.js +13 -5
  73. package/dist/modules/customers/commands/tags.js.map +2 -2
  74. package/dist/modules/dashboards/api/users/widgets/route.js +0 -1
  75. package/dist/modules/dashboards/api/users/widgets/route.js.map +2 -2
  76. package/dist/modules/dashboards/api/widgets/data/route.js +29 -1
  77. package/dist/modules/dashboards/api/widgets/data/route.js.map +2 -2
  78. package/dist/modules/data_sync/lib/sync-engine.js +4 -4
  79. package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
  80. package/dist/modules/data_sync/lib/sync-run-service.js +51 -27
  81. package/dist/modules/data_sync/lib/sync-run-service.js.map +2 -2
  82. package/dist/modules/directory/commands/organizations.js +192 -158
  83. package/dist/modules/directory/commands/organizations.js.map +3 -3
  84. package/dist/modules/inbox_ops/api/emails/[id]/reprocess/route.js +22 -16
  85. package/dist/modules/inbox_ops/api/emails/[id]/reprocess/route.js.map +2 -2
  86. package/dist/modules/messages/commands/messages.js +77 -75
  87. package/dist/modules/messages/commands/messages.js.map +2 -2
  88. package/dist/modules/messages/commands/shared.js +132 -132
  89. package/dist/modules/messages/commands/shared.js.map +2 -2
  90. package/dist/modules/perspectives/api/[tableId]/route.js +37 -26
  91. package/dist/modules/perspectives/api/[tableId]/route.js.map +2 -2
  92. package/dist/modules/progress/acl.js +8 -4
  93. package/dist/modules/progress/acl.js.map +2 -2
  94. package/dist/modules/resources/commands/resources.js +125 -117
  95. package/dist/modules/resources/commands/resources.js.map +2 -2
  96. package/dist/modules/resources/commands/tags.js +7 -3
  97. package/dist/modules/resources/commands/tags.js.map +2 -2
  98. package/dist/modules/sales/api/quotes/send/route.js +12 -11
  99. package/dist/modules/sales/api/quotes/send/route.js.map +2 -2
  100. package/dist/modules/sales/commands/documents.js +629 -478
  101. package/dist/modules/sales/commands/documents.js.map +2 -2
  102. package/dist/modules/sales/commands/payments.js +146 -146
  103. package/dist/modules/sales/commands/payments.js.map +2 -2
  104. package/dist/modules/sales/commands/returns.js +68 -60
  105. package/dist/modules/sales/commands/returns.js.map +2 -2
  106. package/dist/modules/staff/acl.js +10 -1
  107. package/dist/modules/staff/acl.js.map +2 -2
  108. package/dist/modules/staff/analytics.js +33 -0
  109. package/dist/modules/staff/analytics.js.map +7 -0
  110. package/dist/modules/staff/api/guards.js +31 -0
  111. package/dist/modules/staff/api/guards.js.map +7 -0
  112. package/dist/modules/staff/api/interceptors.js +96 -0
  113. package/dist/modules/staff/api/interceptors.js.map +7 -0
  114. package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js +170 -0
  115. package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js.map +7 -0
  116. package/dist/modules/staff/api/timesheets/my-projects/route.js +103 -0
  117. package/dist/modules/staff/api/timesheets/my-projects/route.js.map +7 -0
  118. package/dist/modules/staff/api/timesheets/projects/kpis/route.js +147 -0
  119. package/dist/modules/staff/api/timesheets/projects/kpis/route.js.map +7 -0
  120. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js +171 -0
  121. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.js.map +7 -0
  122. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js +180 -0
  123. package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js.map +7 -0
  124. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +155 -0
  125. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +7 -0
  126. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js +173 -0
  127. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js.map +7 -0
  128. package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js +260 -0
  129. package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js.map +7 -0
  130. package/dist/modules/staff/api/timesheets/time-entries/route.js +188 -0
  131. package/dist/modules/staff/api/timesheets/time-entries/route.js.map +7 -0
  132. package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js +159 -0
  133. package/dist/modules/staff/api/timesheets/time-projects/[id]/employees/route.js.map +7 -0
  134. package/dist/modules/staff/api/timesheets/time-projects/route.js +230 -0
  135. package/dist/modules/staff/api/timesheets/time-projects/route.js.map +7 -0
  136. package/dist/modules/staff/backend/staff/timesheets/page.js +710 -0
  137. package/dist/modules/staff/backend/staff/timesheets/page.js.map +7 -0
  138. package/dist/modules/staff/backend/staff/timesheets/page.meta.js +22 -0
  139. package/dist/modules/staff/backend/staff/timesheets/page.meta.js.map +7 -0
  140. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.js +125 -0
  141. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.js.map +7 -0
  142. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.js +16 -0
  143. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.js.map +7 -0
  144. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js +418 -0
  145. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js.map +7 -0
  146. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.js +16 -0
  147. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.js.map +7 -0
  148. package/dist/modules/staff/backend/staff/timesheets/projects/create/page.js +79 -0
  149. package/dist/modules/staff/backend/staff/timesheets/projects/create/page.js.map +7 -0
  150. package/dist/modules/staff/backend/staff/timesheets/projects/create/page.meta.js +16 -0
  151. package/dist/modules/staff/backend/staff/timesheets/projects/create/page.meta.js.map +7 -0
  152. package/dist/modules/staff/backend/staff/timesheets/projects/page.js +602 -0
  153. package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +7 -0
  154. package/dist/modules/staff/backend/staff/timesheets/projects/page.meta.js +25 -0
  155. package/dist/modules/staff/backend/staff/timesheets/projects/page.meta.js.map +7 -0
  156. package/dist/modules/staff/backend/staff/timesheets/projects/projectFormConfig.js +123 -0
  157. package/dist/modules/staff/backend/staff/timesheets/projects/projectFormConfig.js.map +7 -0
  158. package/dist/modules/staff/cli.js +38 -1
  159. package/dist/modules/staff/cli.js.map +2 -2
  160. package/dist/modules/staff/commands/index.js +2 -0
  161. package/dist/modules/staff/commands/index.js.map +2 -2
  162. package/dist/modules/staff/commands/leave-requests.js +30 -28
  163. package/dist/modules/staff/commands/leave-requests.js.map +3 -3
  164. package/dist/modules/staff/commands/team-members.js +21 -20
  165. package/dist/modules/staff/commands/team-members.js.map +2 -2
  166. package/dist/modules/staff/commands/timesheets-entries.js +409 -0
  167. package/dist/modules/staff/commands/timesheets-entries.js.map +7 -0
  168. package/dist/modules/staff/commands/timesheets-projects.js +618 -0
  169. package/dist/modules/staff/commands/timesheets-projects.js.map +7 -0
  170. package/dist/modules/staff/data/enrichers.js +104 -0
  171. package/dist/modules/staff/data/enrichers.js.map +7 -0
  172. package/dist/modules/staff/data/entities.js +226 -1
  173. package/dist/modules/staff/data/entities.js.map +2 -2
  174. package/dist/modules/staff/data/validators.js +113 -1
  175. package/dist/modules/staff/data/validators.js.map +2 -2
  176. package/dist/modules/staff/events.js +13 -1
  177. package/dist/modules/staff/events.js.map +2 -2
  178. package/dist/modules/staff/lib/crud.js +7 -1
  179. package/dist/modules/staff/lib/crud.js.map +2 -2
  180. package/dist/modules/staff/lib/staffMemberResolver.js +15 -0
  181. package/dist/modules/staff/lib/staffMemberResolver.js.map +7 -0
  182. package/dist/modules/staff/lib/timesheets-projects/computeProjectHoursTrend.js +60 -0
  183. package/dist/modules/staff/lib/timesheets-projects/computeProjectHoursTrend.js.map +7 -0
  184. package/dist/modules/staff/lib/timesheets-projects/computeProjectsKpis.js +260 -0
  185. package/dist/modules/staff/lib/timesheets-projects/computeProjectsKpis.js.map +7 -0
  186. package/dist/modules/staff/lib/timesheets-projects/dateBuckets.js +41 -0
  187. package/dist/modules/staff/lib/timesheets-projects/dateBuckets.js.map +7 -0
  188. package/dist/modules/staff/lib/timesheets-projects/initials.js +10 -0
  189. package/dist/modules/staff/lib/timesheets-projects/initials.js.map +7 -0
  190. package/dist/modules/staff/lib/timesheets-projects/kpiMath.js +12 -0
  191. package/dist/modules/staff/lib/timesheets-projects/kpiMath.js.map +7 -0
  192. package/dist/modules/staff/lib/timesheets-projects/listProjectMembersPreview.js +55 -0
  193. package/dist/modules/staff/lib/timesheets-projects/listProjectMembersPreview.js.map +7 -0
  194. package/dist/modules/staff/lib/timesheets-projects-ui/HoursSparkline.js +66 -0
  195. package/dist/modules/staff/lib/timesheets-projects-ui/HoursSparkline.js.map +7 -0
  196. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectCard.js +81 -0
  197. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectCard.js.map +7 -0
  198. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectMembersAvatarStack.js +58 -0
  199. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectMembersAvatarStack.js.map +7 -0
  200. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectsKpiStrip.js +152 -0
  201. package/dist/modules/staff/lib/timesheets-projects-ui/ProjectsKpiStrip.js.map +7 -0
  202. package/dist/modules/staff/lib/timesheets-projects-ui/SavedViewTabs.js +37 -0
  203. package/dist/modules/staff/lib/timesheets-projects-ui/SavedViewTabs.js.map +7 -0
  204. package/dist/modules/staff/lib/timesheets-projects-ui/ViewModeToggle.js +57 -0
  205. package/dist/modules/staff/lib/timesheets-projects-ui/ViewModeToggle.js.map +7 -0
  206. package/dist/modules/staff/lib/timesheets-projects-ui/useProjectsViewMode.js +50 -0
  207. package/dist/modules/staff/lib/timesheets-projects-ui/useProjectsViewMode.js.map +7 -0
  208. package/dist/modules/staff/lib/timesheets-ui/AddRowDropdown.js +163 -0
  209. package/dist/modules/staff/lib/timesheets-ui/AddRowDropdown.js.map +7 -0
  210. package/dist/modules/staff/lib/timesheets-ui/CalendarPicker.js +209 -0
  211. package/dist/modules/staff/lib/timesheets-ui/CalendarPicker.js.map +7 -0
  212. package/dist/modules/staff/lib/timesheets-ui/ColorPicker.js +52 -0
  213. package/dist/modules/staff/lib/timesheets-ui/ColorPicker.js.map +7 -0
  214. package/dist/modules/staff/lib/timesheets-ui/CreateProjectDialog.js +77 -0
  215. package/dist/modules/staff/lib/timesheets-ui/CreateProjectDialog.js.map +7 -0
  216. package/dist/modules/staff/lib/timesheets-ui/ListView.js +173 -0
  217. package/dist/modules/staff/lib/timesheets-ui/ListView.js.map +7 -0
  218. package/dist/modules/staff/lib/timesheets-ui/ProjectColorDot.js +32 -0
  219. package/dist/modules/staff/lib/timesheets-ui/ProjectColorDot.js.map +7 -0
  220. package/dist/modules/staff/lib/timesheets-ui/TimerBar.js +270 -0
  221. package/dist/modules/staff/lib/timesheets-ui/TimerBar.js.map +7 -0
  222. package/dist/modules/staff/lib/timesheets-ui/ViewSwitcher.js +57 -0
  223. package/dist/modules/staff/lib/timesheets-ui/ViewSwitcher.js.map +7 -0
  224. package/dist/modules/staff/lib/timesheets-ui/colors.js +43 -0
  225. package/dist/modules/staff/lib/timesheets-ui/colors.js.map +7 -0
  226. package/dist/modules/staff/migrations/Migration20260326135612.js +24 -0
  227. package/dist/modules/staff/migrations/Migration20260326135612.js.map +7 -0
  228. package/dist/modules/staff/migrations/Migration20260413102715.js +23 -0
  229. package/dist/modules/staff/migrations/Migration20260413102715.js.map +7 -0
  230. package/dist/modules/staff/migrations/Migration20260413111602.js +13 -0
  231. package/dist/modules/staff/migrations/Migration20260413111602.js.map +7 -0
  232. package/dist/modules/staff/migrations/Migration20260511112759.js +19 -0
  233. package/dist/modules/staff/migrations/Migration20260511112759.js.map +7 -0
  234. package/dist/modules/staff/search.js +35 -0
  235. package/dist/modules/staff/search.js.map +2 -2
  236. package/dist/modules/staff/setup.js +15 -1
  237. package/dist/modules/staff/setup.js.map +2 -2
  238. package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/config.js +16 -0
  239. package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/config.js.map +7 -0
  240. package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.client.js +126 -0
  241. package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.client.js.map +7 -0
  242. package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.js +26 -0
  243. package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.js.map +7 -0
  244. package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/config.js +15 -0
  245. package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/config.js.map +7 -0
  246. package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.js +238 -0
  247. package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.js.map +7 -0
  248. package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.js +26 -0
  249. package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.js.map +7 -0
  250. package/dist/modules/staff/widgets/injection/timer-sidebar-indicator/widget.js +145 -0
  251. package/dist/modules/staff/widgets/injection/timer-sidebar-indicator/widget.js.map +7 -0
  252. package/dist/modules/staff/widgets/injection-table.js +12 -0
  253. package/dist/modules/staff/widgets/injection-table.js.map +7 -0
  254. package/dist/modules/sync_excel/api/import/route.js +19 -17
  255. package/dist/modules/sync_excel/api/import/route.js.map +2 -2
  256. package/dist/modules/translations/commands/translations.js +22 -19
  257. package/dist/modules/translations/commands/translations.js.map +2 -2
  258. package/dist/modules/workflows/backend/events/[id]/page.js +24 -6
  259. package/dist/modules/workflows/backend/events/[id]/page.js.map +2 -2
  260. package/dist/modules/workflows/backend/instances/[id]/page.js +27 -5
  261. package/dist/modules/workflows/backend/instances/[id]/page.js.map +2 -2
  262. package/dist/modules/workflows/backend/tasks/[id]/page.js +25 -6
  263. package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
  264. package/generated/entities/staff_time_entry/index.ts +17 -0
  265. package/generated/entities/staff_time_entry_segment/index.ts +10 -0
  266. package/generated/entities/staff_time_project/index.ts +16 -0
  267. package/generated/entities/staff_time_project_member/index.ts +13 -0
  268. package/generated/entities.ids.generated.ts +5 -1
  269. package/generated/entity-fields-registry.ts +64 -0
  270. package/package.json +7 -7
  271. package/src/helpers/integration/timesheetFixtures.ts +61 -0
  272. package/src/modules/attachments/api/library/[id]/route.ts +24 -17
  273. package/src/modules/attachments/api/route.ts +20 -14
  274. package/src/modules/auth/api/roles/acl/route.ts +11 -5
  275. package/src/modules/auth/api/sidebar/preferences/route.ts +33 -24
  276. package/src/modules/auth/api/users/acl/route.ts +17 -12
  277. package/src/modules/auth/commands/users.ts +96 -80
  278. package/src/modules/auth/services/sidebarPreferencesService.ts +40 -32
  279. package/src/modules/business_rules/backend/logs/[id]/page.tsx +32 -7
  280. package/src/modules/catalog/api/offers/route.ts +20 -5
  281. package/src/modules/catalog/commands/categories.ts +61 -12
  282. package/src/modules/catalog/commands/products.ts +93 -60
  283. package/src/modules/catalog/commands/variants.ts +29 -16
  284. package/src/modules/currencies/backend/currencies/[id]/page.tsx +21 -2
  285. package/src/modules/currencies/commands/currencies.ts +27 -14
  286. package/src/modules/currencies/i18n/de.json +1 -0
  287. package/src/modules/currencies/i18n/en.json +1 -0
  288. package/src/modules/currencies/i18n/es.json +1 -0
  289. package/src/modules/currencies/i18n/pl.json +1 -0
  290. package/src/modules/customer_accounts/api/admin/users.ts +31 -26
  291. package/src/modules/customer_accounts/api/password/reset-confirm.ts +5 -6
  292. package/src/modules/customer_accounts/api/portal/users/[id]/roles.ts +14 -13
  293. package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +34 -11
  294. package/src/modules/customer_accounts/backend/customer_accounts/users/[id]/page.tsx +34 -11
  295. package/src/modules/customers/backend/customers/people/[id]/page.tsx +35 -11
  296. package/src/modules/customers/commands/addresses.ts +35 -23
  297. package/src/modules/customers/commands/companies.ts +166 -165
  298. package/src/modules/customers/commands/deals.ts +2 -4
  299. package/src/modules/customers/commands/interactions.ts +20 -26
  300. package/src/modules/customers/commands/people.ts +18 -15
  301. package/src/modules/customers/commands/personCompanyLinks.ts +109 -100
  302. package/src/modules/customers/commands/pipeline-stages.ts +31 -27
  303. package/src/modules/customers/commands/pipelines.ts +29 -23
  304. package/src/modules/customers/commands/tags.ts +13 -5
  305. package/src/modules/dashboards/api/users/widgets/route.ts +0 -1
  306. package/src/modules/dashboards/api/widgets/data/route.ts +36 -1
  307. package/src/modules/data_sync/lib/sync-engine.ts +4 -5
  308. package/src/modules/data_sync/lib/sync-run-service.ts +57 -28
  309. package/src/modules/directory/commands/organizations.ts +203 -166
  310. package/src/modules/inbox_ops/api/emails/[id]/reprocess/route.ts +26 -18
  311. package/src/modules/messages/commands/messages.ts +82 -80
  312. package/src/modules/messages/commands/shared.ts +138 -133
  313. package/src/modules/perspectives/api/[tableId]/route.ts +38 -27
  314. package/src/modules/progress/acl.ts +4 -0
  315. package/src/modules/resources/commands/resources.ts +127 -117
  316. package/src/modules/resources/commands/tags.ts +7 -3
  317. package/src/modules/sales/api/quotes/send/route.ts +17 -12
  318. package/src/modules/sales/commands/documents.ts +673 -481
  319. package/src/modules/sales/commands/payments.ts +158 -152
  320. package/src/modules/sales/commands/returns.ts +74 -63
  321. package/src/modules/staff/acl.ts +11 -0
  322. package/src/modules/staff/analytics.ts +30 -0
  323. package/src/modules/staff/api/guards.ts +59 -0
  324. package/src/modules/staff/api/interceptors.ts +122 -0
  325. package/src/modules/staff/api/timesheets/my-projects/[projectId]/route.ts +191 -0
  326. package/src/modules/staff/api/timesheets/my-projects/route.ts +115 -0
  327. package/src/modules/staff/api/timesheets/projects/kpis/route.ts +159 -0
  328. package/src/modules/staff/api/timesheets/time-entries/[id]/segments/[segmentId]/route.ts +187 -0
  329. package/src/modules/staff/api/timesheets/time-entries/[id]/segments/route.ts +191 -0
  330. package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +168 -0
  331. package/src/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.ts +191 -0
  332. package/src/modules/staff/api/timesheets/time-entries/bulk/route.ts +292 -0
  333. package/src/modules/staff/api/timesheets/time-entries/route.ts +193 -0
  334. package/src/modules/staff/api/timesheets/time-projects/[id]/employees/route.ts +167 -0
  335. package/src/modules/staff/api/timesheets/time-projects/route.ts +244 -0
  336. package/src/modules/staff/backend/staff/timesheets/page.meta.ts +20 -0
  337. package/src/modules/staff/backend/staff/timesheets/page.tsx +899 -0
  338. package/src/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.meta.ts +12 -0
  339. package/src/modules/staff/backend/staff/timesheets/projects/[id]/edit/page.tsx +141 -0
  340. package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.meta.ts +12 -0
  341. package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.tsx +579 -0
  342. package/src/modules/staff/backend/staff/timesheets/projects/create/page.meta.ts +12 -0
  343. package/src/modules/staff/backend/staff/timesheets/projects/create/page.tsx +90 -0
  344. package/src/modules/staff/backend/staff/timesheets/projects/page.meta.ts +23 -0
  345. package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +765 -0
  346. package/src/modules/staff/backend/staff/timesheets/projects/projectFormConfig.ts +138 -0
  347. package/src/modules/staff/cli.ts +40 -1
  348. package/src/modules/staff/commands/index.ts +2 -0
  349. package/src/modules/staff/commands/leave-requests.ts +37 -29
  350. package/src/modules/staff/commands/team-members.ts +25 -20
  351. package/src/modules/staff/commands/timesheets-entries.ts +504 -0
  352. package/src/modules/staff/commands/timesheets-projects.ts +699 -0
  353. package/src/modules/staff/data/enrichers.ts +134 -0
  354. package/src/modules/staff/data/entities.ts +198 -0
  355. package/src/modules/staff/data/validators.ts +129 -0
  356. package/src/modules/staff/events.ts +13 -0
  357. package/src/modules/staff/i18n/de.json +209 -1
  358. package/src/modules/staff/i18n/en.json +209 -1
  359. package/src/modules/staff/i18n/es.json +209 -1
  360. package/src/modules/staff/i18n/pl.json +209 -1
  361. package/src/modules/staff/lib/crud.ts +8 -0
  362. package/src/modules/staff/lib/staffMemberResolver.ts +22 -0
  363. package/src/modules/staff/lib/timesheets-projects/computeProjectHoursTrend.ts +89 -0
  364. package/src/modules/staff/lib/timesheets-projects/computeProjectsKpis.ts +311 -0
  365. package/src/modules/staff/lib/timesheets-projects/dateBuckets.ts +37 -0
  366. package/src/modules/staff/lib/timesheets-projects/initials.ts +6 -0
  367. package/src/modules/staff/lib/timesheets-projects/kpiMath.ts +8 -0
  368. package/src/modules/staff/lib/timesheets-projects/listProjectMembersPreview.ts +83 -0
  369. package/src/modules/staff/lib/timesheets-projects-ui/HoursSparkline.tsx +75 -0
  370. package/src/modules/staff/lib/timesheets-projects-ui/ProjectCard.tsx +110 -0
  371. package/src/modules/staff/lib/timesheets-projects-ui/ProjectMembersAvatarStack.tsx +73 -0
  372. package/src/modules/staff/lib/timesheets-projects-ui/ProjectsKpiStrip.tsx +185 -0
  373. package/src/modules/staff/lib/timesheets-projects-ui/SavedViewTabs.tsx +53 -0
  374. package/src/modules/staff/lib/timesheets-projects-ui/ViewModeToggle.tsx +63 -0
  375. package/src/modules/staff/lib/timesheets-projects-ui/useProjectsViewMode.ts +63 -0
  376. package/src/modules/staff/lib/timesheets-ui/AddRowDropdown.tsx +188 -0
  377. package/src/modules/staff/lib/timesheets-ui/CalendarPicker.tsx +229 -0
  378. package/src/modules/staff/lib/timesheets-ui/ColorPicker.tsx +65 -0
  379. package/src/modules/staff/lib/timesheets-ui/CreateProjectDialog.tsx +99 -0
  380. package/src/modules/staff/lib/timesheets-ui/ListView.tsx +230 -0
  381. package/src/modules/staff/lib/timesheets-ui/ProjectColorDot.tsx +40 -0
  382. package/src/modules/staff/lib/timesheets-ui/TimerBar.tsx +327 -0
  383. package/src/modules/staff/lib/timesheets-ui/ViewSwitcher.tsx +60 -0
  384. package/src/modules/staff/lib/timesheets-ui/colors.ts +58 -0
  385. package/src/modules/staff/migrations/.snapshot-open-mercato.json +1148 -0
  386. package/src/modules/staff/migrations/Migration20260326135612.ts +26 -0
  387. package/src/modules/staff/migrations/Migration20260413102715.ts +25 -0
  388. package/src/modules/staff/migrations/Migration20260413111602.ts +13 -0
  389. package/src/modules/staff/migrations/Migration20260511112759.ts +21 -0
  390. package/src/modules/staff/search.ts +35 -0
  391. package/src/modules/staff/setup.ts +15 -0
  392. package/src/modules/staff/widgets/dashboard/timesheets-hours-by-project/config.ts +17 -0
  393. package/src/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.client.tsx +158 -0
  394. package/src/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.ts +25 -0
  395. package/src/modules/staff/widgets/dashboard/timesheets-time-reporting/config.ts +15 -0
  396. package/src/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.tsx +297 -0
  397. package/src/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.ts +25 -0
  398. package/src/modules/staff/widgets/injection/timer-sidebar-indicator/widget.tsx +161 -0
  399. package/src/modules/staff/widgets/injection-table.ts +10 -0
  400. package/src/modules/sync_excel/api/import/route.ts +23 -18
  401. package/src/modules/translations/commands/translations.ts +49 -41
  402. package/src/modules/workflows/backend/events/[id]/page.tsx +32 -10
  403. package/src/modules/workflows/backend/instances/[id]/page.tsx +33 -9
  404. package/src/modules/workflows/backend/tasks/[id]/page.tsx +33 -10
  405. package/src/modules/workflows/i18n/de.json +1 -0
  406. package/src/modules/workflows/i18n/en.json +1 -0
  407. package/src/modules/workflows/i18n/es.json +1 -0
  408. package/src/modules/workflows/i18n/pl.json +1 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/sales/commands/returns.ts"],
4
- "sourcesContent": ["import { randomUUID } from 'crypto'\nimport { registerCommand, type CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { LockMode } from '@mikro-orm/core'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { SalesDocumentNumberGenerator } from '../services/salesDocumentNumberGenerator'\nimport type { SalesCalculationService } from '../services/salesCalculationService'\nimport type { SalesAdjustmentDraft, SalesLineSnapshot, SalesDocumentCalculationResult } from '../lib/types'\nimport { cloneJson, ensureOrganizationScope, ensureSameScope, ensureTenantScope, extractUndoPayload, toNumericString } from './shared'\nimport { SalesOrder, SalesOrderAdjustment, SalesOrderLine, SalesReturn, SalesReturnLine } from '../data/entities'\nimport { returnCreateSchema, type ReturnCreateInput } from '../data/validators'\nimport { E } from '#generated/entities.ids.generated'\n\ntype ReturnLineInput = { orderLineId: string; quantity: number }\n\ntype ReturnSnapshot = {\n id: string\n orderId: string\n organizationId: string\n tenantId: string\n returnNumber: string\n returnedAt: string | null\n reason: string | null\n notes: string | null\n lines: Array<{\n id: string\n orderLineId: string\n quantityReturned: number\n unitPriceNet: number\n unitPriceGross: number\n totalNetAmount: number\n totalGrossAmount: number\n }>\n adjustmentIds: string[]\n}\n\ntype ReturnUndoPayload = {\n after?: ReturnSnapshot | null\n}\n\nconst returnCrudEvents: CrudEventsConfig = {\n module: 'sales',\n entity: 'return',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nfunction toNumeric(value: unknown): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim().length) {\n const parsed = Number(value)\n if (Number.isFinite(parsed)) return parsed\n }\n return 0\n}\n\nfunction round(value: number): number {\n return Math.round((value + Number.EPSILON) * 1e4) / 1e4\n}\n\nfunction applyOrderTotals(order: SalesOrder, totals: SalesDocumentCalculationResult['totals'], lineCount: number): void {\n order.subtotalNetAmount = toNumericString(totals.subtotalNetAmount) ?? '0'\n order.subtotalGrossAmount = toNumericString(totals.subtotalGrossAmount) ?? '0'\n order.discountTotalAmount = toNumericString(totals.discountTotalAmount) ?? '0'\n order.taxTotalAmount = toNumericString(totals.taxTotalAmount) ?? '0'\n order.shippingNetAmount = toNumericString(totals.shippingNetAmount) ?? '0'\n order.shippingGrossAmount = toNumericString(totals.shippingGrossAmount) ?? '0'\n order.surchargeTotalAmount = toNumericString(totals.surchargeTotalAmount) ?? '0'\n order.grandTotalNetAmount = toNumericString(totals.grandTotalNetAmount) ?? '0'\n order.grandTotalGrossAmount = toNumericString(totals.grandTotalGrossAmount) ?? '0'\n order.paidTotalAmount = toNumericString(totals.paidTotalAmount) ?? '0'\n order.refundedTotalAmount = toNumericString(totals.refundedTotalAmount) ?? '0'\n order.outstandingAmount = toNumericString(totals.outstandingAmount) ?? '0'\n order.totalsSnapshot = cloneJson(totals)\n order.lineItemCount = lineCount\n}\n\nfunction mapOrderLineEntityToSnapshot(line: SalesOrderLine): SalesLineSnapshot {\n return {\n id: line.id,\n lineNumber: line.lineNumber,\n kind: line.kind,\n productId: line.productId ?? null,\n productVariantId: line.productVariantId ?? null,\n name: line.name ?? null,\n description: line.description ?? null,\n comment: line.comment ?? null,\n quantity: toNumeric(line.quantity),\n quantityUnit: line.quantityUnit ?? null,\n normalizedQuantity: toNumeric(line.normalizedQuantity ?? line.quantity),\n normalizedUnit: line.normalizedUnit ?? line.quantityUnit ?? null,\n uomSnapshot: line.uomSnapshot ? cloneJson(line.uomSnapshot) : null,\n currencyCode: line.currencyCode,\n unitPriceNet: toNumeric(line.unitPriceNet),\n unitPriceGross: toNumeric(line.unitPriceGross),\n discountAmount: toNumeric(line.discountAmount),\n discountPercent: toNumeric(line.discountPercent),\n taxRate: toNumeric(line.taxRate),\n taxAmount: toNumeric(line.taxAmount),\n totalNetAmount: toNumeric(line.totalNetAmount),\n totalGrossAmount: toNumeric(line.totalGrossAmount),\n configuration: line.configuration ? cloneJson(line.configuration) : null,\n promotionCode: line.promotionCode ?? null,\n metadata: line.metadata ? cloneJson(line.metadata) : null,\n customFieldSetId: line.customFieldSetId ?? null,\n }\n}\n\nfunction mapOrderAdjustmentToDraft(adjustment: SalesOrderAdjustment): SalesAdjustmentDraft {\n return {\n id: adjustment.id,\n scope: adjustment.scope ?? 'order',\n kind: adjustment.kind,\n code: adjustment.code ?? null,\n label: adjustment.label ?? null,\n calculatorKey: adjustment.calculatorKey ?? null,\n promotionId: adjustment.promotionId ?? null,\n rate: toNumeric(adjustment.rate),\n amountNet: toNumeric(adjustment.amountNet),\n amountGross: toNumeric(adjustment.amountGross),\n currencyCode: adjustment.currencyCode ?? null,\n metadata: adjustment.metadata ? cloneJson(adjustment.metadata) : null,\n position: adjustment.position ?? 0,\n }\n}\n\nfunction buildCalculationContext(order: SalesOrder) {\n return {\n tenantId: order.tenantId,\n organizationId: order.organizationId,\n currencyCode: order.currencyCode,\n metadata: {\n shippingMethod: order.shippingMethodSnapshot\n ? cloneJson(order.shippingMethodSnapshot as Record<string, unknown>)\n : null,\n paymentMethod: order.paymentMethodSnapshot ? cloneJson(order.paymentMethodSnapshot as Record<string, unknown>) : null,\n },\n }\n}\n\n/**\n * Recalculates order totals (including line-scoped return adjustments) for display.\n * Returns the totals object to merge into an order API response, or null if order not found.\n */\nexport async function recalculateOrderTotalsForDisplay(\n em: EntityManager,\n container: { resolve: (key: string) => unknown },\n orderId: string,\n scope: { tenantId: string; organizationId: string },\n): Promise<SalesDocumentCalculationResult['totals'] | null> {\n const order = await findOneWithDecryption(\n em,\n SalesOrder,\n { id: orderId, deletedAt: null },\n {},\n scope,\n )\n if (!order) return null\n const [orderLines, adjustments] = await Promise.all([\n findWithDecryption(em, SalesOrderLine, { order: order.id, deletedAt: null }, {}, scope),\n findWithDecryption(\n em,\n SalesOrderAdjustment,\n { order: order.id, deletedAt: null },\n { orderBy: { position: 'asc' } },\n scope,\n ),\n ])\n const lineSnapshots: SalesLineSnapshot[] = orderLines.map(mapOrderLineEntityToSnapshot)\n const adjustmentDrafts: SalesAdjustmentDraft[] = adjustments.map(mapOrderAdjustmentToDraft)\n const salesCalculationService = container.resolve('salesCalculationService') as SalesCalculationService\n const calculation = await salesCalculationService.calculateDocumentTotals({\n documentKind: 'order',\n lines: lineSnapshots,\n adjustments: adjustmentDrafts,\n context: buildCalculationContext(order),\n existingTotals: {\n paidTotalAmount: toNumeric(order.paidTotalAmount),\n refundedTotalAmount: toNumeric(order.refundedTotalAmount),\n },\n })\n return calculation.totals\n}\n\nexport async function loadReturnSnapshot(em: EntityManager, id: string): Promise<ReturnSnapshot | null> {\n const header = await findOneWithDecryption(\n em,\n SalesReturn,\n { id, deletedAt: null },\n { populate: ['order'] },\n {},\n )\n if (!header || !header.order) return null\n const orderId = typeof header.order === 'string' ? header.order : header.order.id\n const lines = await findWithDecryption(\n em,\n SalesReturnLine,\n { salesReturn: header.id, deletedAt: null },\n { populate: ['orderLine'] },\n { tenantId: header.tenantId, organizationId: header.organizationId },\n )\n const adjustmentIds: string[] = []\n const adjustments = await findWithDecryption(\n em,\n SalesOrderAdjustment,\n { order: orderId, kind: 'return', deletedAt: null },\n {},\n { tenantId: header.tenantId, organizationId: header.organizationId },\n )\n adjustments.forEach((adj) => {\n const meta = adj.metadata as Record<string, unknown> | null | undefined\n if (meta && meta.returnId === header.id) adjustmentIds.push(adj.id)\n })\n\n return {\n id: header.id,\n orderId,\n organizationId: header.organizationId,\n tenantId: header.tenantId,\n returnNumber: header.returnNumber,\n returnedAt: header.returnedAt ? header.returnedAt.toISOString() : null,\n reason: header.reason ?? null,\n notes: header.notes ?? null,\n lines: lines.map((line) => ({\n id: line.id,\n orderLineId: typeof line.orderLine === 'string' ? line.orderLine : line.orderLine?.id ?? null,\n quantityReturned: toNumeric(line.quantityReturned),\n unitPriceNet: toNumeric(line.unitPriceNet),\n unitPriceGross: toNumeric(line.unitPriceGross),\n totalNetAmount: toNumeric(line.totalNetAmount),\n totalGrossAmount: toNumeric(line.totalGrossAmount),\n })),\n adjustmentIds,\n }\n}\n\nfunction normalizeLinesInput(lines: ReturnCreateInput['lines']): ReturnLineInput[] {\n const seen = new Set<string>()\n const result: ReturnLineInput[] = []\n for (const line of lines) {\n const orderLineId = line.orderLineId\n if (!orderLineId || seen.has(orderLineId)) continue\n const quantity = toNumeric(line.quantity)\n if (!Number.isFinite(quantity) || quantity <= 0) continue\n seen.add(orderLineId)\n result.push({ orderLineId, quantity })\n }\n return result\n}\n\nconst createReturnCommand: CommandHandler<ReturnCreateInput, { returnId: string }> = {\n id: 'sales.returns.create',\n async execute(rawInput, ctx) {\n const input = returnCreateSchema.parse(rawInput ?? {})\n ensureTenantScope(ctx, input.tenantId)\n ensureOrganizationScope(ctx, input.organizationId)\n\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n const requested = normalizeLinesInput(input.lines)\n if (!requested.length) {\n throw new CrudHttpError(400, { error: translate('sales.returns.linesRequired', 'Select at least one line to return.') })\n }\n\n const salesCalculationService = ctx.container.resolve<SalesCalculationService>('salesCalculationService')\n const { header, createdLines } = await em.transactional(async (tx) => {\n const order = await findOneWithDecryption(\n tx,\n SalesOrder,\n { id: input.orderId, deletedAt: null },\n {},\n { tenantId: input.tenantId, organizationId: input.organizationId },\n )\n if (!order) {\n throw new CrudHttpError(404, { error: translate('sales.returns.orderMissing', 'Order not found.') })\n }\n ensureSameScope(order, input.organizationId, input.tenantId)\n\n const orderLines = await findWithDecryption(\n tx,\n SalesOrderLine,\n { order: order.id, deletedAt: null },\n { lockMode: LockMode.PESSIMISTIC_WRITE },\n { tenantId: input.tenantId, organizationId: input.organizationId },\n )\n const lineMap = new Map(orderLines.map((line) => [line.id, line]))\n\n requested.forEach(({ orderLineId, quantity }) => {\n const line = lineMap.get(orderLineId)\n if (!line) {\n throw new CrudHttpError(404, { error: translate('sales.returns.lineMissing', 'Order line not found.') })\n }\n const available = toNumeric(line.quantity) - toNumeric(line.returnedQuantity)\n if (quantity - 1e-6 > available) {\n throw new CrudHttpError(400, { error: translate('sales.returns.quantityExceeded', 'Cannot return more than the remaining quantity.') })\n }\n })\n\n const existingAdjustments = await findWithDecryption(\n tx,\n SalesOrderAdjustment,\n { order: order.id, deletedAt: null },\n { orderBy: { position: 'asc' } },\n { tenantId: input.tenantId, organizationId: input.organizationId },\n )\n const positionStart = existingAdjustments.reduce((acc, adj) => Math.max(acc, adj.position ?? 0), 0) + 1\n\n const numberGenerator = new SalesDocumentNumberGenerator(tx)\n const generated = await numberGenerator.generate({\n kind: 'return',\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n const returnId = randomUUID()\n const entity = tx.create(SalesReturn, {\n id: returnId,\n order,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n returnNumber: generated.number,\n reason: input.reason ?? null,\n notes: input.notes ?? null,\n returnedAt: input.returnedAt ?? new Date(),\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n tx.persist(entity)\n\n const createdAdjustments: SalesOrderAdjustment[] = []\n const createdReturnLines: SalesReturnLine[] = []\n requested.forEach((lineInput, index) => {\n const line = lineMap.get(lineInput.orderLineId)\n if (!line) return\n const quantity = lineInput.quantity\n const lineQuantity = Math.max(toNumeric(line.quantity), 0)\n const unitNet = lineQuantity > 0 ? toNumeric(line.totalNetAmount) / lineQuantity : toNumeric(line.unitPriceNet)\n const unitGross = lineQuantity > 0 ? toNumeric(line.totalGrossAmount) / lineQuantity : toNumeric(line.unitPriceGross)\n const totalNet = -round(Math.max(unitNet, 0) * quantity)\n const totalGross = -round(Math.max(unitGross, 0) * quantity)\n\n const returnLineId = randomUUID()\n const returnLine = tx.create(SalesReturnLine, {\n id: returnLineId,\n salesReturn: entity,\n orderLine: tx.getReference(SalesOrderLine, line.id),\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n quantityReturned: quantity.toString(),\n unitPriceNet: round(unitNet).toString(),\n unitPriceGross: round(unitGross).toString(),\n totalNetAmount: totalNet.toString(),\n totalGrossAmount: totalGross.toString(),\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n createdReturnLines.push(returnLine)\n tx.persist(returnLine)\n\n const adjustment = tx.create(SalesOrderAdjustment, {\n id: randomUUID(),\n order,\n orderLine: tx.getReference(SalesOrderLine, line.id),\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n scope: 'line',\n kind: 'return',\n rate: '0',\n amountNet: totalNet.toString(),\n amountGross: totalGross.toString(),\n currencyCode: order.currencyCode,\n metadata: { returnId, returnLineId },\n position: positionStart + index,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n createdAdjustments.push(adjustment)\n tx.persist(adjustment)\n\n line.returnedQuantity = (toNumeric(line.returnedQuantity) + quantity).toString()\n line.updatedAt = new Date()\n tx.persist(line)\n })\n\n const lineSnapshots: SalesLineSnapshot[] = orderLines.map(mapOrderLineEntityToSnapshot)\n const adjustmentDrafts: SalesAdjustmentDraft[] = [...existingAdjustments, ...createdAdjustments].map(mapOrderAdjustmentToDraft)\n const calculation = await salesCalculationService.calculateDocumentTotals({\n documentKind: 'order',\n lines: lineSnapshots,\n adjustments: adjustmentDrafts,\n context: buildCalculationContext(order),\n })\n applyOrderTotals(order, calculation.totals, calculation.lines.length)\n order.updatedAt = new Date()\n tx.persist(order)\n\n await tx.flush()\n\n return { header: entity, createdLines: createdReturnLines }\n })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: header,\n identifiers: { id: header.id, organizationId: header.organizationId, tenantId: header.tenantId },\n indexer: { entityType: E.sales.sales_return },\n events: returnCrudEvents,\n })\n\n if (createdLines.length) {\n await Promise.all(\n createdLines.map((line) =>\n emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: line,\n identifiers: { id: line.id, organizationId: line.organizationId, tenantId: line.tenantId },\n indexer: { entityType: E.sales.sales_return_line },\n }),\n ),\n )\n }\n\n return { returnId: header.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadReturnSnapshot(em, result.returnId)\n },\n buildLog: async ({ result, snapshots }) => {\n const after = snapshots.after as ReturnSnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('sales.audit.returns.create', 'Create return'),\n resourceKind: 'sales.return',\n resourceId: result.returnId,\n parentResourceKind: 'sales.order',\n parentResourceId: after.orderId ?? null,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotAfter: after,\n payload: {\n undo: { after } satisfies ReturnUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ReturnUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const order = await findOneWithDecryption(\n em,\n SalesOrder,\n { id: after.orderId, deletedAt: null },\n {},\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n if (!order) return\n\n const lines = await findWithDecryption(\n em,\n SalesOrderLine,\n { order: order.id, deletedAt: null },\n {},\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n const lineMap = new Map(lines.map((line) => [line.id, line]))\n after.lines.forEach((entry) => {\n const line = lineMap.get(entry.orderLineId)\n if (!line) return\n const next = Math.max(0, toNumeric(line.returnedQuantity) - entry.quantityReturned)\n line.returnedQuantity = next.toString()\n line.updatedAt = new Date()\n em.persist(line)\n })\n\n if (after.adjustmentIds.length) {\n const adjustments = await findWithDecryption(\n em,\n SalesOrderAdjustment,\n { id: { $in: after.adjustmentIds }, deletedAt: null },\n {},\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n adjustments.forEach((adj) => em.remove(adj))\n }\n\n const header = await findOneWithDecryption(\n em,\n SalesReturn,\n { id: after.id, deletedAt: null },\n {},\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n const returnLines = await findWithDecryption(\n em,\n SalesReturnLine,\n { salesReturn: after.id, deletedAt: null },\n {},\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n returnLines.forEach((line) => em.remove(line))\n if (header) em.remove(header)\n\n const existingAdjustments = await findWithDecryption(\n em,\n SalesOrderAdjustment,\n { order: order.id, deletedAt: null },\n { orderBy: { position: 'asc' } },\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n const salesCalculationService = ctx.container.resolve<SalesCalculationService>('salesCalculationService')\n const lineSnapshots: SalesLineSnapshot[] = lines.map(mapOrderLineEntityToSnapshot)\n const adjustmentDrafts: SalesAdjustmentDraft[] = existingAdjustments.map(mapOrderAdjustmentToDraft)\n const calculation = await salesCalculationService.calculateDocumentTotals({\n documentKind: 'order',\n lines: lineSnapshots,\n adjustments: adjustmentDrafts,\n context: buildCalculationContext(order),\n })\n applyOrderTotals(order, calculation.totals, calculation.lines.length)\n order.updatedAt = new Date()\n em.persist(order)\n\n await em.flush()\n },\n}\n\nregisterCommand(createReturnCommand)\n\nexport const returnCommands = [createReturnCommand]\n"],
5
- "mappings": "AAAA,SAAS,kBAAkB;AAC3B,SAAS,uBAA4C;AACrD,SAAS,gBAAgB;AAEzB,SAAS,qBAAqB;AAC9B,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AAGpC,SAAS,uBAAuB,0BAA0B;AAC1D,SAAS,oCAAoC;AAG7C,SAAS,WAAW,yBAAyB,iBAAiB,mBAAmB,oBAAoB,uBAAuB;AAC5H,SAAS,YAAY,sBAAsB,gBAAgB,aAAa,uBAAuB;AAC/F,SAAS,0BAAkD;AAC3D,SAAS,SAAS;AA6BlB,MAAM,mBAAqC;AAAA,EACzC,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,cAAc,CAAC,SAAS;AAAA,IACtB,IAAI,IAAI,YAAY;AAAA,IACpB,gBAAgB,IAAI,YAAY;AAAA,IAChC,UAAU,IAAI,YAAY;AAAA,EAC5B;AACF;AAEA,SAAS,UAAU,OAAwB;AACzC,MAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,QAAO;AAChE,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,QAAQ;AACpD,UAAM,SAAS,OAAO,KAAK;AAC3B,QAAI,OAAO,SAAS,MAAM,EAAG,QAAO;AAAA,EACtC;AACA,SAAO;AACT;AAEA,SAAS,MAAM,OAAuB;AACpC,SAAO,KAAK,OAAO,QAAQ,OAAO,WAAW,GAAG,IAAI;AACtD;AAEA,SAAS,iBAAiB,OAAmB,QAAkD,WAAyB;AACtH,QAAM,oBAAoB,gBAAgB,OAAO,iBAAiB,KAAK;AACvE,QAAM,sBAAsB,gBAAgB,OAAO,mBAAmB,KAAK;AAC3E,QAAM,sBAAsB,gBAAgB,OAAO,mBAAmB,KAAK;AAC3E,QAAM,iBAAiB,gBAAgB,OAAO,cAAc,KAAK;AACjE,QAAM,oBAAoB,gBAAgB,OAAO,iBAAiB,KAAK;AACvE,QAAM,sBAAsB,gBAAgB,OAAO,mBAAmB,KAAK;AAC3E,QAAM,uBAAuB,gBAAgB,OAAO,oBAAoB,KAAK;AAC7E,QAAM,sBAAsB,gBAAgB,OAAO,mBAAmB,KAAK;AAC3E,QAAM,wBAAwB,gBAAgB,OAAO,qBAAqB,KAAK;AAC/E,QAAM,kBAAkB,gBAAgB,OAAO,eAAe,KAAK;AACnE,QAAM,sBAAsB,gBAAgB,OAAO,mBAAmB,KAAK;AAC3E,QAAM,oBAAoB,gBAAgB,OAAO,iBAAiB,KAAK;AACvE,QAAM,iBAAiB,UAAU,MAAM;AACvC,QAAM,gBAAgB;AACxB;AAEA,SAAS,6BAA6B,MAAyC;AAC7E,SAAO;AAAA,IACL,IAAI,KAAK;AAAA,IACT,YAAY,KAAK;AAAA,IACjB,MAAM,KAAK;AAAA,IACX,WAAW,KAAK,aAAa;AAAA,IAC7B,kBAAkB,KAAK,oBAAoB;AAAA,IAC3C,MAAM,KAAK,QAAQ;AAAA,IACnB,aAAa,KAAK,eAAe;AAAA,IACjC,SAAS,KAAK,WAAW;AAAA,IACzB,UAAU,UAAU,KAAK,QAAQ;AAAA,IACjC,cAAc,KAAK,gBAAgB;AAAA,IACnC,oBAAoB,UAAU,KAAK,sBAAsB,KAAK,QAAQ;AAAA,IACtE,gBAAgB,KAAK,kBAAkB,KAAK,gBAAgB;AAAA,IAC5D,aAAa,KAAK,cAAc,UAAU,KAAK,WAAW,IAAI;AAAA,IAC9D,cAAc,KAAK;AAAA,IACnB,cAAc,UAAU,KAAK,YAAY;AAAA,IACzC,gBAAgB,UAAU,KAAK,cAAc;AAAA,IAC7C,gBAAgB,UAAU,KAAK,cAAc;AAAA,IAC7C,iBAAiB,UAAU,KAAK,eAAe;AAAA,IAC/C,SAAS,UAAU,KAAK,OAAO;AAAA,IAC/B,WAAW,UAAU,KAAK,SAAS;AAAA,IACnC,gBAAgB,UAAU,KAAK,cAAc;AAAA,IAC7C,kBAAkB,UAAU,KAAK,gBAAgB;AAAA,IACjD,eAAe,KAAK,gBAAgB,UAAU,KAAK,aAAa,IAAI;AAAA,IACpE,eAAe,KAAK,iBAAiB;AAAA,IACrC,UAAU,KAAK,WAAW,UAAU,KAAK,QAAQ,IAAI;AAAA,IACrD,kBAAkB,KAAK,oBAAoB;AAAA,EAC7C;AACF;AAEA,SAAS,0BAA0B,YAAwD;AACzF,SAAO;AAAA,IACL,IAAI,WAAW;AAAA,IACf,OAAO,WAAW,SAAS;AAAA,IAC3B,MAAM,WAAW;AAAA,IACjB,MAAM,WAAW,QAAQ;AAAA,IACzB,OAAO,WAAW,SAAS;AAAA,IAC3B,eAAe,WAAW,iBAAiB;AAAA,IAC3C,aAAa,WAAW,eAAe;AAAA,IACvC,MAAM,UAAU,WAAW,IAAI;AAAA,IAC/B,WAAW,UAAU,WAAW,SAAS;AAAA,IACzC,aAAa,UAAU,WAAW,WAAW;AAAA,IAC7C,cAAc,WAAW,gBAAgB;AAAA,IACzC,UAAU,WAAW,WAAW,UAAU,WAAW,QAAQ,IAAI;AAAA,IACjE,UAAU,WAAW,YAAY;AAAA,EACnC;AACF;AAEA,SAAS,wBAAwB,OAAmB;AAClD,SAAO;AAAA,IACL,UAAU,MAAM;AAAA,IAChB,gBAAgB,MAAM;AAAA,IACtB,cAAc,MAAM;AAAA,IACpB,UAAU;AAAA,MACR,gBAAgB,MAAM,yBAClB,UAAU,MAAM,sBAAiD,IACjE;AAAA,MACJ,eAAe,MAAM,wBAAwB,UAAU,MAAM,qBAAgD,IAAI;AAAA,IACnH;AAAA,EACF;AACF;AAMA,eAAsB,iCACpB,IACA,WACA,SACA,OAC0D;AAC1D,QAAM,QAAQ,MAAM;AAAA,IAClB;AAAA,IACA;AAAA,IACA,EAAE,IAAI,SAAS,WAAW,KAAK;AAAA,IAC/B,CAAC;AAAA,IACD;AAAA,EACF;AACA,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,CAAC,YAAY,WAAW,IAAI,MAAM,QAAQ,IAAI;AAAA,IAClD,mBAAmB,IAAI,gBAAgB,EAAE,OAAO,MAAM,IAAI,WAAW,KAAK,GAAG,CAAC,GAAG,KAAK;AAAA,IACtF;AAAA,MACE;AAAA,MACA;AAAA,MACA,EAAE,OAAO,MAAM,IAAI,WAAW,KAAK;AAAA,MACnC,EAAE,SAAS,EAAE,UAAU,MAAM,EAAE;AAAA,MAC/B;AAAA,IACF;AAAA,EACF,CAAC;AACD,QAAM,gBAAqC,WAAW,IAAI,4BAA4B;AACtF,QAAM,mBAA2C,YAAY,IAAI,yBAAyB;AAC1F,QAAM,0BAA0B,UAAU,QAAQ,yBAAyB;AAC3E,QAAM,cAAc,MAAM,wBAAwB,wBAAwB;AAAA,IACxE,cAAc;AAAA,IACd,OAAO;AAAA,IACP,aAAa;AAAA,IACb,SAAS,wBAAwB,KAAK;AAAA,IACtC,gBAAgB;AAAA,MACd,iBAAiB,UAAU,MAAM,eAAe;AAAA,MAChD,qBAAqB,UAAU,MAAM,mBAAmB;AAAA,IAC1D;AAAA,EACF,CAAC;AACD,SAAO,YAAY;AACrB;AAEA,eAAsB,mBAAmB,IAAmB,IAA4C;AACtG,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA,EAAE,IAAI,WAAW,KAAK;AAAA,IACtB,EAAE,UAAU,CAAC,OAAO,EAAE;AAAA,IACtB,CAAC;AAAA,EACH;AACA,MAAI,CAAC,UAAU,CAAC,OAAO,MAAO,QAAO;AACrC,QAAM,UAAU,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ,OAAO,MAAM;AAC/E,QAAM,QAAQ,MAAM;AAAA,IAClB;AAAA,IACA;AAAA,IACA,EAAE,aAAa,OAAO,IAAI,WAAW,KAAK;AAAA,IAC1C,EAAE,UAAU,CAAC,WAAW,EAAE;AAAA,IAC1B,EAAE,UAAU,OAAO,UAAU,gBAAgB,OAAO,eAAe;AAAA,EACrE;AACA,QAAM,gBAA0B,CAAC;AACjC,QAAM,cAAc,MAAM;AAAA,IACxB;AAAA,IACA;AAAA,IACA,EAAE,OAAO,SAAS,MAAM,UAAU,WAAW,KAAK;AAAA,IAClD,CAAC;AAAA,IACD,EAAE,UAAU,OAAO,UAAU,gBAAgB,OAAO,eAAe;AAAA,EACrE;AACA,cAAY,QAAQ,CAAC,QAAQ;AAC3B,UAAM,OAAO,IAAI;AACjB,QAAI,QAAQ,KAAK,aAAa,OAAO,GAAI,eAAc,KAAK,IAAI,EAAE;AAAA,EACpE,CAAC;AAED,SAAO;AAAA,IACL,IAAI,OAAO;AAAA,IACX;AAAA,IACA,gBAAgB,OAAO;AAAA,IACvB,UAAU,OAAO;AAAA,IACjB,cAAc,OAAO;AAAA,IACrB,YAAY,OAAO,aAAa,OAAO,WAAW,YAAY,IAAI;AAAA,IAClE,QAAQ,OAAO,UAAU;AAAA,IACzB,OAAO,OAAO,SAAS;AAAA,IACvB,OAAO,MAAM,IAAI,CAAC,UAAU;AAAA,MAC1B,IAAI,KAAK;AAAA,MACT,aAAa,OAAO,KAAK,cAAc,WAAW,KAAK,YAAY,KAAK,WAAW,MAAM;AAAA,MACzF,kBAAkB,UAAU,KAAK,gBAAgB;AAAA,MACjD,cAAc,UAAU,KAAK,YAAY;AAAA,MACzC,gBAAgB,UAAU,KAAK,cAAc;AAAA,MAC7C,gBAAgB,UAAU,KAAK,cAAc;AAAA,MAC7C,kBAAkB,UAAU,KAAK,gBAAgB;AAAA,IACnD,EAAE;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,oBAAoB,OAAsD;AACjF,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,SAA4B,CAAC;AACnC,aAAW,QAAQ,OAAO;AACxB,UAAM,cAAc,KAAK;AACzB,QAAI,CAAC,eAAe,KAAK,IAAI,WAAW,EAAG;AAC3C,UAAM,WAAW,UAAU,KAAK,QAAQ;AACxC,QAAI,CAAC,OAAO,SAAS,QAAQ,KAAK,YAAY,EAAG;AACjD,SAAK,IAAI,WAAW;AACpB,WAAO,KAAK,EAAE,aAAa,SAAS,CAAC;AAAA,EACvC;AACA,SAAO;AACT;AAEA,MAAM,sBAA+E;AAAA,EACnF,IAAI;AAAA,EACJ,MAAM,QAAQ,UAAU,KAAK;AAC3B,UAAM,QAAQ,mBAAmB,MAAM,YAAY,CAAC,CAAC;AACrD,sBAAkB,KAAK,MAAM,QAAQ;AACrC,4BAAwB,KAAK,MAAM,cAAc;AAEjD,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAE/D,UAAM,YAAY,oBAAoB,MAAM,KAAK;AACjD,QAAI,CAAC,UAAU,QAAQ;AACrB,YAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,+BAA+B,qCAAqC,EAAE,CAAC;AAAA,IACzH;AAEA,UAAM,0BAA0B,IAAI,UAAU,QAAiC,yBAAyB;AACxG,UAAM,EAAE,QAAQ,aAAa,IAAI,MAAM,GAAG,cAAc,OAAO,OAAO;AACpE,YAAM,QAAQ,MAAM;AAAA,QAClB;AAAA,QACA;AAAA,QACA,EAAE,IAAI,MAAM,SAAS,WAAW,KAAK;AAAA,QACrC,CAAC;AAAA,QACD,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAAA,MACnE;AACA,UAAI,CAAC,OAAO;AACV,cAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,8BAA8B,kBAAkB,EAAE,CAAC;AAAA,MACrG;AACA,sBAAgB,OAAO,MAAM,gBAAgB,MAAM,QAAQ;AAE3D,YAAM,aAAa,MAAM;AAAA,QACvB;AAAA,QACA;AAAA,QACA,EAAE,OAAO,MAAM,IAAI,WAAW,KAAK;AAAA,QACnC,EAAE,UAAU,SAAS,kBAAkB;AAAA,QACvC,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAAA,MACnE;AACA,YAAM,UAAU,IAAI,IAAI,WAAW,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AAEjE,gBAAU,QAAQ,CAAC,EAAE,aAAa,SAAS,MAAM;AAC/C,cAAM,OAAO,QAAQ,IAAI,WAAW;AACpC,YAAI,CAAC,MAAM;AACT,gBAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,6BAA6B,uBAAuB,EAAE,CAAC;AAAA,QACzG;AACA,cAAM,YAAY,UAAU,KAAK,QAAQ,IAAI,UAAU,KAAK,gBAAgB;AAC5E,YAAI,WAAW,OAAO,WAAW;AAC/B,gBAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,kCAAkC,iDAAiD,EAAE,CAAC;AAAA,QACxI;AAAA,MACF,CAAC;AAED,YAAM,sBAAsB,MAAM;AAAA,QAChC;AAAA,QACA;AAAA,QACA,EAAE,OAAO,MAAM,IAAI,WAAW,KAAK;AAAA,QACnC,EAAE,SAAS,EAAE,UAAU,MAAM,EAAE;AAAA,QAC/B,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAAA,MACnE;AACA,YAAM,gBAAgB,oBAAoB,OAAO,CAAC,KAAK,QAAQ,KAAK,IAAI,KAAK,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI;AAEtG,YAAM,kBAAkB,IAAI,6BAA6B,EAAE;AAC3D,YAAM,YAAY,MAAM,gBAAgB,SAAS;AAAA,QAC/C,MAAM;AAAA,QACN,UAAU,MAAM;AAAA,QAChB,gBAAgB,MAAM;AAAA,MACxB,CAAC;AACD,YAAM,WAAW,WAAW;AAC5B,YAAM,SAAS,GAAG,OAAO,aAAa;AAAA,QACpC,IAAI;AAAA,QACJ;AAAA,QACA,gBAAgB,MAAM;AAAA,QACtB,UAAU,MAAM;AAAA,QAChB,cAAc,UAAU;AAAA,QACxB,QAAQ,MAAM,UAAU;AAAA,QACxB,OAAO,MAAM,SAAS;AAAA,QACtB,YAAY,MAAM,cAAc,oBAAI,KAAK;AAAA,QACzC,WAAW,oBAAI,KAAK;AAAA,QACpB,WAAW,oBAAI,KAAK;AAAA,MACtB,CAAC;AACD,SAAG,QAAQ,MAAM;AAEjB,YAAM,qBAA6C,CAAC;AACpD,YAAM,qBAAwC,CAAC;AAC/C,gBAAU,QAAQ,CAAC,WAAW,UAAU;AACtC,cAAM,OAAO,QAAQ,IAAI,UAAU,WAAW;AAC9C,YAAI,CAAC,KAAM;AACX,cAAM,WAAW,UAAU;AAC3B,cAAM,eAAe,KAAK,IAAI,UAAU,KAAK,QAAQ,GAAG,CAAC;AACzD,cAAM,UAAU,eAAe,IAAI,UAAU,KAAK,cAAc,IAAI,eAAe,UAAU,KAAK,YAAY;AAC9G,cAAM,YAAY,eAAe,IAAI,UAAU,KAAK,gBAAgB,IAAI,eAAe,UAAU,KAAK,cAAc;AACpH,cAAM,WAAW,CAAC,MAAM,KAAK,IAAI,SAAS,CAAC,IAAI,QAAQ;AACvD,cAAM,aAAa,CAAC,MAAM,KAAK,IAAI,WAAW,CAAC,IAAI,QAAQ;AAE3D,cAAM,eAAe,WAAW;AAChC,cAAM,aAAa,GAAG,OAAO,iBAAiB;AAAA,UAC5C,IAAI;AAAA,UACJ,aAAa;AAAA,UACb,WAAW,GAAG,aAAa,gBAAgB,KAAK,EAAE;AAAA,UAClD,gBAAgB,MAAM;AAAA,UACtB,UAAU,MAAM;AAAA,UAChB,kBAAkB,SAAS,SAAS;AAAA,UACpC,cAAc,MAAM,OAAO,EAAE,SAAS;AAAA,UACtC,gBAAgB,MAAM,SAAS,EAAE,SAAS;AAAA,UAC1C,gBAAgB,SAAS,SAAS;AAAA,UAClC,kBAAkB,WAAW,SAAS;AAAA,UACtC,WAAW,oBAAI,KAAK;AAAA,UACpB,WAAW,oBAAI,KAAK;AAAA,QACtB,CAAC;AACD,2BAAmB,KAAK,UAAU;AAClC,WAAG,QAAQ,UAAU;AAErB,cAAM,aAAa,GAAG,OAAO,sBAAsB;AAAA,UACjD,IAAI,WAAW;AAAA,UACf;AAAA,UACA,WAAW,GAAG,aAAa,gBAAgB,KAAK,EAAE;AAAA,UAClD,gBAAgB,MAAM;AAAA,UACtB,UAAU,MAAM;AAAA,UAChB,OAAO;AAAA,UACP,MAAM;AAAA,UACN,MAAM;AAAA,UACN,WAAW,SAAS,SAAS;AAAA,UAC7B,aAAa,WAAW,SAAS;AAAA,UACjC,cAAc,MAAM;AAAA,UACpB,UAAU,EAAE,UAAU,aAAa;AAAA,UACnC,UAAU,gBAAgB;AAAA,UAC1B,WAAW,oBAAI,KAAK;AAAA,UACpB,WAAW,oBAAI,KAAK;AAAA,QACtB,CAAC;AACD,2BAAmB,KAAK,UAAU;AAClC,WAAG,QAAQ,UAAU;AAErB,aAAK,oBAAoB,UAAU,KAAK,gBAAgB,IAAI,UAAU,SAAS;AAC/E,aAAK,YAAY,oBAAI,KAAK;AAC1B,WAAG,QAAQ,IAAI;AAAA,MACjB,CAAC;AAED,YAAM,gBAAqC,WAAW,IAAI,4BAA4B;AACtF,YAAM,mBAA2C,CAAC,GAAG,qBAAqB,GAAG,kBAAkB,EAAE,IAAI,yBAAyB;AAC9H,YAAM,cAAc,MAAM,wBAAwB,wBAAwB;AAAA,QACxE,cAAc;AAAA,QACd,OAAO;AAAA,QACP,aAAa;AAAA,QACb,SAAS,wBAAwB,KAAK;AAAA,MACxC,CAAC;AACD,uBAAiB,OAAO,YAAY,QAAQ,YAAY,MAAM,MAAM;AACpE,YAAM,YAAY,oBAAI,KAAK;AAC3B,SAAG,QAAQ,KAAK;AAEhB,YAAM,GAAG,MAAM;AAEf,aAAO,EAAE,QAAQ,QAAQ,cAAc,mBAAmB;AAAA,IAC5D,CAAC;AAED,UAAM,aAAa,IAAI,UAAU,QAAQ,YAAY;AACrD,UAAM,oBAAoB;AAAA,MACxB;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,aAAa,EAAE,IAAI,OAAO,IAAI,gBAAgB,OAAO,gBAAgB,UAAU,OAAO,SAAS;AAAA,MAC/F,SAAS,EAAE,YAAY,EAAE,MAAM,aAAa;AAAA,MAC5C,QAAQ;AAAA,IACV,CAAC;AAED,QAAI,aAAa,QAAQ;AACvB,YAAM,QAAQ;AAAA,QACZ,aAAa;AAAA,UAAI,CAAC,SAChB,oBAAoB;AAAA,YAClB;AAAA,YACA,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,aAAa,EAAE,IAAI,KAAK,IAAI,gBAAgB,KAAK,gBAAgB,UAAU,KAAK,SAAS;AAAA,YACzF,SAAS,EAAE,YAAY,EAAE,MAAM,kBAAkB;AAAA,UACnD,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,UAAU,OAAO,GAAG;AAAA,EAC/B;AAAA,EACA,cAAc,OAAO,QAAQ,QAAQ,QAAQ;AAC3C,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,WAAO,mBAAmB,IAAI,OAAO,QAAQ;AAAA,EAC/C;AAAA,EACA,UAAU,OAAO,EAAE,QAAQ,UAAU,MAAM;AACzC,UAAM,QAAQ,UAAU;AACxB,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO;AAAA,MACL,aAAa,UAAU,8BAA8B,eAAe;AAAA,MACpE,cAAc;AAAA,MACd,YAAY,OAAO;AAAA,MACnB,oBAAoB;AAAA,MACpB,kBAAkB,MAAM,WAAW;AAAA,MACnC,UAAU,MAAM;AAAA,MAChB,gBAAgB,MAAM;AAAA,MACtB,eAAe;AAAA,MACf,SAAS;AAAA,QACP,MAAM,EAAE,MAAM;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACjC,UAAM,UAAU,mBAAsC,QAAQ;AAC9D,UAAM,QAAQ,SAAS;AACvB,QAAI,CAAC,MAAO;AACZ,UAAM,KAAM,IAAI,UAAU,QAAQ,IAAI,EAAoB,KAAK;AAC/D,UAAM,QAAQ,MAAM;AAAA,MAClB;AAAA,MACA;AAAA,MACA,EAAE,IAAI,MAAM,SAAS,WAAW,KAAK;AAAA,MACrC,CAAC;AAAA,MACD,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAAA,IACnE;AACA,QAAI,CAAC,MAAO;AAEZ,UAAM,QAAQ,MAAM;AAAA,MAClB;AAAA,MACA;AAAA,MACA,EAAE,OAAO,MAAM,IAAI,WAAW,KAAK;AAAA,MACnC,CAAC;AAAA,MACD,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAAA,IACnE;AACA,UAAM,UAAU,IAAI,IAAI,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AAC5D,UAAM,MAAM,QAAQ,CAAC,UAAU;AAC7B,YAAM,OAAO,QAAQ,IAAI,MAAM,WAAW;AAC1C,UAAI,CAAC,KAAM;AACX,YAAM,OAAO,KAAK,IAAI,GAAG,UAAU,KAAK,gBAAgB,IAAI,MAAM,gBAAgB;AAClF,WAAK,mBAAmB,KAAK,SAAS;AACtC,WAAK,YAAY,oBAAI,KAAK;AAC1B,SAAG,QAAQ,IAAI;AAAA,IACjB,CAAC;AAED,QAAI,MAAM,cAAc,QAAQ;AAC9B,YAAM,cAAc,MAAM;AAAA,QACxB;AAAA,QACA;AAAA,QACA,EAAE,IAAI,EAAE,KAAK,MAAM,cAAc,GAAG,WAAW,KAAK;AAAA,QACpD,CAAC;AAAA,QACD,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAAA,MACnE;AACA,kBAAY,QAAQ,CAAC,QAAQ,GAAG,OAAO,GAAG,CAAC;AAAA,IAC7C;AAEA,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,MACA,EAAE,IAAI,MAAM,IAAI,WAAW,KAAK;AAAA,MAChC,CAAC;AAAA,MACD,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAAA,IACnE;AACA,UAAM,cAAc,MAAM;AAAA,MACxB;AAAA,MACA;AAAA,MACA,EAAE,aAAa,MAAM,IAAI,WAAW,KAAK;AAAA,MACzC,CAAC;AAAA,MACD,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAAA,IACnE;AACA,gBAAY,QAAQ,CAAC,SAAS,GAAG,OAAO,IAAI,CAAC;AAC7C,QAAI,OAAQ,IAAG,OAAO,MAAM;AAE5B,UAAM,sBAAsB,MAAM;AAAA,MAChC;AAAA,MACA;AAAA,MACA,EAAE,OAAO,MAAM,IAAI,WAAW,KAAK;AAAA,MACnC,EAAE,SAAS,EAAE,UAAU,MAAM,EAAE;AAAA,MAC/B,EAAE,UAAU,MAAM,UAAU,gBAAgB,MAAM,eAAe;AAAA,IACnE;AACA,UAAM,0BAA0B,IAAI,UAAU,QAAiC,yBAAyB;AACxG,UAAM,gBAAqC,MAAM,IAAI,4BAA4B;AACjF,UAAM,mBAA2C,oBAAoB,IAAI,yBAAyB;AAClG,UAAM,cAAc,MAAM,wBAAwB,wBAAwB;AAAA,MACxE,cAAc;AAAA,MACd,OAAO;AAAA,MACP,aAAa;AAAA,MACb,SAAS,wBAAwB,KAAK;AAAA,IACxC,CAAC;AACD,qBAAiB,OAAO,YAAY,QAAQ,YAAY,MAAM,MAAM;AACpE,UAAM,YAAY,oBAAI,KAAK;AAC3B,OAAG,QAAQ,KAAK;AAEhB,UAAM,GAAG,MAAM;AAAA,EACjB;AACF;AAEA,gBAAgB,mBAAmB;AAE5B,MAAM,iBAAiB,CAAC,mBAAmB;",
4
+ "sourcesContent": ["import { randomUUID } from 'crypto'\nimport { registerCommand, type CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport { LockMode } from '@mikro-orm/core'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport type { CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { SalesDocumentNumberGenerator } from '../services/salesDocumentNumberGenerator'\nimport type { SalesCalculationService } from '../services/salesCalculationService'\nimport type { SalesAdjustmentDraft, SalesLineSnapshot, SalesDocumentCalculationResult } from '../lib/types'\nimport { cloneJson, ensureOrganizationScope, ensureSameScope, ensureTenantScope, extractUndoPayload, toNumericString } from './shared'\nimport { SalesOrder, SalesOrderAdjustment, SalesOrderLine, SalesReturn, SalesReturnLine } from '../data/entities'\nimport { returnCreateSchema, type ReturnCreateInput } from '../data/validators'\nimport { E } from '#generated/entities.ids.generated'\n\ntype ReturnLineInput = { orderLineId: string; quantity: number }\n\ntype ReturnSnapshot = {\n id: string\n orderId: string\n organizationId: string\n tenantId: string\n returnNumber: string\n returnedAt: string | null\n reason: string | null\n notes: string | null\n lines: Array<{\n id: string\n orderLineId: string\n quantityReturned: number\n unitPriceNet: number\n unitPriceGross: number\n totalNetAmount: number\n totalGrossAmount: number\n }>\n adjustmentIds: string[]\n}\n\ntype ReturnUndoPayload = {\n after?: ReturnSnapshot | null\n}\n\nconst returnCrudEvents: CrudEventsConfig = {\n module: 'sales',\n entity: 'return',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n organizationId: ctx.identifiers.organizationId,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nfunction toNumeric(value: unknown): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim().length) {\n const parsed = Number(value)\n if (Number.isFinite(parsed)) return parsed\n }\n return 0\n}\n\nfunction round(value: number): number {\n return Math.round((value + Number.EPSILON) * 1e4) / 1e4\n}\n\nfunction applyOrderTotals(order: SalesOrder, totals: SalesDocumentCalculationResult['totals'], lineCount: number): void {\n order.subtotalNetAmount = toNumericString(totals.subtotalNetAmount) ?? '0'\n order.subtotalGrossAmount = toNumericString(totals.subtotalGrossAmount) ?? '0'\n order.discountTotalAmount = toNumericString(totals.discountTotalAmount) ?? '0'\n order.taxTotalAmount = toNumericString(totals.taxTotalAmount) ?? '0'\n order.shippingNetAmount = toNumericString(totals.shippingNetAmount) ?? '0'\n order.shippingGrossAmount = toNumericString(totals.shippingGrossAmount) ?? '0'\n order.surchargeTotalAmount = toNumericString(totals.surchargeTotalAmount) ?? '0'\n order.grandTotalNetAmount = toNumericString(totals.grandTotalNetAmount) ?? '0'\n order.grandTotalGrossAmount = toNumericString(totals.grandTotalGrossAmount) ?? '0'\n order.paidTotalAmount = toNumericString(totals.paidTotalAmount) ?? '0'\n order.refundedTotalAmount = toNumericString(totals.refundedTotalAmount) ?? '0'\n order.outstandingAmount = toNumericString(totals.outstandingAmount) ?? '0'\n order.totalsSnapshot = cloneJson(totals)\n order.lineItemCount = lineCount\n}\n\nfunction mapOrderLineEntityToSnapshot(line: SalesOrderLine): SalesLineSnapshot {\n return {\n id: line.id,\n lineNumber: line.lineNumber,\n kind: line.kind,\n productId: line.productId ?? null,\n productVariantId: line.productVariantId ?? null,\n name: line.name ?? null,\n description: line.description ?? null,\n comment: line.comment ?? null,\n quantity: toNumeric(line.quantity),\n quantityUnit: line.quantityUnit ?? null,\n normalizedQuantity: toNumeric(line.normalizedQuantity ?? line.quantity),\n normalizedUnit: line.normalizedUnit ?? line.quantityUnit ?? null,\n uomSnapshot: line.uomSnapshot ? cloneJson(line.uomSnapshot) : null,\n currencyCode: line.currencyCode,\n unitPriceNet: toNumeric(line.unitPriceNet),\n unitPriceGross: toNumeric(line.unitPriceGross),\n discountAmount: toNumeric(line.discountAmount),\n discountPercent: toNumeric(line.discountPercent),\n taxRate: toNumeric(line.taxRate),\n taxAmount: toNumeric(line.taxAmount),\n totalNetAmount: toNumeric(line.totalNetAmount),\n totalGrossAmount: toNumeric(line.totalGrossAmount),\n configuration: line.configuration ? cloneJson(line.configuration) : null,\n promotionCode: line.promotionCode ?? null,\n metadata: line.metadata ? cloneJson(line.metadata) : null,\n customFieldSetId: line.customFieldSetId ?? null,\n }\n}\n\nfunction mapOrderAdjustmentToDraft(adjustment: SalesOrderAdjustment): SalesAdjustmentDraft {\n return {\n id: adjustment.id,\n scope: adjustment.scope ?? 'order',\n kind: adjustment.kind,\n code: adjustment.code ?? null,\n label: adjustment.label ?? null,\n calculatorKey: adjustment.calculatorKey ?? null,\n promotionId: adjustment.promotionId ?? null,\n rate: toNumeric(adjustment.rate),\n amountNet: toNumeric(adjustment.amountNet),\n amountGross: toNumeric(adjustment.amountGross),\n currencyCode: adjustment.currencyCode ?? null,\n metadata: adjustment.metadata ? cloneJson(adjustment.metadata) : null,\n position: adjustment.position ?? 0,\n }\n}\n\nfunction buildCalculationContext(order: SalesOrder) {\n return {\n tenantId: order.tenantId,\n organizationId: order.organizationId,\n currencyCode: order.currencyCode,\n metadata: {\n shippingMethod: order.shippingMethodSnapshot\n ? cloneJson(order.shippingMethodSnapshot as Record<string, unknown>)\n : null,\n paymentMethod: order.paymentMethodSnapshot ? cloneJson(order.paymentMethodSnapshot as Record<string, unknown>) : null,\n },\n }\n}\n\n/**\n * Recalculates order totals (including line-scoped return adjustments) for display.\n * Returns the totals object to merge into an order API response, or null if order not found.\n */\nexport async function recalculateOrderTotalsForDisplay(\n em: EntityManager,\n container: { resolve: (key: string) => unknown },\n orderId: string,\n scope: { tenantId: string; organizationId: string },\n): Promise<SalesDocumentCalculationResult['totals'] | null> {\n const order = await findOneWithDecryption(\n em,\n SalesOrder,\n { id: orderId, deletedAt: null },\n {},\n scope,\n )\n if (!order) return null\n const [orderLines, adjustments] = await Promise.all([\n findWithDecryption(em, SalesOrderLine, { order: order.id, deletedAt: null }, {}, scope),\n findWithDecryption(\n em,\n SalesOrderAdjustment,\n { order: order.id, deletedAt: null },\n { orderBy: { position: 'asc' } },\n scope,\n ),\n ])\n const lineSnapshots: SalesLineSnapshot[] = orderLines.map(mapOrderLineEntityToSnapshot)\n const adjustmentDrafts: SalesAdjustmentDraft[] = adjustments.map(mapOrderAdjustmentToDraft)\n const salesCalculationService = container.resolve('salesCalculationService') as SalesCalculationService\n const calculation = await salesCalculationService.calculateDocumentTotals({\n documentKind: 'order',\n lines: lineSnapshots,\n adjustments: adjustmentDrafts,\n context: buildCalculationContext(order),\n existingTotals: {\n paidTotalAmount: toNumeric(order.paidTotalAmount),\n refundedTotalAmount: toNumeric(order.refundedTotalAmount),\n },\n })\n return calculation.totals\n}\n\nexport async function loadReturnSnapshot(em: EntityManager, id: string): Promise<ReturnSnapshot | null> {\n const header = await findOneWithDecryption(\n em,\n SalesReturn,\n { id, deletedAt: null },\n { populate: ['order'] },\n {},\n )\n if (!header || !header.order) return null\n const orderId = typeof header.order === 'string' ? header.order : header.order.id\n const lines = await findWithDecryption(\n em,\n SalesReturnLine,\n { salesReturn: header.id, deletedAt: null },\n { populate: ['orderLine'] },\n { tenantId: header.tenantId, organizationId: header.organizationId },\n )\n const adjustmentIds: string[] = []\n const adjustments = await findWithDecryption(\n em,\n SalesOrderAdjustment,\n { order: orderId, kind: 'return', deletedAt: null },\n {},\n { tenantId: header.tenantId, organizationId: header.organizationId },\n )\n adjustments.forEach((adj) => {\n const meta = adj.metadata as Record<string, unknown> | null | undefined\n if (meta && meta.returnId === header.id) adjustmentIds.push(adj.id)\n })\n\n return {\n id: header.id,\n orderId,\n organizationId: header.organizationId,\n tenantId: header.tenantId,\n returnNumber: header.returnNumber,\n returnedAt: header.returnedAt ? header.returnedAt.toISOString() : null,\n reason: header.reason ?? null,\n notes: header.notes ?? null,\n lines: lines.map((line) => ({\n id: line.id,\n orderLineId: typeof line.orderLine === 'string' ? line.orderLine : line.orderLine?.id ?? null,\n quantityReturned: toNumeric(line.quantityReturned),\n unitPriceNet: toNumeric(line.unitPriceNet),\n unitPriceGross: toNumeric(line.unitPriceGross),\n totalNetAmount: toNumeric(line.totalNetAmount),\n totalGrossAmount: toNumeric(line.totalGrossAmount),\n })),\n adjustmentIds,\n }\n}\n\nfunction normalizeLinesInput(lines: ReturnCreateInput['lines']): ReturnLineInput[] {\n const seen = new Set<string>()\n const result: ReturnLineInput[] = []\n for (const line of lines) {\n const orderLineId = line.orderLineId\n if (!orderLineId || seen.has(orderLineId)) continue\n const quantity = toNumeric(line.quantity)\n if (!Number.isFinite(quantity) || quantity <= 0) continue\n seen.add(orderLineId)\n result.push({ orderLineId, quantity })\n }\n return result\n}\n\nconst createReturnCommand: CommandHandler<ReturnCreateInput, { returnId: string }> = {\n id: 'sales.returns.create',\n async execute(rawInput, ctx) {\n const input = returnCreateSchema.parse(rawInput ?? {})\n ensureTenantScope(ctx, input.tenantId)\n ensureOrganizationScope(ctx, input.organizationId)\n\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n\n const requested = normalizeLinesInput(input.lines)\n if (!requested.length) {\n throw new CrudHttpError(400, { error: translate('sales.returns.linesRequired', 'Select at least one line to return.') })\n }\n\n const salesCalculationService = ctx.container.resolve<SalesCalculationService>('salesCalculationService')\n const { header, createdLines } = await em.transactional(async (tx) => {\n const order = await findOneWithDecryption(\n tx,\n SalesOrder,\n { id: input.orderId, deletedAt: null },\n {},\n { tenantId: input.tenantId, organizationId: input.organizationId },\n )\n if (!order) {\n throw new CrudHttpError(404, { error: translate('sales.returns.orderMissing', 'Order not found.') })\n }\n ensureSameScope(order, input.organizationId, input.tenantId)\n\n const orderLines = await findWithDecryption(\n tx,\n SalesOrderLine,\n { order: order.id, deletedAt: null },\n { lockMode: LockMode.PESSIMISTIC_WRITE },\n { tenantId: input.tenantId, organizationId: input.organizationId },\n )\n const lineMap = new Map(orderLines.map((line) => [line.id, line]))\n\n requested.forEach(({ orderLineId, quantity }) => {\n const line = lineMap.get(orderLineId)\n if (!line) {\n throw new CrudHttpError(404, { error: translate('sales.returns.lineMissing', 'Order line not found.') })\n }\n const available = toNumeric(line.quantity) - toNumeric(line.returnedQuantity)\n if (quantity - 1e-6 > available) {\n throw new CrudHttpError(400, { error: translate('sales.returns.quantityExceeded', 'Cannot return more than the remaining quantity.') })\n }\n })\n\n const existingAdjustments = await findWithDecryption(\n tx,\n SalesOrderAdjustment,\n { order: order.id, deletedAt: null },\n { orderBy: { position: 'asc' } },\n { tenantId: input.tenantId, organizationId: input.organizationId },\n )\n const positionStart = existingAdjustments.reduce((acc, adj) => Math.max(acc, adj.position ?? 0), 0) + 1\n\n const numberGenerator = new SalesDocumentNumberGenerator(tx)\n const generated = await numberGenerator.generate({\n kind: 'return',\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n })\n const returnId = randomUUID()\n const entity = tx.create(SalesReturn, {\n id: returnId,\n order,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n returnNumber: generated.number,\n reason: input.reason ?? null,\n notes: input.notes ?? null,\n returnedAt: input.returnedAt ?? new Date(),\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n tx.persist(entity)\n\n const createdAdjustments: SalesOrderAdjustment[] = []\n const createdReturnLines: SalesReturnLine[] = []\n requested.forEach((lineInput, index) => {\n const line = lineMap.get(lineInput.orderLineId)\n if (!line) return\n const quantity = lineInput.quantity\n const lineQuantity = Math.max(toNumeric(line.quantity), 0)\n const unitNet = lineQuantity > 0 ? toNumeric(line.totalNetAmount) / lineQuantity : toNumeric(line.unitPriceNet)\n const unitGross = lineQuantity > 0 ? toNumeric(line.totalGrossAmount) / lineQuantity : toNumeric(line.unitPriceGross)\n const totalNet = -round(Math.max(unitNet, 0) * quantity)\n const totalGross = -round(Math.max(unitGross, 0) * quantity)\n\n const returnLineId = randomUUID()\n const returnLine = tx.create(SalesReturnLine, {\n id: returnLineId,\n salesReturn: entity,\n orderLine: tx.getReference(SalesOrderLine, line.id),\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n quantityReturned: quantity.toString(),\n unitPriceNet: round(unitNet).toString(),\n unitPriceGross: round(unitGross).toString(),\n totalNetAmount: totalNet.toString(),\n totalGrossAmount: totalGross.toString(),\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n createdReturnLines.push(returnLine)\n tx.persist(returnLine)\n\n const adjustment = tx.create(SalesOrderAdjustment, {\n id: randomUUID(),\n order,\n orderLine: tx.getReference(SalesOrderLine, line.id),\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n scope: 'line',\n kind: 'return',\n rate: '0',\n amountNet: totalNet.toString(),\n amountGross: totalGross.toString(),\n currencyCode: order.currencyCode,\n metadata: { returnId, returnLineId },\n position: positionStart + index,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n createdAdjustments.push(adjustment)\n tx.persist(adjustment)\n\n line.returnedQuantity = (toNumeric(line.returnedQuantity) + quantity).toString()\n line.updatedAt = new Date()\n tx.persist(line)\n })\n\n const lineSnapshots: SalesLineSnapshot[] = orderLines.map(mapOrderLineEntityToSnapshot)\n const adjustmentDrafts: SalesAdjustmentDraft[] = [...existingAdjustments, ...createdAdjustments].map(mapOrderAdjustmentToDraft)\n const calculation = await salesCalculationService.calculateDocumentTotals({\n documentKind: 'order',\n lines: lineSnapshots,\n adjustments: adjustmentDrafts,\n context: buildCalculationContext(order),\n })\n applyOrderTotals(order, calculation.totals, calculation.lines.length)\n order.updatedAt = new Date()\n tx.persist(order)\n\n await tx.flush()\n\n return { header: entity, createdLines: createdReturnLines }\n })\n\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: header,\n identifiers: { id: header.id, organizationId: header.organizationId, tenantId: header.tenantId },\n indexer: { entityType: E.sales.sales_return },\n events: returnCrudEvents,\n })\n\n if (createdLines.length) {\n await Promise.all(\n createdLines.map((line) =>\n emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: line,\n identifiers: { id: line.id, organizationId: line.organizationId, tenantId: line.tenantId },\n indexer: { entityType: E.sales.sales_return_line },\n }),\n ),\n )\n }\n\n return { returnId: header.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n return loadReturnSnapshot(em, result.returnId)\n },\n buildLog: async ({ result, snapshots }) => {\n const after = snapshots.after as ReturnSnapshot | undefined\n if (!after) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('sales.audit.returns.create', 'Create return'),\n resourceKind: 'sales.return',\n resourceId: result.returnId,\n parentResourceKind: 'sales.order',\n parentResourceId: after.orderId ?? null,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n snapshotAfter: after,\n payload: {\n undo: { after } satisfies ReturnUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<ReturnUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const order = await findOneWithDecryption(\n em,\n SalesOrder,\n { id: after.orderId, deletedAt: null },\n {},\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n if (!order) return\n\n const salesCalculationService = ctx.container.resolve<SalesCalculationService>('salesCalculationService')\n\n // Line reversals, adjustment/return removals, and the order-total recompute\n // interleave queries on the same EntityManager with scalar mutations, so they\n // must run inside an atomic flush to avoid lost updates and partial commits.\n await withAtomicFlush(\n em,\n [\n async () => {\n const lines = await findWithDecryption(\n em,\n SalesOrderLine,\n { order: order.id, deletedAt: null },\n {},\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n const lineMap = new Map(lines.map((line) => [line.id, line]))\n after.lines.forEach((entry) => {\n const line = lineMap.get(entry.orderLineId)\n if (!line) return\n const next = Math.max(0, toNumeric(line.returnedQuantity) - entry.quantityReturned)\n line.returnedQuantity = next.toString()\n line.updatedAt = new Date()\n em.persist(line)\n })\n\n if (after.adjustmentIds.length) {\n const adjustments = await findWithDecryption(\n em,\n SalesOrderAdjustment,\n { id: { $in: after.adjustmentIds }, deletedAt: null },\n {},\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n adjustments.forEach((adj) => em.remove(adj))\n }\n\n const header = await findOneWithDecryption(\n em,\n SalesReturn,\n { id: after.id, deletedAt: null },\n {},\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n const returnLines = await findWithDecryption(\n em,\n SalesReturnLine,\n { salesReturn: after.id, deletedAt: null },\n {},\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n returnLines.forEach((line) => em.remove(line))\n if (header) em.remove(header)\n\n const existingAdjustments = await findWithDecryption(\n em,\n SalesOrderAdjustment,\n { order: order.id, deletedAt: null },\n { orderBy: { position: 'asc' } },\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n const lineSnapshots: SalesLineSnapshot[] = lines.map(mapOrderLineEntityToSnapshot)\n const adjustmentDrafts: SalesAdjustmentDraft[] = existingAdjustments.map(mapOrderAdjustmentToDraft)\n const calculation = await salesCalculationService.calculateDocumentTotals({\n documentKind: 'order',\n lines: lineSnapshots,\n adjustments: adjustmentDrafts,\n context: buildCalculationContext(order),\n })\n applyOrderTotals(order, calculation.totals, calculation.lines.length)\n order.updatedAt = new Date()\n em.persist(order)\n },\n ],\n { transaction: true },\n )\n },\n}\n\nregisterCommand(createReturnCommand)\n\nexport const returnCommands = [createReturnCommand]\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -7,7 +7,16 @@ const features = [
7
7
  { id: "staff.my_availability.manage", title: "Manage my availability", module: "staff" },
8
8
  { id: "staff.my_availability.unavailability", title: "Manage my unavailability", module: "staff" },
9
9
  { id: "staff.my_leave_requests.view", title: "View my leave requests", module: "staff" },
10
- { id: "staff.my_leave_requests.send", title: "Send my leave requests", module: "staff" }
10
+ { id: "staff.my_leave_requests.send", title: "Send my leave requests", module: "staff" },
11
+ // Timesheets (Phase 1)
12
+ { id: "staff.timesheets.view", title: "View own time entries", module: "staff" },
13
+ { id: "staff.timesheets.manage_own", title: "Create/edit/delete own entries", module: "staff" },
14
+ { id: "staff.timesheets.manage_all", title: "Manage all employees entries", module: "staff" },
15
+ { id: "staff.timesheets.projects.view", title: "View time projects", module: "staff" },
16
+ { id: "staff.timesheets.projects.manage", title: "Manage time projects", module: "staff" },
17
+ // Timesheets (Phase 2)
18
+ { id: "staff.timesheets.approve", title: "Approve reportee time", module: "staff" },
19
+ { id: "staff.timesheets.lock", title: "Lock time periods", module: "staff" }
11
20
  ];
12
21
  var acl_default = features;
13
22
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/staff/acl.ts"],
4
- "sourcesContent": ["export const features = [\n { id: 'staff.view', title: 'View employees', module: 'staff' },\n { id: 'staff.manage_team', title: 'Manage employees', module: 'staff' },\n { id: 'staff.leave_requests.send', title: 'Send leave requests', module: 'staff' },\n { id: 'staff.leave_requests.manage', title: 'Manage leave requests', module: 'staff' },\n { id: 'staff.my_availability.view', title: 'View my availability', module: 'staff' },\n { id: 'staff.my_availability.manage', title: 'Manage my availability', module: 'staff' },\n { id: 'staff.my_availability.unavailability', title: 'Manage my unavailability', module: 'staff' },\n { id: 'staff.my_leave_requests.view', title: 'View my leave requests', module: 'staff' },\n { id: 'staff.my_leave_requests.send', title: 'Send my leave requests', module: 'staff' },\n]\n\nexport default features\n"],
5
- "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,EAAE,IAAI,cAAc,OAAO,kBAAkB,QAAQ,QAAQ;AAAA,EAC7D,EAAE,IAAI,qBAAqB,OAAO,oBAAoB,QAAQ,QAAQ;AAAA,EACtE,EAAE,IAAI,6BAA6B,OAAO,uBAAuB,QAAQ,QAAQ;AAAA,EACjF,EAAE,IAAI,+BAA+B,OAAO,yBAAyB,QAAQ,QAAQ;AAAA,EACrF,EAAE,IAAI,8BAA8B,OAAO,wBAAwB,QAAQ,QAAQ;AAAA,EACnF,EAAE,IAAI,gCAAgC,OAAO,0BAA0B,QAAQ,QAAQ;AAAA,EACvF,EAAE,IAAI,wCAAwC,OAAO,4BAA4B,QAAQ,QAAQ;AAAA,EACjG,EAAE,IAAI,gCAAgC,OAAO,0BAA0B,QAAQ,QAAQ;AAAA,EACvF,EAAE,IAAI,gCAAgC,OAAO,0BAA0B,QAAQ,QAAQ;AACzF;AAEA,IAAO,cAAQ;",
4
+ "sourcesContent": ["export const features = [\n { id: 'staff.view', title: 'View employees', module: 'staff' },\n { id: 'staff.manage_team', title: 'Manage employees', module: 'staff' },\n { id: 'staff.leave_requests.send', title: 'Send leave requests', module: 'staff' },\n { id: 'staff.leave_requests.manage', title: 'Manage leave requests', module: 'staff' },\n { id: 'staff.my_availability.view', title: 'View my availability', module: 'staff' },\n { id: 'staff.my_availability.manage', title: 'Manage my availability', module: 'staff' },\n { id: 'staff.my_availability.unavailability', title: 'Manage my unavailability', module: 'staff' },\n { id: 'staff.my_leave_requests.view', title: 'View my leave requests', module: 'staff' },\n { id: 'staff.my_leave_requests.send', title: 'Send my leave requests', module: 'staff' },\n\n // Timesheets (Phase 1)\n { id: 'staff.timesheets.view', title: 'View own time entries', module: 'staff' },\n { id: 'staff.timesheets.manage_own', title: 'Create/edit/delete own entries', module: 'staff' },\n { id: 'staff.timesheets.manage_all', title: 'Manage all employees entries', module: 'staff' },\n { id: 'staff.timesheets.projects.view', title: 'View time projects', module: 'staff' },\n { id: 'staff.timesheets.projects.manage', title: 'Manage time projects', module: 'staff' },\n\n // Timesheets (Phase 2)\n { id: 'staff.timesheets.approve', title: 'Approve reportee time', module: 'staff' },\n { id: 'staff.timesheets.lock', title: 'Lock time periods', module: 'staff' },\n]\n\nexport default features\n"],
5
+ "mappings": "AAAO,MAAM,WAAW;AAAA,EACtB,EAAE,IAAI,cAAc,OAAO,kBAAkB,QAAQ,QAAQ;AAAA,EAC7D,EAAE,IAAI,qBAAqB,OAAO,oBAAoB,QAAQ,QAAQ;AAAA,EACtE,EAAE,IAAI,6BAA6B,OAAO,uBAAuB,QAAQ,QAAQ;AAAA,EACjF,EAAE,IAAI,+BAA+B,OAAO,yBAAyB,QAAQ,QAAQ;AAAA,EACrF,EAAE,IAAI,8BAA8B,OAAO,wBAAwB,QAAQ,QAAQ;AAAA,EACnF,EAAE,IAAI,gCAAgC,OAAO,0BAA0B,QAAQ,QAAQ;AAAA,EACvF,EAAE,IAAI,wCAAwC,OAAO,4BAA4B,QAAQ,QAAQ;AAAA,EACjG,EAAE,IAAI,gCAAgC,OAAO,0BAA0B,QAAQ,QAAQ;AAAA,EACvF,EAAE,IAAI,gCAAgC,OAAO,0BAA0B,QAAQ,QAAQ;AAAA;AAAA,EAGvF,EAAE,IAAI,yBAAyB,OAAO,yBAAyB,QAAQ,QAAQ;AAAA,EAC/E,EAAE,IAAI,+BAA+B,OAAO,kCAAkC,QAAQ,QAAQ;AAAA,EAC9F,EAAE,IAAI,+BAA+B,OAAO,gCAAgC,QAAQ,QAAQ;AAAA,EAC5F,EAAE,IAAI,kCAAkC,OAAO,sBAAsB,QAAQ,QAAQ;AAAA,EACrF,EAAE,IAAI,oCAAoC,OAAO,wBAAwB,QAAQ,QAAQ;AAAA;AAAA,EAGzF,EAAE,IAAI,4BAA4B,OAAO,yBAAyB,QAAQ,QAAQ;AAAA,EAClF,EAAE,IAAI,yBAAyB,OAAO,qBAAqB,QAAQ,QAAQ;AAC7E;AAEA,IAAO,cAAQ;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,33 @@
1
+ const analyticsConfig = {
2
+ entities: [
3
+ {
4
+ entityId: "staff:staff_time_entries",
5
+ requiredFeatures: ["staff.timesheets.view"],
6
+ entityConfig: {
7
+ tableName: "staff_time_entries",
8
+ dateField: "date",
9
+ defaultScopeFields: ["tenant_id", "organization_id"]
10
+ },
11
+ fieldMappings: {
12
+ id: { dbColumn: "id", type: "uuid" },
13
+ durationMinutes: { dbColumn: "duration_minutes", type: "numeric" },
14
+ date: { dbColumn: "date", type: "timestamp" },
15
+ timeProjectId: { dbColumn: "time_project_id", type: "uuid" },
16
+ staffMemberId: { dbColumn: "staff_member_id", type: "uuid" },
17
+ source: { dbColumn: "source", type: "text" }
18
+ },
19
+ labelResolvers: {
20
+ timeProjectId: { table: "staff_time_projects", idColumn: "id", labelColumn: "name" },
21
+ staffMemberId: { table: "staff_team_members", idColumn: "id", labelColumn: "display_name" }
22
+ }
23
+ }
24
+ ]
25
+ };
26
+ const config = analyticsConfig;
27
+ var analytics_default = analyticsConfig;
28
+ export {
29
+ analyticsConfig,
30
+ config,
31
+ analytics_default as default
32
+ };
33
+ //# sourceMappingURL=analytics.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/staff/analytics.ts"],
4
+ "sourcesContent": ["import type { AnalyticsModuleConfig } from '@open-mercato/shared/modules/analytics'\n\nexport const analyticsConfig: AnalyticsModuleConfig = {\n entities: [\n {\n entityId: 'staff:staff_time_entries',\n requiredFeatures: ['staff.timesheets.view'],\n entityConfig: {\n tableName: 'staff_time_entries',\n dateField: 'date',\n defaultScopeFields: ['tenant_id', 'organization_id'],\n },\n fieldMappings: {\n id: { dbColumn: 'id', type: 'uuid' },\n durationMinutes: { dbColumn: 'duration_minutes', type: 'numeric' },\n date: { dbColumn: 'date', type: 'timestamp' },\n timeProjectId: { dbColumn: 'time_project_id', type: 'uuid' },\n staffMemberId: { dbColumn: 'staff_member_id', type: 'uuid' },\n source: { dbColumn: 'source', type: 'text' },\n },\n labelResolvers: {\n timeProjectId: { table: 'staff_time_projects', idColumn: 'id', labelColumn: 'name' },\n staffMemberId: { table: 'staff_team_members', idColumn: 'id', labelColumn: 'display_name' },\n },\n },\n ],\n}\n\nexport const config = analyticsConfig\nexport default analyticsConfig\n"],
5
+ "mappings": "AAEO,MAAM,kBAAyC;AAAA,EACpD,UAAU;AAAA,IACR;AAAA,MACE,UAAU;AAAA,MACV,kBAAkB,CAAC,uBAAuB;AAAA,MAC1C,cAAc;AAAA,QACZ,WAAW;AAAA,QACX,WAAW;AAAA,QACX,oBAAoB,CAAC,aAAa,iBAAiB;AAAA,MACrD;AAAA,MACA,eAAe;AAAA,QACb,IAAI,EAAE,UAAU,MAAM,MAAM,OAAO;AAAA,QACnC,iBAAiB,EAAE,UAAU,oBAAoB,MAAM,UAAU;AAAA,QACjE,MAAM,EAAE,UAAU,QAAQ,MAAM,YAAY;AAAA,QAC5C,eAAe,EAAE,UAAU,mBAAmB,MAAM,OAAO;AAAA,QAC3D,eAAe,EAAE,UAAU,mBAAmB,MAAM,OAAO;AAAA,QAC3D,QAAQ,EAAE,UAAU,UAAU,MAAM,OAAO;AAAA,MAC7C;AAAA,MACA,gBAAgB;AAAA,QACd,eAAe,EAAE,OAAO,uBAAuB,UAAU,MAAM,aAAa,OAAO;AAAA,QACnF,eAAe,EAAE,OAAO,sBAAsB,UAAU,MAAM,aAAa,eAAe;AAAA,MAC5F;AAAA,IACF;AAAA,EACF;AACF;AAEO,MAAM,SAAS;AACtB,IAAO,oBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,31 @@
1
+ import {
2
+ bridgeLegacyGuard,
3
+ runMutationGuards
4
+ } from "@open-mercato/shared/lib/crud/mutation-guard-registry";
5
+ function resolveUserFeatures(auth) {
6
+ const features = auth?.features;
7
+ if (!Array.isArray(features)) return [];
8
+ return features.filter((value) => typeof value === "string");
9
+ }
10
+ async function runStaffMutationGuards(container, input, userFeatures) {
11
+ const legacyGuard = bridgeLegacyGuard(container);
12
+ if (!legacyGuard) {
13
+ return { ok: true, afterSuccessCallbacks: [] };
14
+ }
15
+ return runMutationGuards([legacyGuard], input, { userFeatures });
16
+ }
17
+ async function runStaffMutationGuardAfterSuccess(callbacks, input) {
18
+ for (const callback of callbacks) {
19
+ if (!callback.guard.afterSuccess) continue;
20
+ await callback.guard.afterSuccess({
21
+ ...input,
22
+ metadata: callback.metadata ?? null
23
+ });
24
+ }
25
+ }
26
+ export {
27
+ resolveUserFeatures,
28
+ runStaffMutationGuardAfterSuccess,
29
+ runStaffMutationGuards
30
+ };
31
+ //# sourceMappingURL=guards.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/staff/api/guards.ts"],
4
+ "sourcesContent": ["import type { AwilixContainer } from 'awilix'\nimport {\n bridgeLegacyGuard,\n runMutationGuards,\n type MutationGuard,\n type MutationGuardInput,\n} from '@open-mercato/shared/lib/crud/mutation-guard-registry'\n\ntype GuardAfterCallback = {\n guard: MutationGuard\n metadata: Record<string, unknown> | null\n}\n\nexport function resolveUserFeatures(auth: unknown): string[] {\n const features = (auth as { features?: unknown })?.features\n if (!Array.isArray(features)) return []\n return features.filter((value): value is string => typeof value === 'string')\n}\n\nexport async function runStaffMutationGuards(\n container: AwilixContainer,\n input: MutationGuardInput,\n userFeatures: string[],\n): Promise<{\n ok: boolean\n errorBody?: Record<string, unknown>\n errorStatus?: number\n modifiedPayload?: Record<string, unknown>\n afterSuccessCallbacks: GuardAfterCallback[]\n}> {\n const legacyGuard = bridgeLegacyGuard(container)\n if (!legacyGuard) {\n return { ok: true, afterSuccessCallbacks: [] }\n }\n\n return runMutationGuards([legacyGuard], input, { userFeatures })\n}\n\nexport async function runStaffMutationGuardAfterSuccess(\n callbacks: GuardAfterCallback[],\n input: {\n tenantId: string\n organizationId: string | null\n userId: string\n resourceKind: string\n resourceId: string\n operation: 'create' | 'update' | 'delete'\n requestMethod: string\n requestHeaders: Headers\n },\n): Promise<void> {\n for (const callback of callbacks) {\n if (!callback.guard.afterSuccess) continue\n await callback.guard.afterSuccess({\n ...input,\n metadata: callback.metadata ?? null,\n })\n }\n}\n"],
5
+ "mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAOA,SAAS,oBAAoB,MAAyB;AAC3D,QAAM,WAAY,MAAiC;AACnD,MAAI,CAAC,MAAM,QAAQ,QAAQ,EAAG,QAAO,CAAC;AACtC,SAAO,SAAS,OAAO,CAAC,UAA2B,OAAO,UAAU,QAAQ;AAC9E;AAEA,eAAsB,uBACpB,WACA,OACA,cAOC;AACD,QAAM,cAAc,kBAAkB,SAAS;AAC/C,MAAI,CAAC,aAAa;AAChB,WAAO,EAAE,IAAI,MAAM,uBAAuB,CAAC,EAAE;AAAA,EAC/C;AAEA,SAAO,kBAAkB,CAAC,WAAW,GAAG,OAAO,EAAE,aAAa,CAAC;AACjE;AAEA,eAAsB,kCACpB,WACA,OAUe;AACf,aAAW,YAAY,WAAW;AAChC,QAAI,CAAC,SAAS,MAAM,aAAc;AAClC,UAAM,SAAS,MAAM,aAAa;AAAA,MAChC,GAAG;AAAA,MACH,UAAU,SAAS,YAAY;AAAA,IACjC,CAAC;AAAA,EACH;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,96 @@
1
+ import { hasFeature } from "@open-mercato/shared/security/features";
2
+ import { getStaffMemberByUserId } from "../lib/staffMemberResolver.js";
3
+ async function hasManageAllFeature(context) {
4
+ if (hasFeature(context.userFeatures, "staff.timesheets.manage_all")) return true;
5
+ try {
6
+ const rbac = context.container.resolve("rbacService");
7
+ if (rbac?.userHasAllFeatures) {
8
+ return await rbac.userHasAllFeatures(
9
+ context.userId,
10
+ ["staff.timesheets.manage_all"],
11
+ { tenantId: context.tenantId ?? null, organizationId: context.organizationId ?? null }
12
+ );
13
+ }
14
+ } catch {
15
+ }
16
+ return false;
17
+ }
18
+ const interceptors = [
19
+ {
20
+ id: "staff.timesheets.self-scope-widget-data",
21
+ targetRoute: "dashboards/widgets/data",
22
+ methods: ["POST"],
23
+ priority: 70,
24
+ async before(request, context) {
25
+ const entityType = request.body?.entityType;
26
+ if (entityType !== "staff:staff_time_entries") {
27
+ return { ok: true };
28
+ }
29
+ if (await hasManageAllFeature(context)) {
30
+ return { ok: true };
31
+ }
32
+ const staffMember = await getStaffMemberByUserId(
33
+ context.em,
34
+ context.userId,
35
+ context.tenantId ?? null,
36
+ context.organizationId ?? null
37
+ );
38
+ if (!staffMember) {
39
+ return {
40
+ ok: false,
41
+ statusCode: 403,
42
+ message: "User is not a staff member."
43
+ };
44
+ }
45
+ const existingFilters = Array.isArray(request.body?.filters) ? request.body.filters : [];
46
+ const otherFilters = existingFilters.filter(
47
+ (f) => f.field !== "staffMemberId"
48
+ );
49
+ return {
50
+ ok: true,
51
+ body: {
52
+ ...request.body,
53
+ filters: [
54
+ ...otherFilters,
55
+ { field: "staffMemberId", operator: "eq", value: staffMember.id }
56
+ ]
57
+ }
58
+ };
59
+ }
60
+ },
61
+ {
62
+ id: "staff.timesheets.self-scope-time-entries",
63
+ targetRoute: "staff/timesheets/time-entries",
64
+ methods: ["GET"],
65
+ priority: 70,
66
+ async before(request, context) {
67
+ if (await hasManageAllFeature(context)) {
68
+ return { ok: true };
69
+ }
70
+ const staffMember = await getStaffMemberByUserId(
71
+ context.em,
72
+ context.userId,
73
+ context.tenantId ?? null,
74
+ context.organizationId ?? null
75
+ );
76
+ if (!staffMember) {
77
+ return {
78
+ ok: false,
79
+ statusCode: 403,
80
+ message: "User is not a staff member."
81
+ };
82
+ }
83
+ return {
84
+ ok: true,
85
+ query: {
86
+ ...request.query ?? {},
87
+ staffMemberId: staffMember.id
88
+ }
89
+ };
90
+ }
91
+ }
92
+ ];
93
+ export {
94
+ interceptors
95
+ };
96
+ //# sourceMappingURL=interceptors.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/modules/staff/api/interceptors.ts"],
4
+ "sourcesContent": ["import type { ApiInterceptor, InterceptorContext } from '@open-mercato/shared/lib/crud/api-interceptor'\nimport { hasFeature } from '@open-mercato/shared/security/features'\nimport { getStaffMemberByUserId } from '../lib/staffMemberResolver'\n\ntype RbacServiceLike = {\n userHasAllFeatures: (\n userId: string,\n required: string[],\n scope: { tenantId: string | null; organizationId: string | null },\n ) => Promise<boolean>\n}\n\n/**\n * Checks whether the caller has manage_all rights, honoring wildcard ACL grants\n * (`staff.*`, `*`) and the super-admin flag.\n *\n * `context.userFeatures` is populated only on CRUD-factory routes (factory.ts\n * calls `rbacService.getGrantedFeatures`). Custom routes that wire interceptors\n * manually (like `dashboards/widgets/data`) pass `auth.features` directly, and\n * the JWT does NOT include features \u2014 so we must fall back to loading the ACL\n * from `rbacService.userHasAllFeatures`, which is cached and wildcard-aware.\n */\nasync function hasManageAllFeature(context: InterceptorContext): Promise<boolean> {\n if (hasFeature(context.userFeatures, 'staff.timesheets.manage_all')) return true\n try {\n const rbac = context.container.resolve('rbacService') as RbacServiceLike | undefined\n if (rbac?.userHasAllFeatures) {\n return await rbac.userHasAllFeatures(\n context.userId,\n ['staff.timesheets.manage_all'],\n { tenantId: context.tenantId ?? null, organizationId: context.organizationId ?? null },\n )\n }\n } catch {\n // rbacService not available \u2014 fall through to deny\n }\n return false\n}\n\nexport const interceptors: ApiInterceptor[] = [\n {\n id: 'staff.timesheets.self-scope-widget-data',\n targetRoute: 'dashboards/widgets/data',\n methods: ['POST'],\n priority: 70,\n async before(request, context) {\n const entityType = request.body?.entityType\n if (entityType !== 'staff:staff_time_entries') {\n return { ok: true }\n }\n\n if (await hasManageAllFeature(context)) {\n return { ok: true }\n }\n\n const staffMember = await getStaffMemberByUserId(\n context.em,\n context.userId,\n context.tenantId ?? null,\n context.organizationId ?? null,\n )\n\n if (!staffMember) {\n return {\n ok: false,\n statusCode: 403,\n message: 'User is not a staff member.',\n }\n }\n\n const existingFilters = Array.isArray(request.body?.filters) ? request.body.filters : []\n const otherFilters = existingFilters.filter(\n (f: Record<string, unknown>) => f.field !== 'staffMemberId',\n )\n\n return {\n ok: true,\n body: {\n ...request.body,\n filters: [\n ...otherFilters,\n { field: 'staffMemberId', operator: 'eq', value: staffMember.id },\n ],\n },\n }\n },\n },\n {\n id: 'staff.timesheets.self-scope-time-entries',\n targetRoute: 'staff/timesheets/time-entries',\n methods: ['GET'],\n priority: 70,\n async before(request, context) {\n if (await hasManageAllFeature(context)) {\n return { ok: true }\n }\n\n const staffMember = await getStaffMemberByUserId(\n context.em,\n context.userId,\n context.tenantId ?? null,\n context.organizationId ?? null,\n )\n\n if (!staffMember) {\n return {\n ok: false,\n statusCode: 403,\n message: 'User is not a staff member.',\n }\n }\n\n return {\n ok: true,\n query: {\n ...(request.query ?? {}),\n staffMemberId: staffMember.id,\n },\n }\n },\n },\n]\n"],
5
+ "mappings": "AACA,SAAS,kBAAkB;AAC3B,SAAS,8BAA8B;AAoBvC,eAAe,oBAAoB,SAA+C;AAChF,MAAI,WAAW,QAAQ,cAAc,6BAA6B,EAAG,QAAO;AAC5E,MAAI;AACF,UAAM,OAAO,QAAQ,UAAU,QAAQ,aAAa;AACpD,QAAI,MAAM,oBAAoB;AAC5B,aAAO,MAAM,KAAK;AAAA,QAChB,QAAQ;AAAA,QACR,CAAC,6BAA6B;AAAA,QAC9B,EAAE,UAAU,QAAQ,YAAY,MAAM,gBAAgB,QAAQ,kBAAkB,KAAK;AAAA,MACvF;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEO,MAAM,eAAiC;AAAA,EAC5C;AAAA,IACE,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,SAAS,CAAC,MAAM;AAAA,IAChB,UAAU;AAAA,IACV,MAAM,OAAO,SAAS,SAAS;AAC7B,YAAM,aAAa,QAAQ,MAAM;AACjC,UAAI,eAAe,4BAA4B;AAC7C,eAAO,EAAE,IAAI,KAAK;AAAA,MACpB;AAEA,UAAI,MAAM,oBAAoB,OAAO,GAAG;AACtC,eAAO,EAAE,IAAI,KAAK;AAAA,MACpB;AAEA,YAAM,cAAc,MAAM;AAAA,QACxB,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ,YAAY;AAAA,QACpB,QAAQ,kBAAkB;AAAA,MAC5B;AAEA,UAAI,CAAC,aAAa;AAChB,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,YAAY;AAAA,UACZ,SAAS;AAAA,QACX;AAAA,MACF;AAEA,YAAM,kBAAkB,MAAM,QAAQ,QAAQ,MAAM,OAAO,IAAI,QAAQ,KAAK,UAAU,CAAC;AACvF,YAAM,eAAe,gBAAgB;AAAA,QACnC,CAAC,MAA+B,EAAE,UAAU;AAAA,MAC9C;AAEA,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,MAAM;AAAA,UACJ,GAAG,QAAQ;AAAA,UACX,SAAS;AAAA,YACP,GAAG;AAAA,YACH,EAAE,OAAO,iBAAiB,UAAU,MAAM,OAAO,YAAY,GAAG;AAAA,UAClE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,SAAS,CAAC,KAAK;AAAA,IACf,UAAU;AAAA,IACV,MAAM,OAAO,SAAS,SAAS;AAC7B,UAAI,MAAM,oBAAoB,OAAO,GAAG;AACtC,eAAO,EAAE,IAAI,KAAK;AAAA,MACpB;AAEA,YAAM,cAAc,MAAM;AAAA,QACxB,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ,YAAY;AAAA,QACpB,QAAQ,kBAAkB;AAAA,MAC5B;AAEA,UAAI,CAAC,aAAa;AAChB,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,YAAY;AAAA,UACZ,SAAS;AAAA,QACX;AAAA,MACF;AAEA,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,OAAO;AAAA,UACL,GAAI,QAAQ,SAAS,CAAC;AAAA,UACtB,eAAe,YAAY;AAAA,QAC7B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,170 @@
1
+ import { NextResponse } from "next/server";
2
+ import { z } from "zod";
3
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
5
+ import { resolveOrganizationScopeForRequest } from "@open-mercato/core/modules/directory/utils/organizationScope";
6
+ import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
7
+ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
8
+ import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
9
+ import { readJsonSafe } from "@open-mercato/shared/lib/http/readJsonSafe";
10
+ import { StaffTimeProjectMember, StaffTeamMember } from "../../../../data/entities.js";
11
+ import { staffMyProjectVisibilityUpdateSchema } from "../../../../data/validators.js";
12
+ import {
13
+ resolveUserFeatures,
14
+ runStaffMutationGuardAfterSuccess,
15
+ runStaffMutationGuards
16
+ } from "../../../guards.js";
17
+ const metadata = {
18
+ PATCH: { requireAuth: true, requireFeatures: ["staff.timesheets.manage_own"] }
19
+ };
20
+ function extractProjectIdFromUrl(req) {
21
+ try {
22
+ const url = new URL(req.url);
23
+ const match = url.pathname.match(/\/my-projects\/([^/]+)(?:\/|$)/);
24
+ return match?.[1] ?? null;
25
+ } catch {
26
+ return null;
27
+ }
28
+ }
29
+ async function PATCH(req) {
30
+ try {
31
+ const container = await createRequestContainer();
32
+ const auth = await getAuthFromRequest(req);
33
+ const { translate } = await resolveTranslations();
34
+ if (!auth) {
35
+ throw new CrudHttpError(401, { error: translate("staff.errors.unauthorized", "Unauthorized") });
36
+ }
37
+ const projectId = extractProjectIdFromUrl(req);
38
+ if (!projectId || !z.string().uuid().safeParse(projectId).success) {
39
+ throw new CrudHttpError(400, {
40
+ error: translate("staff.timesheets.errors.invalidProjectId", "Invalid project id.")
41
+ });
42
+ }
43
+ const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req });
44
+ const tenantId = scope?.tenantId ?? auth.tenantId ?? null;
45
+ const organizationId = scope?.selectedId ?? auth.orgId ?? null;
46
+ if (!tenantId || !organizationId) {
47
+ throw new CrudHttpError(400, {
48
+ error: translate("staff.errors.missingScope", "Missing tenant or organization scope.")
49
+ });
50
+ }
51
+ const rawBody = await readJsonSafe(req, {});
52
+ const parsed = staffMyProjectVisibilityUpdateSchema.safeParse(rawBody);
53
+ if (!parsed.success) {
54
+ throw new CrudHttpError(400, {
55
+ error: translate("staff.timesheets.errors.invalidBody", "Invalid request body."),
56
+ details: parsed.error.flatten()
57
+ });
58
+ }
59
+ const em = container.resolve("em").fork();
60
+ const scopeCtx = { tenantId, organizationId };
61
+ const staffMember = await findOneWithDecryption(
62
+ em,
63
+ StaffTeamMember,
64
+ { userId: auth.sub, tenantId, organizationId, deletedAt: null },
65
+ {},
66
+ scopeCtx
67
+ );
68
+ if (!staffMember) {
69
+ throw new CrudHttpError(403, {
70
+ error: translate("staff.timesheets.errors.noStaffMember", "No staff member linked to your account.")
71
+ });
72
+ }
73
+ const membership = await findOneWithDecryption(
74
+ em,
75
+ StaffTimeProjectMember,
76
+ {
77
+ timeProjectId: projectId,
78
+ staffMemberId: staffMember.id,
79
+ tenantId,
80
+ organizationId,
81
+ deletedAt: null,
82
+ status: "active"
83
+ },
84
+ {},
85
+ scopeCtx
86
+ );
87
+ if (!membership) {
88
+ throw new CrudHttpError(404, {
89
+ error: translate("staff.timesheets.errors.notAssigned", "You are not assigned to this project.")
90
+ });
91
+ }
92
+ const guardResult = await runStaffMutationGuards(
93
+ container,
94
+ {
95
+ tenantId,
96
+ organizationId,
97
+ userId: auth.sub ?? "",
98
+ resourceKind: "staff.timesheets.time_project_member",
99
+ resourceId: membership.id,
100
+ operation: "update",
101
+ requestMethod: req.method,
102
+ requestHeaders: req.headers,
103
+ mutationPayload: parsed.data
104
+ },
105
+ resolveUserFeatures(auth)
106
+ );
107
+ if (!guardResult.ok) {
108
+ return NextResponse.json(
109
+ guardResult.errorBody ?? { error: "Operation blocked by guard" },
110
+ { status: guardResult.errorStatus ?? 422 }
111
+ );
112
+ }
113
+ membership.showInGrid = parsed.data.showInGrid;
114
+ await em.flush();
115
+ if (guardResult.afterSuccessCallbacks.length) {
116
+ await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {
117
+ tenantId,
118
+ organizationId,
119
+ userId: auth.sub ?? "",
120
+ resourceKind: "staff.timesheets.time_project_member",
121
+ resourceId: membership.id,
122
+ operation: "update",
123
+ requestMethod: req.method,
124
+ requestHeaders: req.headers
125
+ });
126
+ }
127
+ return NextResponse.json({ ok: true, showInGrid: membership.showInGrid }, { status: 200 });
128
+ } catch (err) {
129
+ if (err instanceof CrudHttpError) {
130
+ return NextResponse.json(err.body, { status: err.status });
131
+ }
132
+ console.error("staff.timesheets.my-projects.patch failed", err);
133
+ const { translate } = await resolveTranslations();
134
+ return NextResponse.json(
135
+ { error: translate("staff.timesheets.errors.updateMyProject", "Failed to update project visibility.") },
136
+ { status: 400 }
137
+ );
138
+ }
139
+ }
140
+ const openApi = {
141
+ tag: "Staff",
142
+ summary: "My project grid visibility",
143
+ methods: {
144
+ PATCH: {
145
+ summary: "Toggle grid visibility for one of the caller's assigned time projects",
146
+ description: "Self-service endpoint. Only the authenticated user can toggle `show_in_grid` on their own active membership for the given project. Does not require the admin-only `staff.timesheets.projects.manage` feature.",
147
+ requestBody: {
148
+ contentType: "application/json",
149
+ schema: staffMyProjectVisibilityUpdateSchema
150
+ },
151
+ responses: [
152
+ {
153
+ status: 200,
154
+ description: "Visibility updated",
155
+ schema: z.object({ ok: z.boolean(), showInGrid: z.boolean() })
156
+ },
157
+ { status: 400, description: "Invalid input", schema: z.object({ error: z.string() }) },
158
+ { status: 401, description: "Unauthorized", schema: z.object({ error: z.string() }) },
159
+ { status: 403, description: "No staff member linked", schema: z.object({ error: z.string() }) },
160
+ { status: 404, description: "Project membership not found", schema: z.object({ error: z.string() }) }
161
+ ]
162
+ }
163
+ }
164
+ };
165
+ export {
166
+ PATCH,
167
+ metadata,
168
+ openApi
169
+ };
170
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/staff/api/timesheets/my-projects/%5BprojectId%5D/route.ts"],
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { StaffTimeProjectMember, StaffTeamMember } from '../../../../data/entities'\nimport { staffMyProjectVisibilityUpdateSchema } from '../../../../data/validators'\nimport {\n resolveUserFeatures,\n runStaffMutationGuardAfterSuccess,\n runStaffMutationGuards,\n} from '../../../guards'\n\nexport const metadata = {\n PATCH: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n}\n\nfunction extractProjectIdFromUrl(req: Request): string | null {\n try {\n const url = new URL(req.url)\n const match = url.pathname.match(/\\/my-projects\\/([^/]+)(?:\\/|$)/)\n return match?.[1] ?? null\n } catch {\n return null\n }\n}\n\n/**\n * PATCH /api/staff/timesheets/my-projects/{projectId}\n *\n * Self-service endpoint for the authenticated user to toggle visibility of a\n * time project on their own My Timesheets grid. Does not require the admin-only\n * `staff.timesheets.projects.manage` feature \u2014 only `staff.timesheets.manage_own`.\n *\n * Added 2026-04-13 to close a gap in the original UX enhancements spec:\n * \"+ Add row\" and the new X remove button need to persist per-user grid membership.\n */\nexport async function PATCH(req: Request) {\n try {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) {\n throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n }\n\n const projectId = extractProjectIdFromUrl(req)\n if (!projectId || !z.string().uuid().safeParse(projectId).success) {\n throw new CrudHttpError(400, {\n error: translate('staff.timesheets.errors.invalidProjectId', 'Invalid project id.'),\n })\n }\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const tenantId = scope?.tenantId ?? auth.tenantId ?? null\n const organizationId = scope?.selectedId ?? auth.orgId ?? null\n if (!tenantId || !organizationId) {\n throw new CrudHttpError(400, {\n error: translate('staff.errors.missingScope', 'Missing tenant or organization scope.'),\n })\n }\n\n const rawBody = await readJsonSafe(req, {})\n const parsed = staffMyProjectVisibilityUpdateSchema.safeParse(rawBody)\n if (!parsed.success) {\n throw new CrudHttpError(400, {\n error: translate('staff.timesheets.errors.invalidBody', 'Invalid request body.'),\n details: parsed.error.flatten(),\n })\n }\n\n const em = (container.resolve('em') as EntityManager).fork()\n const scopeCtx = { tenantId, organizationId }\n\n const staffMember = await findOneWithDecryption(\n em,\n StaffTeamMember,\n { userId: auth.sub, tenantId, organizationId, deletedAt: null },\n {},\n scopeCtx,\n )\n if (!staffMember) {\n throw new CrudHttpError(403, {\n error: translate('staff.timesheets.errors.noStaffMember', 'No staff member linked to your account.'),\n })\n }\n\n const membership = await findOneWithDecryption(\n em,\n StaffTimeProjectMember,\n {\n timeProjectId: projectId,\n staffMemberId: staffMember.id,\n tenantId,\n organizationId,\n deletedAt: null,\n status: 'active',\n },\n {},\n scopeCtx,\n )\n if (!membership) {\n throw new CrudHttpError(404, {\n error: translate('staff.timesheets.errors.notAssigned', 'You are not assigned to this project.'),\n })\n }\n\n const guardResult = await runStaffMutationGuards(\n container,\n {\n tenantId,\n organizationId,\n userId: auth.sub ?? '',\n resourceKind: 'staff.timesheets.time_project_member',\n resourceId: membership.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: parsed.data as unknown as Record<string, unknown>,\n },\n resolveUserFeatures(auth),\n )\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n membership.showInGrid = parsed.data.showInGrid\n await em.flush()\n\n if (guardResult.afterSuccessCallbacks.length) {\n await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId,\n organizationId,\n userId: auth.sub ?? '',\n resourceKind: 'staff.timesheets.time_project_member',\n resourceId: membership.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n }\n\n return NextResponse.json({ ok: true, showInGrid: membership.showInGrid }, { status: 200 })\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n console.error('staff.timesheets.my-projects.patch failed', err)\n const { translate } = await resolveTranslations()\n return NextResponse.json(\n { error: translate('staff.timesheets.errors.updateMyProject', 'Failed to update project visibility.') },\n { status: 400 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'My project grid visibility',\n methods: {\n PATCH: {\n summary: 'Toggle grid visibility for one of the caller\\'s assigned time projects',\n description:\n 'Self-service endpoint. Only the authenticated user can toggle `show_in_grid` on their own active membership for the given project. Does not require the admin-only `staff.timesheets.projects.manage` feature.',\n requestBody: {\n contentType: 'application/json',\n schema: staffMyProjectVisibilityUpdateSchema,\n },\n responses: [\n {\n status: 200,\n description: 'Visibility updated',\n schema: z.object({ ok: z.boolean(), showInGrid: z.boolean() }),\n },\n { status: 400, description: 'Invalid input', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 403, description: 'No staff member linked', schema: z.object({ error: z.string() }) },\n { status: 404, description: 'Project membership not found', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }