Spread the love

Printing is, in my opinion, one of the most lacking features in Business Central, because while you can print using the web browsers printer. However, if you wish to print without being prompted, like when you choose Post and Print, this does not work as intended. If you use the Post and Print function, what Business Central will do is create and download a PDF of your report, which you then manually can print. Now I know that we are living in a modern age where for the most part we do not need to print anything since everything can be handled digitally, sometimes it is just nice to actually be able to have a hard copy of your documents.  

So how do we solve this? Well there are a couple of ways, but the one that I will talk about in this post is, by using a third-party product called PrintNode (https://www.printnode.com/en) how this product works is by allowing you to send either an URL to a PDF file or a base64 encoded string to PrintNodes API which in turn will send the request to a locally installed print client on your PC / Server. 

Let us take a look at some AL code 😊  

First, we need to write some code, that can interact with PrintNode the first function is to create the GetAuthString function. 

local procedure GetAuthString(): Text[250] 
    var 
        Base64: Codeunit "Base64 Convert"; 
        AuthString: Text[250]; 
    begin 
        AuthString := CopyStr(STRSUBSTNO('%1:%2', ‘{API KEY}’, ''), 1, 250); 
        AuthString := CopyStr(Base64.ToBase64(AuthString), 1, 250); 
        AuthString := CopyStr(STRSUBSTNO('Basic %1', AuthString), 1, 250); 
        EXIT(AuthString); 
    end;

The {API KEY} is generated in the PrintNode portal next I use a function called GetJsonToken thank you, Arend-Jan Kauffmann, 😊  

procedure GetJsonToken(JsonObject: JsonObject; TokenKey: Text) JsonToken: JsonToken; 
    begin 
        if not JsonObject.GET(TokenKey, JsonToken) then 
            Error('Could not find token ' + TokenKey); 
    end; 

And the last function we are going to need is CallPrintNode which will send our request to PrintNode 

procedure CallPrintNode(Base64: Text; Title: Text) 
    var 
        client: HttpClient; 
        cont: HttpContent; 
        header: HttpHeaders; 
        response: HttpResponseMessage; 
        Jobject: JsonObject; 
        error: JsonToken; 
        tmpString: Text; 

    begin 
        client.DefaultRequestHeaders().Add('Authorization', GetAuthString()); 
        Clear(Jobject); 
        Jobject.Add('printerId', ‘{printerID}’) 
        Jobject.Add('title', Title); 
        Jobject.Add('contentType', 'pdf_base64'); 
        Jobject.Add('content', Base64); 
        Jobject.Add('source', 'Business Central'); 
        Jobject.WriteTo(tmpString); 
        cont.WriteFrom(tmpString); 
        cont.ReadAs(tmpString); 
        cont.GetHeaders(header); 
        header.Remove('Content-Type'); 
        header.Add('Content-Type', 'application/json');  
        client.Post('https://api.printnode.com/printjobs', cont, response);  

        if not response.IsSuccessStatusCode() then begin 
            response.Content().ReadAs(tmpString); 
            Jobject.ReadFrom(tmpString); 
            Jobject.Get('error', error); 
            Error('Failed: Reason: ' + error.AsValue().AsText()); 
        end 
        else 
            response.Content().ReadAs(tmpString); 
    end; 

And that is all the code that you need, now to use the function you could call it with something like this: 

var 
        SalesShip: Record "Sales Shipment Header"; 
        TempBlob: Codeunit "Temp Blob"; 
        Base64Convert: Codeunit "Base64 Convert";   
        RecRef: RecordRef; 
        MyOutStream: OutStream; 
        MyInStream: InStream; 
        Base64String: Text; 
    begin 
 
            SalesShip.get(SalesShptHdrNo); 
            SalesShip.SetFilter("No.", SalesShptHdrNo); 
            RecRef.GetTable(SalesShip); 
            RecRef.SetView(SalesShip.GetView()); 

            TempBlob.CreateOutStream(MyOutStream); 
            Report.SaveAs(Report::"Sales - Shipment", SalesShip.GetFilters(), ReportFormat::Pdf, MyOutStream, RecRef); 
            TempBlob.CreateInStream(MyInStream);  
            Base64String := Base64Convert.ToBase64(MyInStream); 
            CallPrintNode(Base64String, 'Sales Shipment'); 

        end; 
    end; 

And that is all there is to it 😊 Now I know there are other ways of handling printing from the cloud, where Google Cloud Print also could be a way to go, however using Google Cloud Print will require you to use OAuth and using tokens, which very fast can become a more complicated solution, however once it is set up, Google Cloud Print is free, for now anyway 😉 

And that is all for now, and happy printing 😉  

Leave a Reply