@open-mercato/core 0.4.2-canary-da2b080494 → 0.4.2-canary-19703ca707

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 (449) hide show
  1. package/dist/generated/entities/notification/index.js +57 -0
  2. package/dist/generated/entities/notification/index.js.map +7 -0
  3. package/dist/generated/entities.ids.generated.js +5 -1
  4. package/dist/generated/entities.ids.generated.js.map +2 -2
  5. package/dist/generated/entity-fields-registry.js +2 -0
  6. package/dist/generated/entity-fields-registry.js.map +2 -2
  7. package/dist/modules/api_docs/frontend/docs/api/page.js +3 -2
  8. package/dist/modules/api_docs/frontend/docs/api/page.js.map +2 -2
  9. package/dist/modules/api_keys/backend/api-keys/page.js +1 -1
  10. package/dist/modules/api_keys/backend/api-keys/page.js.map +2 -2
  11. package/dist/modules/attachments/components/AttachmentLibrary.js +4 -0
  12. package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
  13. package/dist/modules/attachments/components/AttachmentPartitionSettings.js +2 -0
  14. package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +2 -2
  15. package/dist/modules/auth/api/admin/nav.js +4 -3
  16. package/dist/modules/auth/api/admin/nav.js.map +2 -2
  17. package/dist/modules/auth/api/login.js +25 -6
  18. package/dist/modules/auth/api/login.js.map +2 -2
  19. package/dist/modules/auth/api/profile/route.js +157 -0
  20. package/dist/modules/auth/api/profile/route.js.map +7 -0
  21. package/dist/modules/auth/api/reset/confirm.js +25 -2
  22. package/dist/modules/auth/api/reset/confirm.js.map +2 -2
  23. package/dist/modules/auth/api/reset.js +23 -0
  24. package/dist/modules/auth/api/reset.js.map +2 -2
  25. package/dist/modules/auth/api/sidebar/preferences/route.js +14 -9
  26. package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
  27. package/dist/modules/auth/api/users/route.js +4 -2
  28. package/dist/modules/auth/api/users/route.js.map +2 -2
  29. package/dist/modules/auth/backend/auth/profile/page.js +141 -0
  30. package/dist/modules/auth/backend/auth/profile/page.js.map +7 -0
  31. package/dist/modules/auth/backend/auth/profile/page.meta.js +13 -0
  32. package/dist/modules/auth/backend/auth/profile/page.meta.js.map +7 -0
  33. package/dist/modules/auth/backend/roles/[id]/edit/page.js +4 -1
  34. package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
  35. package/dist/modules/auth/backend/roles/page.js +3 -3
  36. package/dist/modules/auth/backend/roles/page.js.map +2 -2
  37. package/dist/modules/auth/backend/users/[id]/edit/page.js +18 -3
  38. package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
  39. package/dist/modules/auth/backend/users/create/page.js +15 -2
  40. package/dist/modules/auth/backend/users/create/page.js.map +2 -2
  41. package/dist/modules/auth/backend/users/page.js +3 -3
  42. package/dist/modules/auth/backend/users/page.js.map +2 -2
  43. package/dist/modules/auth/cli.js +25 -11
  44. package/dist/modules/auth/cli.js.map +2 -2
  45. package/dist/modules/auth/commands/users.js +59 -2
  46. package/dist/modules/auth/commands/users.js.map +2 -2
  47. package/dist/modules/auth/data/validators.js +6 -3
  48. package/dist/modules/auth/data/validators.js.map +2 -2
  49. package/dist/modules/auth/frontend/login.js +85 -1
  50. package/dist/modules/auth/frontend/login.js.map +2 -2
  51. package/dist/modules/auth/frontend/reset/[token]/page.js +20 -10
  52. package/dist/modules/auth/frontend/reset/[token]/page.js.map +2 -2
  53. package/dist/modules/auth/lib/setup-app.js +42 -8
  54. package/dist/modules/auth/lib/setup-app.js.map +2 -2
  55. package/dist/modules/auth/notifications.js +112 -0
  56. package/dist/modules/auth/notifications.js.map +7 -0
  57. package/dist/modules/auth/services/authService.js +24 -3
  58. package/dist/modules/auth/services/authService.js.map +2 -2
  59. package/dist/modules/business_rules/api/execute/route.js +7 -1
  60. package/dist/modules/business_rules/api/execute/route.js.map +2 -2
  61. package/dist/modules/business_rules/backend/rules/page.js +4 -0
  62. package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
  63. package/dist/modules/business_rules/backend/sets/page.js +3 -0
  64. package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
  65. package/dist/modules/business_rules/cli.js +2 -1
  66. package/dist/modules/business_rules/cli.js.map +2 -2
  67. package/dist/modules/business_rules/lib/rule-engine.js +33 -3
  68. package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
  69. package/dist/modules/business_rules/notifications.js +28 -0
  70. package/dist/modules/business_rules/notifications.js.map +7 -0
  71. package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js +37 -0
  72. package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js.map +7 -0
  73. package/dist/modules/catalog/components/PriceKindSettings.js +2 -0
  74. package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
  75. package/dist/modules/catalog/components/categories/CategoriesDataTable.js +2 -2
  76. package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
  77. package/dist/modules/catalog/components/products/ProductsDataTable.js +2 -0
  78. package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
  79. package/dist/modules/catalog/notifications.js +28 -0
  80. package/dist/modules/catalog/notifications.js.map +7 -0
  81. package/dist/modules/catalog/subscribers/low-stock-notification.js +38 -0
  82. package/dist/modules/catalog/subscribers/low-stock-notification.js.map +7 -0
  83. package/dist/modules/configs/cli.js +6 -0
  84. package/dist/modules/configs/cli.js.map +2 -2
  85. package/dist/modules/configs/components/CachePanel.js +4 -4
  86. package/dist/modules/configs/components/CachePanel.js.map +2 -2
  87. package/dist/modules/configs/lib/system-status.js +48 -1
  88. package/dist/modules/configs/lib/system-status.js.map +2 -2
  89. package/dist/modules/configs/lib/upgrade-actions.js +18 -0
  90. package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
  91. package/dist/modules/currencies/backend/currencies/page.js +3 -0
  92. package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
  93. package/dist/modules/currencies/backend/exchange-rates/page.js +2 -0
  94. package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
  95. package/dist/modules/customers/backend/customers/companies/page.js +3 -0
  96. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  97. package/dist/modules/customers/backend/customers/deals/page.js +3 -0
  98. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  99. package/dist/modules/customers/backend/customers/people/page.js +3 -0
  100. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  101. package/dist/modules/customers/commands/deals.js +31 -0
  102. package/dist/modules/customers/commands/deals.js.map +2 -2
  103. package/dist/modules/customers/components/CustomerTodosTable.js +1 -0
  104. package/dist/modules/customers/components/CustomerTodosTable.js.map +2 -2
  105. package/dist/modules/customers/notifications.js +48 -0
  106. package/dist/modules/customers/notifications.js.map +7 -0
  107. package/dist/modules/dashboards/cli.js +44 -5
  108. package/dist/modules/dashboards/cli.js.map +2 -2
  109. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +16 -11
  110. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +3 -3
  111. package/dist/modules/dashboards/lib/role-widgets.js +58 -0
  112. package/dist/modules/dashboards/lib/role-widgets.js.map +7 -0
  113. package/dist/modules/dashboards/services/widgetDataService.js +139 -3
  114. package/dist/modules/dashboards/services/widgetDataService.js.map +2 -2
  115. package/dist/modules/dictionaries/components/DictionaryTable.js +2 -0
  116. package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
  117. package/dist/modules/directory/api/get/tenants/lookup.js +68 -0
  118. package/dist/modules/directory/api/get/tenants/lookup.js.map +7 -0
  119. package/dist/modules/directory/backend/directory/organizations/page.js +2 -2
  120. package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
  121. package/dist/modules/directory/backend/directory/tenants/page.js +2 -2
  122. package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
  123. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +2 -2
  124. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  125. package/dist/modules/entities/components/SystemEntitiesTable.js +1 -1
  126. package/dist/modules/entities/components/SystemEntitiesTable.js.map +2 -2
  127. package/dist/modules/entities/components/UserEntitiesTable.js +2 -2
  128. package/dist/modules/entities/components/UserEntitiesTable.js.map +2 -2
  129. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +3 -3
  130. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
  131. package/dist/modules/feature_toggles/components/OverridesTable.js +1 -1
  132. package/dist/modules/feature_toggles/components/OverridesTable.js.map +2 -2
  133. package/dist/modules/notifications/acl.js +11 -0
  134. package/dist/modules/notifications/acl.js.map +7 -0
  135. package/dist/modules/notifications/api/[id]/action/route.js +74 -0
  136. package/dist/modules/notifications/api/[id]/action/route.js.map +7 -0
  137. package/dist/modules/notifications/api/[id]/dismiss/route.js +15 -0
  138. package/dist/modules/notifications/api/[id]/dismiss/route.js.map +7 -0
  139. package/dist/modules/notifications/api/[id]/read/route.js +15 -0
  140. package/dist/modules/notifications/api/[id]/read/route.js.map +7 -0
  141. package/dist/modules/notifications/api/[id]/restore/route.js +53 -0
  142. package/dist/modules/notifications/api/[id]/restore/route.js.map +7 -0
  143. package/dist/modules/notifications/api/batch/route.js +17 -0
  144. package/dist/modules/notifications/api/batch/route.js.map +7 -0
  145. package/dist/modules/notifications/api/feature/route.js +17 -0
  146. package/dist/modules/notifications/api/feature/route.js.map +7 -0
  147. package/dist/modules/notifications/api/mark-all-read/route.js +35 -0
  148. package/dist/modules/notifications/api/mark-all-read/route.js.map +7 -0
  149. package/dist/modules/notifications/api/openapi.js +76 -0
  150. package/dist/modules/notifications/api/openapi.js.map +7 -0
  151. package/dist/modules/notifications/api/role/route.js +17 -0
  152. package/dist/modules/notifications/api/role/route.js.map +7 -0
  153. package/dist/modules/notifications/api/route.js +85 -0
  154. package/dist/modules/notifications/api/route.js.map +7 -0
  155. package/dist/modules/notifications/api/settings/route.js +155 -0
  156. package/dist/modules/notifications/api/settings/route.js.map +7 -0
  157. package/dist/modules/notifications/api/unread-count/route.js +38 -0
  158. package/dist/modules/notifications/api/unread-count/route.js.map +7 -0
  159. package/dist/modules/notifications/backend/config/notifications/page.js +10 -0
  160. package/dist/modules/notifications/backend/config/notifications/page.js.map +7 -0
  161. package/dist/modules/notifications/backend/config/notifications/page.meta.js +24 -0
  162. package/dist/modules/notifications/backend/config/notifications/page.meta.js.map +7 -0
  163. package/dist/modules/notifications/cli.js +16 -0
  164. package/dist/modules/notifications/cli.js.map +7 -0
  165. package/dist/modules/notifications/data/entities.js +112 -0
  166. package/dist/modules/notifications/data/entities.js.map +7 -0
  167. package/dist/modules/notifications/data/validators.js +98 -0
  168. package/dist/modules/notifications/data/validators.js.map +7 -0
  169. package/dist/modules/notifications/di.js +13 -0
  170. package/dist/modules/notifications/di.js.map +7 -0
  171. package/dist/modules/notifications/emails/NotificationEmail.js +58 -0
  172. package/dist/modules/notifications/emails/NotificationEmail.js.map +7 -0
  173. package/dist/modules/notifications/frontend/NotificationInboxPageClient.js +44 -0
  174. package/dist/modules/notifications/frontend/NotificationInboxPageClient.js.map +7 -0
  175. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +220 -0
  176. package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +7 -0
  177. package/dist/modules/notifications/index.js +14 -0
  178. package/dist/modules/notifications/index.js.map +7 -0
  179. package/dist/modules/notifications/lib/deliveryConfig.js +107 -0
  180. package/dist/modules/notifications/lib/deliveryConfig.js.map +7 -0
  181. package/dist/modules/notifications/lib/deliveryStrategies.js +14 -0
  182. package/dist/modules/notifications/lib/deliveryStrategies.js.map +7 -0
  183. package/dist/modules/notifications/lib/events.js +12 -0
  184. package/dist/modules/notifications/lib/events.js.map +7 -0
  185. package/dist/modules/notifications/lib/notificationBuilder.js +66 -0
  186. package/dist/modules/notifications/lib/notificationBuilder.js.map +7 -0
  187. package/dist/modules/notifications/lib/notificationFactory.js +54 -0
  188. package/dist/modules/notifications/lib/notificationFactory.js.map +7 -0
  189. package/dist/modules/notifications/lib/notificationMapper.js +34 -0
  190. package/dist/modules/notifications/lib/notificationMapper.js.map +7 -0
  191. package/dist/modules/notifications/lib/notificationRecipients.js +35 -0
  192. package/dist/modules/notifications/lib/notificationRecipients.js.map +7 -0
  193. package/dist/modules/notifications/lib/notificationService.js +279 -0
  194. package/dist/modules/notifications/lib/notificationService.js.map +7 -0
  195. package/dist/modules/notifications/lib/routeHelpers.js +101 -0
  196. package/dist/modules/notifications/lib/routeHelpers.js.map +7 -0
  197. package/dist/modules/notifications/lib/safeHref.js +24 -0
  198. package/dist/modules/notifications/lib/safeHref.js.map +7 -0
  199. package/dist/modules/notifications/migrations/Migration20260123000001.js +70 -0
  200. package/dist/modules/notifications/migrations/Migration20260123000001.js.map +7 -0
  201. package/dist/modules/notifications/migrations/Migration20260126150000.js +37 -0
  202. package/dist/modules/notifications/migrations/Migration20260126150000.js.map +7 -0
  203. package/dist/modules/notifications/subscribers/deliver-notification.js +165 -0
  204. package/dist/modules/notifications/subscribers/deliver-notification.js.map +7 -0
  205. package/dist/modules/notifications/workers/create-notification.worker.js +70 -0
  206. package/dist/modules/notifications/workers/create-notification.worker.js.map +7 -0
  207. package/dist/modules/planner/backend/planner/availability-rulesets/page.js +2 -2
  208. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  209. package/dist/modules/query_index/components/QueryIndexesTable.js +7 -1
  210. package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
  211. package/dist/modules/resources/backend/resources/resource-types/page.js +2 -2
  212. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  213. package/dist/modules/resources/backend/resources/resources/page.js +2 -2
  214. package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
  215. package/dist/modules/sales/backend/sales/channels/offers/page.js +2 -0
  216. package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
  217. package/dist/modules/sales/backend/sales/channels/page.js +2 -0
  218. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  219. package/dist/modules/sales/commands/documents.js +53 -0
  220. package/dist/modules/sales/commands/documents.js.map +2 -2
  221. package/dist/modules/sales/commands/payments.js +26 -0
  222. package/dist/modules/sales/commands/payments.js.map +2 -2
  223. package/dist/modules/sales/components/AdjustmentKindSettings.js +2 -2
  224. package/dist/modules/sales/components/AdjustmentKindSettings.js.map +2 -2
  225. package/dist/modules/sales/components/PaymentMethodsSettings.js +2 -2
  226. package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
  227. package/dist/modules/sales/components/ShippingMethodsSettings.js +2 -2
  228. package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
  229. package/dist/modules/sales/components/TaxRatesSettings.js +2 -2
  230. package/dist/modules/sales/components/TaxRatesSettings.js.map +2 -2
  231. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +2 -0
  232. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
  233. package/dist/modules/sales/components/documents/AdjustmentsSection.js +2 -0
  234. package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
  235. package/dist/modules/sales/components/documents/PaymentsSection.js +2 -1
  236. package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
  237. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +2 -0
  238. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  239. package/dist/modules/sales/notifications.client.js +51 -0
  240. package/dist/modules/sales/notifications.client.js.map +7 -0
  241. package/dist/modules/sales/notifications.js +88 -0
  242. package/dist/modules/sales/notifications.js.map +7 -0
  243. package/dist/modules/sales/subscribers/quote-expiring-notification.js +38 -0
  244. package/dist/modules/sales/subscribers/quote-expiring-notification.js.map +7 -0
  245. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js +137 -0
  246. package/dist/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.js.map +7 -0
  247. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js +137 -0
  248. package/dist/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.js.map +7 -0
  249. package/dist/modules/sales/widgets/notifications/index.js +7 -0
  250. package/dist/modules/sales/widgets/notifications/index.js.map +7 -0
  251. package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js +60 -0
  252. package/dist/modules/sales/widgets/notifications/useSalesDocumentTotals.js.map +7 -0
  253. package/dist/modules/staff/backend/staff/team-members/page.js +1 -1
  254. package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
  255. package/dist/modules/staff/backend/staff/team-roles/page.js +2 -2
  256. package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
  257. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +2 -2
  258. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  259. package/dist/modules/staff/backend/staff/teams/page.js +2 -2
  260. package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
  261. package/dist/modules/staff/commands/leave-requests.js +79 -0
  262. package/dist/modules/staff/commands/leave-requests.js.map +2 -2
  263. package/dist/modules/staff/notifications.js +75 -0
  264. package/dist/modules/staff/notifications.js.map +7 -0
  265. package/dist/modules/workflows/backend/definitions/page.js +5 -0
  266. package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
  267. package/dist/modules/workflows/backend/instances/page.js +3 -0
  268. package/dist/modules/workflows/backend/instances/page.js.map +2 -2
  269. package/dist/modules/workflows/backend/tasks/page.js +3 -0
  270. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  271. package/dist/modules/workflows/cli.js +12 -12
  272. package/dist/modules/workflows/cli.js.map +2 -2
  273. package/dist/modules/workflows/lib/transition-handler.js +14 -6
  274. package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
  275. package/dist/modules/workflows/notifications.js +28 -0
  276. package/dist/modules/workflows/notifications.js.map +7 -0
  277. package/dist/modules/workflows/subscribers/task-assigned-notification.js +38 -0
  278. package/dist/modules/workflows/subscribers/task-assigned-notification.js.map +7 -0
  279. package/generated/entities/notification/index.ts +27 -0
  280. package/generated/entities.ids.generated.ts +5 -1
  281. package/generated/entity-fields-registry.ts +2 -0
  282. package/package.json +2 -2
  283. package/src/modules/api_docs/frontend/docs/api/page.tsx +3 -2
  284. package/src/modules/api_keys/backend/api-keys/page.tsx +1 -1
  285. package/src/modules/attachments/components/AttachmentLibrary.tsx +4 -0
  286. package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +2 -0
  287. package/src/modules/auth/README.md +1 -1
  288. package/src/modules/auth/__tests__/cli-setup-acl.test.ts +1 -1
  289. package/src/modules/auth/api/__tests__/login.test.ts +2 -0
  290. package/src/modules/auth/api/admin/nav.ts +10 -6
  291. package/src/modules/auth/api/login.ts +26 -7
  292. package/src/modules/auth/api/profile/route.ts +163 -0
  293. package/src/modules/auth/api/reset/confirm.ts +25 -2
  294. package/src/modules/auth/api/reset.ts +23 -0
  295. package/src/modules/auth/api/sidebar/preferences/route.ts +21 -12
  296. package/src/modules/auth/api/users/route.ts +5 -2
  297. package/src/modules/auth/backend/auth/profile/page.meta.ts +9 -0
  298. package/src/modules/auth/backend/auth/profile/page.tsx +174 -0
  299. package/src/modules/auth/backend/roles/[id]/edit/page.tsx +4 -1
  300. package/src/modules/auth/backend/roles/page.tsx +3 -3
  301. package/src/modules/auth/backend/users/[id]/edit/page.tsx +22 -3
  302. package/src/modules/auth/backend/users/create/page.tsx +19 -2
  303. package/src/modules/auth/backend/users/page.tsx +3 -3
  304. package/src/modules/auth/cli.ts +38 -11
  305. package/src/modules/auth/commands/users.ts +73 -2
  306. package/src/modules/auth/data/validators.ts +6 -2
  307. package/src/modules/auth/frontend/login.tsx +106 -2
  308. package/src/modules/auth/frontend/reset/[token]/page.tsx +24 -11
  309. package/src/modules/auth/i18n/de.json +48 -1
  310. package/src/modules/auth/i18n/en.json +48 -1
  311. package/src/modules/auth/i18n/es.json +48 -1
  312. package/src/modules/auth/i18n/pl.json +48 -1
  313. package/src/modules/auth/lib/setup-app.ts +58 -9
  314. package/src/modules/auth/notifications.ts +109 -0
  315. package/src/modules/auth/services/authService.ts +27 -4
  316. package/src/modules/business_rules/api/execute/route.ts +8 -1
  317. package/src/modules/business_rules/backend/rules/page.tsx +4 -0
  318. package/src/modules/business_rules/backend/sets/page.tsx +3 -0
  319. package/src/modules/business_rules/cli.ts +2 -1
  320. package/src/modules/business_rules/i18n/en.json +3 -1
  321. package/src/modules/business_rules/lib/__tests__/rule-engine.test.ts +51 -0
  322. package/src/modules/business_rules/lib/rule-engine.ts +57 -3
  323. package/src/modules/business_rules/notifications.ts +25 -0
  324. package/src/modules/business_rules/subscribers/rule-execution-failed-notification.ts +50 -0
  325. package/src/modules/catalog/components/PriceKindSettings.tsx +2 -0
  326. package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +2 -2
  327. package/src/modules/catalog/components/products/ProductsDataTable.tsx +2 -0
  328. package/src/modules/catalog/i18n/en.json +3 -1
  329. package/src/modules/catalog/notifications.ts +25 -0
  330. package/src/modules/catalog/subscribers/low-stock-notification.ts +52 -0
  331. package/src/modules/configs/cli.ts +6 -0
  332. package/src/modules/configs/components/CachePanel.tsx +4 -4
  333. package/src/modules/configs/i18n/en.json +12 -2
  334. package/src/modules/configs/i18n/pl.json +12 -2
  335. package/src/modules/configs/lib/system-status.ts +48 -1
  336. package/src/modules/configs/lib/system-status.types.ts +1 -0
  337. package/src/modules/configs/lib/upgrade-actions.ts +18 -0
  338. package/src/modules/currencies/backend/currencies/page.tsx +3 -0
  339. package/src/modules/currencies/backend/exchange-rates/page.tsx +2 -0
  340. package/src/modules/customers/backend/customers/companies/page.tsx +3 -0
  341. package/src/modules/customers/backend/customers/deals/page.tsx +3 -0
  342. package/src/modules/customers/backend/customers/people/page.tsx +3 -0
  343. package/src/modules/customers/commands/deals.ts +39 -0
  344. package/src/modules/customers/components/CustomerTodosTable.tsx +1 -0
  345. package/src/modules/customers/i18n/en.json +5 -1
  346. package/src/modules/customers/notifications.ts +44 -0
  347. package/src/modules/dashboards/cli.ts +55 -5
  348. package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +22 -11
  349. package/src/modules/dashboards/lib/role-widgets.ts +80 -0
  350. package/src/modules/dashboards/services/widgetDataService.ts +164 -4
  351. package/src/modules/dictionaries/components/DictionaryTable.tsx +2 -0
  352. package/src/modules/directory/api/get/tenants/lookup.ts +73 -0
  353. package/src/modules/directory/backend/directory/organizations/page.tsx +2 -2
  354. package/src/modules/directory/backend/directory/tenants/page.tsx +2 -2
  355. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +2 -2
  356. package/src/modules/entities/components/SystemEntitiesTable.tsx +1 -1
  357. package/src/modules/entities/components/UserEntitiesTable.tsx +2 -2
  358. package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +3 -4
  359. package/src/modules/feature_toggles/components/OverridesTable.tsx +1 -1
  360. package/src/modules/notifications/__tests__/deliver-notification.test.ts +195 -0
  361. package/src/modules/notifications/__tests__/deliveryStrategies.test.ts +19 -0
  362. package/src/modules/notifications/__tests__/notificationService.test.ts +208 -0
  363. package/src/modules/notifications/acl.ts +7 -0
  364. package/src/modules/notifications/api/[id]/action/route.ts +75 -0
  365. package/src/modules/notifications/api/[id]/dismiss/route.ts +12 -0
  366. package/src/modules/notifications/api/[id]/read/route.ts +12 -0
  367. package/src/modules/notifications/api/[id]/restore/route.ts +53 -0
  368. package/src/modules/notifications/api/batch/route.ts +14 -0
  369. package/src/modules/notifications/api/feature/route.ts +14 -0
  370. package/src/modules/notifications/api/mark-all-read/route.ts +34 -0
  371. package/src/modules/notifications/api/openapi.ts +76 -0
  372. package/src/modules/notifications/api/role/route.ts +14 -0
  373. package/src/modules/notifications/api/route.ts +92 -0
  374. package/src/modules/notifications/api/settings/route.ts +157 -0
  375. package/src/modules/notifications/api/unread-count/route.ts +38 -0
  376. package/src/modules/notifications/backend/config/notifications/page.meta.ts +22 -0
  377. package/src/modules/notifications/backend/config/notifications/page.tsx +12 -0
  378. package/src/modules/notifications/cli.ts +18 -0
  379. package/src/modules/notifications/data/entities.ts +99 -0
  380. package/src/modules/notifications/data/validators.ts +115 -0
  381. package/src/modules/notifications/di.ts +11 -0
  382. package/src/modules/notifications/emails/NotificationEmail.tsx +98 -0
  383. package/src/modules/notifications/frontend/NotificationInboxPageClient.tsx +42 -0
  384. package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +233 -0
  385. package/src/modules/notifications/i18n/de.json +50 -0
  386. package/src/modules/notifications/i18n/en.json +50 -0
  387. package/src/modules/notifications/i18n/es.json +50 -0
  388. package/src/modules/notifications/i18n/pl.json +50 -0
  389. package/src/modules/notifications/index.ts +12 -0
  390. package/src/modules/notifications/lib/deliveryConfig.ts +153 -0
  391. package/src/modules/notifications/lib/deliveryStrategies.ts +50 -0
  392. package/src/modules/notifications/lib/events.ts +48 -0
  393. package/src/modules/notifications/lib/notificationBuilder.ts +121 -0
  394. package/src/modules/notifications/lib/notificationFactory.ts +76 -0
  395. package/src/modules/notifications/lib/notificationMapper.ts +33 -0
  396. package/src/modules/notifications/lib/notificationRecipients.ts +83 -0
  397. package/src/modules/notifications/lib/notificationService.ts +414 -0
  398. package/src/modules/notifications/lib/routeHelpers.ts +151 -0
  399. package/src/modules/notifications/lib/safeHref.ts +29 -0
  400. package/src/modules/notifications/migrations/.snapshot-open-mercato.json +300 -0
  401. package/src/modules/notifications/migrations/Migration20260123000001.ts +73 -0
  402. package/src/modules/notifications/migrations/Migration20260126150000.ts +39 -0
  403. package/src/modules/notifications/subscribers/deliver-notification.ts +204 -0
  404. package/src/modules/notifications/workers/create-notification.worker.ts +122 -0
  405. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +2 -2
  406. package/src/modules/query_index/components/QueryIndexesTable.tsx +8 -2
  407. package/src/modules/resources/backend/resources/resource-types/page.tsx +2 -2
  408. package/src/modules/resources/backend/resources/resources/page.tsx +2 -2
  409. package/src/modules/sales/backend/sales/channels/offers/page.tsx +2 -0
  410. package/src/modules/sales/backend/sales/channels/page.tsx +2 -0
  411. package/src/modules/sales/commands/documents.ts +65 -0
  412. package/src/modules/sales/commands/payments.ts +33 -0
  413. package/src/modules/sales/components/AdjustmentKindSettings.tsx +2 -2
  414. package/src/modules/sales/components/PaymentMethodsSettings.tsx +2 -2
  415. package/src/modules/sales/components/ShippingMethodsSettings.tsx +2 -2
  416. package/src/modules/sales/components/TaxRatesSettings.tsx +2 -2
  417. package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +2 -0
  418. package/src/modules/sales/components/documents/AdjustmentsSection.tsx +2 -0
  419. package/src/modules/sales/components/documents/PaymentsSection.tsx +2 -1
  420. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +2 -0
  421. package/src/modules/sales/i18n/de.json +20 -0
  422. package/src/modules/sales/i18n/en.json +25 -1
  423. package/src/modules/sales/i18n/es.json +20 -0
  424. package/src/modules/sales/i18n/pl.json +20 -0
  425. package/src/modules/sales/notifications.client.ts +65 -0
  426. package/src/modules/sales/notifications.ts +82 -0
  427. package/src/modules/sales/subscribers/quote-expiring-notification.ts +53 -0
  428. package/src/modules/sales/widgets/notifications/SalesOrderCreatedRenderer.tsx +156 -0
  429. package/src/modules/sales/widgets/notifications/SalesQuoteCreatedRenderer.tsx +156 -0
  430. package/src/modules/sales/widgets/notifications/index.ts +2 -0
  431. package/src/modules/sales/widgets/notifications/useSalesDocumentTotals.ts +81 -0
  432. package/src/modules/staff/backend/staff/team-members/page.tsx +1 -1
  433. package/src/modules/staff/backend/staff/team-roles/page.tsx +2 -2
  434. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +2 -2
  435. package/src/modules/staff/backend/staff/teams/page.tsx +2 -2
  436. package/src/modules/staff/commands/leave-requests.ts +94 -0
  437. package/src/modules/staff/i18n/de.json +4 -0
  438. package/src/modules/staff/i18n/en.json +9 -1
  439. package/src/modules/staff/i18n/es.json +4 -0
  440. package/src/modules/staff/i18n/pl.json +4 -0
  441. package/src/modules/staff/notifications.ts +71 -0
  442. package/src/modules/workflows/backend/definitions/page.tsx +5 -0
  443. package/src/modules/workflows/backend/instances/page.tsx +4 -1
  444. package/src/modules/workflows/backend/tasks/page.tsx +4 -1
  445. package/src/modules/workflows/cli.ts +12 -12
  446. package/src/modules/workflows/i18n/en.json +3 -1
  447. package/src/modules/workflows/lib/transition-handler.ts +18 -6
  448. package/src/modules/workflows/notifications.ts +25 -0
  449. package/src/modules/workflows/subscribers/task-assigned-notification.ts +53 -0
@@ -16,6 +16,7 @@ import { DEFAULT_ENCRYPTION_MAPS } from '@open-mercato/core/modules/entities/lib
16
16
  import { createKmsService } from '@open-mercato/shared/lib/encryption/kms'
17
17
  import { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'
18
18
  import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
19
+ import { parseBooleanToken } from '@open-mercato/shared/lib/boolean'
19
20
 
20
21
  const DEFAULT_ROLE_NAMES = ['employee', 'admin', 'superadmin'] as const
21
22
  const DEMO_SUPERADMIN_EMAIL = 'superadmin@acme.com'
@@ -87,6 +88,11 @@ type PrimaryUserInput = {
87
88
  confirm?: boolean
88
89
  }
89
90
 
91
+ const DERIVED_EMAIL_ENV = {
92
+ admin: 'OM_INIT_ADMIN_EMAIL',
93
+ employee: 'OM_INIT_EMPLOYEE_EMAIL',
94
+ } as const
95
+
90
96
  export type SetupInitialTenantOptions = {
91
97
  orgName: string
92
98
  primaryUser: PrimaryUserInput
@@ -170,16 +176,28 @@ export async function setupInitialTenant(
170
176
  })
171
177
 
172
178
  if (!existingUser) {
173
- const baseUsers: Array<{ email: string; roles: string[]; name?: string | null }> = [
179
+ const baseUsers: Array<{
180
+ email: string
181
+ roles: string[]
182
+ name?: string | null
183
+ passwordHash?: string | null
184
+ }> = [
174
185
  { email: primaryUser.email, roles: primaryRoles, name: resolvePrimaryName(primaryUser) },
175
186
  ]
176
187
  if (includeDerivedUsers) {
177
- const [local, domain] = String(primaryUser.email).split('@')
178
- const isSuperadminLocal = (local || '').toLowerCase() === 'superadmin' && !!domain
179
- if (isSuperadminLocal) {
180
- baseUsers.push({ email: `admin@${domain}`, roles: ['admin'] })
181
- baseUsers.push({ email: `employee@${domain}`, roles: ['employee'] })
182
- }
188
+ const [, domain] = String(primaryUser.email).split('@')
189
+ const adminOverride = readEnvValue(DERIVED_EMAIL_ENV.admin)
190
+ const employeeOverride = readEnvValue(DERIVED_EMAIL_ENV.employee)
191
+ const adminEmail = adminOverride ?? (domain ? `admin@${domain}` : '')
192
+ const employeeEmail = employeeOverride ?? (domain ? `employee@${domain}` : '')
193
+ const adminPassword = readEnvValue('OM_INIT_ADMIN_PASSWORD')
194
+ const employeePassword = readEnvValue('OM_INIT_EMPLOYEE_PASSWORD')
195
+ const adminPasswordHash = adminPassword ? await resolvePasswordHash({ email: adminEmail, password: adminPassword }) : null
196
+ const employeePasswordHash = employeePassword
197
+ ? await resolvePasswordHash({ email: employeeEmail, password: employeePassword })
198
+ : null
199
+ addUniqueBaseUser(baseUsers, { email: adminEmail, roles: ['admin'], passwordHash: adminPasswordHash })
200
+ addUniqueBaseUser(baseUsers, { email: employeeEmail, roles: ['employee'], passwordHash: employeePasswordHash })
183
201
  }
184
202
  const passwordHash = await resolvePasswordHash(primaryUser)
185
203
 
@@ -271,13 +289,14 @@ export async function setupInitialTenant(
271
289
  }
272
290
 
273
291
  for (const base of baseUsers) {
292
+ const resolvedPasswordHash = base.passwordHash ?? passwordHash
274
293
  let user = await tem.findOne(User, { email: base.email })
275
294
  const confirm = primaryUser.confirm ?? true
276
295
  const encryptedPayload = encryptionService
277
296
  ? await encryptionService.encryptEntityPayload('auth:user', { email: base.email }, tenantId, organizationId)
278
297
  : { email: base.email, emailHash: computeEmailHash(base.email) }
279
298
  if (user) {
280
- user.passwordHash = passwordHash
299
+ user.passwordHash = resolvedPasswordHash
281
300
  user.organizationId = organizationId
282
301
  user.tenantId = tenantId
283
302
  if (isTenantDataEncryptionEnabled()) {
@@ -292,7 +311,7 @@ export async function setupInitialTenant(
292
311
  user = tem.create(User, {
293
312
  email: (encryptedPayload as any).email ?? base.email,
294
313
  emailHash: isTenantDataEncryptionEnabled() ? (encryptedPayload as any).emailHash ?? computeEmailHash(base.email) : undefined,
295
- passwordHash,
314
+ passwordHash: resolvedPasswordHash,
296
315
  organizationId,
297
316
  tenantId,
298
317
  name: base.name ?? undefined,
@@ -340,6 +359,34 @@ function resolvePrimaryName(input: PrimaryUserInput): string | null {
340
359
  return null
341
360
  }
342
361
 
362
+ function readEnvValue(key: string): string | undefined {
363
+ const value = process.env[key]
364
+ if (typeof value !== 'string') return undefined
365
+ const trimmed = value.trim()
366
+ return trimmed.length > 0 ? trimmed : undefined
367
+ }
368
+
369
+ function addUniqueBaseUser(
370
+ baseUsers: Array<{ email: string; roles: string[]; name?: string | null; passwordHash?: string | null }>,
371
+ entry: { email: string; roles: string[]; name?: string | null; passwordHash?: string | null },
372
+ ) {
373
+ if (!entry.email) return
374
+ const normalized = entry.email.toLowerCase()
375
+ if (baseUsers.some((user) => user.email.toLowerCase() === normalized)) return
376
+ baseUsers.push(entry)
377
+ }
378
+
379
+ function isDemoModeEnabled(): boolean {
380
+ const parsed = parseBooleanToken(process.env.DEMO_MODE ?? '')
381
+ return parsed === false ? false : true
382
+ }
383
+
384
+ function shouldKeepDemoSuperadminDuringInit(): boolean {
385
+ if (process.env.OM_INIT_FLOW !== 'true') return false
386
+ if (!readEnvValue('OM_INIT_SUPERADMIN_EMAIL')) return false
387
+ return isDemoModeEnabled()
388
+ }
389
+
343
390
  async function resolvePasswordHash(input: PrimaryUserInput): Promise<string | null> {
344
391
  if (typeof input.hashedPassword === 'string') return input.hashedPassword
345
392
  if (input.password) return hash(input.password, 10)
@@ -396,6 +443,7 @@ async function ensureDefaultRoleAcls(
396
443
  'dashboards.admin.assign-widgets',
397
444
  'analytics.view',
398
445
  'api_keys.*',
446
+ 'notifications.manage',
399
447
  'perspectives.use',
400
448
  'perspectives.role_defaults',
401
449
  'business_rules.*',
@@ -487,6 +535,7 @@ async function ensureRoleAclFor(
487
535
 
488
536
  async function deactivateDemoSuperAdminIfSelfOnboardingEnabled(em: EntityManager) {
489
537
  if (process.env.SELF_SERVICE_ONBOARDING_ENABLED !== 'true') return
538
+ if (shouldKeepDemoSuperadminDuringInit()) return
490
539
  try {
491
540
  const user = await em.findOne(User, { email: DEMO_SUPERADMIN_EMAIL })
492
541
  if (!user) return
@@ -0,0 +1,109 @@
1
+ import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'
2
+
3
+ export const notificationTypes: NotificationTypeDefinition[] = [
4
+ {
5
+ type: 'auth.password_reset.requested',
6
+ module: 'auth',
7
+ titleKey: 'auth.notifications.passwordReset.requested.title',
8
+ bodyKey: 'auth.notifications.passwordReset.requested.body',
9
+ icon: 'key',
10
+ severity: 'info',
11
+ actions: [
12
+ {
13
+ id: 'view',
14
+ labelKey: 'common.view',
15
+ variant: 'outline',
16
+ href: '/backend/auth/profile',
17
+ icon: 'external-link',
18
+ },
19
+ ],
20
+ linkHref: '/backend/auth/profile',
21
+ expiresAfterHours: 24,
22
+ },
23
+ {
24
+ type: 'auth.password_reset.completed',
25
+ module: 'auth',
26
+ titleKey: 'auth.notifications.passwordReset.completed.title',
27
+ bodyKey: 'auth.notifications.passwordReset.completed.body',
28
+ icon: 'check-circle',
29
+ severity: 'success',
30
+ actions: [],
31
+ expiresAfterHours: 72,
32
+ },
33
+ {
34
+ type: 'auth.account.locked',
35
+ module: 'auth',
36
+ titleKey: 'auth.notifications.account.locked.title',
37
+ bodyKey: 'auth.notifications.account.locked.body',
38
+ icon: 'lock',
39
+ severity: 'warning',
40
+ actions: [
41
+ {
42
+ id: 'contact_support',
43
+ labelKey: 'auth.actions.contactSupport',
44
+ variant: 'default',
45
+ href: '/backend/support',
46
+ icon: 'mail',
47
+ },
48
+ ],
49
+ linkHref: '/backend/support',
50
+ },
51
+ {
52
+ type: 'auth.login.new_device',
53
+ module: 'auth',
54
+ titleKey: 'auth.notifications.login.newDevice.title',
55
+ bodyKey: 'auth.notifications.login.newDevice.body',
56
+ icon: 'smartphone',
57
+ severity: 'info',
58
+ actions: [
59
+ {
60
+ id: 'view_sessions',
61
+ labelKey: 'auth.actions.viewSessions',
62
+ variant: 'outline',
63
+ href: '/backend/auth/sessions',
64
+ icon: 'list',
65
+ },
66
+ ],
67
+ linkHref: '/backend/auth/sessions',
68
+ expiresAfterHours: 168, // 7 days
69
+ },
70
+ {
71
+ type: 'auth.role.assigned',
72
+ module: 'auth',
73
+ titleKey: 'auth.notifications.role.assigned.title',
74
+ bodyKey: 'auth.notifications.role.assigned.body',
75
+ icon: 'user-plus',
76
+ severity: 'success',
77
+ actions: [
78
+ {
79
+ id: 'view_permissions',
80
+ labelKey: 'auth.actions.viewPermissions',
81
+ variant: 'outline',
82
+ href: '/backend/auth/profile',
83
+ icon: 'shield',
84
+ },
85
+ ],
86
+ linkHref: '/backend/auth/profile',
87
+ expiresAfterHours: 168,
88
+ },
89
+ {
90
+ type: 'auth.role.revoked',
91
+ module: 'auth',
92
+ titleKey: 'auth.notifications.role.revoked.title',
93
+ bodyKey: 'auth.notifications.role.revoked.body',
94
+ icon: 'user-minus',
95
+ severity: 'warning',
96
+ actions: [
97
+ {
98
+ id: 'view_profile',
99
+ labelKey: 'common.view',
100
+ variant: 'outline',
101
+ href: '/backend/auth/profile',
102
+ },
103
+ ],
104
+ linkHref: '/backend/auth/profile',
105
+ expiresAfterHours: 168,
106
+ },
107
+ ]
108
+
109
+ export default notificationTypes
@@ -18,6 +18,29 @@ export class AuthService {
18
18
  } as any)
19
19
  }
20
20
 
21
+ async findUsersByEmail(email: string) {
22
+ const emailHash = computeEmailHash(email)
23
+ return this.em.find(User, {
24
+ deletedAt: null,
25
+ $or: [
26
+ { email },
27
+ { emailHash },
28
+ ],
29
+ } as any)
30
+ }
31
+
32
+ async findUserByEmailAndTenant(email: string, tenantId: string) {
33
+ const emailHash = computeEmailHash(email)
34
+ return this.em.findOne(User, {
35
+ tenantId,
36
+ deletedAt: null,
37
+ $or: [
38
+ { email },
39
+ { emailHash },
40
+ ],
41
+ } as any)
42
+ }
43
+
21
44
  async verifyPassword(user: User, password: string) {
22
45
  if (!user.passwordHash) return false
23
46
  return compare(password, user.passwordHash)
@@ -75,15 +98,15 @@ export class AuthService {
75
98
  return { user, token }
76
99
  }
77
100
 
78
- async confirmPasswordReset(token: string, newPassword: string) {
101
+ async confirmPasswordReset(token: string, newPassword: string): Promise<User | null> {
79
102
  const now = new Date()
80
103
  const row = await this.em.findOne(PasswordReset, { token })
81
- if (!row || (row.usedAt && row.usedAt <= now) || row.expiresAt <= now) return false
104
+ if (!row || (row.usedAt && row.usedAt <= now) || row.expiresAt <= now) return null
82
105
  const user = await this.em.findOne(User, { id: row.user.id })
83
- if (!user) return false
106
+ if (!user) return null
84
107
  user.passwordHash = await hash(newPassword, 10)
85
108
  row.usedAt = new Date()
86
109
  await this.em.flush()
87
- return true
110
+ return user
88
111
  }
89
112
  }
@@ -4,6 +4,7 @@ import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
4
4
  import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
5
5
  import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
6
6
  import type { EntityManager } from '@mikro-orm/postgresql'
7
+ import type { EventBus } from '@open-mercato/events'
7
8
  import { ruleEngineContextSchema } from '../../data/validators'
8
9
  import * as ruleEngine from '../../lib/rule-engine'
9
10
 
@@ -46,6 +47,12 @@ export async function POST(req: Request) {
46
47
 
47
48
  const container = await createRequestContainer()
48
49
  const em = container.resolve('em') as EntityManager
50
+ let eventBus: EventBus | null = null
51
+ try {
52
+ eventBus = container.resolve('eventBus') as EventBus
53
+ } catch {
54
+ eventBus = null
55
+ }
49
56
 
50
57
  let body: any
51
58
  try {
@@ -85,7 +92,7 @@ export async function POST(req: Request) {
85
92
  }
86
93
 
87
94
  try {
88
- const result = await ruleEngine.executeRules(em, context)
95
+ const result = await ruleEngine.executeRules(em, context, { eventBus })
89
96
 
90
97
  const response = {
91
98
  allowed: result.allowed,
@@ -264,14 +264,17 @@ export default function RulesListPage() {
264
264
  <RowActions
265
265
  items={[
266
266
  {
267
+ id: 'edit',
267
268
  label: t('common.edit'),
268
269
  href: `/backend/rules/${row.original.id}`,
269
270
  },
270
271
  {
272
+ id: row.original.enabled ? 'disable' : 'enable',
271
273
  label: row.original.enabled ? t('common.disable') : t('common.enable'),
272
274
  onSelect: () => handleToggleEnabled(row.original.id, row.original.enabled),
273
275
  },
274
276
  {
277
+ id: 'duplicate',
275
278
  label: t('common.duplicate'),
276
279
  onSelect: () => {
277
280
  // TODO: Implement duplicate functionality in Step 5.2
@@ -279,6 +282,7 @@ export default function RulesListPage() {
279
282
  },
280
283
  },
281
284
  {
285
+ id: 'delete',
282
286
  label: t('common.delete'),
283
287
  onSelect: () => handleDelete(row.original.id, row.original.ruleName),
284
288
  destructive: true,
@@ -196,14 +196,17 @@ export default function RuleSetsListPage() {
196
196
  <RowActions
197
197
  items={[
198
198
  {
199
+ id: 'edit',
199
200
  label: t('common.edit'),
200
201
  href: `/backend/sets/${row.original.id}`,
201
202
  },
202
203
  {
204
+ id: row.original.enabled ? 'disable' : 'enable',
203
205
  label: row.original.enabled ? t('common.disable') : t('common.enable'),
204
206
  onSelect: () => handleToggleEnabled(row.original.id, row.original.enabled),
205
207
  },
206
208
  {
209
+ id: 'delete',
207
210
  label: t('common.delete'),
208
211
  onSelect: () => handleDelete(row.original.id, row.original.setName),
209
212
  destructive: true,
@@ -44,6 +44,7 @@ const seedGuardRules: ModuleCli = {
44
44
  const rulesPath = path.join(__dirname, '../workflows/examples', 'guard-rules-example.json')
45
45
  const rulesData = JSON.parse(fs.readFileSync(rulesPath, 'utf8'))
46
46
 
47
+ console.log('🧠 Seeding guard rules...')
47
48
  let seededCount = 0
48
49
  let skippedCount = 0
49
50
 
@@ -73,7 +74,7 @@ const seedGuardRules: ModuleCli = {
73
74
  seededCount++
74
75
  }
75
76
 
76
- console.log(`\n Guard rules seeding complete:`)
77
+ console.log(`\n Guard rules seeding complete:`)
77
78
  console.log(` - Seeded: ${seededCount}`)
78
79
  console.log(` - Skipped (existing): ${skippedCount}`)
79
80
  console.log(` - Total: ${rulesData.length}`)
@@ -367,5 +367,7 @@
367
367
  "business_rules.components.conditionRow.field.comparisonPlaceholder": "e.g., user.role",
368
368
  "business_rules.components.conditionRow.value.help": "Use JSON for arrays: [\"a\",\"b\"]",
369
369
  "business_rules.components.conditionRow.field.comparisonHelp": "Field path to compare with",
370
- "business_rules.components.conditionRow.deleteCondition": "Delete condition"
370
+ "business_rules.components.conditionRow.deleteCondition": "Delete condition",
371
+ "businessRules.notifications.rule.executionFailed.title": "Business Rule Failed",
372
+ "businessRules.notifications.rule.executionFailed.body": "Rule \"{ruleName}\" failed to execute{entityType, select, other { on {entityType}}}: {errorMessage}"
371
373
  }
@@ -654,6 +654,57 @@ describe('Rule Engine (Unit Tests)', () => {
654
654
  expect(result.errors).toBeDefined()
655
655
  expect(result.errors![0]).toContain('Rule count limit exceeded')
656
656
  })
657
+
658
+ test('should emit execution_failed event when rule evaluation fails', async () => {
659
+ const mockRule: Partial<BusinessRule> = {
660
+ id: 'rule-1',
661
+ ruleId: 'TEST-001',
662
+ ruleName: 'Test Rule',
663
+ ruleType: 'ACTION',
664
+ entityType: 'WorkOrder',
665
+ conditionExpression: { field: 'status', operator: '=', value: 'RELEASED' },
666
+ enabled: true,
667
+ tenantId: testTenantId,
668
+ organizationId: testOrgId,
669
+ }
670
+
671
+ mockEm.find.mockResolvedValue([mockRule as BusinessRule])
672
+ mockEm.create.mockReturnValue({ id: 'log-1' } as any)
673
+ mockEm.persistAndFlush.mockResolvedValue(undefined)
674
+
675
+ jest.mocked(ruleEvaluator.evaluateSingleRule).mockResolvedValue({
676
+ rule: mockRule as BusinessRule,
677
+ conditionsPassed: false,
678
+ evaluationCompleted: false,
679
+ evaluationTime: 1,
680
+ error: 'Evaluation error',
681
+ })
682
+
683
+ const eventBus = { emitEvent: jest.fn().mockResolvedValue(undefined) }
684
+
685
+ const context: RuleEngineContext = {
686
+ entityType: 'WorkOrder',
687
+ entityId: testEntityId,
688
+ data: { status: 'RELEASED' },
689
+ tenantId: testTenantId,
690
+ organizationId: testOrgId,
691
+ dryRun: false,
692
+ }
693
+
694
+ await ruleEngine.executeRules(mockEm, context, { eventBus })
695
+
696
+ expect(eventBus.emitEvent).toHaveBeenCalledWith(
697
+ 'business_rules.rule.execution_failed',
698
+ expect.objectContaining({
699
+ ruleId: 'TEST-001',
700
+ ruleName: 'Test Rule',
701
+ entityType: 'WorkOrder',
702
+ errorMessage: 'Evaluation error',
703
+ tenantId: testTenantId,
704
+ organizationId: testOrgId,
705
+ })
706
+ )
707
+ })
657
708
  })
658
709
 
659
710
  describe('logRuleExecution', () => {
@@ -1,4 +1,5 @@
1
1
  import type { EntityManager } from '@mikro-orm/core'
2
+ import type { EventBus } from '@open-mercato/events'
2
3
  import { BusinessRule, RuleExecutionLog, type RuleType } from '../data/entities'
3
4
  import * as ruleEvaluator from './rule-evaluator'
4
5
  import * as actionExecutor from './action-executor'
@@ -85,6 +86,19 @@ export interface RuleDiscoveryOptions {
85
86
  ruleType?: RuleType
86
87
  }
87
88
 
89
+ export type RuleEngineExecutionOptions = {
90
+ eventBus?: Pick<EventBus, 'emitEvent'> | null
91
+ }
92
+
93
+ type RuleExecutionFailedPayload = {
94
+ ruleId: string
95
+ ruleName: string
96
+ entityType?: string | null
97
+ errorMessage?: string | null
98
+ tenantId: string
99
+ organizationId?: string | null
100
+ }
101
+
88
102
  /**
89
103
  * Execute a function with a timeout
90
104
  */
@@ -113,7 +127,8 @@ async function withTimeout<T>(
113
127
  */
114
128
  export async function executeRules(
115
129
  em: EntityManager,
116
- context: RuleEngineContext
130
+ context: RuleEngineContext,
131
+ options: RuleEngineExecutionOptions = {}
117
132
  ): Promise<RuleEngineResult> {
118
133
  // Validate input
119
134
  const validation = ruleEngineContextSchema.safeParse(context)
@@ -159,7 +174,7 @@ export async function executeRules(
159
174
  const executionPromise = (async () => {
160
175
  for (const rule of rules) {
161
176
  try {
162
- const ruleResult = await executeSingleRule(em, rule, context)
177
+ const ruleResult = await executeSingleRule(em, rule, context, options)
163
178
  executedRules.push(ruleResult)
164
179
 
165
180
  if (ruleResult.logId) {
@@ -177,6 +192,17 @@ export async function executeRules(
177
192
  `Unexpected error in rule execution [ruleId=${rule.ruleId}, type=${rule.ruleType}]: ${errorMessage}`
178
193
  )
179
194
 
195
+ if (!context.dryRun) {
196
+ await emitRuleExecutionFailed(options.eventBus, {
197
+ ruleId: rule.ruleId,
198
+ ruleName: rule.ruleName,
199
+ entityType: context.entityType ?? null,
200
+ errorMessage,
201
+ tenantId: context.tenantId,
202
+ organizationId: context.organizationId ?? null,
203
+ })
204
+ }
205
+
180
206
  executedRules.push({
181
207
  rule,
182
208
  conditionResult: false,
@@ -233,7 +259,8 @@ export async function executeRules(
233
259
  export async function executeSingleRule(
234
260
  em: EntityManager,
235
261
  rule: BusinessRule,
236
- context: RuleEngineContext
262
+ context: RuleEngineContext,
263
+ options: RuleEngineExecutionOptions = {}
237
264
  ): Promise<RuleExecutionResult> {
238
265
  const startTime = Date.now()
239
266
 
@@ -269,6 +296,15 @@ export async function executeSingleRule(
269
296
  executionTime,
270
297
  error: result.error,
271
298
  })
299
+
300
+ await emitRuleExecutionFailed(options.eventBus, {
301
+ ruleId: rule.ruleId,
302
+ ruleName: rule.ruleName,
303
+ entityType: context.entityType ?? null,
304
+ errorMessage: result.error ?? null,
305
+ tenantId: context.tenantId,
306
+ organizationId: context.organizationId ?? null,
307
+ })
272
308
  }
273
309
 
274
310
  return {
@@ -346,6 +382,15 @@ export async function executeSingleRule(
346
382
  executionTime,
347
383
  error: enhancedError,
348
384
  })
385
+
386
+ await emitRuleExecutionFailed(options.eventBus, {
387
+ ruleId: rule.ruleId,
388
+ ruleName: rule.ruleName,
389
+ entityType: context.entityType ?? null,
390
+ errorMessage: enhancedError,
391
+ tenantId: context.tenantId,
392
+ organizationId: context.organizationId ?? null,
393
+ })
349
394
  }
350
395
 
351
396
  return {
@@ -544,3 +589,12 @@ export async function logRuleExecution(
544
589
 
545
590
  return log.id
546
591
  }
592
+
593
+ async function emitRuleExecutionFailed(
594
+ eventBus: Pick<EventBus, 'emitEvent'> | null | undefined,
595
+ payload: RuleExecutionFailedPayload
596
+ ): Promise<void> {
597
+ if (!eventBus?.emitEvent) return
598
+
599
+ await eventBus.emitEvent('business_rules.rule.execution_failed', payload).catch(() => undefined)
600
+ }
@@ -0,0 +1,25 @@
1
+ import type { NotificationTypeDefinition } from '@open-mercato/shared/modules/notifications/types'
2
+
3
+ export const notificationTypes: NotificationTypeDefinition[] = [
4
+ {
5
+ type: 'business_rules.rule.execution_failed',
6
+ module: 'business_rules',
7
+ titleKey: 'businessRules.notifications.rule.executionFailed.title',
8
+ bodyKey: 'businessRules.notifications.rule.executionFailed.body',
9
+ icon: 'alert-triangle',
10
+ severity: 'error',
11
+ actions: [
12
+ {
13
+ id: 'view',
14
+ labelKey: 'common.view',
15
+ variant: 'outline',
16
+ href: '/backend/business-rules/{sourceEntityId}',
17
+ icon: 'external-link',
18
+ },
19
+ ],
20
+ linkHref: '/backend/business-rules/{sourceEntityId}',
21
+ expiresAfterHours: 168, // 7 days
22
+ },
23
+ ]
24
+
25
+ export default notificationTypes
@@ -0,0 +1,50 @@
1
+ import type { EntityManager } from '@mikro-orm/postgresql'
2
+ import { resolveNotificationService } from '../../notifications/lib/notificationService'
3
+ import { buildFeatureNotificationFromType } from '../../notifications/lib/notificationBuilder'
4
+ import { notificationTypes } from '../notifications'
5
+
6
+ export const metadata = {
7
+ event: 'business_rules.rule.execution_failed',
8
+ persistent: true,
9
+ id: 'business_rules:rule-execution-failed-notification',
10
+ }
11
+
12
+ type RuleExecutionFailedPayload = {
13
+ ruleId: string
14
+ ruleName: string
15
+ entityType?: string | null
16
+ errorMessage?: string | null
17
+ tenantId: string
18
+ organizationId?: string | null
19
+ }
20
+
21
+ type ResolverContext = {
22
+ resolve: <T = unknown>(name: string) => T
23
+ }
24
+
25
+ export default async function handle(payload: RuleExecutionFailedPayload, ctx: ResolverContext) {
26
+ try {
27
+ const notificationService = resolveNotificationService(ctx)
28
+ const typeDef = notificationTypes.find((type) => type.type === 'business_rules.rule.execution_failed')
29
+ if (!typeDef) return
30
+
31
+ const notificationInput = buildFeatureNotificationFromType(typeDef, {
32
+ requiredFeature: 'business_rules.manage',
33
+ bodyVariables: {
34
+ ruleName: payload.ruleName,
35
+ entityType: payload.entityType ?? '',
36
+ errorMessage: payload.errorMessage ?? 'Unknown error',
37
+ },
38
+ sourceEntityType: 'business_rules:rule',
39
+ sourceEntityId: payload.ruleId,
40
+ linkHref: `/backend/business-rules/${payload.ruleId}`,
41
+ })
42
+
43
+ await notificationService.createForFeature(notificationInput, {
44
+ tenantId: payload.tenantId,
45
+ organizationId: payload.organizationId ?? null,
46
+ })
47
+ } catch (err) {
48
+ console.error('[business_rules:rule-execution-failed-notification] Failed to create notification:', err)
49
+ }
50
+ }
@@ -420,10 +420,12 @@ export function PriceKindSettings() {
420
420
  <RowActions
421
421
  items={[
422
422
  {
423
+ id: 'edit',
423
424
  label: t('catalog.priceKinds.actions.edit', 'Edit'),
424
425
  onSelect: () => openDialog({ mode: 'edit', entry }),
425
426
  },
426
427
  {
428
+ id: 'delete',
427
429
  label: t('catalog.priceKinds.actions.delete', 'Delete'),
428
430
  destructive: true,
429
431
  onSelect: () => { void handleDelete(entry) },
@@ -221,8 +221,8 @@ export default function CategoriesDataTable() {
221
221
  canManage ? (
222
222
  <RowActions
223
223
  items={[
224
- { label: t('catalog.categories.list.actions.edit', 'Edit'), href: `/backend/catalog/categories/${row.id}/edit` },
225
- { label: t('catalog.categories.list.actions.delete', 'Delete'), destructive: true, onSelect: () => handleDelete(row) },
224
+ { id: 'edit', label: t('catalog.categories.list.actions.edit', 'Edit'), href: `/backend/catalog/categories/${row.id}/edit` },
225
+ { id: 'delete', label: t('catalog.categories.list.actions.delete', 'Delete'), destructive: true, onSelect: () => handleDelete(row) },
226
226
  ]}
227
227
  />
228
228
  ) : null