Printing on roll paper in Dynamics NAV

I want to show you how I was able to print invoices and credit memos (or other reports) on a roll paper printer from Dynamics NAV.

I was using Dynamics NAV Universal App in a tablet with Android and I wanted to print to a printer connected via Bluetooth.

If you tried it, you’ve already found out that SSRS / RDLC only allows you to print to stationary paper because you have to define the report size in design time.

Besides, I wanted to print from the Universal App and as you may already know, Universal App just generates a PDF file and the printing process is handled by the Android operating system.

A PDF file is like an image so there’s nothing I could change in printer settings to define a different paper size. I needed the PDF file to be created with the final correct size.

How was I able to do it?

First, to preview the layout correctly in Dynamics NAV, showing the invoice in a single page, you must change the Height in InteractiveSize property to 0.

The width was set to 7,5 cm because that’s the width of the paper in use.

These settings have no effect when printing the invoice to the printer or to a PDF file, only when previewing. To change the paper size, you have to change the PageSize property but you need to do it in a dynamically way because the printing size depends, at least, on how many lines the invoice have.

To handle it I’ve done the following:

  1. I’ve created a specific layout that conforms to roll paper width (7,5 cm in my case as stated previously):

InteractiveSize only changes the preview size so I’ve set up 7,5 cm  / 0 cm.

PageSize changes the print size so I’ve set up 7,5 cm / 9 cm. Feel free to setup the height you need to build your layout. You change it dynamically later.

  1. Created a new action in page 132 – Posted Sales Invoice that looks pretty similar to the standard code but calls a method (see PrintMob) I’ve created to print the new invoice layout.
Print - OnAction()
SalesInvHeader := Rec;
CurrPage.SETSELECTIONFILTER(SalesInvHeader);
SalesInvHeader.PrintRecords(TRUE);

PrintMob - OnAction()
SalesInvHeader := Rec;
CurrPage.SETSELECTIONFILTER(SalesInvHeader);
RoleCenterMgt.PrintMobilityPostedSalesInvoice(SalesInvHeader, TRUE);
  1. Created a new codeunit that will have specific code to handle the new layout:
PrintMobilityPostedSalesInvoice(VAR SalesInvoiceHeader : Record "Sales Invoice Header";ShowRequestForm : Boolean)
// RPM,sn
DocumentSendingProfile.TrySendToPrinter(
DummyReportSelections.Usage::"S.Invoice Mob.",SalesInvoiceHeader,SalesInvoiceHeader.GetCustomerNoReportSelection,ShowRequestForm);
// RPM,en

I’m using this code so that I can use the Report Selections functionality.

In order to use the Report Selections I’ve added a new Sales Invoice Mobility option to the Usage field in table 77 – Report Selections.

And made the following changes in table 77 to identify that the layout being printed should be considered as a mobility invoice.

PrintWithGUIYesNo(ReportUsage : Integer;RecordVariant : Variant;IsGUI : Boolean;CustNo : Code[20])
FindPrintUsage(ReportUsage,CustNo,TempReportSelections);

WITH TempReportSelections DO
REPEAT
ReportLayoutSelection.SetTempLayoutSelected("Custom Report Layout Code");
// RPM,sn
IF IsMobility(TempReportSelections) THEN
SingleInstanceCodeunit.SetMobilityReportToPrint(ReportUsage,RecordVariant);
// RPM,en
REPORT.RUNMODAL("Report ID",IsGUI,FALSE,RecordVariant)
UNTIL NEXT = 0;
ReportLayoutSelection.SetTempLayoutSelected('');
// RPM,sn
SingleInstanceCodeunit.ClearMobilityReportToPrint();
// RPM,en

LOCAL IsMobility(TempReportSelections : TEMPORARY Record "Report Selections") : Boolean
// RPM,sn
EXIT(TempReportSelections.Usage IN [TempReportSelections.Usage::"S.Invoice Mob.",TempReportSelections.Usage::"S.Cr.Memo Mob."]);
// RPM,en

I’ve only changed the PrintWithGUIYesNo but you may need to do this change in the other print methods.

The codeunit 50024 – Single Instance Codeunit is a single instance codeunit used to keep session values. When using events, it is a possible solution to pass values between objects.

Name                      DataType   Subtype    Length
MobilityReportUsage        Integer
MobilityDocumentVariant    Variant

SetMobilityReportToPrint(MobilityReportUsage2 : Integer;MobilityDocumentVariant2 : Variant)
// RPM,sn
MobilityReportUsage := MobilityReportUsage2;
MobilityDocumentVariant := MobilityDocumentVariant2;
// RPM,en

ClearMobilityReportToPrint()
// RPM,sn
CLEAR(MobilityReportUsage);
CLEAR(MobilityDocumentVariant);
// RPM,en

IsSetMobilityReportToPrint() : Boolean
// RPM,sn
EXIT(MobilityReportUsage <> 0);
// RPM,en

Now I’ve changed the codeunit 1, methods HasCustomLayout and ReportGetCustomRdlc. These methods are called by Dynamics NAV whenever you print a report and I needed to do two changes:

  1. Change and force the HasCustomLayout method to return the value 1 when the report is set as a mobility layout, which indicates that the report has a custom rdlc. When that happens, the system calls the ReportGetCustomRdlc method to retrieve the layout rdlc.
HasCustomLayout(ObjectType : 'Report,Page';ObjectID : Integer) : Integer
// Return value:
// 0: No custom layout
// 1: RDLC layout
// 2: Word layout
IF ObjectType <> ObjectType::Report THEN
ERROR(NotSupportedErr);

// RPM,sn
IF SingleInstanceCodeunit.IsSetMobilityReportToPrint THEN
EXIT(1);
// RPM,en

EXIT(ReportLayoutSelection.HasCustomLayout(ObjectID));
  1. Change the ReportGetCustomRdlc to inject new code in the report rdlc and make the layout height dynamic. Sounds nice, right?
ReportGetCustomRdlc(ReportId : Integer) : Text
// RPM,sn
IF SingleInstanceCodeunit.IsSetMobilityReportToPrint THEN
EXIT(MobilityLayoutsMgt.GetReportRdlc(ReportId));
// RPM,en
EXIT(CustomReportLayout.GetCustomRdlc(ReportId));

Finally, I’ve created a new codeunit where I have the method to get the updated layout rdlc, with the height being set dynamically.

I’ve used the REPORT.RDLCLAYOUT command to retrieve the original rdlc and created the method UpdateReportRdlc to update the height to the new value.

I believe the code is self-explanatory.

Text Constants
StartTag   <PageHeight>
EndTag     </PageHeight>

GetReportRdlc(ReportId : Integer) : Text
REPORT.RDLCLAYOUT(ReportId,InStr);
InStr.READ(RdlcTxt);
EXIT(UpdateReportRdlc(RdlcTxt));

LOCAL UpdateReportRdlc(RdlcTxt : Text) : Text
CurrentHeightStartPosition := STRPOS(RdlcTxt, StartTag) + STRLEN(StartTag);
CurrentHeightEndPosition := STRPOS(RdlcTxt, EndTag);
RdlcStart := COPYSTR(RdlcTxt, 1, CurrentHeightStartPosition - 1);
RdlcEnd := COPYSTR(RdlcTxt, CurrentHeightEndPosition - 2, STRLEN(RdlcTxt) - CurrentHeightEndPosition + 3);
CurrentHeight := COPYSTR(RdlcTxt, CurrentHeightStartPosition, STRLEN(RdlcTxt) - STRLEN(RdlcStart) - STRLEN(RdlcEnd) - 1);
EXIT(RdlcStart + FORMAT(GetReportNewHeight,0,9) + RdlcEnd);

LOCAL GetReportNewHeight() NewHeight : Decimal
SingleInstanceCodeunit.GetMobilityReportToPrint(ReportUsage,DocumentVariant);
DataTypeManagement.GetRecordRef(DocumentVariant, RecRef);
CASE RecRef.NUMBER OF
DATABASE::"Sales Invoice Header":
BEGIN
SalesInvHeader.COPY(DocumentVariant);
GetSalesInvNrOfLines(SalesInvHeader."No.",NrOfLines);
GetInvoiceHeights(HeaderHeight,VATHeight,TotalsHeight,FooterHeight,HeightPerLine);
Height := HeaderHeight + TotalsHeight + VATHeight + FooterHeight;
Height += NrOfLines * HeightPerLine;
END;

DATABASE::"Sales Cr.Memo Header":
BEGIN
SalesCrMemoHeader.COPY(DocumentVariant);
GetSalesCrMemoNrOfLines(SalesCrMemoHeader."No.",NrOfLines);
GetCrMemoHeights(HeaderHeight,VATHeight,TotalsHeight,FooterHeight,HeightPerLine);
Height := HeaderHeight + TotalsHeight + VATHeight + FooterHeight;
Height += NrOfLines * HeightPerLine;
END;
END;
EXIT(Height);

LOCAL GetSalesInvNrOfLines(SalesInvoiceNo : Code[20];VAR Lines : Integer)
WITH SalesInvLine DO BEGIN
RESET;
SETRANGE("Document No.", SalesInvoiceNo);
Lines := COUNT;
END;

LOCAL GetSalesCrMemoNrOfLines(SalesCrMemoNo : Code[20];VAR Lines : Integer)
WITH SalesCrMemoLine DO BEGIN
RESET;
SETRANGE("Document No.", SalesCrMemoNo);
Lines := COUNT;
END;

LOCAL GetInvoiceHeights(VAR Header : Decimal;VAR VAT : Decimal;VAR Totals : Decimal;VAR Footer : Decimal;VAR SingleLine : Decimal) : Decimal
Header := 11.5;
VAT := 3;
Totals := 2.5;
Footer := 2.5;
SingleLine := 0.5;

LOCAL GetCrMemoHeights(VAR Header : Decimal;VAR VAT : Decimal;VAR Totals : Decimal;VAR Footer : Decimal;VAR SingleLine : Decimal) : Decimal
Header := 11;
VAT := 3;
Totals := 3;
Footer := 2.5;
SingleLine := 0.4;

Feel free to leave your comments or ask me questions.

Share this

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.