Codeunit 2679 Purchase Alloc. Acc. Mgt., source in 29
Source29
src/Layers/W1/BaseApp/Finance/AllocationAccount/Purchase/PurchaseAllocAccMgt.Codeunit.al847 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.Finance.AllocationAccount.Purchase;
using Microsoft.Finance.AllocationAccount;
using Microsoft.Finance.Deferral;
using Microsoft.Finance.Dimension;
using Microsoft.Finance.GeneralLedger.Account;
using Microsoft.Inventory.Posting;
using Microsoft.Purchases.Document;
using Microsoft.Purchases.Posting;
using System.Automation;
/// <summary>
/// Manages allocation account operations for purchase documents including line creation and validation.
/// Handles conversion of allocation accounts to detailed purchase lines with proper account assignments and dimension inheritance.
/// </summary>
codeunit 2679 "Purchase Alloc. Acc. Mgt."
{
internal procedure GetOrGenerateAllocationLines(var AllocationLine: Record "Allocation Line"; var ParentSystemId: Guid)
var
AmountToAllocate: Decimal;
PostingDate: Date;
begin
GetOrGenerateAllocationLines(AllocationLine, ParentSystemId, AmountToAllocate, PostingDate);
end;
internal procedure GetOrGenerateAllocationLines(var AllocationLine: Record "Allocation Line"; var ParentSystemId: Guid; var AmountToAllocate: Decimal; var PostingDate: Date)
var
PurchaseLine: Record "Purchase Line";
AllocationAccount: Record "Allocation Account";
AllocationAccountMgt: Codeunit "Allocation Account Mgt.";
begin
PurchaseLine.ReadIsolation := IsolationLevel::ReadCommitted;
PurchaseLine.SetAutoCalcFields("Alloc. Acc. Modified by User");
PurchaseLine.GetBySystemId(ParentSystemId);
AmountToAllocate := PurchaseLine.Amount;
PostingDate := PurchaseLine.GetPurchHeader()."Posting Date";
if PurchaseLine."Alloc. Acc. Modified by User" then
LoadManualAllocationLines(PurchaseLine, AllocationLine)
else begin
GetAllocationAccount(PurchaseLine, AllocationAccount);
AllocationAccountMgt.GenerateAllocationLines(AllocationAccount, AllocationLine, PurchaseLine.Amount, PostingDate, PurchaseLine."Dimension Set ID", PurchaseLine."Currency Code");
AllocationAccountMgt.SplitQuantitiesIfNeeded(PurchaseLine.Quantity, AllocationLine, AllocationAccount);
ReplaceInheritFromParent(AllocationLine, PurchaseLine);
end;
end;
internal procedure LoadManualAllocationLines(var PurchaseLine: Record "Purchase Line"; var AllocationLine: Record "Allocation Line")
var
AllocAccManualOverride: Record "Alloc. Acc. Manual Override";
begin
AllocAccManualOverride.SetRange("Parent System Id", PurchaseLine.SystemId);
AllocAccManualOverride.SetRange("Parent Table Id", Database::"Purchase Line");
AllocAccManualOverride.ReadIsolation := IsolationLevel::ReadUncommitted;
if not AllocAccManualOverride.FindSet() then
exit;
repeat
AllocationLine."Line No." := AllocAccManualOverride."Line No.";
AllocationLine."Destination Account Type" := AllocAccManualOverride."Destination Account Type";
AllocationLine."Destination Account Number" := AllocAccManualOverride."Destination Account Number";
AllocationLine."Global Dimension 1 Code" := AllocAccManualOverride."Global Dimension 1 Code";
AllocationLine."Global Dimension 2 Code" := AllocAccManualOverride."Global Dimension 2 Code";
AllocationLine."Allocation Account No." := AllocAccManualOverride."Allocation Account No.";
AllocationLine."Dimension Set ID" := AllocAccManualOverride."Dimension Set ID";
AllocationLine.Amount := AllocAccManualOverride.Amount;
AllocationLine.Quantity := AllocAccManualOverride.Quantity;
OnLoadManualAllocationLinesOnBeforeInsertAllocationLine(AllocAccManualOverride, AllocationLine);
AllocationLine.Insert();
until AllocAccManualOverride.Next() = 0;
end;
local procedure ReplaceInheritFromParent(var AllocationLine: Record "Allocation Line"; var PurchaseLine: Record "Purchase Line")
var
CurrentFilters: Text;
begin
CurrentFilters := AllocationLine.GetView();
AllocationLine.Reset();
AllocationLine.SetRange(AllocationLine."Destination Account Type", AllocationLine."Destination Account Type"::"Inherit from Parent");
if AllocationLine.IsEmpty then begin
AllocationLine.Reset();
AllocationLine.SetView(CurrentFilters);
exit;
end;
if PurchaseLine."No." = '' then
Error(MustProvideAccountNoForInheritFromParentErr);
AllocationLine.ModifyAll("Destination Account Number", PurchaseLine."No.");
case PurchaseLine.Type of
PurchaseLine.Type::"G/L Account":
AllocationLine.ModifyAll("Destination Account Type", AllocationLine."Destination Account Type"::"G/L Account");
else
Error(InvalidAccountTypeForInheritFromParentErr, PurchaseLine.Type);
end;
IsInheritFromParent := true;
AllocationLine.Reset();
AllocationLine.SetView(CurrentFilters);
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Purch.-Post", 'OnBeforePostPurchaseDoc', '', false, false)]
local procedure HandlePostDocument(var PurchaseHeader: Record "Purchase Header"; PreviewMode: Boolean; CommitIsSupressed: Boolean; var HideProgressWindow: Boolean; var ItemJnlPostLine: Codeunit "Item Jnl.-Post Line"; var IsHandled: Boolean)
var
AllocAccTelemetry: Codeunit "Alloc. Acc. Telemetry";
ContainsAllocationLines: Boolean;
begin
VerifyLinesFromDocument(PurchaseHeader, ContainsAllocationLines);
if not ContainsAllocationLines then
exit;
AllocAccTelemetry.LogPurchaseInvoicePostingUsage();
CreateLinesFromDocument(PurchaseHeader)
end;
[EventSubscriber(ObjectType::Table, Database::"Purchase Line", 'OnBeforeShowDimensions', '', false, false)]
local procedure HandleShowDimensions(var PurchaseLine: Record "Purchase Line"; xPurchaseLine: Record "Purchase Line"; var IsHandled: Boolean)
begin
if IsHandled then
exit;
if (PurchaseLine."Type" <> PurchaseLine."Type"::"Allocation Account") then
exit;
if GuiAllowed() then
if not Confirm(ChangeDimensionsOnAllocationDistributionsQst) then
Error('');
end;
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Approvals Mgmt.", 'OnAfterCheckPurchaseApprovalPossible', '', false, false)]
local procedure HandleAfterCheckSalesApprovalPossible(var PurchaseHeader: Record "Purchase Header")
var
ContainsAllocationLines: Boolean;
begin
VerifyLinesFromDocument(PurchaseHeader, ContainsAllocationLines);
if not ContainsAllocationLines then
exit;
if not GuiAllowed() then
Error(ReplaceAllocationLinesBeforeSendingToApprovalErr);
if not Confirm(ReplaceAllocationLinesBeforeSendingToApprovalQst) then
Error(ReplaceAllocationLinesBeforeSendingToApprovalErr);
CreateLinesFromDocument(PurchaseHeader);
Commit();
if PurchaseHeader.Find() then;
end;
[EventSubscriber(ObjectType::Table, Database::"Purchase Line", 'OnBeforeModifyEvent', '', false, false)]
local procedure CheckBeforeModifyLine(var Rec: Record "Purchase Line"; var xRec: Record "Purchase Line"; RunTrigger: Boolean)
begin
VerifyPurchaseLine(Rec);
DeleteManualDistributionsIfLineChanged(Rec);
end;
[EventSubscriber(ObjectType::Table, Database::"Purchase Line", 'OnBeforeValidateNo', '', false, false)]
local procedure HandleValidateLineNo(CurrentFieldNo: Integer; var IsHandled: Boolean; var PurchaseLine: Record "Purchase Line"; xPurchaseLine: Record "Purchase Line")
var
AllocationAccount: Record "Allocation Account";
begin
if IsHandled then
exit;
if PurchaseLine."Type" <> PurchaseLine."Type"::"Allocation Account" then
exit;
IsHandled := true;
VerifyPurchaseLine(PurchaseLine);
if PurchaseLine.Description <> '' then
exit;
AllocationAccount.Get(PurchaseLine."No.");
PurchaseLine.Description := AllocationAccount.Name;
end;
local procedure DeleteManualDistributionsIfLineChanged(var PurchaseLine: Record "Purchase Line")
var
PreviousPurchaseLine: Record "Purchase Line";
AllocAccManualOverride: Record "Alloc. Acc. Manual Override";
DeleteAllocAccManualOverrideNeeded: Boolean;
begin
if PurchaseLine.IsTemporary() then
exit;
if (not AllocationAccountUsed(PurchaseLine)) then
exit;
PreviousPurchaseLine.ReadIsolation := IsolationLevel::ReadUncommitted;
if not PreviousPurchaseLine.GetBySystemId(PurchaseLine.SystemId) then
exit;
AllocAccManualOverride.SetRange("Parent System Id", PurchaseLine.SystemId);
AllocAccManualOverride.SetRange("Parent Table Id", Database::"Purchase Line");
AllocAccManualOverride.ReadIsolation := IsolationLevel::ReadUncommitted;
if AllocAccManualOverride.IsEmpty() then
exit;
DeleteAllocAccManualOverrideNeeded := (PurchaseLine."Type" <> PreviousPurchaseLine."Type") or
(PurchaseLine."No." <> PreviousPurchaseLine."No.") or
(PurchaseLine."Line Amount" <> PreviousPurchaseLine."Line Amount");
if not DeleteAllocAccManualOverrideNeeded then
exit;
if GuiAllowed() then
if not Confirm(DeleteManualOverridesQst) then
Error('');
AllocAccManualOverride.DeleteAll();
end;
local procedure AllocationAccountUsed(var PurchaseLine: Record "Purchase Line"): Boolean
begin
exit((PurchaseLine.Type = PurchaseLine.Type::"Allocation Account") or (PurchaseLine."Selected Alloc. Account No." <> ''));
end;
local procedure CreateLinesFromDocument(var PurchaseHeader: Record "Purchase Header")
var
AllocationPurchaseLine: Record "Purchase Line";
IsReopen: Boolean;
begin
CheckPurchaseReleaseStatus(PurchaseHeader, IsReopen);
AllocationPurchaseLine.SetRange("Document No.", PurchaseHeader."No.");
AllocationPurchaseLine.SetRange("Document Type", PurchaseHeader."Document Type");
AllocationPurchaseLine.SetRange("Type", AllocationPurchaseLine."Type"::"Allocation Account");
CreateLines(AllocationPurchaseLine);
AllocationPurchaseLine.DeleteAll();
AllocationPurchaseLine.Reset();
AllocationPurchaseLine.SetRange("Document No.", PurchaseHeader."No.");
AllocationPurchaseLine.SetRange("Document Type", PurchaseHeader."Document Type");
AllocationPurchaseLine.SetFilter("Selected Alloc. Account No.", '<>%1', '');
CreateLines(AllocationPurchaseLine);
AllocationPurchaseLine.DeleteAll();
CheckPurchaseReleaseStatus(PurchaseHeader, IsReopen);
end;
local procedure CreateLines(var AllocationPurchaseLine: Record "Purchase Line")
begin
AllocationPurchaseLine.ReadIsolation := IsolationLevel::ReadCommitted;
if AllocationPurchaseLine.IsEmpty() then
exit;
AllocationPurchaseLine.ReadIsolation := IsolationLevel::UpdLock;
AllocationPurchaseLine.FindSet();
repeat
CreateLinesFromAllocationAccountLine(AllocationPurchaseLine);
until AllocationPurchaseLine.Next() = 0;
end;
/// <summary>
/// Creates individual purchase lines from allocation account distribution lines based on allocation percentages or amounts.
/// Processes allocation account configuration and generates corresponding G/L account lines with calculated amounts.
/// </summary>
/// <param name="AllocationAccountPurchaseLine">Source purchase line containing allocation account to be distributed</param>
procedure CreateLinesFromAllocationAccountLine(var AllocationAccountPurchaseLine: Record "Purchase Line")
var
ExistingAccountPurchaseLine: Record "Purchase Line";
AllocationLine: Record "Allocation Line";
AllocationAccount: Record "Allocation Account";
DescriptionChanged: Boolean;
NextLineNo: Integer;
LastLineNo: Integer;
Increment: Integer;
CreatedLines: List of [Guid];
begin
if not GetAllocationAccount(AllocationAccountPurchaseLine, AllocationAccount) then
Error(CannotGetAllocationAccountFromLineErr, AllocationAccountPurchaseLine."Line No.");
VerifyAllocationAccount(AllocationAccount);
GetOrGenerateAllocationLines(AllocationLine, AllocationAccountPurchaseLine.SystemId);
#pragma warning disable AA0210
AllocationLine.SetFilter(Amount, '<>%1', 0);
#pragma warning restore AA0210
if AllocationLine.Count = 0 then
Error(NoLinesGeneratedLbl, AllocationAccountPurchaseLine.RecordId);
NextLineNo := GetNextLine(AllocationAccountPurchaseLine);
LastLineNo := AllocationAccountPurchaseLine."Line No.";
Increment := GetLineIncrement(AllocationAccountPurchaseLine."Line No.", NextLineNo, AllocationLine.Count);
if Increment < -1 then begin
Increment := 10000;
LastLineNo := GetLastLine(AllocationAccountPurchaseLine)
end;
AllocationLine.Reset();
#pragma warning disable AA0210
AllocationLine.SetFilter(Amount, '<>%1', 0);
#pragma warning restore AA0210
AllocationLine.FindSet();
ExistingAccountPurchaseLine.ReadIsolation := IsolationLevel::ReadUncommitted;
ExistingAccountPurchaseLine.SetAutoCalcFields("Alloc. Acc. Modified by User");
ExistingAccountPurchaseLine.GetBySystemId(AllocationAccountPurchaseLine.SystemId);
DescriptionChanged := GetDescriptionChanged(ExistingAccountPurchaseLine.Description, ExistingAccountPurchaseLine.Type, ExistingAccountPurchaseLine."No.");
repeat
CreatedLines.Add(CreatePurchaseLine(ExistingAccountPurchaseLine, AllocationLine, LastLineNo, Increment, AllocationAccount, DescriptionChanged));
until AllocationLine.Next() = 0;
FixQuantityRounding(CreatedLines, ExistingAccountPurchaseLine, AllocationAccount);
DeleteManualOverrides(AllocationAccountPurchaseLine);
end;
local procedure DeleteManualOverrides(var PurchaseLine: Record "Purchase Line")
var
AllocAccManualOverride: Record "Alloc. Acc. Manual Override";
begin
AllocAccManualOverride.SetRange("Parent System Id", PurchaseLine.SystemId);
AllocAccManualOverride.SetRange("Parent Table Id", Database::"Purchase Line");
AllocAccManualOverride.ReadIsolation := IsolationLevel::ReadUncommitted;
if not AllocAccManualOverride.IsEmpty() then
AllocAccManualOverride.DeleteAll();
end;
local procedure VerifyLinesFromDocument(var PurchaseHeader: Record "Purchase Header"; var ContainsAllocationLines: Boolean)
var
AllocationAccountPurchaseLine: Record "Purchase Line";
begin
AllocationAccountPurchaseLine.SetRange("Document No.", PurchaseHeader."No.");
AllocationAccountPurchaseLine.SetRange("Document Type", PurchaseHeader."Document Type");
AllocationAccountPurchaseLine.SetRange("Type", AllocationAccountPurchaseLine."Type"::"Allocation Account");
AllocationAccountPurchaseLine.ReadIsolation := IsolationLevel::ReadUncommitted;
if AllocationAccountPurchaseLine.FindSet() then begin
ContainsAllocationLines := true;
repeat
VerifyPurchaseLines(AllocationAccountPurchaseLine);
until AllocationAccountPurchaseLine.Next() = 0;
end;
AllocationAccountPurchaseLine.Reset();
AllocationAccountPurchaseLine.SetRange("Document No.", PurchaseHeader."No.");
AllocationAccountPurchaseLine.SetRange("Document Type", PurchaseHeader."Document Type");
AllocationAccountPurchaseLine.SetFilter("Selected Alloc. Account No.", '<>%1', '');
AllocationAccountPurchaseLine.ReadIsolation := IsolationLevel::ReadUncommitted;
if AllocationAccountPurchaseLine.FindSet() then begin
ContainsAllocationLines := true;
repeat
VerifyPurchaseLines(AllocationAccountPurchaseLine);
until AllocationAccountPurchaseLine.Next() = 0;
end;
end;
local procedure CreatePurchaseLine(var AllocationPurchaseLine: Record "Purchase Line"; var AllocationLine: Record "Allocation Line"; var LastLineNo: Integer; Increment: Integer; var AllocationAccount: Record "Allocation Account"; var DescriptionChanged: Boolean): Guid
var
PurchaseLine: Record "Purchase Line";
AllocAccHandleDocPost: Codeunit "Alloc. Acc. Handle Doc. Post";
begin
PurchaseLine.TransferFields(AllocationPurchaseLine, true);
PurchaseLine."Line No." := LastLineNo + Increment;
PurchaseLine."Type" := PurchaseLine."Type"::"G/L Account";
if AllocationPurchaseLine."VAT Bus. Posting Group" <> '' then
AllocAccHandleDocPost.SetVATBusPostingGroupCode(AllocationPurchaseLine."VAT Bus. Posting Group");
if AllocationPurchaseLine."VAT Prod. Posting Group" <> '' then
AllocAccHandleDocPost.SetVATProdPostingGroupCode(AllocationPurchaseLine."VAT Prod. Posting Group");
BindSubscription(AllocAccHandleDocPost);
PurchaseLine.Validate("No.", AllocationLine."Destination Account Number");
if AllocationPurchaseLine."Tax Group Code" <> '' then
PurchaseLine.Validate("Tax Group Code", AllocationPurchaseLine."Tax Group Code");
UnbindSubscription(AllocAccHandleDocPost);
if DescriptionChanged then begin
if AllocationPurchaseLine.Description <> '' then
PurchaseLine.Description := AllocationPurchaseLine.Description;
if AllocationPurchaseLine."Description 2" <> '' then
PurchaseLine."Description 2" := AllocationPurchaseLine."Description 2";
end;
MoveAmounts(PurchaseLine, AllocationPurchaseLine, AllocationLine, AllocationAccount);
MoveQuantities(PurchaseLine, AllocationPurchaseLine);
PurchaseLine."VAT Difference" := AllocationLine.Percentage * AllocationPurchaseLine."VAT Difference" / 100;
PurchaseLine."Deferral Code" := AllocationPurchaseLine."Deferral Code";
CopyDeferralSchedule(PurchaseLine, AllocationPurchaseLine);
TransferDimensionSetID(PurchaseLine, AllocationLine, AllocationPurchaseLine."Alloc. Acc. Modified by User");
PurchaseLine."Allocation Account No." := AllocationLine."Allocation Account No.";
PurchaseLine."Selected Alloc. Account No." := '';
PurchaseLine."Alloc. Purch. Line SystemId" := AllocationPurchaseLine.SystemId;
OnBeforeCreatePurchaseLine(PurchaseLine, AllocationLine, AllocationPurchaseLine);
BindSubscription(AllocAccHandleDocPost);
PurchaseLine.Insert(true);
UnbindSubscription(AllocAccHandleDocPost);
LastLineNo := PurchaseLine."Line No.";
RedistributeQuantitiesIfNeededMoveQuantities(PurchaseLine, AllocationPurchaseLine, AllocationLine, AllocationAccount);
exit(PurchaseLine.SystemId);
end;
local procedure CopyDeferralSchedule(PurchaseLine: Record "Purchase Line"; AllocationPurchaseLine: Record "Purchase Line")
var
DeferralHeader: Record "Deferral Header";
DeferralTemplate: Record "Deferral Template";
DeferralUtilities: Codeunit "Deferral Utilities";
begin
if PurchaseLine."Deferral Code" = '' then
exit;
if not DeferralTemplate.Get(PurchaseLine."Deferral Code") then
exit;
if DeferralTemplate."Calc. Method" <> DeferralTemplate."Calc. Method"::"User-Defined" then
exit;
if not DeferralHeader.Get("Deferral Document Type"::Purchase, '', '', PurchaseLine."Document Type".AsInteger(), PurchaseLine."Document No.", AllocationPurchaseLine."Line No.") then
exit;
DeferralUtilities.CreateCopyOfDeferralSchedule(DeferralHeader, PurchaseLine."Line No.");
end;
local procedure MoveQuantities(var PurchaseLine: Record "Purchase Line"; var AllocationPurchaseLine: Record "Purchase Line")
begin
PurchaseLine.Quantity := AllocationPurchaseLine.Quantity;
PurchaseLine."Outstanding Quantity" := AllocationPurchaseLine."Outstanding Quantity";
PurchaseLine."Quantity Received" := AllocationPurchaseLine."Quantity Received";
PurchaseLine."Quantity Invoiced" := AllocationPurchaseLine."Quantity Invoiced";
PurchaseLine."Qty. to Invoice" := AllocationPurchaseLine."Qty. to Invoice";
PurchaseLine."Qty. to Receive" := AllocationPurchaseLine."Qty. to Receive";
PurchaseLine."Quantity (Base)" := AllocationPurchaseLine."Quantity (Base)";
PurchaseLine."Outstanding Qty. (Base)" := AllocationPurchaseLine."Outstanding Qty. (Base)";
PurchaseLine."Qty. to Receive (Base)" := AllocationPurchaseLine."Qty. to Receive (Base)";
PurchaseLine."Return Qty. to Ship" := AllocationPurchaseLine."Return Qty. to Ship";
PurchaseLine."Return Qty. to Ship (Base)" := AllocationPurchaseLine."Return Qty. to Ship (Base)";
end;
local procedure RedistributeQuantitiesIfNeededMoveQuantities(var PurchaseLine: Record "Purchase Line"; var AllocationPurchaseLine: Record "Purchase Line"; var AllocationLine: Record "Allocation Line"; var AllocationAccount: Record "Allocation Account")
var
LinePercentage: Decimal;
begin
if AllocationAccount."Document Lines Split" <> AllocationAccount."Document Lines Split"::"Split Quantity" then
exit;
if AllocationLine.Percentage <> 0 then
LinePercentage := AllocationLine.Percentage
else
LinePercentage := Round(AllocationLine.Quantity / AllocationPurchaseLine.Quantity * 100, AllocationLine.GetQuantityPrecision());
PurchaseLine.Quantity := 0;
if PurchaseLine.IsInvoiceDocType() then
PurchaseLine.Validate(Quantity, Round(AllocationPurchaseLine.Quantity * LinePercentage / 100, AllocationLine.GetQuantityPrecision()))
else
PurchaseLine.Validate(Quantity, AllocationLine.Quantity);
PurchaseLine."Outstanding Quantity" := Round(AllocationPurchaseLine."Outstanding Quantity" * LinePercentage / 100, AllocationLine.GetQuantityPrecision());
PurchaseLine."Quantity Received" := Round(AllocationPurchaseLine."Quantity Received" * LinePercentage / 100, AllocationLine.GetQuantityPrecision());
PurchaseLine."Quantity Invoiced" := Round(AllocationPurchaseLine."Quantity Invoiced" * LinePercentage / 100, AllocationLine.GetQuantityPrecision());
PurchaseLine."Qty. to Invoice" := Round(AllocationPurchaseLine."Qty. to Invoice" * LinePercentage / 100, AllocationLine.GetQuantityPrecision());
PurchaseLine."Qty. to Receive" := Round(AllocationPurchaseLine."Qty. to Receive" * LinePercentage / 100, AllocationLine.GetQuantityPrecision());
PurchaseLine."Quantity (Base)" := Round(AllocationPurchaseLine."Quantity (Base)" * LinePercentage / 100, AllocationLine.GetQuantityPrecision());
PurchaseLine."Outstanding Qty. (Base)" := Round(AllocationPurchaseLine."Outstanding Qty. (Base)" * LinePercentage / 100, AllocationLine.GetQuantityPrecision());
PurchaseLine."Qty. to Receive (Base)" := Round(AllocationPurchaseLine."Qty. to Receive (Base)" * LinePercentage / 100, AllocationLine.GetQuantityPrecision());
PurchaseLine."Return Qty. to Ship" := Round(AllocationPurchaseLine."Return Qty. to Ship" * LinePercentage / 100, AllocationLine.GetQuantityPrecision());
PurchaseLine."Return Qty. to Ship (Base)" := Round(AllocationPurchaseLine."Return Qty. to Ship (Base)" * LinePercentage / 100, AllocationLine.GetQuantityPrecision());
PurchaseLine.Modify();
end;
local procedure FixQuantityRounding(CreatedLines: List of [Guid]; var ExistingAccountPurchaseLine: Record "Purchase Line"; var AllocationAccount: Record "Allocation Account")
var
PurchaseLine: Record "Purchase Line";
LastLine: Record "Purchase Line";
CreatedLineSystemID: Guid;
ModifyLine: Boolean;
begin
if AllocationAccount."Document Lines Split" <> AllocationAccount."Document Lines Split"::"Split Quantity" then
exit;
PurchaseLine.ReadIsolation := IsolationLevel::ReadCommitted;
foreach CreatedLineSystemID in CreatedLines do begin
PurchaseLine.GetBySystemId(CreatedLineSystemID);
PurchaseLine.Mark(true);
end;
PurchaseLine.MarkedOnly(true);
if not PurchaseLine.FindLast() then
exit;
LastLine.Copy(PurchaseLine);
PurchaseLine.CalcSums("Outstanding Quantity", "Quantity Received", "Quantity Invoiced", "Qty. to Invoice", "Qty. to Receive", "Quantity (Base)", "Outstanding Qty. (Base)", "Qty. to Receive (Base)", "Return Qty. to Ship", "Return Qty. to Ship (Base)");
if ExistingAccountPurchaseLine."Outstanding Quantity" - PurchaseLine."Outstanding Quantity" > 0 then begin
LastLine."Outstanding Quantity" += ExistingAccountPurchaseLine."Outstanding Quantity" - PurchaseLine."Outstanding Quantity";
ModifyLine := true;
end;
if ExistingAccountPurchaseLine."Quantity Received" - PurchaseLine."Quantity Received" > 0 then begin
LastLine."Quantity Received" += ExistingAccountPurchaseLine."Quantity Received" - PurchaseLine."Quantity Received";
ModifyLine := true;
end;
if ExistingAccountPurchaseLine."Quantity Invoiced" - PurchaseLine."Quantity Invoiced" > 0 then begin
LastLine."Quantity Invoiced" += ExistingAccountPurchaseLine."Quantity Invoiced" - PurchaseLine."Quantity Invoiced";
ModifyLine := true;
end;
if ExistingAccountPurchaseLine."Qty. to Invoice" - PurchaseLine."Qty. to Invoice" > 0 then begin
LastLine."Qty. to Invoice" += ExistingAccountPurchaseLine."Qty. to Invoice" - PurchaseLine."Qty. to Invoice";
ModifyLine := true;
end;
if ExistingAccountPurchaseLine."Qty. to Receive" - PurchaseLine."Qty. to Receive" > 0 then begin
LastLine."Qty. to Receive" += ExistingAccountPurchaseLine."Qty. to Receive" - PurchaseLine."Qty. to Receive";
ModifyLine := true;
end;
if ExistingAccountPurchaseLine."Quantity (Base)" - PurchaseLine."Quantity (Base)" > 0 then begin
LastLine."Quantity (Base)" += ExistingAccountPurchaseLine."Quantity (Base)" - PurchaseLine."Quantity (Base)";
ModifyLine := true;
end;
if ExistingAccountPurchaseLine."Outstanding Qty. (Base)" - PurchaseLine."Outstanding Qty. (Base)" > 0 then begin
LastLine."Outstanding Qty. (Base)" += ExistingAccountPurchaseLine."Outstanding Qty. (Base)" - PurchaseLine."Outstanding Qty. (Base)";
ModifyLine := true;
end;
if ExistingAccountPurchaseLine."Qty. to Receive (Base)" - PurchaseLine."Qty. to Receive (Base)" > 0 then begin
LastLine."Qty. to Receive (Base)" += ExistingAccountPurchaseLine."Qty. to Receive (Base)" - PurchaseLine."Qty. to Receive (Base)";
ModifyLine := true;
end;
if ExistingAccountPurchaseLine."Return Qty. to Ship" - PurchaseLine."Return Qty. to Ship" > 0 then begin
LastLine."Return Qty. to Ship" += ExistingAccountPurchaseLine."Return Qty. to Ship" - PurchaseLine."Return Qty. to Ship";
ModifyLine := true;
end;
if ExistingAccountPurchaseLine."Return Qty. to Ship (Base)" - PurchaseLine."Return Qty. to Ship (Base)" > 0 then begin
LastLine."Return Qty. to Ship (Base)" += ExistingAccountPurchaseLine."Return Qty. to Ship (Base)" - PurchaseLine."Return Qty. to Ship (Base)";
ModifyLine := true;
end;
if ModifyLine then
LastLine.Modify();
end;
local procedure MoveAmounts(var PurchaseLine: Record "Purchase Line"; var AllocationPurchaseLine: Record "Purchase Line"; var AllocationLine: Record "Allocation Line"; var AllocationAccount: Record "Allocation Account")
var
AllocationAccountMgt: Codeunit "Allocation Account Mgt.";
AmountRoundingPrecision: Decimal;
begin
PurchaseLine."Unit Cost" := AllocationPurchaseLine."Unit Cost";
if AllocationAccount."Document Lines Split" = AllocationAccount."Document Lines Split"::"Split Amount" then begin
if IsInheritFromParent then begin
PurchaseLine.Validate("Direct Unit Cost", GetUnitPrice(PurchaseLine, AllocationLine.Amount));
PurchaseLine."Line Amount" := GetAmount(PurchaseLine, AllocationLine.Amount);
PurchaseLine.Amount := PurchaseLine."Line Amount";
end else begin
AmountRoundingPrecision := AllocationAccountMgt.GetCurrencyRoundingPrecision(PurchaseLine."Currency Code");
PurchaseLine.Validate("Direct Unit Cost", Round(AllocationLine.Amount / PurchaseLine.Quantity, AmountRoundingPrecision));
PurchaseLine."Line Amount" := AllocationLine.Amount;
PurchaseLine.Amount := AllocationLine.Amount;
PurchaseLine."Line Discount Amount" := 0;
end;
end else begin
PurchaseLine.Validate("Direct Unit Cost", AllocationPurchaseLine."Direct Unit Cost");
PurchaseLine."Line Amount" := AllocationPurchaseLine."Line Amount";
end;
end;
local procedure GetAmount(var PurchaseLine: Record "Purchase Line"; AllocationLineAmount: Decimal): Decimal
var
AllocationAccountMgt: Codeunit "Allocation Account Mgt.";
AmountRoundingPrecision: Decimal;
Amount: Decimal;
begin
if PurchaseLine.GetPurchHeader()."Prices Including VAT" then
AllocationLineAmount += AllocationLineAmount * PurchaseLine."VAT %" / 100;
AmountRoundingPrecision := AllocationAccountMgt.GetCurrencyRoundingPrecision(PurchaseLine."Currency Code");
Amount := Round((AllocationLineAmount + AmountRounding), AmountRoundingPrecision);
AmountRounding += (AllocationLineAmount - Amount);
exit(Amount);
end;
local procedure GetUnitPrice(var PurchaseLine: Record "Purchase Line"; AllocationLineAmount: Decimal): Decimal
var
AllocationAccountMgt: Codeunit "Allocation Account Mgt.";
AmountRoundingPrecision: Decimal;
begin
if PurchaseLine.GetPurchHeader()."Prices Including VAT" then begin
AllocationLineAmount += AllocationLineAmount * PurchaseLine."VAT %" / 100;
AmountRoundingPrecision := AllocationAccountMgt.GetCurrencyRoundingPrecision(PurchaseLine."Currency Code");
exit(Round(AllocationLineAmount / PurchaseLine.Quantity, AmountRoundingPrecision));
end;
exit(AllocationLineAmount / PurchaseLine.Quantity);
end;
local procedure GetNextLine(var AllocationPurchaseLine: Record "Purchase Line"): Integer
var
PurchaseLine: Record "Purchase Line";
begin
PurchaseLine.SetRange("Document No.", AllocationPurchaseLine."Document No.");
PurchaseLine.SetRange("Document Type", AllocationPurchaseLine."Document Type");
PurchaseLine.SetFilter("Line No.", '>%1', AllocationPurchaseLine."Line No.");
PurchaseLine.ReadIsolation := IsolationLevel::ReadCommitted;
if PurchaseLine.FindFirst() then
exit(PurchaseLine."Line No.");
exit(AllocationPurchaseLine."Line No." + 10000);
end;
local procedure GetLastLine(var AllocationPurchaseLine: Record "Purchase Line"): Integer
var
PurchaseLine: Record "Purchase Line";
begin
PurchaseLine.SetRange("Document No.", AllocationPurchaseLine."Document No.");
PurchaseLine.SetRange("Document Type", AllocationPurchaseLine."Document Type");
PurchaseLine.SetFilter("Line No.", '>%1', AllocationPurchaseLine."Line No.");
PurchaseLine.ReadIsolation := IsolationLevel::ReadCommitted;
if PurchaseLine.FindLast() then
exit(PurchaseLine."Line No.");
exit(AllocationPurchaseLine."Line No.");
end;
local procedure GetLineIncrement(CurrentLineNo: Integer; NextLineNo: Integer; LinesToInsert: Integer): Integer
var
Increment: Integer;
begin
Increment := Round((NextLineNo - CurrentLineNo) / LinesToInsert, 1);
if Increment < LinesToInsert then
exit(-1);
if Increment >= 1000 then
exit(1000);
if Increment >= 100 then
exit(100);
if Increment >= 10 then
exit(10);
exit(Increment);
end;
local procedure GetAllocationAccount(var AllocationAccountPurchaseLine: Record "Purchase Line"; var AllocationAccount: Record "Allocation Account"): Boolean
begin
if AllocationAccountPurchaseLine."Selected Alloc. Account No." <> '' then
exit(AllocationAccount.Get(AllocationAccountPurchaseLine."Selected Alloc. Account No."));
if AllocationAccountPurchaseLine."Type" = AllocationAccountPurchaseLine."Type"::"Allocation Account" then
exit(AllocationAccount.Get(AllocationAccountPurchaseLine."No."));
exit(false);
end;
internal procedure VerifyAllocationAccount(var AllocationAccount: Record "Allocation Account")
var
AllocAccountDistribution: Record "Alloc. Account Distribution";
begin
AllocAccountDistribution.SetRange("Allocation Account No.", AllocationAccount."No.");
AllocAccountDistribution.SetFilter("Destination Account Type", '<>%1&<>%2', AllocAccountDistribution."Destination Account Type"::"G/L Account", AllocAccountDistribution."Destination Account Type"::"Inherit from Parent");
if not AllocAccountDistribution.IsEmpty() then
Error(AllocationAccountMustOnlyDistributeToGLAccountsErr);
end;
/// <summary>
/// Transfers dimension set ID from allocation line to purchase line with merge capabilities.
/// Combines dimensions when both records have existing dimension sets or overwrites when specified.
/// </summary>
/// <param name="PurchaseLine">Purchase line to receive dimension set ID</param>
/// <param name="AllocationLine">Source allocation line with dimension set ID</param>
/// <param name="ModifiedByUser">Whether to overwrite existing dimensions when modified by user</param>
procedure TransferDimensionSetID(var PurchaseLine: Record "Purchase Line"; var AllocationLine: Record "Allocation Line"; ModifiedByUser: Boolean)
var
DimensionManagement: Codeunit DimensionManagement;
DimensionSetIDArr: array[10] of Integer;
begin
if AllocationLine."Dimension Set ID" = 0 then
exit;
if PurchaseLine."Dimension Set ID" = AllocationLine."Dimension Set ID" then
exit;
if (PurchaseLine."Dimension Set ID" = 0) or ModifiedByUser then begin
PurchaseLine."Dimension Set ID" := AllocationLine."Dimension Set ID";
DimensionManagement.UpdateGlobalDimFromDimSetID(
PurchaseLine."Dimension Set ID", PurchaseLine."Shortcut Dimension 1 Code", PurchaseLine."Shortcut Dimension 2 Code");
exit;
end;
DimensionSetIDArr[1] := PurchaseLine."Dimension Set ID";
DimensionSetIDArr[2] := AllocationLine."Dimension Set ID";
PurchaseLine."Dimension Set ID" :=
DimensionManagement.GetCombinedDimensionSetID(
DimensionSetIDArr, PurchaseLine."Shortcut Dimension 1 Code", PurchaseLine."Shortcut Dimension 2 Code");
end;
local procedure VerifyPurchaseLines(var AllocationAccountPurchaseLine: Record "Purchase Line")
begin
AllocationAccountPurchaseLine.ReadIsolation := IsolationLevel::ReadCommitted;
if not AllocationAccountPurchaseLine.FindSet() then
exit;
repeat
VerifyPurchaseLine(AllocationAccountPurchaseLine);
until AllocationAccountPurchaseLine.Next() = 0;
end;
/// <summary>
/// Validates the selected allocation account number on purchase line and verifies compatibility.
/// Ensures the allocation account is properly configured and the purchase line uses G/L account type.
/// </summary>
/// <param name="PurchaseLine">Purchase line with selected allocation account to validate</param>
procedure VerifySelectedAllocationAccountNo(var PurchaseLine: Record "Purchase Line")
var
PurchaseHeader: Record "Purchase Header";
AllocationAccount: Record "Allocation Account";
begin
PurchaseHeader.Get(PurchaseLine."Document Type", PurchaseLine."Document No.");
PurchaseHeader.TestField(Status, PurchaseHeader.Status::Open);
if PurchaseLine."Selected Alloc. Account No." = '' then
exit;
if not (PurchaseLine.Type = PurchaseLine.Type::"G/L Account") then
Error(InvalidAccountTypeForInheritFromParentErr, PurchaseLine.Type);
AllocationAccount.Get(PurchaseLine."Selected Alloc. Account No.");
VerifyAllocationAccount(AllocationAccount);
end;
local procedure GetDescriptionChanged(ExistingDescription: Text; AccountType: Enum "Purchase Line Type"; AccountValue: Code[20]): Boolean
var
GLAccount: Record "G/L Account";
AllocationAccount: Record "Allocation Account";
ExpectedDescription: Text;
begin
case AccountType of
AccountType::"G/L Account":
begin
if not GLAccount.Get(AccountValue) then
exit(false);
ExpectedDescription := GLAccount.Name;
end;
AccountType::"Allocation Account":
begin
if not AllocationAccount.Get(AccountValue) then
exit(false);
ExpectedDescription := AllocationAccount.Name;
end;
else
exit(false);
end;
exit(ExistingDescription <> ExpectedDescription);
end;
local procedure VerifyPurchaseLine(var PurchaseLine: Record "Purchase Line")
var
AllocationAccount: Record "Allocation Account";
AllocationAccountMgt: Codeunit "Allocation Account Mgt.";
begin
if not AllocationAccountUsed(PurchaseLine) then
exit;
OnBeforeVerifyPurchaseLine(PurchaseLine);
if PurchaseLine."Selected Alloc. Account No." <> '' then
VerifySelectedAllocationAccountNo(PurchaseLine)
else begin
if PurchaseLine."No." = '' then
exit;
AllocationAccount.Get(PurchaseLine."No.");
VerifyAllocationAccount(AllocationAccount);
AllocationAccountMgt.VerifyNoInheritFromParentUsed(AllocationAccount."No.");
end;
end;
local procedure CheckPurchaseReleaseStatus(var PurchaseHeader: Record "Purchase Header"; var IsReopen: Boolean)
var
PurchaseRelease: Codeunit "Release Purchase Document";
begin
if IsReopen then begin
PurchaseRelease.Run(PurchaseHeader);
PurchaseHeader.Modify();
exit;
end;
if PurchaseHeader.Status <> PurchaseHeader.Status::Released then
exit;
PurchaseRelease.Reopen(PurchaseHeader);
IsReopen := true;
PurchaseRelease.SetSkipCheckReleaseRestrictions();
PurchaseHeader.SetHideValidationDialog(true);
end;
/// <summary>
/// Integration event raised before creating purchase line from allocation line during distribution process.
/// Enables custom modification of purchase line fields before standard allocation line processing.
/// </summary>
/// <param name="PurchaseLine">Target purchase line being created</param>
/// <param name="AllocationLine">Source allocation line with distribution configuration</param>
/// <param name="AllocationPurchaseLine">Original allocation purchase line triggering the distribution</param>
[IntegrationEvent(false, false)]
local procedure OnBeforeCreatePurchaseLine(var PurchaseLine: Record "Purchase Line"; var AllocationLine: Record "Allocation Line"; var AllocationPurchaseLine: Record "Purchase Line")
begin
end;
/// <summary>
/// Integration event raised before verifying purchase line during allocation account validation.
/// Enables custom validation logic for purchase lines before standard allocation verification.
/// </summary>
/// <param name="PurchaseLine">Purchase line being validated for allocation compatibility</param>
[IntegrationEvent(false, false)]
local procedure OnBeforeVerifyPurchaseLine(var PurchaseLine: Record "Purchase Line")
begin
end;
[IntegrationEvent(false, false)]
local procedure OnLoadManualAllocationLinesOnBeforeInsertAllocationLine(var AllocAccManualOverride: Record "Alloc. Acc. Manual Override"; var AllocationLine: Record "Allocation Line")
begin
end;
var
AmountRounding: Decimal;
IsInheritFromParent: Boolean;
AllocationAccountMustOnlyDistributeToGLAccountsErr: Label 'The allocation account must contain G/L accounts as distribution accounts.';
CannotGetAllocationAccountFromLineErr: Label 'Cannot get allocation account from Purchase line %1.', Comment = '%1 - Line No., it is an integer that identifies the line e.g. 10000, 200000.';
NoLinesGeneratedLbl: Label 'No allocation account lines were generated for Purchase line %1.', Comment = '%1 - Unique identification of the line.';
ChangeDimensionsOnAllocationDistributionsQst: Label 'The line is connected to the Allocation Account. Any dimensions that you change through this action will be merged with dimensions that are defined on the Allocation Line. To change the final dimensions you should invoke the Redistribute Account Allocations action.\\Do you want to continue?';
DeleteManualOverridesQst: Label 'Modifying the line will delete all manual overrides for allocation account.\\Do you want to continue?';
InvalidAccountTypeForInheritFromParentErr: Label 'Selected account type - %1 cannot be used for allocation accounts that have inherit from parent defined.', Comment = '%1 - Account type, e.g. G/L Account, Customer, Vendor, Bank Account, Fixed Asset, Item, Resource, Charge, Project, or Blank.';
MustProvideAccountNoForInheritFromParentErr: Label 'You must provide an account number for allocation account with inherit from parent defined.';
ReplaceAllocationLinesBeforeSendingToApprovalErr: Label 'You must replace allocation lines before sending the document to approval.';
ReplaceAllocationLinesBeforeSendingToApprovalQst: Label 'Document contains allocation lines.\\Do you want to replace them before sending the document to approval?';
}