Skip to content

[Add Integration Event Request] Codeunit 99000973 "Purch. Availability Mgt." - procedure "TransferPurchaseLine" #30383

Description

@mariiaaaaaaaa

Why do you need this change?

In codeunit 99000973 "Purch. Availability Mgt.", procedure "TransferPurchaseLine", only the standard purchase document types are handled when transferring purchase line availability data to "Inventory Page Data".

We want to develop custom handling for Blanket Purchase Orders when purchase line availability data is transferred to Inventory Page Data. Currently, the standard procedure raises an error for unsupported purchase document types, which prevents extensions from processing Blanket Purchase Orders in this scenario.

Describe the request

Please change procedure "TransferPurchaseLine" as follows:

local procedure TransferPurchaseLine(InventoryEventBuffer: Record "Inventory Event Buffer"; var InventoryPageData: Record "Inventory Page Data"; SourceType: Integer; SourceSubtype: Integer; SourceID: Code[20])
var
    PurchHeader: Record "Purchase Header";
    RecRef: RecordRef;
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    IsHandled: Boolean;
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
begin
    PurchHeader.Get(SourceSubtype, SourceID);
    RecRef.GetTable(PurchHeader);
    InventoryPageData."Source Document ID" := RecRef.RecordId;
    InventoryPageData.Description := PurchHeader."Buy-from Vendor Name";
    InventoryPageData.Source := StrSubstNo(PurchaseDocumentTxt, Format(PurchHeader."Document Type"));
    InventoryPageData."Document No." := PurchHeader."No.";
    case "Purchase Document Type".FromInteger(SourceSubtype) of
        "Purchase Document Type"::Order,
        "Purchase Document Type"::Invoice,
        "Purchase Document Type"::"Credit Memo":
            begin
                InventoryPageData.Type := InventoryPageData.Type::Purchase;
                InventoryPageData."Scheduled Receipt" := InventoryEventBuffer."Remaining Quantity (Base)";
                InventoryPageData."Reserved Receipt" := InventoryEventBuffer."Reserved Quantity (Base)";
            end;
        "Purchase Document Type"::"Return Order":
            begin
                InventoryPageData.Type := InventoryPageData.Type::"Purch. Return";
                InventoryPageData."Gross Requirement" := InventoryEventBuffer."Remaining Quantity (Base)";
                InventoryPageData."Reserved Requirement" := InventoryEventBuffer."Reserved Quantity (Base)";
            end;
        else begin
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
            IsHandled := false;
            OnTransferPurchaseLineElseCase(InventoryPageData, InventoryEventBuffer, IsHandled, SourceType, SourceSubtype, SourceID, PurchHeader);
            if not IsHandled then
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                Error(UnsupportedEntitySourceErr, SourceType, SourceSubtype);
        end
    end;
end;

New Event

[IntegrationEvent(false, false)]
local procedure OnTransferPurchaseLineElseCase(var InventoryPageData: Record "Inventory Page Data"; InventoryEventBuffer: Record "Inventory Event Buffer"; var IsHandled: Boolean; SourceType: Integer; SourceSubtype: Integer; SourceID: Code[20]; PurchHeader: Record "Purchase Header")
begin
end;

Background / Context

On page 5530 "Item Availability by Event", there is a switch field(IncludeBlanketOrders; IncludeBlanketOrders) (caption "Include Blanket Sales Orders") that, when enabled, includes all Blanket Sales Orders in the underlying matrix. We want to implement an equivalent switch that includes all Blanket Purchase Orders in the matrix.

When a field such as ItemNo, VariantFilter, LocationFilter, ForecastName, IncludePlanningSuggestions, IncludeBlanketOrders, or PeriodType is validated, CalculatePeriodEntries() recalculates the matrix via DetailsForPeriodEntry() (codeunit 5531 "Calc. Inventory Page Data"), which internally calls TransferToPeriodDetails(). The latter dispatches by source type and, for unrecognized types, raises the OnTransferToPeriodDetailsElseCase event:

OnTransferToPeriodDetailsElseCase(InventoryPageData, FromInvtEventBuf, IsHandled, SourceType, SourceSubtype, SourceID, SourceRefNo);

For Sales Lines, codeunit 99000872 "Sales Availability Mgt." subscribes to this event and calls its own TransferSalesLine procedure, which already handles "Blanket Order" (mapped to InventoryPageData.Type::"Blanket Sales Order").

For Purchase Lines, codeunit 99000973 "Purch. Availability Mgt." subscribes the same way and calls TransferPurchaseLine, but this procedure only handles Order, Invoice, Credit Memo and Return Order - Blanket Order is not supported and causes Error(UnsupportedEntitySourceErr, ...).

Neither OnTransferToPeriodDetailsElseCase nor TransferPurchaseLine currently exposes an event that lets an extension handle additional purchase document types (such as Blanket Order) without modifying base application code.

Alternative patterns review

We reviewed the existing OnTransferToPeriodDetailsElseCase event on TransferToPeriodDetails (codeunit 5531 "Calc. Inventory Page Data", verified in source). This event fires before the source-type-specific transfer procedure (TransferPurchaseLine) is even selected/called, so it cannot be used to extend the case statement inside TransferPurchaseLine itself - an extension would have to fully reimplement TransferPurchaseLine's logic (header lookup, source document ID, description, etc.) inside the subscriber, duplicating base code and risking divergence from future base changes. We also reviewed OnDetailsForPeriodEntryOnBeforeInvtPageDataInsert and OnDetailsForPeriodEntryOnBeforeInvtPageDataModify on DetailsForPeriodEntry(), but these fire only after the record has already been processed and inserted/modified, so they cannot supply the type-specific field values (e.g. Gross Requirement, Reserved Requirement) needed before insert. No existing event allows handling an unsupported "Purchase Document Type" at the point where it's determined, which is why a new IsHandled-style event on TransferPurchaseLine itself is needed - matching the same pattern Sales Availability Mgt. already benefits from at the equivalent Sales-side procedure.

Performance considerations

Confirmed via page 5530 "Item Availability by Event": the OnValidate triggers on ItemNo, VariantFilter, LocationFilter, ForecastName, IncludePlanningSuggestions, IncludeBlanketOrders, and PeriodType all lead to InitAndCalculatePeriodEntries() / CalculatePeriodEntries(), which calls DetailsForPeriodEntry() twice (once each for Positive = true/false) and, transitively, TransferPurchaseLine() once per relevant inventory event buffer entry in range. This is a frequently-executed path tied to interactive page validation, not a rare or on-demand calculation. The new event only fires for the else branch of the case statement (i.e., only for purchase document types not already natively handled), so it adds no overhead to the existing standard document types (Order, Invoice, Credit Memo, Return Order), and for extensions that don't subscribe, the event call is a no-op. This mirrors the existing OnTransferToPeriodDetailsElseCase event already present and used in production scenarios, which has not introduced measurable performance issues in that equivalent path.

Data sensitivity/security review

The event exposes the same data already available in-context to the base procedure at that point in execution: InventoryEventBuffer, InventoryPageData (both already being populated by standard code), SourceType, SourceSubtype, SourceID, and the already-retrieved PurchHeader record. No new or additional data is surfaced beyond what the base procedure already reads and processes; this is symmetric with the equivalent Sales-side flow, which exposes the same categories of standard master/document data with no special sensitivity classification.

Multi-extension interaction risks

Following the standard IsHandled boolean pattern already used throughout Business Central (verified in OnTransferToPeriodDetailsElseCase in codeunit 5531), each subscriber is responsible for checking SourceType/SourceSubtype before acting and setting IsHandled := true only if it processed the case. If multiple extensions subscribe, each is expected to handle only the specific document type(s) it targets; if two extensions both attempt to handle the same SourceType/SourceSubtype combination, the last subscriber executed wins (standard, well-understood AL event ordering/limitation), and the resulting behavior is no different from the risk profile already accepted for other IsHandled events in the base application. We are not aware of a scenario where this introduces new conflict classes beyond those already inherent to the existing IsHandled pattern.

Metadata

Metadata

Assignees

No one assigned

    Labels

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

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions