@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
@@ -15,6 +15,7 @@ import {
15
15
  diffCustomFieldChanges,
16
16
  } from '@open-mercato/shared/lib/commands/customFieldSnapshots'
17
17
  import { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'
18
+ import { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'
18
19
  import {
19
20
  parseWithCustomFields,
20
21
  setCustomFieldsIfAny,
@@ -291,36 +292,43 @@ const createOrganizationCommand: CommandHandler<Record<string, unknown>, Organiz
291
292
  const baseSlug = parsed.slug ? parsed.slug : slugify(parsed.name)
292
293
  const slug = baseSlug ? await resolveUniqueSlug(em, tenantId, baseSlug) : null
293
294
  const de = (ctx.container.resolve('dataEngine') as DataEngine)
294
- const organization = await de.createOrmEntity({
295
- entity: Organization,
296
- data: {
297
- tenant: tenantRef,
298
- name: parsed.name,
299
- slug,
300
- isActive: parsed.isActive ?? true,
301
- parentId,
302
- },
303
- })
304
- setInternalTenantId(organization, tenantId)
305
- const recordId = String(organization.id)
306
295
 
307
- if (childIds.length) {
308
- await assignChildren(em, tenantId, recordId, childIds)
309
- }
310
- const childParentsAfter = await loadChildParentSnapshots(em, tenantId, childIds)
311
- setUndoMeta(organization, { childParentsBefore, childParentsAfter })
296
+ let organization!: Organization
297
+ await withAtomicFlush(em, [
298
+ async () => {
299
+ organization = await de.createOrmEntity({
300
+ entity: Organization,
301
+ data: {
302
+ tenant: tenantRef,
303
+ name: parsed.name,
304
+ slug,
305
+ isActive: parsed.isActive ?? true,
306
+ parentId,
307
+ },
308
+ })
309
+ setInternalTenantId(organization, tenantId)
310
+ const recordId = String(organization.id)
312
311
 
313
- await setCustomFieldsIfAny({
314
- dataEngine: de,
315
- entityId: E.directory.organization,
316
- recordId,
317
- tenantId,
318
- organizationId: recordId,
319
- values: custom,
320
- })
312
+ if (childIds.length) {
313
+ await assignChildren(em, tenantId, recordId, childIds)
314
+ }
315
+ const childParentsAfter = await loadChildParentSnapshots(em, tenantId, childIds)
316
+ setUndoMeta(organization, { childParentsBefore, childParentsAfter })
317
+
318
+ await setCustomFieldsIfAny({
319
+ dataEngine: de,
320
+ entityId: E.directory.organization,
321
+ recordId,
322
+ tenantId,
323
+ organizationId: recordId,
324
+ values: custom,
325
+ })
321
326
 
322
- await rebuildHierarchyForTenant(em, tenantId)
327
+ await rebuildHierarchyForTenant(em, tenantId)
328
+ },
329
+ ], { transaction: true })
323
330
 
331
+ const recordId = String(organization.id)
324
332
  const identifiers = { id: recordId, organizationId: recordId, tenantId }
325
333
  await emitCrudSideEffects({
326
334
  dataEngine: de,
@@ -379,27 +387,31 @@ const createOrganizationCommand: CommandHandler<Record<string, unknown>, Organiz
379
387
  if (!tenantId) return
380
388
  const em = (ctx.container.resolve('em') as EntityManager)
381
389
  const de = (ctx.container.resolve('dataEngine') as DataEngine)
382
- await restoreChildParents(em, tenantId, childrenBefore)
383
- if (after.custom && Object.keys(after.custom).length) {
384
- const reset = buildCustomFieldResetMap(undefined, after.custom)
385
- if (Object.keys(reset).length) {
386
- const resetValues = reset as Parameters<DataEngine['setCustomFields']>[0]['values']
387
- await de.setCustomFields({
388
- entityId: E.directory.organization,
389
- recordId: after.id,
390
- tenantId,
391
- organizationId: after.id,
392
- values: resetValues,
393
- notify: false,
390
+ await withAtomicFlush(em, [
391
+ async () => {
392
+ await restoreChildParents(em, tenantId, childrenBefore)
393
+ if (after.custom && Object.keys(after.custom).length) {
394
+ const reset = buildCustomFieldResetMap(undefined, after.custom)
395
+ if (Object.keys(reset).length) {
396
+ const resetValues = reset as Parameters<DataEngine['setCustomFields']>[0]['values']
397
+ await de.setCustomFields({
398
+ entityId: E.directory.organization,
399
+ recordId: after.id,
400
+ tenantId,
401
+ organizationId: after.id,
402
+ values: resetValues,
403
+ notify: false,
404
+ })
405
+ }
406
+ }
407
+ await de.deleteOrmEntity({
408
+ entity: Organization,
409
+ where: { id: after.id, deletedAt: null } as FilterQuery<Organization>,
410
+ soft: false,
394
411
  })
395
- }
396
- }
397
- await de.deleteOrmEntity({
398
- entity: Organization,
399
- where: { id: after.id, deletedAt: null } as FilterQuery<Organization>,
400
- soft: false,
401
- })
402
- await rebuildHierarchyForTenant(em, tenantId)
412
+ await rebuildHierarchyForTenant(em, tenantId)
413
+ },
414
+ ], { transaction: true })
403
415
  },
404
416
  }
405
417
 
@@ -475,48 +487,56 @@ const updateOrganizationCommand: CommandHandler<Record<string, unknown>, Organiz
475
487
  resolvedSlug = parsed.slug ? await resolveUniqueSlug(em, tenantId, parsed.slug, parsed.id) : parsed.slug
476
488
  }
477
489
  const de = (ctx.container.resolve('dataEngine') as DataEngine)
478
- const organization = await de.updateOrmEntity({
479
- entity: Organization,
480
- where: { id: parsed.id, deletedAt: null } as FilterQuery<Organization>,
481
- apply: (entity) => {
482
- if (parsed.name !== undefined) entity.name = parsed.name
483
- if (resolvedSlug !== undefined) entity.slug = resolvedSlug
484
- if (parsed.isActive !== undefined) entity.isActive = parsed.isActive
485
- entity.parentId = parentId
486
- },
487
- })
488
- if (!organization) throw new CrudHttpError(404, { error: 'Not found' })
489
- setInternalTenantId(organization, tenantId)
490
-
491
- const recordId = String(organization.id)
492
- const desiredChildIds = new Set(normalizedChildIds.filter((id) => id !== recordId))
493
- await clearRemovedChildren(em, tenantId, recordId, desiredChildIds)
494
- await assignChildren(em, tenantId, recordId, desiredChildIds)
495
- const childParentsAfter = await loadChildParentSnapshots(em, tenantId, combinedChildIds)
496
- setUndoMeta(organization, { childParentsBefore, childParentsAfter })
497
490
 
498
- await setCustomFieldsIfAny({
499
- dataEngine: de,
500
- entityId: E.directory.organization,
501
- recordId,
502
- tenantId,
503
- organizationId: recordId,
504
- values: custom,
505
- })
491
+ let resolvedOrganization!: Organization
492
+ await withAtomicFlush(em, [
493
+ async () => {
494
+ const organization = await de.updateOrmEntity({
495
+ entity: Organization,
496
+ where: { id: parsed.id, deletedAt: null } as FilterQuery<Organization>,
497
+ apply: (entity) => {
498
+ if (parsed.name !== undefined) entity.name = parsed.name
499
+ if (resolvedSlug !== undefined) entity.slug = resolvedSlug
500
+ if (parsed.isActive !== undefined) entity.isActive = parsed.isActive
501
+ entity.parentId = parentId
502
+ },
503
+ })
504
+ if (!organization) throw new CrudHttpError(404, { error: 'Not found' })
505
+ setInternalTenantId(organization, tenantId)
506
+
507
+ const recordId = String(organization.id)
508
+ const desiredChildIds = new Set(normalizedChildIds.filter((id) => id !== recordId))
509
+ await clearRemovedChildren(em, tenantId, recordId, desiredChildIds)
510
+ await assignChildren(em, tenantId, recordId, desiredChildIds)
511
+ const childParentsAfter = await loadChildParentSnapshots(em, tenantId, combinedChildIds)
512
+ setUndoMeta(organization, { childParentsBefore, childParentsAfter })
513
+
514
+ await setCustomFieldsIfAny({
515
+ dataEngine: de,
516
+ entityId: E.directory.organization,
517
+ recordId,
518
+ tenantId,
519
+ organizationId: recordId,
520
+ values: custom,
521
+ })
506
522
 
507
- await rebuildHierarchyForTenant(em, tenantId)
523
+ await rebuildHierarchyForTenant(em, tenantId)
524
+ resolvedOrganization = organization
525
+ },
526
+ ], { transaction: true })
508
527
 
528
+ const recordId = String(resolvedOrganization.id)
509
529
  const identifiers = { id: recordId, organizationId: recordId, tenantId }
510
530
  await emitCrudSideEffects({
511
531
  dataEngine: de,
512
532
  action: 'updated',
513
- entity: organization,
533
+ entity: resolvedOrganization,
514
534
  identifiers,
515
535
  events: organizationCrudEvents,
516
536
  indexer: organizationCrudIndexer,
517
537
  })
518
538
 
519
- return organization
539
+ return resolvedOrganization
520
540
  },
521
541
  captureAfter: async (_input, result, ctx) => {
522
542
  const em = (ctx.container.resolve('em') as EntityManager).fork()
@@ -571,34 +591,39 @@ const updateOrganizationCommand: CommandHandler<Record<string, unknown>, Organiz
571
591
  if (!tenantId) return
572
592
  const em = (ctx.container.resolve('em') as EntityManager)
573
593
  const de = (ctx.container.resolve('dataEngine') as DataEngine)
574
- const updated = await de.updateOrmEntity({
575
- entity: Organization,
576
- where: { id: before.id } as FilterQuery<Organization>,
577
- apply: (entity) => {
578
- entity.name = before.name
579
- if (before.slug !== undefined) entity.slug = before.slug
580
- entity.isActive = before.isActive
581
- entity.parentId = before.parentId
594
+ let updated: Organization | null = null
595
+ await withAtomicFlush(em, [
596
+ async () => {
597
+ updated = await de.updateOrmEntity({
598
+ entity: Organization,
599
+ where: { id: before.id } as FilterQuery<Organization>,
600
+ apply: (entity) => {
601
+ entity.name = before.name
602
+ if (before.slug !== undefined) entity.slug = before.slug
603
+ entity.isActive = before.isActive
604
+ entity.parentId = before.parentId
605
+ },
606
+ })
607
+ if (updated && tenantId) {
608
+ setInternalTenantId(updated, tenantId)
609
+ }
610
+ const reset = buildCustomFieldResetMap(before.custom, after?.custom)
611
+ if (Object.keys(reset).length) {
612
+ const resetValues = reset as Parameters<DataEngine['setCustomFields']>[0]['values']
613
+ await de.setCustomFields({
614
+ entityId: E.directory.organization,
615
+ recordId: before.id,
616
+ tenantId,
617
+ organizationId: before.id,
618
+ values: resetValues,
619
+ notify: false,
620
+ })
621
+ }
622
+ const childSnapshots = before.childParents
623
+ await restoreChildParents(em, tenantId, childSnapshots)
624
+ await rebuildHierarchyForTenant(em, tenantId)
582
625
  },
583
- })
584
- if (updated && tenantId) {
585
- setInternalTenantId(updated, tenantId)
586
- }
587
- const reset = buildCustomFieldResetMap(before.custom, after?.custom)
588
- if (Object.keys(reset).length) {
589
- const resetValues = reset as Parameters<DataEngine['setCustomFields']>[0]['values']
590
- await de.setCustomFields({
591
- entityId: E.directory.organization,
592
- recordId: before.id,
593
- tenantId,
594
- organizationId: before.id,
595
- values: resetValues,
596
- notify: false,
597
- })
598
- }
599
- const childSnapshots = before.childParents
600
- await restoreChildParents(em, tenantId, childSnapshots)
601
- await rebuildHierarchyForTenant(em, tenantId)
626
+ ], { transaction: true })
602
627
  await emitCrudUndoSideEffects({
603
628
  dataEngine: de,
604
629
  action: 'updated',
@@ -650,41 +675,48 @@ const deleteOrganizationCommand: CommandHandler<{ body: any; query: Record<strin
650
675
  )
651
676
 
652
677
  const de = (ctx.container.resolve('dataEngine') as DataEngine)
653
- const deleted = await de.deleteOrmEntity({
654
- entity: Organization,
655
- where: { id, deletedAt: null } as FilterQuery<Organization>,
656
- soft: true,
657
- softDeleteField: 'deletedAt',
658
- })
659
- if (!deleted) throw new CrudHttpError(404, { error: 'Not found' })
660
- setInternalTenantId(deleted, tenantId)
661
- deleted.isActive = false
662
- deleted.parentId = null
663
-
664
- const childrenFilter: FilterQuery<Organization> = { tenant: tenantId, parentId: id, deletedAt: null }
665
- const children = await em.find(Organization, childrenFilter)
666
- const toPersist: Organization[] = []
667
- for (const child of children) {
668
- child.parentId = parentId
669
- toPersist.push(child)
670
- }
671
- toPersist.push(deleted)
672
- if (toPersist.length) await em.persist(toPersist).flush()
673
- setUndoMeta(deleted, { childParentsBefore: childSnapshotsBefore })
674
678
 
675
- await rebuildHierarchyForTenant(em, tenantId)
679
+ let resolvedDeleted!: Organization
680
+ await withAtomicFlush(em, [
681
+ async () => {
682
+ const deleted = await de.deleteOrmEntity({
683
+ entity: Organization,
684
+ where: { id, deletedAt: null } as FilterQuery<Organization>,
685
+ soft: true,
686
+ softDeleteField: 'deletedAt',
687
+ })
688
+ if (!deleted) throw new CrudHttpError(404, { error: 'Not found' })
689
+ setInternalTenantId(deleted, tenantId)
690
+ deleted.isActive = false
691
+ deleted.parentId = null
692
+
693
+ const childrenFilter: FilterQuery<Organization> = { tenant: tenantId, parentId: id, deletedAt: null }
694
+ const children = await em.find(Organization, childrenFilter)
695
+ const toPersist: Organization[] = []
696
+ for (const child of children) {
697
+ child.parentId = parentId
698
+ toPersist.push(child)
699
+ }
700
+ toPersist.push(deleted)
701
+ if (toPersist.length) await em.persist(toPersist).flush()
702
+ setUndoMeta(deleted, { childParentsBefore: childSnapshotsBefore })
703
+
704
+ await rebuildHierarchyForTenant(em, tenantId)
705
+ resolvedDeleted = deleted
706
+ },
707
+ ], { transaction: true })
676
708
 
677
709
  const identifiers = { id, organizationId: id, tenantId }
678
710
  await emitCrudSideEffects({
679
711
  dataEngine: de,
680
712
  action: 'deleted',
681
- entity: deleted,
713
+ entity: resolvedDeleted,
682
714
  identifiers,
683
715
  events: organizationCrudEvents,
684
716
  indexer: organizationCrudIndexer,
685
717
  })
686
718
 
687
- return deleted
719
+ return resolvedDeleted
688
720
  },
689
721
  buildLog: async ({ snapshots, input, ctx }) => {
690
722
  const { translate } = await resolveTranslations()
@@ -715,45 +747,50 @@ const deleteOrganizationCommand: CommandHandler<{ body: any; query: Record<strin
715
747
  if (!tenantId) return
716
748
  const em = (ctx.container.resolve('em') as EntityManager)
717
749
  const de = (ctx.container.resolve('dataEngine') as DataEngine)
718
- let organization = await em.findOne(Organization, { id: before.id })
719
- if (organization) {
720
- organization.deletedAt = null
721
- organization.isActive = before.isActive
722
- organization.name = before.name
723
- if (before.slug !== undefined) organization.slug = before.slug
724
- organization.parentId = before.parentId
725
- await em.flush()
726
- if (tenantId) setInternalTenantId(organization, tenantId)
727
- } else {
728
- organization = await de.createOrmEntity({
729
- entity: Organization,
730
- data: {
731
- id: before.id,
732
- name: before.name,
733
- slug: before.slug ?? null,
734
- tenant: tenantId ? em.getReference(Tenant, tenantId) : undefined,
735
- isActive: before.isActive,
736
- parentId: before.parentId,
737
- },
738
- })
739
- if (tenantId) setInternalTenantId(organization, tenantId)
740
- }
741
- if (tenantId) {
742
- const customValues = buildCustomFieldResetMap(before.custom, undefined)
743
- if (Object.keys(customValues).length) {
744
- const resetValues = customValues as Parameters<DataEngine['setCustomFields']>[0]['values']
745
- await de.setCustomFields({
746
- entityId: E.directory.organization,
747
- recordId: before.id,
748
- tenantId,
749
- organizationId: before.id,
750
- values: resetValues,
751
- notify: false,
752
- })
753
- }
754
- }
755
- await restoreChildParents(em, tenantId, before.childParents)
756
- await rebuildHierarchyForTenant(em, tenantId)
750
+ let organization: Organization | null = null
751
+ await withAtomicFlush(em, [
752
+ async () => {
753
+ organization = await em.findOne(Organization, { id: before.id })
754
+ if (organization) {
755
+ organization.deletedAt = null
756
+ organization.isActive = before.isActive
757
+ organization.name = before.name
758
+ if (before.slug !== undefined) organization.slug = before.slug
759
+ organization.parentId = before.parentId
760
+ await em.flush()
761
+ if (tenantId) setInternalTenantId(organization, tenantId)
762
+ } else {
763
+ organization = await de.createOrmEntity({
764
+ entity: Organization,
765
+ data: {
766
+ id: before.id,
767
+ name: before.name,
768
+ slug: before.slug ?? null,
769
+ tenant: tenantId ? em.getReference(Tenant, tenantId) : undefined,
770
+ isActive: before.isActive,
771
+ parentId: before.parentId,
772
+ },
773
+ })
774
+ if (tenantId) setInternalTenantId(organization, tenantId)
775
+ }
776
+ if (tenantId) {
777
+ const customValues = buildCustomFieldResetMap(before.custom, undefined)
778
+ if (Object.keys(customValues).length) {
779
+ const resetValues = customValues as Parameters<DataEngine['setCustomFields']>[0]['values']
780
+ await de.setCustomFields({
781
+ entityId: E.directory.organization,
782
+ recordId: before.id,
783
+ tenantId,
784
+ organizationId: before.id,
785
+ values: resetValues,
786
+ notify: false,
787
+ })
788
+ }
789
+ }
790
+ await restoreChildParents(em, tenantId, before.childParents)
791
+ await rebuildHierarchyForTenant(em, tenantId)
792
+ },
793
+ ], { transaction: true })
757
794
  await emitCrudUndoSideEffects({
758
795
  dataEngine: de,
759
796
  action: 'updated',
@@ -1,6 +1,7 @@
1
1
  import { NextResponse } from 'next/server'
2
2
  import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
3
3
  import { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
4
+ import { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'
4
5
  import type { EntityManager } from '@mikro-orm/postgresql'
5
6
  import { InboxDiscrepancy, InboxEmail, InboxProposal, InboxProposalAction } from '../../../../data/entities'
6
7
  import { emitInboxOpsEvent } from '../../../../events'
@@ -48,11 +49,16 @@ export async function POST(req: Request) {
48
49
  return NextResponse.json({ error: 'Email is already queued for processing' }, { status: 409 })
49
50
  }
50
51
 
51
- const retiredCounts = await retireActiveProposalsForEmail(ctx.em, email.id, ctx.userId, ctx.scope)
52
-
53
- email.status = 'received'
54
- email.processingError = null
55
- await ctx.em.flush()
52
+ let retiredCounts = { retiredProposalCount: 0, retiredActionCount: 0 }
53
+ await withAtomicFlush(ctx.em, [
54
+ async () => {
55
+ retiredCounts = await retireActiveProposalsForEmail(ctx.em, email.id, ctx.userId, ctx.scope)
56
+ },
57
+ () => {
58
+ email.status = 'received'
59
+ email.processingError = null
60
+ },
61
+ ], { transaction: true })
56
62
 
57
63
  try {
58
64
  await emitInboxOpsEvent('inbox_ops.email.reprocessed', {
@@ -123,6 +129,21 @@ async function retireActiveProposalsForEmail(
123
129
  throw new ReprocessConflictError('Cannot reprocess after actions were already executed. Open the latest proposal instead.')
124
130
  }
125
131
 
132
+ // Resolve every read before mutating: a query issued between a scalar
133
+ // mutation and the flush can silently reset the Unit of Work (SPEC-018
134
+ // Problem 1). withAtomicFlush flushes once at the end, so ordering is ours.
135
+ const discrepancies = await findWithDecryption(
136
+ em,
137
+ InboxDiscrepancy,
138
+ {
139
+ proposalId: { $in: proposalIds },
140
+ resolved: false,
141
+ deletedAt: null,
142
+ },
143
+ undefined,
144
+ scope,
145
+ )
146
+
126
147
  const now = new Date()
127
148
  const supersededAt = now.toISOString()
128
149
  for (const proposal of proposals) {
@@ -153,23 +174,10 @@ async function retireActiveProposalsForEmail(
153
174
  retiredActionCount += 1
154
175
  }
155
176
 
156
- const discrepancies = await findWithDecryption(
157
- em,
158
- InboxDiscrepancy,
159
- {
160
- proposalId: { $in: proposalIds },
161
- resolved: false,
162
- deletedAt: null,
163
- },
164
- undefined,
165
- scope,
166
- )
167
177
  for (const discrepancy of discrepancies) {
168
178
  discrepancy.resolved = true
169
179
  }
170
180
 
171
- await em.flush()
172
-
173
181
  return {
174
182
  retiredProposalCount: proposals.length,
175
183
  retiredActionCount,