---
name: create-print-layout
description: >
    Use when creating or modifying .printlayoutv2.xml files for CG Mobile printed or exported
    documents (invoices, receipts, reports, PDFs). Invokes create-business-object first if the
    target BO does not yet exist.
    Trigger phrases: "print layout for X", "receipt for X", "invoice for X", "PDF for X",
    ".printlayoutv2.xml", "thermal print for X", "print document for X", "printed report for X".
---

# Create Print Layout — CG Mobile Modeler

A `.printlayoutv2.xml` file is a document template for generating PDFs (standard A4) or
thermal receipts (3-inch ESC/POS). It declares its input data in a `<Declarations>` block,
then describes visual content (tables, headings, images, iteration) inside `<ReportLayout>`.
The layout is invoked from a Process via a `PRINTV2` action that maps ProcessContext variables
to the Declaration names.

All layouts live under `src/<Module>/PL/<LayoutName>/<LayoutName>.printlayoutv2.xml`.

---

## When to Use This Skill

| Invoked by      | Phrase                                                                     |
| --------------- | -------------------------------------------------------------------------- |
| User directly   | "print layout for X", "invoice PDF", "receipt for X", ".printlayoutv2.xml" |
| Workflow skills | `add-detail-screen` when the detail screen needs a print action            |

NOT for:

-   Writing the PRINTV2 process action — use `create-process`
-   Designing the BO that provides the data — use `create-business-object`
-   DataSource query design — use `create-datasource`

---

## Prerequisites

1. **Confirm all `<DataDeclaration>` types resolve to existing BOs or LUs.** If a required BO
   does not exist, invoke `create-business-object` first and return here after completion.

2. **Confirm any image assets referenced by `imageId` exist in `src/Images/`.** Check before
   adding an Image declaration — a missing asset renders a blank image with no build error.

3. **Identify the calling process.** A print layout is always triggered by a PRINTV2 action in
   a `.processflow.xml`. Confirm the process will pass the correct Input parameters matching
   every `<DataDeclaration name="...">` in this layout.

---

## Mandatory Checklist

-   [ ] File: `src/<Module>/PL/<Name>/<Name>.printlayoutv2.xml` — name matches stem exactly
-   [ ] Root `<PrintLayout>` has `xmlns="https://www.salesforce.com/cgcloud/xsds"` and a unique `name`
-   [ ] Every `<DataDeclaration>` type is a real BO or LU class name (verify in codebase)
-   [ ] Image declarations have both `mimeType` and `imageId` set
-   [ ] `<ReportLayout pageMargins="[top,right,bottom,left]">` present on standard PDF (omit on thermal)
-   [ ] All `{{Declarations::X}}` macros reference declared variable names exactly
-   [ ] Every `{{Labels::...}}` macro includes a non-empty `defaultLabel=` fallback
-   [ ] Every `<table>` has a `<thead>` (single `<tr>` row only); column widths in thead sum to 100%

---

## Failure Modes

### Failure mode 1: Print button throws "unknown type"

**Symptom:** Tapping the print button in the simulator shows an error about an unknown or
unresolved BO/LU type.
**Diagnosis:** A `<DataDeclaration type="Bo...">` references a class that does not exist in
the workspace (the BO or LU has not been created or is named differently).
**Fix:** Verify each `type=` value by searching for the corresponding `.businessobject.xml` or
`.lookupobject.xml`. If the BO doesn't exist, create it with `create-business-object`.

---

### Failure mode 2: Image renders blank

**Symptom:** The printed document shows an empty image placeholder where the logo or signature
should appear.
**Diagnosis (a):** The `imageId` in the `<DataDeclaration>` does not match any file in `src/Images/`.
**Diagnosis (b):** The PRINTV2 Input for this declaration was not passed from the calling process.
**Fix (a):** Run `ls src/Images/` and align `imageId` to a
real file name. **Fix (b):** Open the calling process and add the missing `<Input name="...">`.

---

### Failure mode 3: Field shows literal `{{Declarations::...}}` text

**Symptom:** The rendered PDF contains raw macro text instead of resolved values.
**Diagnosis:** Either the variable name in the macro does not match any `<DataDeclaration name="...">`,
or the calling PRINTV2 action does not pass that input.
**Fix:** Compare every `{{Declarations::<name>...}}` reference against the `<Declarations>` block.
The names are case-sensitive. Also confirm the calling process maps a value to that name.

---

### Failure mode 4: Labels show raw id instead of text

**Symptom:** Labels render as the key string (e.g., `"AddressId"`) instead of localized text.
**Diagnosis:** `defaultLabel=` is missing or empty, and no i18n key is registered for that id.
**Fix:** Every `{{Labels::<id>; defaultLabel=<text>}}` macro must include a non-empty `defaultLabel`.
Add the fallback text to guarantee the label renders correctly in all locales.

---

### Failure mode 5: Table layout breaks on long data

**Symptom:** Table columns overflow or overlap; text is truncated.
**Diagnosis:** Columns use pixel widths that don't sum correctly, or no width strategy is set.
**Fix:** Use `width="*"` on the column that should absorb remaining space, or switch all widths
to fixed pixel values that sum to the page width minus margins. Avoid mixing percentage and
pixel widths in the same `<thead>`.

---

### Failure mode 6: Thermal layout validation error — `pageMargins` rejected

**Symptom:** Build fails with a validation error referencing `pageMargins` on a thermal layout.
**Diagnosis:** `<ReportLayout pageMargins="...">` is present — this attribute is not supported
on thermal layouts.
**Fix:** Remove the `pageMargins` attribute from `<ReportLayout>` entirely.

---

## Minimal Template (inline)

Based on `src/Order/PL/OrderConfirmationPDF/OrderConfirmationPDF.printlayoutv2.xml` (standard PDF):

```xml
<PrintLayout xmlns="https://www.salesforce.com/cgcloud/xsds"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             name="OrderConfirmationPDF">
  <Declarations>
    <DataDeclaration name="order"       type="BoOrder" />
    <DataDeclaration name="salesOrg"    type="LuSalesOrg" />
    <DataDeclaration name="myImageName" type="Image" mimeType="image/png" imageId="CompanyLogo" />
    <DataDeclaration name="signature1"  type="Signature" />
  </Declarations>
  <ReportLayout pageMargins="[40,40,40,40]">
    <table name="ManufacturerTable" tableLayout="noBorders">
      <tbody>
        <tr>
          <td width="260" alignment="center" rowSpan="3">
            <img src="{{Declarations::myImageName}}" width="100" />
          </td>
          <td width="100" />
          <td width="130" bold="true">{{Declarations::salesOrg.text}}</td>
        </tr>
      </tbody>
    </table>
    <table name="OrderItemsTable" tableLayout="lightHorizontalLinesMainItemsOnly">
      <thead>
        <tr>
          <th width="*">{{Labels::TextId; defaultLabel=Product}}</th>
          <th width="25" alignment="right">{{Labels::QuantityId; defaultLabel=Qty}}</th>
          <th width="55" alignment="right">{{Labels::AmountId; defaultLabel=Amount}}</th>
        </tr>
      </thead>
      <tbody>
        <each name="itemsEach" value="{{Declarations::order.loPrintItems}}">
          <tr>
            <td>{{.text1}}</td>
            <td alignment="right">{{.quantity}}</td>
            <td alignment="right">{{.valueReceipt; numberFormat=8.2}}</td>
          </tr>
        </each>
      </tbody>
    </table>
  </ReportLayout>
</PrintLayout>
```

Full templates in `templates/`.

---

## Common Variants

### Variant 1 — Standard PDF (order confirmation / invoice)

Use when: generating a full-page A4 document with logo, customer info, line-item table, totals,
and a signature block.

Template: `templates/standard-pdf.printlayoutv2.xml.template`

Real files:

-   `src/Order/PL/OrderConfirmationPDF/OrderConfirmationPDF.printlayoutv2.xml`
-   `src/Order/PL/DirectCashInvoicePDF/DirectCashInvoicePDF.printlayoutv2.xml`
-   `src/Order/PL/VanSalesCashInvoicePDF/VanSalesCashInvoicePDF.printlayoutv2.xml`

Key patterns:

-   `pageMargins="[40,40,40,40]"` on `<ReportLayout>`
-   Logo in a `<td rowSpan="N">` so it spans the header rows
-   Signature via `<img src="{{Declarations::signature1}}" width="160" />`
-   `<sum table="TableName" col="N" numberFormat="8.2" />` for totals
-   `<correlation>` inside `<each>` for per-item condition rows

---

### Variant 2 — Thermal receipt (van sales / POS)

Use when: generating a narrow 3-inch receipt for ESC/POS printers.

Template: `templates/receipt-thermal.printlayoutv2.xml.template`

No real thermal example exists in `src/` — the template
is derived from `ai-wiki/wiki/print-layouts.md` documented patterns. See
`references/thermal-vs-standard.md` for the full list of thermal constraints.

Key patterns:

-   No `pageMargins` attribute on `<ReportLayout>`
-   No `<header>`, `<footer>`, or `<pageNumber/>`
-   `<lineFeed/>` between sections, `<rule/>` as separator
-   Column widths in percentages summing to 100%

---

### Variant 3 — Multi-section PDF with conditions

Use when: the document needs header-level condition rows (discounts, surcharges) plus item
conditions via `<correlation>`.

Extend the standard-pdf template:

1. Add a `loSdoConditions` LO property on the declared BO
2. Add a `<each>` over header conditions with a filter on `sdoItemPKey` being empty
3. Add `<correlation>` inside the items `<each>` for per-item conditions

Real file: `src/Order/PL/OrderConfirmationPDF/OrderConfirmationPDF.printlayoutv2.xml`
(see the `HeaderConditionsTable` and `correlation1` sections)

---

## Decision Table

| User wants...                      | Template                         | Key attributes                                          |
| ---------------------------------- | -------------------------------- | ------------------------------------------------------- |
| Contract / invoice A4 PDF          | `standard-pdf`                   | `pageMargins="[40,40,40,40]"`, logo, signature          |
| Narrow receipt for ESC/POS printer | `receipt-thermal`                | No `pageMargins`, `<lineFeed/>`, `<rule/>`              |
| Multi-page detailed report         | `standard-pdf` with extra tables | Add `<header>` / `<footer>` blocks                      |
| Document with discount breakdown   | Variant 3                        | `<each>` + `<correlation>` + header conditions `<each>` |

---

## File and Folder Layout

```
src/<Module>/
└── PL/
    └── <LayoutName>/
        └── <LayoutName>.printlayoutv2.xml   ← this file
```

The `name` attribute on `<PrintLayout>` must match the folder name and filename stem exactly.
The `name` must be unique across the entire workspace — no two layouts can share the same name.

---

## Real File References

**Standard PDF — order confirmation:**

```
src/Order/PL/OrderConfirmationPDF/OrderConfirmationPDF.printlayoutv2.xml
```

Structure: 5 Declarations (BoOrder, LuSalesOrg, Image, BoOrderPrintTextItems, Signature),
6 tables, 4 `<each>` loops, 2 `<correlation>` sub-tables, 2 `<sum>` aggregates.

**Standard PDF — direct cash invoice:**

```
src/Order/PL/DirectCashInvoicePDF/DirectCashInvoicePDF.printlayoutv2.xml
```

Structure: 4 Declarations (no Signature), same table pattern with payment method and balance rows.

**Standard PDF — van sales invoice with signature:**

```
src/Order/PL/VanSalesCashInvoicePDF/VanSalesCashInvoicePDF.printlayoutv2.xml
```

Adds an `InvoiceNoId` row and two heading rows for invoice number alongside the order number.

---

## PRINTV2 Process Action (caller side)

The process that triggers the print must include a PRINTV2 action that maps ProcessContext
variables to the layout's Declaration names:

```xml
<Action name="PrintDocument" actionType="PRINTV2"
        printId="ProcessContext::PrintLayoutId"
        locale="ApplicationContext::user.languageSpoken"
        showShareButton="true"
        generateAndSave="ProcessContext::TextItems.printV2GenerateAndSave">
  <Parameters>
    <Input name="order"       value="ProcessContext::MainBO" />
    <Input name="salesOrg"    value="ApplicationContext::SalesOrg" />
    <Input name="signature1"  value="ProcessContext::TextItems.signature1MediaPath" />
  </Parameters>
  <Return name="ProcessContext::FinalPath" />
  <TransitionTo action="AfterPrint" />
</Action>
```

`<Input name="order">` must match the layout's `<DataDeclaration name="order">` exactly.

---

## Escape Hatch

When this skill is not enough, consult in order:

1. `references/pl-v2-structure.md` — root element, Declarations, ReportLayout, tables, iteration,
   correlation, sum, inline content elements, file path convention
2. `references/data-binding-macros.md` — `{{Declarations::...}}`, `{{Labels::...}}`,
   formatters (numberFormat, dateTimeFormat, toggleId), current-item `{{.field}}`, `path=` prefix
3. `references/thermal-vs-standard.md` — layout differences, constraints, detection heuristics,
   when no thermal example exists in the codebase
4. `ai-wiki/wiki/print-layouts.md` — full ~223-line wiki covering the PRINTV2 action, `<each>` / `<correlation>` /
   `<sum>` shapes, validation rules, and thermal vs standard comparison table

---

## Verify

After writing the print layout file, run a build:

```bash
# cd to your workspace root
sf mdl build 2>&1 | tail -20
```

Then trigger the print action in the simulator:

```bash
sf mdl simulate
# Navigate to the feature that triggers the PRINTV2 action
# Tap the print/export button
# Check: preview renders with no literal {{...}} text
# Check: all macros resolve (no blank fields other than legitimately empty data)
# Check: tables render with correct columns and alignment
# Check: image and signature blocks render (if Declarations include them)
# Open devtools Console: look for "unknown declaration" or "unresolved macro" errors
```

A clean build and a fully rendered preview with resolved macros confirms the layout is
correctly structured and wired.
