@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
@@ -3,6 +3,7 @@ import { z } from 'zod'
3
3
  import { Dictionary } from '@open-mercato/core/modules/dictionaries/data/entities'
4
4
  import { resolveDictionariesRouteContext } from '@open-mercato/core/modules/dictionaries/api/context'
5
5
  import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
6
+ import { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
6
7
  import type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
7
8
  import {
8
9
  resolveDictionaryEntrySortMode,
@@ -16,10 +17,18 @@ import {
16
17
  dictionaryUpdateSchema,
17
18
  upsertDictionarySchema,
18
19
  } from '../openapi'
20
+ import { dictionaryKeySchema } from '@open-mercato/core/modules/dictionaries/data/validators'
19
21
 
20
22
  const paramsSchema = z.object({ dictionaryId: z.string().uuid() })
23
+ // System dictionaries use namespaced keys (e.g. `sales.deal_loss_reason`,
24
+ // `resources.activity-types`) that the strict create-key regex rejects. The
25
+ // manager edit dialog disables the key field but still resubmits the existing
26
+ // key, so the update parse must accept any stored key verbatim. The strict
27
+ // user-key regex is only enforced below when the key actually changes.
28
+ const updateKeySchema = z.string().trim().min(1).max(100)
21
29
  const updateSchema = upsertDictionarySchema
22
30
  .partial()
31
+ .extend({ key: updateKeySchema.optional() })
23
32
  .refine((data) => Object.keys(data).length > 0, {
24
33
  message: 'Provide at least one field to update.',
25
34
  })
@@ -102,6 +111,13 @@ export async function PATCH(req: Request, ctx: { params?: { dictionaryId?: strin
102
111
  const payload = updateSchema.parse(await req.json().catch(() => ({})))
103
112
  const dictionary = await loadDictionary(context, dictionaryId)
104
113
 
114
+ enforceCommandOptimisticLock({
115
+ resourceKind: 'dictionaries.dictionary',
116
+ resourceId: dictionary.id,
117
+ current: dictionary.updatedAt ?? null,
118
+ request: req,
119
+ })
120
+
105
121
  if (isProtectedCurrencyDictionary(dictionary)) {
106
122
  if (payload.key && payload.key.trim().toLowerCase() !== dictionary.key) {
107
123
  throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.currency_protected', 'The currency dictionary cannot be modified or deleted.') })
@@ -114,6 +130,10 @@ export async function PATCH(req: Request, ctx: { params?: { dictionaryId?: strin
114
130
  if (payload.key) {
115
131
  const key = payload.key.trim().toLowerCase()
116
132
  if (key !== dictionary.key) {
133
+ const strictKey = dictionaryKeySchema.safeParse(key)
134
+ if (!strictKey.success) {
135
+ throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.invalid_key', 'Use lowercase letters, numbers, hyphen, or underscore.') })
136
+ }
117
137
  const organizationId = context.organizationId
118
138
  if (!organizationId) {
119
139
  throw new CrudHttpError(400, { error: context.translate('dictionaries.errors.organization_required', 'Organization context is required') })
@@ -168,6 +188,9 @@ export async function PATCH(req: Request, ctx: { params?: { dictionaryId?: strin
168
188
  if (isCrudHttpError(err)) {
169
189
  return NextResponse.json(err.body, { status: err.status })
170
190
  }
191
+ if (err instanceof z.ZodError) {
192
+ return NextResponse.json({ error: err.issues[0]?.message ?? 'Validation failed' }, { status: 400 })
193
+ }
171
194
  console.error('[dictionaries/:id.PATCH] Unexpected error', err)
172
195
  return NextResponse.json({ error: 'Failed to update dictionary' }, { status: 500 })
173
196
  }
@@ -15,7 +15,9 @@ import {
15
15
  } from '@open-mercato/ui/primitives/select'
16
16
  import { Spinner } from '@open-mercato/ui/primitives/spinner'
17
17
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
18
- import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
18
+ import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
19
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
20
+ import { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'
19
21
  import { useT } from '@open-mercato/shared/lib/i18n/context'
20
22
  import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
21
23
  import { DictionaryEntriesEditor } from './DictionaryEntriesEditor'
@@ -36,6 +38,7 @@ export type DictionarySummary = {
36
38
  organizationId: string
37
39
  isInherited: boolean
38
40
  managerVisibility: 'default' | 'hidden'
41
+ updatedAt?: string | null
39
42
  }
40
43
 
41
44
  type DialogState = {
@@ -113,6 +116,7 @@ export function DictionariesManager() {
113
116
  isInherited: item.isInherited === true,
114
117
  managerVisibility:
115
118
  item.managerVisibility === 'hidden' ? 'hidden' : 'default',
119
+ updatedAt: typeof item.updatedAt === 'string' ? item.updatedAt : null,
116
120
  }))
117
121
  : []
118
122
  const filtered = list.filter((dictionary: DictionarySummary) => dictionary.managerVisibility !== 'hidden')
@@ -232,13 +236,20 @@ export function DictionariesManager() {
232
236
  }
233
237
  flash(t('dictionaries.config.success.create', 'Dictionary created.'), 'success')
234
238
  } else if (dialog.dictionary) {
235
- const call = await apiCall<Record<string, unknown>>(`/api/dictionaries/${dialog.dictionary.id}`, {
236
- method: 'PATCH',
237
- headers: { 'content-type': 'application/json' },
238
- body: JSON.stringify(payload),
239
- })
239
+ const call = await withScopedApiRequestHeaders(
240
+ buildOptimisticLockHeader(dialog.dictionary.updatedAt),
241
+ () =>
242
+ apiCall<Record<string, unknown>>(`/api/dictionaries/${dialog.dictionary!.id}`, {
243
+ method: 'PATCH',
244
+ headers: { 'content-type': 'application/json' },
245
+ body: JSON.stringify(payload),
246
+ }),
247
+ )
240
248
  if (!call.ok) {
241
- throw new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to update dictionary')
249
+ throw Object.assign(
250
+ new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to update dictionary'),
251
+ { status: call.status, ...(call.result && typeof call.result === 'object' ? call.result : {}) },
252
+ )
242
253
  }
243
254
  flash(t('dictionaries.config.success.update', 'Dictionary updated.'), 'success')
244
255
  }
@@ -246,6 +257,9 @@ export function DictionariesManager() {
246
257
  await loadDictionaries()
247
258
  setErrors({})
248
259
  } catch (err) {
260
+ if (surfaceRecordConflict(err, t)) {
261
+ return
262
+ }
249
263
  console.error('Failed to save dictionary', err)
250
264
  flash(t('dictionaries.config.error.save', 'Failed to save dictionary.'), 'error')
251
265
  } finally {
@@ -274,13 +288,22 @@ export function DictionariesManager() {
274
288
  if (!confirmed) return
275
289
  setDeleting(dictionary.id)
276
290
  try {
277
- const call = await apiCall<Record<string, unknown>>(`/api/dictionaries/${dictionary.id}`, { method: 'DELETE' })
291
+ const call = await withScopedApiRequestHeaders(
292
+ buildOptimisticLockHeader(dictionary.updatedAt),
293
+ () => apiCall<Record<string, unknown>>(`/api/dictionaries/${dictionary.id}`, { method: 'DELETE' }),
294
+ )
278
295
  if (!call.ok) {
279
- throw new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to delete dictionary')
296
+ throw Object.assign(
297
+ new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to delete dictionary'),
298
+ { status: call.status, ...(call.result && typeof call.result === 'object' ? call.result : {}) },
299
+ )
280
300
  }
281
301
  flash(t('dictionaries.config.success.delete', 'Dictionary deleted.'), 'success')
282
302
  await loadDictionaries()
283
303
  } catch (err) {
304
+ if (surfaceRecordConflict(err, t)) {
305
+ return
306
+ }
284
307
  console.error('Failed to delete dictionary', err)
285
308
  flash(t('dictionaries.config.error.delete', 'Failed to delete dictionary.'), 'error')
286
309
  } finally {
@@ -14,7 +14,9 @@ import {
14
14
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@open-mercato/ui/primitives/table'
15
15
  import { Spinner } from '@open-mercato/ui/primitives/spinner'
16
16
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
17
- import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
17
+ import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
18
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
19
+ import { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'
18
20
  import { useQueryClient } from '@tanstack/react-query'
19
21
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
20
22
  import { useT } from '@open-mercato/shared/lib/i18n/context'
@@ -45,6 +47,7 @@ type FormState = {
45
47
  label: string
46
48
  color: string | null
47
49
  icon: string | null
50
+ updatedAt?: string | null
48
51
  }
49
52
 
50
53
  export function DictionaryEntriesEditor({ dictionaryId, dictionaryName, readOnly = false }: DictionaryEntriesEditorProps) {
@@ -93,6 +96,7 @@ export function DictionaryEntriesEditor({ dictionaryId, dictionaryName, readOnly
93
96
  label: entry.label,
94
97
  color: entry.color ?? null,
95
98
  icon: entry.icon ?? null,
99
+ updatedAt: entry.updatedAt ?? null,
96
100
  })
97
101
  appearance.setColor(entry.color ?? null)
98
102
  appearance.setIcon(entry.icon ?? null)
@@ -136,17 +140,22 @@ export function DictionaryEntriesEditor({ dictionaryId, dictionaryName, readOnly
136
140
  icon: appearance.icon,
137
141
  }
138
142
  if (formState.id) {
139
- const call = await apiCall<Record<string, unknown>>(
140
- `/api/dictionaries/${dictionaryId}/entries/${formState.id}`,
141
- {
142
- method: 'PATCH',
143
- headers: { 'content-type': 'application/json' },
144
- body: JSON.stringify(payload),
145
- },
143
+ const call = await withScopedApiRequestHeaders(
144
+ buildOptimisticLockHeader(formState.updatedAt),
145
+ () =>
146
+ apiCall<Record<string, unknown>>(
147
+ `/api/dictionaries/${dictionaryId}/entries/${formState.id}`,
148
+ {
149
+ method: 'PATCH',
150
+ headers: { 'content-type': 'application/json' },
151
+ body: JSON.stringify(payload),
152
+ },
153
+ ),
146
154
  )
147
155
  if (!call.ok) {
148
- throw new Error(
149
- typeof call.result?.error === 'string' ? call.result.error : 'Failed to save dictionary entry',
156
+ throw Object.assign(
157
+ new Error(typeof call.result?.error === 'string' ? call.result.error : 'Failed to save dictionary entry'),
158
+ { status: call.status, ...(call.result && typeof call.result === 'object' ? call.result : {}) },
150
159
  )
151
160
  }
152
161
  flash(t('dictionaries.config.entries.success.update', 'Dictionary entry updated.'), 'success')
@@ -173,12 +182,15 @@ export function DictionaryEntriesEditor({ dictionaryId, dictionaryName, readOnly
173
182
  appearance.setIcon(null)
174
183
  setErrors({})
175
184
  } catch (err) {
185
+ if (surfaceRecordConflict(err, t)) {
186
+ return
187
+ }
176
188
  console.error('Failed to save dictionary entry', err)
177
189
  flash(t('dictionaries.config.entries.error.save', 'Failed to save dictionary entry.'), 'error')
178
190
  } finally {
179
191
  setIsSaving(false)
180
192
  }
181
- }, [appearance, dictionaryId, formState.id, formState.label, formState.value, queryClient, readOnly, readOnlyMessage, t])
193
+ }, [appearance, dictionaryId, formState.id, formState.label, formState.updatedAt, formState.value, queryClient, readOnly, readOnlyMessage, t])
182
194
 
183
195
  const handleDelete = React.useCallback(
184
196
  async (entry: Entry) => {
@@ -194,9 +206,13 @@ export function DictionaryEntriesEditor({ dictionaryId, dictionaryName, readOnly
194
206
  if (!confirmDelete) return
195
207
  setIsDeleting(true)
196
208
  try {
197
- const call = await apiCall<Record<string, unknown>>(
198
- `/api/dictionaries/${dictionaryId}/entries/${entry.id}`,
199
- { method: 'DELETE' },
209
+ const call = await withScopedApiRequestHeaders(
210
+ buildOptimisticLockHeader(entry.updatedAt),
211
+ () =>
212
+ apiCall<Record<string, unknown>>(
213
+ `/api/dictionaries/${dictionaryId}/entries/${entry.id}`,
214
+ { method: 'DELETE' },
215
+ ),
200
216
  )
201
217
  if (!call.ok) {
202
218
  throw new Error(
@@ -109,6 +109,7 @@
109
109
  "dictionaries.errors.entry_not_found": "Wörterbucheintrag nicht gefunden",
110
110
  "dictionaries.errors.entry_required": "Wert ist erforderlich",
111
111
  "dictionaries.errors.entry_update_failed": "Wörterbucheintrag konnte nicht aktualisiert werden.",
112
+ "dictionaries.errors.invalid_key": "Verwenden Sie Kleinbuchstaben, Zahlen, Bindestrich oder Unterstrich.",
112
113
  "dictionaries.errors.not_found": "Wörterbuch nicht gefunden",
113
114
  "dictionaries.errors.organization_required": "Organisationskontext ist erforderlich",
114
115
  "dictionaries.errors.unauthorized": "Nicht autorisiert",
@@ -109,6 +109,7 @@
109
109
  "dictionaries.errors.entry_not_found": "Dictionary entry not found",
110
110
  "dictionaries.errors.entry_required": "Value is required",
111
111
  "dictionaries.errors.entry_update_failed": "Failed to update dictionary entry.",
112
+ "dictionaries.errors.invalid_key": "Use lowercase letters, numbers, hyphen, or underscore.",
112
113
  "dictionaries.errors.not_found": "Dictionary not found",
113
114
  "dictionaries.errors.organization_required": "Organization context is required",
114
115
  "dictionaries.errors.unauthorized": "Unauthorized",
@@ -109,6 +109,7 @@
109
109
  "dictionaries.errors.entry_not_found": "Entrada de diccionario no encontrada",
110
110
  "dictionaries.errors.entry_required": "El valor es obligatorio",
111
111
  "dictionaries.errors.entry_update_failed": "Error al actualizar la entrada de diccionario.",
112
+ "dictionaries.errors.invalid_key": "Usa letras minúsculas, números, guion o guion bajo.",
112
113
  "dictionaries.errors.not_found": "Diccionario no encontrado",
113
114
  "dictionaries.errors.organization_required": "Se requiere un contexto de organización",
114
115
  "dictionaries.errors.unauthorized": "No autorizado",
@@ -109,6 +109,7 @@
109
109
  "dictionaries.errors.entry_not_found": "Nie znaleziono wpisu słownika",
110
110
  "dictionaries.errors.entry_required": "Wartość jest wymagana",
111
111
  "dictionaries.errors.entry_update_failed": "Nie udało się zaktualizować wpisu słownikowego.",
112
+ "dictionaries.errors.invalid_key": "Użyj małych liter, cyfr, łącznika lub podkreślenia.",
112
113
  "dictionaries.errors.not_found": "Nie znaleziono słownika",
113
114
  "dictionaries.errors.organization_required": "Wymagany jest kontekst organizacji",
114
115
  "dictionaries.errors.unauthorized": "Brak autoryzacji",
@@ -446,8 +446,10 @@ export async function GET(req: Request) {
446
446
  const orgs = await em.find(Organization, orgListFilter, { orderBy: { name: 'ASC' } })
447
447
  const hierarchy = computeHierarchyForOrganizations(orgs, tenantId)
448
448
  const slugByOrgId = new Map<string, string | null>()
449
+ const updatedAtByOrgId = new Map<string, string | null>()
449
450
  for (const org of orgs) {
450
451
  slugByOrgId.set(String(org.id), org.slug ?? null)
452
+ updatedAtByOrgId.set(String(org.id), org.updatedAt instanceof Date ? org.updatedAt.toISOString() : null)
451
453
  }
452
454
 
453
455
  // Manage view: paginated flat list for a single tenant
@@ -510,6 +512,7 @@ export async function GET(req: Request) {
510
512
  id: node.id,
511
513
  name: node.name,
512
514
  slug: slugByOrgId.get(recordId) ?? null,
515
+ updatedAt: updatedAtByOrgId.get(recordId) ?? null,
513
516
  tenantId: node.tenantId,
514
517
  tenantName,
515
518
  parentId: node.parentId,
@@ -35,6 +35,8 @@ type OrganizationResponse = {
35
35
  descendantIds: string[]
36
36
  isActive: boolean
37
37
  pathLabel: string
38
+ updatedAt?: string | null
39
+ updated_at?: string | null
38
40
  } & Record<string, unknown>>
39
41
  }
40
42
 
@@ -145,6 +147,11 @@ export default function EditOrganizationPage({ params }: { params?: { id?: strin
145
147
  parentId: record.parentId || '',
146
148
  isActive: record.isActive,
147
149
  tenantId: resolvedTenantId,
150
+ updatedAt: typeof record.updatedAt === 'string'
151
+ ? record.updatedAt
152
+ : typeof record.updated_at === 'string'
153
+ ? record.updated_at
154
+ : null,
148
155
  ...customValues,
149
156
  })
150
157
  setPathLabel(record.pathLabel)
@@ -310,6 +317,7 @@ export default function EditOrganizationPage({ params }: { params?: { id?: strin
310
317
  groups={groups}
311
318
  entityId={E.directory.organization}
312
319
  initialValues={initialValues ?? { id: orgId, tenantId: tenantId ?? null, name: '', slug: '', parentId: '', isActive: true, childIds: [] }}
320
+ optimisticLockUpdatedAt={typeof initialValues?.updatedAt === 'string' ? initialValues.updatedAt : null}
313
321
  isLoading={loading}
314
322
  loadingMessage={t('directory.organizations.form.loading', 'Loading organization...')}
315
323
  submitLabel={t('directory.organizations.form.action.save', 'Save')}
@@ -9,7 +9,8 @@ import { RowActions } from '@open-mercato/ui/backend/RowActions'
9
9
  import type { FilterValues } from '@open-mercato/ui/backend/FilterBar'
10
10
  import { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'
11
11
  import { Button } from '@open-mercato/ui/primitives/button'
12
- import { apiCall, apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
12
+ import { apiCall, apiCallOrThrow, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
13
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
13
14
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
14
15
  import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
15
16
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
@@ -32,6 +33,7 @@ type OrganizationRow = {
32
33
  childrenCount: number
33
34
  descendantsCount: number
34
35
  isActive: boolean
36
+ updatedAt?: string | null
35
37
  }
36
38
 
37
39
  type OrganizationsResponse = {
@@ -184,10 +186,13 @@ export default function DirectoryOrganizationsPage() {
184
186
  if (!confirmed) return
185
187
 
186
188
  try {
187
- await apiCallOrThrow(
188
- `/api/directory/organizations?id=${encodeURIComponent(org.id)}`,
189
- { method: 'DELETE' },
190
- { errorMessage: t('directory.organizations.list.error.delete', 'Failed to delete organization') },
189
+ await withScopedApiRequestHeaders(
190
+ buildOptimisticLockHeader(org.updatedAt),
191
+ () => apiCallOrThrow(
192
+ `/api/directory/organizations?id=${encodeURIComponent(org.id)}`,
193
+ { method: 'DELETE' },
194
+ { errorMessage: t('directory.organizations.list.error.delete', 'Failed to delete organization') },
195
+ ),
191
196
  )
192
197
  await queryClient.invalidateQueries({ queryKey: ['directory-organizations'] })
193
198
  flash(t('directory.organizations.flash.deleted', 'Organization deleted'), 'success')
@@ -6,7 +6,8 @@ import { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/b
6
6
  import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'
7
7
  import { updateCrud } from '@open-mercato/ui/backend/utils/crud'
8
8
  import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
9
- import { readApiResultOrThrow, apiCall } from '@open-mercato/ui/backend/utils/apiCall'
9
+ import { readApiResultOrThrow, apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
10
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
10
11
  import { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'
11
12
  import { useT } from '@open-mercato/shared/lib/i18n/context'
12
13
  import { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'
@@ -15,6 +16,7 @@ type TenantFormValues = {
15
16
  id: string
16
17
  name: string
17
18
  isActive: boolean
19
+ updatedAt?: string | null
18
20
  } & Record<string, unknown>
19
21
 
20
22
  export default function EditTenantPage({ params }: { params?: { id?: string } }) {
@@ -57,6 +59,11 @@ export default function EditTenantPage({ params }: { params?: { id?: string } })
57
59
  id: String(row.id),
58
60
  name: String(row.name),
59
61
  isActive: !!row.isActive,
62
+ updatedAt: typeof row.updatedAt === 'string'
63
+ ? row.updatedAt
64
+ : typeof row.updated_at === 'string'
65
+ ? row.updated_at
66
+ : null,
60
67
  ...cfValues,
61
68
  }
62
69
  if (!cancelled) setInitial(values)
@@ -114,6 +121,7 @@ export default function EditTenantPage({ params }: { params?: { id?: string } })
114
121
  groups={groups}
115
122
  entityId={E.directory.tenant}
116
123
  initialValues={(initial || { id: tenantId, name: '', isActive: true }) as Partial<TenantFormValues>}
124
+ optimisticLockUpdatedAt={initial?.updatedAt}
117
125
  isLoading={loading}
118
126
  loadingMessage={t('directory.tenants.form.loading', 'Loading tenant…')}
119
127
  submitLabel={t('common.save', 'Save')}
@@ -137,10 +145,13 @@ export default function EditTenantPage({ params }: { params?: { id?: string } })
137
145
  await updateCrud('directory/tenants', payload)
138
146
  }}
139
147
  onDelete={async () => {
140
- const call = await apiCall(
141
- `/api/directory/tenants?id=${encodeURIComponent(tenantId)}`,
142
- { method: 'DELETE' },
143
- )
148
+ const headers = buildOptimisticLockHeader(initial?.updatedAt)
149
+ const call = await withScopedApiRequestHeaders(headers, () => (
150
+ apiCall(
151
+ `/api/directory/tenants?id=${encodeURIComponent(tenantId)}`,
152
+ { method: 'DELETE' },
153
+ )
154
+ ))
144
155
  if (!call.ok) {
145
156
  await raiseCrudError(call.response, t('directory.tenants.form.errors.delete', 'Failed to delete tenant'))
146
157
  }
@@ -9,7 +9,8 @@ import { RowActions } from '@open-mercato/ui/backend/RowActions'
9
9
  import { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'
10
10
  import type { FilterValues } from '@open-mercato/ui/backend/FilterBar'
11
11
  import { Button } from '@open-mercato/ui/primitives/button'
12
- import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
12
+ import { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
13
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
13
14
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
14
15
  import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
15
16
  import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
@@ -125,9 +126,12 @@ export default function DirectoryTenantsPage() {
125
126
  if (!confirmed) return
126
127
 
127
128
  try {
128
- const call = await apiCall(
129
- `/api/directory/tenants?id=${encodeURIComponent(tenant.id)}`,
130
- { method: 'DELETE' },
129
+ const call = await withScopedApiRequestHeaders(
130
+ buildOptimisticLockHeader(tenant.updatedAt),
131
+ () => apiCall(
132
+ `/api/directory/tenants?id=${encodeURIComponent(tenant.id)}`,
133
+ { method: 'DELETE' },
134
+ ),
131
135
  )
132
136
  if (!call.ok) {
133
137
  await raiseCrudError(call.response, t('directory.tenants.list.error.delete', 'Failed to delete tenant'))
@@ -689,7 +689,10 @@ const deleteOrganizationCommand: CommandHandler<{ body: any; query: Record<strin
689
689
  setInternalTenantId(deleted, tenantId)
690
690
  deleted.isActive = false
691
691
  deleted.parentId = null
692
-
692
+ resolvedDeleted = deleted
693
+ },
694
+ async () => {
695
+ const deleted = resolvedDeleted
693
696
  const childrenFilter: FilterQuery<Organization> = { tenant: tenantId, parentId: id, deletedAt: null }
694
697
  const children = await em.find(Organization, childrenFilter)
695
698
  const toPersist: Organization[] = []
@@ -698,11 +701,11 @@ const deleteOrganizationCommand: CommandHandler<{ body: any; query: Record<strin
698
701
  toPersist.push(child)
699
702
  }
700
703
  toPersist.push(deleted)
701
- if (toPersist.length) await em.persist(toPersist).flush()
704
+ if (toPersist.length) em.persist(toPersist)
702
705
  setUndoMeta(deleted, { childParentsBefore: childSnapshotsBefore })
703
-
706
+ },
707
+ async () => {
704
708
  await rebuildHierarchyForTenant(em, tenantId)
705
- resolvedDeleted = deleted
706
709
  },
707
710
  ], { transaction: true })
708
711
 
@@ -10,6 +10,34 @@ import { parseBooleanToken, parseBooleanWithDefault } from '@open-mercato/shared
10
10
  import { setRecordCustomFields } from '../lib/helpers'
11
11
  import { CustomFieldValue } from '../data/entities'
12
12
  import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
13
+ import { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
14
+ import { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
15
+
16
+ const CUSTOM_ENTITY_RECORD_RESOURCE_KIND = 'entities.record'
17
+
18
+ async function readCustomEntityRecordUpdatedAt(
19
+ em: any,
20
+ input: { entityType: string; entityId: string; organizationId: string | null },
21
+ ): Promise<string | null> {
22
+ try {
23
+ const db = em.getKysely()
24
+ let query = db
25
+ .selectFrom('custom_entities_storage' as any)
26
+ .select(['updated_at' as any])
27
+ .where('entity_type' as any, '=', input.entityType)
28
+ .where('entity_id' as any, '=', input.entityId)
29
+ query = input.organizationId === null
30
+ ? query.where('organization_id' as any, 'is', null as any)
31
+ : query.where('organization_id' as any, '=', input.organizationId)
32
+ const row = await query.executeTakeFirst()
33
+ const value = (row as any)?.updated_at
34
+ if (value instanceof Date) return value.toISOString()
35
+ if (typeof value === 'string' && value.length > 0) return value
36
+ return null
37
+ } catch {
38
+ return null
39
+ }
40
+ }
13
41
 
14
42
  export const metadata = {
15
43
  GET: { requireAuth: true, requireFeatures: ['entities.records.view'] },
@@ -85,6 +113,23 @@ export async function GET(req: Request) {
85
113
  const found = await em.findOne(CustomEntity as any, { entityId, isActive: true })
86
114
  isCustomEntity = !!found
87
115
  } catch {}
116
+ // Read/write symmetry: this endpoint writes every record to custom_entities_storage
117
+ // via the data engine, including module-declared custom entities whose id is a
118
+ // frozen system id and therefore never registered in `custom_entities`. Detect
119
+ // those by their doc-storage rows so `mapRow` strips the `cf_` prefix and the edit
120
+ // form can read back the saved values (mirrors HybridQueryEngine.isCustomEntity).
121
+ if (!isCustomEntity) {
122
+ try {
123
+ const db = em.getKysely()
124
+ const row = await db
125
+ .selectFrom('custom_entities_storage' as any)
126
+ .select(['entity_id' as any])
127
+ .where('entity_type' as any, '=', entityId)
128
+ .limit(1)
129
+ .executeTakeFirst()
130
+ isCustomEntity = !!row
131
+ } catch {}
132
+ }
88
133
  if (organizationIds && organizationIds.length === 0) {
89
134
  return NextResponse.json({ items: [], total: 0, page, pageSize, totalPages: 0 })
90
135
  }
@@ -164,6 +209,41 @@ export async function GET(req: Request) {
164
209
  const rawItems = res.items || []
165
210
  const viewPageItems = rawItems.map(mapRow)
166
211
  const fullPageItems = rawItems.map(mapFullRow)
212
+
213
+ // Expose `updated_at` on custom-entity records. The query engine returns only
214
+ // the `doc` fields + `id`, dropping the base `updated_at` column — which made
215
+ // optimistic locking impossible end-to-end (no version for the edit page to
216
+ // round-trip as the lock header). Batch-read it from storage and merge it in.
217
+ if (isCustomEntity && viewPageItems.length) {
218
+ try {
219
+ const recordIds = viewPageItems
220
+ .map((it: any) => it?.id)
221
+ .filter((v: any): v is string => typeof v === 'string' && v.length > 0)
222
+ if (recordIds.length) {
223
+ const db = em.getKysely()
224
+ const rows = await db
225
+ .selectFrom('custom_entities_storage' as any)
226
+ .select(['entity_id' as any, 'updated_at' as any])
227
+ .where('entity_type' as any, '=', entityId)
228
+ .where('entity_id' as any, 'in', recordIds as any)
229
+ .execute()
230
+ const updatedById = new Map<string, string>()
231
+ for (const row of rows as any[]) {
232
+ const value = row?.updated_at
233
+ const iso = value instanceof Date ? value.toISOString() : (typeof value === 'string' && value.length > 0 ? value : null)
234
+ if (iso && row?.entity_id) updatedById.set(String(row.entity_id), iso)
235
+ }
236
+ for (const item of viewPageItems as any[]) {
237
+ const iso = updatedById.get(String(item?.id))
238
+ if (iso) {
239
+ item.updated_at = iso
240
+ item.updatedAt = iso
241
+ }
242
+ }
243
+ }
244
+ } catch { /* best-effort: locking simply will not engage if storage is unavailable */ }
245
+ }
246
+
167
247
  const total = typeof res.total === 'number' ? res.total : rawItems.length
168
248
  const effectivePageSize = res.pageSize || pageSize
169
249
  const payload = {
@@ -343,6 +423,25 @@ export async function PUT(req: Request) {
343
423
  return NextResponse.json({ ok: true, item: { entityId, recordId: created.id } })
344
424
  }
345
425
 
426
+ try {
427
+ const currentUpdatedAt = await readCustomEntityRecordUpdatedAt(em, {
428
+ entityType: entityId,
429
+ entityId: rid,
430
+ organizationId: targetOrgId,
431
+ })
432
+ enforceCommandOptimisticLock({
433
+ resourceKind: CUSTOM_ENTITY_RECORD_RESOURCE_KIND,
434
+ resourceId: rid,
435
+ current: currentUpdatedAt,
436
+ request: req,
437
+ })
438
+ } catch (lockError) {
439
+ if (isCrudHttpError(lockError)) {
440
+ return NextResponse.json(lockError.body, { status: lockError.status })
441
+ }
442
+ throw lockError
443
+ }
444
+
346
445
  await de.updateCustomEntityRecord({
347
446
  entityId,
348
447
  recordId: rid,
@@ -79,6 +79,13 @@ export default function EditRecordPage({ params }: { params: { entityId?: string
79
79
  entityId={entityId}
80
80
  customEntity
81
81
  initialValues={initialValues || {}}
82
+ optimisticLockUpdatedAt={
83
+ typeof initialValues?.updatedAt === 'string'
84
+ ? initialValues.updatedAt
85
+ : typeof initialValues?.updated_at === 'string'
86
+ ? initialValues.updated_at
87
+ : null
88
+ }
82
89
  isLoading={loading}
83
90
  loadingMessage={t('entities.userEntities.records.loading', 'Loading record...')}
84
91
  submitLabel={t('entities.userEntities.records.form.submitSave', 'Save')}
@@ -11,7 +11,8 @@ import { ContextHelp } from '@open-mercato/ui/backend/ContextHelp'
11
11
  import { Button } from '@open-mercato/ui/primitives/button'
12
12
  import { RowActions } from '@open-mercato/ui/backend/RowActions'
13
13
  import Link from 'next/link'
14
- import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
14
+ import { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
15
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
15
16
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
16
17
  import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
17
18
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
@@ -349,9 +350,12 @@ export RECORD_ID="<record uuid>"`}</code></pre>
349
350
  variant: 'destructive',
350
351
  })
351
352
  if (!confirmed) return
352
- const deleteCall = await apiCall(
353
- `/api/entities/records?entityId=${encodeURIComponent(entityId)}&recordId=${encodeURIComponent(String((row as any).id))}`,
354
- { method: 'DELETE' },
353
+ const deleteCall = await withScopedApiRequestHeaders(
354
+ buildOptimisticLockHeader((row as any).updatedAt),
355
+ () => apiCall(
356
+ `/api/entities/records?entityId=${encodeURIComponent(entityId)}&recordId=${encodeURIComponent(String((row as any).id))}`,
357
+ { method: 'DELETE' },
358
+ ),
355
359
  )
356
360
  if (!deleteCall.ok) {
357
361
  await raiseCrudError(deleteCall.response, 'Failed to delete record')