Skip to content

Integrating the B2Brouter Contact Page Part

The B2Brouter page part links a Business Central customer on your own customer page to a contact in your B2Brouter account. As on the standard Customer Card, it provides contact lookup, creation and updates, Peppol receiver checks, and transport/document type settings.

Requirements and scope

  • Install and license Document Dispatch and configure user access. Reference Document Dispatch and CORE by Simova in your extension's app.json, using the IDs, publisher and compatible versions of the installed apps. Download their symbols.
  • Configure the account ID and API key in B2Brouter Module Setup. Test mode selects staging; otherwise production is used. The example's visibility check only checks for an account ID, without validating the selected connection.
  • Users need access to the host page and Customer, plus the appropriate Document Dispatch permissions. SIM_EDS USER and SIM_EDS ADMIN include the part, its contact table and management codeunit. Visibility does not grant permissions.
  • Use a saved customer with a nonempty number. Adapt object ID 50100, names and captions to your solution.

Custom pages based on Customer

The complete workflow currently requires SourceTable = Customer. Although SetContext accepts a table ID, Create Contact, Update Contact and routing edits internally retrieve a Customer by the supplied number. Update confirmation also resolves the link with Database::Customer. Passing an unrelated custom table ID does not adapt those actions to that table. Map a separate custom entity to a real customer first, then pass that customer's number, table ID and data.

Objects and stored data

The embedded object is page 5492741, SIM_EDS B2Brouter Contact LP. Despite its LP suffix, its PageType is CardPart. Its source is table 5492683, SIM_EDS B2Brouter Contact, with SourceTableTemporary = true.

The part uses a temporary display row and reads persistent setup using the composite key No. + Table Id. Pages using the same customer key share the link. The stored record contains the contact ID, directory settings/results, transport/document types and pending routing state. The key has no account or environment dimension: changing test mode does not select a separate local mapping automatically. Check the linked contact in the selected account.

Populate the part with SetContext

SubPageLink associates the part with the customer key. Additionally call the public SetContext procedure to populate its temporary row and search variables. A SubPageLink alone is insufficient.

procedure SetContext(ParamNoCode: Code[50]; ParamTableIdInteger: Integer;
    ParamNameText: Text; ParamCountryCodeText: Text;
    ParamSchemeCodeText: Text; ParamIdentifierText: Text; ParamEmailText: Text)
Parameter Type Value and purpose
ParamNoCode Code[50] Rec."No.": customer number and first part of the stored key.
ParamTableIdInteger Integer Database::Customer: second part of the key; must match SubPageLink.
ParamNameText Text Rec.Name: search text for contact lookup.
ParamCountryCodeText Text Rec."Country/Region Code": country for directory lookup.
ParamSchemeCodeText Text '' for VAT/country lookup, or a valid B2Brouter scheme code. The part accepts up to 100 characters.
ParamIdentifierText Text Rec."VAT Registration No.": default directory identifier.
ParamEmailText Text Rec."E-Mail": considered when building the available transport choices.

Each call rebuilds the temporary row and loads an existing mapping. Saved Directory Scheme Code overrides the passed scheme. A nonempty saved Lookup Identifier is used when a scheme is selected; otherwise the passed default identifier is used. The part manages scheme selection and validation. Passing an empty scheme does not reset a saved choice.

Opening or switching customers does not create a persistent mapping. Saving directory settings, linking/creating a contact or checking the directory can create one. For a linked contact, SetContext can retrieve routing data from B2Brouter. The loaded context is cached to avoid a new retrieval on every field validation; this is not exclusively a local display operation.

Complete example: custom customer card

This standalone page uses Customer and embeds the part in a B2Brouter group. It refreshes the context on record changes and after edits. Its ten transferred fields request a remote update only when their values actually change.

page 50100 "My B2Brouter Customer Card"
{
    PageType = Card;
    SourceTable = Customer;
    ApplicationArea = All;
    Caption = 'My Customer Card';

    layout
    {
        area(Content)
        {
            group(General)
            {
                Caption = 'General';
                field(CustomerNo; Rec."No.")
                {
                    ApplicationArea = All;
                }
                field(Name; Rec.Name)
                {
                    ApplicationArea = All;
                    trigger OnValidate()
                    begin
                        HandleCustomerChange(Rec.Name <> xRec.Name);
                    end;
                }
                field(Address; Rec."Address")
                {
                    ApplicationArea = All;
                    trigger OnValidate()
                    begin
                        HandleCustomerChange(Rec."Address" <> xRec."Address");
                    end;
                }
                field(Address2; Rec."Address 2")
                {
                    ApplicationArea = All;
                    trigger OnValidate()
                    begin
                        HandleCustomerChange(Rec."Address 2" <> xRec."Address 2");
                    end;
                }
                field(PostCode; Rec."Post Code")
                {
                    ApplicationArea = All;
                    trigger OnValidate()
                    begin
                        HandleCustomerChange(Rec."Post Code" <> xRec."Post Code");
                    end;
                }
                field(City; Rec."City")
                {
                    ApplicationArea = All;
                    trigger OnValidate()
                    begin
                        HandleCustomerChange(Rec."City" <> xRec."City");
                    end;
                }
                field(County; Rec."County")
                {
                    ApplicationArea = All;
                    trigger OnValidate()
                    begin
                        HandleCustomerChange(Rec."County" <> xRec."County");
                    end;
                }
                field(CountryCode; Rec."Country/Region Code")
                {
                    ApplicationArea = All;
                    trigger OnValidate()
                    begin
                        HandleCustomerChange(Rec."Country/Region Code" <> xRec."Country/Region Code");
                    end;
                }
                field(VATRegistrationNo; Rec."VAT Registration No.")
                {
                    ApplicationArea = All;
                    trigger OnValidate()
                    begin
                        HandleCustomerChange(Rec."VAT Registration No." <> xRec."VAT Registration No.");
                    end;
                }
                field(Email; Rec."E-Mail")
                {
                    ApplicationArea = All;
                    trigger OnValidate()
                    begin
                        HandleCustomerChange(Rec."E-Mail" <> xRec."E-Mail");
                    end;
                }
                field(PhoneNo; Rec."Phone No.")
                {
                    ApplicationArea = All;
                    trigger OnValidate()
                    begin
                        HandleCustomerChange(Rec."Phone No." <> xRec."Phone No.");
                    end;
                }
            }
            group(B2Brouter)
            {
                Caption = 'B2Brouter';
                Visible = B2BrouterVisible;
                part(B2BrouterContact; "SIM_EDS B2Brouter Contact LP")
                {
                    ApplicationArea = All;
                    SubPageLink = "No." = field("No."),
                                  "Table Id" = const(Database::Customer);
                }
            }
        }
    }

    trigger OnOpenPage()
    var
        EDSSetup: Record "SIM_EDS Setup";
        COREManagement: Codeunit "SIM_CORE Mgt";
    begin
        if not COREManagement.CheckPermissions(
            UserSecurityId(), 'SIM_EDS USER | SIM_EDS ADMIN | SUPER')
        then
            exit;
        if not EDSSetup.Get() then
            exit;
        B2BrouterVisible := (EDSSetup."B2Brouter Account Id" <> '') or
            (EDSSetup."B2Brouter Account Id Staging" <> '');
    end;

    trigger OnAfterGetCurrRecord()
    begin
        UpdateB2BrouterContext();
    end;

    trigger OnModifyRecord(): Boolean
    begin
        UpdateB2BrouterContext();
        exit(true);
    end;

    local procedure UpdateB2BrouterContext()
    begin
        if not B2BrouterVisible then
            exit;
        CurrPage.B2BrouterContact.Page.SetContext(
            Rec."No.", Database::Customer, Rec.Name,
            Rec."Country/Region Code", '',
            Rec."VAT Registration No.", Rec."E-Mail");
    end;

    local procedure HandleCustomerChange(Changed: Boolean)
    begin
        UpdateB2BrouterContext();
        if B2BrouterVisible and Changed then
            CurrPage.B2BrouterContact.Page.ConfirmCustomerContactUpdate(Rec);
    end;

    var
        B2BrouterVisible: Boolean;
}

Adapt the example to your page

  • Integrate the group, variables, procedures and triggers into your own Customer page. For a Page Extension, place the group using addlast(Content) or an appropriate addafter(...) anchor. Extend existing field validations with modify(...) and OnAfterValidate, calling HandleCustomerChange(Rec.<field> <> xRec.<field>).
  • CurrPage.B2BrouterContact.Page... refers to the control name in part(B2BrouterContact; ...), not the embedded page's object name.
  • Call SetContext in OnAfterGetCurrRecord to show the correct link when navigating. Refresh the context after changes to name, country, VAT number or email and after saving. Preserve existing host validation and save logic.
  • To match the standard card, handle Name, Address, Address 2, Post Code, City, County, Country/Region Code, VAT Registration No., E-Mail and Phone No.. After updating the context, pass the full current customer, including unsaved field edits, to the public ConfirmCustomerContactUpdate(ParamRecordCustomer: Record Customer). It asks before updating a linked contact. Declined or failed updates retain the BC edit.
  • Do not request confirmation merely when opening, navigating or saving unchanged data. The example's OnModifyRecord only refreshes the context. The part manages its actions; the host does not call internal procedures in SIM_EDS B2Brouter Contact Mgt.
  • The standard Customer Card already contains this part. Add it to your own page without duplicating it on the standard card.

Available functions

Lookup Contact selects an existing account contact. Create Contact uses the stored customer data, including address and phone; save the customer first. Update Contact transfers current customer data after confirmation. The host only needs the two public procedures described above.

For Check Peppol Receiver, leave the scheme empty to use VAT number and country, or select a scheme and enter its identifier. The result and check time are stored. Changes to country, scheme or identifier cause the receiver status to indicate changed data until checked again. Receiver registration and an account contact link are separate results.

Transport Type and Document Type are loaded for a linked contact. Use the lookups for supported values and select the transport first. For Peppol, receiver formats must be confirmed for the current identifier. Routing edits request confirmation; declined or failed edits remain Routing Changes Pending. Update Contact retries them. Refresh Contact reloads remote values and asks before discarding pending changes. These settings do not replace the dispatch profile or JSON schema configuration.

Verify the integration

  1. Open a saved customer without a mapping: the visible part should show its context without creating a contact merely by opening the page.
  2. Switch between two customers and reopen the page. Check each link and saved directory setting.
  3. Link a contact, check a receiver and create a contact for another saved customer in the configured test account. Check the data in B2Brouter.
  4. Edit each transferred field. Actual changes to a linked customer should offer an update; opening or saving unchanged data should not prompt. Test acceptance and refusal.
  5. Change routing, decline, then retry with Update Contact. Test Refresh Contact with pending changes and receiver status after changing identifier/country.
Symptom What to check
Part hidden Permissions, setup record, visibility variable and production/staging account IDs.
Empty or wrong context Current Rec, control name, OnAfterGetCurrRecord, all seven SetContext arguments and matching SubPageLink key.
Missing customer during an action Saved customer number and Database::Customer; unrelated table IDs are not supported by the complete workflow.
Connection error or no routing choices Selected environment/account/API key, linked contact, country/scheme/identifier and supported receiver formats.
Repeated update prompts Confirm only actual field changes, not from navigation or save triggers.

See also