@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
@@ -19,6 +19,8 @@ export type LinkedEntity = {
19
19
 
20
20
  export type ScheduleActivityEditData = {
21
21
  id: string
22
+ /** Record version for the OSS optimistic-lock header on edit (#2055). */
23
+ updatedAt?: string | null
22
24
  interactionType?: string
23
25
  title?: string | null
24
26
  body?: string | null
@@ -113,6 +113,7 @@ export type TodoLinkSummary = {
113
113
  description?: string | null
114
114
  dueAt?: string | null
115
115
  todoOrganizationId?: string | null
116
+ updatedAt?: string | null
116
117
  customValues?: Record<string, unknown> | null
117
118
  externalHref?: string | null
118
119
  }
@@ -1897,6 +1897,8 @@ export type PersonOverview = {
1897
1897
  nextInteractionIcon?: string | null
1898
1898
  nextInteractionColor?: string | null
1899
1899
  organizationId?: string | null
1900
+ updatedAt?: string | null
1901
+ updated_at?: string | null
1900
1902
  }
1901
1903
  profile: {
1902
1904
  id: string
@@ -0,0 +1,67 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+ import type { MutationGuard } from '@open-mercato/shared/lib/crud/mutation-guard-registry'
3
+ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
4
+ import { parseOptimisticLockEnv } from '@open-mercato/shared/lib/crud/optimistic-lock'
5
+ import { getAllOptimisticLockReaders } from '@open-mercato/shared/lib/crud/optimistic-lock-store'
6
+ import {
7
+ OPTIMISTIC_LOCK_CONFLICT_CODE,
8
+ OPTIMISTIC_LOCK_CONFLICT_ERROR,
9
+ OPTIMISTIC_LOCK_ENV_VAR,
10
+ OPTIMISTIC_LOCK_HEADER_NAME,
11
+ } from '@open-mercato/shared/lib/crud/optimistic-lock-headers'
12
+
13
+ function normalizeIsoToken(raw: string): string | null {
14
+ const ms = Date.parse(raw)
15
+ if (!Number.isFinite(ms)) return null
16
+ return new Date(ms).toISOString()
17
+ }
18
+
19
+ const optimisticLockGuard: MutationGuard = {
20
+ id: 'customers.optimistic-lock',
21
+ targetEntity: '*',
22
+ operations: ['update', 'delete'],
23
+ priority: 100,
24
+ async validate(input) {
25
+ const config = parseOptimisticLockEnv(process.env[OPTIMISTIC_LOCK_ENV_VAR])
26
+ if (config.mode === 'off') return { ok: true }
27
+ const enabled = config.mode === 'all' || config.entities.has(input.resourceKind.toLowerCase())
28
+ if (!enabled) return { ok: true }
29
+ const readers = getAllOptimisticLockReaders()
30
+ const reader = readers[input.resourceKind]
31
+ if (!reader) return { ok: true }
32
+ const expectedRaw = input.requestHeaders.get(OPTIMISTIC_LOCK_HEADER_NAME)
33
+ if (!expectedRaw || expectedRaw.trim().length === 0) return { ok: true }
34
+ const expectedIso = normalizeIsoToken(expectedRaw.trim())
35
+ if (!expectedIso) return { ok: true }
36
+ if (!input.resourceId) return { ok: true }
37
+ const container = await createRequestContainer()
38
+ let em: EntityManager
39
+ try {
40
+ em = container.resolve('em') as EntityManager
41
+ } catch {
42
+ return { ok: true }
43
+ }
44
+ const currentRaw = await reader(em, {
45
+ resourceKind: input.resourceKind,
46
+ resourceId: input.resourceId,
47
+ tenantId: input.tenantId,
48
+ organizationId: input.organizationId ?? null,
49
+ })
50
+ if (currentRaw == null) return { ok: true }
51
+ const currentIso = normalizeIsoToken(currentRaw)
52
+ if (currentIso == null) return { ok: true }
53
+ if (currentIso === expectedIso) return { ok: true }
54
+ return {
55
+ ok: false,
56
+ status: 409,
57
+ body: {
58
+ error: OPTIMISTIC_LOCK_CONFLICT_ERROR,
59
+ code: OPTIMISTIC_LOCK_CONFLICT_CODE,
60
+ currentUpdatedAt: currentIso,
61
+ expectedUpdatedAt: expectedIso,
62
+ },
63
+ }
64
+ },
65
+ }
66
+
67
+ export const guards: MutationGuard[] = [optimisticLockGuard]
@@ -1,11 +1,77 @@
1
1
  import { asValue } from 'awilix'
2
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
3
  import type { AppContainer } from '@open-mercato/shared/lib/di/container'
4
+ import type { OptimisticLockCurrentReader } from '@open-mercato/shared/lib/crud/optimistic-lock'
5
+ import { registerOptimisticLockReaders } from '@open-mercato/shared/lib/crud/optimistic-lock-store'
3
6
  import { CustomerEntity, CustomerAddress, CustomerInteraction } from './data/entities'
4
7
 
8
+ const RESOURCE_KIND_COMPANY = 'customers.company'
9
+ // The CRUD factory derives resourceKind via singularize-the-second-segment of
10
+ // the commandId. For `customers.companies.update` it produces 'customers.company'.
11
+ // For `customers.people.update` it does NOT singularize 'people' → 'person' (the
12
+ // irregular plural is preserved), so the runtime resourceKind is
13
+ // 'customers.people'. We register the reader under BOTH names so the
14
+ // env opt-in entry can use either form (`customers.person` per spec or
15
+ // `customers.people` matching the factory's derivation).
16
+ const RESOURCE_KIND_PERSON = 'customers.person'
17
+ const RESOURCE_KIND_PEOPLE = 'customers.people'
18
+
19
+ const readCustomerCompanyUpdatedAt: OptimisticLockCurrentReader = async (
20
+ em: EntityManager,
21
+ { resourceId, tenantId, organizationId },
22
+ ) => {
23
+ const row = await em.findOne(
24
+ CustomerEntity,
25
+ {
26
+ id: resourceId,
27
+ tenantId,
28
+ ...(organizationId ? { organizationId } : {}),
29
+ kind: 'company',
30
+ deletedAt: null,
31
+ },
32
+ { fields: ['updatedAt'] as const },
33
+ )
34
+ return row?.updatedAt instanceof Date ? row.updatedAt.toISOString() : null
35
+ }
36
+
37
+ const readCustomerPersonUpdatedAt: OptimisticLockCurrentReader = async (
38
+ em: EntityManager,
39
+ { resourceId, tenantId, organizationId },
40
+ ) => {
41
+ const row = await em.findOne(
42
+ CustomerEntity,
43
+ {
44
+ id: resourceId,
45
+ tenantId,
46
+ ...(organizationId ? { organizationId } : {}),
47
+ kind: 'person',
48
+ deletedAt: null,
49
+ },
50
+ { fields: ['updatedAt'] as const },
51
+ )
52
+ return row?.updatedAt instanceof Date ? row.updatedAt.toISOString() : null
53
+ }
54
+
55
+ // Hand-wired readers must register at module-load time so they LAND BEFORE
56
+ // the factory's `registerOptimisticLockReaderIfAbsent` calls in
57
+ // `makeCrudRoute`. The discriminator (`kind: 'company' | 'person'`) cannot
58
+ // be expressed by the generic auto-reader because both kinds share the
59
+ // `customer_entities` polymorphic table. Registered unconditionally — the
60
+ // guard's mode check short-circuits when `OM_OPTIMISTIC_LOCK=off`.
61
+ registerOptimisticLockReaders({
62
+ [RESOURCE_KIND_COMPANY]: readCustomerCompanyUpdatedAt,
63
+ [RESOURCE_KIND_PERSON]: readCustomerPersonUpdatedAt,
64
+ [RESOURCE_KIND_PEOPLE]: readCustomerPersonUpdatedAt,
65
+ })
66
+
5
67
  export function register(container: AppContainer) {
6
68
  container.register({
7
69
  CustomerEntity: asValue(CustomerEntity),
8
70
  CustomerAddress: asValue(CustomerAddress),
9
71
  CustomerInteraction: asValue(CustomerInteraction),
10
72
  })
73
+ // `crudMutationGuardService` is registered platform-wide in the shared
74
+ // DI bootstrap (`packages/shared/src/lib/di/container.ts`). It already
75
+ // resolves the hand-wired readers above from the global store, so this
76
+ // module no longer needs its own DI binding.
11
77
  }
@@ -317,6 +317,7 @@
317
317
  "customers.companies.detail.deals.loading": "Deals werden geladen…",
318
318
  "customers.companies.detail.deleteConfirmDescription": "Diese Aktion kann nicht rückgängig gemacht werden.",
319
319
  "customers.companies.detail.deleteConfirmTitle": "Unternehmen löschen?",
320
+ "customers.companies.detail.deleteError": "Unternehmen konnte nicht gelöscht werden.",
320
321
  "customers.companies.detail.employees": "Mitarbeiter",
321
322
  "customers.companies.detail.empty.addresses": "Keine Adressen erfasst.",
322
323
  "customers.companies.detail.empty.comments": "Noch keine Notizen.",
@@ -1673,6 +1674,7 @@
1673
1674
  "customers.people.detail.deals.valueInvalid": "Bitte gib einen gültigen Betrag ein",
1674
1675
  "customers.people.detail.deleteConfirmDescription": "Diese Aktion kann nicht rückgängig gemacht werden.",
1675
1676
  "customers.people.detail.deleteConfirmTitle": "Person löschen?",
1677
+ "customers.people.detail.deleteError": "Person konnte nicht gelöscht werden.",
1676
1678
  "customers.people.detail.empty.activities": "Noch keine Aktivitäten",
1677
1679
  "customers.people.detail.empty.addresses": "Noch keine Adressen",
1678
1680
  "customers.people.detail.empty.comments": "Noch keine Notizen",
@@ -317,6 +317,7 @@
317
317
  "customers.companies.detail.deals.loading": "Loading deals…",
318
318
  "customers.companies.detail.deleteConfirmDescription": "This action cannot be undone.",
319
319
  "customers.companies.detail.deleteConfirmTitle": "Delete company?",
320
+ "customers.companies.detail.deleteError": "Failed to delete company.",
320
321
  "customers.companies.detail.employees": "employees",
321
322
  "customers.companies.detail.empty.addresses": "No addresses recorded.",
322
323
  "customers.companies.detail.empty.comments": "No notes yet.",
@@ -1673,6 +1674,7 @@
1673
1674
  "customers.people.detail.deals.valueInvalid": "Enter a valid amount",
1674
1675
  "customers.people.detail.deleteConfirmDescription": "This action cannot be undone.",
1675
1676
  "customers.people.detail.deleteConfirmTitle": "Delete person?",
1677
+ "customers.people.detail.deleteError": "Failed to delete person.",
1676
1678
  "customers.people.detail.empty.activities": "No activity yet",
1677
1679
  "customers.people.detail.empty.addresses": "No addresses yet",
1678
1680
  "customers.people.detail.empty.comments": "No notes yet",
@@ -317,6 +317,7 @@
317
317
  "customers.companies.detail.deals.loading": "Cargando oportunidades…",
318
318
  "customers.companies.detail.deleteConfirmDescription": "Esta acción no se puede deshacer.",
319
319
  "customers.companies.detail.deleteConfirmTitle": "¿Eliminar empresa?",
320
+ "customers.companies.detail.deleteError": "Error al eliminar la empresa.",
320
321
  "customers.companies.detail.employees": "empleados",
321
322
  "customers.companies.detail.empty.addresses": "Sin direcciones registradas.",
322
323
  "customers.companies.detail.empty.comments": "Sin notas aún.",
@@ -1673,6 +1674,7 @@
1673
1674
  "customers.people.detail.deals.valueInvalid": "Introduce un importe válido",
1674
1675
  "customers.people.detail.deleteConfirmDescription": "Esta acción no se puede deshacer.",
1675
1676
  "customers.people.detail.deleteConfirmTitle": "¿Eliminar persona?",
1677
+ "customers.people.detail.deleteError": "Error al eliminar la persona.",
1676
1678
  "customers.people.detail.empty.activities": "Sin actividad",
1677
1679
  "customers.people.detail.empty.addresses": "Sin direcciones",
1678
1680
  "customers.people.detail.empty.comments": "Sin notas",
@@ -317,6 +317,7 @@
317
317
  "customers.companies.detail.deals.loading": "Ładowanie szans sprzedaży…",
318
318
  "customers.companies.detail.deleteConfirmDescription": "Tej operacji nie można cofnąć.",
319
319
  "customers.companies.detail.deleteConfirmTitle": "Usunąć firmę?",
320
+ "customers.companies.detail.deleteError": "Nie udało się usunąć firmy.",
320
321
  "customers.companies.detail.employees": "osób",
321
322
  "customers.companies.detail.empty.addresses": "Brak zarejestrowanych adresów.",
322
323
  "customers.companies.detail.empty.comments": "Brak notatek.",
@@ -1673,6 +1674,7 @@
1673
1674
  "customers.people.detail.deals.valueInvalid": "Podaj poprawną kwotę",
1674
1675
  "customers.people.detail.deleteConfirmDescription": "Tej operacji nie można cofnąć.",
1675
1676
  "customers.people.detail.deleteConfirmTitle": "Usunąć osobę?",
1677
+ "customers.people.detail.deleteError": "Nie udało się usunąć osoby.",
1676
1678
  "customers.people.detail.empty.activities": "Brak aktywności",
1677
1679
  "customers.people.detail.empty.addresses": "Brak adresów",
1678
1680
  "customers.people.detail.empty.comments": "Brak notatek",
@@ -26,6 +26,7 @@ export type CustomerTodoRow = {
26
26
  todoDueAt?: string | null
27
27
  todoCustomValues?: Record<string, unknown> | null
28
28
  todoOrganizationId: string | null
29
+ todoUpdatedAt?: string | null
29
30
  organizationId: string
30
31
  tenantId: string
31
32
  createdAt: string
@@ -46,6 +47,7 @@ export type LegacyTodoDetail = {
46
47
  description: string | null
47
48
  dueAt: string | null
48
49
  organizationId: string | null
50
+ updatedAt: string | null
49
51
  customValues: Record<string, unknown> | null
50
52
  }
51
53
 
@@ -336,6 +338,15 @@ export async function resolveLegacyTodoDetails(
336
338
  }
337
339
  }
338
340
 
341
+ const updatedAt = (() => {
342
+ const candidates = [record.updated_at, record.updatedAt]
343
+ for (const candidate of candidates) {
344
+ const parsed = parseDateValue(candidate)
345
+ if (parsed) return parsed
346
+ }
347
+ return null
348
+ })()
349
+
339
350
  details.set(`${source}:${rawId}`, {
340
351
  title: extractTodoTitle(record),
341
352
  isDone,
@@ -344,6 +355,7 @@ export async function resolveLegacyTodoDetails(
344
355
  description,
345
356
  dueAt,
346
357
  organizationId,
358
+ updatedAt,
347
359
  customValues: Object.keys(customValues).length > 0 ? customValues : null,
348
360
  })
349
361
  }
@@ -550,6 +562,7 @@ export function mapLegacyTodoLinkToRow(
550
562
  todoDueAt: detail?.dueAt ?? null,
551
563
  todoCustomValues: detail?.customValues ?? null,
552
564
  todoOrganizationId: detail?.organizationId ?? link.organizationId ?? null,
565
+ todoUpdatedAt: detail?.updatedAt ?? null,
553
566
  organizationId: link.organizationId,
554
567
  tenantId: link.tenantId,
555
568
  createdAt: link.createdAt.toISOString(),
@@ -595,6 +608,7 @@ export function mapInteractionRecordToTodoRow(
595
608
  todoDueAt: interaction.scheduledAt ?? null,
596
609
  todoCustomValues: Object.keys(customValues).length > 0 ? customValues : null,
597
610
  todoOrganizationId: interaction.organizationId ?? null,
611
+ todoUpdatedAt: interaction.updatedAt ?? null,
598
612
  organizationId: interaction.organizationId ?? '',
599
613
  tenantId: interaction.tenantId ?? '',
600
614
  createdAt: interaction.createdAt,
@@ -242,6 +242,7 @@ export const WidgetVisibilityEditor = React.forwardRef<WidgetVisibilityEditorHan
242
242
  organizationId: organizationId ?? null,
243
243
  widgetIds: selected,
244
244
  }
245
+ // optimistic-lock-exempt: per-role widget visibility preference
245
246
  await apiCallOrThrow('/api/dashboards/roles/widgets', {
246
247
  method: 'PUT',
247
248
  headers: { 'content-type': 'application/json' },
@@ -258,6 +259,7 @@ export const WidgetVisibilityEditor = React.forwardRef<WidgetVisibilityEditorHan
258
259
  mode,
259
260
  widgetIds: selected,
260
261
  }
262
+ // optimistic-lock-exempt: per-user widget visibility preference
261
263
  await apiCallOrThrow('/api/dashboards/users/widgets', {
262
264
  method: 'PUT',
263
265
  headers: { 'content-type': 'application/json' },
@@ -33,9 +33,12 @@ export async function GET(req: Request) {
33
33
  const adapter = getDataSyncAdapter(integration.providerKey as string)
34
34
  if (!adapter) return null
35
35
 
36
- const [credentials, state] = await Promise.all([
37
- credentialsService.resolve(integration.id, scope),
38
- stateService.resolveState(integration.id, scope),
36
+ const [credentials, isEnabled] = await Promise.all([
37
+ credentialsService.resolve(integration.id, scope).catch(() => null),
38
+ stateService
39
+ .resolveState(integration.id, scope)
40
+ .then((state) => state.isEnabled)
41
+ .catch(() => false),
39
42
  ])
40
43
 
41
44
  return {
@@ -48,7 +51,7 @@ export async function GET(req: Request) {
48
51
  canStartRun: adapter.runMode !== 'provider',
49
52
  supportedEntities: adapter.supportedEntities,
50
53
  hasCredentials: Boolean(credentials),
51
- isEnabled: state.isEnabled,
54
+ isEnabled,
52
55
  settingsPath: `/backend/integrations/${encodeURIComponent(integration.id)}`,
53
56
  }
54
57
  }),
@@ -5,6 +5,8 @@ import { readJsonSafe } from '@open-mercato/shared/lib/http/readJsonSafe'
5
5
  import { createSyncScheduleSchema, listSyncSchedulesQuerySchema } from '../../data/validators'
6
6
  import type { SyncScheduleService } from '../../lib/sync-schedule-service'
7
7
  import { serializeSchedule } from './serialize'
8
+ import { readOptimisticLockExpected } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
9
+ import { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
8
10
 
9
11
  export const metadata = {
10
12
  GET: { requireAuth: true, requireFeatures: ['data_sync.configure'] },
@@ -65,12 +67,18 @@ export async function POST(req: Request) {
65
67
  const scheduleService = container.resolve('dataSyncScheduleService') as SyncScheduleService
66
68
 
67
69
  try {
68
- const schedule = await scheduleService.saveSchedule(parsed.data, {
70
+ const schedule = await scheduleService.saveSchedule({
71
+ ...parsed.data,
72
+ expectedUpdatedAt: readOptimisticLockExpected(req),
73
+ }, {
69
74
  organizationId: auth.orgId as string,
70
75
  tenantId: auth.tenantId,
71
76
  })
72
77
  return NextResponse.json(serializeSchedule(schedule), { status: 201 })
73
78
  } catch (error) {
79
+ if (isCrudHttpError(error)) {
80
+ return NextResponse.json(error.body, { status: error.status })
81
+ }
74
82
  const message = error instanceof Error ? error.message : 'Failed to save sync schedule'
75
83
  return NextResponse.json({ error: message }, { status: 422 })
76
84
  }
@@ -23,7 +23,8 @@ import {
23
23
  import { Separator } from '@open-mercato/ui/primitives/separator'
24
24
  import { Switch } from '@open-mercato/ui/primitives/switch'
25
25
  import { RowActions } from '@open-mercato/ui/backend/RowActions'
26
- import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
26
+ import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
27
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
27
28
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
28
29
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
29
30
  import { useT } from '@open-mercato/shared/lib/i18n/context'
@@ -88,6 +89,7 @@ type SyncScheduleRecord = {
88
89
  fullSync: boolean
89
90
  isEnabled: boolean
90
91
  lastRunAt: string | null
92
+ updatedAt?: string | null
91
93
  }
92
94
 
93
95
  type SyncSchedulesResponse = {
@@ -102,6 +104,7 @@ type SyncScheduleEditorState = {
102
104
  fullSync: boolean
103
105
  isEnabled: boolean
104
106
  lastRunAt: string | null
107
+ updatedAt?: string | null
105
108
  }
106
109
 
107
110
  const STATUS_STYLES: Record<string, string> = {
@@ -171,6 +174,7 @@ function buildDefaultScheduleState(entityType: string): SyncScheduleEditorState
171
174
  fullSync: normalized !== 'products',
172
175
  isEnabled: true,
173
176
  lastRunAt: null,
177
+ updatedAt: null,
174
178
  }
175
179
  }
176
180
 
@@ -328,6 +332,7 @@ export default function SyncRunsDashboardPage() {
328
332
  fullSync: record.fullSync,
329
333
  isEnabled: record.isEnabled,
330
334
  lastRunAt: record.lastRunAt,
335
+ updatedAt: record.updatedAt ?? null,
331
336
  })
332
337
  setIsLoadingSchedule(false)
333
338
  }
@@ -341,6 +346,7 @@ export default function SyncRunsDashboardPage() {
341
346
  }, [])
342
347
 
343
348
  const handleCancel = React.useCallback(async (row: SyncRunRow) => {
349
+ // optimistic-lock-exempt: run lifecycle action endpoint (cancel), not a concurrent record edit
344
350
  const call = await apiCall(`/api/data_sync/runs/${encodeURIComponent(row.id)}/cancel`, {
345
351
  method: 'POST',
346
352
  }, { fallback: null })
@@ -353,6 +359,7 @@ export default function SyncRunsDashboardPage() {
353
359
  }, [t])
354
360
 
355
361
  const handleRetry = React.useCallback(async (row: SyncRunRow) => {
362
+ // optimistic-lock-exempt: run lifecycle action endpoint (retry), not a concurrent record edit
356
363
  const call = await apiCall(`/api/data_sync/runs/${encodeURIComponent(row.id)}/retry`, {
357
364
  method: 'POST',
358
365
  headers: { 'Content-Type': 'application/json' },
@@ -391,6 +398,7 @@ export default function SyncRunsDashboardPage() {
391
398
 
392
399
  try {
393
400
  const call = await runMutation({
401
+ // optimistic-lock-exempt: starts a new sync run (create), not a concurrent record edit
394
402
  operation: () => apiCall<{ id: string }>('/api/data_sync/run', {
395
403
  method: 'POST',
396
404
  headers: { 'Content-Type': 'application/json' },
@@ -440,6 +448,7 @@ export default function SyncRunsDashboardPage() {
440
448
  setIsSavingSchedule(true)
441
449
  try {
442
450
  const call = await runMutation({
451
+ // optimistic-lock-exempt: keyed upsert (POST, no record id/version in body) — guard targets id-addressed PUT/PATCH/DELETE
443
452
  operation: () => apiCall<SyncScheduleRecord>('/api/data_sync/schedules', {
444
453
  method: 'POST',
445
454
  headers: { 'Content-Type': 'application/json' },
@@ -484,6 +493,7 @@ export default function SyncRunsDashboardPage() {
484
493
  fullSync: call.result.fullSync,
485
494
  isEnabled: call.result.isEnabled,
486
495
  lastRunAt: call.result.lastRunAt,
496
+ updatedAt: call.result.updatedAt ?? null,
487
497
  })
488
498
  flash(t('data_sync.dashboard.schedule.success', 'Recurring schedule saved'), 'success')
489
499
  } catch (error) {
@@ -500,9 +510,12 @@ export default function SyncRunsDashboardPage() {
500
510
  setIsDeletingSchedule(true)
501
511
  try {
502
512
  const call = await runMutation({
503
- operation: () => apiCall(`/api/data_sync/schedules/${encodeURIComponent(scheduleEditor.id as string)}`, {
504
- method: 'DELETE',
505
- }, { fallback: null }),
513
+ operation: () => withScopedApiRequestHeaders(
514
+ buildOptimisticLockHeader(scheduleEditor.updatedAt),
515
+ () => apiCall(`/api/data_sync/schedules/${encodeURIComponent(scheduleEditor.id as string)}`, {
516
+ method: 'DELETE',
517
+ }, { fallback: null }),
518
+ ),
506
519
  mutationPayload: {
507
520
  scheduleId: scheduleEditor.id,
508
521
  },
@@ -525,7 +538,7 @@ export default function SyncRunsDashboardPage() {
525
538
  } finally {
526
539
  setIsDeletingSchedule(false)
527
540
  }
528
- }, [runMutation, scheduleEditor.id, selectedEntityType, t])
541
+ }, [runMutation, scheduleEditor.id, scheduleEditor.updatedAt, selectedEntityType, t])
529
542
 
530
543
  const filters: FilterDef[] = [
531
544
  {
@@ -1,7 +1,9 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from 'react'
4
- import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
4
+ import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
5
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
6
+ import { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'
5
7
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
6
8
  import { Badge } from '@open-mercato/ui/primitives/badge'
7
9
  import { Button } from '@open-mercato/ui/primitives/button'
@@ -54,6 +56,7 @@ type SyncScheduleRecord = {
54
56
  fullSync: boolean
55
57
  isEnabled: boolean
56
58
  lastRunAt: string | null
59
+ updatedAt?: string | null
57
60
  }
58
61
 
59
62
  type SyncSchedulesResponse = {
@@ -68,6 +71,7 @@ type SyncScheduleEditorState = {
68
71
  fullSync: boolean
69
72
  isEnabled: boolean
70
73
  lastRunAt: string | null
74
+ updatedAt?: string | null
71
75
  }
72
76
 
73
77
  type IntegrationScheduleTabProps = {
@@ -94,6 +98,7 @@ function buildDefaultScheduleState(entityType: string): SyncScheduleEditorState
94
98
  fullSync: normalized !== 'products',
95
99
  isEnabled: true,
96
100
  lastRunAt: null,
101
+ updatedAt: null,
97
102
  }
98
103
  }
99
104
 
@@ -127,6 +132,7 @@ function buildScheduleEditors(
127
132
  fullSync: record.fullSync,
128
133
  isEnabled: record.isEnabled,
129
134
  lastRunAt: record.lastRunAt,
135
+ updatedAt: record.updatedAt ?? null,
130
136
  }
131
137
  : buildDefaultScheduleState(entityType),
132
138
  ])
@@ -214,6 +220,7 @@ export function IntegrationScheduleTab(props: IntegrationScheduleTabProps) {
214
220
  setRunningKey(scheduleKey)
215
221
  try {
216
222
  const scheduleState = schedules[scheduleKey] ?? buildDefaultScheduleState(entityType)
223
+ // optimistic-lock-exempt: starts a new sync run (create), not a concurrent record edit
217
224
  const call = await apiCall<{ id: string }>('/api/data_sync/run', {
218
225
  method: 'POST',
219
226
  headers: { 'content-type': 'application/json' },
@@ -243,23 +250,39 @@ export function IntegrationScheduleTab(props: IntegrationScheduleTabProps) {
243
250
  const scheduleState = schedules[scheduleKey] ?? buildDefaultScheduleState(entityType)
244
251
  setSavingKey(scheduleKey)
245
252
  try {
246
- const call = await apiCall<SyncScheduleRecord>('/api/data_sync/schedules', {
247
- method: 'POST',
248
- headers: { 'content-type': 'application/json' },
249
- body: JSON.stringify({
250
- integrationId: props.integrationId,
251
- entityType,
252
- direction,
253
- scheduleType: scheduleState.scheduleType,
254
- scheduleValue: scheduleState.scheduleValue,
255
- timezone: scheduleState.timezone,
256
- fullSync: scheduleState.fullSync,
257
- isEnabled: scheduleState.isEnabled,
258
- }),
259
- }, { fallback: null })
253
+ // Keyed upsert (POST). When the server resolves an existing row the save
254
+ // version-checks against this schedule's loaded `updatedAt`; for a brand
255
+ // new row the header is empty so the create path is unaffected.
256
+ const call = await withScopedApiRequestHeaders(
257
+ buildOptimisticLockHeader(scheduleState.updatedAt),
258
+ () => apiCall<SyncScheduleRecord>('/api/data_sync/schedules', {
259
+ method: 'POST',
260
+ headers: { 'content-type': 'application/json' },
261
+ body: JSON.stringify({
262
+ integrationId: props.integrationId,
263
+ entityType,
264
+ direction,
265
+ scheduleType: scheduleState.scheduleType,
266
+ scheduleValue: scheduleState.scheduleValue,
267
+ timezone: scheduleState.timezone,
268
+ fullSync: scheduleState.fullSync,
269
+ isEnabled: scheduleState.isEnabled,
270
+ }),
271
+ }, { fallback: null }),
272
+ )
260
273
 
261
274
  if (!call.ok || !call.result) {
262
- throw new Error((call.result as { error?: string } | null)?.error ?? 'Failed to save schedule')
275
+ const conflictError = Object.assign(
276
+ new Error((call.result as { error?: string } | null)?.error ?? 'Failed to save schedule'),
277
+ {
278
+ status: call.status,
279
+ ...(call.result && typeof call.result === 'object' ? call.result : {}),
280
+ },
281
+ )
282
+ if (surfaceRecordConflict(conflictError, t)) {
283
+ return
284
+ }
285
+ throw conflictError
263
286
  }
264
287
 
265
288
  updateScheduleEditor(scheduleKey, {
@@ -270,6 +293,7 @@ export function IntegrationScheduleTab(props: IntegrationScheduleTabProps) {
270
293
  fullSync: call.result.fullSync,
271
294
  isEnabled: call.result.isEnabled,
272
295
  lastRunAt: call.result.lastRunAt,
296
+ updatedAt: call.result.updatedAt ?? null,
273
297
  }, entityType)
274
298
  flash(t('data_sync.dashboard.schedule.success', 'Recurring schedule saved'), 'success')
275
299
  } catch (error) {
@@ -289,9 +313,12 @@ export function IntegrationScheduleTab(props: IntegrationScheduleTabProps) {
289
313
 
290
314
  setDeletingKey(scheduleKey)
291
315
  try {
292
- const call = await apiCall(`/api/data_sync/schedules/${encodeURIComponent(scheduleState.id)}`, {
293
- method: 'DELETE',
294
- }, { fallback: null })
316
+ const call = await withScopedApiRequestHeaders(
317
+ buildOptimisticLockHeader(scheduleState.updatedAt),
318
+ () => apiCall(`/api/data_sync/schedules/${encodeURIComponent(scheduleState.id as string)}`, {
319
+ method: 'DELETE',
320
+ }, { fallback: null }),
321
+ )
295
322
 
296
323
  if (!call.ok) {
297
324
  throw new Error((call.result as { error?: string } | null)?.error ?? 'Failed to delete schedule')
@@ -1,6 +1,7 @@
1
1
  import { randomUUID } from 'node:crypto'
2
2
  import type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'
3
3
  import { findAndCountWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
4
+ import { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
4
5
  import { SyncSchedule } from '../data/entities'
5
6
 
6
7
  type SyncScope = {
@@ -129,11 +130,21 @@ export function createSyncScheduleService(em: EntityManager, schedulerService?:
129
130
  timezone: string
130
131
  fullSync: boolean
131
132
  isEnabled: boolean
133
+ expectedUpdatedAt?: string | null
132
134
  }, scope: SyncScope): Promise<SyncSchedule> {
133
135
  const existing = input.id
134
136
  ? await getById(input.id, scope)
135
137
  : await getByKey(input.integrationId, input.entityType, input.direction, scope)
136
138
 
139
+ if (existing) {
140
+ enforceCommandOptimisticLock({
141
+ resourceKind: 'data_sync.schedule',
142
+ resourceId: existing.id,
143
+ current: existing.updatedAt ?? null,
144
+ expected: input.expectedUpdatedAt ?? null,
145
+ })
146
+ }
147
+
137
148
  const row = existing ?? em.create(SyncSchedule, {
138
149
  id: randomUUID(),
139
150
  integrationId: input.integrationId,
@@ -4,6 +4,7 @@ import { Dictionary, DictionaryEntry } from '@open-mercato/core/modules/dictiona
4
4
  import { resolveDictionariesRouteContext } from '@open-mercato/core/modules/dictionaries/api/context'
5
5
  import { updateDictionaryEntrySchema } from '@open-mercato/core/modules/dictionaries/data/validators'
6
6
  import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
7
+ import { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
7
8
  import type { CommandBus } from '@open-mercato/shared/lib/commands'
8
9
  import { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'
9
10
  import type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
@@ -70,7 +71,13 @@ export async function PATCH(req: Request, ctx: { params?: { dictionaryId?: strin
70
71
  entryId: ctx.params?.entryId,
71
72
  })
72
73
  const dictionary = await loadDictionary(context, dictionaryId)
73
- await loadEntry(context, dictionary, entryId)
74
+ const entry = await loadEntry(context, dictionary, entryId)
75
+ enforceCommandOptimisticLock({
76
+ resourceKind: 'dictionaries.entry',
77
+ resourceId: entry.id,
78
+ current: entry.updatedAt ?? null,
79
+ request: req,
80
+ })
74
81
  const rawBody = await req.json().catch(() => ({}))
75
82
  const payload = updateDictionaryEntrySchema.parse(rawBody)
76
83
  // These nested routes don't use the CRUD factory, so invoke the command bus explicitly.