Skip to content

[Event Request] codeunit 5940 - ServContractManagement - procedures CheckServiceItemBlockedForAll & CheckItemServiceBlocked #30342

Description

@LennartZeKVS

Why do you need this change?

We want to be able to skip the checks for blocked service items and blocked/deleted items when invoicing service contracts, so that active contracts for items that were later blocked or deleted can still be invoiced.

Alternative events or patterns considered

We checked OnCreateAllServLinesOnBeforeServContractLineLoop in codeunit 5940, which fires right before these checks are called from CreateAllServLines, but it has no Skip/IsHandled parameter and cannot prevent CheckServiceItemBlockedForAll / CheckItemServiceBlocked from running. A Skip-parameter design at the call site was considered as well, but placing the events inside the two procedures themselves keeps each check independently skippable and mirrors the same pattern already approved in issue #28616 (codeunit 5900 ServOrderManagement, OnBeforeCheckItemServiceBlocked) for the equivalent scenario.

Why IsHandled is required

Both procedures perform hard-coded TestField/Get validations with no existing extensibility point to bypass them. Customers need to continue invoicing active service contracts for items/service items that were later blocked or deleted, without duplicating or forking codeunit 5940. Since the checks must be skipped entirely rather than supplemented, a regular OnBefore/OnAfter event is not sufficient — only an IsHandled bypass lets the calling process continue instead of raising the error.

Performance considerations

Each event is raised once per service contract line during contract invoicing — the same frequency as the existing OnCreateAllServLinesOnBeforeServContractLineLoop event in the same loop. No additional loops, database reads, or locking beyond what a subscriber may add on its own (e.g., one Get on a setup table).

Data sensitivity review

The events expose Service Contract Line (var), which is already fully accessible in the calling procedures and their existing events (e.g., OnCreateAllServLinesOnBeforeServContractLineLoop). No new or additional sensitive data is exposed.

Multi-extension interaction/conflict risk

Both parameters follow the IsHandled pattern (initialized to false). If multiple extensions subscribe, each subscriber must check whether IsHandled is already true before evaluating its own condition, and should never reset it back to false. This is the same convention used in the accepted precedent (#28616) and avoids conflicting behavior between subscribers.

Describe the request

We need two separate IsHandled event publishers in codeunit 5940 ServContractManagement, one in CheckServiceItemBlockedForAll and one in CheckItemServiceBlocked.


1: Add an event publisher OnBeforeCheckServiceItemBlockedForAll() to codeunit 5940 ServContractManagement with the following signature:

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCheckServiceItemBlockedForAll(var ServiceContractLine: Record "Service Contract Line"; var IsHandled: Boolean)
    begin
    end;

which would be called as follows:

    internal procedure CheckServiceItemBlockedForAll(var ServiceContractLine: Record "Service Contract Line")
    var
        ServiceItem: Record "Service Item";
        IsHandled: Boolean; // << isHandled pattern
    begin
        IsHandled := false; // << isHandled pattern
        OnBeforeCheckServiceItemBlockedForAll(ServiceContractLine, IsHandled); // << Call event
        if IsHandled then   // << isHandled pattern
            exit;           // << isHandled pattern

        if ServiceContractLine."Service Item No." = '' then
            exit;

        ServiceItem.SetLoadFields(Blocked);
        ServiceItem.Get(ServiceContractLine."Service Item No.");
        ServiceItem.ErrorIfBlockedForAll();
    end;

2: Add an event publisher OnBeforeCheckItemServiceBlocked() to codeunit 5940 ServContractManagement with the following signature:

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCheckItemServiceBlocked(var ServiceContractLine: Record "Service Contract Line"; var IsHandled: Boolean)
    begin
    end;

which would be called as follows:

    internal procedure CheckItemServiceBlocked(var ServiceContractLine: Record "Service Contract Line")
    var
        Item: Record Item;
        ItemVariant: Record "Item Variant";
        IsHandled: Boolean; // << isHandled pattern
    begin
        IsHandled := false; // << isHandled pattern
        OnBeforeCheckItemServiceBlocked(ServiceContractLine, IsHandled); // << Call event
        if IsHandled then   // << isHandled pattern
            exit;           // << isHandled pattern

        if ServiceContractLine."Item No." = '' then
            exit;

        Item.SetLoadFields(Blocked, "Service Blocked");
        Item.Get(ServiceContractLine."Item No.");
        Item.TestField(Blocked, false);
        Item.TestField("Service Blocked", false);

        if ServiceContractLine."Variant Code" <> '' then begin
            ItemVariant.SetLoadFields(Blocked, "Service Blocked");
            ItemVariant.Get(ServiceContractLine."Item No.", ServiceContractLine."Variant Code");
            ItemVariant.TestField(Blocked, false);
            ItemVariant.TestField("Service Blocked", false);
        end;
    end;

Metadata

Metadata

Assignees

No one assigned

    Labels

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

    Type

    Fields

    No fields configured for Task.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions