@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
@@ -1,5 +1,6 @@
1
1
  import type { EntityManager } from '@mikro-orm/postgresql'
2
2
  import type { CacheStrategy } from '@open-mercato/cache'
3
+ import { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
3
4
  import { Perspective, RolePerspective } from '../data/entities'
4
5
  import type {
5
6
  PerspectiveSettings,
@@ -216,7 +217,12 @@ export async function loadPerspectivesState(
216
217
  export async function saveUserPerspective(
217
218
  em: EntityManager,
218
219
  cache: CacheStrategy | null | undefined,
219
- options: { scope: PerspectiveScope; tableId: string; input: PerspectiveSaveInput },
220
+ options: {
221
+ scope: PerspectiveScope
222
+ tableId: string
223
+ input: PerspectiveSaveInput
224
+ request?: Request | Headers | null
225
+ },
220
226
  ): Promise<ResolvedPerspective> {
221
227
  const { scope, tableId, input } = options
222
228
  const tenantId = scope.tenantId ?? null
@@ -235,6 +241,12 @@ export async function saveUserPerspective(
235
241
  if (!entity) {
236
242
  throw Object.assign(new Error('Perspective not found'), { code: 'NOT_FOUND' })
237
243
  }
244
+ enforceCommandOptimisticLock({
245
+ resourceKind: 'perspectives.perspective',
246
+ resourceId: entity.id,
247
+ current: entity.updatedAt ?? null,
248
+ request: options.request ?? null,
249
+ })
238
250
  } else {
239
251
  entity = await em.findOne(Perspective, {
240
252
  userId: scope.userId,
@@ -8,7 +8,6 @@ import { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/deta
8
8
  import { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
9
9
  import { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'
10
10
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
11
- import { normalizeCrudServerError } from '@open-mercato/ui/backend/utils/serverErrors'
12
11
  import { AvailabilityRulesEditor, type AvailabilityScheduleItemBuilder } from '@open-mercato/core/modules/planner/components/AvailabilityRulesEditor'
13
12
  import { parseAvailabilityRuleWindow } from '@open-mercato/core/modules/planner/lib/availabilitySchedule'
14
13
  import { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'
@@ -29,6 +28,7 @@ type RuleSetRecord = {
29
28
  description?: string | null
30
29
  timezone: string
31
30
  updatedAt?: string | null
31
+ updated_at?: string | null
32
32
  name_raw?: string | null
33
33
  } & Record<string, unknown>
34
34
 
@@ -40,6 +40,7 @@ export default function PlannerAvailabilityRuleSetDetailPage({ params }: { param
40
40
  const rulesetId = params?.id
41
41
  const translate = useT()
42
42
  const router = useRouter()
43
+ // optimistic-lock: AvailabilityRuleSetForm forwards optimisticLockUpdatedAt from initialValues.updatedAt (wrapper auto-derives the header on save + delete).
43
44
  const [initialValues, setInitialValues] = React.useState<AvailabilityRuleSetFormValues | null>(null)
44
45
  const [error, setError] = React.useState<string | null>(null)
45
46
  const [isNotFound, setIsNotFound] = React.useState(false)
@@ -73,6 +74,11 @@ export default function PlannerAvailabilityRuleSetDetailPage({ params }: { param
73
74
  name: record.name ?? record.name_raw ?? '',
74
75
  description: record.description ?? '',
75
76
  timezone: record.timezone ?? 'UTC',
77
+ updatedAt: typeof record.updatedAt === 'string'
78
+ ? record.updatedAt
79
+ : typeof record.updated_at === 'string'
80
+ ? record.updated_at
81
+ : null,
76
82
  ...customFields,
77
83
  })
78
84
  }
@@ -106,19 +112,15 @@ export default function PlannerAvailabilityRuleSetDetailPage({ params }: { param
106
112
 
107
113
  const handleDelete = React.useCallback(async () => {
108
114
  if (!rulesetId) return
109
- try {
110
- await deleteCrud('planner/availability-rule-sets', rulesetId, {
111
- errorMessage: translate('planner.availabilityRuleSets.form.errors.delete', 'Failed to delete schedule.'),
112
- })
113
- flash(translate('planner.availabilityRuleSets.form.flash.deleted', 'Schedule deleted.'), 'success')
114
- router.push('/backend/planner/availability-rulesets')
115
- } catch (error) {
116
- const normalized = normalizeCrudServerError(error)
117
- flash(
118
- normalized.message ?? translate('planner.availabilityRuleSets.form.errors.delete', 'Failed to delete schedule.'),
119
- 'error',
120
- )
121
- }
115
+ // Let a non-ok DELETE (e.g. the 409 optimistic-lock conflict) propagate so
116
+ // CrudForm surfaces the unified conflict bar and skips its success toast.
117
+ // Swallowing the error here made CrudForm treat the delete as successful and
118
+ // show a false "Schedule deleted." toast even though the record was kept.
119
+ await deleteCrud('planner/availability-rule-sets', rulesetId, {
120
+ errorMessage: translate('planner.availabilityRuleSets.form.errors.delete', 'Failed to delete schedule.'),
121
+ })
122
+ flash(translate('planner.availabilityRuleSets.form.flash.deleted', 'Schedule deleted.'), 'success')
123
+ router.push('/backend/planner/availability-rulesets')
122
124
  }, [router, rulesetId, translate])
123
125
 
124
126
  const buildScheduleItems: AvailabilityScheduleItemBuilder = React.useCallback(({ availabilityRules, bookedEvents, translate: translateLabel }) => {
@@ -9,8 +9,9 @@ import { markdownToPlainText } from '@open-mercato/ui/backend/markdown/markdownT
9
9
  import { DataTable, withDataTableNamespaces } from '@open-mercato/ui/backend/DataTable'
10
10
  import { RowActions } from '@open-mercato/ui/backend/RowActions'
11
11
  import { Button } from '@open-mercato/ui/primitives/button'
12
- import { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
12
+ import { readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
13
13
  import { deleteCrud } from '@open-mercato/ui/backend/utils/crud'
14
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
14
15
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
15
16
  import { normalizeCrudServerError } from '@open-mercato/ui/backend/utils/serverErrors'
16
17
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
@@ -126,7 +127,10 @@ export default function PlannerAvailabilityRuleSetsPage() {
126
127
  })
127
128
  if (!confirmed) return
128
129
  try {
129
- await deleteCrud('planner/availability-rule-sets', entry.id, { errorMessage: labels.errors.delete })
130
+ const headers = buildOptimisticLockHeader(entry.updatedAt)
131
+ await withScopedApiRequestHeaders(headers, () => (
132
+ deleteCrud('planner/availability-rule-sets', entry.id, { errorMessage: labels.errors.delete })
133
+ ))
130
134
  flash(labels.messages.deleted, 'success')
131
135
  handleRefresh()
132
136
  } catch (error) {
@@ -237,4 +241,3 @@ function mapRuleSet(item: Record<string, unknown>): RuleSetRow {
237
241
  updatedAt,
238
242
  }, item)
239
243
  }
240
-
@@ -12,6 +12,7 @@ export type AvailabilityRuleSetFormValues = {
12
12
  name?: string
13
13
  description?: string | null
14
14
  timezone?: string
15
+ updatedAt?: string | null
15
16
  } & Record<string, unknown>
16
17
 
17
18
  export type AvailabilityRuleSetFormProps = {
@@ -92,6 +93,8 @@ export function AvailabilityRuleSetForm(props: AvailabilityRuleSetFormProps) {
92
93
  groups={groups}
93
94
  entityId={E.planner.planner_availability_rule_set}
94
95
  initialValues={initialValues}
96
+ hideFooterActions
97
+ optimisticLockUpdatedAt={initialValues.updatedAt}
95
98
  onSubmit={onSubmit}
96
99
  onDelete={onDelete}
97
100
  isLoading={isLoading}
@@ -14,8 +14,9 @@ import {
14
14
  SelectValue,
15
15
  } from '@open-mercato/ui/primitives/select'
16
16
  import { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'
17
- import { apiCall, apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
17
+ import { apiCall, apiCallOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
18
18
  import { createCrud, deleteCrud, updateCrud } from '@open-mercato/ui/backend/utils/crud'
19
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
19
20
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
20
21
  import { Spinner } from '@open-mercato/ui/primitives/spinner'
21
22
  import { ComboboxInput, TimePicker } from '@open-mercato/ui/backend/inputs'
@@ -53,12 +54,16 @@ type AvailabilityRule = {
53
54
  unavailabilityReasonEntryId?: string | null
54
55
  unavailabilityReasonValue?: string | null
55
56
  createdAt?: string | null
57
+ updatedAt?: string | null
58
+ updated_at?: string | null
56
59
  }
57
60
 
58
61
  type AvailabilityRuleSet = {
59
62
  id: string
60
63
  name: string
61
64
  timezone: string
65
+ updatedAt?: string | null
66
+ updated_at?: string | null
62
67
  }
63
68
 
64
69
  export type AvailabilityBookedEvent = {
@@ -95,6 +100,14 @@ type RuleSetFormValues = {
95
100
  }
96
101
  type WeeklyWindows = TimeWindow[][]
97
102
 
103
+ function withOptimisticLockForRule<T>(rule: AvailabilityRule, mutation: () => Promise<T>): Promise<T> {
104
+ return withScopedApiRequestHeaders(buildOptimisticLockHeader(rule.updatedAt ?? rule.updated_at ?? null), mutation)
105
+ }
106
+
107
+ function withOptimisticLockForRuleSet<T>(ruleSet: AvailabilityRuleSet | null | undefined, mutation: () => Promise<T>): Promise<T> {
108
+ return withScopedApiRequestHeaders(buildOptimisticLockHeader(ruleSet?.updatedAt ?? ruleSet?.updated_at ?? null), mutation)
109
+ }
110
+
98
111
  const DAY_LABELS = [
99
112
  { code: 'SU', short: 'S', nameKey: 'schedule.weekday.sunday', fallback: 'Sunday' },
100
113
  { code: 'MO', short: 'M', nameKey: 'schedule.weekday.monday', fallback: 'Monday' },
@@ -886,16 +899,21 @@ export function AvailabilityRulesEditor({
886
899
  try {
887
900
  const updates: Array<Promise<unknown>> = []
888
901
  rulesToUpdate.forEach((rule) => {
889
- updates.push(updateCrud('planner/availability', {
890
- id: rule.id,
891
- timezone: trimmedTimezone,
892
- }))
902
+ updates.push(withOptimisticLockForRule(rule, () => (
903
+ updateCrud('planner/availability', {
904
+ id: rule.id,
905
+ timezone: trimmedTimezone,
906
+ })
907
+ )))
893
908
  })
894
909
  if (rulesetTimezoneId) {
895
- updates.push(updateCrud('planner/availability-rule-sets', {
896
- id: rulesetTimezoneId,
897
- timezone: trimmedTimezone,
898
- }))
910
+ const ruleSet = ruleSets.find((entry) => entry.id === rulesetTimezoneId) ?? null
911
+ updates.push(withOptimisticLockForRuleSet(ruleSet, () => (
912
+ updateCrud('planner/availability-rule-sets', {
913
+ id: rulesetTimezoneId,
914
+ timezone: trimmedTimezone,
915
+ })
916
+ )))
899
917
  }
900
918
  if (updates.length) {
901
919
  await Promise.all(updates)
@@ -915,6 +933,7 @@ export function AvailabilityRulesEditor({
915
933
  listLabels.timezoneSaveError,
916
934
  refreshAvailability,
917
935
  refreshRuleSetRules,
936
+ ruleSets,
918
937
  effectiveRulesetId,
919
938
  subjectId,
920
939
  subjectType,
@@ -979,8 +998,14 @@ export function AvailabilityRulesEditor({
979
998
  }
980
999
  try {
981
1000
  const idsToDelete = selectCustomRuleIdsToDelete('reset', availabilityRules)
1001
+ const idsToDeleteSet = new Set(idsToDelete)
982
1002
  await Promise.all(
983
- idsToDelete.map((id) => deleteCrud('planner/availability', id, { errorMessage: listLabels.saveWeeklyError })),
1003
+ availabilityRules
1004
+ .filter((rule) => idsToDeleteSet.has(rule.id))
1005
+ .map((rule) => withOptimisticLockForRule(
1006
+ rule,
1007
+ () => deleteCrud('planner/availability', rule.id, { errorMessage: listLabels.saveWeeklyError }),
1008
+ )),
984
1009
  )
985
1010
  setCustomOverridesEnabled(false)
986
1011
  await refreshAvailability()
@@ -996,8 +1021,14 @@ export function AvailabilityRulesEditor({
996
1021
  // Switching preserves saved custom hours (#2325); only an explicit reset clears them.
997
1022
  const idsToDelete = selectCustomRuleIdsToDelete('switch', availabilityRules)
998
1023
  if (idsToDelete.length) {
1024
+ const idsToDeleteSet = new Set(idsToDelete)
999
1025
  await Promise.all(
1000
- idsToDelete.map((id) => deleteCrud('planner/availability', id, { errorMessage: listLabels.saveWeeklyError })),
1026
+ availabilityRules
1027
+ .filter((rule) => idsToDeleteSet.has(rule.id))
1028
+ .map((rule) => withOptimisticLockForRule(
1029
+ rule,
1030
+ () => deleteCrud('planner/availability', rule.id, { errorMessage: listLabels.saveWeeklyError }),
1031
+ )),
1001
1032
  )
1002
1033
  }
1003
1034
  setSelectedRulesetId(nextId)
@@ -1030,7 +1061,11 @@ export function AvailabilityRulesEditor({
1030
1061
  variant: 'destructive',
1031
1062
  }),
1032
1063
  deleteRuleSet: async (id) => {
1033
- await deleteCrud('planner/availability-rule-sets', id, { errorMessage: listLabels.ruleSetDeleteError })
1064
+ // Version-check the schedule delete (the list page already does; the editor
1065
+ // previously sent no header → last-write-wins on delete). #2055 round-5.
1066
+ await withOptimisticLockForRuleSet(selected, () => (
1067
+ deleteCrud('planner/availability-rule-sets', id, { errorMessage: listLabels.ruleSetDeleteError })
1068
+ ))
1034
1069
  },
1035
1070
  clearAssignment: async () => {
1036
1071
  setCustomOverridesEnabled(false)
@@ -1258,9 +1293,12 @@ export function AvailabilityRulesEditor({
1258
1293
  }, { errorMessage: listLabels.saveDateError })
1259
1294
  } else {
1260
1295
  const rulesToDelete = editorRules
1261
- const uniqueRuleIds = Array.from(new Set(rulesToDelete.map((rule) => rule.id)))
1296
+ const uniqueRulesById = new Map(rulesToDelete.map((rule) => [rule.id, rule]))
1262
1297
  await Promise.all(
1263
- uniqueRuleIds.map((id) => deleteCrud('planner/availability', id, { errorMessage: listLabels.saveDateError })),
1298
+ Array.from(uniqueRulesById.values()).map((rule) => withOptimisticLockForRule(
1299
+ rule,
1300
+ () => deleteCrud('planner/availability', rule.id, { errorMessage: listLabels.saveDateError }),
1301
+ )),
1264
1302
  )
1265
1303
 
1266
1304
  const creations: Array<Promise<unknown>> = []
@@ -1607,7 +1645,12 @@ export function AvailabilityRulesEditor({
1607
1645
  variant="outline"
1608
1646
  size="icon"
1609
1647
  onClick={async () => {
1610
- await Promise.all(rules.map((rule) => deleteCrud('planner/availability', rule.id)))
1648
+ await Promise.all(
1649
+ rules.map((rule) => withOptimisticLockForRule(
1650
+ rule,
1651
+ () => deleteCrud('planner/availability', rule.id),
1652
+ )),
1653
+ )
1611
1654
  await refreshAvailability()
1612
1655
  await refreshRuleSetRules()
1613
1656
  }}
@@ -12,6 +12,8 @@ import { Input } from '@open-mercato/ui/primitives/input'
12
12
  import { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'
13
13
  import { useT } from '@open-mercato/shared/lib/i18n/context'
14
14
  import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
15
+ import { withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
16
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
15
17
  import { parseAvailabilityRuleWindow } from '@open-mercato/core/modules/planner/lib/availabilitySchedule'
16
18
  import { GanttChart } from 'lucide-react'
17
19
 
@@ -27,6 +29,8 @@ export type AvailabilityRule = {
27
29
  kind?: 'availability' | 'unavailability'
28
30
  note?: string | null
29
31
  createdAt?: string | null
32
+ updatedAt?: string | null
33
+ updated_at?: string | null
30
34
  }
31
35
 
32
36
  export type AvailabilityScheduleItemBuilder = (params: {
@@ -368,9 +372,12 @@ export function AvailabilitySchedule({
368
372
  exdates: normalizeExdatesInput(values.exdates),
369
373
  }
370
374
  if (editingRule) {
371
- await updateCrud('planner/availability', { id: editingRule.id, ...payload }, {
372
- errorMessage: availabilityLabels.updateError,
373
- })
375
+ const headers = buildOptimisticLockHeader(editingRule.updatedAt ?? editingRule.updated_at ?? null)
376
+ await withScopedApiRequestHeaders(headers, () => (
377
+ updateCrud('planner/availability', { id: editingRule.id, ...payload }, {
378
+ errorMessage: availabilityLabels.updateError,
379
+ })
380
+ ))
374
381
  flash(availabilityLabels.updatedFlash, 'success')
375
382
  } else {
376
383
  await createCrud('planner/availability', payload, {
@@ -386,10 +393,17 @@ export function AvailabilitySchedule({
386
393
 
387
394
  const handleAvailabilityDelete = React.useCallback(async () => {
388
395
  if (!editingRule) return
389
- await deleteCrud('planner/availability', editingRule.id, {
390
- errorMessage: availabilityLabels.deleteError,
391
- })
392
- flash(availabilityLabels.deletedFlash, 'success')
396
+ const headers = buildOptimisticLockHeader(editingRule.updatedAt ?? editingRule.updated_at ?? null)
397
+ await withScopedApiRequestHeaders(headers, () => (
398
+ deleteCrud('planner/availability', editingRule.id, {
399
+ errorMessage: availabilityLabels.deleteError,
400
+ })
401
+ ))
402
+ // Success/conflict UX is owned by the host CrudForm (it flashes delete success
403
+ // and surfaces an optimistic-lock 409 on the conflict bar). Do NOT flash success
404
+ // here: deleteCrud throws on a 409 so this line only ran on a real delete, but the
405
+ // extra toast duplicated CrudForm's and read as a misleading success (#2409). The
406
+ // side effects below still run only after a successful delete.
393
407
  setDialogOpen(false)
394
408
  setEditingRule(null)
395
409
  setSlotSeed(null)
@@ -477,6 +491,7 @@ export function AvailabilitySchedule({
477
491
  embedded
478
492
  fields={availabilityFields}
479
493
  initialValues={availabilityInitialValues}
494
+ optimisticLockUpdatedAt={editingRule?.updatedAt ?? editingRule?.updated_at ?? null}
480
495
  submitLabel={editingRule ? availabilityLabels.submitSave : availabilityLabels.submitCreate}
481
496
  onSubmit={handleAvailabilitySubmit}
482
497
  onDelete={editingRule ? handleAvailabilityDelete : undefined}
@@ -113,6 +113,7 @@ export class HybridQueryEngine implements QueryEngine {
113
113
  private customFieldKeysCache = new Map<string, { expiresAt: number; value: string[] }>()
114
114
  private customFieldKeysTtlMs: number
115
115
  private columnCache = new Map<string, boolean>()
116
+ private customEntityCache = new Map<string, boolean>()
116
117
  private debugVerbosity: boolean | null = null
117
118
  private sqlDebugEnabled: boolean | null = null
118
119
  private forcePartialIndexEnabled: boolean | null = null
@@ -973,6 +974,9 @@ export class HybridQueryEngine implements QueryEngine {
973
974
  }
974
975
 
975
976
  private async isCustomEntity(entity: string): Promise<boolean> {
977
+ const cached = this.customEntityCache.get(entity)
978
+ if (cached !== undefined) return cached
979
+ let result = false
976
980
  try {
977
981
  const db = this.getDb() as any
978
982
  const row = await db
@@ -981,6 +985,36 @@ export class HybridQueryEngine implements QueryEngine {
981
985
  .where('entity_id', '=', entity)
982
986
  .where('is_active', '=', true)
983
987
  .executeTakeFirst()
988
+ if (row) {
989
+ result = true
990
+ } else {
991
+ // Read/write symmetry. Records written through the entities data engine
992
+ // (`de.createCustomEntityRecord`) always land in `custom_entities_storage`,
993
+ // even for module-declared custom entities whose id is also a frozen system
994
+ // id — those are NEVER registered in `custom_entities` (install treats a
995
+ // system id as non-registrable). Without this fallback the query routes to
996
+ // the empty ORM/index path and those records are write-only (created with
997
+ // 200 but unreadable on the edit form). A real ORM entity never writes rows
998
+ // to `custom_entities_storage`, so this can only ever re-classify genuine
999
+ // doc-storage entities — it cannot misroute table-backed entities.
1000
+ result = await this.hasCustomEntityStorageRows(entity)
1001
+ }
1002
+ } catch {
1003
+ result = false
1004
+ }
1005
+ this.customEntityCache.set(entity, result)
1006
+ return result
1007
+ }
1008
+
1009
+ private async hasCustomEntityStorageRows(entity: string): Promise<boolean> {
1010
+ try {
1011
+ const db = this.getDb() as any
1012
+ const row = await db
1013
+ .selectFrom('custom_entities_storage')
1014
+ .select(sql<number>`1`.as('one'))
1015
+ .where('entity_type', '=', entity)
1016
+ .limit(1)
1017
+ .executeTakeFirst()
984
1018
  return !!row
985
1019
  } catch {
986
1020
  return false
@@ -54,8 +54,13 @@ export default function ResourcesResourceTypeEditPage({ params }: { params?: { i
54
54
  ? item.appearanceColor
55
55
  : typeof item.appearance_color === 'string'
56
56
  ? item.appearance_color
57
- : null,
57
+ : null,
58
58
  },
59
+ updatedAt: typeof item.updatedAt === 'string'
60
+ ? item.updatedAt
61
+ : typeof item.updated_at === 'string'
62
+ ? item.updated_at
63
+ : null,
59
64
  ...customValues,
60
65
  })
61
66
  setResourceCount(typeof item.resourceCount === 'number'
@@ -104,6 +109,7 @@ export default function ResourcesResourceTypeEditPage({ params }: { params?: { i
104
109
  {error ? (
105
110
  <ErrorMessage label={error} />
106
111
  ) : null}
112
+ {/* optimistic-lock: ResourceTypeCrudForm forwards optimisticLockUpdatedAt from initialValues.updatedAt (auto-derives the header on save + delete). */}
107
113
  <ResourceTypeCrudForm
108
114
  mode="edit"
109
115
  initialValues={initialValues ?? { id: resourceTypeId, name: '', description: '', appearance: { icon: null, color: null } }}
@@ -10,8 +10,9 @@ import { DataTable, withDataTableNamespaces } from '@open-mercato/ui/backend/Dat
10
10
  import { Button } from '@open-mercato/ui/primitives/button'
11
11
  import { RowActions } from '@open-mercato/ui/backend/RowActions'
12
12
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
13
- import { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
13
+ import { readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
14
14
  import { deleteCrud } from '@open-mercato/ui/backend/utils/crud'
15
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
15
16
  import { renderDictionaryColor, renderDictionaryIcon } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'
16
17
  import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
17
18
  import { Package } from 'lucide-react'
@@ -228,7 +229,10 @@ export default function ResourcesResourceTypesPage() {
228
229
  })
229
230
  if (!confirmed) return
230
231
  try {
231
- await deleteCrud('resources/resource-types', entry.id, { errorMessage: translations.errors.delete })
232
+ const headers = buildOptimisticLockHeader(entry.updatedAt)
233
+ await withScopedApiRequestHeaders(headers, () => (
234
+ deleteCrud('resources/resource-types', entry.id, { errorMessage: translations.errors.delete })
235
+ ))
232
236
  flash(translations.messages.deleted, 'success')
233
237
  handleRefresh()
234
238
  } catch (error) {
@@ -314,4 +318,3 @@ function mapApiResourceType(item: Record<string, unknown>): ResourceTypeRow {
314
318
  : 0
315
319
  return withDataTableNamespaces({ id, name, description, appearanceIcon, appearanceColor, updatedAt, resourceCount }, item)
316
320
  }
317
-
@@ -5,7 +5,8 @@ import { useRouter, useSearchParams } from 'next/navigation'
5
5
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
6
6
  import { FormHeader } from '@open-mercato/ui/backend/forms'
7
7
  import { Button } from '@open-mercato/ui/primitives/button'
8
- import { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
8
+ import { apiCallOrThrow, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
9
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
9
10
  import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'
10
11
  import { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'
11
12
  import { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'
@@ -446,6 +447,12 @@ export default function ResourcesResourceDetailPage({ params }: { params?: { id?
446
447
  capacityUnitValue: resource.capacityUnitValue ?? '',
447
448
  appearance: { icon: resource.appearanceIcon ?? null, color: resource.appearanceColor ?? null },
448
449
  isActive: resource.isActive ?? true,
450
+ updatedAt:
451
+ typeof resource.updatedAt === 'string'
452
+ ? resource.updatedAt
453
+ : typeof resource.updated_at === 'string'
454
+ ? resource.updated_at
455
+ : null,
449
456
  customFieldsetCode: resource.resourceTypeId
450
457
  ? resolveFieldsetCode(resource.resourceTypeId)
451
458
  : RESOURCES_RESOURCE_FIELDSET_DEFAULT,
@@ -472,12 +479,20 @@ export default function ResourcesResourceDetailPage({ params }: { params?: { id?
472
479
 
473
480
  const handleRulesetChange = React.useCallback(async (nextId: string | null) => {
474
481
  if (!resourceId) return
475
- await updateCrud('resources/resources', { id: resourceId, availabilityRuleSetId: nextId }, {
482
+ const updateSchedule = () => updateCrud('resources/resources', { id: resourceId, availabilityRuleSetId: nextId }, {
476
483
  errorMessage: t('resources.resources.availability.ruleset.updateError', 'Failed to update schedule.'),
477
484
  })
485
+ const resourceOptimisticLockHeader = buildOptimisticLockHeader(
486
+ typeof initialValues?.updatedAt === 'string' ? initialValues.updatedAt : null,
487
+ )
488
+ if (Object.keys(resourceOptimisticLockHeader).length > 0) {
489
+ await withScopedApiRequestHeaders(resourceOptimisticLockHeader, updateSchedule)
490
+ } else {
491
+ await updateSchedule()
492
+ }
478
493
  setAvailabilityRuleSetId(nextId)
479
494
  flash(t('resources.resources.availability.ruleset.updateSuccess', 'Schedule updated.'), 'success')
480
- }, [resourceId, t])
495
+ }, [initialValues?.updatedAt, resourceId, t])
481
496
 
482
497
  const resourceTitle =
483
498
  typeof initialValues?.name === 'string' && initialValues.name.trim().length > 0
@@ -647,6 +662,11 @@ export default function ResourcesResourceDetailPage({ params }: { params?: { id?
647
662
  successRedirect="/backend/resources/resources"
648
663
  formConfig={formConfig}
649
664
  initialValues={initialValues ?? undefined}
665
+ optimisticLockUpdatedAt={
666
+ typeof initialValues?.updatedAt === 'string'
667
+ ? initialValues.updatedAt
668
+ : null
669
+ }
650
670
  onSubmit={handleSubmit}
651
671
  onDelete={handleDelete}
652
672
  isLoading={!initialValues}
@@ -9,8 +9,9 @@ import { DataTable, withDataTableNamespaces } from '@open-mercato/ui/backend/Dat
9
9
  import { RowActions } from '@open-mercato/ui/backend/RowActions'
10
10
  import { Button } from '@open-mercato/ui/primitives/button'
11
11
  import { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'
12
- import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
12
+ import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
13
13
  import { deleteCrud } from '@open-mercato/ui/backend/utils/crud'
14
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
14
15
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
15
16
  import type { FilterDef, FilterOption, FilterValues } from '@open-mercato/ui/backend/FilterOverlay'
16
17
  import type { TagOption } from '@open-mercato/ui/backend/detail'
@@ -31,6 +32,7 @@ type ResourceRow = {
31
32
  isActive: boolean
32
33
  appearanceIcon?: string | null
33
34
  appearanceColor?: string | null
35
+ updatedAt?: string | null
34
36
  }
35
37
 
36
38
  type ResourceTypeRow = {
@@ -349,9 +351,12 @@ export default function ResourcesResourcesPage() {
349
351
  })
350
352
  if (!confirmed) return
351
353
  try {
352
- await deleteCrud('resources/resources', row.id, {
353
- errorMessage: t('resources.resources.list.error.delete', 'Failed to delete resource.'),
354
- })
354
+ const headers = buildOptimisticLockHeader(row.updatedAt)
355
+ await withScopedApiRequestHeaders(headers, () => (
356
+ deleteCrud('resources/resources', row.id, {
357
+ errorMessage: t('resources.resources.list.error.delete', 'Failed to delete resource.'),
358
+ })
359
+ ))
355
360
  flash(t('resources.resources.list.flash.deleted', 'Resource deleted.'), 'success')
356
361
  setPage(1)
357
362
  router.refresh()
@@ -536,6 +541,11 @@ function mapApiResource(item: Record<string, unknown>): ResourceRow {
536
541
  ? item.appearance_color
537
542
  : null
538
543
  const tags = Array.isArray(item.tags) ? item.tags as TagOption[] : []
544
+ const updatedAt = typeof item.updatedAt === 'string'
545
+ ? item.updatedAt
546
+ : typeof item.updated_at === 'string'
547
+ ? item.updated_at
548
+ : null
539
549
  return withDataTableNamespaces({
540
550
  id,
541
551
  name,
@@ -545,5 +555,6 @@ function mapApiResource(item: Record<string, unknown>): ResourceRow {
545
555
  isActive,
546
556
  appearanceIcon,
547
557
  appearanceColor,
558
+ updatedAt,
548
559
  }, item)
549
560
  }
@@ -316,6 +316,7 @@ export type ResourcesResourceFormProps = {
316
316
  embedded?: boolean
317
317
  successRedirect?: string
318
318
  initialValues?: Record<string, unknown>
319
+ optimisticLockUpdatedAt?: string | null
319
320
  onSubmit: (values: Record<string, unknown>) => Promise<void>
320
321
  onDelete?: () => Promise<void>
321
322
  isLoading?: boolean
@@ -332,6 +333,7 @@ export function ResourcesResourceForm(props: ResourcesResourceFormProps) {
332
333
  embedded = false,
333
334
  successRedirect,
334
335
  initialValues,
336
+ optimisticLockUpdatedAt,
335
337
  onSubmit,
336
338
  onDelete,
337
339
  isLoading,
@@ -368,6 +370,7 @@ export function ResourcesResourceForm(props: ResourcesResourceFormProps) {
368
370
  fields={formConfig.fields}
369
371
  groups={groups}
370
372
  initialValues={initialValues}
373
+ optimisticLockUpdatedAt={optimisticLockUpdatedAt}
371
374
  entityId={E.resources.resources_resource}
372
375
  customFieldsetBindings={{ [E.resources.resources_resource]: { valueKey: 'customFieldsetCode' } }}
373
376
  onSubmit={onSubmit}
@@ -13,6 +13,7 @@ export type ResourceTypeFormValues = {
13
13
  name: string
14
14
  description?: string
15
15
  appearance?: { icon?: string | null; color?: string | null }
16
+ updatedAt?: string | null
16
17
  } & Record<string, unknown>
17
18
 
18
19
  type ResourceTypeCrudFormProps = {
@@ -118,6 +119,7 @@ export function ResourceTypeCrudForm({
118
119
  groups={groups}
119
120
  entityId={E.resources.resources_resource_type}
120
121
  initialValues={initialValues}
122
+ optimisticLockUpdatedAt={mode === 'edit' ? initialValues.updatedAt : undefined}
121
123
  isLoading={isLoading}
122
124
  onSubmit={onSubmit}
123
125
  onDelete={mode === 'edit' ? onDelete : undefined}
@@ -202,6 +202,12 @@ const mapUpdateResponse = (entity: any) => {
202
202
  paymentMethodId: entity?.paymentMethodId ?? null,
203
203
  paymentMethodCode: entity?.paymentMethodCode ?? null,
204
204
  paymentMethodSnapshot: normalizeJsonRecord(entity?.paymentMethodSnapshot),
205
+ // Return the fresh version so the client can refresh its optimistic-lock
206
+ // token after a successful inline save — otherwise a second save on the same
207
+ // page sends the now-stale updatedAt and falsely 409s (#2055 QA).
208
+ updatedAt: entity?.updatedAt
209
+ ? (entity.updatedAt instanceof Date ? entity.updatedAt.toISOString() : entity.updatedAt)
210
+ : null,
205
211
  }
206
212
  }
207
213
 
@@ -492,7 +498,13 @@ export function buildDocumentCrudOptions(binding: DocumentBinding) {
492
498
  const organizationId =
493
499
  typeof item?.organizationId === 'string' ? item.organizationId : ctx?.selectedOrganizationId ?? ctx?.auth?.orgId ?? null
494
500
  if (orderId && tenantId && organizationId) {
495
- const em = ctx?.container?.resolve?.('em') as import('@mikro-orm/postgresql').EntityManager | undefined
501
+ const requestEm = ctx?.container?.resolve?.('em') as import('@mikro-orm/postgresql').EntityManager | undefined
502
+ // Display-only totals recalculation: run on a forked EntityManager so
503
+ // the order/line/adjustment entities loaded here never enter the
504
+ // request's Unit of Work. This guarantees a GET can never flush an
505
+ // UPDATE (and thus never advance `updated_at`), which would otherwise
506
+ // surface as a spurious optimistic-lock 409 in another tab.
507
+ const em = requestEm?.fork()
496
508
  if (em) {
497
509
  const totals = await recalculateOrderTotalsForDisplay(
498
510
  em,