@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
@@ -726,48 +726,48 @@ const updateVariantCommand: CommandHandler<VariantUpdateInput, { variantId: stri
726
726
  ? await resolveVariantTaxRate(em, product, parsed.taxRateId ?? null, parsed.taxRate)
727
727
  : null
728
728
 
729
- if (parsed.name !== undefined) record.name = parsed.name ?? null
730
- if (parsed.sku !== undefined) record.sku = parsed.sku ?? null
731
- if (parsed.barcode !== undefined) record.barcode = parsed.barcode ?? null
732
- if (parsed.statusEntryId !== undefined) record.statusEntryId = parsed.statusEntryId ?? null
733
- if (parsed.isDefault !== undefined) record.isDefault = parsed.isDefault
734
- if (parsed.isActive !== undefined) record.isActive = parsed.isActive
735
- if (Object.prototype.hasOwnProperty.call(parsed, 'weightValue')) {
736
- record.weightValue = toNumericString(parsed.weightValue)
737
- }
738
- if (parsed.weightUnit !== undefined) record.weightUnit = parsed.weightUnit ?? null
739
- if (parsed.dimensions !== undefined) {
740
- record.dimensions = parsed.dimensions ? cloneJson(parsed.dimensions) : null
741
- }
742
- let metadataSplit: MetadataSplitResult | null = null
743
- if (parsed.metadata !== undefined) {
744
- metadataSplit = splitOptionValuesFromMetadata(parsed.metadata)
745
- record.metadata = metadataSplit.metadata
746
- }
747
- if (parsed.optionValues !== undefined) {
748
- record.optionValues = parsed.optionValues ? cloneJson(parsed.optionValues) : null
749
- } else if (metadataSplit?.hadOptionValues) {
750
- record.optionValues = metadataSplit.optionValues ? cloneJson(metadataSplit.optionValues) : null
751
- }
752
- if (taxRateProvided) {
753
- record.taxRateId = resolvedTaxRate?.taxRateId ?? null
754
- record.taxRate = resolvedTaxRate?.taxRate ?? null
755
- }
756
- if (parsed.customFieldsetCode !== undefined) {
757
- record.customFieldsetCode = parsed.customFieldsetCode ?? null
758
- }
759
-
760
729
  let previousDefaultVariantId: string | null = null
761
730
  try {
762
731
  await withAtomicFlush(
763
732
  em,
764
733
  [
734
+ () => {
735
+ if (parsed.name !== undefined) record.name = parsed.name ?? null
736
+ if (parsed.sku !== undefined) record.sku = parsed.sku ?? null
737
+ if (parsed.barcode !== undefined) record.barcode = parsed.barcode ?? null
738
+ if (parsed.statusEntryId !== undefined) record.statusEntryId = parsed.statusEntryId ?? null
739
+ if (parsed.isDefault !== undefined) record.isDefault = parsed.isDefault
740
+ if (parsed.isActive !== undefined) record.isActive = parsed.isActive
741
+ if (Object.prototype.hasOwnProperty.call(parsed, 'weightValue')) {
742
+ record.weightValue = toNumericString(parsed.weightValue)
743
+ }
744
+ if (parsed.weightUnit !== undefined) record.weightUnit = parsed.weightUnit ?? null
745
+ if (parsed.dimensions !== undefined) {
746
+ record.dimensions = parsed.dimensions ? cloneJson(parsed.dimensions) : null
747
+ }
748
+ let metadataSplit: MetadataSplitResult | null = null
749
+ if (parsed.metadata !== undefined) {
750
+ metadataSplit = splitOptionValuesFromMetadata(parsed.metadata)
751
+ record.metadata = metadataSplit.metadata
752
+ }
753
+ if (parsed.optionValues !== undefined) {
754
+ record.optionValues = parsed.optionValues ? cloneJson(parsed.optionValues) : null
755
+ } else if (metadataSplit?.hadOptionValues) {
756
+ record.optionValues = metadataSplit.optionValues ? cloneJson(metadataSplit.optionValues) : null
757
+ }
758
+ if (taxRateProvided) {
759
+ record.taxRateId = resolvedTaxRate?.taxRateId ?? null
760
+ record.taxRate = resolvedTaxRate?.taxRate ?? null
761
+ }
762
+ if (parsed.customFieldsetCode !== undefined) {
763
+ record.customFieldsetCode = parsed.customFieldsetCode ?? null
764
+ }
765
+ },
765
766
  async () => {
766
767
  if (parsed.isDefault === true) {
767
768
  previousDefaultVariantId = await enforceSingleDefaultVariant(em, record)
768
769
  }
769
770
  },
770
- () => em.flush(),
771
771
  () => aggregateVariantMediaToProduct(em, record),
772
772
  ],
773
773
  { transaction: true }
@@ -17,7 +17,8 @@ import {
17
17
  DialogTitle,
18
18
  } from '@open-mercato/ui/primitives/dialog'
19
19
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
20
- import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
20
+ import { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
21
+ import { buildOptimisticLockHeader, extractOptimisticLockConflict } from '@open-mercato/ui/backend/utils/optimisticLock'
21
22
  import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
22
23
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
23
24
  import { useT } from '@open-mercato/shared/lib/i18n/context'
@@ -211,11 +212,17 @@ export function PriceKindSettings() {
211
212
  dialog.mode === 'edit'
212
213
  ? JSON.stringify({ id: dialog.entry.id, ...payload })
213
214
  : JSON.stringify(payload)
214
- const call = await apiCall(path, {
215
+ const savePriceKind = () => apiCall(path, {
215
216
  method,
216
217
  headers: { 'content-type': 'application/json' },
217
218
  body,
218
219
  })
220
+ const optimisticLockHeader = buildOptimisticLockHeader(
221
+ dialog.mode === 'edit' ? dialog.entry.updatedAt : null,
222
+ )
223
+ const call = Object.keys(optimisticLockHeader).length > 0
224
+ ? await withScopedApiRequestHeaders(optimisticLockHeader, savePriceKind)
225
+ : await savePriceKind()
219
226
  if (!call.ok) {
220
227
  await raiseCrudError(call.response, t('catalog.priceKinds.errors.save', 'Failed to save price kind.'))
221
228
  }
@@ -229,8 +236,9 @@ export function PriceKindSettings() {
229
236
  await loadItems()
230
237
  } catch (err) {
231
238
  console.error('catalog.price-kinds.save failed', err)
232
- const message =
233
- err instanceof Error ? err.message : t('catalog.priceKinds.errors.save', 'Failed to save price kind.')
239
+ const message = extractOptimisticLockConflict(err)
240
+ ? t('ui.forms.flash.recordModified', 'This record was modified by someone else. Refresh and try again.')
241
+ : err instanceof Error ? err.message : t('catalog.priceKinds.errors.save', 'Failed to save price kind.')
234
242
  setError(message)
235
243
  } finally {
236
244
  setSubmitting(false)
@@ -246,11 +254,15 @@ export function PriceKindSettings() {
246
254
  })
247
255
  if (!confirmed) return
248
256
  try {
249
- const call = await apiCall('/api/catalog/price-kinds', {
257
+ const deletePriceKind = () => apiCall('/api/catalog/price-kinds', {
250
258
  method: 'DELETE',
251
259
  headers: { 'content-type': 'application/json' },
252
260
  body: JSON.stringify({ id: entry.id }),
253
261
  })
262
+ const optimisticLockHeader = buildOptimisticLockHeader(entry.updatedAt)
263
+ const call = Object.keys(optimisticLockHeader).length > 0
264
+ ? await withScopedApiRequestHeaders(optimisticLockHeader, deletePriceKind)
265
+ : await deletePriceKind()
254
266
  if (!call.ok) {
255
267
  await raiseCrudError(call.response, t('catalog.priceKinds.errors.delete', 'Failed to delete price kind.'))
256
268
  }
@@ -258,8 +270,9 @@ export function PriceKindSettings() {
258
270
  await loadItems()
259
271
  } catch (err) {
260
272
  console.error('catalog.price-kinds.delete failed', err)
261
- const message =
262
- err instanceof Error ? err.message : t('catalog.priceKinds.errors.delete', 'Failed to delete price kind.')
273
+ const message = extractOptimisticLockConflict(err)
274
+ ? t('ui.forms.flash.recordModified', 'This record was modified by someone else. Refresh and try again.')
275
+ : err instanceof Error ? err.message : t('catalog.priceKinds.errors.delete', 'Failed to delete price kind.')
263
276
  flash(message, 'error')
264
277
  }
265
278
  },
@@ -170,6 +170,7 @@ export default function CategoriesDataTable() {
170
170
  })
171
171
  if (!confirmed) return
172
172
  try {
173
+ // optimistic-lock-exempt: delete-only mutation — no field-level lost-update
173
174
  await apiCallOrThrow(
174
175
  `/api/catalog/categories?id=${encodeURIComponent(category.id)}`,
175
176
  { method: 'DELETE' },
@@ -68,6 +68,7 @@ export function ProductMediaManager({
68
68
  fd.set('entityId', entityId)
69
69
  fd.set('recordId', draftRecordId)
70
70
  fd.set('file', file)
71
+ // optimistic-lock-exempt: media attach/detach/reorder
71
72
  const call = await apiCall<{ ok?: boolean; item?: ProductMediaItem; error?: string }>(
72
73
  '/api/attachments',
73
74
  { method: 'POST', body: fd },
@@ -111,6 +112,7 @@ export function ProductMediaManager({
111
112
  const handleRemove = React.useCallback(
112
113
  async (attachmentId: string) => {
113
114
  setError(null)
115
+ // optimistic-lock-exempt: media attach/detach/reorder
114
116
  const call = await apiCall<{ ok?: boolean; error?: string }>(
115
117
  `/api/attachments?id=${encodeURIComponent(attachmentId)}`,
116
118
  { method: 'DELETE' },
@@ -6,9 +6,10 @@ import type { ColumnDef, SortingState } from '@tanstack/react-table'
6
6
  import { DataTable, type DataTableExportFormat } from '@open-mercato/ui/backend/DataTable'
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
10
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
11
11
  import { deleteCrud, buildCrudExportUrl } from '@open-mercato/ui/backend/utils/crud'
12
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
12
13
  import { useCustomFieldDefs } from '@open-mercato/ui/backend/utils/customFieldDefs'
13
14
  import { applyCustomFieldVisibility } from '@open-mercato/ui/backend/utils/customFieldColumns'
14
15
  import type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'
@@ -619,9 +620,12 @@ export default function ProductsDataTable({
619
620
  })
620
621
  if (!confirmed) return
621
622
  try {
622
- await deleteCrud('catalog/products', row.id, {
623
- errorMessage: t('catalog.products.list.error.delete', 'Failed to delete product'),
624
- })
623
+ const headers = buildOptimisticLockHeader(typeof row.updated_at === 'string' ? row.updated_at : null)
624
+ await withScopedApiRequestHeaders(headers, () => (
625
+ deleteCrud('catalog/products', row.id, {
626
+ errorMessage: t('catalog.products.list.error.delete', 'Failed to delete product'),
627
+ })
628
+ ))
625
629
  flash(t('catalog.products.flash.deleted', 'Product deleted'), 'success')
626
630
  setReloadToken((token) => token + 1)
627
631
  } catch (error) {
@@ -68,6 +68,8 @@ export type ProductUnitConversionDraft = {
68
68
  toBaseFactor: string;
69
69
  sortOrder: string;
70
70
  isActive: boolean;
71
+ /** The conversion row's version, for the per-row optimistic-lock header (#2055). Optional — only loaded rows carry it. */
72
+ updatedAt?: string | null;
71
73
  };
72
74
 
73
75
  export type VariantDraft = {
@@ -116,6 +118,7 @@ export type ProductFormValues = {
116
118
  channelIds: string[];
117
119
  tags: string[];
118
120
  optionSchemaId?: string | null;
121
+ updatedAt?: string | null;
119
122
  };
120
123
 
121
124
  const optionalPositiveNumberInput = z.preprocess((value) => {
@@ -17,6 +17,8 @@ export type VariantPriceDraft = {
17
17
  amount: string
18
18
  currencyCode?: string | null
19
19
  displayMode: 'including-tax' | 'excluding-tax'
20
+ /** The price row's version, for the per-price optimistic-lock header (#2055). */
21
+ updatedAt?: string | null
20
22
  }
21
23
 
22
24
  export type VariantFormValues = {
@@ -34,6 +36,7 @@ export type VariantFormValues = {
34
36
  prices: Record<string, VariantPriceDraft>
35
37
  taxRateId: string | null
36
38
  customFieldsetCode?: string | null
39
+ updatedAt?: string | null
37
40
  }
38
41
 
39
42
  export const VARIANT_BASE_VALUES: VariantFormValues = {
@@ -147,5 +150,11 @@ export function mapPriceItemToDraft(
147
150
  ? item.currencyCode
148
151
  : null,
149
152
  displayMode: kindMode,
153
+ updatedAt:
154
+ typeof item.updatedAt === 'string'
155
+ ? item.updatedAt
156
+ : typeof item.updated_at === 'string'
157
+ ? item.updated_at
158
+ : null,
150
159
  }
151
160
  }
@@ -787,6 +787,11 @@ function DisconnectChannelDialog({
787
787
  let response
788
788
  try {
789
789
  response = await runMutation({
790
+ // optimistic-lock-exempt: self-service connect/disconnect of the
791
+ // signed-in operator's OWN communication channel (an integration link),
792
+ // not a shared multi-editor record. Disconnect is a terminal action
793
+ // keyed by channel id; there is no concurrent-edit lost-update window to
794
+ // guard, and the row carries no client-surfaced `updatedAt` round-trip.
790
795
  operation: () => apiCall(
791
796
  `/api/communication_channels/channels/${encodeURIComponent(channel.id)}`,
792
797
  { method: 'DELETE' },
@@ -7,7 +7,8 @@ import { CrudForm, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'
7
7
  import { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'
8
8
  import { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'
9
9
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
10
- import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
10
+ import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
11
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
11
12
  import { useT } from '@open-mercato/shared/lib/i18n/context'
12
13
  import { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'
13
14
  import { DataLoader } from '@open-mercato/ui/primitives/DataLoader'
@@ -26,6 +27,8 @@ type CurrencyData = {
26
27
  isActive: boolean
27
28
  organizationId: string
28
29
  tenantId: string
30
+ updatedAt?: string | null
31
+ updated_at?: string | null
29
32
  }
30
33
 
31
34
  export default function EditCurrencyPage({ params }: { params?: { id?: string } }) {
@@ -141,11 +144,14 @@ export default function EditCurrencyPage({ params }: { params?: { id?: string }
141
144
  if (!confirmed) return
142
145
 
143
146
  try {
144
- await apiCall('/api/currencies/currencies', {
145
- method: 'DELETE',
146
- headers: { 'Content-Type': 'application/json' },
147
- body: JSON.stringify({ id: currency.id, organizationId: currency.organizationId, tenantId: currency.tenantId }),
148
- })
147
+ const headers = buildOptimisticLockHeader(currency.updatedAt ?? currency.updated_at ?? null)
148
+ await withScopedApiRequestHeaders(headers, () => (
149
+ apiCall('/api/currencies/currencies', {
150
+ method: 'DELETE',
151
+ headers: { 'Content-Type': 'application/json' },
152
+ body: JSON.stringify({ id: currency.id, organizationId: currency.organizationId, tenantId: currency.tenantId }),
153
+ })
154
+ ))
149
155
 
150
156
  flash(t('currencies.flash.deleted'), 'success')
151
157
  router.push('/backend/currencies')
@@ -221,6 +227,7 @@ export default function EditCurrencyPage({ params }: { params?: { id?: string }
221
227
  )}
222
228
  fields={[]}
223
229
  groups={groups}
230
+ optimisticLockUpdatedAt={currency.updatedAt ?? currency.updated_at ?? null}
224
231
  initialValues={{
225
232
  code: currency.code,
226
233
  name: currency.name,
@@ -11,7 +11,8 @@ import { Button } from '@open-mercato/ui/primitives/button'
11
11
  import { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'
12
12
  import { Plus, Star } from 'lucide-react'
13
13
  import { useT } from '@open-mercato/shared/lib/i18n/context'
14
- import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
14
+ import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
15
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
15
16
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
16
17
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
17
18
  import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
@@ -99,11 +100,14 @@ export default function CurrenciesPage() {
99
100
  const handleSetBase = React.useCallback(
100
101
  async (row: CurrencyRow) => {
101
102
  try {
102
- const call = await apiCall('/api/currencies/currencies', {
103
- method: 'PUT',
104
- headers: { 'Content-Type': 'application/json' },
105
- body: JSON.stringify({ id: row.id, isBase: true }),
106
- })
103
+ const call = await withScopedApiRequestHeaders(
104
+ buildOptimisticLockHeader(row.updatedAt),
105
+ () => apiCall('/api/currencies/currencies', {
106
+ method: 'PUT',
107
+ headers: { 'Content-Type': 'application/json' },
108
+ body: JSON.stringify({ id: row.id, isBase: true }),
109
+ }),
110
+ )
107
111
 
108
112
  if (!call.ok) {
109
113
  flash(t('currencies.flash.baseSetError'), 'error')
@@ -128,11 +132,14 @@ export default function CurrenciesPage() {
128
132
  if (!confirmed) return
129
133
 
130
134
  try {
131
- const call = await apiCall(`/api/currencies/currencies`, {
132
- method: 'DELETE',
133
- headers: { 'Content-Type': 'application/json' },
134
- body: JSON.stringify({ id: row.id, organizationId: row.organizationId, tenantId: row.tenantId }),
135
- })
135
+ const call = await withScopedApiRequestHeaders(
136
+ buildOptimisticLockHeader(row.updatedAt),
137
+ () => apiCall(`/api/currencies/currencies`, {
138
+ method: 'DELETE',
139
+ headers: { 'Content-Type': 'application/json' },
140
+ body: JSON.stringify({ id: row.id, organizationId: row.organizationId, tenantId: row.tenantId }),
141
+ }),
142
+ )
136
143
 
137
144
  if (!call.ok) {
138
145
  flash(t('currencies.flash.deleteError'), 'error')
@@ -40,6 +40,8 @@ type ExchangeRateData = {
40
40
  isActive: boolean
41
41
  organizationId: string
42
42
  tenantId: string
43
+ updatedAt?: string | null
44
+ updated_at?: string | null
43
45
  }
44
46
 
45
47
  export default function EditExchangeRatePage({ params }: { params?: { id?: string } }) {
@@ -125,6 +127,7 @@ export default function EditExchangeRatePage({ params }: { params?: { id?: strin
125
127
  versionHistory={{ resourceKind: 'currencies.exchange_rate', resourceId: exchangeRate.id }}
126
128
  fields={[]}
127
129
  groups={groups}
130
+ optimisticLockUpdatedAt={exchangeRate.updatedAt ?? exchangeRate.updated_at ?? null}
128
131
  initialValues={{
129
132
  fromCurrencyCode: exchangeRate.fromCurrencyCode,
130
133
  toCurrencyCode: exchangeRate.toCurrencyCode,
@@ -10,7 +10,8 @@ import { Button } from '@open-mercato/ui/primitives/button'
10
10
  import { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'
11
11
  import { Plus } from 'lucide-react'
12
12
  import { useT } from '@open-mercato/shared/lib/i18n/context'
13
- import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
13
+ import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
14
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
14
15
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
15
16
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
16
17
  import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
@@ -108,11 +109,14 @@ export default function ExchangeRatesPage() {
108
109
  if (!confirmed) return
109
110
 
110
111
  try {
111
- const call = await apiCall(`/api/currencies/exchange-rates`, {
112
- method: 'DELETE',
113
- headers: { 'Content-Type': 'application/json' },
114
- body: JSON.stringify({ id: row.id, organizationId: row.organizationId, tenantId: row.tenantId }),
115
- })
112
+ const call = await withScopedApiRequestHeaders(
113
+ buildOptimisticLockHeader(row.updatedAt),
114
+ () => apiCall(`/api/currencies/exchange-rates`, {
115
+ method: 'DELETE',
116
+ headers: { 'Content-Type': 'application/json' },
117
+ body: JSON.stringify({ id: row.id, organizationId: row.organizationId, tenantId: row.tenantId }),
118
+ }),
119
+ )
116
120
 
117
121
  if (!call.ok) {
118
122
  flash(t('exchangeRates.flash.deleteError'), 'error')
@@ -230,16 +230,21 @@ const updateCurrencyCommand: CommandHandler<CurrencyUpdateInput, { currencyId: s
230
230
  return { currencyId: record.id }
231
231
  }
232
232
 
233
- for (const [key, change] of Object.entries(changes)) {
234
- ;(record as any)[key] = change.to
235
- }
236
- record.updatedAt = new Date()
237
-
238
233
  // Demote any existing base currency and persist the scalar changes in one
239
234
  // transaction; a partial commit would leave zero or two base currencies.
235
+ // The scalar mutations live in the first phase so the per-phase flush issues
236
+ // the pending changeset on the managed `record` before `enforceBaseCurrency`
237
+ // runs its interleaved `nativeUpdate` (which would otherwise drop the pending
238
+ // UPDATE under v7).
240
239
  await withAtomicFlush(
241
240
  em,
242
241
  [
242
+ () => {
243
+ for (const [key, change] of Object.entries(changes)) {
244
+ ;(record as any)[key] = change.to
245
+ }
246
+ record.updatedAt = new Date()
247
+ },
243
248
  () =>
244
249
  parsed.isBase === true && record.isBase
245
250
  ? enforceBaseCurrency(em, record.id, record.organizationId, record.tenantId)
@@ -2,7 +2,8 @@
2
2
 
3
3
  import { useState, useEffect, useCallback, useMemo } from 'react'
4
4
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
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 { Button } from '@open-mercato/ui/primitives/button'
7
8
  import { Spinner } from '@open-mercato/ui/primitives/spinner'
8
9
  import { Switch } from '@open-mercato/ui/primitives/switch'
@@ -17,6 +18,7 @@ interface FetchConfig {
17
18
  lastSyncStatus: string | null
18
19
  lastSyncMessage: string | null
19
20
  lastSyncCount: number | null
21
+ updatedAt?: string | null
20
22
  }
21
23
 
22
24
  export default function CurrencyFetchingConfig() {
@@ -92,16 +94,20 @@ export default function CurrencyFetchingConfig() {
92
94
  }
93
95
  }, [availableProviders, initializeMissingProviders, t])
94
96
 
95
- const toggleEnabled = useCallback(async (configId: string, currentValue: boolean) => {
97
+ const toggleEnabled = useCallback(async (configId: string, currentValue: boolean, updatedAt?: string | null) => {
96
98
  try {
97
- const { result } = await apiCall('/api/currencies/fetch-configs', {
98
- method: 'PUT',
99
- headers: { 'Content-Type': 'application/json' },
100
- body: JSON.stringify({
101
- id: configId,
102
- isEnabled: !currentValue,
103
- }),
104
- })
99
+ const { result } = await withScopedApiRequestHeaders(
100
+ buildOptimisticLockHeader(updatedAt),
101
+ () =>
102
+ apiCall('/api/currencies/fetch-configs', {
103
+ method: 'PUT',
104
+ headers: { 'Content-Type': 'application/json' },
105
+ body: JSON.stringify({
106
+ id: configId,
107
+ isEnabled: !currentValue,
108
+ }),
109
+ }),
110
+ )
105
111
 
106
112
  if (result?.config) {
107
113
  setConfigs((prev) =>
@@ -119,16 +125,20 @@ export default function CurrencyFetchingConfig() {
119
125
  }
120
126
  }, [t])
121
127
 
122
- const updateSyncTime = useCallback(async (configId: string, syncTime: string) => {
128
+ const updateSyncTime = useCallback(async (configId: string, syncTime: string, updatedAt?: string | null) => {
123
129
  try {
124
- const { result } = await apiCall('/api/currencies/fetch-configs', {
125
- method: 'PUT',
126
- headers: { 'Content-Type': 'application/json' },
127
- body: JSON.stringify({
128
- id: configId,
129
- syncTime: syncTime || null,
130
- }),
131
- })
130
+ const { result } = await withScopedApiRequestHeaders(
131
+ buildOptimisticLockHeader(updatedAt),
132
+ () =>
133
+ apiCall('/api/currencies/fetch-configs', {
134
+ method: 'PUT',
135
+ headers: { 'Content-Type': 'application/json' },
136
+ body: JSON.stringify({
137
+ id: configId,
138
+ syncTime: syncTime || null,
139
+ }),
140
+ }),
141
+ )
132
142
 
133
143
  if (result?.config) {
134
144
  setConfigs((prev) =>
@@ -255,7 +265,7 @@ export default function CurrencyFetchingConfig() {
255
265
 
256
266
  <Switch
257
267
  checked={config.isEnabled}
258
- onCheckedChange={() => toggleEnabled(config.id, config.isEnabled)}
268
+ onCheckedChange={() => toggleEnabled(config.id, config.isEnabled, config.updatedAt)}
259
269
  />
260
270
  </div>
261
271
 
@@ -269,7 +279,7 @@ export default function CurrencyFetchingConfig() {
269
279
  <input
270
280
  type="time"
271
281
  value={config.syncTime || '09:00'}
272
- onChange={(e) => updateSyncTime(config.id, e.target.value)}
282
+ onChange={(e) => updateSyncTime(config.id, e.target.value, config.updatedAt)}
273
283
  className="rounded border bg-background px-2 py-1.5 text-sm"
274
284
  />
275
285
  </div>
@@ -7,6 +7,8 @@ import { RbacService } from '@open-mercato/core/modules/auth/services/rbacServic
7
7
  import { CustomerRole, CustomerRoleAcl, CustomerUserRole } from '@open-mercato/core/modules/customer_accounts/data/entities'
8
8
  import { updateRoleSchema } from '@open-mercato/core/modules/customer_accounts/data/validators'
9
9
  import { emitCustomerAccountsEvent } from '@open-mercato/core/modules/customer_accounts/events'
10
+ import { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
11
+ import { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
10
12
 
11
13
  export const metadata = {}
12
14
 
@@ -102,15 +104,30 @@ export async function PUT(req: Request, { params }: { params: { id: string } })
102
104
  return NextResponse.json({ ok: false, error: 'Cannot change name of a system role' }, { status: 400 })
103
105
  }
104
106
 
105
- const updates: Record<string, unknown> = {}
107
+ // Optimistic lock: refuse a stale overwrite so two admins editing the same role
108
+ // in parallel cannot silently clobber each other (#2055). Strictly additive.
109
+ try {
110
+ enforceCommandOptimisticLock({
111
+ resourceKind: 'customer_accounts.role',
112
+ resourceId: role.id,
113
+ current: role.updatedAt ?? null,
114
+ request: req,
115
+ })
116
+ } catch (err) {
117
+ if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })
118
+ throw err
119
+ }
120
+
121
+ // Always bump updated_at so the lock version advances on every save — `nativeUpdate`
122
+ // bypasses MikroORM's `onUpdate` hook, so set it explicitly.
123
+ const nextUpdatedAt = new Date()
124
+ const updates: Record<string, unknown> = { updatedAt: nextUpdatedAt }
106
125
  if (parsed.data.name !== undefined) updates.name = parsed.data.name
107
126
  if (parsed.data.description !== undefined) updates.description = parsed.data.description
108
127
  if (parsed.data.isDefault !== undefined) updates.isDefault = parsed.data.isDefault
109
128
  if (parsed.data.customerAssignable !== undefined) updates.customerAssignable = parsed.data.customerAssignable
110
129
 
111
- if (Object.keys(updates).length > 0) {
112
- await em.nativeUpdate(CustomerRole, { id: role.id }, updates)
113
- }
130
+ await em.nativeUpdate(CustomerRole, { id: role.id }, updates)
114
131
 
115
132
  void emitCustomerAccountsEvent('customer_accounts.role.updated', {
116
133
  id: role.id,
@@ -119,7 +136,7 @@ export async function PUT(req: Request, { params }: { params: { id: string } })
119
136
  organizationId: auth.orgId,
120
137
  }).catch(() => undefined)
121
138
 
122
- return NextResponse.json({ ok: true })
139
+ return NextResponse.json({ ok: true, updatedAt: nextUpdatedAt.toISOString() })
123
140
  }
124
141
 
125
142
  export async function DELETE(req: Request, { params }: { params: { id: string } }) {
@@ -150,6 +167,19 @@ export async function DELETE(req: Request, { params }: { params: { id: string }
150
167
  return NextResponse.json({ ok: false, error: 'Cannot delete a system role' }, { status: 400 })
151
168
  }
152
169
 
170
+ // Optimistic lock: refuse a stale delete. Strictly additive.
171
+ try {
172
+ enforceCommandOptimisticLock({
173
+ resourceKind: 'customer_accounts.role',
174
+ resourceId: role.id,
175
+ current: role.updatedAt ?? null,
176
+ request: req,
177
+ })
178
+ } catch (err) {
179
+ if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })
180
+ throw err
181
+ }
182
+
153
183
  const assignedUsersCount = await em.count(CustomerUserRole, {
154
184
  role: role.id as any,
155
185
  deletedAt: null,
@@ -62,6 +62,7 @@ export async function GET(req: Request) {
62
62
  isSystem: role.isSystem,
63
63
  customerAssignable: role.customerAssignable,
64
64
  createdAt: role.createdAt,
65
+ updatedAt: role.updatedAt || null,
65
66
  }))
66
67
 
67
68
  return NextResponse.json({ ok: true, items, total, totalPages, page })
@@ -159,6 +160,7 @@ const roleSchema = z.object({
159
160
  isSystem: z.boolean(),
160
161
  customerAssignable: z.boolean(),
161
162
  createdAt: z.string().datetime(),
163
+ updatedAt: z.string().datetime().nullable(),
162
164
  })
163
165
 
164
166
  const roleResponseSchema = z.object({