@open-mercato/core 0.4.2-canary-d0a025141f → 0.4.2-canary-3efa759f5c
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.
- package/dist/generated/entities.ids.generated.js +0 -1
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +0 -2
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/business_rules/data/validators.js +0 -34
- package/dist/modules/business_rules/data/validators.js.map +2 -2
- package/dist/modules/business_rules/index.js +1 -21
- package/dist/modules/business_rules/index.js.map +2 -2
- package/dist/modules/business_rules/lib/rule-engine.js +1 -182
- package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
- package/dist/modules/sales/acl.js +0 -1
- package/dist/modules/sales/acl.js.map +2 -2
- package/dist/modules/sales/backend/sales/documents/[id]/page.js +0 -12
- package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +0 -62
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/lib/dictionaries.js +0 -3
- package/dist/modules/sales/lib/dictionaries.js.map +2 -2
- package/dist/modules/workflows/acl.js +0 -2
- package/dist/modules/workflows/acl.js.map +2 -2
- package/dist/modules/workflows/api/instances/route.js +6 -18
- package/dist/modules/workflows/api/instances/route.js.map +2 -2
- package/dist/modules/workflows/api/tasks/route.js +1 -6
- package/dist/modules/workflows/api/tasks/route.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/[id]/page.js +1 -9
- package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/[id]/page.meta.js +1 -1
- package/dist/modules/workflows/backend/definitions/[id]/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/create/page.js +15 -24
- package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/create/page.meta.js +1 -1
- package/dist/modules/workflows/backend/definitions/create/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +132 -150
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.meta.js +1 -1
- package/dist/modules/workflows/backend/definitions/visual-editor/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/events/[id]/page.js +1 -1
- package/dist/modules/workflows/backend/events/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/events/[id]/page.meta.js +2 -2
- package/dist/modules/workflows/backend/events/[id]/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/instances/[id]/page.meta.js +2 -2
- package/dist/modules/workflows/backend/instances/[id]/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/[id]/page.js +1 -1
- package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/[id]/page.meta.js +2 -2
- package/dist/modules/workflows/backend/tasks/[id]/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/page.js +6 -5
- package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
- package/dist/modules/workflows/cli.js +3 -81
- package/dist/modules/workflows/cli.js.map +3 -3
- package/dist/modules/workflows/data/entities.js +1 -64
- package/dist/modules/workflows/data/entities.js.map +2 -2
- package/dist/modules/workflows/data/validators.js +0 -115
- package/dist/modules/workflows/data/validators.js.map +2 -2
- package/dist/modules/workflows/examples/checkout-demo-definition.json +5 -1
- package/dist/modules/workflows/lib/activity-executor.js +13 -75
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/dist/modules/workflows/lib/graph-utils.js +2 -71
- package/dist/modules/workflows/lib/graph-utils.js.map +2 -2
- package/dist/modules/workflows/lib/seeds.js +5 -22
- package/dist/modules/workflows/lib/seeds.js.map +2 -2
- package/dist/modules/workflows/lib/start-validator.js +23 -33
- package/dist/modules/workflows/lib/start-validator.js.map +2 -2
- package/dist/modules/workflows/lib/transition-handler.js +45 -157
- package/dist/modules/workflows/lib/transition-handler.js.map +3 -3
- package/generated/entities.ids.generated.ts +0 -1
- package/generated/entity-fields-registry.ts +0 -2
- package/package.json +2 -2
- package/src/modules/business_rules/data/validators.ts +0 -40
- package/src/modules/business_rules/index.ts +0 -25
- package/src/modules/business_rules/lib/rule-engine.ts +1 -281
- package/src/modules/sales/acl.ts +0 -1
- package/src/modules/sales/backend/sales/documents/[id]/page.tsx +0 -16
- package/src/modules/sales/commands/documents.ts +1 -74
- package/src/modules/sales/lib/dictionaries.ts +0 -3
- package/src/modules/workflows/acl.ts +0 -2
- package/src/modules/workflows/api/__tests__/instances.route.test.ts +2 -5
- package/src/modules/workflows/api/instances/route.ts +7 -21
- package/src/modules/workflows/api/tasks/route.ts +1 -7
- package/src/modules/workflows/backend/definitions/[id]/page.meta.ts +1 -1
- package/src/modules/workflows/backend/definitions/[id]/page.tsx +0 -9
- package/src/modules/workflows/backend/definitions/create/page.meta.ts +1 -1
- package/src/modules/workflows/backend/definitions/create/page.tsx +0 -9
- package/src/modules/workflows/backend/definitions/visual-editor/page.meta.ts +1 -1
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +3 -21
- package/src/modules/workflows/backend/events/[id]/page.meta.ts +2 -2
- package/src/modules/workflows/backend/events/[id]/page.tsx +1 -1
- package/src/modules/workflows/backend/instances/[id]/page.meta.ts +2 -2
- package/src/modules/workflows/backend/tasks/[id]/page.meta.ts +2 -2
- package/src/modules/workflows/backend/tasks/[id]/page.tsx +1 -1
- package/src/modules/workflows/backend/tasks/page.tsx +6 -5
- package/src/modules/workflows/cli.ts +0 -111
- package/src/modules/workflows/data/entities.ts +0 -124
- package/src/modules/workflows/data/validators.ts +0 -138
- package/src/modules/workflows/examples/checkout-demo-definition.json +5 -1
- package/src/modules/workflows/i18n/en.json +0 -71
- package/src/modules/workflows/lib/__tests__/activity-executor.test.ts +36 -43
- package/src/modules/workflows/lib/__tests__/transition-handler.test.ts +90 -170
- package/src/modules/workflows/lib/activity-executor.ts +16 -129
- package/src/modules/workflows/lib/graph-utils.ts +2 -117
- package/src/modules/workflows/lib/seeds.ts +8 -34
- package/src/modules/workflows/lib/start-validator.ts +28 -38
- package/src/modules/workflows/lib/transition-handler.ts +55 -208
- package/dist/generated/entities/workflow_event_trigger/index.js +0 -33
- package/dist/generated/entities/workflow_event_trigger/index.js.map +0 -7
- package/dist/modules/auth/events.js +0 -30
- package/dist/modules/auth/events.js.map +0 -7
- package/dist/modules/business_rules/api/execute/[ruleId]/route.js +0 -145
- package/dist/modules/business_rules/api/execute/[ruleId]/route.js.map +0 -7
- package/dist/modules/catalog/events.js +0 -34
- package/dist/modules/catalog/events.js.map +0 -7
- package/dist/modules/customers/events.js +0 -49
- package/dist/modules/customers/events.js.map +0 -7
- package/dist/modules/directory/events.js +0 -23
- package/dist/modules/directory/events.js.map +0 -7
- package/dist/modules/sales/events.js +0 -63
- package/dist/modules/sales/events.js.map +0 -7
- package/dist/modules/sales/lib/frontend/documentDataEvents.js +0 -25
- package/dist/modules/sales/lib/frontend/documentDataEvents.js.map +0 -7
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js +0 -481
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +0 -7
- package/dist/modules/workflows/components/EventTriggersEditor.js +0 -553
- package/dist/modules/workflows/components/EventTriggersEditor.js.map +0 -7
- package/dist/modules/workflows/events.js +0 -38
- package/dist/modules/workflows/events.js.map +0 -7
- package/dist/modules/workflows/examples/order-approval-definition.json +0 -257
- package/dist/modules/workflows/examples/order-approval-guard-rules.json +0 -32
- package/dist/modules/workflows/lib/event-trigger-service.js +0 -308
- package/dist/modules/workflows/lib/event-trigger-service.js.map +0 -7
- package/dist/modules/workflows/migrations/Migration20260123143500.js +0 -36
- package/dist/modules/workflows/migrations/Migration20260123143500.js.map +0 -7
- package/dist/modules/workflows/subscribers/event-trigger.js +0 -78
- package/dist/modules/workflows/subscribers/event-trigger.js.map +0 -7
- package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js +0 -323
- package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js.map +0 -7
- package/dist/modules/workflows/widgets/injection/order-approval/widget.js +0 -17
- package/dist/modules/workflows/widgets/injection/order-approval/widget.js.map +0 -7
- package/dist/modules/workflows/widgets/injection-table.js +0 -19
- package/dist/modules/workflows/widgets/injection-table.js.map +0 -7
- package/generated/entities/workflow_event_trigger/index.ts +0 -15
- package/src/modules/auth/events.ts +0 -39
- package/src/modules/business_rules/api/execute/[ruleId]/route.ts +0 -163
- package/src/modules/catalog/events.ts +0 -45
- package/src/modules/customers/events.ts +0 -63
- package/src/modules/directory/events.ts +0 -31
- package/src/modules/sales/events.ts +0 -82
- package/src/modules/sales/lib/frontend/documentDataEvents.ts +0 -28
- package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +0 -581
- package/src/modules/workflows/components/EventTriggersEditor.tsx +0 -664
- package/src/modules/workflows/events.ts +0 -49
- package/src/modules/workflows/examples/order-approval-definition.json +0 -257
- package/src/modules/workflows/examples/order-approval-guard-rules.json +0 -32
- package/src/modules/workflows/lib/event-trigger-service.ts +0 -557
- package/src/modules/workflows/migrations/Migration20260123143500.ts +0 -38
- package/src/modules/workflows/subscribers/event-trigger.ts +0 -109
- package/src/modules/workflows/widgets/injection/order-approval/widget.client.tsx +0 -446
- package/src/modules/workflows/widgets/injection/order-approval/widget.ts +0 -16
- package/src/modules/workflows/widgets/injection-table.ts +0 -21
|
@@ -462,35 +462,15 @@ describe('Transition Handler (Unit Tests)', () => {
|
|
|
462
462
|
})
|
|
463
463
|
|
|
464
464
|
test('should reject transition if pre-conditions fail', async () => {
|
|
465
|
-
// Create a definition with preConditions
|
|
466
|
-
const definitionWithPreConditions = {
|
|
467
|
-
...mockDefinition,
|
|
468
|
-
definition: {
|
|
469
|
-
...mockDefinition.definition,
|
|
470
|
-
transitions: [
|
|
471
|
-
{ fromStepId: 'start', toStepId: 'step-1' },
|
|
472
|
-
{
|
|
473
|
-
fromStepId: 'step-1',
|
|
474
|
-
toStepId: 'step-2',
|
|
475
|
-
preConditions: [{ ruleId: 'test-guard-rule', required: true }],
|
|
476
|
-
},
|
|
477
|
-
{ fromStepId: 'step-2', toStepId: 'end' },
|
|
478
|
-
],
|
|
479
|
-
},
|
|
480
|
-
}
|
|
481
|
-
|
|
482
465
|
mockEm.findOne
|
|
483
|
-
.mockResolvedValueOnce(
|
|
484
|
-
.mockResolvedValueOnce(
|
|
485
|
-
|
|
486
|
-
;(ruleEngine.
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
actionsExecuted: null,
|
|
492
|
-
executionTime: 10,
|
|
493
|
-
error: undefined,
|
|
466
|
+
.mockResolvedValueOnce(mockDefinition) // evaluateTransition
|
|
467
|
+
.mockResolvedValueOnce(mockDefinition) // evaluatePreConditions
|
|
468
|
+
|
|
469
|
+
;(ruleEngine.executeRules as jest.Mock).mockResolvedValueOnce({
|
|
470
|
+
allowed: false,
|
|
471
|
+
executedRules: [],
|
|
472
|
+
totalExecutionTime: 10,
|
|
473
|
+
errors: ['Pre-condition rule failed'],
|
|
494
474
|
})
|
|
495
475
|
|
|
496
476
|
mockEm.create.mockReturnValue({} as any)
|
|
@@ -511,35 +491,15 @@ describe('Transition Handler (Unit Tests)', () => {
|
|
|
511
491
|
})
|
|
512
492
|
|
|
513
493
|
test('should log transition rejection event', async () => {
|
|
514
|
-
// Create a definition with preConditions
|
|
515
|
-
const definitionWithPreConditions = {
|
|
516
|
-
...mockDefinition,
|
|
517
|
-
definition: {
|
|
518
|
-
...mockDefinition.definition,
|
|
519
|
-
transitions: [
|
|
520
|
-
{ fromStepId: 'start', toStepId: 'step-1' },
|
|
521
|
-
{
|
|
522
|
-
fromStepId: 'step-1',
|
|
523
|
-
toStepId: 'step-2',
|
|
524
|
-
preConditions: [{ ruleId: 'test-guard-rule', required: true }],
|
|
525
|
-
},
|
|
526
|
-
{ fromStepId: 'step-2', toStepId: 'end' },
|
|
527
|
-
],
|
|
528
|
-
},
|
|
529
|
-
}
|
|
530
|
-
|
|
531
494
|
mockEm.findOne
|
|
532
|
-
.mockResolvedValueOnce(
|
|
533
|
-
.mockResolvedValueOnce(
|
|
534
|
-
|
|
535
|
-
;(ruleEngine.
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
actionsExecuted: null,
|
|
541
|
-
executionTime: 10,
|
|
542
|
-
error: undefined,
|
|
495
|
+
.mockResolvedValueOnce(mockDefinition)
|
|
496
|
+
.mockResolvedValueOnce(mockDefinition)
|
|
497
|
+
|
|
498
|
+
;(ruleEngine.executeRules as jest.Mock).mockResolvedValueOnce({
|
|
499
|
+
allowed: false,
|
|
500
|
+
executedRules: [],
|
|
501
|
+
totalExecutionTime: 10,
|
|
502
|
+
errors: ['Rule XYZ failed'],
|
|
543
503
|
})
|
|
544
504
|
|
|
545
505
|
mockEm.create.mockReturnValue({} as any)
|
|
@@ -567,36 +527,27 @@ describe('Transition Handler (Unit Tests)', () => {
|
|
|
567
527
|
})
|
|
568
528
|
|
|
569
529
|
test('should execute transition even if post-conditions fail (warning only)', async () => {
|
|
570
|
-
// Create a definition with postConditions
|
|
571
|
-
const definitionWithPostConditions = {
|
|
572
|
-
...mockDefinition,
|
|
573
|
-
definition: {
|
|
574
|
-
...mockDefinition.definition,
|
|
575
|
-
transitions: [
|
|
576
|
-
{ fromStepId: 'start', toStepId: 'step-1' },
|
|
577
|
-
{
|
|
578
|
-
fromStepId: 'step-1',
|
|
579
|
-
toStepId: 'step-2',
|
|
580
|
-
postConditions: [{ ruleId: 'test-post-rule', required: true }],
|
|
581
|
-
},
|
|
582
|
-
{ fromStepId: 'step-2', toStepId: 'end' },
|
|
583
|
-
],
|
|
584
|
-
},
|
|
585
|
-
}
|
|
586
|
-
|
|
587
530
|
mockEm.findOne.mockReset()
|
|
588
|
-
mockEm.findOne
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
531
|
+
mockEm.findOne
|
|
532
|
+
.mockResolvedValueOnce(mockDefinition)
|
|
533
|
+
.mockResolvedValueOnce(mockDefinition)
|
|
534
|
+
.mockResolvedValueOnce(mockDefinition)
|
|
535
|
+
.mockResolvedValueOnce(mockDefinition)
|
|
536
|
+
.mockResolvedValueOnce(mockDefinition)
|
|
537
|
+
.mockResolvedValue(mockDefinition)
|
|
538
|
+
|
|
539
|
+
;(ruleEngine.executeRules as jest.Mock)
|
|
540
|
+
.mockResolvedValueOnce({
|
|
541
|
+
allowed: true,
|
|
542
|
+
executedRules: [],
|
|
543
|
+
totalExecutionTime: 10,
|
|
544
|
+
})
|
|
545
|
+
.mockResolvedValueOnce({
|
|
546
|
+
allowed: false, // Post-condition failed
|
|
547
|
+
executedRules: [],
|
|
548
|
+
totalExecutionTime: 5,
|
|
549
|
+
errors: ['Post-condition rule failed'],
|
|
550
|
+
})
|
|
600
551
|
|
|
601
552
|
mockEm.create.mockReturnValue({} as any)
|
|
602
553
|
|
|
@@ -758,33 +709,22 @@ describe('Transition Handler (Unit Tests)', () => {
|
|
|
758
709
|
|
|
759
710
|
describe('Business Rules Integration', () => {
|
|
760
711
|
test('should call rule engine for pre-conditions with correct entityType', async () => {
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
...mockDefinition.definition,
|
|
766
|
-
transitions: [
|
|
767
|
-
{ fromStepId: 'start', toStepId: 'step-1' },
|
|
768
|
-
{
|
|
769
|
-
fromStepId: 'step-1',
|
|
770
|
-
toStepId: 'step-2',
|
|
771
|
-
preConditions: [{ ruleId: 'test-guard-rule', required: true }],
|
|
772
|
-
},
|
|
773
|
-
{ fromStepId: 'step-2', toStepId: 'end' },
|
|
774
|
-
],
|
|
775
|
-
},
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
mockEm.findOne.mockResolvedValue(definitionWithPreConditions)
|
|
712
|
+
mockEm.findOne
|
|
713
|
+
.mockResolvedValueOnce(mockDefinition)
|
|
714
|
+
.mockResolvedValueOnce(mockDefinition)
|
|
715
|
+
.mockResolvedValueOnce(mockDefinition)
|
|
779
716
|
|
|
780
|
-
;(ruleEngine.
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
717
|
+
;(ruleEngine.executeRules as jest.Mock)
|
|
718
|
+
.mockResolvedValueOnce({
|
|
719
|
+
allowed: true,
|
|
720
|
+
executedRules: [],
|
|
721
|
+
totalExecutionTime: 10,
|
|
722
|
+
})
|
|
723
|
+
.mockResolvedValueOnce({
|
|
724
|
+
allowed: true,
|
|
725
|
+
executedRules: [],
|
|
726
|
+
totalExecutionTime: 5,
|
|
727
|
+
})
|
|
788
728
|
|
|
789
729
|
mockEm.create.mockReturnValue({} as any)
|
|
790
730
|
|
|
@@ -797,10 +737,9 @@ describe('Transition Handler (Unit Tests)', () => {
|
|
|
797
737
|
{ workflowContext: {} }
|
|
798
738
|
)
|
|
799
739
|
|
|
800
|
-
expect(ruleEngine.
|
|
740
|
+
expect(ruleEngine.executeRules).toHaveBeenCalledWith(
|
|
801
741
|
mockEm,
|
|
802
742
|
expect.objectContaining({
|
|
803
|
-
ruleId: 'test-guard-rule',
|
|
804
743
|
entityType: 'workflow:simple-approval:transition',
|
|
805
744
|
eventType: 'pre_transition',
|
|
806
745
|
data: expect.objectContaining({
|
|
@@ -812,34 +751,26 @@ describe('Transition Handler (Unit Tests)', () => {
|
|
|
812
751
|
})
|
|
813
752
|
|
|
814
753
|
test('should call rule engine for post-conditions with correct eventType', async () => {
|
|
815
|
-
// Create a definition with postConditions
|
|
816
|
-
const definitionWithPostConditions = {
|
|
817
|
-
...mockDefinition,
|
|
818
|
-
definition: {
|
|
819
|
-
...mockDefinition.definition,
|
|
820
|
-
transitions: [
|
|
821
|
-
{ fromStepId: 'start', toStepId: 'step-1' },
|
|
822
|
-
{
|
|
823
|
-
fromStepId: 'step-1',
|
|
824
|
-
toStepId: 'step-2',
|
|
825
|
-
postConditions: [{ ruleId: 'test-post-rule', required: true }],
|
|
826
|
-
},
|
|
827
|
-
{ fromStepId: 'step-2', toStepId: 'end' },
|
|
828
|
-
],
|
|
829
|
-
},
|
|
830
|
-
}
|
|
831
|
-
|
|
832
754
|
mockEm.findOne.mockReset()
|
|
833
|
-
mockEm.findOne
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
755
|
+
mockEm.findOne
|
|
756
|
+
.mockResolvedValueOnce(mockDefinition)
|
|
757
|
+
.mockResolvedValueOnce(mockDefinition)
|
|
758
|
+
.mockResolvedValueOnce(mockDefinition)
|
|
759
|
+
.mockResolvedValueOnce(mockDefinition)
|
|
760
|
+
.mockResolvedValueOnce(mockDefinition)
|
|
761
|
+
.mockResolvedValue(mockDefinition)
|
|
762
|
+
|
|
763
|
+
;(ruleEngine.executeRules as jest.Mock)
|
|
764
|
+
.mockResolvedValueOnce({
|
|
765
|
+
allowed: true,
|
|
766
|
+
executedRules: [],
|
|
767
|
+
totalExecutionTime: 10,
|
|
768
|
+
})
|
|
769
|
+
.mockResolvedValueOnce({
|
|
770
|
+
allowed: true,
|
|
771
|
+
executedRules: [],
|
|
772
|
+
totalExecutionTime: 5,
|
|
773
|
+
})
|
|
843
774
|
|
|
844
775
|
mockEm.create.mockReturnValue({} as any)
|
|
845
776
|
|
|
@@ -852,11 +783,11 @@ describe('Transition Handler (Unit Tests)', () => {
|
|
|
852
783
|
{ workflowContext: {} }
|
|
853
784
|
)
|
|
854
785
|
|
|
855
|
-
// Check call to
|
|
856
|
-
expect(ruleEngine.
|
|
786
|
+
// Check second call to executeRules (post-conditions)
|
|
787
|
+
expect(ruleEngine.executeRules).toHaveBeenNthCalledWith(
|
|
788
|
+
2,
|
|
857
789
|
mockEm,
|
|
858
790
|
expect.objectContaining({
|
|
859
|
-
ruleId: 'test-post-rule',
|
|
860
791
|
entityType: 'workflow:simple-approval:transition',
|
|
861
792
|
eventType: 'post_transition',
|
|
862
793
|
})
|
|
@@ -864,33 +795,22 @@ describe('Transition Handler (Unit Tests)', () => {
|
|
|
864
795
|
})
|
|
865
796
|
|
|
866
797
|
test('should pass workflow context and trigger data to rule engine', async () => {
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
...mockDefinition.definition,
|
|
872
|
-
transitions: [
|
|
873
|
-
{ fromStepId: 'start', toStepId: 'step-1' },
|
|
874
|
-
{
|
|
875
|
-
fromStepId: 'step-1',
|
|
876
|
-
toStepId: 'step-2',
|
|
877
|
-
preConditions: [{ ruleId: 'test-guard-rule', required: true }],
|
|
878
|
-
},
|
|
879
|
-
{ fromStepId: 'step-2', toStepId: 'end' },
|
|
880
|
-
],
|
|
881
|
-
},
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
mockEm.findOne.mockResolvedValue(definitionWithPreConditions)
|
|
798
|
+
mockEm.findOne
|
|
799
|
+
.mockResolvedValueOnce(mockDefinition)
|
|
800
|
+
.mockResolvedValueOnce(mockDefinition)
|
|
801
|
+
.mockResolvedValueOnce(mockDefinition)
|
|
885
802
|
|
|
886
|
-
;(ruleEngine.
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
803
|
+
;(ruleEngine.executeRules as jest.Mock)
|
|
804
|
+
.mockResolvedValueOnce({
|
|
805
|
+
allowed: true,
|
|
806
|
+
executedRules: [],
|
|
807
|
+
totalExecutionTime: 10,
|
|
808
|
+
})
|
|
809
|
+
.mockResolvedValueOnce({
|
|
810
|
+
allowed: true,
|
|
811
|
+
executedRules: [],
|
|
812
|
+
totalExecutionTime: 5,
|
|
813
|
+
})
|
|
894
814
|
|
|
895
815
|
mockEm.create.mockReturnValue({} as any)
|
|
896
816
|
|
|
@@ -910,7 +830,7 @@ describe('Transition Handler (Unit Tests)', () => {
|
|
|
910
830
|
}
|
|
911
831
|
)
|
|
912
832
|
|
|
913
|
-
expect(ruleEngine.
|
|
833
|
+
expect(ruleEngine.executeRules).toHaveBeenCalledWith(
|
|
914
834
|
mockEm,
|
|
915
835
|
expect.objectContaining({
|
|
916
836
|
data: expect.objectContaining({
|
|
@@ -448,30 +448,7 @@ export async function executeEmitEvent(
|
|
|
448
448
|
/**
|
|
449
449
|
* UPDATE_ENTITY activity handler
|
|
450
450
|
*
|
|
451
|
-
* Updates an entity via
|
|
452
|
-
*
|
|
453
|
-
* Config format:
|
|
454
|
-
* ```json
|
|
455
|
-
* {
|
|
456
|
-
* "commandId": "sales.documents.update",
|
|
457
|
-
* "input": {
|
|
458
|
-
* "id": "{{context.orderId}}",
|
|
459
|
-
* "statusEntryId": "{{context.approvedStatusId}}"
|
|
460
|
-
* }
|
|
461
|
-
* }
|
|
462
|
-
* ```
|
|
463
|
-
*
|
|
464
|
-
* Alternative format with statusValue (auto-resolves to statusEntryId):
|
|
465
|
-
* ```json
|
|
466
|
-
* {
|
|
467
|
-
* "commandId": "sales.orders.update",
|
|
468
|
-
* "statusDictionary": "sales.order_status",
|
|
469
|
-
* "input": {
|
|
470
|
-
* "id": "{{context.id}}",
|
|
471
|
-
* "statusValue": "pending_approval"
|
|
472
|
-
* }
|
|
473
|
-
* }
|
|
474
|
-
* ```
|
|
451
|
+
* Updates an entity via query engine
|
|
475
452
|
*/
|
|
476
453
|
export async function executeUpdateEntity(
|
|
477
454
|
em: EntityManager,
|
|
@@ -479,119 +456,29 @@ export async function executeUpdateEntity(
|
|
|
479
456
|
context: ActivityContext,
|
|
480
457
|
container: AwilixContainer
|
|
481
458
|
): Promise<any> {
|
|
482
|
-
const {
|
|
483
|
-
|
|
484
|
-
if (!commandId) {
|
|
485
|
-
throw new Error('UPDATE_ENTITY requires "commandId" field (e.g., "sales.documents.update")')
|
|
486
|
-
}
|
|
459
|
+
const { entityType, entityId, updates } = config
|
|
487
460
|
|
|
488
|
-
if (!
|
|
489
|
-
throw new Error('UPDATE_ENTITY requires "
|
|
461
|
+
if (!entityType || !entityId || !updates) {
|
|
462
|
+
throw new Error('UPDATE_ENTITY requires "entityType", "entityId", and "updates" fields')
|
|
490
463
|
}
|
|
491
464
|
|
|
492
|
-
//
|
|
493
|
-
const
|
|
494
|
-
|
|
495
|
-
if (!commandBus || typeof commandBus.execute !== 'function') {
|
|
496
|
-
throw new Error('CommandBus not available in container')
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// Prepare final input, resolving statusValue if provided
|
|
500
|
-
let finalInput = { ...input }
|
|
501
|
-
|
|
502
|
-
// If statusValue is provided with a statusDictionary, resolve it to statusEntryId
|
|
503
|
-
if (finalInput.statusValue && statusDictionary) {
|
|
504
|
-
const statusEntryId = await resolveDictionaryEntryId(
|
|
505
|
-
em,
|
|
506
|
-
statusDictionary,
|
|
507
|
-
finalInput.statusValue,
|
|
508
|
-
context.workflowInstance.tenantId,
|
|
509
|
-
context.workflowInstance.organizationId
|
|
510
|
-
)
|
|
511
|
-
if (statusEntryId) {
|
|
512
|
-
finalInput.statusEntryId = statusEntryId
|
|
513
|
-
}
|
|
514
|
-
delete finalInput.statusValue
|
|
515
|
-
}
|
|
465
|
+
// Get query engine from container
|
|
466
|
+
const queryEngine = container.resolve('queryEngine')
|
|
516
467
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
const SYSTEM_USER_ID = '00000000-0000-0000-0000-000000000000'
|
|
520
|
-
const ctx = {
|
|
521
|
-
container,
|
|
522
|
-
auth: {
|
|
523
|
-
sub: context.userId || SYSTEM_USER_ID,
|
|
524
|
-
tenantId: context.workflowInstance.tenantId,
|
|
525
|
-
orgId: context.workflowInstance.organizationId,
|
|
526
|
-
isSuperAdmin: false,
|
|
527
|
-
},
|
|
528
|
-
organizationScope: null,
|
|
529
|
-
selectedOrganizationId: context.workflowInstance.organizationId,
|
|
530
|
-
organizationIds: context.workflowInstance.organizationId
|
|
531
|
-
? [context.workflowInstance.organizationId]
|
|
532
|
-
: null,
|
|
468
|
+
if (!queryEngine || typeof queryEngine.update !== 'function') {
|
|
469
|
+
throw new Error('Query engine not available in container')
|
|
533
470
|
}
|
|
534
471
|
|
|
535
|
-
// Execute
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
472
|
+
// Execute update with tenant scoping
|
|
473
|
+
await queryEngine.update({
|
|
474
|
+
entity: entityType,
|
|
475
|
+
where: { id: entityId },
|
|
476
|
+
data: updates,
|
|
477
|
+
tenantId: context.workflowInstance.tenantId,
|
|
478
|
+
organizationId: context.workflowInstance.organizationId,
|
|
539
479
|
})
|
|
540
480
|
|
|
541
|
-
return {
|
|
542
|
-
executed: true,
|
|
543
|
-
commandId,
|
|
544
|
-
result,
|
|
545
|
-
logEntryId: logEntry?.id,
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
/**
|
|
550
|
-
* Helper to resolve dictionary entry ID by value
|
|
551
|
-
*/
|
|
552
|
-
async function resolveDictionaryEntryId(
|
|
553
|
-
em: EntityManager,
|
|
554
|
-
dictionaryKey: string,
|
|
555
|
-
value: string,
|
|
556
|
-
tenantId: string,
|
|
557
|
-
organizationId: string
|
|
558
|
-
): Promise<string | null> {
|
|
559
|
-
try {
|
|
560
|
-
// Import here to avoid circular dependencies
|
|
561
|
-
const { Dictionary, DictionaryEntry } = await import('@open-mercato/core/modules/dictionaries/data/entities')
|
|
562
|
-
|
|
563
|
-
// Find the dictionary
|
|
564
|
-
const dictionary = await em.findOne(Dictionary, {
|
|
565
|
-
key: dictionaryKey,
|
|
566
|
-
tenantId,
|
|
567
|
-
organizationId,
|
|
568
|
-
deletedAt: null,
|
|
569
|
-
})
|
|
570
|
-
|
|
571
|
-
if (!dictionary) {
|
|
572
|
-
console.warn(`[UPDATE_ENTITY] Dictionary not found: ${dictionaryKey}`)
|
|
573
|
-
return null
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
// Find the entry by normalized value
|
|
577
|
-
const normalizedValue = value.toLowerCase().trim()
|
|
578
|
-
const entry = await em.findOne(DictionaryEntry, {
|
|
579
|
-
dictionary: dictionary.id,
|
|
580
|
-
tenantId,
|
|
581
|
-
organizationId,
|
|
582
|
-
normalizedValue,
|
|
583
|
-
})
|
|
584
|
-
|
|
585
|
-
if (!entry) {
|
|
586
|
-
console.warn(`[UPDATE_ENTITY] Dictionary entry not found: ${dictionaryKey}/${value}`)
|
|
587
|
-
return null
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
return entry.id
|
|
591
|
-
} catch (error) {
|
|
592
|
-
console.error(`[UPDATE_ENTITY] Error resolving dictionary entry:`, error)
|
|
593
|
-
return null
|
|
594
|
-
}
|
|
481
|
+
return { updated: true, entityType, entityId, updates }
|
|
595
482
|
}
|
|
596
483
|
|
|
597
484
|
/**
|
|
@@ -205,20 +205,12 @@ export function definitionToGraph(
|
|
|
205
205
|
definition: WorkflowDefinition['definition'],
|
|
206
206
|
options: DefinitionToGraphOptions = {}
|
|
207
207
|
): { nodes: Node[]; edges: Edge[] } {
|
|
208
|
-
const { autoLayout = true, layoutSpacing = { vertical:
|
|
209
|
-
|
|
210
|
-
// Build step map for quick lookup
|
|
211
|
-
const stepMap = new Map(definition.steps.map(step => [step.stepId, step]))
|
|
212
|
-
|
|
213
|
-
// Calculate smart layout positions if autoLayout is enabled
|
|
214
|
-
const positions = autoLayout
|
|
215
|
-
? calculateSmartLayout(definition.steps, definition.transitions, layoutSpacing)
|
|
216
|
-
: null
|
|
208
|
+
const { autoLayout = true, layoutSpacing = { vertical: 250, horizontal: 100 } } = options
|
|
217
209
|
|
|
218
210
|
// Convert steps to nodes
|
|
219
211
|
const nodes: Node[] = definition.steps.map((step, index) => {
|
|
220
212
|
// Determine position
|
|
221
|
-
let position =
|
|
213
|
+
let position = { x: 250, y: 50 + index * layoutSpacing.vertical }
|
|
222
214
|
|
|
223
215
|
// Use stored position if available and not auto-layouting
|
|
224
216
|
if (!autoLayout && (step as any)._editorPosition) {
|
|
@@ -334,113 +326,6 @@ export function definitionToGraph(
|
|
|
334
326
|
return { nodes, edges }
|
|
335
327
|
}
|
|
336
328
|
|
|
337
|
-
/**
|
|
338
|
-
* Calculate smart layout positions for workflow nodes
|
|
339
|
-
* Uses a layered/hierarchical layout algorithm that:
|
|
340
|
-
* 1. Assigns levels (ranks) to nodes based on graph topology
|
|
341
|
-
* 2. Spreads sibling nodes horizontally at the same level
|
|
342
|
-
* 3. Centers merge points below their incoming nodes
|
|
343
|
-
*/
|
|
344
|
-
function calculateSmartLayout(
|
|
345
|
-
steps: any[],
|
|
346
|
-
transitions: any[],
|
|
347
|
-
spacing: { vertical: number; horizontal: number }
|
|
348
|
-
): Map<string, { x: number; y: number }> {
|
|
349
|
-
const positions = new Map<string, { x: number; y: number }>()
|
|
350
|
-
|
|
351
|
-
if (steps.length === 0) return positions
|
|
352
|
-
|
|
353
|
-
// Build adjacency lists
|
|
354
|
-
const outgoing = new Map<string, string[]>() // node -> children
|
|
355
|
-
const incoming = new Map<string, string[]>() // node -> parents
|
|
356
|
-
|
|
357
|
-
for (const step of steps) {
|
|
358
|
-
outgoing.set(step.stepId, [])
|
|
359
|
-
incoming.set(step.stepId, [])
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
for (const t of transitions) {
|
|
363
|
-
const children = outgoing.get(t.fromStepId) || []
|
|
364
|
-
children.push(t.toStepId)
|
|
365
|
-
outgoing.set(t.fromStepId, children)
|
|
366
|
-
|
|
367
|
-
const parents = incoming.get(t.toStepId) || []
|
|
368
|
-
parents.push(t.fromStepId)
|
|
369
|
-
incoming.set(t.toStepId, parents)
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// Find start node(s) - nodes with no incoming edges
|
|
373
|
-
const startNodes = steps.filter(s => (incoming.get(s.stepId) || []).length === 0)
|
|
374
|
-
if (startNodes.length === 0) {
|
|
375
|
-
// Fallback: use first step as start
|
|
376
|
-
startNodes.push(steps[0])
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// Assign levels using BFS (longest path from start)
|
|
380
|
-
const levels = new Map<string, number>()
|
|
381
|
-
const queue: Array<{ id: string; level: number }> = []
|
|
382
|
-
|
|
383
|
-
for (const start of startNodes) {
|
|
384
|
-
queue.push({ id: start.stepId, level: 0 })
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
while (queue.length > 0) {
|
|
388
|
-
const { id, level } = queue.shift()!
|
|
389
|
-
const currentLevel = levels.get(id)
|
|
390
|
-
|
|
391
|
-
// Take the maximum level (longest path)
|
|
392
|
-
if (currentLevel === undefined || level > currentLevel) {
|
|
393
|
-
levels.set(id, level)
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
const children = outgoing.get(id) || []
|
|
397
|
-
for (const child of children) {
|
|
398
|
-
queue.push({ id: child, level: level + 1 })
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// Group nodes by level
|
|
403
|
-
const nodesByLevel = new Map<number, string[]>()
|
|
404
|
-
for (const [nodeId, level] of levels) {
|
|
405
|
-
const nodesAtLevel = nodesByLevel.get(level) || []
|
|
406
|
-
nodesAtLevel.push(nodeId)
|
|
407
|
-
nodesByLevel.set(level, nodesAtLevel)
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// Calculate positions
|
|
411
|
-
const centerX = 400 // Center line for the graph
|
|
412
|
-
const startY = 50
|
|
413
|
-
|
|
414
|
-
for (const [level, nodeIds] of nodesByLevel) {
|
|
415
|
-
const count = nodeIds.length
|
|
416
|
-
const y = startY + level * spacing.vertical
|
|
417
|
-
|
|
418
|
-
if (count === 1) {
|
|
419
|
-
// Single node at this level - center it
|
|
420
|
-
positions.set(nodeIds[0], { x: centerX, y })
|
|
421
|
-
} else {
|
|
422
|
-
// Multiple nodes at this level - spread them horizontally
|
|
423
|
-
const totalWidth = (count - 1) * spacing.horizontal
|
|
424
|
-
const startX = centerX - totalWidth / 2
|
|
425
|
-
|
|
426
|
-
// Sort nodes by their parent's position for consistent ordering
|
|
427
|
-
nodeIds.sort((a, b) => {
|
|
428
|
-
const parentsA = incoming.get(a) || []
|
|
429
|
-
const parentsB = incoming.get(b) || []
|
|
430
|
-
const parentPosA = parentsA.length > 0 ? (positions.get(parentsA[0])?.x || 0) : 0
|
|
431
|
-
const parentPosB = parentsB.length > 0 ? (positions.get(parentsB[0])?.x || 0) : 0
|
|
432
|
-
return parentPosA - parentPosB
|
|
433
|
-
})
|
|
434
|
-
|
|
435
|
-
nodeIds.forEach((nodeId, idx) => {
|
|
436
|
-
positions.set(nodeId, { x: startX + idx * spacing.horizontal, y })
|
|
437
|
-
})
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
return positions
|
|
442
|
-
}
|
|
443
|
-
|
|
444
329
|
/**
|
|
445
330
|
* Map node type to step type (for graph → definition)
|
|
446
331
|
*/
|