Thursday, November 11, 2021

Import Base 64 Data to BLOB Field in Business Central

 Hello Friends,

To convert base 64 string data to blob field.

Process blob field data to xmlport.

=======

We create the codeunit function to process base 64 data.

Below code will import Base 64 data to Blob field

  procedure FillBase64(Base64Text_iTxt: Text)
    var
        ImportLog_lRec: Record "Import Log Detail";
        VarOutStream: OutStream;
        Base64CU: Codeunit "Base64 Convert";
    begin
        ImportLog_lRec.Init();
        ImportLog_lRec.Insert();
        ImportLog_lRec.CalcFields("File Data");
        ImportLog_lRec."File Data".CreateOutStream(VarOutStream);
        Base64CU.FromBase64(Base64Text_iTxt, VarOutStream);
        ImportLog_lRec.Modify();
    end;

Call webservice function to pass base 64 data in function


























below code will process base 64 data (text import data) and process in xmlport import

CalcFields("File Data");
"File Data".CreateInStream(Instr);

Clear(ImportiMIS_xmlPort);
ImportiMIS_xmlPort.SetSource(Instr);
ImportiMIS_xmlPort.Import();

Download base 64 file blob field data to text file

procedure ExportDataAsText()
    var
      Instr: InStream;
    begin
        if not "File Data".HasValue then
            exit;

        CalcFields("File Data");
        "File Data".CreateInStream(Instr);
        DownloadFromStream(Instr, '', '', '', "File Name");
  end;

Call this web service function from postman

Method: Post

Authentication : Basic

Header: 

SOAPAction : "urn:microsoft-dynamics-schemas/codeunit/ImportSOFile:ImportSO"

Content Type: text/xml;charset=UTF-8

API: https://api.businesscentral.dynamics.com/v2.0/0083fe57-c7f0-4c02-9d2b-17d11377b1f1/Sandboxnew/WS/XXXXX/Codeunit/ImportSOFile

Body:

<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
    <Body>
        <ImportSO xmlns="urn:microsoft-dynamics-schemas/codeunit/ImportSOFile">
            <fromMailID_iTxt>test@test.com</fromMailID_iTxt>
            <fileName>newtest01.csv</fileName>
            <fileData_BigTxt>SCwyNDcyNTgsMTEvMTEvMjAyMSwsLCw5MzQ2Mzc2MDk....CksMSw1My44MSwwLA==</fileData_BigTxt>
        </ImportSO>
    </Body>
</Envelope>















Postman Snapshot:








Thank you for reading 

Keep Sharing......Keep Growing......

Tuesday, November 9, 2021

Business Central 14 On-Prime to Install Base App and Sync it than Transfer Data to Extension

 Hello Friends,

Below blog help to install base System and Application app in Converted BC 14 On-Prime DB

After we can Generate System Table Symbol and transfer data from Dummy 50K Series Table to Actual Extension Table

1)      Run Power ISE by Admin

Import the PS1 file

Import-Module 'C:\Program Files\Microsoft Dynamics 365 Business Central\140\Service\NavAdminTool.ps1'







 

Now install the System Base App and Sync it below primary steps to follow.

-          Publish

-          restart

-          Sync tenant

-          Sync App

-          Install

============

Start First with Publish App

Publish-NAVApp -ServerInstance BC140 -Path "C:\Users\navadmin\Desktop\Temp\1 - Effvision_Standard_TableExtension\.alpackages\Microsoft_System_14.0.47286.0.app" -SkipVerification 

Publish-NAVApp -ServerInstance BC140 -Path "C:\Users\navadmin\Desktop\Temp\1 - Effvision_Standard_TableExtension\.alpackages\Microsoft_Application_14.0.47286.0.app" -SkipVerification

 Restart Service

Restart-NAVServerInstance -ServerInstance BC140 

Sync Tenant 

Sync-NAVTenant -ServerInstance BC140 -Mode Sync -Tenant Default

 

Sync NAVApp 

Sync-NAVApp -ServerInstance BC140 -Tenant Default -Name "System" -Version 14.0.47286.0 

Sync-NAVApp -ServerInstance BC140 -Tenant Default -Name "Application" -Version 14.0.47286.0

 

Install NAVApp 

Install-NAVApp -ServerInstance BC140 -Tenant Default -Name "System" -Version 14.0.47286.0  

Install-NAVApp -ServerInstance BC140 -Tenant Default -Name "Application" -Version 14.0.47286.0  

===================

Now you can generate table symbol for upgrade


After you can publish your developer BC Extension with Table Extension and Custome Table

Once you publish primary exte



NAV 2016 - Save Image Blob File on Server - Convert from Base 64

 Hello Friends,

Today we received request from client to save the image to server folder from base 64 image string (it will call from powerapp using NAV web service)

Below function we create for development

---------------------------------------------------

    PROCEDURE SaveDocumentImage_gFnc@33027920(DocNo_iCod@33027921 : Code[20];Image_Base64_iBigTxt@33027931 : BigText) : Boolean;

    VAR

      PurchaseHeader_lRec@33027929 : Record 38;

      RecordLink_lRec@33027920 : Record 2000000068;

      FoundRecID_lRecID@33027922 : RecordID;

      FileSaveLocalFolderFullpath_lTxt@33027923 : Text;

      FileGloabalURLPath_lTxt@33027924 : Text;

      NameofFile_lTxt@33027925 : Text;

      Bytes@33027930 : DotNet "'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Array";

      Convert@33027928 : DotNet "'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Convert";

      MemoryStream@33027927 : DotNet "'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.IO.MemoryStream";

      OStream@33027926 : OutStream;

      TempBlob@33027932 : Record 99008535;

      IStream@33027933 : InStream;

      GLSetup_lRec@33027934 : Record 98;

      FileManagement_lCdu@33027935 : Codeunit 419;

    BEGIN

      IF DocNo_iCod = '' THEN

        ERROR('DCNo_iCod cannot be blank');


      IF FORMAT(Image_Base64_iBigTxt) = '' THEN

        ERROR('Image_Base64_iBigTxt cannot be blank');


      GLSetup_lRec.GET;

      GLSetup_lRec.TESTFIELD("Server Att Local Folder Path");

      GLSetup_lRec.TESTFIELD("Server Web Global URL for Att");


      NameofFile_lTxt := FORMAT(CURRENTDATETIME,0,'<Day,2>_<Month,2>_<Year4>_<Hours24>_<Minutes,2>_<Seconds,2>') +'_'+ FORMAT(RANDOM(100)) + '.jpg';

      FileSaveLocalFolderFullpath_lTxt := GLSetup_lRec."Server Att Local Folder Path" + NameofFile_lTxt;

      FileGloabalURLPath_lTxt := GLSetup_lRec."Server Web Global URL for Att" + NameofFile_lTxt;


      PurchaseHeader_lRec.GET(PurchaseHeader_lRec."Document Type"::Order,DocNo_iCod);

      FoundRecID_lRecID := PurchaseHeader_lRec.RECORDID;


      Bytes := Convert.FromBase64String(Image_Base64_iBigTxt);

      MemoryStream := MemoryStream.MemoryStream(Bytes);

      TempBlob.Blob.CREATEOUTSTREAM(OStream);

      TempBlob.Blob.CREATEINSTREAM(IStream);

      MemoryStream.WriteTo(OStream);


      FileManagement_lCdu.BLOBExportToServerFile(TempBlob,FileSaveLocalFolderFullpath_lTxt);


      //SaveImageAttachment_lFnc(Image_Base64_iBigTxt,GateEntryHeader_lRec);


      CLEAR(RecordLink_lRec);

      RecordLink_lRec.INIT;

      RecordLink_lRec."Record ID" := FoundRecID_lRecID;

      RecordLink_lRec.URL1 := FileGloabalURLPath_lTxt;

      RecordLink_lRec.Description := NameofFile_lTxt;

      RecordLink_lRec.Company := COMPANYNAME;

      RecordLink_lRec."User ID" := USERID;

      RecordLink_lRec.Created := CURRENTDATETIME;

      RecordLink_lRec.Type := RecordLink_lRec.Type::Link;

      RecordLink_lRec.INSERT;

      COMMIT;


      EXIT(TRUE);

    END;


==============






Friday, October 1, 2021

Develope Report Extension for Business Central 18+ and setup new RDL File

Dear friends,

Create new report extension and setup the default RDL Layout as default build-in


reportextension 65879 MyExtension65879 extends 10121
{
    RDLCLayout = '.\Layouts\10121_PurchaseInvoice_Updated.rdl';


    dataset
    {
        add("Purch. Inv. Header")
        {
            column(DivisionBreakdown_gTxtArr; DivisionBreakdown_gTxtArr[1])
            {

            }
            column(DivisionBreakdown_gTxtArr2; DivisionBreakdown_gTxtArr[2])
            {

            }
            column(DivisionBreakdown_gTxtArr3; DivisionBreakdown_gTxtArr[3])
            {

            }
            column(DivisionBreakdown_gTxtArr4; DivisionBreakdown_gTxtArr[4])
            {

            }
            column(DivisionBreakdown_gTxtArr5; DivisionBreakdown_gTxtArr[5])
            {

            }
            column(DivisionBreakdownVal_gDecArr; DivisionBreakdownVal_gDecArr[1])
            {

            }
            column(DivisionBreakdownVal_gDecArr2; DivisionBreakdownVal_gDecArr[2])
            {

            }
            column(DivisionBreakdownVal_gDecArr3; DivisionBreakdownVal_gDecArr[3])
            {

            }
            column(DivisionBreakdownVal_gDecArr4; DivisionBreakdownVal_gDecArr[4])
            {

            }
            column(DivisionBreakdownVal_gDecArr5; DivisionBreakdownVal_gDecArr[5])
            {

            }
            column(CurrSign_gTxt; CurrSign_gTxt)
            {

            }


        }
        modify("Purch. Inv. Header")
        {
            trigger OnAfterAfterGetRecord()
            var
                i_Int: Integer;
                PurchInvHeader_lRecTmp: Record "Purch. Inv. Header" temporary;
                PurchInvLine_lRec: Record "Purch. Inv. Line";
                Currency_lRec: Record Currency;
                GLSetup_lRec: Record "General Ledger Setup";
                Exit_lBln: Boolean;
            begin
                Clear(GLSetup_lRec);
                Clear(Currency_lRec);
                Clear(CurrSign_gTxt);
                if Currency_lRec.get("Purch. Inv. Header"."Currency Code"then
                    CurrSign_gTxt := Currency_lRec.Symbol
                else begin
                    Clear(GLSetup_lRec);
                    GLSetup_lRec.Get();
                    CurrSign_gTxt := GLSetup_lRec."Local Currency Symbol";
                end;

                If NOT PurchInvHeader_lRecTmp.IsTemporary then
                    Error('Variable PurchInvHeader_lRecTmp must be temp');

                Clear(PurchInvHeader_lRecTmp);
                Clear(PurchInvLine_lRec);
                Clear(DivisionBreakdown_gTxtArr);
                Clear(DivisionBreakdownVal_gDecArr);
                Clear(i_Int);
                PurchInvLine_lRec.SetCurrentKey("Shortcut Dimension 1 Code");
                PurchInvLine_lRec.SetRange("Document No.", "Purch. Inv. Header"."No.");
                if PurchInvLine_lRec.FindSet() then begin
                    repeat
                        i_Int += 1;
                        if i_Int < 6 then begin
                            PurchInvHeader_lRecTmp.Reset();
                            PurchInvHeader_lRecTmp.SetRange("No.", PurchInvLine_lRec."Shortcut Dimension 1 Code");
                            if PurchInvHeader_lRecTmp.FindFirst() then begin
                                PurchInvHeader_lRecTmp."Currency Factor" += PurchInvLine_lRec.Amount + PurchInvLine_lRec."Inv. Discount Amount";
                                PurchInvHeader_lRecTmp.Modify();
                            end else begin
                                Clear(PurchInvHeader_lRecTmp);
                                PurchInvHeader_lRecTmp."No." := PurchInvLine_lRec."Shortcut Dimension 1 Code";
                                PurchInvHeader_lRecTmp.Insert();

                                PurchInvHeader_lRecTmp."Currency Factor" := PurchInvLine_lRec.Amount + PurchInvLine_lRec."Inv. Discount Amount";
                                PurchInvHeader_lRecTmp.Modify();
                            end;
                        end;
                    until PurchInvLine_lRec.Next() 0;
                end;

                Clear(i_Int);
                PurchInvHeader_lRecTmp.Reset();
                PurchInvHeader_lRecTmp.SetFilter("No.", '<>%1''');
                if PurchInvHeader_lRecTmp.FindSet() then begin
                    repeat
                        i_Int += 1;
                        if i_Int < 6 then begin
                            DivisionBreakdown_gTxtArr[i_Int] := PurchInvHeader_lRecTmp."No.";
                            DivisionBreakdownVal_gDecArr[i_Int] := PurchInvHeader_lRecTmp."Currency Factor";
                        end;
                    until PurchInvHeader_lRecTmp.Next() 0;
                end;

                Clear(i_Int);
                Exit_lBln := false;
                PurchInvHeader_lRecTmp.Reset();
                PurchInvHeader_lRecTmp.SetFilter("No.", '');
                if PurchInvHeader_lRecTmp.FindFirst() then begin
                    for i_Int := 1 to 5 do begin
                        if Exit_lBln then
                            break;

                        if DivisionBreakdown_gTxtArr[i_Int] = '' then begin
                            DivisionBreakdown_gTxtArr[i_Int] := 'BLANK';
                            DivisionBreakdownVal_gDecArr[i_Int] := PurchInvHeader_lRecTmp."Currency Factor";
                            Exit_lBln := true;
                        end;
                    end;
                end;
            end;
        }
        add("Purch. Inv. Line")
        {
            column(Shortcut_Dimension_1_Code; "Shortcut Dimension 1 Code")
            {

            }
            column(No_; GLAccNo_gCod)
            {

            }
            column(GLAccName_gTxt; GLAccName_gTxt)
            {

            }
        }
        modify("Purch. Inv. Line")
        {
            trigger OnAfterAfterGetRecord()
            var
                GLAcc_lRec: Record "G/L Account";
            begin
                Clear(GLAcc_lRec);
                Clear(GLAccName_gTxt);
                if "Purch. Inv. Line".Type = "Purch. Inv. Line".Type::"G/L Account" then begin
                    GLAccNo_gCod := "Purch. Inv. Line"."No.";
                    if GLAcc_lRec.Get("Purch. Inv. Line"."No."then
                        GLAccName_gTxt := GLAcc_lRec.Name;
                end;
            end;
        }
    }


    requestpage
    {

    }
    var
        DivisionBreakdown_gTxtArr: array[5of Text[50];
        DivisionBreakdownVal_gDecArr: array[5of Decimal;
        GLAccName_gTxt: Text;
        GLAccNo_gCod: Code[20];
        CurrSign_gTxt: Text;

}


in business central publish and extension and you can see the published layout in built-in list





Preview of  report - new field added in std repot


Thank you for reading.....
Keep Sharing...Keep Growing....

Friday, September 10, 2021

Read Multiple Level Response in Business Cental AL Extension by Json Token, Json Object & Json Array

Dear Firends,

Use below sample code to read multiple array document respone in AL Extension

Sample AL Input File 



{
   "orders":[
      {
         "DocumentNo":"SO004349",
         "Status":"Partially Shipped",
         "lines":[
            {
               "LineNo":20000,
               "QtyToShip":1,
               "Status":"Finalised"
            }
         ]
      },
      {
         "DocumentNo":"SO004424",
         "Status":"Partially Shipped",
         "lines":[
            {
               "LineNo":10000,
               "QtyToShip":5,
               "Status":"Finalised"
            },
            {
               "LineNo":20000,
               "QtyToShip":5,
               "Status":"Finalised"
            }
         ]
      },
      {
         "DocumentNo":"SO004423",
         "Status":"Partially Shipped",
         "lines":[
            {
               "LineNo":10000,
               "QtyToShip":4,
               "Status":"Finalised"
            }
         ]
      }
   ]
}

AL Extensoin Code to Read Above File:

procedure UpdateSOShipmentStatus_gFnc(JsonText: Text)Boolean
    var
        SalesHeader_lRec: Record "Sales Header";
        SalesLine_lRec: Record "Sales Line";
        WarehouseShipLine_lRec: Record "Warehouse Shipment Line";
        Jtoken: JsonToken;
        JObject: JsonObject;
        jsonval: JsonValue;
        Jarray: JsonArray;
        i: Integer;
        j: Integer;
        TotalCount: Integer;
        OrderCount: Integer;
        Location_lRec: Record Location;
        ReqWareShip_lBln: Boolean;
        AnylineFound_lBln: Boolean;
    begin
        if not JObject.ReadFrom(JsonTextthen
            Error('Invalid response, expected a JSON object');

        JObject.Get('orders', Jtoken);

        if not Jarray.ReadFrom(Format(Jtoken)) then
            Error('Array not Reading Properly');

        TotalCount := 0;
        OrderCount := 0;
        TotalCount := Jarray.Count();
        for i := 0 to Jarray.Count() 1 do begin
            Jarray.Get(i, Jtoken);
            JObject := Jtoken.AsObject();

            Jtoken.AsObject.Get('DocumentNo', Jtoken);
            IF SalesHeader_lRec.GET(SalesHeader_lRec."Document Type"::Order, Jtoken.AsValue().AsCode()) then begin
                OrderCount += 1;

                Jarray.Get(i, Jtoken);
                if Jtoken.AsObject.Get('Status', Jtokenthen
                    SalesHeader_lRec."Shipment Status" := Jtoken.AsValue().AsText();

                JObject.Get('lines', Jtoken);
                for j := 0 to Jtoken.AsArray().Count() 1 do begin
                    JObject.Get('lines', Jtoken);
                    if Jtoken.AsArray().Get(j, Jtokenthen
                        Jtoken.AsObject.Get('LineNo', Jtoken);

                    if SalesLine_lRec.Get(SalesLine_lRec."Document Type"::Order, SalesHeader_lRec."No.", Jtoken.AsValue().AsInteger()) then begin
                        JObject.Get('lines', Jtoken);
                        if Jtoken.AsArray().Get(j, Jtokenthen
                            if Jtoken.AsObject.Get('QtyToShip', Jtokenthen begin
                                ReqWareShip_lBln := false;
                                IF Location_lRec.GET(SalesLine_lRec."Location Code"Then begin
                                    IF Location_lRec."Require Receive" Then begin
                                        ReqWareShip_lBln := true;
                                        WarehouseShipLine_lRec.RESET;
                                        WarehouseShipLine_lRec.Setrange("Source Type", 37);
                                        WarehouseShipLine_lRec.Setrange("Source Subtype", SalesLine_lRec."Document Type");
                                        WarehouseShipLine_lRec.Setrange("Source No.", SalesLine_lRec."Document No.");
                                        WarehouseShipLine_lRec.Setrange("Source Line No.", SalesLine_lRec."Line No.");
                                        IF WarehouseShipLine_lRec.FindFirst() Then begin
                                            IF WarehouseShipLine_lRec."Qty. Outstanding" <= Jtoken.AsValue().AsDecimal() THen
                                                WarehouseShipLine_lRec.Validate("Qty. to Ship", Jtoken.AsValue().AsDecimal())
                                            Else
                                                WarehouseShipLine_lRec.Validate("Qty. to Ship", WarehouseShipLine_lRec."Qty. Outstanding");

                                            WarehouseShipLine_lRec.Modify();
                                            AnylineFound_lBln := true;
                                        end;

                                    end;
                                end;
                                IF NOT ReqWareShip_lBln Then
                                    SalesLine_lRec.Validate("Qty. to Ship", Jtoken.AsValue().AsDecimal());
                            End;

                        JObject.Get('lines', Jtoken);
                        if Jtoken.AsArray().Get(j, Jtokenthen
                            if Jtoken.AsObject.Get('Status', Jtokenthen begin
                                SalesLine_lRec."Shipment Status" := Jtoken.AsValue().AsText();
                                SalesLine_lRec.Modify(true);
                                AnylineFound_lBln := true;

                                WarehouseShipLine_lRec.RESET;
                                WarehouseShipLine_lRec.Setrange("Source Type", 37);
                                WarehouseShipLine_lRec.Setrange("Source Subtype", SalesLine_lRec."Document Type");
                                WarehouseShipLine_lRec.Setrange("Source No.", SalesLine_lRec."Document No.");
                                WarehouseShipLine_lRec.Setrange("Source Line No.", SalesLine_lRec."Line No.");
                                IF WarehouseShipLine_lRec.FindFirst() Then begin
                                    WarehouseShipLine_lRec."Shipment Status" := SalesLine_lRec."Shipment Status";
                                    WarehouseShipLine_lRec.Modify();
                                    AnylineFound_lBln := true;
                                End;


                            end;
                    end;
                    SalesHeader_lRec.Modify(true);
                    AnylineFound_lBln := true;
                end;
            end;
        end;
        exit(AnylineFound_lBln);
    End;

========================================================================

New Example:

Simple single level Json Response:

{
    "access_token": "088e04b1-8efd-4491-a579-1cc4f07a7e68",
    "token_type": "bearer",
    "refresh_token": "cf822cb1-0b1e-48a4-aeda-db648b96d62a",
    "expires_in": 1479,
    "scope": "gstapi"
}
Reading AL code:

procedure ReadJson(JSONInput_iTxt: Text): TEXT
    var
        Jtoken: JsonToken;
        JObject: JsonObject;
        Customer_lRec: Record Customer;
        i: Integer;
        j: Integer;
        access_token: Text[100];
        token_type: Text[100];
        refresh_token: Text[100];
        expires_in: Text[80];
        scope: Guid;
        TotalCount: Integer;
        CustomerCount: Integer;
    begin
        If Not JObject.ReadFrom(JSONInput_iTxt) then
            Error('Invalid response, expected a JSON object');

        JObject.Get('access_token', Jtoken);
        access_token := Jtoken.AsValue().AsText();

        JObject.Get('token_type', Jtoken);
        token_type := Jtoken.AsValue().AsText();

        JObject.Get('refresh_token', Jtoken);
        refresh_token := Jtoken.AsValue().AsText();

        JObject.Get('expires_in', Jtoken);
        expires_in := Jtoken.AsValue().AsText();

        //JObject.Get('scope', Jtoken);
        //scope := Jtoken.AsValue().as();

        Exit(access_token);
    end;

=====================================================================

Second example where there are object inside Json Response

{
    "status": "1",
    "message": "Record Added Successfully",
    "data": {
        "docNum": "INVOICE00126",
        "ctin": "27GSPMH2101G1Z2",
        "docId": "612f4e83218788617684a121"
    }
}

Reading AL code:

 procedure ReadJosn(JsonText: Text): Boolean
    var
        Jtoken: JsonToken;
        JtokenInner: JsonToken;
        JObject: JsonObject;
        jsonval: JsonValue;
        Jarray: JsonArray;
        i: Integer;
        j: Integer;

        Status_lTxt: Text[30];
        Message_lTxt: Text[30];
        DocNum_lTxt: Text[30];
        CTIN_lTxt: Text[30];
        DocId_lTxt: Text[30];
    begin
        If Not JObject.ReadFrom(JsonText) then
            Error('Invalid response, expected a JSON object');

        if JObject.Get('status', Jtoken) then
            Status_lTxt := Jtoken.AsValue().AsText();

        if JObject.Get('message', Jtoken) then
            Message_lTxt := Jtoken.AsValue().AsText();

        JObject.Get('data', Jtoken);

        If Jtoken.AsObject().Get('docNum', JtokenInner) then
            DocNum_lTxt := JtokenInner.AsValue().AsText();

        If Jtoken.AsObject().Get('ctin', JtokenInner) then
            CTIN_lTxt := JtokenInner.AsValue().AsText();

        If Jtoken.AsObject().Get('docId', JtokenInner) then
            DocId_lTxt := JtokenInner.AsValue().AsText();

        Message(Status_lTxt, Message_lTxt, DocNum_lTxt, CTIN_lTxt, DocId_lTxt);
    end;