@open-mercato/core 0.6.5-develop.4534.1.b459babe6d → 0.6.5-develop.4559.1.839e136509

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 (644) 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/communicationChannelsFixtures.js.map +2 -2
  10. package/dist/helpers/integration/dbFixtures.js +2 -1
  11. package/dist/helpers/integration/dbFixtures.js.map +2 -2
  12. package/dist/helpers/integration/optimisticLockUi.js +104 -0
  13. package/dist/helpers/integration/optimisticLockUi.js.map +7 -0
  14. package/dist/helpers/integration/salesFixtures.js +17 -0
  15. package/dist/helpers/integration/salesFixtures.js.map +2 -2
  16. package/dist/modules/api_keys/backend/api-keys/page.js +9 -5
  17. package/dist/modules/api_keys/backend/api-keys/page.js.map +2 -2
  18. package/dist/modules/attachments/components/AttachmentPartitionSettings.js +17 -9
  19. package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +2 -2
  20. package/dist/modules/auth/api/roles/acl/route.js +32 -13
  21. package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
  22. package/dist/modules/auth/api/roles/route.js +3 -1
  23. package/dist/modules/auth/api/roles/route.js.map +2 -2
  24. package/dist/modules/auth/api/sidebar/preferences/route.js +71 -3
  25. package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
  26. package/dist/modules/auth/api/users/acl/route.js +42 -19
  27. package/dist/modules/auth/api/users/acl/route.js.map +2 -2
  28. package/dist/modules/auth/api/users/route.js +3 -1
  29. package/dist/modules/auth/api/users/route.js.map +2 -2
  30. package/dist/modules/auth/backend/roles/[id]/edit/page.js +24 -4
  31. package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
  32. package/dist/modules/auth/backend/roles/page.js +8 -4
  33. package/dist/modules/auth/backend/roles/page.js.map +2 -2
  34. package/dist/modules/auth/backend/users/[id]/edit/page.js +27 -5
  35. package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
  36. package/dist/modules/auth/backend/users/page.js +6 -2
  37. package/dist/modules/auth/backend/users/page.js.map +2 -2
  38. package/dist/modules/auth/components/AclEditor.js +3 -1
  39. package/dist/modules/auth/components/AclEditor.js.map +2 -2
  40. package/dist/modules/auth/data/entities.js +6 -0
  41. package/dist/modules/auth/data/entities.js.map +2 -2
  42. package/dist/modules/auth/services/sidebarPreferencesService.js +32 -4
  43. package/dist/modules/auth/services/sidebarPreferencesService.js.map +2 -2
  44. package/dist/modules/business_rules/api/rules/route.js +28 -0
  45. package/dist/modules/business_rules/api/rules/route.js.map +2 -2
  46. package/dist/modules/business_rules/api/sets/route.js +28 -0
  47. package/dist/modules/business_rules/api/sets/route.js.map +2 -2
  48. package/dist/modules/business_rules/backend/rules/[id]/page.js +11 -4
  49. package/dist/modules/business_rules/backend/rules/[id]/page.js.map +3 -3
  50. package/dist/modules/business_rules/backend/rules/page.js +20 -11
  51. package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
  52. package/dist/modules/business_rules/backend/sets/[id]/page.js +11 -4
  53. package/dist/modules/business_rules/backend/sets/[id]/page.js.map +2 -2
  54. package/dist/modules/business_rules/backend/sets/page.js +20 -11
  55. package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
  56. package/dist/modules/catalog/api/categories/route.js +2 -0
  57. package/dist/modules/catalog/api/categories/route.js.map +2 -2
  58. package/dist/modules/catalog/api/products/route.js +2 -1
  59. package/dist/modules/catalog/api/products/route.js.map +2 -2
  60. package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js +2 -0
  61. package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js.map +2 -2
  62. package/dist/modules/catalog/backend/catalog/products/[id]/page.js +94 -40
  63. package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
  64. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +37 -8
  65. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
  66. package/dist/modules/catalog/backend/catalog/products/optionSchemaClient.js.map +2 -2
  67. package/dist/modules/catalog/commands/variants.js +32 -31
  68. package/dist/modules/catalog/commands/variants.js.map +2 -2
  69. package/dist/modules/catalog/components/PriceKindSettings.js +12 -5
  70. package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
  71. package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
  72. package/dist/modules/catalog/components/products/ProductMediaManager.js.map +2 -2
  73. package/dist/modules/catalog/components/products/ProductsDataTable.js +5 -3
  74. package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
  75. package/dist/modules/catalog/components/products/productForm.js.map +2 -2
  76. package/dist/modules/catalog/components/products/variantForm.js +2 -1
  77. package/dist/modules/catalog/components/products/variantForm.js.map +2 -2
  78. package/dist/modules/communication_channels/api/post/test-seed/route.js +23 -2
  79. package/dist/modules/communication_channels/api/post/test-seed/route.js.map +2 -2
  80. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js +5 -0
  81. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +2 -2
  82. package/dist/modules/communication_channels/commands/set-primary-channel.js +2 -1
  83. package/dist/modules/communication_channels/commands/set-primary-channel.js.map +2 -2
  84. package/dist/modules/currencies/backend/currencies/[id]/page.js +6 -3
  85. package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
  86. package/dist/modules/currencies/backend/currencies/page.js +18 -11
  87. package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
  88. package/dist/modules/currencies/backend/exchange-rates/[id]/page.js +1 -0
  89. package/dist/modules/currencies/backend/exchange-rates/[id]/page.js.map +2 -2
  90. package/dist/modules/currencies/backend/exchange-rates/page.js +10 -6
  91. package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
  92. package/dist/modules/currencies/commands/currencies.js +7 -5
  93. package/dist/modules/currencies/commands/currencies.js.map +2 -2
  94. package/dist/modules/currencies/components/CurrencyFetchingConfig.js +26 -19
  95. package/dist/modules/currencies/components/CurrencyFetchingConfig.js.map +2 -2
  96. package/dist/modules/customer_accounts/api/admin/roles/[id].js +28 -5
  97. package/dist/modules/customer_accounts/api/admin/roles/[id].js.map +2 -2
  98. package/dist/modules/customer_accounts/api/admin/roles.js +4 -2
  99. package/dist/modules/customer_accounts/api/admin/roles.js.map +2 -2
  100. package/dist/modules/customer_accounts/api/admin/users/[id].js +28 -5
  101. package/dist/modules/customer_accounts/api/admin/users/[id].js.map +2 -2
  102. package/dist/modules/customer_accounts/api/admin/users.js +2 -0
  103. package/dist/modules/customer_accounts/api/admin/users.js.map +2 -2
  104. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js +16 -8
  105. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js.map +2 -2
  106. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +8 -4
  107. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +2 -2
  108. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js +8 -4
  109. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js.map +2 -2
  110. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js +29 -18
  111. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js.map +2 -2
  112. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js +18 -11
  113. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js.map +2 -2
  114. package/dist/modules/customers/api/companies/route.js +13 -2
  115. package/dist/modules/customers/api/companies/route.js.map +2 -2
  116. package/dist/modules/customers/api/deals/route.js +2 -0
  117. package/dist/modules/customers/api/deals/route.js.map +2 -2
  118. package/dist/modules/customers/api/people/route.js +11 -2
  119. package/dist/modules/customers/api/people/route.js.map +2 -2
  120. package/dist/modules/customers/api/todos/route.js +1 -0
  121. package/dist/modules/customers/api/todos/route.js.map +2 -2
  122. package/dist/modules/customers/backend/config/customers/deals/page.js.map +2 -2
  123. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js +34 -21
  124. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js.map +2 -2
  125. package/dist/modules/customers/backend/customers/companies/[id]/page.js +45 -27
  126. package/dist/modules/customers/backend/customers/companies/[id]/page.js.map +2 -2
  127. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  128. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +22 -5
  129. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
  130. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealFormHandlers.js +30 -8
  131. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealFormHandlers.js.map +2 -2
  132. package/dist/modules/customers/backend/customers/deals/[id]/page.js +1 -0
  133. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  134. package/dist/modules/customers/backend/customers/deals/page.js +16 -6
  135. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  136. package/dist/modules/customers/backend/customers/deals/pipeline/page.js +62 -39
  137. package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
  138. package/dist/modules/customers/backend/customers/people/[id]/page.js +41 -26
  139. package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
  140. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  141. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +50 -23
  142. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  143. package/dist/modules/customers/commands/addresses.js +16 -14
  144. package/dist/modules/customers/commands/addresses.js.map +2 -2
  145. package/dist/modules/customers/commands/companies.js +1 -1
  146. package/dist/modules/customers/commands/companies.js.map +2 -2
  147. package/dist/modules/customers/commands/interactions.js +41 -4
  148. package/dist/modules/customers/commands/interactions.js.map +2 -2
  149. package/dist/modules/customers/commands/people.js +1 -1
  150. package/dist/modules/customers/commands/people.js.map +2 -2
  151. package/dist/modules/customers/commands/personCompanyLinks.js +8 -5
  152. package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
  153. package/dist/modules/customers/commands/pipeline-stages.js +13 -11
  154. package/dist/modules/customers/commands/pipeline-stages.js.map +3 -3
  155. package/dist/modules/customers/components/AddressFormatSettings.js.map +2 -2
  156. package/dist/modules/customers/components/DictionarySettings.js +20 -13
  157. package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
  158. package/dist/modules/customers/components/DictionarySortSettings.js +4 -0
  159. package/dist/modules/customers/components/DictionarySortSettings.js.map +2 -2
  160. package/dist/modules/customers/components/PipelineSettings.js +38 -23
  161. package/dist/modules/customers/components/PipelineSettings.js.map +2 -2
  162. package/dist/modules/customers/components/detail/ActivityTimeline.js +1 -1
  163. package/dist/modules/customers/components/detail/ActivityTimeline.js.map +2 -2
  164. package/dist/modules/customers/components/detail/AddressesSection.js +4 -0
  165. package/dist/modules/customers/components/detail/AddressesSection.js.map +2 -2
  166. package/dist/modules/customers/components/detail/CompanyPeopleSection.js +28 -22
  167. package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
  168. package/dist/modules/customers/components/detail/DealsSection.js +36 -24
  169. package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
  170. package/dist/modules/customers/components/detail/EmailCardActions.js +5 -0
  171. package/dist/modules/customers/components/detail/EmailCardActions.js.map +2 -2
  172. package/dist/modules/customers/components/detail/EntityTagsDialog.js +7 -0
  173. package/dist/modules/customers/components/detail/EntityTagsDialog.js.map +2 -2
  174. package/dist/modules/customers/components/detail/ManageTagsDialog.js +34 -22
  175. package/dist/modules/customers/components/detail/ManageTagsDialog.js.map +2 -2
  176. package/dist/modules/customers/components/detail/PersonCompaniesSection.js +41 -29
  177. package/dist/modules/customers/components/detail/PersonCompaniesSection.js.map +2 -2
  178. package/dist/modules/customers/components/detail/RoleAssignmentRow.js +14 -8
  179. package/dist/modules/customers/components/detail/RoleAssignmentRow.js.map +2 -2
  180. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +14 -6
  181. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  182. package/dist/modules/customers/components/detail/hooks/useInteractionMutations.js +29 -13
  183. package/dist/modules/customers/components/detail/hooks/useInteractionMutations.js.map +2 -2
  184. package/dist/modules/customers/components/detail/hooks/useInteractions.js +77 -35
  185. package/dist/modules/customers/components/detail/hooks/useInteractions.js.map +2 -2
  186. package/dist/modules/customers/components/detail/hooks/usePersonTasks.js +25 -17
  187. package/dist/modules/customers/components/detail/hooks/usePersonTasks.js.map +2 -2
  188. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
  189. package/dist/modules/customers/components/formConfig.js.map +2 -2
  190. package/dist/modules/customers/data/guards.js +66 -0
  191. package/dist/modules/customers/data/guards.js.map +7 -0
  192. package/dist/modules/customers/di.js +37 -0
  193. package/dist/modules/customers/di.js.map +2 -2
  194. package/dist/modules/customers/lib/todoCompatibility.js +11 -0
  195. package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
  196. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +2 -2
  197. package/dist/modules/data_sync/api/options.js +4 -4
  198. package/dist/modules/data_sync/api/options.js.map +2 -2
  199. package/dist/modules/data_sync/api/schedules/route.js +9 -1
  200. package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
  201. package/dist/modules/data_sync/backend/data-sync/page.js +17 -8
  202. package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
  203. package/dist/modules/data_sync/components/IntegrationScheduleTab.js +43 -22
  204. package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +2 -2
  205. package/dist/modules/data_sync/lib/sync-schedule-service.js +9 -0
  206. package/dist/modules/data_sync/lib/sync-schedule-service.js.map +2 -2
  207. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +8 -1
  208. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
  209. package/dist/modules/dictionaries/api/[dictionaryId]/route.js +17 -1
  210. package/dist/modules/dictionaries/api/[dictionaryId]/route.js.map +2 -2
  211. package/dist/modules/dictionaries/components/DictionariesManager.js +31 -10
  212. package/dist/modules/dictionaries/components/DictionariesManager.js.map +2 -2
  213. package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js +28 -15
  214. package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js.map +2 -2
  215. package/dist/modules/directory/api/organizations/route.js +3 -0
  216. package/dist/modules/directory/api/organizations/route.js.map +2 -2
  217. package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js +2 -0
  218. package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js.map +2 -2
  219. package/dist/modules/directory/backend/directory/organizations/page.js +9 -5
  220. package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
  221. package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js +7 -3
  222. package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js.map +2 -2
  223. package/dist/modules/directory/backend/directory/tenants/page.js +8 -4
  224. package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
  225. package/dist/modules/directory/commands/organizations.js +7 -2
  226. package/dist/modules/directory/commands/organizations.js.map +2 -2
  227. package/dist/modules/entities/api/records.js +66 -0
  228. package/dist/modules/entities/api/records.js.map +2 -2
  229. package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js +1 -0
  230. package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js.map +2 -2
  231. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +8 -4
  232. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  233. package/dist/modules/entities/lib/helpers.js +17 -0
  234. package/dist/modules/entities/lib/helpers.js.map +2 -2
  235. package/dist/modules/feature_toggles/api/global/[id]/override/route.js +2 -1
  236. package/dist/modules/feature_toggles/api/global/[id]/override/route.js.map +2 -2
  237. package/dist/modules/feature_toggles/api/overrides/route.js +15 -0
  238. package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
  239. package/dist/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.js +15 -14
  240. package/dist/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.js.map +2 -2
  241. package/dist/modules/feature_toggles/components/FeatureToggleOverrideCard.js +20 -12
  242. package/dist/modules/feature_toggles/components/FeatureToggleOverrideCard.js.map +2 -2
  243. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +6 -2
  244. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
  245. package/dist/modules/feature_toggles/components/formConfig.js +2 -1
  246. package/dist/modules/feature_toggles/components/formConfig.js.map +2 -2
  247. package/dist/modules/feature_toggles/components/overrideFormConfig.js +5 -1
  248. package/dist/modules/feature_toggles/components/overrideFormConfig.js.map +2 -2
  249. package/dist/modules/feature_toggles/data/validators.js +7 -4
  250. package/dist/modules/feature_toggles/data/validators.js.map +2 -2
  251. package/dist/modules/inbox_ops/api/settings/route.js +17 -2
  252. package/dist/modules/inbox_ops/api/settings/route.js.map +2 -2
  253. package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.js +13 -8
  254. package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.js.map +2 -2
  255. package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js +9 -4
  256. package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js.map +2 -2
  257. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +18 -11
  258. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
  259. package/dist/modules/integrations/backend/integrations/page.js +12 -8
  260. package/dist/modules/integrations/backend/integrations/page.js.map +2 -2
  261. package/dist/modules/messages/commands/messages.js +13 -10
  262. package/dist/modules/messages/commands/messages.js.map +2 -2
  263. package/dist/modules/perspectives/api/[tableId]/route.js +39 -30
  264. package/dist/modules/perspectives/api/[tableId]/route.js.map +2 -2
  265. package/dist/modules/perspectives/services/perspectiveService.js +7 -0
  266. package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
  267. package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js +6 -14
  268. package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js.map +3 -3
  269. package/dist/modules/planner/backend/planner/availability-rulesets/page.js +4 -2
  270. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  271. package/dist/modules/planner/components/AvailabilityRuleSetForm.js +2 -0
  272. package/dist/modules/planner/components/AvailabilityRuleSetForm.js.map +2 -2
  273. package/dist/modules/planner/components/AvailabilityRulesEditor.js +36 -11
  274. package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
  275. package/dist/modules/planner/components/AvailabilitySchedule.js +9 -5
  276. package/dist/modules/planner/components/AvailabilitySchedule.js.map +2 -2
  277. package/dist/modules/query_index/lib/engine.js +19 -0
  278. package/dist/modules/query_index/lib/engine.js.map +2 -2
  279. package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js +1 -0
  280. package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js.map +2 -2
  281. package/dist/modules/resources/backend/resources/resource-types/page.js +4 -2
  282. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  283. package/dist/modules/resources/backend/resources/resources/[id]/page.js +14 -3
  284. package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
  285. package/dist/modules/resources/backend/resources/resources/page.js +8 -4
  286. package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
  287. package/dist/modules/resources/components/ResourceCrudForm.js +2 -0
  288. package/dist/modules/resources/components/ResourceCrudForm.js.map +2 -2
  289. package/dist/modules/resources/components/ResourceTypeCrudForm.js +1 -0
  290. package/dist/modules/resources/components/ResourceTypeCrudForm.js.map +2 -2
  291. package/dist/modules/sales/api/documents/factory.js +7 -2
  292. package/dist/modules/sales/api/documents/factory.js.map +2 -2
  293. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js +3 -1
  294. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js.map +2 -2
  295. package/dist/modules/sales/backend/sales/channels/offers/page.js +13 -4
  296. package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
  297. package/dist/modules/sales/backend/sales/channels/page.js +16 -4
  298. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  299. package/dist/modules/sales/backend/sales/documents/[id]/page.js +68 -22
  300. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  301. package/dist/modules/sales/backend/sales/documents/create/page.js.map +2 -2
  302. package/dist/modules/sales/commands/documentAddresses.js +181 -2
  303. package/dist/modules/sales/commands/documentAddresses.js.map +2 -2
  304. package/dist/modules/sales/commands/documents.js +29 -1
  305. package/dist/modules/sales/commands/documents.js.map +2 -2
  306. package/dist/modules/sales/commands/returns.js +12 -2
  307. package/dist/modules/sales/commands/returns.js.map +2 -2
  308. package/dist/modules/sales/commands/shared.js +15 -0
  309. package/dist/modules/sales/commands/shared.js.map +2 -2
  310. package/dist/modules/sales/commands/shipments.js +4 -1
  311. package/dist/modules/sales/commands/shipments.js.map +2 -2
  312. package/dist/modules/sales/components/AdjustmentKindSettings.js +19 -11
  313. package/dist/modules/sales/components/AdjustmentKindSettings.js.map +2 -2
  314. package/dist/modules/sales/components/DocumentNumberSettings.js.map +2 -2
  315. package/dist/modules/sales/components/OrderEditingSettings.js.map +2 -2
  316. package/dist/modules/sales/components/PaymentMethodsSettings.js +12 -4
  317. package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
  318. package/dist/modules/sales/components/ShippingMethodsSettings.js +12 -4
  319. package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
  320. package/dist/modules/sales/components/StatusSettings.js +18 -11
  321. package/dist/modules/sales/components/StatusSettings.js.map +2 -2
  322. package/dist/modules/sales/components/TaxRatesSettings.js +12 -4
  323. package/dist/modules/sales/components/TaxRatesSettings.js.map +2 -2
  324. package/dist/modules/sales/components/channels/ChannelOfferForm.js +47 -16
  325. package/dist/modules/sales/components/channels/ChannelOfferForm.js.map +2 -2
  326. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +8 -4
  327. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
  328. package/dist/modules/sales/components/documents/AddressesSection.js +44 -25
  329. package/dist/modules/sales/components/documents/AddressesSection.js.map +2 -2
  330. package/dist/modules/sales/components/documents/AdjustmentsSection.js +43 -23
  331. package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
  332. package/dist/modules/sales/components/documents/ItemsSection.js +22 -13
  333. package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
  334. package/dist/modules/sales/components/documents/LineItemDialog.js +23 -10
  335. package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
  336. package/dist/modules/sales/components/documents/PaymentDialog.js +29 -14
  337. package/dist/modules/sales/components/documents/PaymentDialog.js.map +2 -2
  338. package/dist/modules/sales/components/documents/PaymentsSection.js +20 -10
  339. package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
  340. package/dist/modules/sales/components/documents/ReturnDialog.js +26 -17
  341. package/dist/modules/sales/components/documents/ReturnDialog.js.map +2 -2
  342. package/dist/modules/sales/components/documents/ReturnsSection.js +3 -1
  343. package/dist/modules/sales/components/documents/ReturnsSection.js.map +2 -2
  344. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +10 -5
  345. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  346. package/dist/modules/sales/components/documents/ShipmentDialog.js +21 -7
  347. package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
  348. package/dist/modules/sales/components/documents/ShipmentsSection.js +19 -10
  349. package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
  350. package/dist/modules/sales/components/documents/optimisticLock.js +27 -0
  351. package/dist/modules/sales/components/documents/optimisticLock.js.map +7 -0
  352. package/dist/modules/sales/di.js +18 -0
  353. package/dist/modules/sales/di.js.map +2 -2
  354. package/dist/modules/staff/api/job-histories.js +11 -2
  355. package/dist/modules/staff/api/job-histories.js.map +2 -2
  356. package/dist/modules/staff/api/timesheets/time-entries/route.js +11 -4
  357. package/dist/modules/staff/api/timesheets/time-entries/route.js.map +2 -2
  358. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +13 -8
  359. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
  360. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js +2 -1
  361. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js.map +2 -2
  362. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +7 -4
  363. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  364. package/dist/modules/staff/backend/staff/team-members/page.js +4 -2
  365. package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
  366. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js +1 -0
  367. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js.map +2 -2
  368. package/dist/modules/staff/backend/staff/team-roles/page.js +4 -2
  369. package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
  370. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +5 -2
  371. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  372. package/dist/modules/staff/backend/staff/teams/page.js +12 -3
  373. package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
  374. package/dist/modules/staff/backend/staff/timesheets/page.js +4 -1
  375. package/dist/modules/staff/backend/staff/timesheets/page.js.map +2 -2
  376. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js.map +2 -2
  377. package/dist/modules/staff/backend/staff/timesheets/projects/page.js +12 -3
  378. package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
  379. package/dist/modules/staff/commands/job-histories.js +40 -3
  380. package/dist/modules/staff/commands/job-histories.js.map +2 -2
  381. package/dist/modules/staff/components/LeaveRequestForm.js +1 -0
  382. package/dist/modules/staff/components/LeaveRequestForm.js.map +2 -2
  383. package/dist/modules/staff/components/TeamForm.js +1 -0
  384. package/dist/modules/staff/components/TeamForm.js.map +2 -2
  385. package/dist/modules/staff/components/TeamMemberForm.js +1 -0
  386. package/dist/modules/staff/components/TeamMemberForm.js.map +2 -2
  387. package/dist/modules/staff/components/TeamRoleForm.js +1 -0
  388. package/dist/modules/staff/components/TeamRoleForm.js.map +2 -2
  389. package/dist/modules/staff/components/detail/JobHistorySection.js +20 -7
  390. package/dist/modules/staff/components/detail/JobHistorySection.js.map +2 -2
  391. package/dist/modules/staff/data/validators.js +7 -1
  392. package/dist/modules/staff/data/validators.js.map +2 -2
  393. package/dist/modules/staff/lib/leaveRequestHelpers.js +2 -1
  394. package/dist/modules/staff/lib/leaveRequestHelpers.js.map +2 -2
  395. package/dist/modules/translations/components/TranslationManager.js +12 -8
  396. package/dist/modules/translations/components/TranslationManager.js.map +2 -2
  397. package/dist/modules/workflows/api/definitions/[id]/route.js +106 -0
  398. package/dist/modules/workflows/api/definitions/[id]/route.js.map +2 -2
  399. package/dist/modules/workflows/backend/definitions/[id]/page.js +11 -3
  400. package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
  401. package/dist/modules/workflows/backend/definitions/page.js +19 -8
  402. package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
  403. package/dist/modules/workflows/backend/definitions/visual-editor/page.js +29 -16
  404. package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
  405. package/dist/modules/workflows/components/formConfig.js +4 -1
  406. package/dist/modules/workflows/components/formConfig.js.map +2 -2
  407. package/dist/modules/workflows/di.js +12 -0
  408. package/dist/modules/workflows/di.js.map +2 -2
  409. package/generated/entities/role/index.ts +1 -0
  410. package/generated/entities/user/index.ts +1 -0
  411. package/generated/entity-fields-registry.ts +2 -0
  412. package/jest.setup.ts +17 -0
  413. package/package.json +8 -7
  414. package/src/helpers/integration/communicationChannelsFixtures.ts +6 -0
  415. package/src/helpers/integration/dbFixtures.ts +1 -1
  416. package/src/helpers/integration/optimisticLockUi.ts +172 -0
  417. package/src/helpers/integration/salesFixtures.ts +29 -0
  418. package/src/modules/api_keys/backend/api-keys/page.tsx +10 -5
  419. package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +19 -9
  420. package/src/modules/auth/api/roles/acl/route.ts +37 -11
  421. package/src/modules/auth/api/roles/route.ts +2 -0
  422. package/src/modules/auth/api/sidebar/preferences/route.ts +73 -0
  423. package/src/modules/auth/api/users/acl/route.ts +46 -18
  424. package/src/modules/auth/api/users/route.ts +2 -0
  425. package/src/modules/auth/backend/roles/[id]/edit/page.tsx +29 -4
  426. package/src/modules/auth/backend/roles/page.tsx +9 -4
  427. package/src/modules/auth/backend/users/[id]/edit/page.tsx +37 -4
  428. package/src/modules/auth/backend/users/page.tsx +7 -2
  429. package/src/modules/auth/components/AclEditor.tsx +10 -1
  430. package/src/modules/auth/data/entities.ts +7 -1
  431. package/src/modules/auth/services/sidebarPreferencesService.ts +38 -4
  432. package/src/modules/business_rules/api/rules/route.ts +30 -0
  433. package/src/modules/business_rules/api/sets/route.ts +30 -0
  434. package/src/modules/business_rules/backend/rules/[id]/page.tsx +16 -4
  435. package/src/modules/business_rules/backend/rules/page.tsx +20 -11
  436. package/src/modules/business_rules/backend/sets/[id]/page.tsx +16 -4
  437. package/src/modules/business_rules/backend/sets/page.tsx +20 -11
  438. package/src/modules/catalog/api/categories/route.ts +3 -0
  439. package/src/modules/catalog/api/products/route.ts +4 -0
  440. package/src/modules/catalog/backend/catalog/categories/[id]/edit/page.tsx +5 -0
  441. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +112 -35
  442. package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +56 -7
  443. package/src/modules/catalog/backend/catalog/products/optionSchemaClient.ts +2 -0
  444. package/src/modules/catalog/commands/variants.ts +32 -32
  445. package/src/modules/catalog/components/PriceKindSettings.tsx +20 -7
  446. package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +1 -0
  447. package/src/modules/catalog/components/products/ProductMediaManager.tsx +2 -0
  448. package/src/modules/catalog/components/products/ProductsDataTable.tsx +8 -4
  449. package/src/modules/catalog/components/products/productForm.ts +3 -0
  450. package/src/modules/catalog/components/products/variantForm.ts +9 -0
  451. package/src/modules/communication_channels/api/post/test-seed/route.ts +28 -1
  452. package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +5 -0
  453. package/src/modules/communication_channels/commands/set-primary-channel.ts +10 -7
  454. package/src/modules/currencies/backend/currencies/[id]/page.tsx +13 -6
  455. package/src/modules/currencies/backend/currencies/page.tsx +18 -11
  456. package/src/modules/currencies/backend/exchange-rates/[id]/page.tsx +3 -0
  457. package/src/modules/currencies/backend/exchange-rates/page.tsx +10 -6
  458. package/src/modules/currencies/commands/currencies.ts +10 -5
  459. package/src/modules/currencies/components/CurrencyFetchingConfig.tsx +31 -21
  460. package/src/modules/customer_accounts/api/admin/roles/[id].ts +35 -5
  461. package/src/modules/customer_accounts/api/admin/roles.ts +2 -0
  462. package/src/modules/customer_accounts/api/admin/users/[id].ts +38 -5
  463. package/src/modules/customer_accounts/api/admin/users.ts +2 -0
  464. package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +34 -20
  465. package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +9 -4
  466. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/page.tsx +11 -4
  467. package/src/modules/customer_accounts/backend/customer_accounts/users/[id]/page.tsx +28 -17
  468. package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +19 -11
  469. package/src/modules/customers/AGENTS.md +2 -2
  470. package/src/modules/customers/api/companies/route.ts +14 -1
  471. package/src/modules/customers/api/deals/route.ts +3 -0
  472. package/src/modules/customers/api/people/route.ts +12 -1
  473. package/src/modules/customers/api/todos/route.ts +1 -0
  474. package/src/modules/customers/backend/config/customers/deals/page.tsx +1 -0
  475. package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +36 -21
  476. package/src/modules/customers/backend/customers/companies/[id]/page.tsx +52 -27
  477. package/src/modules/customers/backend/customers/companies/page.tsx +2 -0
  478. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +27 -5
  479. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealFormHandlers.ts +39 -7
  480. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +1 -0
  481. package/src/modules/customers/backend/customers/deals/page.tsx +18 -6
  482. package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +64 -39
  483. package/src/modules/customers/backend/customers/people/[id]/page.tsx +46 -26
  484. package/src/modules/customers/backend/customers/people/page.tsx +2 -0
  485. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +84 -24
  486. package/src/modules/customers/commands/addresses.ts +16 -14
  487. package/src/modules/customers/commands/companies.ts +3 -1
  488. package/src/modules/customers/commands/interactions.ts +50 -4
  489. package/src/modules/customers/commands/people.ts +2 -1
  490. package/src/modules/customers/commands/personCompanyLinks.ts +8 -5
  491. package/src/modules/customers/commands/pipeline-stages.ts +16 -16
  492. package/src/modules/customers/components/AddressFormatSettings.tsx +1 -0
  493. package/src/modules/customers/components/DictionarySettings.tsx +18 -13
  494. package/src/modules/customers/components/DictionarySortSettings.tsx +4 -0
  495. package/src/modules/customers/components/PipelineSettings.tsx +42 -21
  496. package/src/modules/customers/components/detail/ActivityTimeline.tsx +3 -3
  497. package/src/modules/customers/components/detail/AddressesSection.tsx +4 -0
  498. package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +2 -0
  499. package/src/modules/customers/components/detail/DealsSection.tsx +4 -0
  500. package/src/modules/customers/components/detail/EmailCardActions.tsx +5 -0
  501. package/src/modules/customers/components/detail/EntityTagsDialog.tsx +7 -0
  502. package/src/modules/customers/components/detail/ManageTagsDialog.tsx +4 -0
  503. package/src/modules/customers/components/detail/PersonCompaniesSection.tsx +4 -0
  504. package/src/modules/customers/components/detail/RoleAssignmentRow.tsx +2 -0
  505. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +23 -7
  506. package/src/modules/customers/components/detail/hooks/useInteractionMutations.ts +25 -15
  507. package/src/modules/customers/components/detail/hooks/useInteractions.ts +76 -35
  508. package/src/modules/customers/components/detail/hooks/usePersonTasks.ts +30 -17
  509. package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +2 -0
  510. package/src/modules/customers/components/detail/types.ts +1 -0
  511. package/src/modules/customers/components/formConfig.tsx +2 -0
  512. package/src/modules/customers/data/guards.ts +67 -0
  513. package/src/modules/customers/di.ts +66 -0
  514. package/src/modules/customers/i18n/de.json +2 -0
  515. package/src/modules/customers/i18n/en.json +2 -0
  516. package/src/modules/customers/i18n/es.json +2 -0
  517. package/src/modules/customers/i18n/pl.json +2 -0
  518. package/src/modules/customers/lib/todoCompatibility.ts +14 -0
  519. package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +2 -0
  520. package/src/modules/data_sync/api/options.ts +7 -4
  521. package/src/modules/data_sync/api/schedules/route.ts +9 -1
  522. package/src/modules/data_sync/backend/data-sync/page.tsx +18 -5
  523. package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +46 -19
  524. package/src/modules/data_sync/lib/sync-schedule-service.ts +11 -0
  525. package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +8 -1
  526. package/src/modules/dictionaries/api/[dictionaryId]/route.ts +23 -0
  527. package/src/modules/dictionaries/components/DictionariesManager.tsx +32 -9
  528. package/src/modules/dictionaries/components/DictionaryEntriesEditor.tsx +30 -14
  529. package/src/modules/dictionaries/i18n/de.json +1 -0
  530. package/src/modules/dictionaries/i18n/en.json +1 -0
  531. package/src/modules/dictionaries/i18n/es.json +1 -0
  532. package/src/modules/dictionaries/i18n/pl.json +1 -0
  533. package/src/modules/directory/api/organizations/route.ts +3 -0
  534. package/src/modules/directory/backend/directory/organizations/[id]/edit/page.tsx +8 -0
  535. package/src/modules/directory/backend/directory/organizations/page.tsx +10 -5
  536. package/src/modules/directory/backend/directory/tenants/[id]/edit/page.tsx +16 -5
  537. package/src/modules/directory/backend/directory/tenants/page.tsx +8 -4
  538. package/src/modules/directory/commands/organizations.ts +7 -4
  539. package/src/modules/entities/api/records.ts +99 -0
  540. package/src/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.tsx +7 -0
  541. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +8 -4
  542. package/src/modules/entities/lib/helpers.ts +17 -0
  543. package/src/modules/feature_toggles/api/global/[id]/override/route.ts +1 -0
  544. package/src/modules/feature_toggles/api/overrides/route.ts +19 -0
  545. package/src/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.tsx +19 -13
  546. package/src/modules/feature_toggles/components/FeatureToggleOverrideCard.tsx +22 -12
  547. package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +7 -2
  548. package/src/modules/feature_toggles/components/formConfig.tsx +2 -1
  549. package/src/modules/feature_toggles/components/overrideFormConfig.tsx +10 -1
  550. package/src/modules/feature_toggles/data/validators.ts +11 -3
  551. package/src/modules/inbox_ops/api/settings/route.ts +18 -0
  552. package/src/modules/inbox_ops/backend/inbox-ops/settings/page.tsx +15 -10
  553. package/src/modules/inbox_ops/components/proposals/EditActionDialog.tsx +9 -4
  554. package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +20 -11
  555. package/src/modules/integrations/backend/integrations/page.tsx +13 -8
  556. package/src/modules/messages/commands/messages.ts +27 -15
  557. package/src/modules/perspectives/api/[tableId]/route.ts +11 -2
  558. package/src/modules/perspectives/services/perspectiveService.ts +13 -1
  559. package/src/modules/planner/backend/planner/availability-rulesets/[id]/page.tsx +16 -14
  560. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +6 -3
  561. package/src/modules/planner/components/AvailabilityRuleSetForm.tsx +3 -0
  562. package/src/modules/planner/components/AvailabilityRulesEditor.tsx +58 -15
  563. package/src/modules/planner/components/AvailabilitySchedule.tsx +22 -7
  564. package/src/modules/query_index/lib/engine.ts +34 -0
  565. package/src/modules/resources/backend/resources/resource-types/[id]/edit/page.tsx +7 -1
  566. package/src/modules/resources/backend/resources/resource-types/page.tsx +6 -3
  567. package/src/modules/resources/backend/resources/resources/[id]/page.tsx +23 -3
  568. package/src/modules/resources/backend/resources/resources/page.tsx +15 -4
  569. package/src/modules/resources/components/ResourceCrudForm.tsx +3 -0
  570. package/src/modules/resources/components/ResourceTypeCrudForm.tsx +2 -0
  571. package/src/modules/sales/api/documents/factory.ts +13 -1
  572. package/src/modules/sales/backend/sales/channels/[channelId]/edit/page.tsx +6 -0
  573. package/src/modules/sales/backend/sales/channels/offers/page.tsx +10 -4
  574. package/src/modules/sales/backend/sales/channels/page.tsx +19 -4
  575. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +73 -20
  576. package/src/modules/sales/backend/sales/documents/create/page.tsx +2 -0
  577. package/src/modules/sales/commands/documentAddresses.ts +226 -4
  578. package/src/modules/sales/commands/documents.ts +28 -0
  579. package/src/modules/sales/commands/returns.ts +12 -3
  580. package/src/modules/sales/commands/shared.ts +36 -0
  581. package/src/modules/sales/commands/shipments.ts +17 -1
  582. package/src/modules/sales/components/AdjustmentKindSettings.tsx +20 -11
  583. package/src/modules/sales/components/DocumentNumberSettings.tsx +1 -0
  584. package/src/modules/sales/components/OrderEditingSettings.tsx +1 -0
  585. package/src/modules/sales/components/PaymentMethodsSettings.tsx +12 -4
  586. package/src/modules/sales/components/ShippingMethodsSettings.tsx +12 -4
  587. package/src/modules/sales/components/StatusSettings.tsx +20 -11
  588. package/src/modules/sales/components/TaxRatesSettings.tsx +12 -5
  589. package/src/modules/sales/components/channels/ChannelOfferForm.tsx +67 -14
  590. package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +7 -4
  591. package/src/modules/sales/components/documents/AddressesSection.tsx +35 -25
  592. package/src/modules/sales/components/documents/AdjustmentsSection.tsx +50 -25
  593. package/src/modules/sales/components/documents/ItemsSection.tsx +24 -13
  594. package/src/modules/sales/components/documents/LineItemDialog.tsx +26 -9
  595. package/src/modules/sales/components/documents/PaymentDialog.tsx +33 -14
  596. package/src/modules/sales/components/documents/PaymentsSection.tsx +22 -10
  597. package/src/modules/sales/components/documents/ReturnDialog.tsx +28 -17
  598. package/src/modules/sales/components/documents/ReturnsSection.tsx +4 -1
  599. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +11 -4
  600. package/src/modules/sales/components/documents/ShipmentDialog.tsx +23 -8
  601. package/src/modules/sales/components/documents/ShipmentsSection.tsx +20 -10
  602. package/src/modules/sales/components/documents/optimisticLock.ts +34 -0
  603. package/src/modules/sales/components/documents/shipmentTypes.ts +1 -0
  604. package/src/modules/sales/di.ts +35 -0
  605. package/src/modules/sales/i18n/de.json +3 -0
  606. package/src/modules/sales/i18n/en.json +3 -0
  607. package/src/modules/sales/i18n/es.json +3 -0
  608. package/src/modules/sales/i18n/pl.json +3 -0
  609. package/src/modules/staff/api/job-histories.ts +12 -2
  610. package/src/modules/staff/api/timesheets/time-entries/route.ts +16 -4
  611. package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +12 -7
  612. package/src/modules/staff/backend/staff/my-leave-requests/[id]/page.tsx +2 -0
  613. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +16 -5
  614. package/src/modules/staff/backend/staff/team-members/page.tsx +6 -2
  615. package/src/modules/staff/backend/staff/team-roles/[id]/edit/page.tsx +8 -0
  616. package/src/modules/staff/backend/staff/team-roles/page.tsx +6 -2
  617. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +13 -3
  618. package/src/modules/staff/backend/staff/teams/page.tsx +9 -3
  619. package/src/modules/staff/backend/staff/timesheets/page.tsx +10 -1
  620. package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.tsx +4 -0
  621. package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +9 -3
  622. package/src/modules/staff/commands/job-histories.ts +42 -3
  623. package/src/modules/staff/components/LeaveRequestForm.tsx +2 -0
  624. package/src/modules/staff/components/TeamForm.tsx +2 -0
  625. package/src/modules/staff/components/TeamMemberForm.tsx +2 -0
  626. package/src/modules/staff/components/TeamRoleForm.tsx +2 -0
  627. package/src/modules/staff/components/detail/JobHistorySection.tsx +28 -6
  628. package/src/modules/staff/data/validators.ts +6 -0
  629. package/src/modules/staff/i18n/de.json +1 -0
  630. package/src/modules/staff/i18n/en.json +1 -0
  631. package/src/modules/staff/i18n/es.json +1 -0
  632. package/src/modules/staff/i18n/pl.json +1 -0
  633. package/src/modules/staff/lib/leaveRequestHelpers.ts +4 -0
  634. package/src/modules/translations/components/TranslationManager.tsx +13 -8
  635. package/src/modules/workflows/api/definitions/[id]/route.ts +112 -0
  636. package/src/modules/workflows/backend/definitions/[id]/page.tsx +20 -4
  637. package/src/modules/workflows/backend/definitions/page.tsx +20 -9
  638. package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +29 -16
  639. package/src/modules/workflows/components/formConfig.tsx +5 -0
  640. package/src/modules/workflows/di.ts +20 -0
  641. package/src/modules/workflows/i18n/de.json +1 -0
  642. package/src/modules/workflows/i18n/en.json +1 -0
  643. package/src/modules/workflows/i18n/es.json +1 -0
  644. package/src/modules/workflows/i18n/pl.json +1 -0
@@ -12,7 +12,7 @@ import { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/
12
12
  import { SalesDocumentNumberGenerator } from '../services/salesDocumentNumberGenerator'
13
13
  import type { SalesCalculationService } from '../services/salesCalculationService'
14
14
  import type { SalesAdjustmentDraft, SalesLineSnapshot, SalesDocumentCalculationResult } from '../lib/types'
15
- import { cloneJson, ensureOrganizationScope, ensureSameScope, ensureTenantScope, extractUndoPayload, toNumericString } from './shared'
15
+ import { cloneJson, ensureOrganizationScope, ensureSameScope, ensureTenantScope, extractUndoPayload, toNumericString, enforceSalesDocumentOptimisticLock, SALES_RESOURCE_KIND_ORDER } from './shared'
16
16
  import { SalesOrder, SalesOrderAdjustment, SalesOrderLine, SalesReturn, SalesReturnLine } from '../data/entities'
17
17
  import { returnCreateSchema, type ReturnCreateInput } from '../data/validators'
18
18
  import { E } from '#generated/entities.ids.generated'
@@ -286,6 +286,7 @@ const createReturnCommand: CommandHandler<ReturnCreateInput, { returnId: string
286
286
  throw new CrudHttpError(404, { error: translate('sales.returns.orderMissing', 'Order not found.') })
287
287
  }
288
288
  ensureSameScope(order, input.organizationId, input.tenantId)
289
+ enforceSalesDocumentOptimisticLock(ctx, order, SALES_RESOURCE_KIND_ORDER)
289
290
 
290
291
  const orderLines = await findWithDecryption(
291
292
  tx,
@@ -476,11 +477,12 @@ const createReturnCommand: CommandHandler<ReturnCreateInput, { returnId: string
476
477
  // Line reversals, adjustment/return removals, and the order-total recompute
477
478
  // interleave queries on the same EntityManager with scalar mutations, so they
478
479
  // must run inside an atomic flush to avoid lost updates and partial commits.
480
+ let lines: SalesOrderLine[] = []
479
481
  await withAtomicFlush(
480
482
  em,
481
483
  [
482
484
  async () => {
483
- const lines = await findWithDecryption(
485
+ lines = await findWithDecryption(
484
486
  em,
485
487
  SalesOrderLine,
486
488
  { order: order.id, deletedAt: null },
@@ -496,7 +498,14 @@ const createReturnCommand: CommandHandler<ReturnCreateInput, { returnId: string
496
498
  line.updatedAt = new Date()
497
499
  em.persist(line)
498
500
  })
499
-
501
+ },
502
+ // The line returnedQuantity reversals above are persisted by
503
+ // withAtomicFlush's per-phase flush boundary before the adjustment /
504
+ // header / return-line lookups below run any query on this
505
+ // EntityManager. MikroORM v7 would otherwise silently discard the pending
506
+ // scalar changes on the managed `lines` when the next read resets the
507
+ // changeset (see SPEC-018).
508
+ async () => {
500
509
  if (after.adjustmentIds.length) {
501
510
  const adjustments = await findWithDecryption(
502
511
  em,
@@ -2,10 +2,46 @@ import type { EntityManager } from '@mikro-orm/postgresql'
2
2
  import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
3
3
  import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
4
4
  import type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'
5
+ import { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
5
6
  export { assertFound } from '@open-mercato/shared/lib/crud/errors'
6
7
  export { ensureOrganizationScope, ensureSameScope, ensureTenantScope } from '@open-mercato/shared/lib/commands/scope'
7
8
  export { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'
8
9
 
10
+ /** Resource kinds used by the document-aggregate optimistic-lock check. */
11
+ export const SALES_RESOURCE_KIND_ORDER = 'sales.order'
12
+ export const SALES_RESOURCE_KIND_QUOTE = 'sales.quote'
13
+
14
+ /**
15
+ * Enforce the document-aggregate OSS optimistic lock for a sales sub-resource
16
+ * command (lines, adjustments, shipments, payments, returns, quote
17
+ * conversion). The client sends the parent order/quote's expected `updated_at`
18
+ * via the optimistic-lock extension header; this compares it against the
19
+ * already-loaded document and throws the structured 409 on mismatch.
20
+ *
21
+ * The parent document is the consistency boundary: sub-resource mutations
22
+ * recalculate document totals (or transition the document), which dirties the
23
+ * parent so its `updated_at` advances on flush — meaning concurrent sub-edits
24
+ * observe each other and conflict. Call this AFTER loading + scope-checking the
25
+ * document and BEFORE mutating, so `document.updatedAt` is the pre-mutation
26
+ * version.
27
+ *
28
+ * Strictly additive: when the client sends no header the check is a no-op, so
29
+ * existing API consumers are unaffected. Respects `OM_OPTIMISTIC_LOCK`.
30
+ */
31
+ export function enforceSalesDocumentOptimisticLock(
32
+ ctx: CommandRuntimeContext,
33
+ document: { id: string; updatedAt?: Date | string | null } | null | undefined,
34
+ resourceKind: string,
35
+ ): void {
36
+ if (!document) return
37
+ enforceCommandOptimisticLock({
38
+ resourceKind,
39
+ resourceId: document.id,
40
+ current: document.updatedAt ?? null,
41
+ request: ctx.request ?? null,
42
+ })
43
+ }
44
+
9
45
  export function cloneJson<T>(value: T): T {
10
46
  if (value === null || value === undefined) return value
11
47
  return JSON.parse(JSON.stringify(value)) as T
@@ -657,11 +657,17 @@ const updateShipmentCommand: CommandHandler<ShipmentUpdateInput, { shipmentId: s
657
657
  : null
658
658
  const normalizedItems = validatedItems?.items ?? null
659
659
  const lineMap = validatedItems?.lineMap ?? new Map<string, SalesOrderLine>()
660
+ // Resolve the status label BEFORE mutating shipment scalars so this
661
+ // dictionary read does not interleave with (and drop) the pending
662
+ // shipment changeset under MikroORM v7 (SPEC-018 / #2453 class).
663
+ const resolvedShipmentStatus = input.statusEntryId !== undefined
664
+ ? await resolveDictionaryEntryValue(tx, input.statusEntryId ?? null)
665
+ : undefined
660
666
  if (input.shipmentNumber !== undefined) shipmentEntity.shipmentNumber = input.shipmentNumber ?? null
661
667
  if (input.shippingMethodId !== undefined) shipmentEntity.shippingMethodId = input.shippingMethodId ?? null
662
668
  if (input.statusEntryId !== undefined) {
663
669
  shipmentEntity.statusEntryId = input.statusEntryId ?? null
664
- shipmentEntity.status = await resolveDictionaryEntryValue(tx, input.statusEntryId ?? null)
670
+ shipmentEntity.status = resolvedShipmentStatus ?? null
665
671
  }
666
672
  if (input.carrierName !== undefined) shipmentEntity.carrierName = input.carrierName ?? null
667
673
  if (input.trackingNumbers !== undefined) shipmentEntity.trackingNumbers = parseTrackingNumbers(input.trackingNumbers)
@@ -685,6 +691,13 @@ const updateShipmentCommand: CommandHandler<ShipmentUpdateInput, { shipmentId: s
685
691
  }
686
692
  shipmentEntity.updatedAt = new Date()
687
693
 
694
+ // Persist the shipment scalar mutations before the item reads below run on
695
+ // the same EntityManager. Under MikroORM v7 an interleaved query (the
696
+ // findWithDecryption on SalesShipmentItem here and in the snapshot step)
697
+ // resets the identity-map changeset, so shippingMethodId/status/carrier/etc.
698
+ // would silently not persist even though the write returns 200 (#2453).
699
+ await tx.flush()
700
+
688
701
  const shouldLoadItems = Boolean(normalizedItems || input.lineStatusEntryId !== undefined)
689
702
  const existingItems = shouldLoadItems ? await findWithDecryption(tx, SalesShipmentItem, { shipment: shipmentEntity }, {}, { tenantId: shipmentEntity.tenantId, organizationId: shipmentEntity.organizationId }) : []
690
703
  const newItems: SalesShipmentItem[] = []
@@ -758,6 +771,9 @@ const updateShipmentCommand: CommandHandler<ShipmentUpdateInput, { shipmentId: s
758
771
  }
759
772
  }
760
773
 
774
+ // Flush any order/line status mutations applied above before the snapshot
775
+ // read below queries the same EntityManager (same interleaved-read hazard).
776
+ await tx.flush()
761
777
  const itemsForSnapshot =
762
778
  normalizedItems || shouldLoadItems
763
779
  ? (normalizedItems ? newItems : existingItems)
@@ -16,7 +16,8 @@ import {
16
16
  import { Input } from '@open-mercato/ui/primitives/input'
17
17
  import { Label } from '@open-mercato/ui/primitives/label'
18
18
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
19
- import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
19
+ import { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
20
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
20
21
  import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
21
22
  import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
22
23
  import { AppearanceSelector } from '@open-mercato/core/modules/dictionaries/components/AppearanceSelector'
@@ -219,11 +220,15 @@ export function AdjustmentKindSettings() {
219
220
  ? JSON.stringify(payload)
220
221
  : JSON.stringify({ id: dialog.entry.id, ...payload })
221
222
 
222
- const call = await apiCall('/api/sales/adjustment-kinds', {
223
- method,
224
- headers: { 'content-type': 'application/json' },
225
- body,
226
- })
223
+ const lockHeader =
224
+ dialog.mode === 'edit' ? buildOptimisticLockHeader(dialog.entry.updatedAt) : {}
225
+ const call = await withScopedApiRequestHeaders(lockHeader, () =>
226
+ apiCall('/api/sales/adjustment-kinds', {
227
+ method,
228
+ headers: { 'content-type': 'application/json' },
229
+ body,
230
+ })
231
+ )
227
232
  if (!call.ok) {
228
233
  await raiseCrudError(call.response, labels.saveError)
229
234
  }
@@ -248,11 +253,15 @@ export function AdjustmentKindSettings() {
248
253
  })
249
254
  if (!confirmed) return
250
255
  try {
251
- const call = await apiCall('/api/sales/adjustment-kinds', {
252
- method: 'DELETE',
253
- headers: { 'content-type': 'application/json' },
254
- body: JSON.stringify({ id: entry.id }),
255
- })
256
+ const call = await withScopedApiRequestHeaders(
257
+ buildOptimisticLockHeader(entry.updatedAt),
258
+ () =>
259
+ apiCall('/api/sales/adjustment-kinds', {
260
+ method: 'DELETE',
261
+ headers: { 'content-type': 'application/json' },
262
+ body: JSON.stringify({ id: entry.id }),
263
+ })
264
+ )
256
265
  if (!call.ok) {
257
266
  await raiseCrudError(call.response, labels.deleteError)
258
267
  }
@@ -128,6 +128,7 @@ export function DocumentNumberSettings() {
128
128
  orderNextNumber: Number.parseInt(formState.orderNextNumber, 10) || undefined,
129
129
  quoteNextNumber: Number.parseInt(formState.quoteNextNumber, 10) || undefined,
130
130
  }
131
+ // optimistic-lock-exempt: single-row tenant numbering settings blob — no per-record version / concurrent record edit
131
132
  const call = await apiCall<SettingsResponse>('/api/sales/settings/document-numbers', {
132
133
  method: 'PUT',
133
134
  headers: { 'content-type': 'application/json' },
@@ -121,6 +121,7 @@ export function OrderEditingSettings() {
121
121
  orderCustomerEditableStatuses: customerStatuses,
122
122
  orderAddressEditableStatuses: addressStatuses,
123
123
  }
124
+ // optimistic-lock-exempt: single-row tenant order-editing settings blob — no per-record version / concurrent record edit
124
125
  const call = await apiCall<SettingsResponse>('/api/sales/settings/order-editing', {
125
126
  method: 'PUT',
126
127
  headers: { 'content-type': 'application/json' },
@@ -15,7 +15,9 @@ import {
15
15
  import { Label } from '@open-mercato/ui/primitives/label'
16
16
  import { CrudForm, type CrudField, type CrudCustomFieldRenderProps } from '@open-mercato/ui/backend/CrudForm'
17
17
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
18
- import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
18
+ import { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
19
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
20
+ import { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'
19
21
  import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
20
22
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
21
23
  import { useT } from '@open-mercato/shared/lib/i18n/context'
@@ -291,12 +293,14 @@ export function PaymentMethodsSettings() {
291
293
  })
292
294
  if (!confirmed) return
293
295
  try {
294
- const call = await apiCall('/api/sales/payment-methods', {
296
+ const headers = buildOptimisticLockHeader(entry.updatedAt)
297
+ const call = await withScopedApiRequestHeaders(headers, () => apiCall('/api/sales/payment-methods', {
295
298
  method: 'DELETE',
296
299
  headers: { 'content-type': 'application/json' },
297
300
  body: JSON.stringify({ id: entry.id }),
298
- })
301
+ }))
299
302
  if (!call.ok) {
303
+ if (surfaceRecordConflict({ status: call.status, body: call.result }, t)) return
300
304
  await raiseCrudError(call.response, translations.errors.delete)
301
305
  }
302
306
  flash(translations.messages.deleted, 'success')
@@ -325,12 +329,15 @@ export function PaymentMethodsSettings() {
325
329
  const method = dialog.mode === 'create' ? 'POST' : 'PUT'
326
330
  if (dialog.mode === 'edit') payload.id = dialog.entry.id
327
331
  try {
328
- const call = await apiCall(path, {
332
+ const savePaymentMethod = () => apiCall(path, {
329
333
  method,
330
334
  headers: { 'content-type': 'application/json' },
331
335
  body: JSON.stringify(payload),
332
336
  })
337
+ const headers = buildOptimisticLockHeader(dialog.mode === 'edit' ? dialog.entry.updatedAt : null)
338
+ const call = await withScopedApiRequestHeaders(headers, savePaymentMethod)
333
339
  if (!call.ok) {
340
+ if (surfaceRecordConflict({ status: call.status, body: call.result }, t)) return
334
341
  await raiseCrudError(call.response, translations.errors.save)
335
342
  }
336
343
  flash(translations.messages.saved, 'success')
@@ -466,6 +473,7 @@ export function PaymentMethodsSettings() {
466
473
  schema={paymentFormSchema}
467
474
  fields={fields}
468
475
  initialValues={formValues}
476
+ optimisticLockUpdatedAt={dialog?.mode === 'edit' ? dialog.entry.updatedAt : null}
469
477
  submitLabel={translations.form.save}
470
478
  onSubmit={handleSubmit}
471
479
  cancelHref={undefined}
@@ -24,7 +24,9 @@ import { Label } from '@open-mercato/ui/primitives/label'
24
24
  import { SwitchField } from '@open-mercato/ui/primitives/switch-field'
25
25
  import { CrudForm, type CrudCustomFieldRenderProps, type CrudField } from '@open-mercato/ui/backend/CrudForm'
26
26
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
27
- import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
27
+ import { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
28
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
29
+ import { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'
28
30
  import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
29
31
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
30
32
  import { useT } from '@open-mercato/shared/lib/i18n/context'
@@ -549,12 +551,14 @@ export function ShippingMethodsSettings() {
549
551
  })
550
552
  if (!confirmed) return
551
553
  try {
552
- const call = await apiCall('/api/sales/shipping-methods', {
554
+ const headers = buildOptimisticLockHeader(entry.updatedAt)
555
+ const call = await withScopedApiRequestHeaders(headers, () => apiCall('/api/sales/shipping-methods', {
553
556
  method: 'DELETE',
554
557
  headers: { 'content-type': 'application/json' },
555
558
  body: JSON.stringify({ id: entry.id }),
556
- })
559
+ }))
557
560
  if (!call.ok) {
561
+ if (surfaceRecordConflict({ status: call.status, body: call.result }, t)) return
558
562
  await raiseCrudError(call.response, translations.errors.delete)
559
563
  }
560
564
  flash(translations.messages.deleted, 'success')
@@ -642,12 +646,15 @@ export function ShippingMethodsSettings() {
642
646
  const method = dialog.mode === 'create' ? 'POST' : 'PUT'
643
647
  if (dialog.mode === 'edit') payload.id = dialog.entry.id
644
648
  try {
645
- const call = await apiCall(path, {
649
+ const saveShippingMethod = () => apiCall(path, {
646
650
  method,
647
651
  headers: { 'content-type': 'application/json' },
648
652
  body: JSON.stringify(payload),
649
653
  })
654
+ const headers = buildOptimisticLockHeader(dialog.mode === 'edit' ? dialog.entry.updatedAt : null)
655
+ const call = await withScopedApiRequestHeaders(headers, saveShippingMethod)
650
656
  if (!call.ok) {
657
+ if (surfaceRecordConflict({ status: call.status, body: call.result }, t)) return
651
658
  await raiseCrudError(call.response, translations.errors.save)
652
659
  }
653
660
  flash(translations.messages.saved, 'success')
@@ -770,6 +777,7 @@ export function ShippingMethodsSettings() {
770
777
  schema={shippingFormSchema}
771
778
  fields={fields}
772
779
  initialValues={formValues}
780
+ optimisticLockUpdatedAt={dialog?.mode === 'edit' ? dialog.entry.updatedAt : null}
773
781
  submitLabel={translations.form.save}
774
782
  cancelHref={undefined}
775
783
  embedded
@@ -8,7 +8,8 @@ import {
8
8
  DialogTitle,
9
9
  } from '@open-mercato/ui/primitives/dialog'
10
10
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
11
- import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
11
+ import { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
12
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
12
13
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
13
14
  import { useT } from '@open-mercato/shared/lib/i18n/context'
14
15
  import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
@@ -197,11 +198,15 @@ export function StatusSettings() {
197
198
  })
198
199
  if (!confirmed) return
199
200
  try {
200
- const call = await apiCall(apiPaths[kind], {
201
- method: 'DELETE',
202
- headers: { 'content-type': 'application/json' },
203
- body: JSON.stringify({ id: entry.id }),
204
- })
201
+ const call = await withScopedApiRequestHeaders(
202
+ buildOptimisticLockHeader(entry.updatedAt),
203
+ () =>
204
+ apiCall(apiPaths[kind], {
205
+ method: 'DELETE',
206
+ headers: { 'content-type': 'application/json' },
207
+ body: JSON.stringify({ id: entry.id }),
208
+ })
209
+ )
205
210
  if (!call.ok) {
206
211
  await raiseCrudError(call.response, translate('sales.config.statuses.error.delete', 'Failed to delete status.'))
207
212
  }
@@ -238,11 +243,15 @@ export function StatusSettings() {
238
243
  if (nextColor !== (entry.color ?? null)) body.color = nextColor
239
244
  const nextIcon = values.icon ?? null
240
245
  if (nextIcon !== (entry.icon ?? null)) body.icon = nextIcon
241
- const call = await apiCall(path, {
242
- method: 'PUT',
243
- headers: { 'content-type': 'application/json' },
244
- body: JSON.stringify(body),
245
- })
246
+ const call = await withScopedApiRequestHeaders(
247
+ buildOptimisticLockHeader(entry.updatedAt),
248
+ () =>
249
+ apiCall(path, {
250
+ method: 'PUT',
251
+ headers: { 'content-type': 'application/json' },
252
+ body: JSON.stringify(body),
253
+ })
254
+ )
246
255
  if (!call.ok) {
247
256
  await raiseCrudError(call.response, translate('sales.config.statuses.error.save', 'Failed to save status.'))
248
257
  }
@@ -10,7 +10,9 @@ import { Badge } from '@open-mercato/ui/primitives/badge'
10
10
  import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@open-mercato/ui/primitives/dialog'
11
11
  import { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'
12
12
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
13
- import { readApiResultOrThrow, apiCall } from '@open-mercato/ui/backend/utils/apiCall'
13
+ import { readApiResultOrThrow, apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
14
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
15
+ import { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'
14
16
  import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
15
17
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
16
18
  import { useT } from '@open-mercato/shared/lib/i18n/context'
@@ -206,12 +208,15 @@ export function TaxRatesSettings() {
206
208
  payload.id = dialog.entry.id
207
209
  }
208
210
  try {
209
- const call = await apiCall('/api/sales/tax-rates', {
211
+ const saveTaxRate = () => apiCall('/api/sales/tax-rates', {
210
212
  method,
211
213
  headers: { 'content-type': 'application/json' },
212
214
  body: JSON.stringify(payload),
213
215
  })
216
+ const headers = buildOptimisticLockHeader(dialog.mode === 'edit' ? dialog.entry.updatedAt : null)
217
+ const call = await withScopedApiRequestHeaders(headers, saveTaxRate)
214
218
  if (!call.ok) {
219
+ if (surfaceRecordConflict({ status: call.status, body: call.result }, t)) return
215
220
  await raiseCrudError(call.response, translations.errors.save)
216
221
  return
217
222
  }
@@ -232,12 +237,14 @@ export function TaxRatesSettings() {
232
237
  })
233
238
  if (!confirmed) return
234
239
  try {
235
- const call = await apiCall('/api/sales/tax-rates', {
240
+ const headers = buildOptimisticLockHeader(entry.updatedAt)
241
+ const call = await withScopedApiRequestHeaders(headers, () => apiCall('/api/sales/tax-rates', {
236
242
  method: 'DELETE',
237
243
  headers: { 'content-type': 'application/json' },
238
244
  body: JSON.stringify({ id: entry.id }),
239
- })
245
+ }))
240
246
  if (!call.ok) {
247
+ if (surfaceRecordConflict({ status: call.status, body: call.result }, t)) return
241
248
  await raiseCrudError(call.response, translations.errors.delete)
242
249
  return
243
250
  }
@@ -361,6 +368,7 @@ export function TaxRatesSettings() {
361
368
  schema={taxRateFormSchema}
362
369
  fields={fields}
363
370
  initialValues={dialogValues}
371
+ optimisticLockUpdatedAt={dialog?.mode === 'edit' ? dialog.entry.updatedAt : null}
364
372
  submitLabel={translations.form.save}
365
373
  cancelHref={undefined}
366
374
  embedded
@@ -387,4 +395,3 @@ function formatLocation(entry: TaxRateRow): string {
387
395
  return parts.length ? parts.join(', ') : '—'
388
396
  }
389
397
 
390
-
@@ -7,7 +7,9 @@ import { CrudForm, type CrudField, type CrudFormGroup, type CrudFormGroupCompone
7
7
  import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'
8
8
  import { createCrud, updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'
9
9
  import { createCrudFormError, type CrudServerFieldErrors } from '@open-mercato/ui/backend/utils/serverErrors'
10
- import { readApiResultOrThrow, apiCall } from '@open-mercato/ui/backend/utils/apiCall'
10
+ import { readApiResultOrThrow, apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
11
+ import { buildOptimisticLockHeader, extractOptimisticLockConflict } from '@open-mercato/ui/backend/utils/optimisticLock'
12
+ import { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'
11
13
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
12
14
  import { Button } from '@open-mercato/ui/primitives/button'
13
15
  import { Input } from '@open-mercato/ui/primitives/input'
@@ -41,6 +43,8 @@ type PriceOverrideDraft = {
41
43
  currencyCode?: string | null
42
44
  displayMode?: 'including-tax' | 'excluding-tax' | null
43
45
  amount?: string
46
+ /** The price row's version, for the per-price optimistic-lock header (#2332). */
47
+ updatedAt?: string | null
44
48
  }
45
49
 
46
50
  export type OfferFormValues = {
@@ -51,6 +55,7 @@ export type OfferFormValues = {
51
55
  defaultMediaId?: string | null
52
56
  isActive: boolean
53
57
  priceOverrides: PriceOverrideDraft[]
58
+ updatedAt?: string | null
54
59
  } & Record<string, unknown>
55
60
 
56
61
  type ChannelOfferFormProps = {
@@ -141,13 +146,15 @@ export function ChannelOfferForm({ channelId: lockedChannelId, offerId, mode }:
141
146
  const variantMediaCache = React.useRef<Map<string, VariantThumbnailInfo>>(new Map())
142
147
  const [selectedChannelId, setSelectedChannelId] = React.useState<string | null>(lockedChannelId ?? null)
143
148
  const manualMediaSelections = React.useRef<Set<string>>(new Set())
144
- const initialPriceIdsRef = React.useRef<Set<string>>(new Set())
149
+ // Map of the loaded price-override id → its `updatedAt`, so deletes during the
150
+ // offer save can send the price's own optimistic-lock version (#2332).
151
+ const initialPriceVersionsRef = React.useRef<Map<string, string | null>>(new Map())
145
152
  const [currentProductId, setCurrentProductId] = React.useState<string | null>(null)
146
153
  React.useEffect(() => {
147
154
  if (initialValues) {
148
- initialPriceIdsRef.current = collectPriceIds(initialValues.priceOverrides)
155
+ initialPriceVersionsRef.current = collectPriceVersions(initialValues.priceOverrides)
149
156
  } else {
150
- initialPriceIdsRef.current = new Set()
157
+ initialPriceVersionsRef.current = new Map()
151
158
  }
152
159
  }, [initialValues])
153
160
  const channelOffersHref = React.useMemo(
@@ -438,12 +445,16 @@ export function ChannelOfferForm({ channelId: lockedChannelId, offerId, mode }:
438
445
  if (!priceId) return true
439
446
  if (mode !== 'edit') return true
440
447
  try {
441
- await deleteCrud('catalog/prices', priceId, {
442
- errorMessage: t('sales.channels.offers.errors.removePrice', 'Failed to remove price override.'),
443
- })
444
- initialPriceIdsRef.current.delete(priceId)
448
+ await withScopedApiRequestHeaders(
449
+ buildOptimisticLockHeader(draft.updatedAt),
450
+ () => deleteCrud('catalog/prices', priceId, {
451
+ errorMessage: t('sales.channels.offers.errors.removePrice', 'Failed to remove price override.'),
452
+ }),
453
+ )
454
+ initialPriceVersionsRef.current.delete(priceId)
445
455
  return true
446
456
  } catch (err) {
457
+ if (surfaceRecordConflict(err, t)) return false
447
458
  console.error('sales.channels.pricing.remove', err)
448
459
  flash(t('sales.channels.offers.errors.removePrice', 'Failed to remove price override.'), 'error')
449
460
  return false
@@ -594,10 +605,11 @@ export function ChannelOfferForm({ channelId: lockedChannelId, offerId, mode }:
594
605
  }
595
606
  const submittedPriceIds = collectPriceIds(overrides)
596
607
  const deletedIdSet = new Set<string>()
597
- initialPriceIdsRef.current.forEach((id) => {
608
+ initialPriceVersionsRef.current.forEach((_version, id) => {
598
609
  if (!submittedPriceIds.has(id)) deletedIdSet.add(id)
599
610
  })
600
611
  const deletedIds = Array.from(deletedIdSet)
612
+ const deletedVersions = new Map(deletedIds.map((id) => [id, initialPriceVersionsRef.current.get(id) ?? null]))
601
613
  let savedId = offerId ?? null
602
614
  try {
603
615
  if (mode === 'create') {
@@ -612,6 +624,13 @@ export function ChannelOfferForm({ channelId: lockedChannelId, offerId, mode }:
612
624
  savedId = offerId
613
625
  }
614
626
  } catch (err) {
627
+ // Let an optimistic-lock 409 propagate untouched so CrudForm's
628
+ // extractOptimisticLockConflict still sees the top-level `code` /
629
+ // `currentUpdatedAt` / `expectedUpdatedAt` and surfaces the unified
630
+ // conflict bar. Re-wrapping via createCrudFormError below would drop them.
631
+ if (extractOptimisticLockConflict(err)) {
632
+ throw err
633
+ }
615
634
  const details = (err as { details?: unknown })?.details
616
635
  const rawFieldErrors = (err as { fieldErrors?: unknown })?.fieldErrors
617
636
  const fieldErrors = rawFieldErrors && typeof rawFieldErrors === 'object'
@@ -634,15 +653,16 @@ export function ChannelOfferForm({ channelId: lockedChannelId, offerId, mode }:
634
653
  await syncPriceOverrides({
635
654
  overrides,
636
655
  deletedIds,
656
+ deletedVersions,
637
657
  offerId: savedId,
638
658
  channelId,
639
659
  productId,
640
660
  })
641
- initialPriceIdsRef.current = submittedPriceIds
661
+ initialPriceVersionsRef.current = collectPriceVersions(overrides)
642
662
  }
643
663
  flash(t('sales.channels.offers.messages.saved', 'Offer saved.'), 'success')
644
664
  router.push(buildChannelOffersHref(channelId))
645
- }, [attachmentCache, initialPriceIdsRef, lockedChannelId, mode, offerId, router, selectedChannelId, t])
665
+ }, [attachmentCache, lockedChannelId, mode, offerId, router, selectedChannelId, t])
646
666
 
647
667
  const handleDelete = React.useCallback(async () => {
648
668
  if (!offerId) return
@@ -669,6 +689,7 @@ export function ChannelOfferForm({ channelId: lockedChannelId, offerId, mode }:
669
689
  fields={fields}
670
690
  groups={groups}
671
691
  initialValues={initialValues ?? undefined}
692
+ optimisticLockUpdatedAt={initialValues?.updatedAt}
672
693
  isLoading={loading}
673
694
  loadingMessage={t('sales.channels.offers.form.loading', 'Loading offer…')}
674
695
  submitLabel={mode === 'create'
@@ -706,6 +727,11 @@ function mapOfferToFormValues(item: Record<string, unknown>, lockedChannelId?: s
706
727
  : null,
707
728
  isActive: item.isActive === true || item.is_active === true,
708
729
  priceOverrides: [],
730
+ updatedAt: typeof item.updatedAt === 'string'
731
+ ? item.updatedAt
732
+ : typeof item.updated_at === 'string'
733
+ ? item.updated_at
734
+ : null,
709
735
  }
710
736
  mergeCustomFieldValues(values, item)
711
737
  return values
@@ -742,6 +768,11 @@ function mapPriceRow(row: Record<string, unknown>): PriceOverrideDraft {
742
768
  : typeof row.unit_price_gross === 'string'
743
769
  ? row.unit_price_gross
744
770
  : '',
771
+ updatedAt: typeof row.updatedAt === 'string'
772
+ ? row.updatedAt
773
+ : typeof row.updated_at === 'string'
774
+ ? row.updated_at
775
+ : null,
745
776
  }
746
777
  }
747
778
 
@@ -753,6 +784,17 @@ function collectPriceIds(source: PriceOverrideDraft[] | null | undefined): Set<s
753
784
  return new Set(ids)
754
785
  }
755
786
 
787
+ function collectPriceVersions(source: PriceOverrideDraft[] | null | undefined): Map<string, string | null> {
788
+ const versions = new Map<string, string | null>()
789
+ if (!Array.isArray(source)) return versions
790
+ for (const entry of source) {
791
+ if (typeof entry?.priceId === 'string' && entry.priceId) {
792
+ versions.set(entry.priceId, typeof entry.updatedAt === 'string' ? entry.updatedAt : null)
793
+ }
794
+ }
795
+ return versions
796
+ }
797
+
756
798
  function mergeCustomFieldValues(target: Record<string, unknown>, source: Record<string, unknown> | null | undefined) {
757
799
  Object.assign(target, extractCustomFieldEntries(source ?? {}))
758
800
  }
@@ -766,11 +808,12 @@ function buildChannelOffersHref(channelId?: string | null): string {
766
808
  async function syncPriceOverrides(params: {
767
809
  overrides: PriceOverrideDraft[]
768
810
  deletedIds: string[]
811
+ deletedVersions: Map<string, string | null>
769
812
  offerId: string
770
813
  channelId: string
771
814
  productId: string
772
815
  }) {
773
- const { overrides, deletedIds, offerId, channelId, productId } = params
816
+ const { overrides, deletedIds, deletedVersions, offerId, channelId, productId } = params
774
817
  for (const draft of overrides) {
775
818
  if (!draft.priceKindId || !draft.amount) continue
776
819
  const amount = Number(draft.amount)
@@ -788,7 +831,14 @@ async function syncPriceOverrides(params: {
788
831
  payload.unitPriceNet = amount
789
832
  }
790
833
  if (draft.priceId) {
791
- await updateCrud('catalog/prices', { id: draft.priceId, ...payload })
834
+ // Send the PRICE's own version, overriding the offer header the parent
835
+ // CrudForm submit scope put on the stack (otherwise the price guard would
836
+ // compare the offer's `updated_at` and 409 falsely). #2332.
837
+ const priceId = draft.priceId
838
+ await withScopedApiRequestHeaders(
839
+ buildOptimisticLockHeader(draft.updatedAt),
840
+ () => updateCrud('catalog/prices', { id: priceId, ...payload }),
841
+ )
792
842
  } else {
793
843
  await createCrud('catalog/prices', payload)
794
844
  }
@@ -799,7 +849,10 @@ async function syncPriceOverrides(params: {
799
849
  for (const id of uniqueDeletedIds) {
800
850
  if (!id) continue
801
851
  try {
802
- await deleteCrud('catalog/prices', id)
852
+ await withScopedApiRequestHeaders(
853
+ buildOptimisticLockHeader(deletedVersions.get(id) ?? null),
854
+ () => deleteCrud('catalog/prices', id),
855
+ )
803
856
  } catch (err) {
804
857
  console.error('catalog.prices.delete', err)
805
858
  }