Grouping Print Jobs
If a printer is used by more than a single user, you may run into situations where print jobs from different users are mixed in the output bin. Pages that belong to the same print job are printed together without being mixed, but the jobs themselves may come out in a mixed order.
This problem becomes even worse if you have multiple print services or instances of print services. In this case, you cannot be sure that multiple jobs from the same user will be printed in a predictable order. The jobs are printed in the order they are handed to the Windows spooler process by the different instances of the service.
In most use cases, this is not a problem, but you may have situations where it is important to ensure that the order of reports and pages is consistent. Let's say that you are in a warehouse where you ship orders. You are printing an invoice and a shipping list to put in a box with the items to be shipped. Here, the invoice and the shipping list must follow each other and not be mixed up with other orders and shipping lists.
Print jobs on hold
The solution to the print sequence problem described above is to create a direct printer definition and set the initial job status to On Hold.
When this printer is used, jobs are written to the print queue and have the status On Hold.
The user's session ID is also written to the print queue. Session IDs are later used to group jobs, helping us find the jobs that belong together.
Collect the print jobs
When the last print job in the group is in the print queue, it is time to collect all the jobs and mark them ready for printing.
Jobs are collected by calling the GroupSessionPrintJobs function on the queue table.
var
DirPrtQueue: Record "ForNAV DirPrt Queue";
begin
DirPrtQueue.GroupSessionPrintJobs(true);
end;
All jobs with the current session ID are collected, and the result is placed as a new entry on the print queue with the type set to Collection. Collected jobs are marked as finished, and the latest job is marked as ready.
The GroupSessionPrintJobs function takes a parameter that tells the print service how to handle the collection of jobs. The print service can print the jobs individually or merge them before printing them. Regular print jobs are stored as PDFs on the print queue, and combining the separate PDF documents into one PDF before printing will ensure that the printers handle all the documents as one print job. This prevents other service instances from inserting print jobs between the jobs in the collection.
Only jobs based on PDF documents are supported for merging before print. Zebra ZPL jobs do not support this.
Group by a custom filter
When you call GroupSessionPrintJobs, your jobs are grouped by the user session. If you need to group by another criteria, you can use the more generic GroupJobs function.
procedure GroupJobs(var DirPrtQueue: Record "ForNAV DirPrt Queue"; MergeCollection: Boolean)
Your jobs will be grouped based on the filter on the DirPrtQueue record parameter.
When to collect
You decide when to call the function to collect the On Hold jobs. If your documents are printed by a user action on a page, it would make sense to subscribe to the OnAfterActionEvent and then call the GroupSessionPrintJobs function from there. If you don't have access to the page code, this could be done in a page extension.
Here is an example of the code. Remember to modify it to fit your scenario.
[EventSubscriber(ObjectType::Page, Page::"ForNAV Reports", 'OnAfterActionEvent', 'Run', true, true)]
local procedure MyOnAfterActionEvent()
var
DirPrtQueue: Record "ForNAV DirPrt Queue";
begin
DirPrtQueue.GroupSessionPrintJobs(true);
end;
Multiple printers
Print jobs are grouped per printer. This means that if you have jobs on hold for multiple printers in your session, the system will create a collection for each printer in the job queue.
Client-print
You can combine document grouping from both client-print and service-print. The methods described earlier apply to service-print. For grouping print jobs with client-print, use the DownloadPrintJobs function in the print queue table. This function groups and downloads jobs based on a specified filter. You can find more details on grouping client-print jobs here:
Print multiple documents with client-print
Handling Mixed Print Jobs
If you need to process multiple reports where some use service-print and others use client-print, follow this approach:
Configure Direct Printers for Client-Print
- Set them to queue jobs.
- Ensure the initial status is set to on-hold.
Tagging Client Printers
- Add a tag to client printers to identify them as client printers.
- This tag is transferred to the print queue during print operations.
- You may need to display the tag field on the direct printer page, as it is hidden by default.
Processing Print Jobs
- First, collect client-print jobs by applying a filter to the print queue using session information and the assigned tag.
- Download the jobs.
Handling Remaining Service-Print Jobs
- Once all client-print jobs for your session are processed, the remaining jobs will be service-print jobs.
- Use the GroupSessionPrintJobs function in the print queue to collect these.
Below is an example of the implementation for this process:
trigger OnAction()
var
LocalPrinter: Record "ForNAV Local Printer";
DirPrtQueue: Record "ForNAV DirPrt Queue";
i: Integer;
merge: Boolean;
printerNameService, printerNameClient : Text;
begin
// Create direct service printer
printerNameService := 'Service - Sequence Printer';
LocalPrinter.SetRange("Cloud Printer Name", printerNameService);
LocalPrinter.DeleteAll();
LocalPrinter.Init();
LocalPrinter."Cloud Printer Name" := printerNameService;
LocalPrinter."Local Printer Name" := 'ForNAV PDF Network Printer';
LocalPrinter.IsPrintService := true;
LocalPrinter."Initial Job Status" := LocalPrinter."Initial Job Status"::OnHold;
LocalPrinter.Insert(true);
// Create direct client printer
printerNameClient := 'Client - Sequence Printer';
LocalPrinter.SetRange("Cloud Printer Name", printerNameClient);
LocalPrinter.DeleteAll();
LocalPrinter.Init();
LocalPrinter."Cloud Printer Name" := printerNameClient;
LocalPrinter."Local Printer Name" := 'DEFAULT';
LocalPrinter.IsPrintService := true;
LocalPrinter.Tag := 'Client Print';
LocalPrinter."Initial Job Status" := LocalPrinter."Initial Job Status"::OnHold;
LocalPrinter.Insert(true);
// Create service print jobs
for i := 1 to 2 do
Report.Print(Report::"ForNAV Customer - List", '', printerNameService);
// Create client print jobs
for i := 1 to 2 do
Report.Print(Report::"ForNAV Customer - List", '', printerNameClient);
// Ask if we should merge the service print jobs
merge := Confirm('Merge print jobs?');
// Download client print jobs from this session
// Use the tag from the printer to filter the print jobs
Clear(DirPrtQueue);
DirPrtQueue.SetRange("Session ID", Database.SessionId());
DirPrtQueue.SetRange(Status, DirPrtQueue.Status::OnHold);
DirPrtQueue.SetRange(Tag, 'Client Print');
DirPrtQueue.DownloadPrintJobs(DirPrtQueue);
// Download service print jobs from this session
DirPrtQueue.GroupSessionPrintJobs(merge);
Commit();
Page.RunModal(Page::"ForNAV DirPrt Queue");
end;
Note
The tag field on the direct printers was added in version 8.0.0.1.
Note
Grouping of printjobs are available since extension version 7.4.0.3 and binaries version 7.4.0.2576.