Multi Approval Configurator - Business central customized solution for approval management

 

Multi Approval Configurator

Business Requirement

Approval must be provided for the Sales document, Purchase document, Service document, and customer Approval based on the Department, Business Type, Order type, and values. Each Department, Order type, and value will have different approval hierarchies. These approval parameters vary across the business units.

To handle delegation of the approvers on the vacation/holidays time. Based on the leave calendar. Delegation can be done to different users each time, which needs to be recorded.

Technical Design


We have customized the workflow with a configurator, allowing users to define multiple combinations of document types.

i.e. Department code is dimension 1, Order type is master data, and Values are calculated from the document.

Each line of combination will have a set of workflow user groups. We will configure a standard workflow template and define a blank workflow group in the approval setup.

We can define a workflow template from sales order approval and assign the workflow type as a workflow user group. Define a common workflow user group and select it.

We must use the Event in Approval Management code unit to change the workflow user group code based on the combination of approvals.

OnBeforeCreateApprReqForApprTypeWorkflowUserGroup : Use this event to change the workflow group code to assign customized.

We need to use the same logic to skip the approval process at the time of the manual release of the document.

Delegation:

Declaration of vacation is captured in a leave calendar table for the start and end dates. Delegate User details also will be captured in the table. Based on the active leave date delegation will be triggered

We have used the default delegation process only to report and update the triggers based on the leave calendar to delegate all the approval requests to the respective users.

 

Sample Code

[EventSubscriber(ObjectType::Codeunit, Codeunit::"Approvals Mgmt.", OnBeforeCreateApprReqForApprTypeWorkflowUserGroup, '', false, false)]
    local procedure UserGroupChangeForWorkflowApprover(ApprovalEntry: Record "Approval Entry"; sender: Codeunit "Approvals Mgmt.";
            SequenceNo:
                Integer; var IsHandled: Boolean; var WorkflowUserGroupMember: Record "Workflow User Group Member";
            WorkflowStepArgument:
                Record "Workflow Step Argument")
    var
        WorkflowConfi: Record "Workflow-Configuration";
        SalesHdr: Record "Sales Header";
        PurchHdr: Record "Purchase Header";
        UserSetup: Record "User Setup";
        WorfklowUserGrp: Record "Workflow User Group";
        Appmang:
                Codeunit "Approvals Mgmt.";
        ApproverId:
                Code[50];
        NoWFUserGroupMembersErr:
                Label 'A workflow user group with at least one member must be set up.';
        UserIdNotInSetupErr:
                Label 'User ID %1 does not exist in the Approval User Setup window.', Comment = 'User ID BC User does not exist in the Approval User Setup window.';
        WFUserGroupNotInSetupErr:
                Label 'The workflow user group member with user ID %1 does not exist in the Approval User Setup window.', Comment = 'The workflow user group member with user ID BC User does not exist in the Approval User Setup window.';

        CustomCompsetup: Record "Custom Company Setup";
        WorkflowCode: Code[20];
    begin
        WorkflowCode := GetWorkflowUserGroupCode(ApprovalEntry."Document No.", ApprovalEntry."Document Type", ApprovalEntry."Table ID");
        IF WorkflowCode <> '' then begin
            WorkflowStepArgument."Workflow User Group Code" := WorkflowCode;
            WorkflowStepArgument.Modify();

            if WorkflowStepArgument."Workflow User Group Code" <> '' then begin
                if not IsHandled then begin
                    if not UserSetup.Get(UserId) then
                        Error(UserIdNotInSetupErr, UserId);
                    SequenceNo := Appmang.GetLastSequenceNo(ApprovalEntry);

                    WorkflowUserGroupMember.SetCurrentKey("Workflow User Group Code", "Sequence No.");
                    WorkflowUserGroupMember.SetRange("Workflow User Group Code", WorkflowStepArgument."Workflow User Group Code");

                    if not WorkflowUserGroupMember.FindSet() then
                        Error(NoWFUserGroupMembersErr);

                    repeat
                        ApproverId := WorkflowUserGroupMember."User Name";
                        if not UserSetup.Get(ApproverId) then
                            Error(WFUserGroupNotInSetupErr, ApproverId);
                        IsHandled := false;
                        if not IsHandled then
                            Appmang.MakeApprovalEntry(ApprovalEntry, SequenceNo + WorkflowUserGroupMember."Sequence No.", ApproverId, WorkflowStepArgument);
                    until WorkflowUserGroupMember.Next() = 0;
                end;
            end;
            IsHandled := true;
        end
        else begin
            if (ApprovalEntry."Table ID" = Database::"Sales Header") and (ApprovalEntry."Table ID" = Database::"Purchase Header") and (ApprovalEntry."Table ID" = Database::"Service Header") then
                IsHandled := true;
        end;
    end;


procedure GetWorkflowUserGroupCode(var DocumentNo: Code[20]; var documenttype: Enum "Approval Document Type"; TableID: Integer): Code[20]
    var
        WorkflowConfi: Record "Workflow-Configuration";
        salesHdr: Record "Sales Header";
        SalesHdr1: Record "Sales Header";
        TotalSalesLineLCY: Record "Sales Line";
        TotalPurchLineLCY: Record "Purchase Line";
        TempTable: Record "Sales Header" temporary;
        PurchHdr: Record "Purchase Header";
        PurchHdr1: Record "Purchase Header";
        ServiceHdr: Record "Service Header";
        ServiceHdr1: Record "Service Header";
        CustL: Record Customer;
        CustL1: Record Customer;
        CurrExchRate: Record "Currency Exchange Rate";
        CustomCompsetup: Record "Custom Company Setup";
    begin
        case TableID of
            database::"Sales Header":
                begin
                    SalesHdr.Reset();
                    SalesHdr.SetRange("No.", DocumentNo);
                    SalesHdr.SetRange("Document Type", documenttype);
                    if salesHdr.FindSet() then begin
                        WorkflowConfi.Reset();
                        WorkflowConfi.SetRange("Document Type", documenttype);
                        WorkflowConfi.Setfilter("Workflow User Group", '<>%1', '');
                        WorkflowConfi.SetRange("Table ID", TableID);
                        WorkflowConfi.SetFilter("Customer Posting Group", '%1', '*' + salesHdr."Customer Posting Group" + '*');
                        
                        if salesHdr."Order type" <> '' then
                            WorkflowConfi.SetFilter("Sub Transaction Type", '%1', salesHdr."Order type" + '*');
                        WorkflowConfi.SetRange(Disable, false);
                        if WorkflowConfi.FindSet() then begin
                            if salesHdr."Currency Code" <> '' then begin
                                TotalSalesLineLCY.Reset();
                                TotalSalesLineLCY.SetRange("Document Type", documenttype);
                                TotalSalesLineLCY.SetRange("Document No.", DocumentNo);
                                if TotalSalesLineLCY.FindSet() then begin
                                    TotalSalesLineLCY.CalcSums(Amount);
                                    SalesHdr."Total Order Value" := CurrExchRate.ExchangeAmtFCYToLCY(WorkDate(), SalesHdr."Currency Code", TotalSalesLineLCY.Amount, SalesHdr."Currency Factor");
                                    salesHdr.Modify();
                                end;
                            end;
                            repeat
                                SalesHdr1.Reset();
                                SalesHdr1.SetRange("No.", DocumentNo);
                                SalesHdr1.SetRange("Document Type", documenttype);
                                SalesHdr1.SetFilter("Shortcut Dimension 1 Code", WorkflowConfi."Department Code");
                                if WorkflowConfi."Value Threshold" <> '' then
                                    if salesHdr."Currency Code" = '' then
                                        salesHdr1.SetFilter(Amount, WorkflowConfi."Value Threshold")
                                    else begin
                                        salesHdr1.SetFilter("Total Order Value", WorkflowConfi."Value Threshold");
                                    end;
                                if salesHdr1.FindSet() then
                                    exit(WorkflowConfi."Workflow User Group");
                            until WorkflowConfi.Next() = 0;
                        end
                        else
                            exit('');
                    end;
                end;
            Database::"Purchase header":
                begin
                    PurchHdr.Reset();
                    PurchHdr.SetRange("No.", DocumentNo);
                    PurchHdr.SetRange("Document Type", documenttype);
                    if PurchHdr.FindSet() then begin
                        WorkflowConfi.Reset();
                        WorkflowConfi.SetRange("Document Type", documenttype);
                        WorkflowConfi.Setfilter("Workflow User Group", '<>%1', '');
                        WorkflowConfi.SetRange("Table ID", TableID);
                        WorkflowConfi.SetFilter("Sub Transaction Type", '%1', '*' + PurchHdr."Order type" + '*');
                        WorkflowConfi.SetRange("Source Of Purchase", PurchHdr."Single Source Purchase");
                        WorkflowConfi.SetRange("Advance Purchase", PurchHdr."Advance Purchase Order");
                        
                        WorkflowConfi.SetRange(Disable, false);
                        if WorkflowConfi.FindSet() then begin
                            if PurchHdr."Currency Code" <> '' then begin
                                TotalPurchLineLCY.Reset();
                                TotalPurchLineLCY.SetRange("Document Type", documenttype);
                                TotalPurchLineLCY.SetRange("Document No.", DocumentNo);
                                if TotalPurchLineLCY.FindSet() then begin
                                    TotalPurchLineLCY.CalcSums(Amount);
                                    PurchHdr."Total Order Value" := CurrExchRate.ExchangeAmtFCYToLCY(PurchHdr."Order Date", PurchHdr."Currency Code", TotalPurchLineLCY.Amount, PurchHdr."Currency Factor");
                                    PurchHdr.Modify();
                                end;
                            End;
                            repeat
                                PurchHdr1.Reset();
                                PurchHdr1.SetRange("No.", DocumentNo);
                                PurchHdr1.SetRange("Document Type", documenttype);
                                PurchHdr1.SetFilter("Shortcut Dimension 1 Code", WorkflowConfi."Department Code");
                                if WorkflowConfi."Value Threshold" <> '' then
                                    if PurchHdr."Currency Code" = '' then
                                        PurchHdr1.SetFilter(Amount, WorkflowConfi."Value Threshold")
                                    else
                                        PurchHdr1.SetFilter("Total Order Value", WorkflowConfi."Value Threshold");

                                if PurchHdr1.FindSet() then
                                    exit(WorkflowConfi."Workflow User Group");
                            until WorkflowConfi.Next() = 0;
                        end
                        else
                            exit('');
                    end;
                end;
            Database::"Service Header":
                begin
                    ServiceHdr.Reset();
                    ServiceHdr.SetRange("No.", DocumentNo);
                    ServiceHdr.SetRange("Document Type", documenttype);
                    if ServiceHdr.FindSet() then begin
                        WorkflowConfi.Reset();
                        WorkflowConfi.SetRange("Document Type", documenttype);
                        WorkflowConfi.Setfilter("Workflow User Group", '<>%1', '');
                        WorkflowConfi.SetRange("Table ID", TableID);
                        WorkflowConfi.SetRange(Disable, false);
                        if WorkflowConfi.FindSet() then begin
                            repeat
                                ServiceHdr1.Reset();
                                ServiceHdr1.SetRange("No.", DocumentNo);
                                ServiceHdr1.SetRange("Document Type", documenttype);
                                ServiceHdr1.SetFilter("Shortcut Dimension 1 Code", WorkflowConfi."Department Code");
                                if WorkflowConfi."Value Threshold" <> '' then
                                    ServiceHdr1.SetFilter("Invoice Amount", WorkflowConfi."Value Threshold");
                                if ServiceHdr1.FindSet() then
                                    exit(WorkflowConfi."Workflow User Group");
                            until WorkflowConfi.Next() = 0;
                        end
                        else
                            exit('');
                    end;
                end;
            Database::"Customer":
                begin
                    CustL.Reset();
                    CustL.SetRange("No.", DocumentNo);
                    if CustL.FindSet() then begin
                        WorkflowConfi.Reset();
                        WorkflowConfi.Setfilter("Workflow User Group", '<>%1', '');
                        WorkflowConfi.SetRange("Table ID", TableID);
                        WorkflowConfi.SetRange(Disable, false);
                        if WorkflowConfi.FindSet() then begin
                            repeat
                                CustL1.Reset();
                                CustL1.SetRange("No.", DocumentNo);
                                CustL1.SetFilter("Department Code", WorkflowConfi."Department Code");
                                if WorkflowConfi."Value Threshold" <> '' then
                                    CustL1.SetFilter("Credit Limit (LCY)", WorkflowConfi."Value Threshold");
                                if CustL1.FindSet() then
                                    exit(WorkflowConfi."Workflow User Group");
                            until WorkflowConfi.Next() = 0;
                        end
                        else
                            exit('');
                    end;
                end;
        end;
    End;


Sample data

Comments

Popular posts from this blog

Dimension Correction in Business Central

Telemetry Analysis Hidden facts - Business Central