import { Callout } from "zudoku/ui/Callout";

# Placeholders of your own

Users compose posting texts and messages to the recipient from placeholders such as `{Sender Name}` or `{Due Date:Month}`. Your extension can add placeholders of its own: they are listed in the **Insert Placeholder** picker in a group of their own, shown with your caption in the placeholder field and resolved like the placeholders of 365 business Banking, date placeholders including their parts.

The integration point is the public codeunit `bdev.BNK Placeholder Events` (`5524229`). It publishes two events:

| Event | Raised | You |
| --- | --- | --- |
| `OnCollectPlaceholders` | when the picker or the placeholder field lists what can be inserted | add your placeholders with `AddText` or `AddDate` |
| `OnResolvePlaceholders` | when a text is resolved, after 365 business Banking set its own values | set the values of your placeholders with `SetValue` |

Both events receive the `bdev.BNK Placeholder Area` the text belongs to and the temporary buffer `bdev.BNK Placeholder` (`5523863`).

<Callout type="tip" title="Good to know">
The placeholder events have been introduced with 365 business Banking version 18.4.
</Callout>

## Placeholder Area

The enum `bdev.BNK Placeholder Area` (`5523844`) says which text is collected or resolved. Add a placeholder only to the areas where your value exists.

| Value | Text | Source record in `OnResolvePlaceholders` |
| --- | --- | --- |
| `Reconciliation Rule` | posting text of a reconciliation rule | `Bank Acc. Reconciliation Line` |
| `Split Rule` | posting text of a split rule line | `Bank Acc. Reconciliation Line`: the line that is split; the new line may not be inserted yet |
| `Payment Deduction` | posting text of a deduction | `bdev.BNK Payment Deduction` |
| `Customer Message` | customer message to the recipient of the payment suggestion | `Cust. Ledger Entry` that is refunded |
| `Vendor Message` | vendor message to the recipient | `Vendor Ledger Entry` that is paid |
| `Employee Message` | employee message to the recipient | `Employee Ledger Entry` that is paid |

## OnCollectPlaceholders - Event

```pascal
    /// <summary>
    /// Raised when the placeholders that can be inserted into a text are collected - for the
    /// placeholder picker and for the captions and sample values shown in the editor.
    /// </summary>
    [IntegrationEvent(false, false)]
    local procedure OnCollectPlaceholders(PlaceholderArea: Enum "bdev.BNK Placeholder Area"; var Placeholder: Record "bdev.BNK Placeholder" temporary)
    begin
    end;
```

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `PlaceholderArea` | Enum "bdev.BNK Placeholder Area" | The text the placeholders are collected for. |
| `Placeholder` | Record "bdev.BNK Placeholder" temporary | The catalog. Add entries with `AddText` or `AddDate`; do not change the existing entries. |

### AddText and AddDate

```pascal
    procedure AddText(GroupCaption: Text; NewToken: Text; NewCaption: Text; NewExample: Text)
    procedure AddDate(GroupCaption: Text; NewToken: Text; NewCaption: Text; NewExample: Date)
```

| Parameter | Description |
| --------- | ----------- |
| `GroupCaption` | The group the placeholder is listed under in the picker, typically the name of your app. Localize it with a label. |
| `NewToken` | The token that is stored in the text, written as `{Token}`. Keep it in English and stable, because users' texts refer to it. It must not contain `{`, `}` or `:`. |
| `NewCaption` | The caption shown in the picker and in the placeholder field, in the user's language. |
| `NewExample` | A sample value for the preview. For a date, the picker derives the samples of the date parts from it. |

A token that is already in the catalog is ignored. You cannot replace a placeholder of 365 business Banking or of another extension that was added before yours. Choose tokens that are unlikely to collide, for example by prefixing them with your product name.

## OnResolvePlaceholders - Event

```pascal
    /// <summary>
    /// Raised when a text is resolved, after 365 business Banking set the values of its own
    /// placeholders. Set the value of your tokens with SetValue.
    /// </summary>
    [IntegrationEvent(false, false)]
    local procedure OnResolvePlaceholders(PlaceholderArea: Enum "bdev.BNK Placeholder Area"; SourceRecord: RecordRef; var Placeholder: Record "bdev.BNK Placeholder" temporary)
    begin
    end;
```

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `PlaceholderArea` | Enum "bdev.BNK Placeholder Area" | The text that is resolved. |
| `SourceRecord` | RecordRef | The record the text is resolved for, see [Placeholder Area](#placeholder-area). |
| `Placeholder` | Record "bdev.BNK Placeholder" temporary | The values. Set yours with `SetValue`. |

### SetValue

```pascal
    procedure SetValue(ForToken: Text; NewValue: Text)
    procedure SetValue(ForToken: Text; NewValue: Date)
```

Set a text with the first overload and a date with the second: only a date set as a date can be written with its parts: `{Token:Day}`, `{Token:Month}`, `{Token:MonthName}`, `{Token:Quarter}`, `{Token:Year}` and `{Token:Week}`. An empty text or `0D` removes the placeholder from the text. A placeholder you do not set stays in the text as typed, so the user sees that it was not resolved.

The event is raised once per text that is resolved: for every reconciliation line a rule is applied to and for every entry the payment suggestion pays. An empty text is not resolved, and the event is not raised for it or for the preview in the editor. Read only what you need and keep the subscriber fast.

## Example

The following example offers the project number and the project start date of a project app in the vendor message to the recipient:

```pascal
codeunit 50000 "My Banking Placeholders"
{
    [EventSubscriber(ObjectType::Codeunit, Codeunit::"bdev.BNK Placeholder Events", 'OnCollectPlaceholders', '', false, false)]
    local procedure AddProjectPlaceholders(PlaceholderArea: Enum "bdev.BNK Placeholder Area"; var Placeholder: Record "bdev.BNK Placeholder" temporary)
    begin
        if (PlaceholderArea <> PlaceholderArea::"Vendor Message") then
            exit;

        Placeholder.AddText(ProjectGroupLbl, ProjectNoTok, ProjectNoLbl, 'PRJ-1001');
        Placeholder.AddDate(ProjectGroupLbl, ProjectStartTok, ProjectStartLbl, 20260301D);
    end;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"bdev.BNK Placeholder Events", 'OnResolvePlaceholders', '', false, false)]
    local procedure ResolveProjectPlaceholders(PlaceholderArea: Enum "bdev.BNK Placeholder Area"; SourceRecord: RecordRef; var Placeholder: Record "bdev.BNK Placeholder" temporary)
    var
        VendorLedgerEntry: Record "Vendor Ledger Entry";
        Project: Record "My Project";
    begin
        if (PlaceholderArea <> PlaceholderArea::"Vendor Message") then
            exit;

        SourceRecord.SetTable(VendorLedgerEntry);
        if (not Project.Get(VendorLedgerEntry."My Project No.")) then
            exit;

        Placeholder.SetValue(ProjectNoTok, Project."No.");
        Placeholder.SetValue(ProjectStartTok, Project."Starting Date");
    end;

    var
        ProjectGroupLbl: Label 'Projects';
        ProjectNoLbl: Label 'Project No.';
        ProjectStartLbl: Label 'Project Start';
        ProjectNoTok: Label 'MyApp Project No.', Locked = true;
        ProjectStartTok: Label 'MyApp Project Start', Locked = true;
}
```

A user can then write `{MyApp Project No.} from {MyApp Project Start:Month}/{MyApp Project Start:Year}`, which becomes *PRJ-1001 from 03/2026*.

## Posting text providers

Placeholders that belong to the posting texts only can also be contributed as a value of the extensible enum `bdev.BNK Posting Text Plchldr.`, implementing `bdev.BNK IPostingTextPlchldr.`. The caption of your enum value is the group the placeholders are listed under. Implement `bdev.BNK IPlchldr. Date Value` in the same codeunit to offer your dates with their parts. For new extensions, the events above are the simpler choice: they work for the messages to the recipient as well, and they hand you the source record.
