rez_core 6.5.58 → 6.5.59

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 (27) hide show
  1. package/dist/module/listmaster/service/list-master.service.d.ts +14 -1
  2. package/dist/module/listmaster/service/list-master.service.js +216 -36
  3. package/dist/module/listmaster/service/list-master.service.js.map +1 -1
  4. package/dist/module/meta/controller/entity-dynamic.controller.js +6 -3
  5. package/dist/module/meta/controller/entity-dynamic.controller.js.map +1 -1
  6. package/dist/module/meta/controller/entity.controller.js +1 -1
  7. package/dist/module/meta/controller/entity.controller.js.map +1 -1
  8. package/dist/module/meta/entity/attribute-master.entity.d.ts +4 -0
  9. package/dist/module/meta/entity/attribute-master.entity.js +16 -0
  10. package/dist/module/meta/entity/attribute-master.entity.js.map +1 -1
  11. package/dist/module/meta/service/entity-dynamic.service.d.ts +9 -12
  12. package/dist/module/meta/service/entity-dynamic.service.js +20 -22
  13. package/dist/module/meta/service/entity-dynamic.service.js.map +1 -1
  14. package/dist/module/meta/service/entity-service-impl.service.d.ts +1 -1
  15. package/dist/module/meta/service/entity-service-impl.service.js +1 -1
  16. package/dist/module/meta/service/entity-service-impl.service.js.map +1 -1
  17. package/dist/module/workflow/service/task.service.js +1 -1
  18. package/dist/module/workflow/service/task.service.js.map +1 -1
  19. package/dist/tsconfig.build.tsbuildinfo +1 -1
  20. package/package.json +1 -1
  21. package/src/module/listmaster/service/list-master.service.ts +436 -66
  22. package/src/module/meta/controller/entity-dynamic.controller.ts +6 -8
  23. package/src/module/meta/controller/entity.controller.ts +0 -1
  24. package/src/module/meta/entity/attribute-master.entity.ts +13 -0
  25. package/src/module/meta/service/entity-dynamic.service.ts +23 -26
  26. package/src/module/meta/service/entity-service-impl.service.ts +0 -1
  27. package/src/module/workflow/service/task.service.ts +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rez_core",
3
- "version": "6.5.58",
3
+ "version": "6.5.59",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "private": false,
@@ -422,91 +422,373 @@ export class ListMasterService {
422
422
  entity_type: string,
423
423
  attribute_key: string,
424
424
  loggedInUser: UserData,
425
- body: Record<string, string>,
425
+ body: Record<string, any>,
426
426
  ) {
427
- let entityMaster = await this.entityMasterService.getEntityData(
428
- entity_type,
429
- loggedInUser,
430
- );
431
- let appCode = entityMaster.appcode;
427
+ try {
428
+ // Extract inactiveIds from body
429
+ const { inactiveIds, ...params } = body;
430
+ const inactiveIdsArray = inactiveIds
431
+ ? inactiveIds.split(',').map((id) => parseInt(id, 10))
432
+ : [];
433
+
434
+ // Fetch Attribute Master to get data source configuration
435
+ const attributeMaster =
436
+ await this.attributeMasterService.findByMappedEntityTypeAndAttributeKey(
437
+ entity_type,
438
+ attribute_key,
439
+ loggedInUser,
440
+ );
432
441
 
433
- const { inactiveIds, ...params } = body;
442
+ if (!attributeMaster) {
443
+ throw new BadRequestException(
444
+ `Attribute '${attribute_key}' not found for entity '${entity_type}'`,
445
+ );
446
+ }
434
447
 
435
- const inactiveIdsArray = inactiveIds
436
- ? inactiveIds.split(',').map((id) => parseInt(id, 10))
437
- : [];
448
+ // Extract data source configuration from AttributeMaster
449
+ const {
450
+ ds_category, // ENT_LIST | LM | CAPI | CK | EXTAPI
451
+ ds_entitytype, // Entity type or list code
452
+ ds_filtercode, // Additional filter code
453
+ ds_appcode, // Target app code
454
+ } = attributeMaster;
438
455
 
439
- const currentAppCode = this.configService.get<string>('appcode');
456
+ if (!ds_category) {
457
+ throw new BadRequestException(
458
+ `Data source category not configured for attribute '${attribute_key}'`,
459
+ );
460
+ }
440
461
 
441
- if (currentAppCode === appCode || !currentAppCode) {
442
- const entityAttribute =
443
- await this.attributeMasterService.findByMappedEntityTypeAndAttributeKey(
462
+ const currentAppCode = this.configService.get<string>('appcode');
463
+
464
+ // Check if request is for same app (SELF) or cross-app (CROSS)
465
+ const isSelfApp =
466
+ !ds_appcode ||
467
+ ds_appcode.toUpperCase() === 'SELF' ||
468
+ currentAppCode === ds_appcode;
469
+
470
+ if (isSelfApp) {
471
+ // SELF APP - Process locally
472
+ return this.processLocalDropdown(
473
+ ds_category,
474
+ ds_entitytype,
475
+ ds_filtercode,
476
+ params,
477
+ inactiveIdsArray,
478
+ loggedInUser,
479
+ );
480
+ } else {
481
+ // CROSS APP - Forward to target microservice
482
+ return this.processCrossAppDropdown(
483
+ ds_appcode,
444
484
  entity_type,
445
485
  attribute_key,
446
486
  loggedInUser,
487
+ body,
447
488
  );
489
+ }
490
+ } catch (error) {
491
+ console.error('❌ Error in getDropDownData:', error.message);
492
+ throw error;
493
+ }
494
+ }
448
495
 
449
- if (entityAttribute && entityAttribute.data_source_type) {
450
- const listMaster = await this.listMasterRepo.findByType(
451
- entityAttribute.datasource_list,
452
- loggedInUser?.enterprise_id,
496
+ /**
497
+ * Process dropdown data locally (SELF APP)
498
+ */
499
+ private async processLocalDropdown(
500
+ ds_category: string,
501
+ ds_entitytype: string,
502
+ ds_filtercode: string,
503
+ params: Record<string, any>,
504
+ inactiveIdsArray: number[],
505
+ loggedInUser: UserData,
506
+ ) {
507
+ // Route to appropriate handler based on data source category
508
+ switch (ds_category.toUpperCase()) {
509
+ case 'ENT_LIST':
510
+ case 'ENTITY':
511
+ // Merge ds_filtercode params with request params
512
+ const mergedParams = { ...params };
513
+ if (ds_filtercode) {
514
+ const filterParams = this.parseFilterCode(ds_filtercode);
515
+ Object.assign(mergedParams, filterParams);
516
+ }
517
+ return this.fetchFromEntity(
518
+ ds_entitytype,
519
+ mergedParams,
520
+ inactiveIdsArray,
521
+ loggedInUser,
453
522
  );
454
523
 
455
- if (!listMaster) {
456
- return;
457
- }
524
+ case 'LM':
525
+ case 'LIST_MASTER':
526
+ return this.fetchFromListMaster(
527
+ ds_entitytype,
528
+ ds_filtercode,
529
+ params,
530
+ inactiveIdsArray,
531
+ loggedInUser,
532
+ );
533
+
534
+ case 'CAPI':
535
+ case 'CUSTOM_API':
536
+ return this.fetchFromCustomInternalService(
537
+ ds_entitytype,
538
+ ds_filtercode,
539
+ params,
540
+ loggedInUser,
541
+ );
542
+
543
+ case 'CK':
544
+ case 'CUSTOM_KEY':
545
+ return this.fetchFromCustomKey(
546
+ ds_entitytype,
547
+ ds_filtercode,
548
+ params,
549
+ loggedInUser,
550
+ );
551
+
552
+ case 'EXTAPI':
553
+ case 'EXTERNAL_API':
554
+ return this.fetchFromExternalAPI(
555
+ ds_entitytype,
556
+ ds_filtercode,
557
+ params,
558
+ loggedInUser,
559
+ );
560
+
561
+ default:
562
+ throw new BadRequestException(
563
+ `Unsupported data source category: ${ds_category}`,
564
+ );
565
+ }
566
+ }
567
+
568
+ /**
569
+ * Fetch dropdown from List Master
570
+ */
571
+ private async fetchFromListMaster(
572
+ lmCode: string,
573
+ filterCode: string,
574
+ params: Record<string, any>,
575
+ inactiveIdsArray: number[],
576
+ loggedInUser: UserData,
577
+ ) {
578
+ try {
579
+ // Get List Master configuration
580
+ const listMaster = await this.listMasterRepo.findByType(
581
+ lmCode,
582
+ loggedInUser.enterprise_id,
583
+ );
584
+
585
+ if (!listMaster) {
586
+ throw new BadRequestException(`List Master '${lmCode}' not found`);
587
+ }
588
+
589
+ // Use repository to fetch items directly with filter
590
+ return this.listItemsRepo.findItemsByType(
591
+ lmCode,
592
+ listMaster.sort_by,
593
+ inactiveIdsArray,
594
+ loggedInUser.enterprise_id,
595
+ params,
596
+ );
597
+ } catch (error) {
598
+ console.error(`❌ Error fetching from List Master '${lmCode}':`, error.message);
599
+ throw new BadRequestException(
600
+ `Failed to fetch from List Master: ${error.message}`,
601
+ );
602
+ }
603
+ }
604
+
605
+ /**
606
+ * Fetch dropdown from Custom Internal Service (Message Pattern)
607
+ */
608
+ private async fetchFromCustomInternalService(
609
+ serviceName: string,
610
+ filterCode: string,
611
+ params: Record<string, any>,
612
+ loggedInUser: UserData,
613
+ ) {
614
+ try {
615
+ // Get internal service client
616
+ const client = this.factory.getClient('INTERNAL_SERVICE');
617
+
618
+ if (!client) {
619
+ throw new BadRequestException('Internal service client not available');
620
+ }
621
+
622
+ // Call internal microservice using message pattern
623
+ const response = await client
624
+ .send(serviceName, {
625
+ filterCode,
626
+ params,
627
+ loggedInUser,
628
+ })
629
+ .toPromise();
630
+
631
+ return response;
632
+ } catch (error) {
633
+ console.error(
634
+ `❌ Error calling custom internal service '${serviceName}':`,
635
+ error.message,
636
+ );
637
+ throw new BadRequestException(
638
+ `Failed to call custom internal service: ${error.message}`,
639
+ );
640
+ }
641
+ }
642
+
643
+ /**
644
+ * Fetch dropdown from Custom Key (Predefined logic)
645
+ */
646
+ private async fetchFromCustomKey(
647
+ customKey: string,
648
+ filterCode: string,
649
+ params: Record<string, any>,
650
+ loggedInUser: UserData,
651
+ ) {
652
+ try {
653
+ // Handle predefined custom logic based on custom key
654
+ switch (customKey.toUpperCase()) {
655
+ case 'USER_ROLES':
656
+ return this.getUserRolesDropdown(filterCode, params, loggedInUser);
657
+
658
+ case 'DEPARTMENTS':
659
+ return this.getDepartmentsDropdown(filterCode, params, loggedInUser);
660
+
661
+ case 'FINANCIAL_YEARS':
662
+ return this.getFinancialYearsDropdown(filterCode, params, loggedInUser);
458
663
 
459
- switch (listMaster.source) {
460
- case 'entity':
461
- return this.fetchFromEntity(
462
- listMaster.type,
463
- params,
464
- inactiveIdsArray,
465
- loggedInUser,
466
- );
467
-
468
- case 'master':
469
- return this.listItemsRepo.findItemsByType(
470
- listMaster.type,
471
- listMaster.sort_by,
472
- inactiveIdsArray,
473
- loggedInUser.enterprise_id,
474
- params,
475
- );
476
-
477
- case 'operator':
478
- return this.listItemsRepo.findOperatorsByType(
479
- listMaster?.type,
480
- loggedInUser?.enterprise_id,
481
- );
482
-
483
- case 'custom':
484
- // If you want Axios call here too:
485
- try {
486
- const response = await axios.get(
487
- `https://external-source.com/${listMaster.custom_source_id}`,
488
- { params },
489
- );
490
- return response.data;
491
- } catch (error) {
492
- console.error('⚠️ Custom source fetch failed:', error.message);
493
- throw new BadRequestException('Failed to fetch custom source');
494
- }
495
-
496
- default:
497
- throw new BadRequestException(
498
- `Unknown source: ${listMaster.source}`,
499
- );
664
+ case 'OPERATORS':
665
+ return this.getOperatorsDropdown(filterCode, params, loggedInUser);
666
+
667
+ // Add more custom key handlers as needed
668
+ default:
669
+ throw new BadRequestException(`Unknown custom key: ${customKey}`);
670
+ }
671
+ } catch (error) {
672
+ console.error(`❌ Error fetching custom key '${customKey}':`, error.message);
673
+ throw new BadRequestException(
674
+ `Failed to fetch custom key: ${error.message}`,
675
+ );
676
+ }
677
+ }
678
+
679
+ /**
680
+ * Fetch dropdown from External API
681
+ */
682
+ private async fetchFromExternalAPI(
683
+ apiUrl: string,
684
+ filterCode: string,
685
+ params: Record<string, any>,
686
+ loggedInUser: UserData,
687
+ ) {
688
+ try {
689
+ // Build full API URL with filter code if provided
690
+ const fullUrl = filterCode ? `${apiUrl}/${filterCode}` : apiUrl;
691
+
692
+ // Make external API call using Axios
693
+ const response = await axios.get(fullUrl, {
694
+ params: {
695
+ ...params,
696
+ enterprise_id: loggedInUser.enterprise_id,
697
+ },
698
+ timeout: 10000, // 10 seconds timeout
699
+ headers: {
700
+ 'Content-Type': 'application/json',
701
+ 'Authorization': `Bearer ${this.configService.get('EXTERNAL_API_TOKEN')}`,
702
+ },
703
+ });
704
+
705
+ return response.data;
706
+ } catch (error) {
707
+ console.error(`❌ Error calling external API '${apiUrl}':`, error.message);
708
+
709
+ if (error.response) {
710
+ throw new BadRequestException(
711
+ `External API error: ${error.response.status} - ${error.response.statusText}`,
712
+ );
713
+ } else if (error.request) {
714
+ throw new BadRequestException('External API is not responding');
715
+ } else {
716
+ throw new BadRequestException(
717
+ `Failed to call external API: ${error.message}`,
718
+ );
719
+ }
720
+ }
721
+ }
722
+
723
+ /**
724
+ * Apply filter code to query (custom WHERE conditions)
725
+ */
726
+ private applyFilterCode(
727
+ query: any,
728
+ tableName: string,
729
+ filterCode: string,
730
+ loggedInUser: UserData,
731
+ ): any {
732
+ // Parse filter code and apply conditions
733
+ // Example: "status=active|type=premium"
734
+ const filters = filterCode.split('|');
735
+
736
+ filters.forEach((filter) => {
737
+ const [key, value] = filter.split('=');
738
+ if (key && value) {
739
+ // Handle special variables
740
+ let filterValue: any = value;
741
+ if (value === '{USER_ID}') {
742
+ filterValue = loggedInUser.id;
743
+ } else if (value === '{ENTERPRISE_ID}') {
744
+ filterValue = loggedInUser.enterprise_id;
500
745
  }
746
+
747
+ query = query.andWhere(`${tableName}.${key} = :${key}`, {
748
+ [key]: filterValue,
749
+ });
501
750
  }
502
- } else {
503
- const client = this.factory.getClient(appCode);
751
+ });
752
+
753
+ return query;
754
+ }
755
+
756
+ /**
757
+ * Format dropdown response with consistent structure
758
+ */
759
+ private formatDropdownResponse(data: any[], config: any): any[] {
760
+ return data.map((item) => ({
761
+ id: item.id,
762
+ value: item.value || item.name || item.title,
763
+ label: item.label || item.display_name || item.name || item.title,
764
+ code: item.code,
765
+ ...item, // Include all other fields
766
+ }));
767
+ }
768
+
769
+ /**
770
+ * Forward request to cross-app microservice (CROSS APP)
771
+ */
772
+ private async processCrossAppDropdown(
773
+ targetAppCode: string,
774
+ entity_type: string,
775
+ attribute_key: string,
776
+ loggedInUser: UserData,
777
+ body: Record<string, any>,
778
+ ) {
779
+ try {
780
+ // Get microservice client for target app
781
+ const client = this.factory.getClient(targetAppCode);
504
782
 
505
783
  if (!client) {
506
- return;
784
+ throw new BadRequestException(
785
+ `Microservice client for app '${targetAppCode}' not available`,
786
+ );
507
787
  }
508
788
 
509
- return client
789
+ // Forward the request to target microservice
790
+ // The target microservice will execute the same process recursively
791
+ const response = await client
510
792
  .send('getDropdownData', {
511
793
  entity_type,
512
794
  attribute_key,
@@ -514,6 +796,94 @@ export class ListMasterService {
514
796
  body,
515
797
  })
516
798
  .toPromise();
799
+
800
+ return response;
801
+ } catch (error) {
802
+ console.error(
803
+ `❌ Error calling cross-app microservice '${targetAppCode}':`,
804
+ error.message,
805
+ );
806
+ throw new BadRequestException(
807
+ `Failed to fetch dropdown from app '${targetAppCode}': ${error.message}`,
808
+ );
517
809
  }
518
810
  }
811
+
812
+ // Example custom key handlers
813
+ private async getUserRolesDropdown(
814
+ filterCode: string,
815
+ params: Record<string, any>,
816
+ loggedInUser: UserData,
817
+ ) {
818
+ // TODO: Implement roles service integration
819
+ // return this.rolesService.getActiveRoles(loggedInUser.enterprise_id, params);
820
+ throw new BadRequestException('USER_ROLES handler not implemented yet');
821
+ }
822
+
823
+ private async getDepartmentsDropdown(
824
+ filterCode: string,
825
+ params: Record<string, any>,
826
+ loggedInUser: UserData,
827
+ ) {
828
+ // TODO: Implement departments service integration
829
+ // return this.departmentService.getActiveDepartments(loggedInUser.enterprise_id, params);
830
+ throw new BadRequestException('DEPARTMENTS handler not implemented yet');
831
+ }
832
+
833
+ private async getFinancialYearsDropdown(
834
+ filterCode: string,
835
+ params: Record<string, any>,
836
+ loggedInUser: UserData,
837
+ ) {
838
+ // Generate financial years dynamically
839
+ const currentYear = new Date().getFullYear();
840
+ const years: Array<{ id: number; value: string; label: string }> = [];
841
+ for (let i = -5; i <= 5; i++) {
842
+ const year = currentYear + i;
843
+ years.push({
844
+ id: year,
845
+ value: `FY${year}-${year + 1}`,
846
+ label: `FY ${year}-${year + 1}`,
847
+ });
848
+ }
849
+ return years;
850
+ }
851
+
852
+ private async getOperatorsDropdown(
853
+ filterCode: string,
854
+ params: Record<string, any>,
855
+ loggedInUser: UserData,
856
+ ) {
857
+ // Return predefined operators
858
+ return [
859
+ { id: 1, value: '=', label: 'Equals' },
860
+ { id: 2, value: '!=', label: 'Not Equals' },
861
+ { id: 3, value: '>', label: 'Greater Than' },
862
+ { id: 4, value: '<', label: 'Less Than' },
863
+ { id: 5, value: '>=', label: 'Greater Than or Equal' },
864
+ { id: 6, value: '<=', label: 'Less Than or Equal' },
865
+ { id: 7, value: 'LIKE', label: 'Contains' },
866
+ { id: 8, value: 'IN', label: 'In List' },
867
+ ];
868
+ }
869
+
870
+ /**
871
+ * Parse filter code string into key-value params
872
+ * Example: "status=active|type=premium" => { status: 'active', type: 'premium' }
873
+ */
874
+ private parseFilterCode(filterCode: string): Record<string, any> {
875
+ if (!filterCode) return {};
876
+
877
+ const params: Record<string, any> = {};
878
+ const filters = filterCode.split('|');
879
+
880
+ filters.forEach((filter) => {
881
+ const [key, value] = filter.split('=');
882
+ if (key && value) {
883
+ params[key.trim()] = value.trim();
884
+ }
885
+ });
886
+
887
+ return params;
888
+ }
519
889
  }
@@ -33,9 +33,8 @@ export class EntityDynamicController {
33
33
  if (!entityType) {
34
34
  throw new BadRequestException(`Query param "entity_type" is required`);
35
35
  }
36
-
37
- return await this.entityDynamicService.createEntityWithRelation(
38
- entityType,
36
+ data['entity_type'] = entityType;
37
+ return await this.entityDynamicService.createEntity(
39
38
  data,
40
39
  loggedInUser,
41
40
  );
@@ -63,10 +62,9 @@ export class EntityDynamicController {
63
62
  if (!existingEntity) {
64
63
  throw new NotFoundException(`No entity found for id "${id}"`);
65
64
  }
66
-
67
- return await this.entityDynamicService.updateEntityWithRelations(
68
- entityType,
69
- id,
65
+ data['id'] = id;
66
+ data['entity_type'] = entityType;
67
+ return await this.entityDynamicService.updateEntity(
70
68
  data,
71
69
  loggedInUser,
72
70
  );
@@ -85,7 +83,7 @@ export class EntityDynamicController {
85
83
  throw new BadRequestException(`Query param "entity_type" is required`);
86
84
  }
87
85
 
88
- const entity = await this.entityDynamicService.getEntityWithRelation(
86
+ const entity = await this.entityDynamicService.getEntityData(
89
87
  entityType,
90
88
  id,
91
89
  loggedInUser,
@@ -171,7 +171,6 @@ export class EntityController {
171
171
  entityData,
172
172
  loggedInUser as UserData,
173
173
  null,
174
- appcode,
175
174
  );
176
175
 
177
176
  // ✅ Run workflow automation (no preUpdateStates for CREATE)
@@ -89,4 +89,17 @@ export class AttributeMaster extends BaseEntity {
89
89
 
90
90
  @Column({ name: 'flat_json_key', nullable: true })
91
91
  flat_json_key: string;
92
+
93
+ // Dropdown data source fields
94
+ @Column({ name: 'ds_category', type: 'varchar', nullable: true, length: 50 })
95
+ ds_category: string;
96
+
97
+ @Column({ name: 'ds_entitytype', type: 'varchar', nullable: true, length: 255 })
98
+ ds_entitytype: string;
99
+
100
+ @Column({ name: 'ds_filtercode', type: 'varchar', nullable: true, length: 500 })
101
+ ds_filtercode: string;
102
+
103
+ @Column({ name: 'ds_appcode', type: 'varchar', nullable: true, length: 50 })
104
+ ds_appcode: string;
92
105
  }