Codeunit 99000793 Calculate Low-Level Code

App
Base Application
Namespace
Microsoft.Manufacturing.ProductionBOM
Versions
17-28

Procedures, 7Events, 9

Versions171819202122232425262728

Source29

Source in 29

src/Layers/W1/BaseApp/Manufacturing/ProductionBOM/CalculateLowLevelCode.Codeunit.al349 lines, Copyright (c) Microsoft Corporation. MIT

// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// ------------------------------------------------------------------------------------------------
namespace Microsoft.Manufacturing.ProductionBOM;

using Microsoft.Inventory.BOM;
using Microsoft.Inventory.Item;
using Microsoft.Inventory.Location;
using Microsoft.Manufacturing.Setup;

codeunit 99000793 "Calculate Low-Level Code"
{
    Permissions = TableData Item = rm,
                  TableData "Manufacturing Setup" = r,
                    TableData "Stockkeeping Unit" = r;
    TableNo = Item;

    trigger OnRun()
    var
        ProdBOM: Record "Production BOM Header";
        Item2: Record Item;
    begin
        Item2.Copy(Rec);
        Item := Item2; // to store the last item- used in RecalcLowerLevels
        Item2."Low-Level Code" := CalcLevels(1, Item2."No.", 0, 0);
        if ProdBOM.Get(Item."Production BOM No.") then
            SetRecursiveLevelsOnBOM(ProdBOM, Item2."Low-Level Code" + 1, false);
        RecalcSKULowerLevels(Item2."No.", Item2."Low-Level Code" + 1, false);
        OnBeforeItemModify(Item2);
        Item2.Modify();
        Rec.Copy(Item2);
    end;

    var
        Item: Record Item;
        ActualProdBOM: Record "Production BOM Header";

#pragma warning disable AA0470
        ProdBomErr: Label 'The maximum number of BOM levels, %1, was exceeded. The process stopped at item number %2, BOM header number %3, BOM level %4.';
#pragma warning restore AA0470

    procedure CalcLevels(Type: Option " ",Item,"Production BOM",Assembly; No: Code[20]; Level: Integer; LevelDepth: Integer) Result: Integer
    var
        Item2: Record Item;
        ProdBOMLine: Record "Production BOM Line";
        AsmBOMComp: Record "BOM Component";
        SKU: Record "Stockkeeping Unit";
        ProcessedItemNos: List of [Code[20]];
        ActLevel: Integer;
        TotalLevels: Integer;
        CalculateDeeperLevel: Boolean;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCalcLevels(Type, No, Level, LevelDepth, Result, IsHandled);
        if IsHandled then
            exit(Result);

        if LevelDepth > 50 then
            Error(ProdBomErr, 50, Item."No.", No, Level);

        TotalLevels := Level;

        case Type of
            Type::"Production BOM":
                begin
                    Item2.SetCurrentKey("Production BOM No.");
                    Item2.SetRange("Production BOM No.", No);
                    if Item2.FindSet() then
                        repeat
                            ActLevel := CalcLevels(Type::Item, Item2."No.", Level + 1, LevelDepth + 1);
                            if ActLevel > TotalLevels then
                                TotalLevels := ActLevel;
                        until Item2.Next() = 0;

                    SKU.SetCurrentKey("Production BOM No.");
                    SKU.SetRange("Production BOM No.", No);
                    if SKU.FindSet() then
                        repeat
                            if not ProcessedItemNos.Contains(SKU."Item No.") then begin
                                ProcessedItemNos.Add(SKU."Item No.");
                                ActLevel := CalcLevels(Type::Item, SKU."Item No.", Level + 1, LevelDepth + 1);
                                if ActLevel > TotalLevels then
                                    TotalLevels := ActLevel;
                            end;
                        until SKU.Next() = 0;
                    OnCalcLevelsForProdBOM(Item2, No, Level, LevelDepth, TotalLevels);
                end;
            Type::Assembly:
                begin
                    Item2.Get(No);
                    ActLevel := CalcLevels(Type::Item, Item2."No.", Level + 1, LevelDepth + 1);
                    if ActLevel > TotalLevels then
                        TotalLevels := ActLevel;
                end;
            else
                Item2.Get(No);
        end;

        AsmBOMComp.SetCurrentKey(Type, "No.");
        AsmBOMComp.SetRange(Type, Type);
        AsmBOMComp.SetRange("No.", No);
        if AsmBOMComp.FindSet() then
            repeat
                ActLevel := CalcLevels(Type::Assembly, AsmBOMComp."Parent Item No.", Level, LevelDepth + 1);
                if ActLevel > TotalLevels then
                    TotalLevels := ActLevel;
            until AsmBOMComp.Next() = 0;

        ProdBOMLine.SetCurrentKey(Type, "No.");
        ProdBOMLine.SetRange(Type, Type);
        ProdBOMLine.SetRange("No.", No);
        if ProdBOMLine.FindSet() then
            repeat
                CalculateDeeperLevel := CalcDeeperLevel(ProdBOMLine, No, Level);
                if CalculateDeeperLevel then begin
                    ActLevel := CalcLevels(Type::"Production BOM", ProdBOMLine."Production BOM No.", Level, LevelDepth + 1);
                    if ActLevel > TotalLevels then
                        TotalLevels := ActLevel;
                end;
            until ProdBOMLine.Next() = 0;

        OnAfterCalcLevels(Type, No, TotalLevels, Level, LevelDepth);
        exit(TotalLevels);
    end;

    procedure RecalcLowerLevels(ProdBOMNo: Code[20]; LowLevelCode: Integer; IgnoreMissingItemsOrBOMs: Boolean)
    var
        CompItem: Record Item;
        CompBOM: Record "Production BOM Header";
        ProdBOMLine: Record "Production BOM Line";
        ProductionBOMVersion: Record "Production BOM Version";
        EntityPresent: Boolean;
        CalculateDeeperLevel: Boolean;
    begin
        if LowLevelCode > 50 then
            Error(ProdBomErr, 50, Item."No.", ProdBOMNo, LowLevelCode);

        ProdBOMLine.SetRange("Production BOM No.", ProdBOMNo);
        ProdBOMLine.SetFilter("No.", '<>%1', '');

        if ProdBOMLine.FindSet() then
            repeat
                if ProdBOMLine."Version Code" <> '' then begin
                    ProductionBOMVersion.Get(ProdBOMLine."Production BOM No.", ProdBOMLine."Version Code");
                    CalculateDeeperLevel := ProductionBOMVersion.Status <> ProductionBOMVersion.Status::Closed;
                end else begin
                    CompBOM.Get(ProdBOMLine."Production BOM No.");
                    CalculateDeeperLevel := CompBOM.Status <> CompBOM.Status::Closed;
                end;

                // closed BOMs are skipped
                if CalculateDeeperLevel then
                    case ProdBOMLine.Type of
                        ProdBOMLine.Type::Item:
                            begin
                                EntityPresent := CompItem.Get(ProdBOMLine."No.");
                                if EntityPresent or (not IgnoreMissingItemsOrBOMs) then
                                    SetRecursiveLevelsOnItem(CompItem, LowLevelCode, IgnoreMissingItemsOrBOMs);
                            end;
                        ProdBOMLine.Type::"Production BOM":
                            begin
                                EntityPresent := CompBOM.Get(ProdBOMLine."No.");
                                if EntityPresent or (not IgnoreMissingItemsOrBOMs) then
                                    SetRecursiveLevelsOnBOM(CompBOM, LowLevelCode, IgnoreMissingItemsOrBOMs);
                            end
                    end;
            until ProdBOMLine.Next() = 0;
    end;

    local procedure RecalcSKULowerLevels(ItemNo: Code[20]; LowLevelCode: Integer; IgnoreMissingItemsOrBOMs: Boolean)
    var
        SKU: Record "Stockkeeping Unit";
        CompBOM: Record "Production BOM Header";
        TempCompBOM: Record "Production BOM Header" temporary;
        EntityPresent: Boolean;
    begin
        if LowLevelCode > 50 then
            Error(ProdBomErr, 50, Item."No.", ItemNo, LowLevelCode);
        SKU.SetCurrentKey("Item No.");
        SKU.SetRange("Item No.", ItemNo);
        SKU.SetFilter("Production BOM No.", '<>%1', '');
        if SKU.FindSet() then
            repeat
                if not TempCompBOM.Get(SKU."Production BOM No.") then begin
                    EntityPresent := CompBOM.Get(SKU."Production BOM No.");
                    if EntityPresent or (not IgnoreMissingItemsOrBOMs) then begin
                        SetRecursiveLevelsOnBOM(CompBOM, LowLevelCode, IgnoreMissingItemsOrBOMs);
                        if EntityPresent then
                            TempCompBOM := CompBOM
                        else begin
                            TempCompBOM.Init();
                            TempCompBOM."No." := SKU."Production BOM No.";
                        end;
                        TempCompBOM.Insert();
                    end;
                end;
            until SKU.Next() = 0;
    end;

    procedure RecalcAsmLowerLevels(ParentItemNo: Code[20]; LowLevelCode: Integer; IgnoreMissingItemsOrBOMs: Boolean)
    var
        CompItem: Record Item;
        BOMComp: Record "BOM Component";
        EntityPresent: Boolean;
    begin
        if LowLevelCode > 50 then
            Error(ProdBomErr, 50, Item."No.", Item."No.", LowLevelCode);

        BOMComp.SetRange("Parent Item No.", ParentItemNo);
        BOMComp.SetRange(Type, BOMComp.Type::Item);
        BOMComp.SetFilter("No.", '<>%1', '');
        if BOMComp.FindSet() then
            repeat
                EntityPresent := CompItem.Get(BOMComp."No.");
                if EntityPresent or not IgnoreMissingItemsOrBOMs then
                    SetRecursiveLevelsOnItem(CompItem, LowLevelCode, IgnoreMissingItemsOrBOMs);
            until BOMComp.Next() = 0;
    end;

    procedure SetRecursiveLevelsOnItem(var CompItem: Record Item; LowLevelCode: Integer; IgnoreMissingItemsOrBOMs: Boolean)
    var
        CompBOM: Record "Production BOM Header";
        xLowLevelCode: Integer;
        EntityPresent: Boolean;
        IsHandled: Boolean;
    begin
        OnBeforeSetRecursiveLevelsOnItem(CompItem, LowLevelCode, IgnoreMissingItemsOrBOMs, IsHandled);
        if IsHandled then
            exit;

        Item := CompItem; // to store the last item- used in RecalcLowerLevels
        xLowLevelCode := CompItem."Low-Level Code";
        CompItem."Low-Level Code" := GetMax(Item."Low-Level Code", LowLevelCode);
        if xLowLevelCode <> CompItem."Low-Level Code" then begin
            CompItem.CalcFields("Assembly BOM");
            if CompItem."Assembly BOM" then
                RecalcAsmLowerLevels(CompItem."No.", CompItem."Low-Level Code" + 1, IgnoreMissingItemsOrBOMs);
            if CompItem."Production BOM No." <> '' then begin
                // calc low level code for BOM set in the item
                EntityPresent := CompBOM.Get(CompItem."Production BOM No.");
                if EntityPresent or (not IgnoreMissingItemsOrBOMs) then
                    SetRecursiveLevelsOnBOM(CompBOM, CompItem."Low-Level Code" + 1, IgnoreMissingItemsOrBOMs);
            end;

            RecalcSKULowerLevels(CompItem."No.", CompItem."Low-Level Code" + 1, IgnoreMissingItemsOrBOMs);
            OnSetRecursiveLevelsOnItemOnBeforeCompItemModify(CompItem, IgnoreMissingItemsOrBOMs);
            CompItem.Modify();
        end;
    end;

    procedure SetRecursiveLevelsOnBOM(var CompBOM: Record "Production BOM Header"; LowLevelCode: Integer; IgnoreMissingItemsOrBOMs: Boolean)
    var
        xLowLevelCode: Integer;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeSetRecursiveLevelsOnBOM(CompBOM, LowLevelCode, IgnoreMissingItemsOrBOMs, IsHandled);
        if IsHandled then
            exit;

        xLowLevelCode := CompBOM."Low-Level Code";
        if CompBOM.Status = CompBOM.Status::Certified then begin
            // set low level on this BOM
            CompBOM."Low-Level Code" := GetMax(CompBOM."Low-Level Code", LowLevelCode);
            if xLowLevelCode <> CompBOM."Low-Level Code" then begin
                RecalcLowerLevels(CompBOM."No.", LowLevelCode, IgnoreMissingItemsOrBOMs);
                CompBOM.Modify();
            end;
        end;
    end;

    procedure GetMax(Level1: Integer; Level2: Integer) Result: Integer
    begin
        if Level1 > Level2 then
            Result := Level1
        else
            Result := Level2;
    end;

    procedure SetActualProdBOM(ActualProdBOM2: Record "Production BOM Header")
    begin
        ActualProdBOM := ActualProdBOM2;
    end;

    local procedure CalcDeeperLevel(ProdBOMLine: Record "Production BOM Line"; No: Code[20]; Level: Integer): Boolean
    var
        ProdBOMHeader: Record "Production BOM Header";
        ProductionBOMVersion: Record "Production BOM Version";
    begin
        ProdBOMHeader.SetLoadFields(Status);
        if not ProdBOMHeader.Get(ProdBOMLine."Production BOM No.") then
            exit(false);

        if ProdBOMLine."Version Code" <> '' then begin
            ProductionBOMVersion.SetLoadFields(Status);
            ProductionBOMVersion.Get(ProdBOMLine."Production BOM No.", ProdBOMLine."Version Code");

            if ProductionBOMVersion.Status = ProductionBOMVersion.Status::Closed then
                exit(false);
        end;

        if ProdBOMHeader."No." = ActualProdBOM."No." then
            Error(ProdBomErr, 50, Item."No.", No, Level);

        if ProdBOMLine."Version Code" <> '' then
            exit(ProductionBOMVersion.Status = ProductionBOMVersion.Status::Certified)
        else
            exit(ProdBOMHeader.Status = ProdBOMHeader.Status::Certified);
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterCalcLevels(Type: Option " ",Item,"Production BOM",Assembly; No: Code[20]; var TotalLevels: Integer; Level: Integer; LevelDepth: Integer)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCalcLevels(Type: Option; No: Code[20]; Level: Integer; LevelDepth: Integer; var Result: Integer; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeItemModify(var Item: Record Item)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeSetRecursiveLevelsOnBOM(var ProductionBOMHeader: Record "Production BOM Header"; LowLevelCode: Integer; IgnoreMissingItemsOrBOMs: Boolean; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalcLevelsForProdBOM(var Item: Record Item; No: Code[20]; Level: Integer; LevelDepth: Integer; var TotalLevels: Integer)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnSetRecursiveLevelsOnItemOnBeforeCompItemModify(var CompItem: Record Item; IgnoreMissingItemsOrBOMs: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeSetRecursiveLevelsOnItem(var CompItem: Record Item; LowLevelCode: Integer; IgnoreMissingItemsOrBOMs: Boolean; var IsHandled: Boolean)
    begin
    end;
}