av6-core 1.7.15 → 1.7.17
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/index.d.mts +249 -1
- package/dist/index.d.ts +249 -1
- package/dist/index.js +316 -0
- package/dist/index.mjs +312 -0
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -6,6 +6,7 @@ import { Readable } from 'stream';
|
|
|
6
6
|
import winston from 'winston';
|
|
7
7
|
import * as PrismaNamespace from '@prisma/client';
|
|
8
8
|
import { PrismaClient, Prisma } from '@prisma/client';
|
|
9
|
+
import EventEmitter from 'events';
|
|
9
10
|
|
|
10
11
|
declare enum ErrorMessageType {
|
|
11
12
|
INVALID_ID = "Invalid id: %1 Numeric value expected.",
|
|
@@ -527,6 +528,42 @@ declare enum ApprovalStatus {
|
|
|
527
528
|
REJECTED = 3,
|
|
528
529
|
CANCELLED = 4
|
|
529
530
|
}
|
|
531
|
+
declare enum FlowType {
|
|
532
|
+
STATUS = 0,
|
|
533
|
+
CO_PAY = 1,
|
|
534
|
+
INDIVIDUAL_DISCOUNT = 2,
|
|
535
|
+
OVERALL_DISCOUNT = 3,
|
|
536
|
+
SPECIAL_DISCOUNT = 4,
|
|
537
|
+
REFUND = 5
|
|
538
|
+
}
|
|
539
|
+
type StepType = "MIN_MAX" | "NORMAL";
|
|
540
|
+
type ApprovalStep = {
|
|
541
|
+
id?: number;
|
|
542
|
+
flowId: number;
|
|
543
|
+
level: number;
|
|
544
|
+
minAmount?: Decimal | DecimalJsLike | number | string | null;
|
|
545
|
+
maxAmount?: Decimal | DecimalJsLike | number | string | null;
|
|
546
|
+
stepType?: StepType;
|
|
547
|
+
config?: InputJsonValue;
|
|
548
|
+
childConfig?: InputJsonValue;
|
|
549
|
+
isActive?: boolean;
|
|
550
|
+
createdBy?: number | null;
|
|
551
|
+
updatedBy?: number | null;
|
|
552
|
+
createdAt?: Date | string;
|
|
553
|
+
updatedAt?: Date | string;
|
|
554
|
+
};
|
|
555
|
+
type ApproverMapping = {
|
|
556
|
+
id?: number;
|
|
557
|
+
staffId?: number | null;
|
|
558
|
+
stepId: number;
|
|
559
|
+
ccId: number;
|
|
560
|
+
roleId?: number | null;
|
|
561
|
+
isActive?: boolean;
|
|
562
|
+
createdBy?: number | null;
|
|
563
|
+
updatedBy?: number | null;
|
|
564
|
+
createdAt?: Date | string;
|
|
565
|
+
updatedAt?: Date | string;
|
|
566
|
+
};
|
|
530
567
|
type ApprovalInstance = {
|
|
531
568
|
id?: number;
|
|
532
569
|
flowId: number;
|
|
@@ -544,6 +581,28 @@ type ApprovalInstance = {
|
|
|
544
581
|
createdAt?: Date | string;
|
|
545
582
|
updatedAt?: Date | string;
|
|
546
583
|
};
|
|
584
|
+
type ApprovalFlow = {
|
|
585
|
+
name: string;
|
|
586
|
+
id: number;
|
|
587
|
+
subjectType: string;
|
|
588
|
+
service: string;
|
|
589
|
+
isActive: boolean;
|
|
590
|
+
createdBy: number | null;
|
|
591
|
+
updatedBy: number | null;
|
|
592
|
+
createdAt: Date;
|
|
593
|
+
updatedAt: Date;
|
|
594
|
+
flowType: FlowType;
|
|
595
|
+
};
|
|
596
|
+
type ApprovalAction = {
|
|
597
|
+
id: number;
|
|
598
|
+
isActive: boolean;
|
|
599
|
+
level: number;
|
|
600
|
+
instanceId: number;
|
|
601
|
+
actedBy: number;
|
|
602
|
+
statusAfter: ApprovalStatus;
|
|
603
|
+
comment: string | null;
|
|
604
|
+
actedAt: Date;
|
|
605
|
+
};
|
|
547
606
|
type LevelReadyEvt = {
|
|
548
607
|
instanceId: number;
|
|
549
608
|
subjectType: string;
|
|
@@ -566,6 +625,195 @@ declare global {
|
|
|
566
625
|
"approval:REJECTED": (i: ApprovalInstance) => void;
|
|
567
626
|
}
|
|
568
627
|
}
|
|
628
|
+
interface CreateApprovalFlow {
|
|
629
|
+
subjectType: string;
|
|
630
|
+
name: string;
|
|
631
|
+
}
|
|
632
|
+
interface UpdateApprovalFlow extends CreateApprovalFlow {
|
|
633
|
+
id: number;
|
|
634
|
+
}
|
|
635
|
+
interface IApprovalStep {
|
|
636
|
+
id?: number;
|
|
637
|
+
flowId: number;
|
|
638
|
+
subjectType: string;
|
|
639
|
+
}
|
|
640
|
+
type FlowWithStepsResponse = ApprovalFlow & {
|
|
641
|
+
steps: ApprovalStep[];
|
|
642
|
+
};
|
|
643
|
+
interface FlowWithSelectedStepResponse extends ApprovalFlow {
|
|
644
|
+
step: ApprovalStep | null;
|
|
645
|
+
}
|
|
646
|
+
interface ActInput {
|
|
647
|
+
instanceId: number;
|
|
648
|
+
approverId: number;
|
|
649
|
+
action: "APPROVE" | "REJECT";
|
|
650
|
+
ccId: number;
|
|
651
|
+
comment?: string;
|
|
652
|
+
}
|
|
653
|
+
interface RawFlowWithSelectedStepResponse {
|
|
654
|
+
flowId: number;
|
|
655
|
+
stepId: number;
|
|
656
|
+
level: number;
|
|
657
|
+
minAmount: number;
|
|
658
|
+
maxAmount: number;
|
|
659
|
+
stepType: string;
|
|
660
|
+
ccId: number;
|
|
661
|
+
service: string;
|
|
662
|
+
}
|
|
663
|
+
interface StartFlowReq {
|
|
664
|
+
service: string;
|
|
665
|
+
subjectType: string;
|
|
666
|
+
subjectId: number;
|
|
667
|
+
netTotal: number;
|
|
668
|
+
ccId: number;
|
|
669
|
+
refNo: string;
|
|
670
|
+
level?: number;
|
|
671
|
+
extra?: Record<string, string | number | boolean | null>;
|
|
672
|
+
}
|
|
673
|
+
interface NotificationEvent {
|
|
674
|
+
instanceId: number;
|
|
675
|
+
subjectType: string;
|
|
676
|
+
service: string;
|
|
677
|
+
subjectId: number;
|
|
678
|
+
level: number;
|
|
679
|
+
ccId: number;
|
|
680
|
+
approvers: ApproverMapping[];
|
|
681
|
+
}
|
|
682
|
+
interface CommonApproveReq {
|
|
683
|
+
service: string;
|
|
684
|
+
subjectType: string;
|
|
685
|
+
id: number;
|
|
686
|
+
comment?: string;
|
|
687
|
+
ccId: number;
|
|
688
|
+
approverId: number;
|
|
689
|
+
approveType: "APPROVE" | "REJECT";
|
|
690
|
+
}
|
|
691
|
+
interface CommonGetApprovalActionReq {
|
|
692
|
+
service: string;
|
|
693
|
+
subjectType: string;
|
|
694
|
+
id: number;
|
|
695
|
+
}
|
|
696
|
+
interface GetPendingApprovalReq {
|
|
697
|
+
service: string;
|
|
698
|
+
ccId: number;
|
|
699
|
+
staffId: number;
|
|
700
|
+
}
|
|
701
|
+
interface IParentConfigJSON {
|
|
702
|
+
tableName: string;
|
|
703
|
+
approvedAt?: string;
|
|
704
|
+
approvedBy?: string;
|
|
705
|
+
approvalNote?: string;
|
|
706
|
+
status?: string;
|
|
707
|
+
refundAmount?: string;
|
|
708
|
+
discountType?: string;
|
|
709
|
+
discountValue?: string;
|
|
710
|
+
discountAmount?: string;
|
|
711
|
+
totalCopaymentAmount?: string;
|
|
712
|
+
isApproved?: string;
|
|
713
|
+
paymentStatus?: string;
|
|
714
|
+
}
|
|
715
|
+
interface IParentConfigData {
|
|
716
|
+
approvedAt?: string | null;
|
|
717
|
+
approvedBy?: string | null;
|
|
718
|
+
approvalNote?: string | null;
|
|
719
|
+
status?: string | null;
|
|
720
|
+
refundAmount?: number | null;
|
|
721
|
+
discountType?: string | null;
|
|
722
|
+
discountValue?: number | null;
|
|
723
|
+
discountAmount?: number | null;
|
|
724
|
+
totalCopaymentAmount?: number | null;
|
|
725
|
+
isApproved?: string | null;
|
|
726
|
+
paymentStatus?: string | null;
|
|
727
|
+
}
|
|
728
|
+
interface IChildConfigJSON {
|
|
729
|
+
tableName: string;
|
|
730
|
+
discountType?: string;
|
|
731
|
+
discountValue?: string;
|
|
732
|
+
discountAmount?: string;
|
|
733
|
+
copayValue?: string;
|
|
734
|
+
copayType?: string;
|
|
735
|
+
copayAmount?: string;
|
|
736
|
+
coPayModificationStatus?: string;
|
|
737
|
+
}
|
|
738
|
+
interface IChildConfigData {
|
|
739
|
+
discountType?: string | null;
|
|
740
|
+
discountValue?: number | null;
|
|
741
|
+
discountAmount?: number | null;
|
|
742
|
+
copayValue?: number | null;
|
|
743
|
+
copayType?: string | null;
|
|
744
|
+
copayAmount?: number | null;
|
|
745
|
+
coPayModificationStatus?: string | null;
|
|
746
|
+
}
|
|
747
|
+
interface ICommonApprovalUpdate {
|
|
748
|
+
id: number;
|
|
749
|
+
flowType: FlowType;
|
|
750
|
+
status?: string | null;
|
|
751
|
+
approvedAt?: string | null;
|
|
752
|
+
approvedBy?: string | null;
|
|
753
|
+
approvalNote?: string | null;
|
|
754
|
+
refundAmount?: number | null;
|
|
755
|
+
}
|
|
756
|
+
interface EventInstance {
|
|
757
|
+
subjectType: string;
|
|
758
|
+
service: string;
|
|
759
|
+
instanceId: number;
|
|
760
|
+
flowType: FlowType;
|
|
761
|
+
subjectId: number;
|
|
762
|
+
approverId: number;
|
|
763
|
+
step: ApprovalStep;
|
|
764
|
+
comment?: string;
|
|
765
|
+
}
|
|
766
|
+
interface ApprovalInstanceByUser {
|
|
767
|
+
id: number;
|
|
768
|
+
flowId: number;
|
|
769
|
+
subjectType: string;
|
|
770
|
+
service: string;
|
|
771
|
+
subjectId: number;
|
|
772
|
+
refNo: number;
|
|
773
|
+
currentStepId: number;
|
|
774
|
+
netTotal: number;
|
|
775
|
+
status: ApprovalStatus;
|
|
776
|
+
createdBy: string;
|
|
777
|
+
createdAt: Date;
|
|
778
|
+
level: number;
|
|
779
|
+
flowType: FlowType;
|
|
780
|
+
flowName: string;
|
|
781
|
+
extra?: string | null;
|
|
782
|
+
}
|
|
783
|
+
interface ApprovalActionDto extends Omit<ApprovalAction, "actedBy"> {
|
|
784
|
+
actedByDetails: EmployeeCache | null;
|
|
785
|
+
}
|
|
786
|
+
interface GetMyApprovalFlow extends Omit<CommonFilterWithDate, "sortBy"> {
|
|
787
|
+
staffId: number;
|
|
788
|
+
ccId: number;
|
|
789
|
+
service: string;
|
|
790
|
+
status?: ApprovalStatus[];
|
|
791
|
+
flowType?: FlowType;
|
|
792
|
+
}
|
|
793
|
+
interface ApprovalDeps {
|
|
794
|
+
helpers: Helpers;
|
|
795
|
+
logger: winston.Logger;
|
|
796
|
+
requestStorage: AsyncLocalStorage<Store>;
|
|
797
|
+
prisma: PrismaClient;
|
|
798
|
+
eventBus: EventEmitter;
|
|
799
|
+
}
|
|
800
|
+
type PrismaTransactionClient = Omit<PrismaClient, "$connect" | "$disconnect" | "$on" | "$transaction" | "$use" | "$extends">;
|
|
801
|
+
|
|
802
|
+
declare class ApprovalService {
|
|
803
|
+
private deps;
|
|
804
|
+
private approvalRepo;
|
|
805
|
+
constructor(deps: ApprovalDeps);
|
|
806
|
+
startFlow(tx: PrismaClient | PrismaTransactionClient, { service, subjectType, subjectId, netTotal, ccId, refNo, level, extra }: StartFlowReq): Promise<void>;
|
|
807
|
+
lastLevel(steps: ApprovalStep[]): Promise<number>;
|
|
808
|
+
/** Approver clicks “Approve” or “Reject”. */
|
|
809
|
+
act({ instanceId, approverId, action, ccId, comment }: ActInput): Promise<any>;
|
|
810
|
+
private emitEvents;
|
|
811
|
+
private assertPermission;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
declare const approvalRepository: (helpers: Helpers) => {
|
|
815
|
+
findMatchingFlow(tx: PrismaClient | Prisma.TransactionClient, type: string, service: string, ccId: number, netTotal: number, level?: number): Promise<RawFlowWithSelectedStepResponse | null>;
|
|
816
|
+
};
|
|
569
817
|
|
|
570
818
|
declare function customOmit<T extends object, K extends keyof T>(obj: T, keys: K[]): {
|
|
571
819
|
rest: Omit<T, K>;
|
|
@@ -760,4 +1008,4 @@ declare class AuditProxy<Module extends string = "OPD" | "PROCEDURE" | "GENERAL_
|
|
|
760
1008
|
createAuditedService<T extends object>(serviceName: string, service: T): T;
|
|
761
1009
|
}
|
|
762
1010
|
|
|
763
|
-
export { type AuditContext, type AuditContextProvider, AuditCore, type AuditLogPayload, AuditLogger, AuditProxy, type BulkAtomicResult, type BulkConfig, type BulkConflictConfig, type BulkOnConflict, type CacheAdapter, type CalculationRes, type ColValue, type CommonCreateRequestRepository, type CommonExcelRequest, type CommonFilterRequest, type CommonFilterWithDate, type CommonServiceResponse, type CommonUpdateRequestRepository, type Config, type Context, type CreateUINConfigRequest, type CrudContext, type CrudDelegate, type DataType, type DeepMerge, type DeleteParams, type DeleteRequestRepository, type Deps, type DropdownRequest, type DropdownRequestService, type DtoFromMapping, type DtoNullOnMissing, type DynamicCreateInput, type DynamicCrudConfig, type DynamicShortCode, type DynamicUpdateInput, type EmitPayload, type EmployeeCache, type ExcelConfig, type ExportExcel, type ExportExcelRequestService, type FetchRequest, type FetchRequestRepository, type FieldConfig, type FieldRules, type FieldType, type FixedMap, type FixedSearchRequest, type FixedSearchRequestService, type Helpers, type ImportExcel, type ImportExcelRequestService, type LockUnlockParams, type LockUnlockRequestRepository, type LogicNode, type Mapper, type MergeAll, type NewFixedSearchRequest, type NewFixedSearchRequestService, type NewSearchRequest, NotificationEmitter, type Op, type PaginatedResponse, type PathToSelectWithSelect, type PathValue, type PathsToSelectWithSelect, type Presence, type Recipient, type RelationConfig, type RelationStrategy, type RelationWriteConfig, type SearchRequest, type SearchRequestService, type ServiceCacheAdapter, type SingleValidationMapping, type SourcePath, type Store, type ToggleActive, type TxClient, type UINConfigDTO, type UINPreviewRequest, type UINSegment, type UINSegmentType, type UIN_RESET_POLICY, type UinDeps, type UnionToIntersection, type UniqueConfig, type UpdateConfigByCodeInput, type UpdateStatusRequestRepository, type UpdateUINConfigRequest, type ValidationErrorItem, commonService, convertArrayPatternToEachBlocksGeneric, customOmit, findDifferences, formatDatesDeep, fromTimestampToSqlDatetime, getDynamicValue, getNestedValue, getNestedValueV2, getPattern, interpolate, objectTo2DArray, renderEmailTemplate, renderTemplate, toNumberOrNull, toUINConfigDTO, uinConfigService, type updateStatusParams };
|
|
1011
|
+
export { type ActInput, type ApprovalAction, type ApprovalActionDto, type ApprovalDeps, type ApprovalFlow, type ApprovalInstance, type ApprovalInstanceByUser, ApprovalService, ApprovalStatus, type ApprovalStep, type ApproverMapping, type AuditContext, type AuditContextProvider, AuditCore, type AuditLogPayload, AuditLogger, AuditProxy, type BulkAtomicResult, type BulkConfig, type BulkConflictConfig, type BulkOnConflict, type CacheAdapter, type CalculationRes, type ColValue, type CommonApproveReq, type CommonCreateRequestRepository, type CommonExcelRequest, type CommonFilterRequest, type CommonFilterWithDate, type CommonGetApprovalActionReq, type CommonServiceResponse, type CommonUpdateRequestRepository, type Config, type Context, type CreateApprovalFlow, type CreateUINConfigRequest, type CrudContext, type CrudDelegate, type DataType, type DeepMerge, type DeleteParams, type DeleteRequestRepository, type Deps, type DropdownRequest, type DropdownRequestService, type DtoFromMapping, type DtoNullOnMissing, type DynamicCreateInput, type DynamicCrudConfig, type DynamicShortCode, type DynamicUpdateInput, type EmitPayload, type EmployeeCache, type EventInstance, type ExcelConfig, type ExportExcel, type ExportExcelRequestService, type FetchRequest, type FetchRequestRepository, type FieldConfig, type FieldRules, type FieldType, type FixedMap, type FixedSearchRequest, type FixedSearchRequestService, FlowType, type FlowWithSelectedStepResponse, type FlowWithStepsResponse, type GetMyApprovalFlow, type GetPendingApprovalReq, type Helpers, type IApprovalStep, type IChildConfigData, type IChildConfigJSON, type ICommonApprovalUpdate, type IParentConfigData, type IParentConfigJSON, type ImportExcel, type ImportExcelRequestService, type LevelDoneEvt, type LevelReadyEvt, type LockUnlockParams, type LockUnlockRequestRepository, type LogicNode, type Mapper, type MergeAll, type NewFixedSearchRequest, type NewFixedSearchRequestService, type NewSearchRequest, NotificationEmitter, type NotificationEvent, type Op, type PaginatedResponse, type PathToSelectWithSelect, type PathValue, type PathsToSelectWithSelect, type Presence, type PrismaTransactionClient, type RawFlowWithSelectedStepResponse, type Recipient, type RelationConfig, type RelationStrategy, type RelationWriteConfig, type SearchRequest, type SearchRequestService, type ServiceCacheAdapter, type SingleValidationMapping, type SourcePath, type StartFlowReq, type StepType, type Store, type ToggleActive, type TxClient, type UINConfigDTO, type UINPreviewRequest, type UINSegment, type UINSegmentType, type UIN_RESET_POLICY, type UinDeps, type UnionToIntersection, type UniqueConfig, type UpdateApprovalFlow, type UpdateConfigByCodeInput, type UpdateStatusRequestRepository, type UpdateUINConfigRequest, type ValidationErrorItem, approvalRepository, commonService, convertArrayPatternToEachBlocksGeneric, customOmit, findDifferences, formatDatesDeep, fromTimestampToSqlDatetime, getDynamicValue, getNestedValue, getNestedValueV2, getPattern, interpolate, objectTo2DArray, renderEmailTemplate, renderTemplate, toNumberOrNull, toUINConfigDTO, uinConfigService, type updateStatusParams };
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { Readable } from 'stream';
|
|
|
6
6
|
import winston from 'winston';
|
|
7
7
|
import * as PrismaNamespace from '@prisma/client';
|
|
8
8
|
import { PrismaClient, Prisma } from '@prisma/client';
|
|
9
|
+
import EventEmitter from 'events';
|
|
9
10
|
|
|
10
11
|
declare enum ErrorMessageType {
|
|
11
12
|
INVALID_ID = "Invalid id: %1 Numeric value expected.",
|
|
@@ -527,6 +528,42 @@ declare enum ApprovalStatus {
|
|
|
527
528
|
REJECTED = 3,
|
|
528
529
|
CANCELLED = 4
|
|
529
530
|
}
|
|
531
|
+
declare enum FlowType {
|
|
532
|
+
STATUS = 0,
|
|
533
|
+
CO_PAY = 1,
|
|
534
|
+
INDIVIDUAL_DISCOUNT = 2,
|
|
535
|
+
OVERALL_DISCOUNT = 3,
|
|
536
|
+
SPECIAL_DISCOUNT = 4,
|
|
537
|
+
REFUND = 5
|
|
538
|
+
}
|
|
539
|
+
type StepType = "MIN_MAX" | "NORMAL";
|
|
540
|
+
type ApprovalStep = {
|
|
541
|
+
id?: number;
|
|
542
|
+
flowId: number;
|
|
543
|
+
level: number;
|
|
544
|
+
minAmount?: Decimal | DecimalJsLike | number | string | null;
|
|
545
|
+
maxAmount?: Decimal | DecimalJsLike | number | string | null;
|
|
546
|
+
stepType?: StepType;
|
|
547
|
+
config?: InputJsonValue;
|
|
548
|
+
childConfig?: InputJsonValue;
|
|
549
|
+
isActive?: boolean;
|
|
550
|
+
createdBy?: number | null;
|
|
551
|
+
updatedBy?: number | null;
|
|
552
|
+
createdAt?: Date | string;
|
|
553
|
+
updatedAt?: Date | string;
|
|
554
|
+
};
|
|
555
|
+
type ApproverMapping = {
|
|
556
|
+
id?: number;
|
|
557
|
+
staffId?: number | null;
|
|
558
|
+
stepId: number;
|
|
559
|
+
ccId: number;
|
|
560
|
+
roleId?: number | null;
|
|
561
|
+
isActive?: boolean;
|
|
562
|
+
createdBy?: number | null;
|
|
563
|
+
updatedBy?: number | null;
|
|
564
|
+
createdAt?: Date | string;
|
|
565
|
+
updatedAt?: Date | string;
|
|
566
|
+
};
|
|
530
567
|
type ApprovalInstance = {
|
|
531
568
|
id?: number;
|
|
532
569
|
flowId: number;
|
|
@@ -544,6 +581,28 @@ type ApprovalInstance = {
|
|
|
544
581
|
createdAt?: Date | string;
|
|
545
582
|
updatedAt?: Date | string;
|
|
546
583
|
};
|
|
584
|
+
type ApprovalFlow = {
|
|
585
|
+
name: string;
|
|
586
|
+
id: number;
|
|
587
|
+
subjectType: string;
|
|
588
|
+
service: string;
|
|
589
|
+
isActive: boolean;
|
|
590
|
+
createdBy: number | null;
|
|
591
|
+
updatedBy: number | null;
|
|
592
|
+
createdAt: Date;
|
|
593
|
+
updatedAt: Date;
|
|
594
|
+
flowType: FlowType;
|
|
595
|
+
};
|
|
596
|
+
type ApprovalAction = {
|
|
597
|
+
id: number;
|
|
598
|
+
isActive: boolean;
|
|
599
|
+
level: number;
|
|
600
|
+
instanceId: number;
|
|
601
|
+
actedBy: number;
|
|
602
|
+
statusAfter: ApprovalStatus;
|
|
603
|
+
comment: string | null;
|
|
604
|
+
actedAt: Date;
|
|
605
|
+
};
|
|
547
606
|
type LevelReadyEvt = {
|
|
548
607
|
instanceId: number;
|
|
549
608
|
subjectType: string;
|
|
@@ -566,6 +625,195 @@ declare global {
|
|
|
566
625
|
"approval:REJECTED": (i: ApprovalInstance) => void;
|
|
567
626
|
}
|
|
568
627
|
}
|
|
628
|
+
interface CreateApprovalFlow {
|
|
629
|
+
subjectType: string;
|
|
630
|
+
name: string;
|
|
631
|
+
}
|
|
632
|
+
interface UpdateApprovalFlow extends CreateApprovalFlow {
|
|
633
|
+
id: number;
|
|
634
|
+
}
|
|
635
|
+
interface IApprovalStep {
|
|
636
|
+
id?: number;
|
|
637
|
+
flowId: number;
|
|
638
|
+
subjectType: string;
|
|
639
|
+
}
|
|
640
|
+
type FlowWithStepsResponse = ApprovalFlow & {
|
|
641
|
+
steps: ApprovalStep[];
|
|
642
|
+
};
|
|
643
|
+
interface FlowWithSelectedStepResponse extends ApprovalFlow {
|
|
644
|
+
step: ApprovalStep | null;
|
|
645
|
+
}
|
|
646
|
+
interface ActInput {
|
|
647
|
+
instanceId: number;
|
|
648
|
+
approverId: number;
|
|
649
|
+
action: "APPROVE" | "REJECT";
|
|
650
|
+
ccId: number;
|
|
651
|
+
comment?: string;
|
|
652
|
+
}
|
|
653
|
+
interface RawFlowWithSelectedStepResponse {
|
|
654
|
+
flowId: number;
|
|
655
|
+
stepId: number;
|
|
656
|
+
level: number;
|
|
657
|
+
minAmount: number;
|
|
658
|
+
maxAmount: number;
|
|
659
|
+
stepType: string;
|
|
660
|
+
ccId: number;
|
|
661
|
+
service: string;
|
|
662
|
+
}
|
|
663
|
+
interface StartFlowReq {
|
|
664
|
+
service: string;
|
|
665
|
+
subjectType: string;
|
|
666
|
+
subjectId: number;
|
|
667
|
+
netTotal: number;
|
|
668
|
+
ccId: number;
|
|
669
|
+
refNo: string;
|
|
670
|
+
level?: number;
|
|
671
|
+
extra?: Record<string, string | number | boolean | null>;
|
|
672
|
+
}
|
|
673
|
+
interface NotificationEvent {
|
|
674
|
+
instanceId: number;
|
|
675
|
+
subjectType: string;
|
|
676
|
+
service: string;
|
|
677
|
+
subjectId: number;
|
|
678
|
+
level: number;
|
|
679
|
+
ccId: number;
|
|
680
|
+
approvers: ApproverMapping[];
|
|
681
|
+
}
|
|
682
|
+
interface CommonApproveReq {
|
|
683
|
+
service: string;
|
|
684
|
+
subjectType: string;
|
|
685
|
+
id: number;
|
|
686
|
+
comment?: string;
|
|
687
|
+
ccId: number;
|
|
688
|
+
approverId: number;
|
|
689
|
+
approveType: "APPROVE" | "REJECT";
|
|
690
|
+
}
|
|
691
|
+
interface CommonGetApprovalActionReq {
|
|
692
|
+
service: string;
|
|
693
|
+
subjectType: string;
|
|
694
|
+
id: number;
|
|
695
|
+
}
|
|
696
|
+
interface GetPendingApprovalReq {
|
|
697
|
+
service: string;
|
|
698
|
+
ccId: number;
|
|
699
|
+
staffId: number;
|
|
700
|
+
}
|
|
701
|
+
interface IParentConfigJSON {
|
|
702
|
+
tableName: string;
|
|
703
|
+
approvedAt?: string;
|
|
704
|
+
approvedBy?: string;
|
|
705
|
+
approvalNote?: string;
|
|
706
|
+
status?: string;
|
|
707
|
+
refundAmount?: string;
|
|
708
|
+
discountType?: string;
|
|
709
|
+
discountValue?: string;
|
|
710
|
+
discountAmount?: string;
|
|
711
|
+
totalCopaymentAmount?: string;
|
|
712
|
+
isApproved?: string;
|
|
713
|
+
paymentStatus?: string;
|
|
714
|
+
}
|
|
715
|
+
interface IParentConfigData {
|
|
716
|
+
approvedAt?: string | null;
|
|
717
|
+
approvedBy?: string | null;
|
|
718
|
+
approvalNote?: string | null;
|
|
719
|
+
status?: string | null;
|
|
720
|
+
refundAmount?: number | null;
|
|
721
|
+
discountType?: string | null;
|
|
722
|
+
discountValue?: number | null;
|
|
723
|
+
discountAmount?: number | null;
|
|
724
|
+
totalCopaymentAmount?: number | null;
|
|
725
|
+
isApproved?: string | null;
|
|
726
|
+
paymentStatus?: string | null;
|
|
727
|
+
}
|
|
728
|
+
interface IChildConfigJSON {
|
|
729
|
+
tableName: string;
|
|
730
|
+
discountType?: string;
|
|
731
|
+
discountValue?: string;
|
|
732
|
+
discountAmount?: string;
|
|
733
|
+
copayValue?: string;
|
|
734
|
+
copayType?: string;
|
|
735
|
+
copayAmount?: string;
|
|
736
|
+
coPayModificationStatus?: string;
|
|
737
|
+
}
|
|
738
|
+
interface IChildConfigData {
|
|
739
|
+
discountType?: string | null;
|
|
740
|
+
discountValue?: number | null;
|
|
741
|
+
discountAmount?: number | null;
|
|
742
|
+
copayValue?: number | null;
|
|
743
|
+
copayType?: string | null;
|
|
744
|
+
copayAmount?: number | null;
|
|
745
|
+
coPayModificationStatus?: string | null;
|
|
746
|
+
}
|
|
747
|
+
interface ICommonApprovalUpdate {
|
|
748
|
+
id: number;
|
|
749
|
+
flowType: FlowType;
|
|
750
|
+
status?: string | null;
|
|
751
|
+
approvedAt?: string | null;
|
|
752
|
+
approvedBy?: string | null;
|
|
753
|
+
approvalNote?: string | null;
|
|
754
|
+
refundAmount?: number | null;
|
|
755
|
+
}
|
|
756
|
+
interface EventInstance {
|
|
757
|
+
subjectType: string;
|
|
758
|
+
service: string;
|
|
759
|
+
instanceId: number;
|
|
760
|
+
flowType: FlowType;
|
|
761
|
+
subjectId: number;
|
|
762
|
+
approverId: number;
|
|
763
|
+
step: ApprovalStep;
|
|
764
|
+
comment?: string;
|
|
765
|
+
}
|
|
766
|
+
interface ApprovalInstanceByUser {
|
|
767
|
+
id: number;
|
|
768
|
+
flowId: number;
|
|
769
|
+
subjectType: string;
|
|
770
|
+
service: string;
|
|
771
|
+
subjectId: number;
|
|
772
|
+
refNo: number;
|
|
773
|
+
currentStepId: number;
|
|
774
|
+
netTotal: number;
|
|
775
|
+
status: ApprovalStatus;
|
|
776
|
+
createdBy: string;
|
|
777
|
+
createdAt: Date;
|
|
778
|
+
level: number;
|
|
779
|
+
flowType: FlowType;
|
|
780
|
+
flowName: string;
|
|
781
|
+
extra?: string | null;
|
|
782
|
+
}
|
|
783
|
+
interface ApprovalActionDto extends Omit<ApprovalAction, "actedBy"> {
|
|
784
|
+
actedByDetails: EmployeeCache | null;
|
|
785
|
+
}
|
|
786
|
+
interface GetMyApprovalFlow extends Omit<CommonFilterWithDate, "sortBy"> {
|
|
787
|
+
staffId: number;
|
|
788
|
+
ccId: number;
|
|
789
|
+
service: string;
|
|
790
|
+
status?: ApprovalStatus[];
|
|
791
|
+
flowType?: FlowType;
|
|
792
|
+
}
|
|
793
|
+
interface ApprovalDeps {
|
|
794
|
+
helpers: Helpers;
|
|
795
|
+
logger: winston.Logger;
|
|
796
|
+
requestStorage: AsyncLocalStorage<Store>;
|
|
797
|
+
prisma: PrismaClient;
|
|
798
|
+
eventBus: EventEmitter;
|
|
799
|
+
}
|
|
800
|
+
type PrismaTransactionClient = Omit<PrismaClient, "$connect" | "$disconnect" | "$on" | "$transaction" | "$use" | "$extends">;
|
|
801
|
+
|
|
802
|
+
declare class ApprovalService {
|
|
803
|
+
private deps;
|
|
804
|
+
private approvalRepo;
|
|
805
|
+
constructor(deps: ApprovalDeps);
|
|
806
|
+
startFlow(tx: PrismaClient | PrismaTransactionClient, { service, subjectType, subjectId, netTotal, ccId, refNo, level, extra }: StartFlowReq): Promise<void>;
|
|
807
|
+
lastLevel(steps: ApprovalStep[]): Promise<number>;
|
|
808
|
+
/** Approver clicks “Approve” or “Reject”. */
|
|
809
|
+
act({ instanceId, approverId, action, ccId, comment }: ActInput): Promise<any>;
|
|
810
|
+
private emitEvents;
|
|
811
|
+
private assertPermission;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
declare const approvalRepository: (helpers: Helpers) => {
|
|
815
|
+
findMatchingFlow(tx: PrismaClient | Prisma.TransactionClient, type: string, service: string, ccId: number, netTotal: number, level?: number): Promise<RawFlowWithSelectedStepResponse | null>;
|
|
816
|
+
};
|
|
569
817
|
|
|
570
818
|
declare function customOmit<T extends object, K extends keyof T>(obj: T, keys: K[]): {
|
|
571
819
|
rest: Omit<T, K>;
|
|
@@ -760,4 +1008,4 @@ declare class AuditProxy<Module extends string = "OPD" | "PROCEDURE" | "GENERAL_
|
|
|
760
1008
|
createAuditedService<T extends object>(serviceName: string, service: T): T;
|
|
761
1009
|
}
|
|
762
1010
|
|
|
763
|
-
export { type AuditContext, type AuditContextProvider, AuditCore, type AuditLogPayload, AuditLogger, AuditProxy, type BulkAtomicResult, type BulkConfig, type BulkConflictConfig, type BulkOnConflict, type CacheAdapter, type CalculationRes, type ColValue, type CommonCreateRequestRepository, type CommonExcelRequest, type CommonFilterRequest, type CommonFilterWithDate, type CommonServiceResponse, type CommonUpdateRequestRepository, type Config, type Context, type CreateUINConfigRequest, type CrudContext, type CrudDelegate, type DataType, type DeepMerge, type DeleteParams, type DeleteRequestRepository, type Deps, type DropdownRequest, type DropdownRequestService, type DtoFromMapping, type DtoNullOnMissing, type DynamicCreateInput, type DynamicCrudConfig, type DynamicShortCode, type DynamicUpdateInput, type EmitPayload, type EmployeeCache, type ExcelConfig, type ExportExcel, type ExportExcelRequestService, type FetchRequest, type FetchRequestRepository, type FieldConfig, type FieldRules, type FieldType, type FixedMap, type FixedSearchRequest, type FixedSearchRequestService, type Helpers, type ImportExcel, type ImportExcelRequestService, type LockUnlockParams, type LockUnlockRequestRepository, type LogicNode, type Mapper, type MergeAll, type NewFixedSearchRequest, type NewFixedSearchRequestService, type NewSearchRequest, NotificationEmitter, type Op, type PaginatedResponse, type PathToSelectWithSelect, type PathValue, type PathsToSelectWithSelect, type Presence, type Recipient, type RelationConfig, type RelationStrategy, type RelationWriteConfig, type SearchRequest, type SearchRequestService, type ServiceCacheAdapter, type SingleValidationMapping, type SourcePath, type Store, type ToggleActive, type TxClient, type UINConfigDTO, type UINPreviewRequest, type UINSegment, type UINSegmentType, type UIN_RESET_POLICY, type UinDeps, type UnionToIntersection, type UniqueConfig, type UpdateConfigByCodeInput, type UpdateStatusRequestRepository, type UpdateUINConfigRequest, type ValidationErrorItem, commonService, convertArrayPatternToEachBlocksGeneric, customOmit, findDifferences, formatDatesDeep, fromTimestampToSqlDatetime, getDynamicValue, getNestedValue, getNestedValueV2, getPattern, interpolate, objectTo2DArray, renderEmailTemplate, renderTemplate, toNumberOrNull, toUINConfigDTO, uinConfigService, type updateStatusParams };
|
|
1011
|
+
export { type ActInput, type ApprovalAction, type ApprovalActionDto, type ApprovalDeps, type ApprovalFlow, type ApprovalInstance, type ApprovalInstanceByUser, ApprovalService, ApprovalStatus, type ApprovalStep, type ApproverMapping, type AuditContext, type AuditContextProvider, AuditCore, type AuditLogPayload, AuditLogger, AuditProxy, type BulkAtomicResult, type BulkConfig, type BulkConflictConfig, type BulkOnConflict, type CacheAdapter, type CalculationRes, type ColValue, type CommonApproveReq, type CommonCreateRequestRepository, type CommonExcelRequest, type CommonFilterRequest, type CommonFilterWithDate, type CommonGetApprovalActionReq, type CommonServiceResponse, type CommonUpdateRequestRepository, type Config, type Context, type CreateApprovalFlow, type CreateUINConfigRequest, type CrudContext, type CrudDelegate, type DataType, type DeepMerge, type DeleteParams, type DeleteRequestRepository, type Deps, type DropdownRequest, type DropdownRequestService, type DtoFromMapping, type DtoNullOnMissing, type DynamicCreateInput, type DynamicCrudConfig, type DynamicShortCode, type DynamicUpdateInput, type EmitPayload, type EmployeeCache, type EventInstance, type ExcelConfig, type ExportExcel, type ExportExcelRequestService, type FetchRequest, type FetchRequestRepository, type FieldConfig, type FieldRules, type FieldType, type FixedMap, type FixedSearchRequest, type FixedSearchRequestService, FlowType, type FlowWithSelectedStepResponse, type FlowWithStepsResponse, type GetMyApprovalFlow, type GetPendingApprovalReq, type Helpers, type IApprovalStep, type IChildConfigData, type IChildConfigJSON, type ICommonApprovalUpdate, type IParentConfigData, type IParentConfigJSON, type ImportExcel, type ImportExcelRequestService, type LevelDoneEvt, type LevelReadyEvt, type LockUnlockParams, type LockUnlockRequestRepository, type LogicNode, type Mapper, type MergeAll, type NewFixedSearchRequest, type NewFixedSearchRequestService, type NewSearchRequest, NotificationEmitter, type NotificationEvent, type Op, type PaginatedResponse, type PathToSelectWithSelect, type PathValue, type PathsToSelectWithSelect, type Presence, type PrismaTransactionClient, type RawFlowWithSelectedStepResponse, type Recipient, type RelationConfig, type RelationStrategy, type RelationWriteConfig, type SearchRequest, type SearchRequestService, type ServiceCacheAdapter, type SingleValidationMapping, type SourcePath, type StartFlowReq, type StepType, type Store, type ToggleActive, type TxClient, type UINConfigDTO, type UINPreviewRequest, type UINSegment, type UINSegmentType, type UIN_RESET_POLICY, type UinDeps, type UnionToIntersection, type UniqueConfig, type UpdateApprovalFlow, type UpdateConfigByCodeInput, type UpdateStatusRequestRepository, type UpdateUINConfigRequest, type ValidationErrorItem, approvalRepository, commonService, convertArrayPatternToEachBlocksGeneric, customOmit, findDifferences, formatDatesDeep, fromTimestampToSqlDatetime, getDynamicValue, getNestedValue, getNestedValueV2, getPattern, interpolate, objectTo2DArray, renderEmailTemplate, renderTemplate, toNumberOrNull, toUINConfigDTO, uinConfigService, type updateStatusParams };
|
package/dist/index.js
CHANGED
|
@@ -30,10 +30,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
ApprovalService: () => ApprovalService,
|
|
34
|
+
ApprovalStatus: () => ApprovalStatus,
|
|
33
35
|
AuditCore: () => AuditCore,
|
|
34
36
|
AuditLogger: () => AuditLogger,
|
|
35
37
|
AuditProxy: () => AuditProxy,
|
|
38
|
+
FlowType: () => FlowType,
|
|
36
39
|
NotificationEmitter: () => NotificationEmitter,
|
|
40
|
+
approvalRepository: () => approvalRepository,
|
|
37
41
|
commonService: () => commonService,
|
|
38
42
|
convertArrayPatternToEachBlocksGeneric: () => convertArrayPatternToEachBlocksGeneric,
|
|
39
43
|
customOmit: () => customOmit,
|
|
@@ -2300,6 +2304,314 @@ var commonService = (serviceDeps) => {
|
|
|
2300
2304
|
};
|
|
2301
2305
|
};
|
|
2302
2306
|
|
|
2307
|
+
// src/repository/approval.repository.ts
|
|
2308
|
+
var approvalRepository = (helpers) => {
|
|
2309
|
+
return {
|
|
2310
|
+
async findMatchingFlow(tx, type, service, ccId, netTotal, level = 1) {
|
|
2311
|
+
const result = await tx.$queryRaw(`
|
|
2312
|
+
SELECT af.id AS flowId,
|
|
2313
|
+
s.id AS stepId,
|
|
2314
|
+
s.level,
|
|
2315
|
+
s.min_amount AS minAmount,
|
|
2316
|
+
s.max_amount AS maxAmount,
|
|
2317
|
+
s.step_type AS stepType,
|
|
2318
|
+
af.service
|
|
2319
|
+
FROM core_approval_flow AS af
|
|
2320
|
+
JOIN core_approval_step AS s ON s.flow_id = af.id AND s.level = ${level}
|
|
2321
|
+
WHERE af.subject_type = ${type}
|
|
2322
|
+
AND af.service = ${service}
|
|
2323
|
+
AND af.is_active = TRUE
|
|
2324
|
+
AND s.is_active = TRUE
|
|
2325
|
+
AND ( (s.step_type = 'MIN_MAX'
|
|
2326
|
+
AND s.min_amount <= ${netTotal}
|
|
2327
|
+
AND s.max_amount >= ${netTotal})
|
|
2328
|
+
OR (s.step_type = 'NORMAL') )
|
|
2329
|
+
LIMIT 1; -- we expect exactly one matching step
|
|
2330
|
+
`);
|
|
2331
|
+
if (result.length === 0) {
|
|
2332
|
+
throw new helpers.ErrorHandler(400, "No matching flow found.");
|
|
2333
|
+
}
|
|
2334
|
+
return result[0];
|
|
2335
|
+
}
|
|
2336
|
+
};
|
|
2337
|
+
};
|
|
2338
|
+
|
|
2339
|
+
// src/types/approval.type.ts
|
|
2340
|
+
var ApprovalStatus = /* @__PURE__ */ ((ApprovalStatus2) => {
|
|
2341
|
+
ApprovalStatus2[ApprovalStatus2["PENDING"] = 0] = "PENDING";
|
|
2342
|
+
ApprovalStatus2[ApprovalStatus2["PARTIALLY_APPROVED"] = 1] = "PARTIALLY_APPROVED";
|
|
2343
|
+
ApprovalStatus2[ApprovalStatus2["APPROVED"] = 2] = "APPROVED";
|
|
2344
|
+
ApprovalStatus2[ApprovalStatus2["REJECTED"] = 3] = "REJECTED";
|
|
2345
|
+
ApprovalStatus2[ApprovalStatus2["CANCELLED"] = 4] = "CANCELLED";
|
|
2346
|
+
return ApprovalStatus2;
|
|
2347
|
+
})(ApprovalStatus || {});
|
|
2348
|
+
var FlowType = /* @__PURE__ */ ((FlowType3) => {
|
|
2349
|
+
FlowType3[FlowType3["STATUS"] = 0] = "STATUS";
|
|
2350
|
+
FlowType3[FlowType3["CO_PAY"] = 1] = "CO_PAY";
|
|
2351
|
+
FlowType3[FlowType3["INDIVIDUAL_DISCOUNT"] = 2] = "INDIVIDUAL_DISCOUNT";
|
|
2352
|
+
FlowType3[FlowType3["OVERALL_DISCOUNT"] = 3] = "OVERALL_DISCOUNT";
|
|
2353
|
+
FlowType3[FlowType3["SPECIAL_DISCOUNT"] = 4] = "SPECIAL_DISCOUNT";
|
|
2354
|
+
FlowType3[FlowType3["REFUND"] = 5] = "REFUND";
|
|
2355
|
+
return FlowType3;
|
|
2356
|
+
})(FlowType || {});
|
|
2357
|
+
|
|
2358
|
+
// src/services/approval.service.ts
|
|
2359
|
+
var ApprovalService = class {
|
|
2360
|
+
constructor(deps) {
|
|
2361
|
+
this.deps = deps;
|
|
2362
|
+
this.approvalRepo = approvalRepository(deps.helpers);
|
|
2363
|
+
}
|
|
2364
|
+
deps;
|
|
2365
|
+
approvalRepo;
|
|
2366
|
+
async startFlow(tx, { service, subjectType, subjectId, netTotal, ccId, refNo, level = 1, extra }) {
|
|
2367
|
+
const store = this.deps.requestStorage.getStore();
|
|
2368
|
+
const currentUser = store?.user?.id;
|
|
2369
|
+
const flow = await this.approvalRepo.findMatchingFlow(tx, subjectType, service, ccId, netTotal, level);
|
|
2370
|
+
if (!flow) throw new Error("No approval flow configured");
|
|
2371
|
+
await tx.approvalInstance.updateMany({
|
|
2372
|
+
where: {
|
|
2373
|
+
service: flow.service,
|
|
2374
|
+
subjectType,
|
|
2375
|
+
subjectId
|
|
2376
|
+
},
|
|
2377
|
+
data: {
|
|
2378
|
+
isActive: false,
|
|
2379
|
+
updatedBy: currentUser
|
|
2380
|
+
}
|
|
2381
|
+
});
|
|
2382
|
+
const inst = await tx.approvalInstance.create({
|
|
2383
|
+
data: {
|
|
2384
|
+
flowId: flow.flowId,
|
|
2385
|
+
service: flow.service,
|
|
2386
|
+
subjectType,
|
|
2387
|
+
subjectId,
|
|
2388
|
+
currentStep: flow.stepId,
|
|
2389
|
+
netTotal,
|
|
2390
|
+
refNo,
|
|
2391
|
+
extra,
|
|
2392
|
+
createdBy: currentUser
|
|
2393
|
+
}
|
|
2394
|
+
});
|
|
2395
|
+
const approvers = await tx.approverMapping.findMany({
|
|
2396
|
+
where: { stepId: flow.stepId, ccId, isActive: true }
|
|
2397
|
+
});
|
|
2398
|
+
this.deps.eventBus.emit("approval:LEVEL_READY", {
|
|
2399
|
+
instanceId: inst.id,
|
|
2400
|
+
subjectType: inst.subjectType,
|
|
2401
|
+
service: inst.service,
|
|
2402
|
+
subjectId: inst.subjectId,
|
|
2403
|
+
level: 1,
|
|
2404
|
+
approvers,
|
|
2405
|
+
ccId
|
|
2406
|
+
});
|
|
2407
|
+
}
|
|
2408
|
+
async lastLevel(steps) {
|
|
2409
|
+
if (steps.length === 0) throw new Error("No steps defined in the approval flow");
|
|
2410
|
+
return Math.max(...steps.map((s) => s.level));
|
|
2411
|
+
}
|
|
2412
|
+
/** Approver clicks “Approve” or “Reject”. */
|
|
2413
|
+
async act({ instanceId, approverId, action, ccId, comment }) {
|
|
2414
|
+
return this.deps.prisma.$transaction(async (tx) => {
|
|
2415
|
+
const inst = await tx.approvalInstance.findUnique({
|
|
2416
|
+
where: { id: instanceId },
|
|
2417
|
+
include: { flow: { where: { isActive: true }, include: { steps: { where: { isActive: true } } } } }
|
|
2418
|
+
});
|
|
2419
|
+
if (!inst) throw new this.deps.helpers.ErrorHandler(400, "Approval instance not found");
|
|
2420
|
+
if (!inst.flow) throw new this.deps.helpers.ErrorHandler(400, "Approval flow not found");
|
|
2421
|
+
const step = inst.flow.steps.find((s) => s.id === inst.currentStep);
|
|
2422
|
+
if (!step) throw new this.deps.helpers.ErrorHandler(400, "Current step not found in the flow");
|
|
2423
|
+
await this.assertPermission(step, approverId, instanceId, ccId, tx);
|
|
2424
|
+
inst.flow.steps = inst.flow.steps.filter((s) => {
|
|
2425
|
+
return s.stepType === "NORMAL" || s.stepType === "MIN_MAX" && Number(inst.netTotal) >= Number(s.minAmount) && Number(inst.netTotal) <= Number(s.maxAmount);
|
|
2426
|
+
});
|
|
2427
|
+
const lastLevel = await this.lastLevel(inst.flow.steps);
|
|
2428
|
+
const newStatus = action === "REJECT" ? 3 /* REJECTED */ : step.level === lastLevel ? 2 /* APPROVED */ : 1 /* PARTIALLY_APPROVED */;
|
|
2429
|
+
await tx.approvalAction.create({
|
|
2430
|
+
data: {
|
|
2431
|
+
instanceId,
|
|
2432
|
+
level: step.level,
|
|
2433
|
+
actedBy: approverId,
|
|
2434
|
+
comment,
|
|
2435
|
+
statusAfter: newStatus
|
|
2436
|
+
}
|
|
2437
|
+
});
|
|
2438
|
+
let newFlow = null;
|
|
2439
|
+
if (newStatus === 1 /* PARTIALLY_APPROVED */) {
|
|
2440
|
+
newFlow = await this.approvalRepo.findMatchingFlow(
|
|
2441
|
+
tx,
|
|
2442
|
+
inst.flow.subjectType,
|
|
2443
|
+
inst.flow.service,
|
|
2444
|
+
ccId,
|
|
2445
|
+
Number(inst.netTotal || 0),
|
|
2446
|
+
step.level + 1
|
|
2447
|
+
);
|
|
2448
|
+
if (!newFlow) {
|
|
2449
|
+
throw new this.deps.helpers.ErrorHandler(400, "No next step found for the approval flow");
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
const updated = await tx.approvalInstance.update({
|
|
2453
|
+
where: { id: instanceId },
|
|
2454
|
+
data: {
|
|
2455
|
+
currentStep: newStatus === 1 /* PARTIALLY_APPROVED */ ? newFlow?.stepId : step.id,
|
|
2456
|
+
status: newStatus
|
|
2457
|
+
}
|
|
2458
|
+
});
|
|
2459
|
+
setImmediate(() => this.emitEvents(updated, inst.flow?.flowType, approverId, step, comment));
|
|
2460
|
+
this.deps.eventBus.emit("approval:LEVEL_DONE", {
|
|
2461
|
+
instanceId: inst.id,
|
|
2462
|
+
level: step.level,
|
|
2463
|
+
actedBy: approverId,
|
|
2464
|
+
action,
|
|
2465
|
+
comment
|
|
2466
|
+
});
|
|
2467
|
+
if (newStatus === 1 /* PARTIALLY_APPROVED */) {
|
|
2468
|
+
const nextLevel = step.level + 1;
|
|
2469
|
+
const approvers = await tx.approverMapping.findMany({
|
|
2470
|
+
where: { stepId: updated.currentStep, ccId, isActive: true }
|
|
2471
|
+
});
|
|
2472
|
+
this.deps.eventBus.emit("approval:LEVEL_READY", {
|
|
2473
|
+
instanceId: inst.id,
|
|
2474
|
+
subjectType: inst.subjectType,
|
|
2475
|
+
service: inst.service,
|
|
2476
|
+
subjectId: inst.subjectId,
|
|
2477
|
+
level: nextLevel,
|
|
2478
|
+
approvers,
|
|
2479
|
+
ccId
|
|
2480
|
+
});
|
|
2481
|
+
}
|
|
2482
|
+
return updated;
|
|
2483
|
+
});
|
|
2484
|
+
}
|
|
2485
|
+
/* ------------ private helpers ------------ */
|
|
2486
|
+
// private async advance(id: number, ccId: number, stepId: number, tx: PrismaClient | PrismaTransactionClient = this.deps.prisma) {
|
|
2487
|
+
// console.log(`Advancing approval instance ${id}`);
|
|
2488
|
+
// // // Fetch the approval instance by its id, including associated flow steps
|
|
2489
|
+
// const inst = await tx.approvalInstance.findUniqueOrThrow({
|
|
2490
|
+
// where: { id },
|
|
2491
|
+
// include: { flow: { include: { steps: true } } },
|
|
2492
|
+
// });
|
|
2493
|
+
// if (!inst) throw new this.deps.helpers.ErrorHandler(400, "Approval instance not found");
|
|
2494
|
+
// if (!inst.flow) throw new this.deps.helpers.ErrorHandler(400, "Approval flow not found");
|
|
2495
|
+
// // Get the current step for the instance
|
|
2496
|
+
// let nextLevel: number;
|
|
2497
|
+
// if (inst.currentLevel === 0) {
|
|
2498
|
+
// nextLevel = 1;
|
|
2499
|
+
// } else {
|
|
2500
|
+
// const currentStep = inst.flow.steps.find((s) => s.level === inst.currentLevel);
|
|
2501
|
+
// if (!currentStep) throw new Error(`Invalid level ${inst.currentLevel} for the instance`);
|
|
2502
|
+
// // If this step is done (e.g., approved or rejected), move on to the next level
|
|
2503
|
+
// nextLevel = inst.currentLevel + 1;
|
|
2504
|
+
// }
|
|
2505
|
+
// // Check if there is a next level defined
|
|
2506
|
+
// const nextStep = inst.flow.steps.find((s) => s.level === nextLevel);
|
|
2507
|
+
// if (nextStep) {
|
|
2508
|
+
// // Update the instance to move to the next level
|
|
2509
|
+
// await tx.approvalInstance.update({
|
|
2510
|
+
// where: { id },
|
|
2511
|
+
// data: {
|
|
2512
|
+
// currentLevel: nextLevel,
|
|
2513
|
+
// status: "PENDING", // Reset to "PENDING" as we are progressing the approval to the next level
|
|
2514
|
+
// },
|
|
2515
|
+
// });
|
|
2516
|
+
// const approvers = await tx.approverMapping.findMany({
|
|
2517
|
+
// where: { stepId: nextStep.id, ccId, isActive: true },
|
|
2518
|
+
// });
|
|
2519
|
+
// // Emit event for the next level approvers
|
|
2520
|
+
// this.deps.eventBus.emit("approval:LEVEL_READY", {
|
|
2521
|
+
// instanceId: inst.id,
|
|
2522
|
+
// subjectType: inst.subjectType,
|
|
2523
|
+
// subjectId: inst.subjectId,
|
|
2524
|
+
// level: nextLevel,
|
|
2525
|
+
// approvers: approvers,
|
|
2526
|
+
// ccId,
|
|
2527
|
+
// });
|
|
2528
|
+
// } else {
|
|
2529
|
+
// // If no next step, mark the instance as fully approved (completed)
|
|
2530
|
+
// await tx.approvalInstance.update({
|
|
2531
|
+
// where: { id },
|
|
2532
|
+
// data: { status: "APPROVED" }, // or REJECTED if the final level is not approved
|
|
2533
|
+
// });
|
|
2534
|
+
// // Emit event for final approval
|
|
2535
|
+
// this.deps.eventBus.emit("approval:APPROVED", {
|
|
2536
|
+
// instanceId: inst.id,
|
|
2537
|
+
// subjectType: inst.subjectType,
|
|
2538
|
+
// subjectId: inst.subjectId,
|
|
2539
|
+
// });
|
|
2540
|
+
// }
|
|
2541
|
+
// }
|
|
2542
|
+
emitEvents(instance, flowType, approverId, step, comment) {
|
|
2543
|
+
this.deps.eventBus.emit(`approval:${instance.status}`, {
|
|
2544
|
+
instanceId: instance.id,
|
|
2545
|
+
flowType,
|
|
2546
|
+
subjectId: instance.subjectId,
|
|
2547
|
+
approverId,
|
|
2548
|
+
step,
|
|
2549
|
+
comment,
|
|
2550
|
+
subjectType: instance.subjectType,
|
|
2551
|
+
service: instance.service
|
|
2552
|
+
});
|
|
2553
|
+
}
|
|
2554
|
+
async assertPermission(step, approverId, instanceId, ccId, tx) {
|
|
2555
|
+
const result = await tx.$queryRaw(`
|
|
2556
|
+
SELECT COUNT(*) AS count
|
|
2557
|
+
FROM (
|
|
2558
|
+
SELECT am.staff_id AS staff_id
|
|
2559
|
+
FROM core_approver_mapping am
|
|
2560
|
+
WHERE am.step_id = ${step.id}
|
|
2561
|
+
AND am.is_active = TRUE
|
|
2562
|
+
AND am.cc_id = ${ccId}
|
|
2563
|
+
AND am.staff_id = ${approverId}
|
|
2564
|
+
|
|
2565
|
+
UNION
|
|
2566
|
+
|
|
2567
|
+
SELECT scc.staff_id AS staff_id
|
|
2568
|
+
FROM core_approver_mapping am
|
|
2569
|
+
LEFT JOIN staff_roles sr
|
|
2570
|
+
ON sr.role_id = am.role_id
|
|
2571
|
+
LEFT JOIN staff_collection_center scc
|
|
2572
|
+
ON scc.collection_center_id = am.cc_id
|
|
2573
|
+
AND scc.staff_id = sr.staff_id
|
|
2574
|
+
WHERE am.step_id = ${step.id}
|
|
2575
|
+
AND am.is_active = TRUE
|
|
2576
|
+
AND am.cc_id = ${ccId}
|
|
2577
|
+
AND scc.staff_id = ${approverId}
|
|
2578
|
+
) AS staff_union;
|
|
2579
|
+
`);
|
|
2580
|
+
if (Number(result[0].count) === 0) {
|
|
2581
|
+
throw new this.deps.helpers.ErrorHandler(403, "You are not allowed to act on this approval step");
|
|
2582
|
+
}
|
|
2583
|
+
const existingActsQuery = `
|
|
2584
|
+
SELECT COUNT(*) AS count
|
|
2585
|
+
FROM core_approval_action a
|
|
2586
|
+
JOIN core_approval_instance ai ON ai.id = a.instance_id
|
|
2587
|
+
WHERE ai.id = ?
|
|
2588
|
+
AND a.level = ?
|
|
2589
|
+
AND a.acted_by = ?
|
|
2590
|
+
AND a.is_active = true
|
|
2591
|
+
AND ai.is_active = true
|
|
2592
|
+
`;
|
|
2593
|
+
const actionsResult = await tx.$queryRawUnsafe(existingActsQuery, instanceId, step.level, approverId);
|
|
2594
|
+
if (Number(actionsResult[0].count) > 0) {
|
|
2595
|
+
throw new this.deps.helpers.ErrorHandler(409, "You have already submitted a decision for this level");
|
|
2596
|
+
}
|
|
2597
|
+
const prevApproversQuery = `
|
|
2598
|
+
SELECT COUNT(*) AS count
|
|
2599
|
+
FROM core_approval_action a
|
|
2600
|
+
JOIN core_approval_instance ai ON ai.id = a.instance_id
|
|
2601
|
+
WHERE ai.id = ?
|
|
2602
|
+
AND a.level < ?
|
|
2603
|
+
AND a.is_active = true
|
|
2604
|
+
AND ai.is_active = true
|
|
2605
|
+
AND a.acted_by IS NOT NULL
|
|
2606
|
+
`;
|
|
2607
|
+
const prevActionsResult = await tx.$queryRawUnsafe(prevApproversQuery, instanceId, step.level);
|
|
2608
|
+
if (Number(prevActionsResult[0].count) !== step.level - 1) {
|
|
2609
|
+
throw new this.deps.helpers.ErrorHandler(403, "You must wait for previous approvers to act first");
|
|
2610
|
+
}
|
|
2611
|
+
return step;
|
|
2612
|
+
}
|
|
2613
|
+
};
|
|
2614
|
+
|
|
2303
2615
|
// src/utils/audit.utils.ts
|
|
2304
2616
|
function isValidDate(value) {
|
|
2305
2617
|
if (value instanceof Date) {
|
|
@@ -4294,10 +4606,14 @@ var AuditProxy = class {
|
|
|
4294
4606
|
};
|
|
4295
4607
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4296
4608
|
0 && (module.exports = {
|
|
4609
|
+
ApprovalService,
|
|
4610
|
+
ApprovalStatus,
|
|
4297
4611
|
AuditCore,
|
|
4298
4612
|
AuditLogger,
|
|
4299
4613
|
AuditProxy,
|
|
4614
|
+
FlowType,
|
|
4300
4615
|
NotificationEmitter,
|
|
4616
|
+
approvalRepository,
|
|
4301
4617
|
commonService,
|
|
4302
4618
|
convertArrayPatternToEachBlocksGeneric,
|
|
4303
4619
|
customOmit,
|
package/dist/index.mjs
CHANGED
|
@@ -2244,6 +2244,314 @@ var commonService = (serviceDeps) => {
|
|
|
2244
2244
|
};
|
|
2245
2245
|
};
|
|
2246
2246
|
|
|
2247
|
+
// src/repository/approval.repository.ts
|
|
2248
|
+
var approvalRepository = (helpers) => {
|
|
2249
|
+
return {
|
|
2250
|
+
async findMatchingFlow(tx, type, service, ccId, netTotal, level = 1) {
|
|
2251
|
+
const result = await tx.$queryRaw(`
|
|
2252
|
+
SELECT af.id AS flowId,
|
|
2253
|
+
s.id AS stepId,
|
|
2254
|
+
s.level,
|
|
2255
|
+
s.min_amount AS minAmount,
|
|
2256
|
+
s.max_amount AS maxAmount,
|
|
2257
|
+
s.step_type AS stepType,
|
|
2258
|
+
af.service
|
|
2259
|
+
FROM core_approval_flow AS af
|
|
2260
|
+
JOIN core_approval_step AS s ON s.flow_id = af.id AND s.level = ${level}
|
|
2261
|
+
WHERE af.subject_type = ${type}
|
|
2262
|
+
AND af.service = ${service}
|
|
2263
|
+
AND af.is_active = TRUE
|
|
2264
|
+
AND s.is_active = TRUE
|
|
2265
|
+
AND ( (s.step_type = 'MIN_MAX'
|
|
2266
|
+
AND s.min_amount <= ${netTotal}
|
|
2267
|
+
AND s.max_amount >= ${netTotal})
|
|
2268
|
+
OR (s.step_type = 'NORMAL') )
|
|
2269
|
+
LIMIT 1; -- we expect exactly one matching step
|
|
2270
|
+
`);
|
|
2271
|
+
if (result.length === 0) {
|
|
2272
|
+
throw new helpers.ErrorHandler(400, "No matching flow found.");
|
|
2273
|
+
}
|
|
2274
|
+
return result[0];
|
|
2275
|
+
}
|
|
2276
|
+
};
|
|
2277
|
+
};
|
|
2278
|
+
|
|
2279
|
+
// src/types/approval.type.ts
|
|
2280
|
+
var ApprovalStatus = /* @__PURE__ */ ((ApprovalStatus2) => {
|
|
2281
|
+
ApprovalStatus2[ApprovalStatus2["PENDING"] = 0] = "PENDING";
|
|
2282
|
+
ApprovalStatus2[ApprovalStatus2["PARTIALLY_APPROVED"] = 1] = "PARTIALLY_APPROVED";
|
|
2283
|
+
ApprovalStatus2[ApprovalStatus2["APPROVED"] = 2] = "APPROVED";
|
|
2284
|
+
ApprovalStatus2[ApprovalStatus2["REJECTED"] = 3] = "REJECTED";
|
|
2285
|
+
ApprovalStatus2[ApprovalStatus2["CANCELLED"] = 4] = "CANCELLED";
|
|
2286
|
+
return ApprovalStatus2;
|
|
2287
|
+
})(ApprovalStatus || {});
|
|
2288
|
+
var FlowType = /* @__PURE__ */ ((FlowType3) => {
|
|
2289
|
+
FlowType3[FlowType3["STATUS"] = 0] = "STATUS";
|
|
2290
|
+
FlowType3[FlowType3["CO_PAY"] = 1] = "CO_PAY";
|
|
2291
|
+
FlowType3[FlowType3["INDIVIDUAL_DISCOUNT"] = 2] = "INDIVIDUAL_DISCOUNT";
|
|
2292
|
+
FlowType3[FlowType3["OVERALL_DISCOUNT"] = 3] = "OVERALL_DISCOUNT";
|
|
2293
|
+
FlowType3[FlowType3["SPECIAL_DISCOUNT"] = 4] = "SPECIAL_DISCOUNT";
|
|
2294
|
+
FlowType3[FlowType3["REFUND"] = 5] = "REFUND";
|
|
2295
|
+
return FlowType3;
|
|
2296
|
+
})(FlowType || {});
|
|
2297
|
+
|
|
2298
|
+
// src/services/approval.service.ts
|
|
2299
|
+
var ApprovalService = class {
|
|
2300
|
+
constructor(deps) {
|
|
2301
|
+
this.deps = deps;
|
|
2302
|
+
this.approvalRepo = approvalRepository(deps.helpers);
|
|
2303
|
+
}
|
|
2304
|
+
deps;
|
|
2305
|
+
approvalRepo;
|
|
2306
|
+
async startFlow(tx, { service, subjectType, subjectId, netTotal, ccId, refNo, level = 1, extra }) {
|
|
2307
|
+
const store = this.deps.requestStorage.getStore();
|
|
2308
|
+
const currentUser = store?.user?.id;
|
|
2309
|
+
const flow = await this.approvalRepo.findMatchingFlow(tx, subjectType, service, ccId, netTotal, level);
|
|
2310
|
+
if (!flow) throw new Error("No approval flow configured");
|
|
2311
|
+
await tx.approvalInstance.updateMany({
|
|
2312
|
+
where: {
|
|
2313
|
+
service: flow.service,
|
|
2314
|
+
subjectType,
|
|
2315
|
+
subjectId
|
|
2316
|
+
},
|
|
2317
|
+
data: {
|
|
2318
|
+
isActive: false,
|
|
2319
|
+
updatedBy: currentUser
|
|
2320
|
+
}
|
|
2321
|
+
});
|
|
2322
|
+
const inst = await tx.approvalInstance.create({
|
|
2323
|
+
data: {
|
|
2324
|
+
flowId: flow.flowId,
|
|
2325
|
+
service: flow.service,
|
|
2326
|
+
subjectType,
|
|
2327
|
+
subjectId,
|
|
2328
|
+
currentStep: flow.stepId,
|
|
2329
|
+
netTotal,
|
|
2330
|
+
refNo,
|
|
2331
|
+
extra,
|
|
2332
|
+
createdBy: currentUser
|
|
2333
|
+
}
|
|
2334
|
+
});
|
|
2335
|
+
const approvers = await tx.approverMapping.findMany({
|
|
2336
|
+
where: { stepId: flow.stepId, ccId, isActive: true }
|
|
2337
|
+
});
|
|
2338
|
+
this.deps.eventBus.emit("approval:LEVEL_READY", {
|
|
2339
|
+
instanceId: inst.id,
|
|
2340
|
+
subjectType: inst.subjectType,
|
|
2341
|
+
service: inst.service,
|
|
2342
|
+
subjectId: inst.subjectId,
|
|
2343
|
+
level: 1,
|
|
2344
|
+
approvers,
|
|
2345
|
+
ccId
|
|
2346
|
+
});
|
|
2347
|
+
}
|
|
2348
|
+
async lastLevel(steps) {
|
|
2349
|
+
if (steps.length === 0) throw new Error("No steps defined in the approval flow");
|
|
2350
|
+
return Math.max(...steps.map((s) => s.level));
|
|
2351
|
+
}
|
|
2352
|
+
/** Approver clicks “Approve” or “Reject”. */
|
|
2353
|
+
async act({ instanceId, approverId, action, ccId, comment }) {
|
|
2354
|
+
return this.deps.prisma.$transaction(async (tx) => {
|
|
2355
|
+
const inst = await tx.approvalInstance.findUnique({
|
|
2356
|
+
where: { id: instanceId },
|
|
2357
|
+
include: { flow: { where: { isActive: true }, include: { steps: { where: { isActive: true } } } } }
|
|
2358
|
+
});
|
|
2359
|
+
if (!inst) throw new this.deps.helpers.ErrorHandler(400, "Approval instance not found");
|
|
2360
|
+
if (!inst.flow) throw new this.deps.helpers.ErrorHandler(400, "Approval flow not found");
|
|
2361
|
+
const step = inst.flow.steps.find((s) => s.id === inst.currentStep);
|
|
2362
|
+
if (!step) throw new this.deps.helpers.ErrorHandler(400, "Current step not found in the flow");
|
|
2363
|
+
await this.assertPermission(step, approverId, instanceId, ccId, tx);
|
|
2364
|
+
inst.flow.steps = inst.flow.steps.filter((s) => {
|
|
2365
|
+
return s.stepType === "NORMAL" || s.stepType === "MIN_MAX" && Number(inst.netTotal) >= Number(s.minAmount) && Number(inst.netTotal) <= Number(s.maxAmount);
|
|
2366
|
+
});
|
|
2367
|
+
const lastLevel = await this.lastLevel(inst.flow.steps);
|
|
2368
|
+
const newStatus = action === "REJECT" ? 3 /* REJECTED */ : step.level === lastLevel ? 2 /* APPROVED */ : 1 /* PARTIALLY_APPROVED */;
|
|
2369
|
+
await tx.approvalAction.create({
|
|
2370
|
+
data: {
|
|
2371
|
+
instanceId,
|
|
2372
|
+
level: step.level,
|
|
2373
|
+
actedBy: approverId,
|
|
2374
|
+
comment,
|
|
2375
|
+
statusAfter: newStatus
|
|
2376
|
+
}
|
|
2377
|
+
});
|
|
2378
|
+
let newFlow = null;
|
|
2379
|
+
if (newStatus === 1 /* PARTIALLY_APPROVED */) {
|
|
2380
|
+
newFlow = await this.approvalRepo.findMatchingFlow(
|
|
2381
|
+
tx,
|
|
2382
|
+
inst.flow.subjectType,
|
|
2383
|
+
inst.flow.service,
|
|
2384
|
+
ccId,
|
|
2385
|
+
Number(inst.netTotal || 0),
|
|
2386
|
+
step.level + 1
|
|
2387
|
+
);
|
|
2388
|
+
if (!newFlow) {
|
|
2389
|
+
throw new this.deps.helpers.ErrorHandler(400, "No next step found for the approval flow");
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
const updated = await tx.approvalInstance.update({
|
|
2393
|
+
where: { id: instanceId },
|
|
2394
|
+
data: {
|
|
2395
|
+
currentStep: newStatus === 1 /* PARTIALLY_APPROVED */ ? newFlow?.stepId : step.id,
|
|
2396
|
+
status: newStatus
|
|
2397
|
+
}
|
|
2398
|
+
});
|
|
2399
|
+
setImmediate(() => this.emitEvents(updated, inst.flow?.flowType, approverId, step, comment));
|
|
2400
|
+
this.deps.eventBus.emit("approval:LEVEL_DONE", {
|
|
2401
|
+
instanceId: inst.id,
|
|
2402
|
+
level: step.level,
|
|
2403
|
+
actedBy: approverId,
|
|
2404
|
+
action,
|
|
2405
|
+
comment
|
|
2406
|
+
});
|
|
2407
|
+
if (newStatus === 1 /* PARTIALLY_APPROVED */) {
|
|
2408
|
+
const nextLevel = step.level + 1;
|
|
2409
|
+
const approvers = await tx.approverMapping.findMany({
|
|
2410
|
+
where: { stepId: updated.currentStep, ccId, isActive: true }
|
|
2411
|
+
});
|
|
2412
|
+
this.deps.eventBus.emit("approval:LEVEL_READY", {
|
|
2413
|
+
instanceId: inst.id,
|
|
2414
|
+
subjectType: inst.subjectType,
|
|
2415
|
+
service: inst.service,
|
|
2416
|
+
subjectId: inst.subjectId,
|
|
2417
|
+
level: nextLevel,
|
|
2418
|
+
approvers,
|
|
2419
|
+
ccId
|
|
2420
|
+
});
|
|
2421
|
+
}
|
|
2422
|
+
return updated;
|
|
2423
|
+
});
|
|
2424
|
+
}
|
|
2425
|
+
/* ------------ private helpers ------------ */
|
|
2426
|
+
// private async advance(id: number, ccId: number, stepId: number, tx: PrismaClient | PrismaTransactionClient = this.deps.prisma) {
|
|
2427
|
+
// console.log(`Advancing approval instance ${id}`);
|
|
2428
|
+
// // // Fetch the approval instance by its id, including associated flow steps
|
|
2429
|
+
// const inst = await tx.approvalInstance.findUniqueOrThrow({
|
|
2430
|
+
// where: { id },
|
|
2431
|
+
// include: { flow: { include: { steps: true } } },
|
|
2432
|
+
// });
|
|
2433
|
+
// if (!inst) throw new this.deps.helpers.ErrorHandler(400, "Approval instance not found");
|
|
2434
|
+
// if (!inst.flow) throw new this.deps.helpers.ErrorHandler(400, "Approval flow not found");
|
|
2435
|
+
// // Get the current step for the instance
|
|
2436
|
+
// let nextLevel: number;
|
|
2437
|
+
// if (inst.currentLevel === 0) {
|
|
2438
|
+
// nextLevel = 1;
|
|
2439
|
+
// } else {
|
|
2440
|
+
// const currentStep = inst.flow.steps.find((s) => s.level === inst.currentLevel);
|
|
2441
|
+
// if (!currentStep) throw new Error(`Invalid level ${inst.currentLevel} for the instance`);
|
|
2442
|
+
// // If this step is done (e.g., approved or rejected), move on to the next level
|
|
2443
|
+
// nextLevel = inst.currentLevel + 1;
|
|
2444
|
+
// }
|
|
2445
|
+
// // Check if there is a next level defined
|
|
2446
|
+
// const nextStep = inst.flow.steps.find((s) => s.level === nextLevel);
|
|
2447
|
+
// if (nextStep) {
|
|
2448
|
+
// // Update the instance to move to the next level
|
|
2449
|
+
// await tx.approvalInstance.update({
|
|
2450
|
+
// where: { id },
|
|
2451
|
+
// data: {
|
|
2452
|
+
// currentLevel: nextLevel,
|
|
2453
|
+
// status: "PENDING", // Reset to "PENDING" as we are progressing the approval to the next level
|
|
2454
|
+
// },
|
|
2455
|
+
// });
|
|
2456
|
+
// const approvers = await tx.approverMapping.findMany({
|
|
2457
|
+
// where: { stepId: nextStep.id, ccId, isActive: true },
|
|
2458
|
+
// });
|
|
2459
|
+
// // Emit event for the next level approvers
|
|
2460
|
+
// this.deps.eventBus.emit("approval:LEVEL_READY", {
|
|
2461
|
+
// instanceId: inst.id,
|
|
2462
|
+
// subjectType: inst.subjectType,
|
|
2463
|
+
// subjectId: inst.subjectId,
|
|
2464
|
+
// level: nextLevel,
|
|
2465
|
+
// approvers: approvers,
|
|
2466
|
+
// ccId,
|
|
2467
|
+
// });
|
|
2468
|
+
// } else {
|
|
2469
|
+
// // If no next step, mark the instance as fully approved (completed)
|
|
2470
|
+
// await tx.approvalInstance.update({
|
|
2471
|
+
// where: { id },
|
|
2472
|
+
// data: { status: "APPROVED" }, // or REJECTED if the final level is not approved
|
|
2473
|
+
// });
|
|
2474
|
+
// // Emit event for final approval
|
|
2475
|
+
// this.deps.eventBus.emit("approval:APPROVED", {
|
|
2476
|
+
// instanceId: inst.id,
|
|
2477
|
+
// subjectType: inst.subjectType,
|
|
2478
|
+
// subjectId: inst.subjectId,
|
|
2479
|
+
// });
|
|
2480
|
+
// }
|
|
2481
|
+
// }
|
|
2482
|
+
emitEvents(instance, flowType, approverId, step, comment) {
|
|
2483
|
+
this.deps.eventBus.emit(`approval:${instance.status}`, {
|
|
2484
|
+
instanceId: instance.id,
|
|
2485
|
+
flowType,
|
|
2486
|
+
subjectId: instance.subjectId,
|
|
2487
|
+
approverId,
|
|
2488
|
+
step,
|
|
2489
|
+
comment,
|
|
2490
|
+
subjectType: instance.subjectType,
|
|
2491
|
+
service: instance.service
|
|
2492
|
+
});
|
|
2493
|
+
}
|
|
2494
|
+
async assertPermission(step, approverId, instanceId, ccId, tx) {
|
|
2495
|
+
const result = await tx.$queryRaw(`
|
|
2496
|
+
SELECT COUNT(*) AS count
|
|
2497
|
+
FROM (
|
|
2498
|
+
SELECT am.staff_id AS staff_id
|
|
2499
|
+
FROM core_approver_mapping am
|
|
2500
|
+
WHERE am.step_id = ${step.id}
|
|
2501
|
+
AND am.is_active = TRUE
|
|
2502
|
+
AND am.cc_id = ${ccId}
|
|
2503
|
+
AND am.staff_id = ${approverId}
|
|
2504
|
+
|
|
2505
|
+
UNION
|
|
2506
|
+
|
|
2507
|
+
SELECT scc.staff_id AS staff_id
|
|
2508
|
+
FROM core_approver_mapping am
|
|
2509
|
+
LEFT JOIN staff_roles sr
|
|
2510
|
+
ON sr.role_id = am.role_id
|
|
2511
|
+
LEFT JOIN staff_collection_center scc
|
|
2512
|
+
ON scc.collection_center_id = am.cc_id
|
|
2513
|
+
AND scc.staff_id = sr.staff_id
|
|
2514
|
+
WHERE am.step_id = ${step.id}
|
|
2515
|
+
AND am.is_active = TRUE
|
|
2516
|
+
AND am.cc_id = ${ccId}
|
|
2517
|
+
AND scc.staff_id = ${approverId}
|
|
2518
|
+
) AS staff_union;
|
|
2519
|
+
`);
|
|
2520
|
+
if (Number(result[0].count) === 0) {
|
|
2521
|
+
throw new this.deps.helpers.ErrorHandler(403, "You are not allowed to act on this approval step");
|
|
2522
|
+
}
|
|
2523
|
+
const existingActsQuery = `
|
|
2524
|
+
SELECT COUNT(*) AS count
|
|
2525
|
+
FROM core_approval_action a
|
|
2526
|
+
JOIN core_approval_instance ai ON ai.id = a.instance_id
|
|
2527
|
+
WHERE ai.id = ?
|
|
2528
|
+
AND a.level = ?
|
|
2529
|
+
AND a.acted_by = ?
|
|
2530
|
+
AND a.is_active = true
|
|
2531
|
+
AND ai.is_active = true
|
|
2532
|
+
`;
|
|
2533
|
+
const actionsResult = await tx.$queryRawUnsafe(existingActsQuery, instanceId, step.level, approverId);
|
|
2534
|
+
if (Number(actionsResult[0].count) > 0) {
|
|
2535
|
+
throw new this.deps.helpers.ErrorHandler(409, "You have already submitted a decision for this level");
|
|
2536
|
+
}
|
|
2537
|
+
const prevApproversQuery = `
|
|
2538
|
+
SELECT COUNT(*) AS count
|
|
2539
|
+
FROM core_approval_action a
|
|
2540
|
+
JOIN core_approval_instance ai ON ai.id = a.instance_id
|
|
2541
|
+
WHERE ai.id = ?
|
|
2542
|
+
AND a.level < ?
|
|
2543
|
+
AND a.is_active = true
|
|
2544
|
+
AND ai.is_active = true
|
|
2545
|
+
AND a.acted_by IS NOT NULL
|
|
2546
|
+
`;
|
|
2547
|
+
const prevActionsResult = await tx.$queryRawUnsafe(prevApproversQuery, instanceId, step.level);
|
|
2548
|
+
if (Number(prevActionsResult[0].count) !== step.level - 1) {
|
|
2549
|
+
throw new this.deps.helpers.ErrorHandler(403, "You must wait for previous approvers to act first");
|
|
2550
|
+
}
|
|
2551
|
+
return step;
|
|
2552
|
+
}
|
|
2553
|
+
};
|
|
2554
|
+
|
|
2247
2555
|
// src/utils/audit.utils.ts
|
|
2248
2556
|
function isValidDate(value) {
|
|
2249
2557
|
if (value instanceof Date) {
|
|
@@ -4237,10 +4545,14 @@ var AuditProxy = class {
|
|
|
4237
4545
|
}
|
|
4238
4546
|
};
|
|
4239
4547
|
export {
|
|
4548
|
+
ApprovalService,
|
|
4549
|
+
ApprovalStatus,
|
|
4240
4550
|
AuditCore,
|
|
4241
4551
|
AuditLogger,
|
|
4242
4552
|
AuditProxy,
|
|
4553
|
+
FlowType,
|
|
4243
4554
|
NotificationEmitter,
|
|
4555
|
+
approvalRepository,
|
|
4244
4556
|
commonService,
|
|
4245
4557
|
convertArrayPatternToEachBlocksGeneric,
|
|
4246
4558
|
customOmit,
|