Skip to content

[Event Request] table 38 "Purchase Header" #30222

Description

@Simon110394

Why do you need this change?

We need an event inside the trigger OnValidate of field "Pay-to Vendor No.", to be able to run the procedure RecreatePurchLines if a custom if statement is satisfied or, alternatively, run a custom procedure.
To manage an IsHandled variable is the only way to skip RecreatePurchLines (if a particular custom condition is satisfied) and run a custom procedure instead. At the moment there are no other ways to do that.

Describe the request

In trigger OnValidate of field(4; "Pay-to Vendor No."; Code[20]) of table 38 "Purchase Header" we need an event:

[IntegrationEvent(false, false)]
local procedure OnBeforeRecreatePurchLines(var Ishandled: Boolean; var Rec: record "Purchase Header"; xRec: record "Purchase Header")
begin
end;

Changes between **:

trigger OnValidate()
            var
                IsHandled: Boolean;
            begin
                IsHandled := false;
                OnBeforeValidatePayToVendorNo(Rec, xRec, Confirmed, IsHandled);
                if IsHandled then
                    exit;

                TestStatusOpen();
                if (xRec."Pay-to Vendor No." <> "Pay-to Vendor No.") and
                   (xRec."Pay-to Vendor No." <> '')
                then
                    if ConfirmUpdateField(FieldNo("Pay-to Vendor No.")) then begin
                        OnValidatePayToVendorNoOnAfterConfirmed(Rec);
                        PurchLine.SetRange("Document Type", "Document Type");
                        PurchLine.SetRange("Document No.", "No.");

                        CheckReceiptInfo(PurchLine, true);
                        CheckPrepmtInfo(PurchLine);
                        CheckReturnInfo(PurchLine, true);

                        PurchLine.Reset();
                    end else
                        "Pay-to Vendor No." := xRec."Pay-to Vendor No.";

                OnValidatePayToVendorNoOnBeforeGetPayToVend(Rec);
                GetVend("Pay-to Vendor No.");
                CheckBlockedVendOnDocs(Vend);
                Vend.TestField("Vendor Posting Group");
                PostingSetupMgt.CheckVendPostingGroupPayablesAccount("Vendor Posting Group");
                OnAfterCheckPayToVendor(Rec, xRec, Vend);

                "Pay-to Name" := Vend.Name;
                "Pay-to Name 2" := Vend."Name 2";
                CopyPayToVendorAddressFieldsFromVendor(Vend, false);
                "Individual Person" := Vend."Individual Person";
                Resident := Vend.Resident;
                "First Name" := Vend."First Name";
                "Last Name" := Vend."Last Name";
                "Date of Birth" := Vend."Date of Birth";
                "Birth City" := Vend."Birth City";
                "Tax Representative Type" := Vend."Tax Representative Type";
                "Tax Representative No." := Vend."Tax Representative No.";
                "VAT Country/Region Code" := Vend."Country/Region Code";
                if not SkipPayToContact then
                    "Pay-to Contact" := Vend.Contact;
                "Payment Terms Code" := Vend."Payment Terms Code";
                "Prepmt. Payment Terms Code" := Vend."Prepmt. Payment Terms Code";
                "Payment Method Code" := Vend."Payment Method Code";
                "Price Calculation Method" := Vend.GetPriceCalculationMethod();
                if "Buy-from Vendor No." = Vend."No." then
                    Validate("Shipment Method Code", Vend."Shipment Method Code");
                "Vendor Posting Group" := Vend."Vendor Posting Group";
                OnAfterCopyPayToVendorFieldsFromVendor(Rec, Vend, xRec);

                GLSetup.Get();
                if GLSetup."Bill-to/Sell-to VAT Calc." = GLSetup."Bill-to/Sell-to VAT Calc."::"Bill-to/Pay-to No." then begin
                    "VAT Bus. Posting Group" := Vend."VAT Bus. Posting Group";
                    "VAT Country/Region Code" := Vend."Country/Region Code";
                    AssignVATRegistrationNo("Pay-to Vendor No.");
                    "Gen. Bus. Posting Group" := Vend."Gen. Bus. Posting Group";
                end;
                if not CheckVATExemption() then
                    FieldError("Pay-to Vendor No.");
                "Prices Including VAT" := Vend."Prices Including VAT";
                "Currency Code" := Vend."Currency Code";
                "Invoice Disc. Code" := Vend."Invoice Disc. Code";
                "Language Code" := Vend."Language Code";
                "Format Region" := Vend."Format Region";
                SetPurchaserCode(Vend."Purchaser Code", "Purchaser Code");
                Validate("Payment Terms Code");
                Validate("Payment Method Code");
                Validate("Currency Code");
                Validate("Creditor No.", Vend."Creditor No.");
                OnValidatePurchaseHeaderPayToVendorNoOnBeforeCheckDocType(Vend, Rec, xRec, SkipPayToContact);

                if "Document Type" = "Document Type"::Order then
                    Validate("Prepayment %", Vend."Prepayment %");

                if "Pay-to Vendor No." = xRec."Pay-to Vendor No." then
                    if ReceivedPurchLinesExist() then
                        TestField("Currency Code", xRec."Currency Code");

                CreateDimensionsFromValidatePayToVendorNo();

                OnValidatePaytoVendorNoBeforeRecreateLines(Rec, CurrFieldNo);

                if (xRec."Buy-from Vendor No." = "Buy-from Vendor No.") and
                   (xRec."Pay-to Vendor No." <> "Pay-to Vendor No.")
                then begin
					**ishandled := false;
					OnBeforeRecreatePurchLines(Ishandled, Rec, xRec);
					if not ishandled then**
						RecreatePurchLines(PayToVendorTxt);
				end;
				
                    

                if not SkipPayToContact then
                    UpdatePayToCont("Pay-to Vendor No.");

                "Pay-to IC Partner Code" := Vend."IC Partner Code";

                OnValidatePayToVendorNoOnBeforeRecallModifyAddressNotification(Rec, xRec, Vend);
                if (xRec."Pay-to Vendor No." <> '') and (xRec."Pay-to Vendor No." <> "Pay-to Vendor No.") then
                    Rec.RecallModifyAddressNotification(GetModifyPayToVendorAddressNotificationId());
            end;

Alternatives evaluated

The following existing extensibility points were evaluated:

  • OnBeforeValidatePayToVendorNo
  • OnValidatePayToVendorNoOnAfterConfirmed
  • OnAfterCopyPayToVendorFieldsFromVendor
  • OnValidatePurchaseHeaderPayToVendorNoOnBeforeCheckDocType
  • OnValidatePaytoVendorNoBeforeRecreateLines
  • OnValidatePayToVendorNoOnBeforeRecallModifyAddressNotification

None of these events allow partners to conditionally replace or bypass the call to:

RecreatePurchLines(PayToVendorTxt);

The existing OnValidatePaytoVendorNoBeforeRecreateLines event is raised immediately before the call, but it does not provide an IsHandled parameter and therefore cannot prevent the execution of RecreatePurchLines().

The only alternative would be duplicating the entire "Pay-to Vendor No." validation logic, which is difficult to maintain and introduces upgrade risks.


Justification for IsHandled

The business requirement is to execute a custom line recreation process only under specific conditions.

For example, some custom purchase lines contain additional information that must be preserved when the Pay-to Vendor changes. In these scenarios, executing the standard:

RecreatePurchLines(PayToVendorTxt);

would remove or recreate lines in a way that is not compatible with the custom business process.

The extension therefore needs to:

  1. Evaluate a custom condition.
  2. Skip the standard RecreatePurchLines() call when that condition is satisfied.
  3. Execute an alternative recreation procedure that preserves the custom information.

A regular integration event is insufficient because the standard RecreatePurchLines() would still execute afterwards and overwrite the custom processing.

Therefore, an IsHandled event is required.


Performance considerations

The proposed event is executed only when:

  • field "Pay-to Vendor No." is validated;
  • the Pay-to Vendor has changed;
  • the Buy-from Vendor has not changed.

This is a relatively infrequent operation, typically performed by a user while editing purchase documents.

The proposed change introduces only:

OnBeforeRecreatePurchLines(IsHandled, Rec, xRec);

and a Boolean check. No additional database operations are introduced by the platform change itself.

Therefore, the performance impact is negligible.


Data sensitivity review

The event exposes only:

  • var Rec: Record "Purchase Header"
  • xRec: Record "Purchase Header"
  • var IsHandled: Boolean

These records are already available through other events in the purchase document process and do not expose any new categories of information.

No personally identifiable information, credentials, secrets, or sensitive business data are introduced by this change.

Therefore, the requested event does not introduce any security or data exposure concerns.


Multi-extension interaction

As with any IsHandled event, multiple extensions could subscribe and set IsHandled := true, causing the standard RecreatePurchLines() logic to be skipped.

Potential conflicts include:

  • one extension replacing the recreation logic while another extension expects the standard behavior;
  • multiple extensions implementing different recreation strategies.

This risk is considered acceptable because:

  • the event is narrowly scoped and affects only the recreation of purchase lines after a Pay-to Vendor change;
  • extensions should set IsHandled := true only when intentionally replacing the standard recreation process;
  • without this event, partners are forced to duplicate the entire "Pay-to Vendor No." validation trigger, resulting in significantly higher maintenance and upgrade risks.

An extensibility point already exists at the required location, but it cannot influence the subsequent call to RecreatePurchLines(). Adding an IsHandled event is therefore a minimal and low-risk change that completes the existing extensibility model.

Metadata

Metadata

Assignees

No one assigned

    Labels

    missing-infoThe issue misses information that prevents it from completion.

    Type

    No fields configured for Task.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions