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

# Remittance Advices (REMADV)

365 business Banking imports EDIFACT/EANCOM REMADV remittance advices and exports them for outgoing payments. For how users work with them, see [Remittance advices](../../en-us/365-business-banking/remittance-advice/index.mdx). Your extension can hand in files it received itself, take over or adjust the import and the export, add segments to the messages that are written, and transmit the exported files instead of offering them for download.

| You want to | Use |
| --- | --- |
| Import a file your extension received, for example from an EDI provider | `ImportFromIntegration` of `bdev.BNK REMADV Import` |
| Adjust a parsed advice or find the payer yourself | `OnAfterParseRemittanceAdviceMessage`, `OnAfterResolvePayer` |
| Route a file to an importer of your own | `OnBeforeImportRemittanceAdvice` |
| React to every import, for example to notify someone | `OnAfterImportRemittanceAdvice` |
| Create the export from code | `CreateExport` of `bdev.BNK REMADV Export` |
| Change sender or recipient identification, or add segments | `OnBeforeWriteInterchange`, `OnAfterBuildRemittanceAdviceMessage` |
| Send the exported file yourself instead of downloading it | `OnTransmitRemittanceAdviceExport` |
| Read or write EDIFACT yourself | codeunit `bdev.BNK EDIFACT Syntax` |

<Callout type="tip" title="Good to know">
The REMADV import and export, their events and the EDIFACT codeunit have been introduced with 365 business Banking version 18.4.
</Callout>

## Import

The public codeunit `bdev.BNK REMADV Import` (`5524301`) imports one file at a time.

```pascal
    procedure ImportFromIntegration(var InStr: InStream; FileName: Text): Integer
```

| Parameter | Type | Description |
| --------- | ---- | ----------- |
| `InStr` | InStream | The content of the REMADV file. |
| `FileName` | Text | The name of the file, kept in the import protocol. |
| *returns* | Integer | The entry number of the `bdev.BNK REMADV Import` protocol entry, or 0 when a subscriber of `OnBeforeImportRemittanceAdvice` took over. |

The import always writes a protocol entry, also when the file cannot be parsed; its **Imported Via** is *Integration*. For every message of a syntactically consistent file, it creates a remittance advice (`bdev.BNK Remit. Advice Header`) with its documents and deductions. A file whose UNH/UNT or UNB/UNZ counters or references do not match, or that contains no message, is recorded with status *Error*, and nothing else is created from it. A message that was already imported (same payer, advice number and date) is skipped and named in the protocol's error text. A message that cannot be processed is rolled back on its own: the other messages of the file are kept, and the protocol entry gets status *Error* with the reason in **Error Text**.

<Callout type="caution" title="The import commits">
`ImportFromIntegration` calls `Commit()` several times: after writing the protocol entry, after a syntax error, after reading the interchange header and after the last message. Do not call it inside a write transaction you want to be able to roll back as a whole.
</Callout>

`ImportFromStream` does the same for a file a user uploaded and records *Upload* as the origin; use `ImportFromIntegration` from your code. `UploadFiles` asks the user for one file after another and is meant for actions on pages.

### OnBeforeImportRemittanceAdvice - Event

```pascal
    [IntegrationEvent(false, false)]
    local procedure OnBeforeImportRemittanceAdvice(var InStr: InStream; FileName: Text; var IsHandled: Boolean)
    begin
    end;
```

Raised before a file is imported. Set `IsHandled` to `true` to take over the import yourself, for example to route the file to a different importer; the standard import is skipped and the import procedure returns 0.

### OnAfterParseRemittanceAdviceMessage - Event

```pascal
    [IntegrationEvent(false, false)]
    local procedure OnAfterParseRemittanceAdviceMessage(var RemitAdvHeader: Record "bdev.BNK Remit. Advice Header"; var TempSegment: Record "bdev.BNK EDIFACT Segment" temporary)
    begin
    end;
```

Raised for every message after it has been mapped into a remittance advice header and before the header is checked for cancellation, replacement and duplicates. Change the header to correct what a sender writes in a non-standard way. `TempSegment` is filtered to the segments of this message, from UNH to UNT; read them with `bdev.BNK EDIFACT Syntax`.

### OnAfterResolvePayer - Event

```pascal
    [IntegrationEvent(false, false)]
    local procedure OnAfterResolvePayer(var RemitAdvHeader: Record "bdev.BNK Remit. Advice Header"; var Resolved: Boolean)
    begin
    end;
```

Raised after the standard payer resolution, which tries the REMADV partners, then the GLN of the customers, then the REMADV identification of customers and vendors, and then the payer's IBAN. `Resolved` says whether an account was found. Set **Account Type** and **Account No.** of the header yourself to resolve a payer the standard does not find, for example through an identification your extension stores.

### OnAfterImportRemittanceAdvice - Event

```pascal
    [IntegrationEvent(false, false)]
    local procedure OnAfterImportRemittanceAdvice(var REMADVImport: Record "bdev.BNK REMADV Import")
    begin
    end;
```

Raised after a file has been imported, whether or not it could be parsed. It is not raised when a subscriber of `OnBeforeImportRemittanceAdvice` took over the import. **Status** (`bdev.BNK REMADV Import Status`) and **Error Text** of the protocol entry tell what happened.

### Example: importing files from an EDI provider

```pascal
codeunit 50010 "My EDI REMADV Inbox"
{
    procedure ImportReceivedFile(var MyEdiMessage: Record "My EDI Message")
    var
        REMADVImport: Codeunit "bdev.BNK REMADV Import";
        InStr: InStream;
        EntryNo: Integer;
    begin
        MyEdiMessage.CalcFields(Content);
        MyEdiMessage.Content.CreateInStream(InStr);
        EntryNo := REMADVImport.ImportFromIntegration(InStr, MyEdiMessage."File Name");

        MyEdiMessage."REMADV Import Entry No." := EntryNo;
        MyEdiMessage.Modify();
    end;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"bdev.BNK REMADV Import", 'OnAfterResolvePayer', '', false, false)]
    local procedure ResolveByEdiPartner(var RemitAdvHeader: Record "bdev.BNK Remit. Advice Header"; var Resolved: Boolean)
    var
        MyEdiPartner: Record "My EDI Partner";
    begin
        if (Resolved) then
            exit;
        if (not MyEdiPartner.Get(RemitAdvHeader."Payer ID")) then
            exit;

        RemitAdvHeader."Account Type" := RemitAdvHeader."Account Type"::Customer;
        RemitAdvHeader."Account No." := MyEdiPartner."Customer No.";
        Resolved := true;
    end;
}
```

## Export

The public codeunit `bdev.BNK REMADV Export` (`5524302`) writes REMADV files for payment journal lines.

```pascal
    procedure CreateExport(var GenJnlLine: Record "Gen. Journal Line")
```

`GenJnlLine` carries the lines to export, as a record or as filters. The export is limited to the journal batch of the first line within them. A line qualifies when it is a payment or refund to a customer or vendor whose card has **Send Remittance Advice (REMADV)** set and an identification: a REMADV partner ID with its qualifier, or else a GLN. A qualifying line without a remittance advice number gets the next number from the number series. Lines that do not qualify are listed in one message at the end. The sender identification is the **REMADV Sender ID** of the Banking Setup, or else the GLN of the company information; without either, the export stops with an error. One file is written per recipient and stored in the export history (`bdev.BNK REMADV Export`), with the file in **File Content**. In an interactive session the files are downloaded, several of them as a ZIP. Without a user interface, for example in a job queue entry, the download is skipped and the files stay in the history.

<Callout type="caution" title="The export commits">
`CreateExport` calls `Commit()` while it writes the files and the export history.
</Callout>

### OnBeforeCreateRemittanceAdviceExport - Event

```pascal
    [IntegrationEvent(false, false)]
    local procedure OnBeforeCreateRemittanceAdviceExport(var GenJnlLine: Record "Gen. Journal Line"; var IsHandled: Boolean)
    begin
    end;
```

Raised before the export starts. Set `IsHandled` to `true` to take over the whole export.

### OnBeforeWriteInterchange - Event

```pascal
    [IntegrationEvent(false, false)]
    local procedure OnBeforeWriteInterchange(var SenderId: Text[35]; var SenderQualifier: Code[4]; var RecipientId: Text[35]; var RecipientQualifier: Code[4])
    begin
    end;
```

Raised for every file before its interchange header (UNB) is written. Change the sender or recipient identification, for example when your EDI provider expects its own routing IDs.

### OnAfterBuildRemittanceAdviceMessage - Event

```pascal
    [IntegrationEvent(false, false)]
    local procedure OnAfterBuildRemittanceAdviceMessage(var GenJnlLine: Record "Gen. Journal Line"; var EDIFACTSyntax: Codeunit "bdev.BNK EDIFACT Syntax")
    begin
    end;
```

Raised for every message after its header segments and its documents with their deductions have been written, before the message summary (UNS, MOA+12) and UNT. `GenJnlLine` is the first payment journal line of the message. Call `EDIFACTSyntax.AddSegment` to add segments to the open message; the segment count in UNT includes them.

### OnTransmitRemittanceAdviceExport - Event

```pascal
    [IntegrationEvent(false, false)]
    local procedure OnTransmitRemittanceAdviceExport(var REMADVExport: Record "bdev.BNK REMADV Export"; var TempBlob: Codeunit "Temp Blob"; var Handled: Boolean)
    begin
    end;
```

Raised for every file after it has been written and its history entry committed. Send the file in `TempBlob` yourself and set `Handled` to `true`: the history entry is marked **Transmitted**, and the file is not offered for download.

### OnAfterRemittanceAdviceExport - Event

```pascal
    [IntegrationEvent(false, false)]
    local procedure OnAfterRemittanceAdviceExport(var REMADVExport: Record "bdev.BNK REMADV Export")
    begin
    end;
```

Raised for every file at the end, after it was transmitted by a subscriber or added to the download.

### Example: sending the files through an EDI provider

```pascal
codeunit 50011 "My EDI REMADV Outbox"
{
    [EventSubscriber(ObjectType::Codeunit, Codeunit::"bdev.BNK REMADV Export", 'OnTransmitRemittanceAdviceExport', '', false, false)]
    local procedure SendThroughEdiProvider(var REMADVExport: Record "bdev.BNK REMADV Export"; var TempBlob: Codeunit "Temp Blob"; var Handled: Boolean)
    var
        MyEdiClient: Codeunit "My EDI Client";
    begin
        Handled := MyEdiClient.Send(REMADVExport."File Name", TempBlob);
    end;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"bdev.BNK REMADV Export", 'OnAfterBuildRemittanceAdviceMessage', '', false, false)]
    local procedure AddContactSegment(var GenJnlLine: Record "Gen. Journal Line"; var EDIFACTSyntax: Codeunit "bdev.BNK EDIFACT Syntax")
    var
        Elements: List of [Text];
    begin
        // CTA+AD+:ACCOUNTS PAYABLE
        Elements.Add('AD');
        Elements.Add(':' + EDIFACTSyntax.Escape('ACCOUNTS PAYABLE'));
        EDIFACTSyntax.AddSegment('CTA', Elements);
    end;
}
```

`Send` stands for whatever your extension uses to deliver the file; when it fails and returns `false`, the file is offered for download as usual.

## EDIFACT Syntax

The public codeunit `bdev.BNK EDIFACT Syntax` (`5524300`) reads and writes EDIFACT interchanges. It carries no business logic. The import uses it to read files, and the export hands its writer to `OnAfterBuildRemittanceAdviceMessage`.

### Reading

`Parse` splits an interchange into the temporary table `bdev.BNK EDIFACT Segment` (`5524308`): one record per segment, with its **Tag**, its **Raw Text** and the separators the interchange declares in UNA (or the EDIFACT defaults).

| Procedure | Returns |
| --- | --- |
| `Parse(var InStr; var TempSegment)` | fills `TempSegment` with the segments of the interchange |
| `Validate(var TempSegment; var Errors)` | adds an error text for every UNH/UNT and UNB/UNZ counter or reference that does not match |
| `ElementCount(var TempSegment)` | the number of elements of the current segment |
| `GetElement(var TempSegment; ElementNo)` | an element, unescaped, components still joined |
| `ComponentCount(var TempSegment; ElementNo)` | the number of components of an element |
| `GetComponent(var TempSegment; ElementNo; ComponentNo)` | one component, unescaped |
| `GetDecimal` / `TryGetDecimal` | a component as a decimal, honoring the declared decimal mark (a comma is accepted as well); `TryGetDecimal` reports whether the text was a number instead of returning 0 |
| `ParseDecimalText` / `TryParseDecimalText` | the same for a text you already extracted |
| `GetDate(Value; FormatQualifier)` | a date from a DTM value with format 102 (CCYYMMDD), 101 (YYMMDD) or 203 (CCYYMMDDHHMM) |

Element and component numbers start at 1.

### Writing

| Procedure | Does |
| --- | --- |
| `BeginInterchange(SenderId; SenderQualifier; RecipientId; RecipientQualifier; ControlRef)` | starts an interchange: UNA and UNB |
| `BeginMessage(MessageRef)` | opens a message: UNH |
| `AddSegment(Tag; ElementValues)` | writes a segment; every element must already be escaped |
| `Escape(Value)` | escapes one simple element |
| `BuildComposite(ComponentValues)` | escapes components and joins them into one composite element |
| `EndMessage()` | closes the message: UNT with the segment count |
| `EndInterchange()` | closes the interchange: UNZ with the message count |
| `SetUseCRLF(NewUseCRLF)` | writes a line break after every segment; off by default, which is EANCOM conformant |
| `WriteTo(var OutStr)` | writes the interchange built so far |

## Tables

| Table | Holds |
| --- | --- |
| `bdev.BNK REMADV Import` (`5524300`) | one entry per imported file: the original file, **Imported Via**, **Status**, **Error Text**, the interchange's sender, recipient and control reference |
| `bdev.BNK Remit. Advice Header` (`5524301`) | one remittance advice per message: advice number and dates, payer identification, IBAN and name, the resolved **Account Type** and **Account No.**, amounts and status |
| `bdev.BNK REMADV Export` (`5524306`) | one entry per exported file: batch, file, interchange control reference, number of messages, total amount, **Transmitted** |
| `bdev.BNK EDIFACT Segment` (`5524308`) | temporary: the segments of a parsed interchange |
