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.PrintAsyncwrites 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
tableand a childZRTable.tableare exclusive (SectionTableXor). Setting both reprints every grid on every line.When a Zr script fails to compile, is only a
PrintFormBase, orPrintAsyncthrows, print does not fall back toGetDataAsyncor the raw record. A fallback would look successful: the sheet would print blanks with no error. Preview shows the reason in the yellow strip.
MetaAgentis the job scheduler. Print agents arePrintAgent. 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#
- Print a document — the click path
- Print forms
- Print form PDF
- Print agents
- Document