@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
@@ -8,7 +8,8 @@ import { DataTable } from '@open-mercato/ui/backend/DataTable'
8
8
  import type { ColumnDef } from '@tanstack/react-table'
9
9
  import { Button } from '@open-mercato/ui/primitives/button'
10
10
  import { RowActions } from '@open-mercato/ui/backend/RowActions'
11
- import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
11
+ import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
12
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
12
13
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
13
14
  import { useQuery, useQueryClient } from '@tanstack/react-query'
14
15
  import { useT } from '@open-mercato/shared/lib/i18n/context'
@@ -92,9 +93,13 @@ export default function RulesListPage() {
92
93
  })
93
94
  if (!confirmed) return
94
95
 
95
- const result = await apiCall(`/api/business_rules/rules?id=${id}`, {
96
- method: 'DELETE',
97
- })
96
+ const record = (data || []).find((item) => item.id === id)
97
+ const result = await withScopedApiRequestHeaders(
98
+ buildOptimisticLockHeader(record?.updatedAt),
99
+ () => apiCall(`/api/business_rules/rules?id=${id}`, {
100
+ method: 'DELETE',
101
+ }),
102
+ )
98
103
 
99
104
  if (result.ok) {
100
105
  flash(t('business_rules.messages.deleted'), 'success')
@@ -105,14 +110,18 @@ export default function RulesListPage() {
105
110
  }
106
111
 
107
112
  const handleToggleEnabled = async (id: string, currentEnabled: boolean) => {
108
- const result = await apiCall('/api/business_rules/rules', {
109
- method: 'PUT',
110
- headers: { 'Content-Type': 'application/json' },
111
- body: JSON.stringify({
112
- id,
113
- enabled: !currentEnabled,
113
+ const record = (data || []).find((item) => item.id === id)
114
+ const result = await withScopedApiRequestHeaders(
115
+ buildOptimisticLockHeader(record?.updatedAt),
116
+ () => apiCall('/api/business_rules/rules', {
117
+ method: 'PUT',
118
+ headers: { 'Content-Type': 'application/json' },
119
+ body: JSON.stringify({
120
+ id,
121
+ enabled: !currentEnabled,
122
+ }),
114
123
  }),
115
- })
124
+ )
116
125
 
117
126
  if (result.ok) {
118
127
  flash(t('business_rules.messages.updated'), 'success')
@@ -9,8 +9,11 @@ import { CrudForm } from '@open-mercato/ui/backend/CrudForm'
9
9
  import type { CrudField } from '@open-mercato/ui/backend/CrudForm'
10
10
  import { Spinner } from '@open-mercato/ui/primitives/spinner'
11
11
  import { Button } from '@open-mercato/ui/primitives/button'
12
- import { apiFetch } from '@open-mercato/ui/backend/utils/api'
12
+ import { apiFetch, withScopedApiHeaders } from '@open-mercato/ui/backend/utils/api'
13
13
  import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
14
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
15
+ import { readJsonSafe } from '@open-mercato/ui/backend/utils/serverErrors'
16
+ import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
14
17
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
15
18
  import { useT } from '@open-mercato/shared/lib/i18n/context'
16
19
  import { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
@@ -85,15 +88,23 @@ export default function EditRuleSetPage() {
85
88
  ...values,
86
89
  }
87
90
 
88
- const response = await apiFetch('/api/business_rules/sets', {
91
+ const updateSet = () => apiFetch('/api/business_rules/sets', {
89
92
  method: 'PUT',
90
93
  headers: { 'Content-Type': 'application/json' },
91
94
  body: JSON.stringify(payload),
92
95
  })
96
+ const headers = buildOptimisticLockHeader(ruleSet?.updatedAt ?? null)
97
+ const response = Object.keys(headers).length
98
+ ? await withScopedApiHeaders(headers, updateSet)
99
+ : await updateSet()
93
100
 
94
101
  if (!response.ok) {
95
- const error = await response.json()
96
- throw new Error(error.error || error.message || t('business_rules.sets.errors.updateFailed'))
102
+ const body = (await readJsonSafe<Record<string, unknown>>(response)) ?? {}
103
+ const message =
104
+ (typeof body.error === 'string' && body.error) ||
105
+ (typeof body.message === 'string' && body.message) ||
106
+ t('business_rules.sets.errors.updateFailed')
107
+ throw new CrudHttpError(response.status, { ...body, error: message })
97
108
  }
98
109
 
99
110
  flash(t('business_rules.sets.messages.updated'), 'success')
@@ -265,6 +276,7 @@ export default function EditRuleSetPage() {
265
276
  schema={ruleSetFormSchema}
266
277
  fields={fields}
267
278
  initialValues={initialValues}
279
+ optimisticLockUpdatedAt={ruleSet.updatedAt}
268
280
  onSubmit={handleSubmit}
269
281
  cancelHref="/backend/sets"
270
282
  submitLabel={t('business_rules.sets.form.update')}
@@ -8,7 +8,8 @@ import { DataTable } from '@open-mercato/ui/backend/DataTable'
8
8
  import type { ColumnDef } from '@tanstack/react-table'
9
9
  import { Button } from '@open-mercato/ui/primitives/button'
10
10
  import { RowActions } from '@open-mercato/ui/backend/RowActions'
11
- import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
11
+ import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
12
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
12
13
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
13
14
  import { useQuery, useQueryClient } from '@tanstack/react-query'
14
15
  import { useT } from '@open-mercato/shared/lib/i18n/context'
@@ -83,9 +84,13 @@ export default function RuleSetsListPage() {
83
84
  })
84
85
  if (!confirmed) return
85
86
 
86
- const result = await apiCall(`/api/business_rules/sets?id=${id}`, {
87
- method: 'DELETE',
88
- })
87
+ const record = (data || []).find((item) => item.id === id)
88
+ const result = await withScopedApiRequestHeaders(
89
+ buildOptimisticLockHeader(record?.updatedAt),
90
+ () => apiCall(`/api/business_rules/sets?id=${id}`, {
91
+ method: 'DELETE',
92
+ }),
93
+ )
89
94
 
90
95
  if (result.ok) {
91
96
  flash(t('business_rules.sets.messages.deleted'), 'success')
@@ -96,14 +101,18 @@ export default function RuleSetsListPage() {
96
101
  }
97
102
 
98
103
  const handleToggleEnabled = async (id: string, currentEnabled: boolean) => {
99
- const result = await apiCall('/api/business_rules/sets', {
100
- method: 'PUT',
101
- headers: { 'Content-Type': 'application/json' },
102
- body: JSON.stringify({
103
- id,
104
- enabled: !currentEnabled,
104
+ const record = (data || []).find((item) => item.id === id)
105
+ const result = await withScopedApiRequestHeaders(
106
+ buildOptimisticLockHeader(record?.updatedAt),
107
+ () => apiCall('/api/business_rules/sets', {
108
+ method: 'PUT',
109
+ headers: { 'Content-Type': 'application/json' },
110
+ body: JSON.stringify({
111
+ id,
112
+ enabled: !currentEnabled,
113
+ }),
105
114
  }),
106
- })
115
+ )
107
116
 
108
117
  if (result.ok) {
109
118
  flash(t('business_rules.sets.messages.updated'), 'success')
@@ -99,6 +99,7 @@ type ManageCategoryRow = {
99
99
  childCount: number
100
100
  descendantCount: number
101
101
  isActive: boolean
102
+ updatedAt: string | null
102
103
  organizationId: string
103
104
  tenantId: string
104
105
  }
@@ -262,6 +263,7 @@ export async function GET(req: Request) {
262
263
  childCount: node.childIds.length,
263
264
  descendantCount: node.descendantIds.length,
264
265
  isActive: node.isActive,
266
+ updatedAt: category?.updatedAt ? new Date(category.updatedAt).toISOString() : null,
265
267
  organizationId,
266
268
  tenantId,
267
269
  ...(cfValues[recordId] ?? {}),
@@ -297,6 +299,7 @@ const categoryListItemSchema = z.object({
297
299
  childCount: z.number(),
298
300
  descendantCount: z.number(),
299
301
  isActive: z.boolean(),
302
+ updatedAt: z.string().nullable().optional(),
300
303
  organizationId: z.string().uuid(),
301
304
  tenantId: z.string().uuid(),
302
305
  })
@@ -425,6 +425,10 @@ async function decorateProductsAfterList(
425
425
  defaultMediaId: offer.defaultMediaId ?? null,
426
426
  defaultMediaUrl: offer.defaultMediaUrl ?? null,
427
427
  metadata: offer.metadata ?? null,
428
+ updatedAt:
429
+ offer.updatedAt instanceof Date
430
+ ? offer.updatedAt.toISOString()
431
+ : (typeof offer.updatedAt === "string" ? offer.updatedAt : null),
428
432
  });
429
433
  offersByProduct.set(productId, entry);
430
434
  }
@@ -22,6 +22,8 @@ type CategoryRow = {
22
22
  description: string | null
23
23
  parentId: string | null
24
24
  isActive: boolean
25
+ updatedAt?: string | null
26
+ updated_at?: string | null
25
27
  pathLabel?: string
26
28
  }
27
29
 
@@ -36,6 +38,7 @@ type CategoryFormValues = {
36
38
  description?: string
37
39
  parentId?: string | null
38
40
  isActive?: boolean
41
+ updatedAt?: string | null
39
42
  }
40
43
 
41
44
  async function submitCategoryUpdate(
@@ -116,6 +119,7 @@ export default function EditCatalogCategoryPage({ params }: { params?: { id?: st
116
119
  description: record.description ?? '',
117
120
  parentId: record.parentId ?? '',
118
121
  isActive: record.isActive,
122
+ updatedAt: record.updatedAt ?? record.updated_at ?? null,
119
123
  ...customValues,
120
124
  })
121
125
  setPathLabel(record.pathLabel ?? '')
@@ -238,6 +242,7 @@ export default function EditCatalogCategoryPage({ params }: { params?: { id?: st
238
242
  groups={groups}
239
243
  entityId={E.catalog.catalog_product_category}
240
244
  initialValues={initialValues ?? { id: categoryId, name: '', slug: '', description: '', parentId: '', isActive: true }}
245
+ optimisticLockUpdatedAt={initialValues?.updatedAt}
241
246
  isLoading={loading}
242
247
  loadingMessage={t('catalog.categories.form.loading', 'Loading category...')}
243
248
  submitLabel={t('catalog.categories.form.action.save', 'Save')}
@@ -37,7 +37,10 @@ import { Spinner } from "@open-mercato/ui/primitives/spinner";
37
37
  import {
38
38
  apiCall,
39
39
  readApiResultOrThrow,
40
+ withScopedApiRequestHeaders,
40
41
  } from "@open-mercato/ui/backend/utils/apiCall";
42
+ import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
43
+ import { surfaceRecordConflict } from "@open-mercato/ui/backend/conflicts";
41
44
  import { useT } from "@open-mercato/shared/lib/i18n/context";
42
45
  import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
43
46
  import { E } from "#generated/entities.ids.generated";
@@ -157,6 +160,8 @@ type VariantSummaryApi = {
157
160
  default_media_id?: string | null;
158
161
  defaultMediaId?: string | null;
159
162
  metadata?: Record<string, unknown> | null;
163
+ updated_at?: string | null;
164
+ updatedAt?: string | null;
160
165
  };
161
166
 
162
167
  type AttachmentListResponse = {
@@ -181,6 +186,7 @@ type VariantSummary = {
181
186
  defaultMediaId: string | null;
182
187
  prices: VariantPriceSummary[];
183
188
  optionValues: Record<string, string> | null;
189
+ updatedAt: string | null;
184
190
  };
185
191
 
186
192
  type VariantPriceListResponse = {
@@ -221,6 +227,7 @@ type OfferSnapshot = {
221
227
  isActive: boolean;
222
228
  channelName: string | null;
223
229
  channelCode: string | null;
230
+ updatedAt: string | null;
224
231
  };
225
232
 
226
233
  function mapVariantPriceSummary(
@@ -273,7 +280,7 @@ function readProductConversionRows(
273
280
  ): ProductUnitConversionDraft[] {
274
281
  const rows = Array.isArray(items) ? items : [];
275
282
  return rows
276
- .map((item) => {
283
+ .map((item): ProductUnitConversionDraft | null => {
277
284
  const id = toTrimmedOrNull(item.id);
278
285
  const unitCode = canonicalizeUnitCode(item.unit_code ?? item.unitCode);
279
286
  const factor = toPositiveNumberOrNull(
@@ -298,6 +305,12 @@ function readProductConversionRows(
298
305
  toBaseFactor: String(factor),
299
306
  sortOrder: String(sortOrderRaw),
300
307
  isActive,
308
+ updatedAt:
309
+ typeof item.updated_at === "string"
310
+ ? item.updated_at
311
+ : typeof item.updatedAt === "string"
312
+ ? item.updatedAt
313
+ : null,
301
314
  } satisfies ProductUnitConversionDraft;
302
315
  })
303
316
  .filter((entry): entry is ProductUnitConversionDraft => Boolean(entry));
@@ -405,6 +418,12 @@ export default function EditCatalogProductPage({
405
418
  : null,
406
419
  prices: priceMap[variantId] ?? [],
407
420
  optionValues,
421
+ updatedAt:
422
+ typeof variant.updatedAt === "string"
423
+ ? variant.updatedAt
424
+ : typeof variant.updated_at === "string"
425
+ ? variant.updated_at
426
+ : null,
408
427
  };
409
428
  })
410
429
  .filter((entry): entry is VariantSummary => Boolean(entry));
@@ -712,6 +731,12 @@ export default function EditCatalogProductPage({
712
731
  categoryIds,
713
732
  channelIds,
714
733
  tags: tagValues,
734
+ updatedAt:
735
+ typeof record.updatedAt === "string"
736
+ ? record.updatedAt
737
+ : typeof record.updated_at === "string"
738
+ ? record.updated_at
739
+ : null,
715
740
  };
716
741
  if (!cancelled) {
717
742
  setInitialValues({ ...initial, ...customValues });
@@ -1225,12 +1250,18 @@ export default function EditCatalogProductPage({
1225
1250
  try {
1226
1251
  for (const offer of removedOffers) {
1227
1252
  if (!offer.id) continue;
1228
- await deleteCrud("catalog/offers", offer.id, {
1229
- errorMessage: t(
1230
- "catalog.products.edit.offers.deleteError",
1231
- "Failed to remove sales channel offer.",
1232
- ),
1233
- });
1253
+ const offerId = offer.id;
1254
+ // Send the offer's own version, overriding the product header the
1255
+ // parent CrudForm submit scope put on the stack (#2055).
1256
+ await withScopedApiRequestHeaders(
1257
+ buildOptimisticLockHeader(offer.updatedAt),
1258
+ () => deleteCrud("catalog/offers", offerId, {
1259
+ errorMessage: t(
1260
+ "catalog.products.edit.offers.deleteError",
1261
+ "Failed to remove sales channel offer.",
1262
+ ),
1263
+ }),
1264
+ );
1234
1265
  }
1235
1266
  } catch (err) {
1236
1267
  console.error("catalog.products.edit.offers.delete", err);
@@ -1248,6 +1279,15 @@ export default function EditCatalogProductPage({
1248
1279
  .map((entry) => toTrimmedOrNull(entry.id))
1249
1280
  .filter((id): id is string => Boolean(id)),
1250
1281
  );
1282
+ // Loaded conversion id → version, so the sync sends each row's own
1283
+ // optimistic-lock version (overriding the product header leaked by the
1284
+ // parent CrudForm submit scope onto the catalog/product-unit-conversions
1285
+ // guard) (#2055).
1286
+ const conversionVersions = new Map<string, string | null>();
1287
+ for (const entry of initialConversionsRef.current) {
1288
+ const cid = toTrimmedOrNull(entry.id);
1289
+ if (cid) conversionVersions.set(cid, entry.updatedAt ?? null);
1290
+ }
1251
1291
  const nextConversionIds = new Set(
1252
1292
  conversionInputs
1253
1293
  .map((entry) =>
@@ -1259,23 +1299,30 @@ export default function EditCatalogProductPage({
1259
1299
  (id) => !nextConversionIds.has(id),
1260
1300
  );
1261
1301
  for (const conversionId of removedConversionIds) {
1262
- await deleteCrud("catalog/product-unit-conversions", conversionId, {
1263
- errorMessage: t(
1264
- "catalog.products.uom.errors.sync",
1265
- "Failed to synchronize product conversions.",
1266
- ),
1267
- });
1302
+ await withScopedApiRequestHeaders(
1303
+ buildOptimisticLockHeader(conversionVersions.get(conversionId) ?? null),
1304
+ () => deleteCrud("catalog/product-unit-conversions", conversionId, {
1305
+ errorMessage: t(
1306
+ "catalog.products.uom.errors.sync",
1307
+ "Failed to synchronize product conversions.",
1308
+ ),
1309
+ }),
1310
+ );
1268
1311
  }
1269
1312
  const persistedConversions: ProductUnitConversionDraft[] = [];
1270
1313
  for (const conversion of conversionInputs) {
1271
1314
  if (conversion.id) {
1272
- await updateCrud("catalog/product-unit-conversions", {
1273
- id: conversion.id,
1274
- unitCode: conversion.unitCode,
1275
- toBaseFactor: conversion.toBaseFactor,
1276
- sortOrder: conversion.sortOrder,
1277
- isActive: conversion.isActive,
1278
- });
1315
+ const conversionId = conversion.id;
1316
+ await withScopedApiRequestHeaders(
1317
+ buildOptimisticLockHeader(conversionVersions.get(conversionId) ?? null),
1318
+ () => updateCrud("catalog/product-unit-conversions", {
1319
+ id: conversionId,
1320
+ unitCode: conversion.unitCode,
1321
+ toBaseFactor: conversion.toBaseFactor,
1322
+ sortOrder: conversion.sortOrder,
1323
+ isActive: conversion.isActive,
1324
+ }),
1325
+ );
1279
1326
  persistedConversions.push({
1280
1327
  id: conversion.id,
1281
1328
  unitCode: conversion.unitCode,
@@ -1766,23 +1813,29 @@ function ProductOptionsSection({ values, setValue }: ProductFormGroupProps) {
1766
1813
 
1767
1814
  const handleDeleteSchema = React.useCallback(
1768
1815
  async (id: string) => {
1816
+ const target = schemaTemplates.find((entry) => entry.id === id);
1817
+ const lockVersion = target?.updatedAt ?? target?.updated_at ?? null;
1769
1818
  try {
1770
- await deleteCrud("catalog/option-schemas", id, {
1771
- errorMessage: t(
1772
- "catalog.products.edit.schemas.deleteError",
1773
- "Failed to delete schema.",
1774
- ),
1775
- });
1819
+ await withScopedApiRequestHeaders(
1820
+ buildOptimisticLockHeader(lockVersion),
1821
+ () => deleteCrud("catalog/option-schemas", id, {
1822
+ errorMessage: t(
1823
+ "catalog.products.edit.schemas.deleteError",
1824
+ "Failed to delete schema.",
1825
+ ),
1826
+ }),
1827
+ );
1776
1828
  flash(
1777
1829
  t("catalog.products.edit.schemas.deleted", "Schema deleted."),
1778
1830
  "success",
1779
1831
  );
1780
1832
  void loadSchemas();
1781
1833
  } catch (err) {
1834
+ if (surfaceRecordConflict(err, t)) { void loadSchemas(); return; }
1782
1835
  console.error("catalog.option-schemas.delete failed", err);
1783
1836
  }
1784
1837
  },
1785
- [loadSchemas, t],
1838
+ [loadSchemas, schemaTemplates, t],
1786
1839
  );
1787
1840
 
1788
1841
  const handleSaveSchema = React.useCallback(
@@ -1814,8 +1867,25 @@ function ProductOptionsSection({ values, setValue }: ProductFormGroupProps) {
1814
1867
  isActive: true,
1815
1868
  };
1816
1869
  if (schemaToEdit?.id) payload.id = schemaToEdit.id;
1817
- if (schemaToEdit?.id) await updateCrud("catalog/option-schemas", payload);
1818
- else await createCrud("catalog/option-schemas", payload);
1870
+ try {
1871
+ if (schemaToEdit?.id) {
1872
+ const lockVersion = schemaToEdit.updatedAt ?? schemaToEdit.updated_at ?? null;
1873
+ await withScopedApiRequestHeaders(
1874
+ buildOptimisticLockHeader(lockVersion),
1875
+ () => updateCrud("catalog/option-schemas", payload),
1876
+ );
1877
+ } else {
1878
+ await createCrud("catalog/option-schemas", payload);
1879
+ }
1880
+ } catch (err) {
1881
+ if (surfaceRecordConflict(err, t)) {
1882
+ setSaveSchemaOpen(false);
1883
+ setSchemaToEdit(null);
1884
+ void loadSchemas();
1885
+ return;
1886
+ }
1887
+ throw err;
1888
+ }
1819
1889
  flash(
1820
1890
  t("catalog.products.edit.schemas.saved", "Schema saved."),
1821
1891
  "success",
@@ -2138,18 +2208,23 @@ function ProductVariantsSection({
2138
2208
  if (!confirmed) return;
2139
2209
  setDeletingId(variant.id);
2140
2210
  try {
2141
- await deleteCrud("catalog/variants", variant.id, {
2142
- errorMessage: t(
2143
- "catalog.variants.form.deleteError",
2144
- "Failed to delete variant.",
2145
- ),
2146
- });
2211
+ await withScopedApiRequestHeaders(
2212
+ buildOptimisticLockHeader(variant.updatedAt),
2213
+ () =>
2214
+ deleteCrud("catalog/variants", variant.id, {
2215
+ errorMessage: t(
2216
+ "catalog.variants.form.deleteError",
2217
+ "Failed to delete variant.",
2218
+ ),
2219
+ }),
2220
+ );
2147
2221
  flash(
2148
2222
  t("catalog.variants.form.deleted", "Variant deleted."),
2149
2223
  "success",
2150
2224
  );
2151
2225
  onVariantDeleted(variant.id);
2152
2226
  } catch (err) {
2227
+ if (surfaceRecordConflict(err, t)) return;
2153
2228
  console.error("catalog.products.edit.variants.delete", err);
2154
2229
  flash(
2155
2230
  t("catalog.variants.form.deleteError", "Failed to delete variant."),
@@ -2767,6 +2842,7 @@ function readOfferSnapshots(record: Record<string, unknown>): OfferSnapshot[] {
2767
2842
  getString(offer.channelName) ?? getString(offer.channel_name),
2768
2843
  channelCode:
2769
2844
  getString(offer.channelCode) ?? getString(offer.channel_code),
2845
+ updatedAt: getString(offer.updatedAt) ?? getString(offer.updated_at) ?? null,
2770
2846
  });
2771
2847
  }
2772
2848
  return snapshots;
@@ -2867,6 +2943,7 @@ function mergeOfferSnapshots(
2867
2943
  isActive: entry.isActive ?? previous?.isActive ?? true,
2868
2944
  channelName: previous?.channelName ?? null,
2869
2945
  channelCode: previous?.channelCode ?? null,
2946
+ updatedAt: previous?.updatedAt ?? null,
2870
2947
  };
2871
2948
  });
2872
2949
  }
@@ -7,7 +7,9 @@ import { CrudForm, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'
7
7
  import { createCrud, updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'
8
8
  import { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'
9
9
  import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'
10
- import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
10
+ import { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
11
+ import { buildOptimisticLockHeader } 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 { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'
13
15
  import { useT } from '@open-mercato/shared/lib/i18n/context'
@@ -67,6 +69,17 @@ type AttachmentListResponse = {
67
69
  items?: ProductMediaItem[]
68
70
  }
69
71
 
72
+ export function handleVariantDeleteError(
73
+ err: unknown,
74
+ t: (key: string, fallback?: string) => string,
75
+ ): void {
76
+ if (surfaceRecordConflict(err, t)) return
77
+ const message = err instanceof Error && err.message
78
+ ? err.message
79
+ : t('catalog.variants.form.deleteError', 'Failed to delete variant.')
80
+ flash(message, 'error')
81
+ }
82
+
70
83
  function resolveVariantPriceLabel(prices: Record<string, VariantPriceDraft> | undefined): string | null {
71
84
  if (!prices || typeof prices !== 'object') return null
72
85
  const entries = Object.values(prices)
@@ -93,6 +106,10 @@ export default function EditVariantPage({ params }: { params?: { productId?: str
93
106
  const [optionDefinitions, setOptionDefinitions] = React.useState<OptionDefinition[]>([])
94
107
  const [initialValues, setInitialValues] = React.useState<VariantFormValues | null>(null)
95
108
  const [existingPriceIds, setExistingPriceIds] = React.useState<Record<string, string>>({})
109
+ // price-kind id → loaded price `updatedAt`, so the price sync sends each price's
110
+ // own optimistic-lock version (the variant CrudForm submit scope would otherwise
111
+ // leak the variant's version onto the catalog/prices guard) (#2055).
112
+ const [existingPriceVersions, setExistingPriceVersions] = React.useState<Record<string, string | null>>({})
96
113
  const [loading, setLoading] = React.useState(true)
97
114
  const [error, setError] = React.useState<string | null>(null)
98
115
  const [isNotFound, setIsNotFound] = React.useState(false)
@@ -191,10 +208,15 @@ export default function EditVariantPage({ params }: { params?: { productId?: str
191
208
  const attachments = await fetchVariantAttachments(variantId!)
192
209
  const priceDrafts = await loadVariantPrices(variantId!, priceKinds)
193
210
  const priceIdMap: Record<string, string> = {}
211
+ const priceVersionMap: Record<string, string | null> = {}
194
212
  Object.entries(priceDrafts).forEach(([kindId, draft]) => {
195
- if (draft.priceId) priceIdMap[kindId] = draft.priceId
213
+ if (draft.priceId) {
214
+ priceIdMap[kindId] = draft.priceId
215
+ priceVersionMap[kindId] = draft.updatedAt ?? null
216
+ }
196
217
  })
197
218
  setExistingPriceIds(priceIdMap)
219
+ setExistingPriceVersions(priceVersionMap)
198
220
  const customDefaults = extractCustomFieldEntries(record)
199
221
  let loadedOptionDefinitions: OptionDefinition[] = []
200
222
  if (resolvedProductId) {
@@ -300,6 +322,12 @@ export default function EditVariantPage({ params }: { params?: { productId?: str
300
322
  : typeof record.customFieldsetCode === 'string'
301
323
  ? record.customFieldsetCode
302
324
  : null,
325
+ updatedAt:
326
+ typeof record.updatedAt === 'string'
327
+ ? record.updatedAt
328
+ : typeof record.updated_at === 'string'
329
+ ? record.updated_at
330
+ : null,
303
331
  ...customDefaults,
304
332
  })
305
333
  }
@@ -429,6 +457,9 @@ export default function EditVariantPage({ params }: { params?: { productId?: str
429
457
  : t('catalog.variants.form.editTitle', 'Edit variant')
430
458
  const productVariantsHref = `/backend/catalog/products/${currentProductId}#variants`
431
459
 
460
+ // When the variant was deleted (e.g. concurrently in another tab) the GET
461
+ // returns no record. Render a dedicated not-found state with a recovery link
462
+ // instead of an empty CrudForm that throws runtime errors (#2055 QA).
432
463
  if (isNotFound) {
433
464
  return (
434
465
  <Page>
@@ -549,6 +580,7 @@ export default function EditVariantPage({ params }: { params?: { productId?: str
549
580
  priceKinds,
550
581
  priceDrafts: values.prices ?? {},
551
582
  existingPriceIds,
583
+ existingPriceVersions,
552
584
  productId: currentProductId,
553
585
  variantId,
554
586
  taxRates,
@@ -560,9 +592,14 @@ export default function EditVariantPage({ params }: { params?: { productId?: str
560
592
  router.push(productVariantsHref)
561
593
  }}
562
594
  onDelete={async () => {
563
- await deleteCrud('catalog/variants', variantId!, {
564
- errorMessage: t('catalog.variants.form.deleteError', 'Failed to delete variant.'),
565
- })
595
+ try {
596
+ await deleteCrud('catalog/variants', variantId!, {
597
+ errorMessage: t('catalog.variants.form.deleteError', 'Failed to delete variant.'),
598
+ })
599
+ } catch (err) {
600
+ handleVariantDeleteError(err, t)
601
+ throw err
602
+ }
566
603
  flash(t('catalog.variants.form.deleted', 'Variant deleted.'), 'success')
567
604
  router.push(productVariantsHref)
568
605
  }}
@@ -669,6 +706,7 @@ async function syncVariantPricesUpdate({
669
706
  priceKinds,
670
707
  priceDrafts,
671
708
  existingPriceIds,
709
+ existingPriceVersions,
672
710
  productId,
673
711
  variantId,
674
712
  taxRates,
@@ -679,6 +717,7 @@ async function syncVariantPricesUpdate({
679
717
  priceKinds: PriceKindSummary[]
680
718
  priceDrafts: Record<string, VariantPriceDraft>
681
719
  existingPriceIds: Record<string, string>
720
+ existingPriceVersions: Record<string, string | null>
682
721
  productId: string
683
722
  variantId: string
684
723
  taxRates: TaxRateSummary[]
@@ -700,10 +739,17 @@ async function syncVariantPricesUpdate({
700
739
  const draft = priceDrafts?.[kind.id]
701
740
  const amount = typeof draft?.amount === 'string' ? draft.amount.trim() : ''
702
741
  const existingId = draft?.priceId ?? existingPriceIds[kind.id]
742
+ // The price's own version — overrides the variant header the parent CrudForm
743
+ // submit scope put on the stack, so the catalog/prices guard compares the
744
+ // right row (otherwise a stale/false 409). #2055.
745
+ const lockVersion = draft?.updatedAt ?? existingPriceVersions[kind.id] ?? null
703
746
  if (!amount) {
704
747
  if (existingId) {
705
748
  try {
706
- await deleteCrud('catalog/prices', existingId)
749
+ await withScopedApiRequestHeaders(
750
+ buildOptimisticLockHeader(lockVersion),
751
+ () => deleteCrud('catalog/prices', existingId),
752
+ )
707
753
  } catch (err) {
708
754
  console.error('catalog.prices.delete', err)
709
755
  }
@@ -723,7 +769,10 @@ async function syncVariantPricesUpdate({
723
769
  if (kind.displayMode === 'including-tax') payload.unitPriceGross = numeric
724
770
  else payload.unitPriceNet = numeric
725
771
  if (existingId) {
726
- await updateCrud('catalog/prices', { id: existingId, ...payload })
772
+ await withScopedApiRequestHeaders(
773
+ buildOptimisticLockHeader(lockVersion),
774
+ () => updateCrud('catalog/prices', { id: existingId, ...payload }),
775
+ )
727
776
  } else {
728
777
  await createCrud('catalog/prices', payload)
729
778
  }
@@ -20,6 +20,8 @@ export type OptionSchemaTemplateSummary = {
20
20
  name?: string | null
21
21
  description?: string | null
22
22
  schema?: OptionSchemaRecord | null
23
+ updatedAt?: string | null
24
+ updated_at?: string | null
23
25
  }
24
26
 
25
27
  type OptionSchemaTemplateListResponse = {