ZuloOne
English
Sign in
Menu

Print a form

Updated

A print form is a MetaPrintForm (Name + Caption), a data script, and — for ZuloReport — LayoutJson plus ZR sections. This page is the developer declaration, not the click path. The click path is Print a document. The long contract is Print forms.

Note

ZuloReport does not fill from one SlimTable. PrintAsync writes a catalog of cells (values that print once) and tables (rows that grow). The layout binds to those names. The engine paginates; the script does not print each line.

How to#

Files live in your model, next to the document or dictionary they print:

Documents/<Doc>/PrintForms/<Name>.printform.json (the form, its bindings, LayoutJson) plus the script pair (*.script.json + *.cs, scriptType: "PrintForm"). A dictionary uses Dictionaries/<Dict>/PrintForms/. Without those files the form lives in the database only and is lost when the model is shipped.

The cheapest way to create them is the studio (/printforms, or the Print forms block on a subtype / the Print forms tab on a dictionary). Creating a ZuloReport or Html form immediately creates the script and writes ScriptMetaId.

Engine Zr is the canonical layout (banded JSON, HTML renderer, visual designer). Zr is the only accepted engine value for that layout. Controls are ZR* (ZRLabel, ZRTable, ZRBarCode, …). The script subclasses ZuloReportForm and overrides PrintAsync:

Report["Number"] = invoice.ID;
Report.Table("Lines").Add(new { LineNo = 1, Item = "Nail", Qty = 2m });

Html and Xlsx still subclass PrintFormBase and fill GetDataTemplate / GetDataAsync (SlimTable / SlimRow).

A Body section's table repeats the whole section once per catalog row (a row template of labels, no inner grid). A child ZRTable.table expands one grid inside a section that prints once. Each ZRTable binds to one catalog table. Header / Data / Footer rows of that grid bind to cells or to columns of that table.

Where the Print button shows the form is MetaPrintFormBindings: TargetKind = DocumentType / DocumentSubtype / Dictionary, Scope = Record / List. Empty bindings = visible on the form's own document type (list and card). A dictionary with no explicit binding is not shown. A form created from a dictionary has DocumentTypeMetaId = Guid.Empty and can only be created with a Dictionary binding.

context.GetService<T>() inside PrintAsync / GetDataAsync resolves the same way as in an event handler. Bump modelVersion in the same change.

From a script, bytes are PDF (IDocumentPdfService.RenderAsync) and paper on a warehouse printer is a print agent (IPrintQueueService.EnqueuePrintFormAsync). Preview stays in the browser.

Warning

A Body table and a child ZRTable.table are exclusive (SectionTableXor). Setting both reprints every grid on every line.

When a Zr script fails to compile, is only a PrintFormBase, or PrintAsync throws, print does not fall back to GetDataAsync or the raw record. A fallback would look successful: the sheet would print blanks with no error. Preview shows the reason in the yellow strip.

MetaAgent is the job scheduler. Print agents are PrintAgent. Mixing the two looks like an empty printer catalog while jobs still run on a warehouse box.

Check on the form (POST /api/metadata/print-forms/{id}/check) is the form's compile. Codes such as UnknownField and DataScript are on Print forms.

Element scripts (scriptsSource on the layout, a handler name on each control) are not the catalog fill. Changing a live section from that code (FindBand, AddBand, MoveBand) is the same long page.

What's not there yet#

A journal batch is browser HTML only. Server PDF, Xlsx, and a print-agent job each take one record; the dialog refuses a multi-record request for those actions. A Subreport prints a nested catalog table, not another print form. There is no import of a foreign binary layout.

Where next#

Was this page helpful?