Skip to content

Crystal Forms (Crystal Reports)

P21 uses Crystal Reports for all printed output — invoices, purchase orders, pick tickets, packing slips, and reports.

Reference Documents

The following PDF reference guides are included in this repository:

Document Location Description
Crystal Forms Dictionary Chrystal Forms/ Complete datastream field dictionary — all available fields for every form type
DynaChange Designer Guide Dynachange Designer/ Form layout and design tool reference

Start with the Dictionary

The Crystal Forms Dictionary PDF is the most important reference for form development. It lists every field available in every P21 datastream (invoice, PO, pick ticket, etc.) along with data types and descriptions.

How P21 Form Printing Works

  1. A user triggers a print action in P21 (e.g., post an invoice)
  2. P21 generates a datastream — an XML document containing all the data for that form
  3. The datastream is passed to Crystal Reports along with the .rpt template
  4. Crystal Reports renders the output (PDF, printer, preview)
  5. The output is delivered per the session's PrintMode setting (Browser, Local, Network)

Datastream Structure

Every form datastream is an XML document with a predictable structure:

<datastream>
  <form type="INVOICE">
    <header>
      <!-- Header-level data groups -->
      <group name="INVOICEHEADER">
        <invoice_no>INV-001234</invoice_no>
        <customer_id>C100001</customer_id>
        <customer_name>Acme Corp</customer_name>
        <invoice_date>2025-03-12</invoice_date>
        <invoice_total>1250.00</invoice_total>
      </group>
    </header>
    <lines>
      <!-- One group per line item -->
      <group name="INVOICELINE">
        <line_no>1</line_no>
        <item_id>WIDGET-001</item_id>
        <qty_invoiced>10</qty_invoiced>
        <unit_price>125.00</unit_price>
        <extended_price>1250.00</extended_price>
      </group>
    </lines>
  </form>
</datastream>

Customizing Forms via Business Rules

The most powerful way to add data to Crystal Forms is through Business Rules triggered on the Form Datastream Created event. This lets you inject additional data groups into the XML before it reaches Crystal.

See: Business Rules → Form Datastream Example

Adding Custom Groups

// Triggered on: Form Datastream Created (Invoice)
public class InvoiceFormCustomizer : Rule
{
    public override RuleResult Execute()
    {
        // Add a header group — maps to a Crystal Reports section named HDRTSTXDEF
        // The group name must exactly match what's defined in your .rpt file

        var customHeaderData = new Dictionary<string, string>
        {
            { "custom_po_number", Data.Fields["customer_po"]?.ToString() },
            { "custom_notes", Data.Fields["special_instructions"]?.ToString() }
        };
        Data.XMLDatastream.AddHeaderGroup("HDRTSTXDEF", customHeaderData);

        // Add line-level data
        foreach (DataRow line in Data.Set.Tables["invoice_line"].Rows)
        {
            var lineData = new Dictionary<string, string>
            {
                { "udf_field_1", line.Field<string>("udf_field_1") }
            };
            Data.XMLDatastream.AddLineGroup("LINETSTDEF", lineData);
        }

        return RuleResult.Success;
    }
}

Sorting Form Lines

You can also reorder lines before they reach Crystal:

// Sort invoice lines by a UDF sort field retrieved from the database
public override RuleResult Execute()
{
    var forms = Data.XMLDatastream.Forms;

    foreach (var form in forms)
    {
        // Retrieve sort data from DB
        var lineData = GetLineSortOrder(form.InvoiceNo);

        // Sort the lines collection
        form.Lines = form.Lines
            .OrderBy(l => lineData.GetValueOrDefault(l.LineNo, 0))
            .ToList();
    }

    return RuleResult.Success;
}

PrintMode Settings

Controlled via the PrintMode parameter when creating a session:

Mode Behavior
Browser PDF rendered in the user's web browser
Local Sent directly to user's local printer
Network Sent to a network printer

Common Form Types

Form Type P21 Trigger Datastream Name
Invoice Post invoice INVOICE
Purchase Order Print PO PURCHASEORDER
Pick Ticket Release order PICKTICKET
Packing Slip Ship order PACKINGSLIP
Statement Print statement STATEMENT
Check Print check CHECK

Datastream Group Names Are Case-Sensitive

The group names in your business rule (HDRTSTXDEF, LINETSTDEF) must exactly match the section names in your Crystal .rpt file. A mismatch silently produces no output.