Skip to content

[Event Request] Codeunit 1303 "Correct Posted Sales Invoice" - OnBeforeTestIfDropShipmentDocument #29981

@NVLBjartur

Description

@NVLBjartur

Why do you need this change?

When correcting or canceling a posted sales invoice, Business Central calls
TestIfDropShipmentDocument, which raises a hard error if any invoice line is
flagged as Drop Shipment. In certain business scenarios — for example, when a
partner solution manages drop-shipment fulfillment externally or when the
purchase order has already been fully reconciled — this check is unnecessarily
restrictive. There is currently no extensibility point that allows a partner
extension to evaluate the situation and decide whether the check should be
bypassed, making it impossible to support these valid use cases without
source-code modifications.

Describe the request

Add an OnBeforeTestIfDropShipmentDocument IsHandled integration event at the
start of the TestIfDropShipmentDocument local procedure in
Codeunit 1303 "Correct Posted Sales Invoice".

  1. Problem Statement

    The local procedure TestIfDropShipmentDocument unconditionally raises an
    error whenever a posted sales invoice contains drop-shipment lines. No
    existing event or extensibility point allows subscribers to override or
    supplement this validation. Partners cannot handle legitimate edge cases
    without modifying base-application code.

  2. Proposed Code Change

    Modified procedure (additions marked with **):

    local procedure TestIfDropShipmentDocument(SalesInvoiceHeader: Record "Sales Invoice Header")
    var
    SalesInvoiceLine: Record "Sales Invoice Line";
    TempSalesShipmentLine: Record "Sales Shipment Line" temporary;
    ** IsHandled: Boolean; **
    begin
    ** IsHandled := false; **
    ** OnBeforeTestIfDropShipmentDocument(SalesInvoiceHeader, IsHandled); **
    ** if IsHandled then **
    ** exit; **
    SalesInvoiceLine.SetRange("Document No.", SalesInvoiceHeader."No.");
    SalesInvoiceLine.SetRange("Drop Shipment", true);
    if SalesInvoiceLine.FindFirst() then begin
    SalesInvoiceLine.GetSalesShptLines(TempSalesShipmentLine);
    Error(DropShipmentDocumentExistsErr, TempSalesShipmentLine."Purchase Order No.");
    end;
    end;

    New event publisher (to be added at the bottom of the codeunit's event region):

    [IntegrationEvent(false, false)]
    local procedure OnBeforeTestIfDropShipmentDocument(SalesInvoiceHeader: Record "Sales Invoice Header"; var
    IsHandled: Boolean)
    begin
    end;

  3. Invocation Example

    The following subscriber illustrates the intended use — allowing the check
    to be skipped when the associated purchase order has already been closed by
    the partner solution:

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Correct Posted Sales Invoice",
    'OnBeforeTestIfDropShipmentDocument', '', false, false)]
    local procedure HandleDropShipmentCheck(
    SalesInvoiceHeader: Record "Sales Invoice Header";
    var IsHandled: Boolean)
    begin
    if IsHandled then
    exit;

    // Example: allow correction when the partner PO tracker marks the order as reconciled
    if MyPOTracker.IsFullyReconciled(SalesInvoiceHeader."No.") then
        IsHandled := true;
    

    end;

  4. Alternatives Evaluated

    • Existing events in Codeunit 1303 ("Correct Posted Sales Invoice"): None of
      the current integration events (e.g., OnAfterCreateCreditMemoCopyDocument)
      fire before the error is raised; the procedure exits with an error before any
      post-processing event could be used to compensate.
    • Replacing the codeunit entirely: Not feasible in an extension without
      breaking official upgrade paths.
    • Using an event on the calling procedure TestCorrectInvoiceIsAllowed: That
      event fires after several other checks and cannot cleanly exit before the
      drop-shipment error without replicating the entire validation chain.
  5. Justification for IsHandled

    The sole purpose of TestIfDropShipmentDocument is to raise an error that
    stops execution. There is no positive "extra work" a subscriber could add —
    the only meaningful extension is to suppress the error for a specific
    scenario. A regular (non-IsHandled) event would not allow a subscriber to
    prevent the subsequent Error(...) call. The IsHandled pattern is therefore
    the minimum change that achieves the extensibility goal while leaving the
    default behavior completely unchanged when no subscriber sets IsHandled = true.

  6. Performance Considerations

    The event is raised at most once per correction/cancellation action triggered
    by a user. It is never inside a loop or batch operation. The performance
    impact is negligible (a single event raise with no data passed by reference
    other than the header record and a Boolean).

  7. Data Sensitivity Review

    The event exposes only the Sales Invoice Header record, which is already
    accessible to extensions that have read permission on that table. No
    passwords, personal data, cryptographic material, or security-sensitive
    fields are introduced. The Boolean parameter carries no sensitive information.

  8. Multi-Extension Interaction

    Because the pattern uses IsHandled, a subscriber that sets IsHandled = true
    prevents subsequent subscribers from re-evaluating the same check. Each
    subscriber should therefore guard with if IsHandled then exit; (as shown in
    the invocation example) so that the first extension that considers the
    document safe takes responsibility and later subscribers are not invoked
    redundantly. The risk of conflicting overrides is low given that this event
    fires only on a deliberate user action; if two extensions subscribe, the
    alphabetical binding order determines precedence, which is the standard
    AL event behavior.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions