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:
- 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.
- 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);
- 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:
- 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));
- 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.