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".
-
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.
-
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;
-
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;
-
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.
-
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.
-
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).
-
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.
-
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.
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 isflagged 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
OnBeforeTestIfDropShipmentDocumentIsHandled integration event at thestart of the
TestIfDropShipmentDocumentlocal procedure inCodeunit 1303 "Correct Posted Sales Invoice".
Problem Statement
The local procedure
TestIfDropShipmentDocumentunconditionally raises anerror 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.
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;
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;
end;
Alternatives Evaluated
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.
breaking official upgrade paths.
TestCorrectInvoiceIsAllowed: Thatevent fires after several other checks and cannot cleanly exit before the
drop-shipment error without replicating the entire validation chain.
Justification for IsHandled
The sole purpose of
TestIfDropShipmentDocumentis to raise an error thatstops 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 thereforethe minimum change that achieves the extensibility goal while leaving the
default behavior completely unchanged when no subscriber sets IsHandled = true.
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).
Data Sensitivity Review
The event exposes only the
Sales Invoice Headerrecord, which is alreadyaccessible 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.
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 inthe 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.