PEPPOL Sales Documents – Extension Points
The RSMSTA SalesInvPEPPOLBIS3.0 (XMLport 10058600) and RSMSTA SalesCrMemoPEPPOLBIS3.0 (XMLport 10058601) XMLports generate outbound PEPPOL BIS 3.0 sales invoices and credit memos. Both XMLports share a nearly identical structure and expose the same extension points unless noted otherwise.
There are three layers of extension points:
- XMLport events — declared directly on the XMLport (namespace
WiseCourier.Standard.eDocuments) - Courier events — raised by helper codeunits called during XMLport execution
- PEPPOL Management events — standard BC events (namespace
Microsoft.Sales.Peppol) that the XMLport delegates to for most data retrieval
Subscribe to these events in your own codeunit — no modification of the base XMLport is required.
The XMLports are responsible only for XML generation. Data validation does not occur inside the XMLport — it happens afterwards when the XML is submitted through the web service for delivery. Subscribers to these events can add or modify data freely without triggering validation errors at generation time.
XMLport events
OnBeforeAdditionalItemPropertyLoop
Fires once per line before writing AdditionalItemProperty elements. Call AddAdditionalItemPropertyToList() on the XMLport instance to inject name/value pairs that appear as <cac:AdditionalItemProperty> elements in the output.
This event uses
[IntegrationEvent(true, false)]— the first parametertruemeans “include sender”, so the XMLport instance is the sender. You callAddAdditionalItemPropertyToListon that sender instance.
// Invoice
[EventSubscriber(ObjectType::XmlPort, XmlPort::"RSMSTA SalesInvPEPPOLBIS3.0",
'OnBeforeAdditionalItemPropertyLoop', '', false, false)]
local procedure AddCustomItemProperties(SalesLine: Record "Sales Line")
var
SalesInvXmlPort: XmlPort "RSMSTA SalesInvPEPPOLBIS3.0";
begin
SalesInvXmlPort.AddAdditionalItemPropertyToList('Color', SalesLine."RSMSTA Color");
SalesInvXmlPort.AddAdditionalItemPropertyToList('BatchNo', SalesLine."Lot No.");
end;
// Credit Memo — identical signature, different XMLport
[EventSubscriber(ObjectType::XmlPort, XmlPort::"RSMSTA SalesCrMemoPEPPOLBIS3.0",
'OnBeforeAdditionalItemPropertyLoop', '', false, false)]
local procedure AddCustomItemPropertiesCrMemo(SalesLine: Record "Sales Line")
var
SalesCrMemoXmlPort: XmlPort "RSMSTA SalesCrMemoPEPPOLBIS3.0";
begin
SalesCrMemoXmlPort.AddAdditionalItemPropertyToList('Color', SalesLine."RSMSTA Color");
end;
OnGetFinalDueDateClaim (Invoice only)
Lets you override the <cbc:DueDate> element and/or inject an AdditionalDocumentReference entry with DocumentDescription = 'EINDAGI' (used for final due date claims in Icelandic invoicing).
| Parameter | Direction | Effect |
|---|---|---|
ClaimDueDate | var Text (YYYYMMDD) | Replaces <cbc:DueDate> in the XML |
ClaimFinalDueDate | var Text (YYYYMMDD) | Adds an AdditionalDocumentReference with ID = <date> and description EINDAGI |
[EventSubscriber(ObjectType::XmlPort, XmlPort::"RSMSTA SalesInvPEPPOLBIS3.0",
'OnGetFinalDueDateClaim', '', false, false)]
local procedure SetFinalDueDateClaim(
SalesHeader: Record "Sales Header";
var ClaimFinalDueDate: Text;
var ClaimDueDate: Text)
begin
if SalesHeader."Payment Method Code" = 'CLAIM30' then begin
ClaimDueDate := Format(SalesHeader."Due Date" + 30, 0, '<Year4><Month,2><Day,2>');
ClaimFinalDueDate := Format(SalesHeader."Due Date" + 60, 0, '<Year4><Month,2><Day,2>');
end;
end;
Courier events
These events are raised by helper codeunits called during XMLport execution. Subscribe on RSMSTA Wise Courier Events or RSMSTA EDocIntegrationHelpFunc as indicated.
OnAfterGetDueDateForInvoice (Invoice only)
Raised after the due date is resolved. Modify DueDate as an ISO 8601 text string before it is written to the XML.
[EventSubscriber(ObjectType::Codeunit, Codeunit::"RSMSTA Wise Courier Events",
'OnAfterGetDueDateForInvoice', '', false, false)]
local procedure AdjustDueDate(var DueDate: Text; SalesHeader: Record "Sales Header")
begin
if SalesHeader."Currency Code" = 'USD' then
DueDate := Format(SalesHeader."Due Date" + 5, 0, 9);
end;
CourierGetAccountingSupplierPartyPostalAddr
Override the supplier postal address (street, city, postal zone, country).
[EventSubscriber(ObjectType::Codeunit, Codeunit::"RSMSTA Wise Courier Events",
'CourierGetAccountingSupplierPartyPostalAddr', '', false, false)]
local procedure OverrideSupplierAddress(
SalesHeader: Record "Sales Header";
var StreetName: Text; var AdditionalStreetName: Text;
var CityName: Text; var PostalZone: Text;
var CountrySubentity: Text; var IdentificationCode: Text;
var DummyListID: Text)
begin
if SalesHeader."Responsibility Center" = 'WAREHOUSE' then begin
StreetName := 'Warehouse Road 1';
CityName := 'Reykjavik';
PostalZone := '108';
IdentificationCode := 'IS';
end;
end;
CourierGetAccountingSupplierPartyTaxSchemeBIS
Override CompanyID, schemeID, and TaxSchemeID inside PartyTaxScheme for the supplier party.
CourierOnBeforeGetAccountingSupplierPartyInfoByFormat / CourierOnAfterGetAccountingSupplierPartyInfoByFormat
Override or modify the supplier EndpointID, SchemeID, and Name before/after the standard lookup runs.
OnBeforeGetAdditionalDocRefInfoSalesInvoice / OnAfterGetAdditionalDocRefInfoSalesInvoice
Control AdditionalDocumentReference entries on invoices — attachments, embedded PDFs, external URIs. The OnBefore variant has an IsHandled parameter to skip the standard logic entirely.
OnBeforeGetAdditionalDocRefInfoSalesCrMemo / OnAfterGetAdditionalDocRefInfoSalesCrMemo
Same as above for credit memos.
OnBeforeCountAttachmentsSalesOrServiceInvoices / OnAfterCountAttachmentsSalesOrServiceInvoices
Control how many AdditionalDocumentReference loop iterations are created on invoices. Use this to add extra attachment slots beyond what the standard logic allocates.
OnBeforeCountAttachmentsCrMemoOrServiceInvoices / OnAfterCountAttachmentsCrMemoOrServiceInvoices
Same as above for credit memos.
OnAfterGetPaymentMeansPayeeFinancialAccBISInternal
Override the PayeeFinancialAccount (IBAN) and FinancialInstitutionBranch (SWIFT/BIC) inside the XMLport’s PaymentMeans section. This fires from the XMLport directly and takes precedence over the bank account lookup from the RSMSTA Use for e-Invoice flag.
[EventSubscriber(ObjectType::Codeunit, Codeunit::"RSMSTA EDocIntegrationHelpFunc",
'OnAfterGetPaymentMeansPayeeFinancialAccBISInternal', '', false, false)]
local procedure OverrideBankAccountInternal(
SalesHeader: Record "Sales Header";
var PayeeFinancialAccountID: Text;
var FinancialInstitutionBranchID: Text)
begin
if SalesHeader."Currency Code" = 'EUR' then begin
PayeeFinancialAccountID := 'IS140159260076545510730339';
FinancialInstitutionBranchID := 'NBIIISREXX';
end;
end;
OnBeforeCreateSalesInvoiceXML / OnAfterCreateSalesInvoiceXML
Intercept the entire XML generation pass for invoices.
OnBefore— fires before the XMLport runs. SetIsHandled := trueto completely replace XML generation with your own logic.OnAfter— fires after the XMLport has written the XML into aTempBlob. Post-process the blob directly (e.g., inject custom namespaces or elements via DOM manipulation).
OnBeforeCreateSalesCrMemoXML / OnAfterCreateSalesCrMemoXML
Same as above for credit memos.
PEPPOL Management events
The XMLports call PEPPOL Management (codeunit 1605) for most data retrieval. Subscribe to its events to override individual fields without touching the XMLports. These events apply to both invoices and credit memos unless noted.
Invoice header
OnAfterGetGeneralInfo — Note, TaxPointDate, AccountingCost, InvoiceTypeCode
[EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management",
'OnAfterGetGeneralInfo', '', false, false)]
local procedure SetInvoiceHeaderFields(
SalesHeader: Record "Sales Header";
var ID: Text; var IssueDate: Text; var InvoiceTypeCode: Text;
var Note: Text; var TaxPointDate: Text;
var DocumentCurrencyCode: Text; var AccountingCost: Text)
begin
Note := SalesHeader."RSMSTA Invoice Note";
TaxPointDate := Format(SalesHeader."Posting Date", 0, 9);
AccountingCost := SalesHeader."Your Reference";
end;
OnAfterGetOrderReferenceInfo
[EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management",
'OnAfterGetOrderReferenceInfo', '', false, false)]
local procedure ForceOrderReference(
SalesHeader: Record "Sales Header"; var OrderReferenceID: Text)
begin
if SalesHeader."RSMSTA Original Order No." <> '' then
OrderReferenceID := SalesHeader."RSMSTA Original Order No.";
end;
OnAfterGetBuyerReference
[EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management",
'OnAfterGetBuyerReference', '', false, false)]
local procedure SetBuyerReference(
SalesHeader: Record "Sales Header"; var BuyerReference: Text)
begin
if BuyerReference = '' then
BuyerReference := SalesHeader."External Document No.";
end;
OnAfterGetContractDocRefInfo
Override ContractDocumentReference/ID and related fields.
OnAfterGetAdditionalDocRefInfo
Inject or modify AdditionalDocumentReference entries at the PEPPOL Management level (document attachments from the Document Attachment table).
[EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management",
'OnAfterGetAdditionalDocRefInfo', '', false, false)]
local procedure AttachDeliveryNoteURI(
var AdditionalDocumentReferenceID: Text;
var AdditionalDocRefDocumentType: Text;
var URI: Text; var MimeCode: Text;
var EmbeddedDocumentBinaryObject: Text;
SalesHeader: Record "Sales Header";
ProcessedDocType: Option Sale,Service;
var DocumentAttachments: Record "Document Attachment";
var FileName: Text)
begin
if AdditionalDocumentReferenceID <> '' then
exit;
URI := 'https://docs.mycompany.com/delivery/' + SalesHeader."No.";
AdditionalDocumentReferenceID := SalesHeader."No." + '-DN';
AdditionalDocRefDocumentType := 'DeliveryNote';
end;
Supplier party
OnAfterGetAccountingSupplierPartyInfoByFormat — EndpointID, SchemeID
[EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management",
'OnAfterGetAccountingSupplierPartyInfoByFormat', '', false, false)]
local procedure SetSupplierEndpoint(
var SupplierEndpointID: Text; var SupplierSchemeID: Text;
var SupplierName: Text; IsBISBilling: Boolean)
begin
if IsBISBilling and (SupplierSchemeID = '') then begin
SupplierEndpointID := '5012345678901';
SupplierSchemeID := '0088';
end;
end;
OnAfterGetAccountingSupplierPartyLegalEntityByFormat
Override RegistrationName, CompanyID, and schemeID in PartyLegalEntity.
OnAfterGetAccountingSupplierPartyContact
Override contact name, telephone, fax, and email for the supplier.
[EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management",
'OnAfterGetAccountingSupplierPartyContact', '', false, false)]
local procedure SetSupplierContact(
SalesHeader: Record "Sales Header";
var ContactID: Text; var ContactName: Text;
var Telephone: Text; var Telefax: Text; var ElectronicMail: Text)
begin
ElectronicMail := 'invoicing@mycompany.com';
end;
Customer party
OnAfterGetAccountingCustomerPartyInfoByFormat — EndpointID, SchemeID
[EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management",
'OnAfterGetAccountingCustomerPartyInfoByFormat', '', false, false)]
local procedure OverrideCustomerEndpoint(
SalesHeader: Record "Sales Header";
var CustomerEndpointID: Text; var CustomerSchemeID: Text;
var CustomerPartyIdentificationID: Text;
var CustomerPartyIDSchemeID: Text; var CustomerName: Text;
IsBISBilling: Boolean)
var
Customer: Record Customer;
begin
if Customer.Get(SalesHeader."Bill-to Customer No.") then
if Customer."RSMSTA PEPPOL Endpoint" <> '' then begin
CustomerEndpointID := Customer."RSMSTA PEPPOL Endpoint";
CustomerSchemeID := Customer."RSMSTA PEPPOL Scheme";
end;
end;
OnAfterGetAccountingCustomerPartyLegalEntityByFormat
Override RegistrationName, CompanyID, and schemeID in the customer PartyLegalEntity.
OnAfterGetAccountingCustomerPartyContact
Override customer contact name, telephone, fax, and email.
Invoice lines
OnAfterGetLineGeneralInfo — Note, quantity, extension amount, AccountingCost
The primary hook for writing <cbc:Note> on each line. By default the Note element contains the sales line type text — set it to '' to suppress that or replace it with your own value.
[EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management",
'OnAfterGetLineGeneralInfo', '', false, false)]
local procedure SetInvoiceLineFields(
SalesLine: Record "Sales Line"; SalesHeader: Record "Sales Header";
var InvoiceLineID: Text; var InvoiceLineNote: Text;
var InvoicedQuantity: Text; var InvoiceLineExtensionAmount: Text;
var InvoiceLineAccountingCost: Text)
begin
if SalesLine."RSMSTA Line Comment" <> '' then
InvoiceLineNote := SalesLine."RSMSTA Line Comment"
else
InvoiceLineNote := '';
InvoiceLineAccountingCost := SalesLine."RSMSTA Cost Center";
end;
OnAfterGetLineItemInfo — Description, Name, item identifiers, origin country
[EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management",
'OnAfterGetLineItemInfo', '', false, false)]
local procedure EnrichItemInfo(
SalesLine: Record "Sales Line";
var Description: Text; var Name: Text;
var SellersItemIdentificationID: Text;
var StandardItemIdentificationID: Text; var StdItemIdIDSchemeID: Text;
var OriginCountryIdCode: Text; var OriginCountryIdCodeListID: Text)
var
Item: Record Item;
begin
if SalesLine.Type = SalesLine.Type::Item then
if Item.Get(SalesLine."No.") then begin
if Item."Country/Region of Origin Code" <> '' then
OriginCountryIdCode := Item."Country/Region of Origin Code";
if Description = '' then
Description := Item."Description 2";
end;
end;
OnAfterGetLinePriceInfo — Unit price, base quantity, unit code
[EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management",
'OnAfterGetLinePriceInfo', '', false, false)]
local procedure AdjustLinePrice(
SalesLine: Record "Sales Line"; SalesHeader: Record "Sales Header";
var InvoiceLinePriceAmount: Text;
var BaseQuantity: Text; var UnitCode: Text)
begin
if SalesLine.Type = SalesLine.Type::"G/L Account" then
UnitCode := 'HUR'; // hours
end;
Payment
OnAfterGetPaymentMeansInfo — Payment means code, payment ID, due date
[EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management",
'OnAfterGetPaymentMeansInfo', '', false, false)]
local procedure SetPaymentMeans(
SalesHeader: Record "Sales Header";
var PaymentMeansCode: Text; var PaymentMeansListID: Text;
var PaymentDueDate: Text; var PaymentChannelCode: Text;
var PaymentID: Text;
var PrimaryAccountNumberID: Text; var NetworkID: Text)
begin
PaymentID := SalesHeader."RSMSTA KID Number";
if SalesHeader."Payment Method Code" = 'CARD' then
PaymentMeansCode := '48';
end;
OnAfterGetPaymentMeansPayeeFinancialAccBIS — IBAN, BIC/branch ID
[EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management",
'OnAfterGetPaymentMeansPayeeFinancialAccBIS', '', false, false)]
local procedure OverrideBankAccount(
SalesHeader: Record "Sales Header";
var PayeeFinancialAccountID: Text;
var FinancialInstitutionBranchID: Text)
begin
if SalesHeader."Currency Code" = 'EUR' then begin
PayeeFinancialAccountID := 'IS140159260076545510730339';
FinancialInstitutionBranchID := 'NBIIISREXX';
end;
end;
Tax
OnAfterGetTaxTotalInfo
Modify the aggregate <cbc:TaxAmount> after it is calculated from the VAT amount lines.
OnAfterGetTaxSubtotalInfo
Modify individual TaxSubtotal fields: taxable amount, tax amount, category ID, percent, scheme ID.
OnGetTotalsOnBeforeInsertVATAmtLine
Intercept each VAT amount line before it is inserted into the aggregation table. Set IsHandled := true to replace the standard insert entirely.
[EventSubscriber(ObjectType::Codeunit, Codeunit::"PEPPOL Management",
'OnGetTotalsOnBeforeInsertVATAmtLine', '', false, false)]
local procedure OverrideTaxCategory(
SalesLine: Record "Sales Line";
var VATAmtLine: Record "VAT Amount Line";
VATPostingSetup: Record "VAT Posting Setup";
var IsHandled: Boolean)
begin
if SalesLine."VAT Bus. Posting Group" = 'EU' then
VATAmtLine."Tax Category" := 'AE';
end;
OnAfterGetLegalMonetaryInfoWithInvRounding
Modify the LegalMonetaryTotal block after it is calculated, including LineExtensionAmount, TaxExclusiveAmount, TaxInclusiveAmount, AllowanceTotalAmount, PrepaidAmount, PayableRoundingAmount, and PayableAmount.
Coverage summary
| XML section | Events available |
|---|---|
| Header fields (Note, TaxPointDate, AccountingCost, InvoiceTypeCode) | OnAfterGetGeneralInfo |
| DueDate | OnGetFinalDueDateClaim, OnAfterGetDueDateForInvoice |
| OrderReference | OnAfterGetOrderReferenceInfo |
| BuyerReference | OnAfterGetBuyerReference |
| ContractDocumentReference | OnAfterGetContractDocRefInfo |
| AdditionalDocumentReference | OnBefore/AfterGetAdditionalDocRefInfo..., OnBefore/AfterCountAttachments... |
| Supplier party (identity, address, tax) | CourierOnBefore/AfterGetAccountingSupplierPartyInfoByFormat, CourierGetAccountingSupplierPartyPostalAddr, CourierGetAccountingSupplierPartyTaxSchemeBIS, OnAfterGetAccountingSupplierPartyLegalEntityByFormat, OnAfterGetAccountingSupplierPartyContact |
| Customer party (identity, address, tax) | OnAfterGetAccountingCustomerPartyInfoByFormat, OnAfterGetAccountingCustomerPartyLegalEntityByFormat, OnAfterGetAccountingCustomerPartyContact |
| Delivery | No |
| PaymentMeans | OnAfterGetPaymentMeansInfo, OnAfterGetPaymentMeansPayeeFinancialAccBIS, OnAfterGetPaymentMeansPayeeFinancialAccBISInternal |
| TaxTotal / TaxSubtotal | OnAfterGetTaxTotalInfo, OnAfterGetTaxSubtotalInfo, OnGetTotalsOnBeforeInsertVATAmtLine |
| LegalMonetaryTotal | OnAfterGetLegalMonetaryInfoWithInvRounding |
| Line: Note, quantity, amounts, AccountingCost | OnAfterGetLineGeneralInfo |
| Line: Item info, origin country | OnAfterGetLineItemInfo |
| Line: Price, unit code | OnAfterGetLinePriceInfo |
| Line: AdditionalItemProperty | OnBeforeAdditionalItemPropertyLoop + AddAdditionalItemPropertyToList() |
| Full XML blob | OnBefore/AfterCreateSalesInvoiceXML, OnBefore/AfterCreateSalesCrMemoXML |
For sections without a dedicated event, use OnAfterCreateSalesInvoiceXML / OnAfterCreateSalesCrMemoXML to post-process the full XML blob via DOM manipulation.
Quick reference
| XML element | Event to subscribe |
|---|---|
cbc:Note (header) | PEPPOL Management · OnAfterGetGeneralInfo |
cbc:TaxPointDate | PEPPOL Management · OnAfterGetGeneralInfo |
cbc:AccountingCost (header) | PEPPOL Management · OnAfterGetGeneralInfo |
cbc:DueDate | XMLport · OnGetFinalDueDateClaim (ClaimDueDate) |
Final due date AdditionalDocumentReference | XMLport · OnGetFinalDueDateClaim (ClaimFinalDueDate) |
OrderReference/cbc:ID | PEPPOL Management · OnAfterGetOrderReferenceInfo |
cbc:BuyerReference | PEPPOL Management · OnAfterGetBuyerReference |
ContractDocumentReference | PEPPOL Management · OnAfterGetContractDocRefInfo |
AdditionalDocumentReference | Courier · OnBefore/AfterGetAdditionalDocRefInfo... |
Supplier EndpointID / SchemeID | Courier · CourierOnBefore/AfterGetAccountingSupplierPartyInfoByFormat |
| Supplier postal address | Courier · CourierGetAccountingSupplierPartyPostalAddr |
Supplier PartyTaxScheme | Courier · CourierGetAccountingSupplierPartyTaxSchemeBIS |
Supplier PartyLegalEntity | PEPPOL Management · OnAfterGetAccountingSupplierPartyLegalEntityByFormat |
| Supplier contact | PEPPOL Management · OnAfterGetAccountingSupplierPartyContact |
Customer EndpointID / SchemeID | PEPPOL Management · OnAfterGetAccountingCustomerPartyInfoByFormat |
Customer PartyLegalEntity | PEPPOL Management · OnAfterGetAccountingCustomerPartyLegalEntityByFormat |
| Customer contact | PEPPOL Management · OnAfterGetAccountingCustomerPartyContact |
PaymentMeans code / payment ID | PEPPOL Management · OnAfterGetPaymentMeansInfo |
| Bank account (IBAN / BIC) | Courier · OnAfterGetPaymentMeansPayeeFinancialAccBISInternal |
TaxTotal/cbc:TaxAmount | PEPPOL Management · OnAfterGetTaxTotalInfo |
TaxSubtotal fields | PEPPOL Management · OnAfterGetTaxSubtotalInfo |
| Tax category per line | PEPPOL Management · OnGetTotalsOnBeforeInsertVATAmtLine |
LegalMonetaryTotal | PEPPOL Management · OnAfterGetLegalMonetaryInfoWithInvRounding |
InvoiceLine/cbc:Note | PEPPOL Management · OnAfterGetLineGeneralInfo (InvoiceLineNote) |
InvoiceLine/cbc:AccountingCost | PEPPOL Management · OnAfterGetLineGeneralInfo (InvoiceLineAccountingCost) |
| Line quantity / extension amount | PEPPOL Management · OnAfterGetLineGeneralInfo |
Item Name / Description | PEPPOL Management · OnAfterGetLineItemInfo |
| Item origin country | PEPPOL Management · OnAfterGetLineItemInfo |
| Unit price / unit code | PEPPOL Management · OnAfterGetLinePriceInfo |
AdditionalItemProperty per line | XMLport · OnBeforeAdditionalItemPropertyLoop + AddAdditionalItemPropertyToList() |
| Full XML blob | Courier · OnBefore/AfterCreateSalesInvoiceXML / OnBefore/AfterCreateSalesCrMemoXML |