Codeunit 63 Sales-Explode BOM, source in 29
Source29
src/Layers/W1/BaseApp/Sales/Document/SalesExplodeBOM.Codeunit.al361 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.Sales.Document;
using Microsoft.Assembly.Document;
using Microsoft.Foundation.ExtendedText;
using Microsoft.Foundation.UOM;
using Microsoft.Inventory.Availability;
using Microsoft.Inventory.BOM;
using Microsoft.Inventory.Item;
using Microsoft.Inventory.Tracking;
using Microsoft.Projects.Resources.Resource;
/// <summary>
/// Expands a BOM item on a sales line into its individual component items.
/// </summary>
codeunit 63 "Sales-Explode BOM"
{
TableNo = "Sales Line";
trigger OnRun()
var
AssembleToOrderLink: Record "Assemble-to-Order Link";
HideDialog: Boolean;
IsHandled: Boolean;
begin
OnBeforeOnRun(Rec, IsHandled);
if IsHandled then
exit;
SalesHeader := Rec.GetSalesHeader();
SalesHeader.TestStatusOpen();
CheckSalesLine(Rec);
ReservMgt.SetReservSource(Rec);
ReservMgt.SetItemTrackingHandling(1);
ReservMgt.DeleteReservEntries(true, 0);
FromBOMComp.SetRange("Parent Item No.", Rec."No.");
OnRunOnBeforeCalcNoOfBOMComp(FromBOMComp, Rec);
NoOfBOMComp := FromBOMComp.Count();
OnBeforeConfirmExplosion(Rec, Selection, HideDialog, NoOfBOMComp);
if not HideDialog then begin
if NoOfBOMComp = 0 then
Error(Text001, Rec."No.");
Selection := StrMenu(Text004, 2);
if Selection = 0 then
exit;
end else
Selection := 2;
OnAfterConfirmExplosion(Rec, Selection, HideDialog);
if Rec."Document Type" in [Rec."Document Type"::Order, Rec."Document Type"::Invoice] then begin
ToSalesLine := Rec;
FromBOMComp.SetRange(Type, FromBOMComp.Type::Item);
FromBOMComp.SetFilter("No.", '<>%1', '');
IsHandled := false;
OnRunOnAfterFromBOMCompSetFilters(FromBOMComp, Rec, IsHandled, ToSalesLine);
if not IsHandled then
if FromBOMComp.FindSet() then
repeat
FromBOMComp.TestField(Type, FromBOMComp.Type::Item);
OnBeforeCopyFromBOMToSalesLine(ToSalesLine, FromBOMComp);
Item.Get(FromBOMComp."No.");
ToSalesLine."Line No." := 0;
ToSalesLine."No." := FromBOMComp."No.";
ToSalesLine."Variant Code" := FromBOMComp."Variant Code";
ToSalesLine."Unit of Measure Code" := FromBOMComp."Unit of Measure Code";
ToSalesLine."Qty. per Unit of Measure" := UOMMgt.GetQtyPerUnitOfMeasure(Item, FromBOMComp."Unit of Measure Code");
ToSalesLine."Outstanding Quantity" := Round(Rec."Quantity (Base)" * FromBOMComp."Quantity per", UOMMgt.QtyRndPrecision());
IsHandled := false;
OnRunOnBeforeItemCheckAvailSalesLineCheck(ToSalesLine, FromBOMComp, Rec, IsHandled, HideDialog);
if not IsHandled then
if ToSalesLine."Outstanding Quantity" > 0 then
if ItemCheckAvail.SalesLineCheck(ToSalesLine) then
ItemCheckAvail.RaiseUpdateInterruptedError();
until FromBOMComp.Next() = 0;
end;
if Rec."BOM Item No." = '' then
BOMItemNo := Rec."No."
else
BOMItemNo := Rec."BOM Item No.";
if Rec.Type = Rec.Type::Item then
AssembleToOrderLink.DeleteAsmFromSalesLine(Rec);
InitParentItemLine(Rec);
if TransferExtendedText.SalesCheckIfAnyExtText(ToSalesLine, false) then
TransferExtendedText.InsertSalesExtText(ToSalesLine);
IsHandled := false;
OnRunOnBeforeExplodeBOMCompLines(Rec, ToSalesLine, NoOfBOMComp, Selection, IsHandled, BOMItemNo);
if not IsHandled then
ExplodeBOMCompLines(Rec);
OnAfterOnRun(ToSalesLine, Rec);
end;
var
ToSalesLine: Record "Sales Line";
FromBOMComp: Record "BOM Component";
SalesHeader: Record "Sales Header";
ItemTranslation: Record "Item Translation";
Item: Record Item;
Resource: Record Resource;
ItemCheckAvail: Codeunit "Item-Check Avail.";
UOMMgt: Codeunit "Unit of Measure Management";
TransferExtendedText: Codeunit "Transfer Extended Text";
ReservMgt: Codeunit "Reservation Management";
BOMItemNo: Code[20];
LineSpacing: Integer;
NextLineNo: Integer;
NoOfBOMComp: Integer;
Selection: Integer;
#pragma warning disable AA0074
#pragma warning disable AA0470
Text000: Label 'The BOM cannot be exploded on the sales lines because it is associated with purchase order %1.';
Text001: Label 'Item %1 is not a BOM.';
#pragma warning restore AA0470
Text003: Label 'There is not enough space to explode the BOM.';
Text004: Label '&Copy dimensions from BOM,&Retrieve dimensions from components';
#pragma warning restore AA0074
/// <summary>
/// Explodes the BOM component lines for the specified sales line.
/// </summary>
/// <param name="SalesLine">The sales line containing the BOM item to explode.</param>
procedure CallExplodeBOMCompLines(SalesLine: Record "Sales Line")
begin
ExplodeBOMCompLines(SalesLine);
end;
local procedure ExplodeBOMCompLines(SalesLine: Record "Sales Line")
var
PreviousSalesLine: Record "Sales Line";
InsertLinesBetween: Boolean;
begin
ToSalesLine.Reset();
ToSalesLine.SetRange("Document Type", SalesLine."Document Type");
ToSalesLine.SetRange("Document No.", SalesLine."Document No.");
ToSalesLine := SalesLine;
NextLineNo := SalesLine."Line No.";
InsertLinesBetween := false;
if ToSalesLine.Find('>') then
if ToSalesLine.IsExtendedText() and (ToSalesLine."Attached to Line No." = SalesLine."Line No.") then begin
ToSalesLine.SetRange("Attached to Line No.", SalesLine."Line No.");
ToSalesLine.FindLast();
ToSalesLine.SetRange("Attached to Line No.");
NextLineNo := ToSalesLine."Line No.";
InsertLinesBetween := ToSalesLine.Find('>');
end else
InsertLinesBetween := true;
if InsertLinesBetween then
LineSpacing := (ToSalesLine."Line No." - NextLineNo) div (1 + NoOfBOMComp)
else
LineSpacing := 10000;
if LineSpacing = 0 then
Error(Text003);
FromBOMComp.Reset();
FromBOMComp.SetRange("Parent Item No.", SalesLine."No.");
OnExplodeBOMCompLinesOnAfterFromBOMCompSetFilters(FromBOMComp, SalesLine, LineSpacing, NextLineNo);
FromBOMComp.FindSet();
repeat
ToSalesLine.Init();
NextLineNo := NextLineNo + LineSpacing;
ToSalesLine."Line No." := NextLineNo;
case FromBOMComp.Type of
FromBOMComp.Type::" ":
ToSalesLine.Type := ToSalesLine.Type::" ";
FromBOMComp.Type::Item:
ToSalesLine.Type := ToSalesLine.Type::Item;
FromBOMComp.Type::Resource:
ToSalesLine.Type := ToSalesLine.Type::Resource;
end;
OnExplodeBOMCompLinesOnAfterAssignType(ToSalesLine, SalesLine, FromBOMComp, SalesHeader);
if ToSalesLine.Type <> ToSalesLine.Type::" " then begin
FromBOMComp.TestField("No.");
ToSalesLine.Validate("No.", FromBOMComp."No.");
if SalesHeader."Location Code" <> SalesLine."Location Code" then
ToSalesLine.Validate("Location Code", SalesLine."Location Code");
if FromBOMComp."Variant Code" <> '' then
ToSalesLine.Validate("Variant Code", FromBOMComp."Variant Code");
if ToSalesLine.Type = ToSalesLine.Type::Item then begin
ToSalesLine."Drop Shipment" := SalesLine."Drop Shipment";
Item.Get(FromBOMComp."No.");
ToSalesLine.Validate("Unit of Measure Code", FromBOMComp."Unit of Measure Code");
ToSalesLine."Qty. per Unit of Measure" := UOMMgt.GetQtyPerUnitOfMeasure(Item, ToSalesLine."Unit of Measure Code");
ToSalesLine.Validate(Quantity, Round(SalesLine."Quantity (Base)" * FromBOMComp."Quantity per", UOMMgt.QtyRndPrecision()));
end else
if ToSalesLine.Type = ToSalesLine.Type::Resource then begin
Resource.Get(FromBOMComp."No.");
ToSalesLine.Validate("Unit of Measure Code", FromBOMComp."Unit of Measure Code");
ToSalesLine."Qty. per Unit of Measure" := UOMMgt.GetResQtyPerUnitOfMeasure(Resource, ToSalesLine."Unit of Measure Code");
ToSalesLine.Validate(Quantity, Round(SalesLine."Quantity (Base)" * FromBOMComp."Quantity per", UOMMgt.QtyRndPrecision()));
end else
ToSalesLine.Validate(Quantity, SalesLine."Quantity (Base)" * FromBOMComp."Quantity per");
if SalesHeader."Shipment Date" <> SalesLine."Shipment Date" then
ToSalesLine.Validate("Shipment Date", SalesLine."Shipment Date");
end;
if SalesHeader."Language Code" = '' then
ToSalesLine.Description := FromBOMComp.Description
else
if not ItemTranslation.Get(FromBOMComp."No.", FromBOMComp."Variant Code", SalesHeader."Language Code") then
ToSalesLine.Description := FromBOMComp.Description;
ToSalesLine."BOM Item No." := BOMItemNo;
OnInsertOfExplodedBOMLineToSalesLine(ToSalesLine, SalesLine, FromBOMComp, SalesHeader, LineSpacing);
ToSalesLine.Insert();
OnExplodeBOMCompLinesOnAfterToSalesLineInsert(ToSalesLine, SalesLine, FromBOMComp, SalesHeader, NextLineNo);
ToSalesLine.Validate("Qty. to Assemble to Order");
if (ToSalesLine.Type = ToSalesLine.Type::Item) and (ToSalesLine.Reserve = ToSalesLine.Reserve::Always) then
ToSalesLine.AutoReserve();
if Selection = 1 then begin
ToSalesLine."Shortcut Dimension 1 Code" := SalesLine."Shortcut Dimension 1 Code";
ToSalesLine."Shortcut Dimension 2 Code" := SalesLine."Shortcut Dimension 2 Code";
ToSalesLine."Dimension Set ID" := SalesLine."Dimension Set ID";
ToSalesLine.Modify();
end;
if PreviousSalesLine."Document No." <> '' then
if TransferExtendedText.SalesCheckIfAnyExtText(PreviousSalesLine, false) then
TransferExtendedText.InsertSalesExtText(PreviousSalesLine);
PreviousSalesLine := ToSalesLine;
until FromBOMComp.Next() = 0;
if TransferExtendedText.SalesCheckIfAnyExtText(ToSalesLine, false) then
TransferExtendedText.InsertSalesExtText(ToSalesLine);
OnAfterExplodeBOMCompLines(SalesLine, Selection, LineSpacing, ToSalesLine);
end;
local procedure CheckSalesLine(SalesLine: Record "Sales Line")
begin
SalesLine.TestField(Type, SalesLine.Type::Item);
SalesLine.TestField("Quantity Shipped", 0);
SalesLine.TestField("Return Qty. Received", 0);
SalesLine.CalcFields("Reserved Qty. (Base)");
SalesLine.TestField("Reserved Qty. (Base)", 0);
if SalesLine."Purch. Order Line No." <> 0 then
Error(Text000, SalesLine."Purchase Order No.");
if SalesLine."Job Contract Entry No." <> 0 then begin
SalesLine.TestField("Job No.", '');
SalesLine.TestField("Job Contract Entry No.", 0);
end;
OnAfterCheckSalesLine(SalesLine);
end;
local procedure InitParentItemLine(var FromSalesLine: Record "Sales Line")
begin
ToSalesLine := FromSalesLine;
ToSalesLine.Init();
ToSalesLine.Description := FromSalesLine.Description;
ToSalesLine."Description 2" := FromSalesLine."Description 2";
ToSalesLine."BOM Item No." := BOMItemNo;
OnBeforeToSalesLineModify(ToSalesLine, FromSalesLine);
ToSalesLine.Modify();
end;
[IntegrationEvent(false, false)]
local procedure OnAfterConfirmExplosion(var SalesLine: Record "Sales Line"; var Selection: Integer; var HideDialog: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnAfterExplodeBOMCompLines(var SalesLine: Record "Sales Line"; Selection: Integer; LineSpacing: Integer; var ToSalesLine: Record "Sales Line")
begin
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeOnRun(var SalesLine: Record "Sales Line"; var IsHandled: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeConfirmExplosion(var SalesLine: Record "Sales Line"; var Selection: Integer; var HideDialog: Boolean; var NoOfBOMComp: Integer)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeCopyFromBOMToSalesLine(var SalesLine: Record "Sales Line"; BOMComponent: Record "BOM Component")
begin
end;
[IntegrationEvent(false, false)]
local procedure OnExplodeBOMCompLinesOnAfterFromBOMCompSetFilters(var BOMComponent: Record "BOM Component"; SalesLine: Record "Sales Line"; var LineSpacing: Integer; var NextLineNo: Integer)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnBeforeToSalesLineModify(var ToSalesLine: Record "Sales Line"; FromSalesLine: Record "Sales Line")
begin
end;
[IntegrationEvent(false, false)]
local procedure OnInsertOfExplodedBOMLineToSalesLine(var ToSalesLine: Record "Sales Line"; SalesLine: Record "Sales Line"; BOMComponent: Record "BOM Component"; var SalesHeader: Record "Sales Header"; LineSpacing: Integer)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnRunOnBeforeItemCheckAvailSalesLineCheck(var ToSalesLine: Record "Sales Line"; FromBOMComp: Record "BOM Component"; SalesLine: Record "Sales Line"; var IsHandled: Boolean; var HideDialog: Boolean)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnExplodeBOMCompLinesOnAfterToSalesLineInsert(ToSalesLine: Record "Sales Line"; SalesLine: Record "Sales Line"; FromBOMComp: Record "BOM Component"; SalesHeader: Record "Sales Header"; var NextLineNo: Integer)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnExplodeBOMCompLinesOnAfterAssignType(var ToSalesLine: Record "Sales Line"; SalesLine: Record "Sales Line"; BOMComponent: Record "BOM Component"; var SalesHeader: Record "Sales Header");
begin
end;
[IntegrationEvent(false, false)]
local procedure OnAfterOnRun(ToSalesLine: Record "Sales Line"; SalesLine: Record "Sales Line")
begin
end;
[IntegrationEvent(false, false)]
local procedure OnRunOnBeforeCalcNoOfBOMComp(var BOMComponent: Record "BOM Component"; SalesLine: Record "Sales Line")
begin
end;
[IntegrationEvent(false, false)]
local procedure OnRunOnAfterFromBOMCompSetFilters(var BOMComponent: Record "BOM Component"; SalesLine: Record "Sales Line"; var IsHandled: Boolean; var ToSalesLine: Record "Sales Line")
begin
end;
[IntegrationEvent(false, false)]
local procedure OnRunOnBeforeExplodeBOMCompLines(var SalesLine: Record "Sales Line"; var ToSalesLine: Record "Sales Line"; var NoOfBOMComp: Integer; var Selection: Integer; var IsHandled: Boolean; var BOMItemNo: Code[20])
begin
end;
[IntegrationEvent(false, false)]
local procedure OnAfterCheckSalesLine(SalesLine: Record "Sales Line")
begin
end;
}