@open-mercato/core 0.6.5-develop.4534.1.b459babe6d → 0.6.5-develop.4544.1.71c003c861

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 (633) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +5 -0
  3. package/dist/generated/entities/role/index.js +3 -1
  4. package/dist/generated/entities/role/index.js.map +2 -2
  5. package/dist/generated/entities/user/index.js +3 -1
  6. package/dist/generated/entities/user/index.js.map +2 -2
  7. package/dist/generated/entity-fields-registry.js +2 -0
  8. package/dist/generated/entity-fields-registry.js.map +2 -2
  9. package/dist/helpers/integration/optimisticLockUi.js +104 -0
  10. package/dist/helpers/integration/optimisticLockUi.js.map +7 -0
  11. package/dist/helpers/integration/salesFixtures.js +17 -0
  12. package/dist/helpers/integration/salesFixtures.js.map +2 -2
  13. package/dist/modules/api_keys/backend/api-keys/page.js +9 -5
  14. package/dist/modules/api_keys/backend/api-keys/page.js.map +2 -2
  15. package/dist/modules/attachments/components/AttachmentPartitionSettings.js +17 -9
  16. package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +2 -2
  17. package/dist/modules/auth/api/roles/acl/route.js +32 -13
  18. package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
  19. package/dist/modules/auth/api/roles/route.js +3 -1
  20. package/dist/modules/auth/api/roles/route.js.map +2 -2
  21. package/dist/modules/auth/api/sidebar/preferences/route.js +71 -3
  22. package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
  23. package/dist/modules/auth/api/users/acl/route.js +42 -19
  24. package/dist/modules/auth/api/users/acl/route.js.map +2 -2
  25. package/dist/modules/auth/api/users/route.js +3 -1
  26. package/dist/modules/auth/api/users/route.js.map +2 -2
  27. package/dist/modules/auth/backend/roles/[id]/edit/page.js +24 -4
  28. package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
  29. package/dist/modules/auth/backend/roles/page.js +8 -4
  30. package/dist/modules/auth/backend/roles/page.js.map +2 -2
  31. package/dist/modules/auth/backend/users/[id]/edit/page.js +27 -5
  32. package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
  33. package/dist/modules/auth/backend/users/page.js +6 -2
  34. package/dist/modules/auth/backend/users/page.js.map +2 -2
  35. package/dist/modules/auth/components/AclEditor.js +3 -1
  36. package/dist/modules/auth/components/AclEditor.js.map +2 -2
  37. package/dist/modules/auth/data/entities.js +6 -0
  38. package/dist/modules/auth/data/entities.js.map +2 -2
  39. package/dist/modules/auth/services/sidebarPreferencesService.js +32 -4
  40. package/dist/modules/auth/services/sidebarPreferencesService.js.map +2 -2
  41. package/dist/modules/business_rules/api/rules/route.js +28 -0
  42. package/dist/modules/business_rules/api/rules/route.js.map +2 -2
  43. package/dist/modules/business_rules/api/sets/route.js +28 -0
  44. package/dist/modules/business_rules/api/sets/route.js.map +2 -2
  45. package/dist/modules/business_rules/backend/rules/[id]/page.js +11 -4
  46. package/dist/modules/business_rules/backend/rules/[id]/page.js.map +3 -3
  47. package/dist/modules/business_rules/backend/rules/page.js +20 -11
  48. package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
  49. package/dist/modules/business_rules/backend/sets/[id]/page.js +11 -4
  50. package/dist/modules/business_rules/backend/sets/[id]/page.js.map +2 -2
  51. package/dist/modules/business_rules/backend/sets/page.js +20 -11
  52. package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
  53. package/dist/modules/catalog/api/categories/route.js +2 -0
  54. package/dist/modules/catalog/api/categories/route.js.map +2 -2
  55. package/dist/modules/catalog/api/products/route.js +2 -1
  56. package/dist/modules/catalog/api/products/route.js.map +2 -2
  57. package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js +2 -0
  58. package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js.map +2 -2
  59. package/dist/modules/catalog/backend/catalog/products/[id]/page.js +94 -40
  60. package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
  61. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +37 -8
  62. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
  63. package/dist/modules/catalog/backend/catalog/products/optionSchemaClient.js.map +2 -2
  64. package/dist/modules/catalog/commands/variants.js +32 -31
  65. package/dist/modules/catalog/commands/variants.js.map +2 -2
  66. package/dist/modules/catalog/components/PriceKindSettings.js +12 -5
  67. package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
  68. package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
  69. package/dist/modules/catalog/components/products/ProductMediaManager.js.map +2 -2
  70. package/dist/modules/catalog/components/products/ProductsDataTable.js +5 -3
  71. package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
  72. package/dist/modules/catalog/components/products/productForm.js.map +2 -2
  73. package/dist/modules/catalog/components/products/variantForm.js +2 -1
  74. package/dist/modules/catalog/components/products/variantForm.js.map +2 -2
  75. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js +5 -0
  76. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +2 -2
  77. package/dist/modules/currencies/backend/currencies/[id]/page.js +6 -3
  78. package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
  79. package/dist/modules/currencies/backend/currencies/page.js +18 -11
  80. package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
  81. package/dist/modules/currencies/backend/exchange-rates/[id]/page.js +1 -0
  82. package/dist/modules/currencies/backend/exchange-rates/[id]/page.js.map +2 -2
  83. package/dist/modules/currencies/backend/exchange-rates/page.js +10 -6
  84. package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
  85. package/dist/modules/currencies/commands/currencies.js +7 -5
  86. package/dist/modules/currencies/commands/currencies.js.map +2 -2
  87. package/dist/modules/currencies/components/CurrencyFetchingConfig.js +26 -19
  88. package/dist/modules/currencies/components/CurrencyFetchingConfig.js.map +2 -2
  89. package/dist/modules/customer_accounts/api/admin/roles/[id].js +28 -5
  90. package/dist/modules/customer_accounts/api/admin/roles/[id].js.map +2 -2
  91. package/dist/modules/customer_accounts/api/admin/roles.js +4 -2
  92. package/dist/modules/customer_accounts/api/admin/roles.js.map +2 -2
  93. package/dist/modules/customer_accounts/api/admin/users/[id].js +28 -5
  94. package/dist/modules/customer_accounts/api/admin/users/[id].js.map +2 -2
  95. package/dist/modules/customer_accounts/api/admin/users.js +2 -0
  96. package/dist/modules/customer_accounts/api/admin/users.js.map +2 -2
  97. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js +16 -8
  98. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js.map +2 -2
  99. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +8 -4
  100. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +2 -2
  101. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js +8 -4
  102. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js.map +2 -2
  103. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js +29 -18
  104. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js.map +2 -2
  105. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js +18 -11
  106. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js.map +2 -2
  107. package/dist/modules/customers/api/companies/route.js +13 -2
  108. package/dist/modules/customers/api/companies/route.js.map +2 -2
  109. package/dist/modules/customers/api/deals/route.js +2 -0
  110. package/dist/modules/customers/api/deals/route.js.map +2 -2
  111. package/dist/modules/customers/api/people/route.js +11 -2
  112. package/dist/modules/customers/api/people/route.js.map +2 -2
  113. package/dist/modules/customers/api/todos/route.js +1 -0
  114. package/dist/modules/customers/api/todos/route.js.map +2 -2
  115. package/dist/modules/customers/backend/config/customers/deals/page.js.map +2 -2
  116. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js +34 -21
  117. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js.map +2 -2
  118. package/dist/modules/customers/backend/customers/companies/[id]/page.js +45 -27
  119. package/dist/modules/customers/backend/customers/companies/[id]/page.js.map +2 -2
  120. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  121. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +22 -5
  122. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
  123. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealFormHandlers.js +30 -8
  124. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealFormHandlers.js.map +2 -2
  125. package/dist/modules/customers/backend/customers/deals/[id]/page.js +1 -0
  126. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  127. package/dist/modules/customers/backend/customers/deals/page.js +16 -6
  128. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  129. package/dist/modules/customers/backend/customers/deals/pipeline/page.js +62 -39
  130. package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
  131. package/dist/modules/customers/backend/customers/people/[id]/page.js +41 -26
  132. package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
  133. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  134. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +50 -23
  135. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  136. package/dist/modules/customers/commands/addresses.js +16 -14
  137. package/dist/modules/customers/commands/addresses.js.map +2 -2
  138. package/dist/modules/customers/commands/companies.js +1 -1
  139. package/dist/modules/customers/commands/companies.js.map +2 -2
  140. package/dist/modules/customers/commands/interactions.js +41 -4
  141. package/dist/modules/customers/commands/interactions.js.map +2 -2
  142. package/dist/modules/customers/commands/people.js +1 -1
  143. package/dist/modules/customers/commands/people.js.map +2 -2
  144. package/dist/modules/customers/commands/personCompanyLinks.js +8 -5
  145. package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
  146. package/dist/modules/customers/commands/pipeline-stages.js +13 -11
  147. package/dist/modules/customers/commands/pipeline-stages.js.map +3 -3
  148. package/dist/modules/customers/components/AddressFormatSettings.js.map +2 -2
  149. package/dist/modules/customers/components/DictionarySettings.js +20 -13
  150. package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
  151. package/dist/modules/customers/components/DictionarySortSettings.js +4 -0
  152. package/dist/modules/customers/components/DictionarySortSettings.js.map +2 -2
  153. package/dist/modules/customers/components/PipelineSettings.js +38 -23
  154. package/dist/modules/customers/components/PipelineSettings.js.map +2 -2
  155. package/dist/modules/customers/components/detail/ActivityTimeline.js +1 -1
  156. package/dist/modules/customers/components/detail/ActivityTimeline.js.map +2 -2
  157. package/dist/modules/customers/components/detail/AddressesSection.js +4 -0
  158. package/dist/modules/customers/components/detail/AddressesSection.js.map +2 -2
  159. package/dist/modules/customers/components/detail/CompanyPeopleSection.js +28 -22
  160. package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
  161. package/dist/modules/customers/components/detail/DealsSection.js +36 -24
  162. package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
  163. package/dist/modules/customers/components/detail/EmailCardActions.js +5 -0
  164. package/dist/modules/customers/components/detail/EmailCardActions.js.map +2 -2
  165. package/dist/modules/customers/components/detail/EntityTagsDialog.js +7 -0
  166. package/dist/modules/customers/components/detail/EntityTagsDialog.js.map +2 -2
  167. package/dist/modules/customers/components/detail/ManageTagsDialog.js +34 -22
  168. package/dist/modules/customers/components/detail/ManageTagsDialog.js.map +2 -2
  169. package/dist/modules/customers/components/detail/PersonCompaniesSection.js +41 -29
  170. package/dist/modules/customers/components/detail/PersonCompaniesSection.js.map +2 -2
  171. package/dist/modules/customers/components/detail/RoleAssignmentRow.js +14 -8
  172. package/dist/modules/customers/components/detail/RoleAssignmentRow.js.map +2 -2
  173. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +14 -6
  174. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  175. package/dist/modules/customers/components/detail/hooks/useInteractionMutations.js +29 -13
  176. package/dist/modules/customers/components/detail/hooks/useInteractionMutations.js.map +2 -2
  177. package/dist/modules/customers/components/detail/hooks/useInteractions.js +77 -35
  178. package/dist/modules/customers/components/detail/hooks/useInteractions.js.map +2 -2
  179. package/dist/modules/customers/components/detail/hooks/usePersonTasks.js +25 -17
  180. package/dist/modules/customers/components/detail/hooks/usePersonTasks.js.map +2 -2
  181. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
  182. package/dist/modules/customers/components/formConfig.js.map +2 -2
  183. package/dist/modules/customers/data/guards.js +66 -0
  184. package/dist/modules/customers/data/guards.js.map +7 -0
  185. package/dist/modules/customers/di.js +37 -0
  186. package/dist/modules/customers/di.js.map +2 -2
  187. package/dist/modules/customers/lib/todoCompatibility.js +11 -0
  188. package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
  189. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +2 -2
  190. package/dist/modules/data_sync/api/options.js +4 -4
  191. package/dist/modules/data_sync/api/options.js.map +2 -2
  192. package/dist/modules/data_sync/api/schedules/route.js +9 -1
  193. package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
  194. package/dist/modules/data_sync/backend/data-sync/page.js +17 -8
  195. package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
  196. package/dist/modules/data_sync/components/IntegrationScheduleTab.js +43 -22
  197. package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +2 -2
  198. package/dist/modules/data_sync/lib/sync-schedule-service.js +9 -0
  199. package/dist/modules/data_sync/lib/sync-schedule-service.js.map +2 -2
  200. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +8 -1
  201. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
  202. package/dist/modules/dictionaries/api/[dictionaryId]/route.js +17 -1
  203. package/dist/modules/dictionaries/api/[dictionaryId]/route.js.map +2 -2
  204. package/dist/modules/dictionaries/components/DictionariesManager.js +31 -10
  205. package/dist/modules/dictionaries/components/DictionariesManager.js.map +2 -2
  206. package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js +28 -15
  207. package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js.map +2 -2
  208. package/dist/modules/directory/api/organizations/route.js +3 -0
  209. package/dist/modules/directory/api/organizations/route.js.map +2 -2
  210. package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js +2 -0
  211. package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js.map +2 -2
  212. package/dist/modules/directory/backend/directory/organizations/page.js +9 -5
  213. package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
  214. package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js +7 -3
  215. package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js.map +2 -2
  216. package/dist/modules/directory/backend/directory/tenants/page.js +8 -4
  217. package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
  218. package/dist/modules/directory/commands/organizations.js +7 -2
  219. package/dist/modules/directory/commands/organizations.js.map +2 -2
  220. package/dist/modules/entities/api/records.js +66 -0
  221. package/dist/modules/entities/api/records.js.map +2 -2
  222. package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js +1 -0
  223. package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js.map +2 -2
  224. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +8 -4
  225. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  226. package/dist/modules/entities/lib/helpers.js +17 -0
  227. package/dist/modules/entities/lib/helpers.js.map +2 -2
  228. package/dist/modules/feature_toggles/api/global/[id]/override/route.js +2 -1
  229. package/dist/modules/feature_toggles/api/global/[id]/override/route.js.map +2 -2
  230. package/dist/modules/feature_toggles/api/overrides/route.js +15 -0
  231. package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
  232. package/dist/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.js +15 -14
  233. package/dist/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.js.map +2 -2
  234. package/dist/modules/feature_toggles/components/FeatureToggleOverrideCard.js +20 -12
  235. package/dist/modules/feature_toggles/components/FeatureToggleOverrideCard.js.map +2 -2
  236. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +6 -2
  237. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
  238. package/dist/modules/feature_toggles/components/formConfig.js +2 -1
  239. package/dist/modules/feature_toggles/components/formConfig.js.map +2 -2
  240. package/dist/modules/feature_toggles/components/overrideFormConfig.js +5 -1
  241. package/dist/modules/feature_toggles/components/overrideFormConfig.js.map +2 -2
  242. package/dist/modules/feature_toggles/data/validators.js +7 -4
  243. package/dist/modules/feature_toggles/data/validators.js.map +2 -2
  244. package/dist/modules/inbox_ops/api/settings/route.js +17 -2
  245. package/dist/modules/inbox_ops/api/settings/route.js.map +2 -2
  246. package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.js +13 -8
  247. package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.js.map +2 -2
  248. package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js +9 -4
  249. package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js.map +2 -2
  250. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +18 -11
  251. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
  252. package/dist/modules/integrations/backend/integrations/page.js +12 -8
  253. package/dist/modules/integrations/backend/integrations/page.js.map +2 -2
  254. package/dist/modules/messages/commands/messages.js +13 -10
  255. package/dist/modules/messages/commands/messages.js.map +2 -2
  256. package/dist/modules/perspectives/api/[tableId]/route.js +39 -30
  257. package/dist/modules/perspectives/api/[tableId]/route.js.map +2 -2
  258. package/dist/modules/perspectives/services/perspectiveService.js +7 -0
  259. package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
  260. package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js +6 -14
  261. package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js.map +3 -3
  262. package/dist/modules/planner/backend/planner/availability-rulesets/page.js +4 -2
  263. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  264. package/dist/modules/planner/components/AvailabilityRuleSetForm.js +2 -0
  265. package/dist/modules/planner/components/AvailabilityRuleSetForm.js.map +2 -2
  266. package/dist/modules/planner/components/AvailabilityRulesEditor.js +36 -11
  267. package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
  268. package/dist/modules/planner/components/AvailabilitySchedule.js +9 -5
  269. package/dist/modules/planner/components/AvailabilitySchedule.js.map +2 -2
  270. package/dist/modules/query_index/lib/engine.js +19 -0
  271. package/dist/modules/query_index/lib/engine.js.map +2 -2
  272. package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js +1 -0
  273. package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js.map +2 -2
  274. package/dist/modules/resources/backend/resources/resource-types/page.js +4 -2
  275. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  276. package/dist/modules/resources/backend/resources/resources/[id]/page.js +14 -3
  277. package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
  278. package/dist/modules/resources/backend/resources/resources/page.js +8 -4
  279. package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
  280. package/dist/modules/resources/components/ResourceCrudForm.js +2 -0
  281. package/dist/modules/resources/components/ResourceCrudForm.js.map +2 -2
  282. package/dist/modules/resources/components/ResourceTypeCrudForm.js +1 -0
  283. package/dist/modules/resources/components/ResourceTypeCrudForm.js.map +2 -2
  284. package/dist/modules/sales/api/documents/factory.js +7 -2
  285. package/dist/modules/sales/api/documents/factory.js.map +2 -2
  286. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js +3 -1
  287. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js.map +2 -2
  288. package/dist/modules/sales/backend/sales/channels/offers/page.js +13 -4
  289. package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
  290. package/dist/modules/sales/backend/sales/channels/page.js +16 -4
  291. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  292. package/dist/modules/sales/backend/sales/documents/[id]/page.js +68 -22
  293. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  294. package/dist/modules/sales/backend/sales/documents/create/page.js.map +2 -2
  295. package/dist/modules/sales/commands/documentAddresses.js +181 -2
  296. package/dist/modules/sales/commands/documentAddresses.js.map +2 -2
  297. package/dist/modules/sales/commands/documents.js +29 -1
  298. package/dist/modules/sales/commands/documents.js.map +2 -2
  299. package/dist/modules/sales/commands/returns.js +12 -2
  300. package/dist/modules/sales/commands/returns.js.map +2 -2
  301. package/dist/modules/sales/commands/shared.js +15 -0
  302. package/dist/modules/sales/commands/shared.js.map +2 -2
  303. package/dist/modules/sales/commands/shipments.js +4 -1
  304. package/dist/modules/sales/commands/shipments.js.map +2 -2
  305. package/dist/modules/sales/components/AdjustmentKindSettings.js +19 -11
  306. package/dist/modules/sales/components/AdjustmentKindSettings.js.map +2 -2
  307. package/dist/modules/sales/components/DocumentNumberSettings.js.map +2 -2
  308. package/dist/modules/sales/components/OrderEditingSettings.js.map +2 -2
  309. package/dist/modules/sales/components/PaymentMethodsSettings.js +12 -4
  310. package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
  311. package/dist/modules/sales/components/ShippingMethodsSettings.js +12 -4
  312. package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
  313. package/dist/modules/sales/components/StatusSettings.js +18 -11
  314. package/dist/modules/sales/components/StatusSettings.js.map +2 -2
  315. package/dist/modules/sales/components/TaxRatesSettings.js +12 -4
  316. package/dist/modules/sales/components/TaxRatesSettings.js.map +2 -2
  317. package/dist/modules/sales/components/channels/ChannelOfferForm.js +47 -16
  318. package/dist/modules/sales/components/channels/ChannelOfferForm.js.map +2 -2
  319. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +8 -4
  320. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
  321. package/dist/modules/sales/components/documents/AddressesSection.js +44 -25
  322. package/dist/modules/sales/components/documents/AddressesSection.js.map +2 -2
  323. package/dist/modules/sales/components/documents/AdjustmentsSection.js +43 -23
  324. package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
  325. package/dist/modules/sales/components/documents/ItemsSection.js +22 -13
  326. package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
  327. package/dist/modules/sales/components/documents/LineItemDialog.js +23 -10
  328. package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
  329. package/dist/modules/sales/components/documents/PaymentDialog.js +29 -14
  330. package/dist/modules/sales/components/documents/PaymentDialog.js.map +2 -2
  331. package/dist/modules/sales/components/documents/PaymentsSection.js +20 -10
  332. package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
  333. package/dist/modules/sales/components/documents/ReturnDialog.js +26 -17
  334. package/dist/modules/sales/components/documents/ReturnDialog.js.map +2 -2
  335. package/dist/modules/sales/components/documents/ReturnsSection.js +3 -1
  336. package/dist/modules/sales/components/documents/ReturnsSection.js.map +2 -2
  337. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +10 -5
  338. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  339. package/dist/modules/sales/components/documents/ShipmentDialog.js +21 -7
  340. package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
  341. package/dist/modules/sales/components/documents/ShipmentsSection.js +19 -10
  342. package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
  343. package/dist/modules/sales/components/documents/optimisticLock.js +27 -0
  344. package/dist/modules/sales/components/documents/optimisticLock.js.map +7 -0
  345. package/dist/modules/sales/di.js +18 -0
  346. package/dist/modules/sales/di.js.map +2 -2
  347. package/dist/modules/staff/api/job-histories.js +11 -2
  348. package/dist/modules/staff/api/job-histories.js.map +2 -2
  349. package/dist/modules/staff/api/timesheets/time-entries/route.js +11 -4
  350. package/dist/modules/staff/api/timesheets/time-entries/route.js.map +2 -2
  351. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +13 -8
  352. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
  353. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js +2 -1
  354. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js.map +2 -2
  355. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +7 -4
  356. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  357. package/dist/modules/staff/backend/staff/team-members/page.js +4 -2
  358. package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
  359. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js +1 -0
  360. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js.map +2 -2
  361. package/dist/modules/staff/backend/staff/team-roles/page.js +4 -2
  362. package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
  363. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +5 -2
  364. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  365. package/dist/modules/staff/backend/staff/teams/page.js +12 -3
  366. package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
  367. package/dist/modules/staff/backend/staff/timesheets/page.js +4 -1
  368. package/dist/modules/staff/backend/staff/timesheets/page.js.map +2 -2
  369. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js.map +2 -2
  370. package/dist/modules/staff/backend/staff/timesheets/projects/page.js +12 -3
  371. package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
  372. package/dist/modules/staff/commands/job-histories.js +40 -3
  373. package/dist/modules/staff/commands/job-histories.js.map +2 -2
  374. package/dist/modules/staff/components/LeaveRequestForm.js +1 -0
  375. package/dist/modules/staff/components/LeaveRequestForm.js.map +2 -2
  376. package/dist/modules/staff/components/TeamForm.js +1 -0
  377. package/dist/modules/staff/components/TeamForm.js.map +2 -2
  378. package/dist/modules/staff/components/TeamMemberForm.js +1 -0
  379. package/dist/modules/staff/components/TeamMemberForm.js.map +2 -2
  380. package/dist/modules/staff/components/TeamRoleForm.js +1 -0
  381. package/dist/modules/staff/components/TeamRoleForm.js.map +2 -2
  382. package/dist/modules/staff/components/detail/JobHistorySection.js +20 -7
  383. package/dist/modules/staff/components/detail/JobHistorySection.js.map +2 -2
  384. package/dist/modules/staff/data/validators.js +7 -1
  385. package/dist/modules/staff/data/validators.js.map +2 -2
  386. package/dist/modules/staff/lib/leaveRequestHelpers.js +2 -1
  387. package/dist/modules/staff/lib/leaveRequestHelpers.js.map +2 -2
  388. package/dist/modules/translations/components/TranslationManager.js +12 -8
  389. package/dist/modules/translations/components/TranslationManager.js.map +2 -2
  390. package/dist/modules/workflows/api/definitions/[id]/route.js +106 -0
  391. package/dist/modules/workflows/api/definitions/[id]/route.js.map +2 -2
  392. package/dist/modules/workflows/backend/definitions/[id]/page.js +11 -3
  393. package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
  394. package/dist/modules/workflows/backend/definitions/page.js +19 -8
  395. package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
  396. package/dist/modules/workflows/backend/definitions/visual-editor/page.js +29 -16
  397. package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
  398. package/dist/modules/workflows/components/formConfig.js +4 -1
  399. package/dist/modules/workflows/components/formConfig.js.map +2 -2
  400. package/dist/modules/workflows/di.js +12 -0
  401. package/dist/modules/workflows/di.js.map +2 -2
  402. package/generated/entities/role/index.ts +1 -0
  403. package/generated/entities/user/index.ts +1 -0
  404. package/generated/entity-fields-registry.ts +2 -0
  405. package/jest.setup.ts +17 -0
  406. package/package.json +8 -7
  407. package/src/helpers/integration/optimisticLockUi.ts +172 -0
  408. package/src/helpers/integration/salesFixtures.ts +29 -0
  409. package/src/modules/api_keys/backend/api-keys/page.tsx +10 -5
  410. package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +19 -9
  411. package/src/modules/auth/api/roles/acl/route.ts +37 -11
  412. package/src/modules/auth/api/roles/route.ts +2 -0
  413. package/src/modules/auth/api/sidebar/preferences/route.ts +73 -0
  414. package/src/modules/auth/api/users/acl/route.ts +46 -18
  415. package/src/modules/auth/api/users/route.ts +2 -0
  416. package/src/modules/auth/backend/roles/[id]/edit/page.tsx +29 -4
  417. package/src/modules/auth/backend/roles/page.tsx +9 -4
  418. package/src/modules/auth/backend/users/[id]/edit/page.tsx +37 -4
  419. package/src/modules/auth/backend/users/page.tsx +7 -2
  420. package/src/modules/auth/components/AclEditor.tsx +10 -1
  421. package/src/modules/auth/data/entities.ts +7 -1
  422. package/src/modules/auth/services/sidebarPreferencesService.ts +38 -4
  423. package/src/modules/business_rules/api/rules/route.ts +30 -0
  424. package/src/modules/business_rules/api/sets/route.ts +30 -0
  425. package/src/modules/business_rules/backend/rules/[id]/page.tsx +16 -4
  426. package/src/modules/business_rules/backend/rules/page.tsx +20 -11
  427. package/src/modules/business_rules/backend/sets/[id]/page.tsx +16 -4
  428. package/src/modules/business_rules/backend/sets/page.tsx +20 -11
  429. package/src/modules/catalog/api/categories/route.ts +3 -0
  430. package/src/modules/catalog/api/products/route.ts +4 -0
  431. package/src/modules/catalog/backend/catalog/categories/[id]/edit/page.tsx +5 -0
  432. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +112 -35
  433. package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +56 -7
  434. package/src/modules/catalog/backend/catalog/products/optionSchemaClient.ts +2 -0
  435. package/src/modules/catalog/commands/variants.ts +32 -32
  436. package/src/modules/catalog/components/PriceKindSettings.tsx +20 -7
  437. package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +1 -0
  438. package/src/modules/catalog/components/products/ProductMediaManager.tsx +2 -0
  439. package/src/modules/catalog/components/products/ProductsDataTable.tsx +8 -4
  440. package/src/modules/catalog/components/products/productForm.ts +3 -0
  441. package/src/modules/catalog/components/products/variantForm.ts +9 -0
  442. package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +5 -0
  443. package/src/modules/currencies/backend/currencies/[id]/page.tsx +13 -6
  444. package/src/modules/currencies/backend/currencies/page.tsx +18 -11
  445. package/src/modules/currencies/backend/exchange-rates/[id]/page.tsx +3 -0
  446. package/src/modules/currencies/backend/exchange-rates/page.tsx +10 -6
  447. package/src/modules/currencies/commands/currencies.ts +10 -5
  448. package/src/modules/currencies/components/CurrencyFetchingConfig.tsx +31 -21
  449. package/src/modules/customer_accounts/api/admin/roles/[id].ts +35 -5
  450. package/src/modules/customer_accounts/api/admin/roles.ts +2 -0
  451. package/src/modules/customer_accounts/api/admin/users/[id].ts +38 -5
  452. package/src/modules/customer_accounts/api/admin/users.ts +2 -0
  453. package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +34 -20
  454. package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +9 -4
  455. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/page.tsx +11 -4
  456. package/src/modules/customer_accounts/backend/customer_accounts/users/[id]/page.tsx +28 -17
  457. package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +19 -11
  458. package/src/modules/customers/AGENTS.md +2 -2
  459. package/src/modules/customers/api/companies/route.ts +14 -1
  460. package/src/modules/customers/api/deals/route.ts +3 -0
  461. package/src/modules/customers/api/people/route.ts +12 -1
  462. package/src/modules/customers/api/todos/route.ts +1 -0
  463. package/src/modules/customers/backend/config/customers/deals/page.tsx +1 -0
  464. package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +36 -21
  465. package/src/modules/customers/backend/customers/companies/[id]/page.tsx +52 -27
  466. package/src/modules/customers/backend/customers/companies/page.tsx +2 -0
  467. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +27 -5
  468. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealFormHandlers.ts +39 -7
  469. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +1 -0
  470. package/src/modules/customers/backend/customers/deals/page.tsx +18 -6
  471. package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +64 -39
  472. package/src/modules/customers/backend/customers/people/[id]/page.tsx +46 -26
  473. package/src/modules/customers/backend/customers/people/page.tsx +2 -0
  474. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +84 -24
  475. package/src/modules/customers/commands/addresses.ts +16 -14
  476. package/src/modules/customers/commands/companies.ts +3 -1
  477. package/src/modules/customers/commands/interactions.ts +50 -4
  478. package/src/modules/customers/commands/people.ts +2 -1
  479. package/src/modules/customers/commands/personCompanyLinks.ts +8 -5
  480. package/src/modules/customers/commands/pipeline-stages.ts +16 -16
  481. package/src/modules/customers/components/AddressFormatSettings.tsx +1 -0
  482. package/src/modules/customers/components/DictionarySettings.tsx +18 -13
  483. package/src/modules/customers/components/DictionarySortSettings.tsx +4 -0
  484. package/src/modules/customers/components/PipelineSettings.tsx +42 -21
  485. package/src/modules/customers/components/detail/ActivityTimeline.tsx +3 -3
  486. package/src/modules/customers/components/detail/AddressesSection.tsx +4 -0
  487. package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +2 -0
  488. package/src/modules/customers/components/detail/DealsSection.tsx +4 -0
  489. package/src/modules/customers/components/detail/EmailCardActions.tsx +5 -0
  490. package/src/modules/customers/components/detail/EntityTagsDialog.tsx +7 -0
  491. package/src/modules/customers/components/detail/ManageTagsDialog.tsx +4 -0
  492. package/src/modules/customers/components/detail/PersonCompaniesSection.tsx +4 -0
  493. package/src/modules/customers/components/detail/RoleAssignmentRow.tsx +2 -0
  494. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +23 -7
  495. package/src/modules/customers/components/detail/hooks/useInteractionMutations.ts +25 -15
  496. package/src/modules/customers/components/detail/hooks/useInteractions.ts +76 -35
  497. package/src/modules/customers/components/detail/hooks/usePersonTasks.ts +30 -17
  498. package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +2 -0
  499. package/src/modules/customers/components/detail/types.ts +1 -0
  500. package/src/modules/customers/components/formConfig.tsx +2 -0
  501. package/src/modules/customers/data/guards.ts +67 -0
  502. package/src/modules/customers/di.ts +66 -0
  503. package/src/modules/customers/i18n/de.json +2 -0
  504. package/src/modules/customers/i18n/en.json +2 -0
  505. package/src/modules/customers/i18n/es.json +2 -0
  506. package/src/modules/customers/i18n/pl.json +2 -0
  507. package/src/modules/customers/lib/todoCompatibility.ts +14 -0
  508. package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +2 -0
  509. package/src/modules/data_sync/api/options.ts +7 -4
  510. package/src/modules/data_sync/api/schedules/route.ts +9 -1
  511. package/src/modules/data_sync/backend/data-sync/page.tsx +18 -5
  512. package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +46 -19
  513. package/src/modules/data_sync/lib/sync-schedule-service.ts +11 -0
  514. package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +8 -1
  515. package/src/modules/dictionaries/api/[dictionaryId]/route.ts +23 -0
  516. package/src/modules/dictionaries/components/DictionariesManager.tsx +32 -9
  517. package/src/modules/dictionaries/components/DictionaryEntriesEditor.tsx +30 -14
  518. package/src/modules/dictionaries/i18n/de.json +1 -0
  519. package/src/modules/dictionaries/i18n/en.json +1 -0
  520. package/src/modules/dictionaries/i18n/es.json +1 -0
  521. package/src/modules/dictionaries/i18n/pl.json +1 -0
  522. package/src/modules/directory/api/organizations/route.ts +3 -0
  523. package/src/modules/directory/backend/directory/organizations/[id]/edit/page.tsx +8 -0
  524. package/src/modules/directory/backend/directory/organizations/page.tsx +10 -5
  525. package/src/modules/directory/backend/directory/tenants/[id]/edit/page.tsx +16 -5
  526. package/src/modules/directory/backend/directory/tenants/page.tsx +8 -4
  527. package/src/modules/directory/commands/organizations.ts +7 -4
  528. package/src/modules/entities/api/records.ts +99 -0
  529. package/src/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.tsx +7 -0
  530. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +8 -4
  531. package/src/modules/entities/lib/helpers.ts +17 -0
  532. package/src/modules/feature_toggles/api/global/[id]/override/route.ts +1 -0
  533. package/src/modules/feature_toggles/api/overrides/route.ts +19 -0
  534. package/src/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.tsx +19 -13
  535. package/src/modules/feature_toggles/components/FeatureToggleOverrideCard.tsx +22 -12
  536. package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +7 -2
  537. package/src/modules/feature_toggles/components/formConfig.tsx +2 -1
  538. package/src/modules/feature_toggles/components/overrideFormConfig.tsx +10 -1
  539. package/src/modules/feature_toggles/data/validators.ts +11 -3
  540. package/src/modules/inbox_ops/api/settings/route.ts +18 -0
  541. package/src/modules/inbox_ops/backend/inbox-ops/settings/page.tsx +15 -10
  542. package/src/modules/inbox_ops/components/proposals/EditActionDialog.tsx +9 -4
  543. package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +20 -11
  544. package/src/modules/integrations/backend/integrations/page.tsx +13 -8
  545. package/src/modules/messages/commands/messages.ts +27 -15
  546. package/src/modules/perspectives/api/[tableId]/route.ts +11 -2
  547. package/src/modules/perspectives/services/perspectiveService.ts +13 -1
  548. package/src/modules/planner/backend/planner/availability-rulesets/[id]/page.tsx +16 -14
  549. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +6 -3
  550. package/src/modules/planner/components/AvailabilityRuleSetForm.tsx +3 -0
  551. package/src/modules/planner/components/AvailabilityRulesEditor.tsx +58 -15
  552. package/src/modules/planner/components/AvailabilitySchedule.tsx +22 -7
  553. package/src/modules/query_index/lib/engine.ts +34 -0
  554. package/src/modules/resources/backend/resources/resource-types/[id]/edit/page.tsx +7 -1
  555. package/src/modules/resources/backend/resources/resource-types/page.tsx +6 -3
  556. package/src/modules/resources/backend/resources/resources/[id]/page.tsx +23 -3
  557. package/src/modules/resources/backend/resources/resources/page.tsx +15 -4
  558. package/src/modules/resources/components/ResourceCrudForm.tsx +3 -0
  559. package/src/modules/resources/components/ResourceTypeCrudForm.tsx +2 -0
  560. package/src/modules/sales/api/documents/factory.ts +13 -1
  561. package/src/modules/sales/backend/sales/channels/[channelId]/edit/page.tsx +6 -0
  562. package/src/modules/sales/backend/sales/channels/offers/page.tsx +10 -4
  563. package/src/modules/sales/backend/sales/channels/page.tsx +19 -4
  564. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +73 -20
  565. package/src/modules/sales/backend/sales/documents/create/page.tsx +2 -0
  566. package/src/modules/sales/commands/documentAddresses.ts +226 -4
  567. package/src/modules/sales/commands/documents.ts +28 -0
  568. package/src/modules/sales/commands/returns.ts +12 -3
  569. package/src/modules/sales/commands/shared.ts +36 -0
  570. package/src/modules/sales/commands/shipments.ts +17 -1
  571. package/src/modules/sales/components/AdjustmentKindSettings.tsx +20 -11
  572. package/src/modules/sales/components/DocumentNumberSettings.tsx +1 -0
  573. package/src/modules/sales/components/OrderEditingSettings.tsx +1 -0
  574. package/src/modules/sales/components/PaymentMethodsSettings.tsx +12 -4
  575. package/src/modules/sales/components/ShippingMethodsSettings.tsx +12 -4
  576. package/src/modules/sales/components/StatusSettings.tsx +20 -11
  577. package/src/modules/sales/components/TaxRatesSettings.tsx +12 -5
  578. package/src/modules/sales/components/channels/ChannelOfferForm.tsx +67 -14
  579. package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +7 -4
  580. package/src/modules/sales/components/documents/AddressesSection.tsx +35 -25
  581. package/src/modules/sales/components/documents/AdjustmentsSection.tsx +50 -25
  582. package/src/modules/sales/components/documents/ItemsSection.tsx +24 -13
  583. package/src/modules/sales/components/documents/LineItemDialog.tsx +26 -9
  584. package/src/modules/sales/components/documents/PaymentDialog.tsx +33 -14
  585. package/src/modules/sales/components/documents/PaymentsSection.tsx +22 -10
  586. package/src/modules/sales/components/documents/ReturnDialog.tsx +28 -17
  587. package/src/modules/sales/components/documents/ReturnsSection.tsx +4 -1
  588. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +11 -4
  589. package/src/modules/sales/components/documents/ShipmentDialog.tsx +23 -8
  590. package/src/modules/sales/components/documents/ShipmentsSection.tsx +20 -10
  591. package/src/modules/sales/components/documents/optimisticLock.ts +34 -0
  592. package/src/modules/sales/components/documents/shipmentTypes.ts +1 -0
  593. package/src/modules/sales/di.ts +35 -0
  594. package/src/modules/sales/i18n/de.json +3 -0
  595. package/src/modules/sales/i18n/en.json +3 -0
  596. package/src/modules/sales/i18n/es.json +3 -0
  597. package/src/modules/sales/i18n/pl.json +3 -0
  598. package/src/modules/staff/api/job-histories.ts +12 -2
  599. package/src/modules/staff/api/timesheets/time-entries/route.ts +16 -4
  600. package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +12 -7
  601. package/src/modules/staff/backend/staff/my-leave-requests/[id]/page.tsx +2 -0
  602. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +16 -5
  603. package/src/modules/staff/backend/staff/team-members/page.tsx +6 -2
  604. package/src/modules/staff/backend/staff/team-roles/[id]/edit/page.tsx +8 -0
  605. package/src/modules/staff/backend/staff/team-roles/page.tsx +6 -2
  606. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +13 -3
  607. package/src/modules/staff/backend/staff/teams/page.tsx +9 -3
  608. package/src/modules/staff/backend/staff/timesheets/page.tsx +10 -1
  609. package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.tsx +4 -0
  610. package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +9 -3
  611. package/src/modules/staff/commands/job-histories.ts +42 -3
  612. package/src/modules/staff/components/LeaveRequestForm.tsx +2 -0
  613. package/src/modules/staff/components/TeamForm.tsx +2 -0
  614. package/src/modules/staff/components/TeamMemberForm.tsx +2 -0
  615. package/src/modules/staff/components/TeamRoleForm.tsx +2 -0
  616. package/src/modules/staff/components/detail/JobHistorySection.tsx +28 -6
  617. package/src/modules/staff/data/validators.ts +6 -0
  618. package/src/modules/staff/i18n/de.json +1 -0
  619. package/src/modules/staff/i18n/en.json +1 -0
  620. package/src/modules/staff/i18n/es.json +1 -0
  621. package/src/modules/staff/i18n/pl.json +1 -0
  622. package/src/modules/staff/lib/leaveRequestHelpers.ts +4 -0
  623. package/src/modules/translations/components/TranslationManager.tsx +13 -8
  624. package/src/modules/workflows/api/definitions/[id]/route.ts +112 -0
  625. package/src/modules/workflows/backend/definitions/[id]/page.tsx +20 -4
  626. package/src/modules/workflows/backend/definitions/page.tsx +20 -9
  627. package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +29 -16
  628. package/src/modules/workflows/components/formConfig.tsx +5 -0
  629. package/src/modules/workflows/di.ts +20 -0
  630. package/src/modules/workflows/i18n/de.json +1 -0
  631. package/src/modules/workflows/i18n/en.json +1 -0
  632. package/src/modules/workflows/i18n/es.json +1 -0
  633. package/src/modules/workflows/i18n/pl.json +1 -0
@@ -5,8 +5,9 @@ import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
5
5
  import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
6
6
  import { logCrudAccess } from '@open-mercato/shared/lib/crud/factory'
7
7
  import { forbidden, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
8
- import { UserAcl } from '@open-mercato/core/modules/auth/data/entities'
8
+ import { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
9
9
  import { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'
10
+ import { UserAcl } from '@open-mercato/core/modules/auth/data/entities'
10
11
  import { assertActorCanModifySuperAdminUserTarget } from '@open-mercato/core/modules/auth/lib/grantChecks'
11
12
  import type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'
12
13
  import type { EntityManager } from '@mikro-orm/postgresql'
@@ -29,6 +30,7 @@ const userAclResponseSchema = z.object({
29
30
  isSuperAdmin: z.boolean(),
30
31
  features: z.array(z.string()),
31
32
  organizations: z.array(z.string()).nullable(),
33
+ updatedAt: z.string().nullable(),
32
34
  })
33
35
 
34
36
  const userAclUpdateResponseSchema = z.object({
@@ -73,8 +75,9 @@ export async function GET(req: Request) {
73
75
  isSuperAdmin: !!acl.isSuperAdmin,
74
76
  features: Array.isArray(acl.featuresJson) ? acl.featuresJson : [],
75
77
  organizations: Array.isArray(acl.organizationsJson) ? acl.organizationsJson : null,
78
+ updatedAt: acl.updatedAt instanceof Date ? acl.updatedAt.toISOString() : null,
76
79
  }
77
- : { hasCustomAcl: false, isSuperAdmin: false, features: [], organizations: null }
80
+ : { hasCustomAcl: false, isSuperAdmin: false, features: [], organizations: null, updatedAt: null }
78
81
 
79
82
  await logCrudAccess({
80
83
  container,
@@ -128,6 +131,22 @@ export async function PUT(req: Request) {
128
131
  const organizations = Array.isArray(parsed.data.organizations) ? parsed.data.organizations : null
129
132
 
130
133
  let acl = await em.findOne(UserAcl, { user: parsed.data.userId as any, tenantId: auth.tenantId as any })
134
+ // Optimistic lock: refuse a stale per-user ACL overwrite so concurrent edits
135
+ // cannot silently clobber each other (#2055). Strictly additive — a no-op when
136
+ // the client sends no expected-version header; skipped when no ACL row exists.
137
+ if (acl) {
138
+ try {
139
+ enforceCommandOptimisticLock({
140
+ resourceKind: 'auth.user_acl',
141
+ resourceId: acl.id,
142
+ current: acl.updatedAt ?? null,
143
+ request: req,
144
+ })
145
+ } catch (err) {
146
+ if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })
147
+ throw err
148
+ }
149
+ }
131
150
  const existingIsSuperAdmin = acl ? !!acl.isSuperAdmin : false
132
151
  const existingFeatures = acl && Array.isArray(acl.featuresJson) ? normalizeFeatureList(acl.featuresJson) : []
133
152
 
@@ -151,22 +170,31 @@ export async function PUT(req: Request) {
151
170
 
152
171
  const hasCustomAcl = effectiveIsSuperAdmin || effectiveFeatures.length > 0
153
172
 
154
- await withAtomicFlush(em, [
155
- () => {
156
- if (!hasCustomAcl) {
157
- if (acl) em.remove(acl)
158
- } else {
159
- if (!acl) {
160
- acl = em.create(UserAcl, { user: parsed.data.userId as any, tenantId: auth.tenantId as any })
161
- }
162
- const aclRecord = acl as any
163
- aclRecord.isSuperAdmin = effectiveIsSuperAdmin
164
- aclRecord.featuresJson = effectiveFeatures
165
- aclRecord.organizationsJson = organizations
166
- em.persist(acl)
167
- }
168
- },
169
- ], { transaction: true })
173
+ // Persist the ACL mutation inside a transaction so the per-user permission
174
+ // write (or removal) commits atomically (proper ACL-edit transaction handling).
175
+ if (!hasCustomAcl) {
176
+ if (acl) {
177
+ const aclToRemove = acl
178
+ await withAtomicFlush(em, [() => em.remove(aclToRemove)], { transaction: true })
179
+ }
180
+ } else {
181
+ if (!acl) {
182
+ acl = em.create(UserAcl, { user: parsed.data.userId as any, tenantId: auth.tenantId as any })
183
+ }
184
+ const aclRecord = acl as any
185
+ await withAtomicFlush(
186
+ em,
187
+ [
188
+ () => {
189
+ aclRecord.isSuperAdmin = effectiveIsSuperAdmin
190
+ aclRecord.featuresJson = effectiveFeatures
191
+ aclRecord.organizationsJson = organizations
192
+ em.persist(aclRecord)
193
+ },
194
+ ],
195
+ { transaction: true },
196
+ )
197
+ }
170
198
 
171
199
  // Invalidate cache for this user
172
200
  await rbacService.invalidateUserCache(parsed.data.userId)
@@ -80,6 +80,7 @@ const userListItemSchema = z.object({
80
80
  tenantName: z.string().nullable(),
81
81
  roles: z.array(z.string()),
82
82
  roleIds: z.array(z.string().uuid()).optional(),
83
+ updatedAt: z.string().nullable().optional(),
83
84
  })
84
85
 
85
86
  const userListResponseSchema = z.object({
@@ -429,6 +430,7 @@ export async function GET(req: Request) {
429
430
  roles: roleMap[uid] || [],
430
431
  roleIds: roleIdMap[uid] || [],
431
432
  hasPassword: !!u.passwordHash,
433
+ updatedAt: u.updatedAt instanceof Date ? u.updatedAt.toISOString() : null,
432
434
  ...(cfByUser[uid] || {}),
433
435
  }
434
436
  })
@@ -2,7 +2,8 @@
2
2
  import * as React from 'react'
3
3
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
4
4
  import { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'
5
- import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
5
+ import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
6
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
6
7
  import { deleteCrud, updateCrud } from '@open-mercato/ui/backend/utils/crud'
7
8
  import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'
8
9
  import { AclEditor, type AclData } from '@open-mercato/core/modules/auth/components/AclEditor'
@@ -16,6 +17,7 @@ import { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-
16
17
  type EditRoleFormValues = {
17
18
  name?: string
18
19
  tenantId?: string | null
20
+ updatedAt?: string | null
19
21
  } & Record<string, unknown>
20
22
 
21
23
  type RoleRecord = {
@@ -24,6 +26,7 @@ type RoleRecord = {
24
26
  tenantId: string | null
25
27
  tenantName?: string | null
26
28
  usersCount?: number | null
29
+ updatedAt?: string | null
27
30
  } & Record<string, unknown>
28
31
 
29
32
  type RoleListResponse = {
@@ -37,6 +40,7 @@ export default function EditRolePage({ params }: { params?: { id?: string } }) {
37
40
  const [initial, setInitial] = React.useState<RoleRecord | null>(null)
38
41
  const [loading, setLoading] = React.useState(true)
39
42
  const [aclData, setAclData] = React.useState<AclData>({ isSuperAdmin: false, features: [], organizations: null })
43
+ const [aclUpdatedAt, setAclUpdatedAt] = React.useState<string | null>(null)
40
44
  const [actorIsSuperAdmin, setActorIsSuperAdmin] = React.useState(false)
41
45
  const [selectedTenantId, setSelectedTenantId] = React.useState<string | null>(null)
42
46
  const widgetEditorRef = React.useRef<WidgetVisibilityEditorHandle | null>(null)
@@ -158,6 +162,7 @@ export default function EditRolePage({ params }: { params?: { id?: string } }) {
158
162
  canEditOrganizations
159
163
  value={aclData}
160
164
  onChange={setAclData}
165
+ onVersionChange={setAclUpdatedAt}
161
166
  currentUserIsSuperAdmin={actorIsSuperAdmin}
162
167
  tenantId={selectedTenantId ?? null}
163
168
  preserveOnTenantChange
@@ -213,17 +218,37 @@ export default function EditRolePage({ params }: { params?: { id?: string } }) {
213
218
  if (Object.keys(customFields).length) {
214
219
  payload.customFields = customFields
215
220
  }
216
- await updateCrud('auth/roles', payload)
217
- await updateCrud('auth/roles/acl', { roleId: id, tenantId: effectiveTenantId, ...aclData }, {
221
+ const roleOptimisticLockHeader = buildOptimisticLockHeader(initial?.updatedAt)
222
+ if (Object.keys(roleOptimisticLockHeader).length > 0) {
223
+ await withScopedApiRequestHeaders(roleOptimisticLockHeader, () => updateCrud('auth/roles', payload))
224
+ } else {
225
+ await updateCrud('auth/roles', payload)
226
+ }
227
+ // Optimistic lock the ACL save against the loaded RoleAcl version so a
228
+ // concurrent permission edit cannot silently overwrite (#2055). CrudForm
229
+ // surfaces the 409 as the unified conflict bar.
230
+ const aclLockHeader = buildOptimisticLockHeader(aclUpdatedAt)
231
+ const saveRoleAcl = () => updateCrud('auth/roles/acl', { roleId: id, tenantId: effectiveTenantId, ...aclData }, {
218
232
  errorMessage: t('auth.roles.form.errors.aclUpdate', 'Failed to update role access control'),
219
233
  })
234
+ if (Object.keys(aclLockHeader).length > 0) {
235
+ await withScopedApiRequestHeaders(aclLockHeader, saveRoleAcl)
236
+ } else {
237
+ await saveRoleAcl()
238
+ }
220
239
  await widgetEditorRef.current?.save()
221
240
  try { window.dispatchEvent(new Event('om:refresh-sidebar')) } catch {}
222
241
  }}
223
242
  onDelete={async () => {
224
- await deleteCrud('auth/roles', String(id), {
243
+ const roleOptimisticLockHeader = buildOptimisticLockHeader(initial?.updatedAt)
244
+ const deleteRole = () => deleteCrud('auth/roles', String(id), {
225
245
  errorMessage: t('auth.roles.form.errors.delete', 'Failed to delete role'),
226
246
  })
247
+ if (Object.keys(roleOptimisticLockHeader).length > 0) {
248
+ await withScopedApiRequestHeaders(roleOptimisticLockHeader, deleteRole)
249
+ } else {
250
+ await deleteRole()
251
+ }
227
252
  }}
228
253
  deleteRedirect={`/backend/roles?flash=${encodeURIComponent(t('auth.roles.flash.deleted', 'Role deleted'))}&type=success`}
229
254
  />
@@ -6,7 +6,8 @@ import { DataTable } from '@open-mercato/ui/backend/DataTable'
6
6
  import type { ColumnDef, SortingState } from '@tanstack/react-table'
7
7
  import { Button } from '@open-mercato/ui/primitives/button'
8
8
  import { RowActions } from '@open-mercato/ui/backend/RowActions'
9
- import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
9
+ import { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
10
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
10
11
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
11
12
  import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
12
13
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
@@ -20,6 +21,7 @@ type Row = {
20
21
  tenantId?: string | null
21
22
  tenantIds?: string[]
22
23
  tenantName?: string | null
24
+ updatedAt?: string | null
23
25
  }
24
26
 
25
27
  export default function RolesListPage() {
@@ -77,9 +79,12 @@ export default function RolesListPage() {
77
79
  })
78
80
  if (!confirmed) return
79
81
  try {
80
- const call = await apiCall(
81
- `/api/auth/roles?id=${encodeURIComponent(row.id)}`,
82
- { method: 'DELETE' },
82
+ const call = await withScopedApiRequestHeaders(
83
+ buildOptimisticLockHeader(row.updatedAt),
84
+ () => apiCall(
85
+ `/api/auth/roles?id=${encodeURIComponent(row.id)}`,
86
+ { method: 'DELETE' },
87
+ ),
83
88
  )
84
89
  if (!call.ok) {
85
90
  await raiseCrudError(call.response, t('auth.roles.list.error.delete', 'Failed to delete role'))
@@ -3,7 +3,8 @@ import * as React from 'react'
3
3
  import { E } from '#generated/entities.ids.generated'
4
4
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
5
5
  import { CrudForm, type CrudField, type CrudFormGroup, type CrudFieldOption } from '@open-mercato/ui/backend/CrudForm'
6
- import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
6
+ import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
7
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
7
8
  import { deleteCrud, updateCrud } from '@open-mercato/ui/backend/utils/crud'
8
9
  import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'
9
10
  import { AclEditor, type AclData } from '@open-mercato/core/modules/auth/components/AclEditor'
@@ -27,6 +28,7 @@ type EditUserFormValues = {
27
28
  tenantId: string | null
28
29
  organizationId: string | null
29
30
  roles: string[]
31
+ updatedAt?: string | null
30
32
  } & Record<string, unknown>
31
33
 
32
34
  type LoadedUser = {
@@ -40,6 +42,7 @@ type LoadedUser = {
40
42
  roles: string[]
41
43
  roleIds: string[]
42
44
  hasPassword: boolean
45
+ updatedAt: string | null
43
46
  }
44
47
 
45
48
  type UserApiItem = {
@@ -53,6 +56,8 @@ type UserApiItem = {
53
56
  roles?: unknown
54
57
  roleIds?: unknown
55
58
  hasPassword?: boolean
59
+ updatedAt?: string | null
60
+ updated_at?: string | null
56
61
  }
57
62
 
58
63
  type UserListResponse = {
@@ -123,6 +128,7 @@ export default function EditUserPage({ params }: { params?: { id?: string } }) {
123
128
  const [isNotFound, setIsNotFound] = React.useState(false)
124
129
  const [canEditOrgs, setCanEditOrgs] = React.useState(false)
125
130
  const [aclData, setAclData] = React.useState<AclData>({ isSuperAdmin: false, features: [], organizations: null })
131
+ const [aclUpdatedAt, setAclUpdatedAt] = React.useState<string | null>(null)
126
132
  const [customFieldValues, setCustomFieldValues] = React.useState<Record<string, unknown>>({})
127
133
  const [actorIsSuperAdmin, setActorIsSuperAdmin] = React.useState(false)
128
134
  const [actorResolved, setActorResolved] = React.useState(false)
@@ -209,6 +215,11 @@ export default function EditUserPage({ params }: { params?: { id?: string } }) {
209
215
  roles: roleNames,
210
216
  roleIds: roleIds.length > 0 ? roleIds : roleNames,
211
217
  hasPassword: item.hasPassword !== false,
218
+ updatedAt: typeof item.updatedAt === 'string'
219
+ ? item.updatedAt
220
+ : typeof item.updated_at === 'string'
221
+ ? item.updated_at
222
+ : null,
212
223
  })
213
224
  setSelectedTenantId(item.tenantId ? String(item.tenantId) : null)
214
225
  const custom = extractCustomFieldEntries(item as Record<string, unknown>)
@@ -353,6 +364,7 @@ export default function EditUserPage({ params }: { params?: { id?: string } }) {
353
364
  canEditOrganizations={canEditOrgs}
354
365
  value={aclData}
355
366
  onChange={setAclData}
367
+ onVersionChange={setAclUpdatedAt}
356
368
  userRoles={initialUser?.roles || []}
357
369
  currentUserIsSuperAdmin={actorIsSuperAdmin}
358
370
  tenantId={selectedTenantId ?? null}
@@ -393,6 +405,7 @@ export default function EditUserPage({ params }: { params?: { id?: string } }) {
393
405
  tenantId: initialUser.tenantId,
394
406
  organizationId: initialUser.organizationId,
395
407
  roles: initialUser.roleIds,
408
+ updatedAt: initialUser.updatedAt,
396
409
  ...customFieldValues,
397
410
  }
398
411
  }
@@ -471,17 +484,37 @@ export default function EditUserPage({ params }: { params?: { id?: string } }) {
471
484
  roles: Array.isArray(values.roles) ? values.roles : [],
472
485
  ...(Object.keys(customFields).length ? { customFields } : {}),
473
486
  }
474
- await updateCrud('auth/users', payload)
475
- await updateCrud('auth/users/acl', { userId: id, ...aclData }, {
487
+ const userOptimisticLockHeader = buildOptimisticLockHeader(initialUser?.updatedAt)
488
+ if (Object.keys(userOptimisticLockHeader).length > 0) {
489
+ await withScopedApiRequestHeaders(userOptimisticLockHeader, () => updateCrud('auth/users', payload))
490
+ } else {
491
+ await updateCrud('auth/users', payload)
492
+ }
493
+ // Optimistic lock the ACL save against the loaded UserAcl version so a
494
+ // concurrent permission edit cannot silently overwrite (#2055). CrudForm
495
+ // surfaces the 409 as the unified conflict bar.
496
+ const aclLockHeader = buildOptimisticLockHeader(aclUpdatedAt)
497
+ const saveUserAcl = () => updateCrud('auth/users/acl', { userId: id, ...aclData }, {
476
498
  errorMessage: t('auth.users.form.errors.aclUpdate', 'Failed to update user access control'),
477
499
  })
500
+ if (Object.keys(aclLockHeader).length > 0) {
501
+ await withScopedApiRequestHeaders(aclLockHeader, saveUserAcl)
502
+ } else {
503
+ await saveUserAcl()
504
+ }
478
505
  await widgetEditorRef.current?.save()
479
506
  try { window.dispatchEvent(new Event('om:refresh-sidebar')) } catch {}
480
507
  }}
481
508
  onDelete={async () => {
482
- await deleteCrud('auth/users', String(id), {
509
+ const userOptimisticLockHeader = buildOptimisticLockHeader(initialUser?.updatedAt)
510
+ const deleteUser = () => deleteCrud('auth/users', String(id), {
483
511
  errorMessage: t('auth.users.form.errors.delete', 'Failed to delete user'),
484
512
  })
513
+ if (Object.keys(userOptimisticLockHeader).length > 0) {
514
+ await withScopedApiRequestHeaders(userOptimisticLockHeader, deleteUser)
515
+ } else {
516
+ await deleteUser()
517
+ }
485
518
  }}
486
519
  deleteRedirect={`/backend/users?flash=${encodeURIComponent(t('auth.users.flash.deleted', 'User deleted'))}&type=success`}
487
520
  />
@@ -8,7 +8,8 @@ import type { ColumnDef, SortingState } from '@tanstack/react-table'
8
8
  import type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'
9
9
  import { Button } from '@open-mercato/ui/primitives/button'
10
10
  import { RowActions } from '@open-mercato/ui/backend/RowActions'
11
- import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
11
+ import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
12
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
12
13
  import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
13
14
  import { useQuery, useQueryClient } from '@tanstack/react-query'
14
15
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
@@ -26,6 +27,7 @@ type Row = {
26
27
  tenantId: string | null
27
28
  tenantName?: string | null
28
29
  roles: string[]
30
+ updatedAt?: string | null
29
31
  }
30
32
 
31
33
  type FilterOption = { value: string; label: string }
@@ -388,7 +390,10 @@ export default function UsersListPage() {
388
390
  if (!confirmed) return
389
391
  const deleteErrorMessage = t('auth.users.list.error.delete', 'Failed to delete user')
390
392
  try {
391
- const call = await apiCall(`/api/auth/users?id=${encodeURIComponent(row.id)}`, { method: 'DELETE' })
393
+ const call = await withScopedApiRequestHeaders(
394
+ buildOptimisticLockHeader(row.updatedAt),
395
+ () => apiCall(`/api/auth/users?id=${encodeURIComponent(row.id)}`, { method: 'DELETE' }),
396
+ )
392
397
  if (!call.ok) {
393
398
  await raiseCrudError(call.response, deleteErrorMessage)
394
399
  }
@@ -68,6 +68,7 @@ type AclPayload = {
68
68
  isSuperAdmin?: boolean
69
69
  features?: unknown
70
70
  organizations?: unknown
71
+ updatedAt?: string | null
71
72
  }
72
73
  type OrganizationListResponse = { items?: Array<{ id?: string; name?: string }> }
73
74
 
@@ -99,6 +100,7 @@ export function AclEditor({
99
100
  canEditOrganizations,
100
101
  value,
101
102
  onChange,
103
+ onVersionChange,
102
104
  userRoles,
103
105
  currentUserIsSuperAdmin,
104
106
  tenantId,
@@ -109,6 +111,12 @@ export function AclEditor({
109
111
  canEditOrganizations: boolean
110
112
  value?: AclData
111
113
  onChange?: (data: AclData) => void
114
+ /**
115
+ * Reports the loaded ACL row's `updatedAt` (or null when none exists) so the
116
+ * parent can send the optimistic-lock header on save and reject stale ACL
117
+ * overwrites (#2055).
118
+ */
119
+ onVersionChange?: (updatedAt: string | null) => void
112
120
  userRoles?: string[]
113
121
  currentUserIsSuperAdmin?: boolean
114
122
  tenantId?: string | null
@@ -168,8 +176,9 @@ export function AclEditor({
168
176
  setIsSuperAdmin(!!aclJson.isSuperAdmin)
169
177
  setGranted(actorSanitizeFeatures(aclJson.features))
170
178
  setOrganizations(aclJson.organizations == null ? null : Array.isArray(aclJson.organizations) ? aclJson.organizations : [])
179
+ onVersionChange?.(typeof aclJson.updatedAt === 'string' ? aclJson.updatedAt : null)
171
180
  } catch {}
172
- }, [kind, targetId, actorSanitizeFeatures])
181
+ }, [kind, targetId, actorSanitizeFeatures, onVersionChange])
173
182
 
174
183
  React.useEffect(() => {
175
184
  const cancelled = { current: false }
@@ -33,7 +33,10 @@ export class User {
33
33
  @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })
34
34
  createdAt: Date = new Date()
35
35
 
36
- @Property({ name: 'deleted_at', type: Date, nullable: true })
36
+ @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date(), nullable: true })
37
+ updatedAt?: Date | null
38
+
39
+ @Property({ name: 'deleted_at', type: Date, nullable: true })
37
40
  deletedAt?: Date | null
38
41
  }
39
42
 
@@ -52,6 +55,9 @@ export class Role {
52
55
  @Property({ name: 'created_at', type: Date, onCreate: () => new Date() })
53
56
  createdAt: Date = new Date()
54
57
 
58
+ @Property({ name: 'updated_at', type: Date, onCreate: () => new Date(), onUpdate: () => new Date(), nullable: true })
59
+ updatedAt?: Date | null
60
+
55
61
  @Property({ name: 'deleted_at', type: Date, nullable: true })
56
62
  deletedAt?: Date | null
57
63
  }
@@ -54,6 +54,38 @@ export async function loadSidebarPreference(
54
54
  return normalizeSidebarSettings(existing?.settingsJson as SidebarPreferencesSettings | undefined)
55
55
  }
56
56
 
57
+ export async function loadSidebarPreferenceUpdatedAt(
58
+ em: EntityManager,
59
+ scope: SidebarPreferenceScope,
60
+ ): Promise<{ id: string; updatedAt: Date | null } | null> {
61
+ const { userId, tenantId, organizationId } = normalizeScope(scope)
62
+ const existing = await findOneWithDecryption(
63
+ em,
64
+ UserSidebarPreference,
65
+ { user: userId, tenantId, organizationId },
66
+ undefined,
67
+ { tenantId, organizationId },
68
+ )
69
+ if (!existing) return null
70
+ return { id: existing.id, updatedAt: existing.updatedAt ?? null }
71
+ }
72
+
73
+ export async function loadRoleSidebarPreferenceUpdatedAt(
74
+ em: EntityManager,
75
+ scope: RoleSidebarPreferenceScope,
76
+ ): Promise<{ id: string; updatedAt: Date | null } | null> {
77
+ const { roleId, tenantId } = normalizeRoleScope(scope)
78
+ const existing = await findOneWithDecryption(
79
+ em,
80
+ RoleSidebarPreference,
81
+ { role: roleId, tenantId },
82
+ undefined,
83
+ { tenantId, organizationId: null },
84
+ )
85
+ if (!existing) return null
86
+ return { id: existing.id, updatedAt: existing.updatedAt ?? null }
87
+ }
88
+
57
89
  export async function saveSidebarPreference(
58
90
  em: EntityManager,
59
91
  scope: SidebarPreferenceScope,
@@ -389,7 +421,7 @@ export async function updateSidebarVariant(
389
421
  if (!variant) return null
390
422
  const target = variant
391
423
  await withAtomicFlush(em, [
392
- async () => {
424
+ () => {
393
425
  if (typeof input.name === 'string' && input.name.trim().length > 0) {
394
426
  target.name = input.name.trim()
395
427
  }
@@ -400,12 +432,14 @@ export async function updateSidebarVariant(
400
432
  })
401
433
  }
402
434
  if (typeof input.isActive === 'boolean') {
403
- if (input.isActive) {
404
- await deactivateAllVariants(em, scope, variantId)
405
- }
406
435
  target.isActive = input.isActive
407
436
  }
408
437
  },
438
+ async () => {
439
+ if (input.isActive === true) {
440
+ await deactivateAllVariants(em, scope, variantId)
441
+ }
442
+ },
409
443
  ], { transaction: true })
410
444
  return toVariantRecord(target)
411
445
  }
@@ -4,6 +4,8 @@ import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
4
4
  import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
5
5
  import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
6
6
  import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
7
+ import { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
8
+ import { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
7
9
  import { BusinessRule } from '../../data/entities'
8
10
  import type { EntityManager } from '@mikro-orm/postgresql'
9
11
  import { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'
@@ -277,6 +279,20 @@ export async function PUT(req: Request) {
277
279
  return NextResponse.json({ error: 'Rule not found' }, { status: 404 })
278
280
  }
279
281
 
282
+ try {
283
+ enforceCommandOptimisticLock({
284
+ resourceKind: 'business_rules.rule',
285
+ resourceId: rule.id,
286
+ current: rule.updatedAt ?? null,
287
+ request: req,
288
+ })
289
+ } catch (err) {
290
+ if (isCrudHttpError(err)) {
291
+ return NextResponse.json(err.body, { status: err.status })
292
+ }
293
+ throw err
294
+ }
295
+
280
296
  em.assign(rule, parsed.data)
281
297
 
282
298
  try {
@@ -321,6 +337,20 @@ export async function DELETE(req: Request) {
321
337
  return NextResponse.json({ error: 'Rule not found' }, { status: 404 })
322
338
  }
323
339
 
340
+ try {
341
+ enforceCommandOptimisticLock({
342
+ resourceKind: 'business_rules.rule',
343
+ resourceId: rule.id,
344
+ current: rule.updatedAt ?? null,
345
+ request: req,
346
+ })
347
+ } catch (err) {
348
+ if (isCrudHttpError(err)) {
349
+ return NextResponse.json(err.body, { status: err.status })
350
+ }
351
+ throw err
352
+ }
353
+
324
354
  rule.deletedAt = new Date()
325
355
  await em.persist(rule).flush()
326
356
  await invalidateBusinessRuleDiscoveryCache(cache, rule.tenantId, rule.organizationId)
@@ -3,6 +3,8 @@ import { z } from 'zod'
3
3
  import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
4
4
  import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
5
5
  import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
6
+ import { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
7
+ import { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
6
8
  import { RuleSet } from '../../data/entities'
7
9
  import type { EntityManager } from '@mikro-orm/postgresql'
8
10
  import { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'
@@ -221,6 +223,20 @@ export async function PUT(req: Request) {
221
223
  return NextResponse.json({ error: 'Rule set not found' }, { status: 404 })
222
224
  }
223
225
 
226
+ try {
227
+ enforceCommandOptimisticLock({
228
+ resourceKind: 'business_rules.ruleSet',
229
+ resourceId: ruleSet.id,
230
+ current: ruleSet.updatedAt ?? null,
231
+ request: req,
232
+ })
233
+ } catch (err) {
234
+ if (isCrudHttpError(err)) {
235
+ return NextResponse.json(err.body, { status: err.status })
236
+ }
237
+ throw err
238
+ }
239
+
224
240
  em.assign(ruleSet, parsed.data)
225
241
  await em.persist(ruleSet).flush()
226
242
 
@@ -254,6 +270,20 @@ export async function DELETE(req: Request) {
254
270
  return NextResponse.json({ error: 'Rule set not found' }, { status: 404 })
255
271
  }
256
272
 
273
+ try {
274
+ enforceCommandOptimisticLock({
275
+ resourceKind: 'business_rules.ruleSet',
276
+ resourceId: ruleSet.id,
277
+ current: ruleSet.updatedAt ?? null,
278
+ request: req,
279
+ })
280
+ } catch (err) {
281
+ if (isCrudHttpError(err)) {
282
+ return NextResponse.json(err.body, { status: err.status })
283
+ }
284
+ throw err
285
+ }
286
+
257
287
  ruleSet.deletedAt = new Date()
258
288
  await em.persist(ruleSet).flush()
259
289
 
@@ -8,7 +8,10 @@ import { Page, PageBody } from '@open-mercato/ui/backend/Page'
8
8
  import { CrudForm } from '@open-mercato/ui/backend/CrudForm'
9
9
  import { Spinner } from '@open-mercato/ui/primitives/spinner'
10
10
  import { Button } from '@open-mercato/ui/primitives/button'
11
- import { apiFetch } from '@open-mercato/ui/backend/utils/api'
11
+ import { apiFetch, withScopedApiHeaders } from '@open-mercato/ui/backend/utils/api'
12
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
13
+ import { readJsonSafe } from '@open-mercato/ui/backend/utils/serverErrors'
14
+ import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
12
15
  import { useT } from '@open-mercato/shared/lib/i18n/context'
13
16
  import { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
14
17
  import {
@@ -67,7 +70,7 @@ export default function EditBusinessRulePage() {
67
70
 
68
71
  const payload = buildRulePayload(values, effectiveTenantId, effectiveOrgId, undefined)
69
72
 
70
- const response = await apiFetch('/api/business_rules/rules', {
73
+ const updateRule = () => apiFetch('/api/business_rules/rules', {
71
74
  method: 'PUT',
72
75
  headers: { 'Content-Type': 'application/json' },
73
76
  body: JSON.stringify({
@@ -75,10 +78,18 @@ export default function EditBusinessRulePage() {
75
78
  id: ruleId,
76
79
  }),
77
80
  })
81
+ const headers = buildOptimisticLockHeader(rule?.updatedAt ?? rule?.updated_at ?? null)
82
+ const response = Object.keys(headers).length
83
+ ? await withScopedApiHeaders(headers, updateRule)
84
+ : await updateRule()
78
85
 
79
86
  if (!response.ok) {
80
- const error = await response.json()
81
- throw new Error(error.error || error.message || t('business_rules.errors.updateFailed'))
87
+ const body = (await readJsonSafe<Record<string, unknown>>(response)) ?? {}
88
+ const message =
89
+ (typeof body.error === 'string' && body.error) ||
90
+ (typeof body.message === 'string' && body.message) ||
91
+ t('business_rules.errors.updateFailed')
92
+ throw new CrudHttpError(response.status, { ...body, error: message })
82
93
  }
83
94
 
84
95
  router.push('/backend/rules')
@@ -134,6 +145,7 @@ export default function EditBusinessRulePage() {
134
145
  schema={businessRuleFormSchema}
135
146
  fields={fields}
136
147
  initialValues={initialValues}
148
+ optimisticLockUpdatedAt={rule?.updatedAt ?? rule?.updated_at ?? null}
137
149
  onSubmit={handleSubmit}
138
150
  cancelHref="/backend/rules"
139
151
  groups={formGroups}