@open-mercato/core 0.6.4-develop.4236.1.9fa6806b34 → 0.6.4-develop.4254.1.7a123d970c

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 (118) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/helpers/integration/authFixtures.js +70 -1
  3. package/dist/helpers/integration/authFixtures.js.map +2 -2
  4. package/dist/helpers/integration/dbFixtures.js +98 -0
  5. package/dist/helpers/integration/dbFixtures.js.map +7 -0
  6. package/dist/modules/business_rules/api/execute/route.js +2 -1
  7. package/dist/modules/business_rules/api/execute/route.js.map +2 -2
  8. package/dist/modules/business_rules/api/rules/route.js +10 -0
  9. package/dist/modules/business_rules/api/rules/route.js.map +2 -2
  10. package/dist/modules/business_rules/backend/logs/[id]/page.js +24 -5
  11. package/dist/modules/business_rules/backend/logs/[id]/page.js.map +2 -2
  12. package/dist/modules/business_rules/cli.js +6 -0
  13. package/dist/modules/business_rules/cli.js.map +2 -2
  14. package/dist/modules/business_rules/lib/rule-engine.js +116 -9
  15. package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
  16. package/dist/modules/business_rules/subscribers/crud-rule-trigger.js +3 -2
  17. package/dist/modules/business_rules/subscribers/crud-rule-trigger.js.map +2 -2
  18. package/dist/modules/catalog/api/offers/route.js +15 -5
  19. package/dist/modules/catalog/api/offers/route.js.map +2 -2
  20. package/dist/modules/catalog/api/products/route.js +21 -4
  21. package/dist/modules/catalog/api/products/route.js.map +2 -2
  22. package/dist/modules/catalog/lib/pricing.js +6 -0
  23. package/dist/modules/catalog/lib/pricing.js.map +2 -2
  24. package/dist/modules/catalog/services/catalogPricingService.js +5 -1
  25. package/dist/modules/catalog/services/catalogPricingService.js.map +2 -2
  26. package/dist/modules/currencies/backend/currencies/[id]/page.js +19 -2
  27. package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
  28. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js +27 -7
  29. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js.map +2 -2
  30. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js +27 -7
  31. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js.map +2 -2
  32. package/dist/modules/customers/api/activities/route.js +15 -2
  33. package/dist/modules/customers/api/activities/route.js.map +2 -2
  34. package/dist/modules/customers/api/comments/route.js +15 -3
  35. package/dist/modules/customers/api/comments/route.js.map +2 -2
  36. package/dist/modules/customers/api/companies/[id]/people/route.js +2 -4
  37. package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
  38. package/dist/modules/customers/api/companies/[id]/route.js +2 -4
  39. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  40. package/dist/modules/customers/api/deals/[id]/companies/route.js +2 -4
  41. package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
  42. package/dist/modules/customers/api/deals/[id]/people/route.js +2 -4
  43. package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
  44. package/dist/modules/customers/api/deals/[id]/route.js +2 -9
  45. package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
  46. package/dist/modules/customers/api/deals/[id]/stats/route.js +2 -9
  47. package/dist/modules/customers/api/deals/[id]/stats/route.js.map +2 -2
  48. package/dist/modules/customers/api/entity-roles-factory.js +2 -8
  49. package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
  50. package/dist/modules/customers/api/people/[id]/companies/context.js +2 -4
  51. package/dist/modules/customers/api/people/[id]/companies/context.js.map +2 -2
  52. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +2 -4
  53. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
  54. package/dist/modules/customers/api/people/[id]/route.js +2 -4
  55. package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
  56. package/dist/modules/customers/backend/customers/people/[id]/page.js +29 -8
  57. package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
  58. package/dist/modules/directory/utils/organizationScopeGuard.js +22 -0
  59. package/dist/modules/directory/utils/organizationScopeGuard.js.map +7 -0
  60. package/dist/modules/progress/acl.js +8 -4
  61. package/dist/modules/progress/acl.js.map +2 -2
  62. package/dist/modules/workflows/backend/events/[id]/page.js +24 -6
  63. package/dist/modules/workflows/backend/events/[id]/page.js.map +2 -2
  64. package/dist/modules/workflows/backend/instances/[id]/page.js +27 -5
  65. package/dist/modules/workflows/backend/instances/[id]/page.js.map +2 -2
  66. package/dist/modules/workflows/backend/tasks/[id]/page.js +25 -6
  67. package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
  68. package/dist/modules/workflows/cli.js +8 -0
  69. package/dist/modules/workflows/cli.js.map +2 -2
  70. package/dist/modules/workflows/lib/seeds.js +8 -4
  71. package/dist/modules/workflows/lib/seeds.js.map +2 -2
  72. package/dist/modules/workflows/setup.js +3 -1
  73. package/dist/modules/workflows/setup.js.map +2 -2
  74. package/package.json +7 -7
  75. package/src/helpers/integration/authFixtures.ts +98 -0
  76. package/src/helpers/integration/dbFixtures.ts +144 -0
  77. package/src/modules/business_rules/api/execute/route.ts +2 -1
  78. package/src/modules/business_rules/api/rules/route.ts +10 -0
  79. package/src/modules/business_rules/backend/logs/[id]/page.tsx +32 -7
  80. package/src/modules/business_rules/cli.ts +6 -0
  81. package/src/modules/business_rules/lib/rule-engine.ts +163 -9
  82. package/src/modules/business_rules/subscribers/crud-rule-trigger.ts +3 -2
  83. package/src/modules/catalog/api/offers/route.ts +20 -5
  84. package/src/modules/catalog/api/products/route.ts +23 -4
  85. package/src/modules/catalog/lib/pricing.ts +9 -0
  86. package/src/modules/catalog/services/catalogPricingService.ts +6 -0
  87. package/src/modules/currencies/backend/currencies/[id]/page.tsx +21 -2
  88. package/src/modules/currencies/i18n/de.json +1 -0
  89. package/src/modules/currencies/i18n/en.json +1 -0
  90. package/src/modules/currencies/i18n/es.json +1 -0
  91. package/src/modules/currencies/i18n/pl.json +1 -0
  92. package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +34 -11
  93. package/src/modules/customer_accounts/backend/customer_accounts/users/[id]/page.tsx +34 -11
  94. package/src/modules/customers/api/activities/route.ts +16 -5
  95. package/src/modules/customers/api/comments/route.ts +15 -5
  96. package/src/modules/customers/api/companies/[id]/people/route.ts +2 -4
  97. package/src/modules/customers/api/companies/[id]/route.ts +2 -5
  98. package/src/modules/customers/api/deals/[id]/companies/route.ts +2 -4
  99. package/src/modules/customers/api/deals/[id]/people/route.ts +2 -4
  100. package/src/modules/customers/api/deals/[id]/route.ts +2 -9
  101. package/src/modules/customers/api/deals/[id]/stats/route.ts +2 -9
  102. package/src/modules/customers/api/entity-roles-factory.ts +2 -12
  103. package/src/modules/customers/api/people/[id]/companies/context.ts +2 -5
  104. package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +2 -5
  105. package/src/modules/customers/api/people/[id]/route.ts +2 -5
  106. package/src/modules/customers/backend/customers/people/[id]/page.tsx +35 -11
  107. package/src/modules/directory/utils/organizationScopeGuard.ts +39 -0
  108. package/src/modules/progress/acl.ts +4 -0
  109. package/src/modules/workflows/backend/events/[id]/page.tsx +32 -10
  110. package/src/modules/workflows/backend/instances/[id]/page.tsx +33 -9
  111. package/src/modules/workflows/backend/tasks/[id]/page.tsx +33 -10
  112. package/src/modules/workflows/cli.ts +8 -0
  113. package/src/modules/workflows/i18n/de.json +1 -0
  114. package/src/modules/workflows/i18n/en.json +1 -0
  115. package/src/modules/workflows/i18n/es.json +1 -0
  116. package/src/modules/workflows/i18n/pl.json +1 -0
  117. package/src/modules/workflows/lib/seeds.ts +13 -3
  118. package/src/modules/workflows/setup.ts +3 -1
@@ -19,6 +19,7 @@ import { MobileInstanceOverview } from '../../../components/mobile/MobileInstanc
19
19
  import { useIsMobile } from '@open-mercato/ui/hooks/useIsMobile'
20
20
  import { definitionToGraph } from '../../../lib/graph-utils'
21
21
  import { Node } from '@xyflow/react'
22
+ import { RecordNotFoundState, ErrorMessage } from '@open-mercato/ui/backend/detail'
22
23
 
23
24
  export default function WorkflowInstanceDetailPage({ params }: { params?: { id?: string } }) {
24
25
  const id = params?.id
@@ -32,7 +33,13 @@ export default function WorkflowInstanceDetailPage({ params }: { params?: { id?:
32
33
  queryFn: async () => {
33
34
  const response = await apiFetch(`/api/workflows/instances/${id}`)
34
35
  if (!response.ok) {
35
- throw new Error(t('workflows.instances.notFound') || 'Instance not found')
36
+ const httpErr = new Error(
37
+ response.status === 404
38
+ ? t('workflows.instances.detail.notFound', 'Workflow instance not found.')
39
+ : t('workflows.instances.loadFailed', 'Failed to load workflow instance.')
40
+ ) as Error & { status: number }
41
+ httpErr.status = response.status
42
+ throw httpErr
36
43
  }
37
44
  const data = await response.json()
38
45
  return data.data as WorkflowInstance
@@ -368,18 +375,35 @@ export default function WorkflowInstanceDetailPage({ params }: { params?: { id?:
368
375
  )
369
376
  }
370
377
 
378
+ const isNotFound = !isLoading && (error as (Error & { status?: number }) | null)?.status === 404
379
+
380
+ if (isNotFound) {
381
+ return (
382
+ <Page>
383
+ <PageBody>
384
+ <RecordNotFoundState
385
+ label={t('workflows.instances.detail.notFound', 'Workflow instance not found.')}
386
+ backHref="/backend/instances"
387
+ backLabel={t('workflows.instances.actions.backToList', 'Back to instances')}
388
+ />
389
+ </PageBody>
390
+ {ConfirmDialogElement}
391
+ </Page>
392
+ )
393
+ }
394
+
371
395
  if (error || !instance) {
372
396
  return (
373
397
  <Page>
374
398
  <PageBody>
375
- <div className="flex h-[50vh] flex-col items-center justify-center gap-2 text-muted-foreground">
376
- <p>{error ? t('workflows.instances.loadFailed') : t('workflows.instances.detail.notFound') || 'Workflow instance not found.'}</p>
377
- <Button asChild variant="outline">
378
- <Link href="/backend/instances">
379
- {t('workflows.instances.actions.backToList') || 'Back to instances'}
380
- </Link>
381
- </Button>
382
- </div>
399
+ <ErrorMessage
400
+ label={(error as Error | null)?.message ?? t('workflows.instances.loadFailed', 'Failed to load workflow instance.')}
401
+ action={
402
+ <Button asChild variant="outline" size="sm">
403
+ <Link href="/backend/instances">{t('workflows.instances.actions.backToList', 'Back to instances')}</Link>
404
+ </Button>
405
+ }
406
+ />
383
407
  </PageBody>
384
408
  {ConfirmDialogElement}
385
409
  </Page>
@@ -25,6 +25,7 @@ import { useT } from '@open-mercato/shared/lib/i18n/context'
25
25
  import { MobileTaskForm } from '../../../components/mobile/MobileTaskForm'
26
26
  import { useIsMobile } from '@open-mercato/ui/hooks/useIsMobile'
27
27
  import type { UserTaskResponse, UserTaskStatus } from '../../../data/types'
28
+ import { RecordNotFoundState, ErrorMessage } from '@open-mercato/ui/backend/detail'
28
29
 
29
30
  export default function UserTaskDetailPage({ params }: { params: { id: string } }) {
30
31
  const router = useRouter()
@@ -44,12 +45,16 @@ export default function UserTaskDetailPage({ params }: { params: { id: string }
44
45
  const result = await apiCall<{ data: UserTaskResponse }>(
45
46
  `/api/workflows/tasks/${params.id}`
46
47
  )
47
-
48
48
  if (!result.ok) {
49
- throw new Error('Failed to fetch task')
49
+ const httpErr = new Error(
50
+ result.status === 404
51
+ ? t('workflows.tasks.detail.notFound', 'Task not found')
52
+ : t('workflows.tasks.detail.loadFailed', 'Failed to load task')
53
+ ) as Error & { status: number }
54
+ httpErr.status = result.status
55
+ throw httpErr
50
56
  }
51
-
52
- return result.result?.data || null
57
+ return result.result?.data ?? null
53
58
  },
54
59
  })
55
60
 
@@ -347,16 +352,34 @@ export default function UserTaskDetailPage({ params }: { params: { id: string }
347
352
  )
348
353
  }
349
354
 
355
+ const isNotFound = !isLoading && (error as (Error & { status?: number }) | null)?.status === 404
356
+
357
+ if (isNotFound) {
358
+ return (
359
+ <Page>
360
+ <PageBody>
361
+ <RecordNotFoundState
362
+ label={t('workflows.tasks.detail.notFound', 'Task not found')}
363
+ backHref="/backend/tasks"
364
+ backLabel={t('workflows.tasks.detail.backToList', 'Back to Tasks')}
365
+ />
366
+ </PageBody>
367
+ </Page>
368
+ )
369
+ }
370
+
350
371
  if (error || !task) {
351
372
  return (
352
373
  <Page>
353
374
  <PageBody>
354
- <div className="p-8 text-center">
355
- <p className="text-status-error-text">{t('workflows.tasks.detail.notFound')}</p>
356
- <Button onClick={() => router.push('/backend/tasks')} className="mt-4">
357
- {t('workflows.tasks.detail.backToList')}
358
- </Button>
359
- </div>
375
+ <ErrorMessage
376
+ label={(error as Error | null)?.message ?? t('workflows.tasks.detail.loadFailed', 'Failed to load task')}
377
+ action={
378
+ <Button asChild variant="outline" size="sm">
379
+ <Link href="/backend/tasks">{t('workflows.tasks.detail.backToList', 'Back to Tasks')}</Link>
380
+ </Button>
381
+ }
382
+ />
360
383
  </PageBody>
361
384
  </Page>
362
385
  )
@@ -4,6 +4,10 @@ import { getRedisUrl, getRedisUrlOrThrow } from '@open-mercato/shared/lib/redis/
4
4
  import type { EntityManager } from '@mikro-orm/postgresql'
5
5
  import { WorkflowDefinition } from './data/entities'
6
6
  import { BusinessRule, type RuleType } from '@open-mercato/core/modules/business_rules/data/entities'
7
+ import {
8
+ invalidateBusinessRuleDiscoveryCache,
9
+ resolveBusinessRuleDiscoveryCache,
10
+ } from '@open-mercato/core/modules/business_rules/lib/rule-engine'
7
11
  import * as fs from 'fs'
8
12
  import * as path from 'path'
9
13
  import { fileURLToPath } from 'url'
@@ -69,6 +73,7 @@ const seedDemoWithRules: ModuleCli = {
69
73
  try {
70
74
  const { resolve } = await createRequestContainer()
71
75
  const em = resolve<EntityManager>('em')
76
+ const cache = resolveBusinessRuleDiscoveryCache(resolve)
72
77
 
73
78
  // Import BusinessRule entity
74
79
  const { BusinessRule } = await import('../business_rules/data/entities')
@@ -100,6 +105,7 @@ const seedDemoWithRules: ModuleCli = {
100
105
  })
101
106
 
102
107
  await em.persist(rule).flush()
108
+ await invalidateBusinessRuleDiscoveryCache(cache, tenantId, organizationId)
103
109
  console.log(` ✓ Seeded guard rule: ${rule.ruleName}`)
104
110
  seededCount++
105
111
  }
@@ -211,6 +217,7 @@ const seedOrderApproval: ModuleCli = {
211
217
  try {
212
218
  const { resolve } = await createRequestContainer()
213
219
  const em = resolve<EntityManager>('em')
220
+ const cache = resolveBusinessRuleDiscoveryCache(resolve)
214
221
 
215
222
  // 1. Seed order approval guard rules first
216
223
  const guardRulesPath = path.join(__dirname, 'examples', 'order-approval-guard-rules.json')
@@ -252,6 +259,7 @@ const seedOrderApproval: ModuleCli = {
252
259
 
253
260
  if (rulesSeeded > 0) {
254
261
  await em.flush()
262
+ await invalidateBusinessRuleDiscoveryCache(cache, tenantId, organizationId)
255
263
  }
256
264
 
257
265
  console.log(`✅ Seeded order approval guard rules`)
@@ -1007,6 +1007,7 @@
1007
1007
  "workflows.tasks.detail.commentsPlaceholder": "Fügen Sie Kommentare oder Anmerkungen zu dieser Aufgabe hinzu…",
1008
1008
  "workflows.tasks.detail.completeTask": "Aufgabe abschließen",
1009
1009
  "workflows.tasks.detail.form.selectOption": "-- Option auswählen --",
1010
+ "workflows.tasks.detail.loadFailed": "Aufgabe konnte nicht geladen werden",
1010
1011
  "workflows.tasks.detail.loading": "Aufgabe wird geladen…",
1011
1012
  "workflows.tasks.detail.noFormSchema": "Diese Aufgabe hat kein auszufüllendes Formular. Sie können optionale Kommentare hinzufügen und absenden.",
1012
1013
  "workflows.tasks.detail.notFound": "Aufgabe nicht gefunden",
@@ -1007,6 +1007,7 @@
1007
1007
  "workflows.tasks.detail.commentsPlaceholder": "Add any comments or notes about this task...",
1008
1008
  "workflows.tasks.detail.completeTask": "Complete Task",
1009
1009
  "workflows.tasks.detail.form.selectOption": "-- Select an option --",
1010
+ "workflows.tasks.detail.loadFailed": "Failed to load task",
1010
1011
  "workflows.tasks.detail.loading": "Loading task...",
1011
1012
  "workflows.tasks.detail.noFormSchema": "This task has no form to complete. You can add optional comments and submit.",
1012
1013
  "workflows.tasks.detail.notFound": "Task not found",
@@ -1007,6 +1007,7 @@
1007
1007
  "workflows.tasks.detail.commentsPlaceholder": "Agrega comentarios o notas sobre esta tarea...",
1008
1008
  "workflows.tasks.detail.completeTask": "Completar tarea",
1009
1009
  "workflows.tasks.detail.form.selectOption": "-- Selecciona una opcion --",
1010
+ "workflows.tasks.detail.loadFailed": "Error al cargar la tarea",
1010
1011
  "workflows.tasks.detail.loading": "Cargando tarea...",
1011
1012
  "workflows.tasks.detail.noFormSchema": "Esta tarea no tiene formulario. Puedes agregar comentarios opcionales y enviar.",
1012
1013
  "workflows.tasks.detail.notFound": "Tarea no encontrada",
@@ -1007,6 +1007,7 @@
1007
1007
  "workflows.tasks.detail.commentsPlaceholder": "Dodaj komentarze lub notatki dotyczące tego zadania...",
1008
1008
  "workflows.tasks.detail.completeTask": "Zakończ Zadanie",
1009
1009
  "workflows.tasks.detail.form.selectOption": "-- Wybierz opcję --",
1010
+ "workflows.tasks.detail.loadFailed": "Nie udało się załadować zadania",
1010
1011
  "workflows.tasks.detail.loading": "Ładowanie zadania...",
1011
1012
  "workflows.tasks.detail.noFormSchema": "To zadanie nie ma formularza do wypełnienia. Możesz dodać opcjonalne komentarze i wysłać.",
1012
1013
  "workflows.tasks.detail.notFound": "Zadanie nie znalezione",
@@ -4,6 +4,10 @@ import * as path from 'path'
4
4
  import { fileURLToPath } from 'node:url'
5
5
  import { WorkflowDefinition, type WorkflowDefinitionData } from '../data/entities'
6
6
  import { BusinessRule, type RuleType } from '@open-mercato/core/modules/business_rules/data/entities'
7
+ import {
8
+ invalidateBusinessRuleDiscoveryCache,
9
+ type RuleDiscoveryCache,
10
+ } from '@open-mercato/core/modules/business_rules/lib/rule-engine'
7
11
 
8
12
  const __esmDirname = path.dirname(fileURLToPath(import.meta.url))
9
13
 
@@ -130,6 +134,7 @@ async function seedGuardRules(
130
134
  em: EntityManager,
131
135
  scope: WorkflowSeedScope,
132
136
  fileName: string,
137
+ cache?: RuleDiscoveryCache | null,
133
138
  ): Promise<{ seeded: number; skipped: number; updated: number }> {
134
139
  const seeds = readExampleJson<GuardRuleSeed[]>(fileName)
135
140
  if (!Array.isArray(seeds)) {
@@ -185,16 +190,21 @@ async function seedGuardRules(
185
190
  }
186
191
  if (seeded > 0 || updated > 0) {
187
192
  await em.flush()
193
+ await invalidateBusinessRuleDiscoveryCache(cache, scope.tenantId, scope.organizationId)
188
194
  }
189
195
  return { seeded, skipped, updated }
190
196
  }
191
197
 
192
- export async function seedExampleWorkflows(em: EntityManager, scope: WorkflowSeedScope): Promise<void> {
198
+ export async function seedExampleWorkflows(
199
+ em: EntityManager,
200
+ scope: WorkflowSeedScope,
201
+ options: { cache?: RuleDiscoveryCache | null } = {},
202
+ ): Promise<void> {
193
203
  // workflows.checkout-demo and workflows.simple-approval are now code-defined
194
204
  // (see packages/core/src/modules/workflows/workflows.ts). Seeding DB rows for
195
205
  // them would shadow the code definitions in the merge layer, so they are no
196
206
  // longer seeded here. Existing tenants are migrated via Migration20260428102318.
197
- await seedGuardRules(em, scope, 'guard-rules-example.json')
207
+ await seedGuardRules(em, scope, 'guard-rules-example.json', options.cache)
198
208
  await seedWorkflowDefinition(em, scope, 'sales-pipeline-definition.json')
199
- await seedGuardRules(em, scope, 'order-approval-guard-rules.json')
209
+ await seedGuardRules(em, scope, 'order-approval-guard-rules.json', options.cache)
200
210
  }
@@ -1,10 +1,12 @@
1
1
  import type { ModuleSetupConfig } from '@open-mercato/shared/modules/setup'
2
+ import { resolveBusinessRuleDiscoveryCache } from '@open-mercato/core/modules/business_rules/lib/rule-engine'
2
3
  import { seedExampleWorkflows } from './lib/seeds'
3
4
 
4
5
  export const setup: ModuleSetupConfig = {
5
6
  seedDefaults: async (ctx) => {
6
7
  const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }
7
- await seedExampleWorkflows(ctx.em, scope)
8
+ const cache = resolveBusinessRuleDiscoveryCache(ctx.container.resolve.bind(ctx.container))
9
+ await seedExampleWorkflows(ctx.em, scope, { cache })
8
10
  },
9
11
 
10
12
  defaultRoleFeatures: {