Codeunit 2670 Variable Allocation Mgt.
- App
- Base Application
- Namespace
- Microsoft.Finance.AllocationAccount
- Versions
- 23-28
Versions171819202122232425262728
Source29
Source in 29
src/Layers/W1/BaseApp/Finance/AllocationAccount/VariableAllocationMgt.Codeunit.al362 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;
using Microsoft.Bank.Ledger;
using Microsoft.Finance.GeneralLedger.Ledger;
using Microsoft.Foundation.Period;
/// <summary>
/// Manages variable allocation calculations based on dynamic account balances and configurable time periods.
/// Provides comprehensive balance calculation logic for G/L accounts and bank accounts with dimension filtering.
/// </summary>
codeunit 2670 "Variable Allocation Mgt."
{
var
NoAccountingPeriodDefinedErr: Label 'The next accounting period for workdate %1 is not defined.\Verify the accounting period setup.', Comment = '%1 - Represents the date.';
PreviousYearIsNotDefinedErr: Label 'Previous year is not defined in accounting period.';
internal procedure CalculateAmountDistributions(var AllocationAccount: Record "Allocation Account"; AmountToDistribute: Decimal; var AmountDistributions: Dictionary of [Guid, Decimal]; var ShareDistributions: Dictionary of [Guid, Decimal]; PostingDate: Date; CurrencyCode: Code[10])
var
AllocationAccountMgt: Codeunit "Allocation Account Mgt.";
AccountSystemID: Guid;
AccountBalance: Decimal;
TotalBalance: Decimal;
AmountDistribution: Decimal;
TotalDistributedAmount: Decimal;
FixedShareDistributions: Dictionary of [Guid, Decimal];
AmountRoundingPrecision: Decimal;
begin
CalculateVariableBalance(AllocationAccount, ShareDistributions, TotalBalance, PostingDate);
FixBalances(ShareDistributions, FixedShareDistributions, TotalBalance);
AmountRoundingPrecision := AllocationAccountMgt.GetCurrencyRoundingPrecision(CurrencyCode);
foreach AccountSystemID in FixedShareDistributions.Keys() do begin
FixedShareDistributions.Get(AccountSystemID, AccountBalance);
if TotalBalance <> 0 then
AmountDistribution := Round(AmountToDistribute * AccountBalance / TotalBalance, AmountRoundingPrecision);
AmountDistributions.Add(AccountSystemID, AmountDistribution);
TotalDistributedAmount += AmountDistribution;
end;
if TotalDistributedAmount = AmountToDistribute then
exit;
foreach AccountSystemID in AmountDistributions.Keys() do begin
AmountDistributions.Get(AccountSystemID, AccountBalance);
if AccountBalance <> 0 then begin
AccountBalance += AmountToDistribute - TotalDistributedAmount;
AmountDistributions.Set(AccountSystemID, AccountBalance);
exit;
end;
end;
end;
internal procedure FixBalances(var ShareDistributions: Dictionary of [Guid, Decimal]; var FixedDistributions: Dictionary of [Guid, Decimal]; var TotalBalance: Decimal)
var
AmountDistribution: Decimal;
AccountSystemID: Guid;
AllZeros: Boolean;
begin
AllZeros := true;
foreach AccountSystemID in ShareDistributions.Keys() do begin
ShareDistributions.Get(AccountSystemID, AmountDistribution);
if AmountDistribution <> 0 then
AllZeros := false;
end;
if AllZeros then begin
foreach AccountSystemID in ShareDistributions.Keys() do begin
FixedDistributions.Add(AccountSystemID, 100);
TotalBalance += 100;
end;
exit;
end;
foreach AccountSystemID in ShareDistributions.Keys() do begin
ShareDistributions.Get(AccountSystemID, AmountDistribution);
FixedDistributions.Add(AccountSystemID, AmountDistribution);
end;
exit;
end;
internal procedure CalculateVariableBalance(var AllocationAccount: Record "Allocation Account"; var ShareDistributions: Dictionary of [Guid, Decimal]; var TotalBalance: Decimal; PostingDate: Date)
var
AllocAccountDistribution: Record "Alloc. Account Distribution";
AccountBalance: Decimal;
StartDate: Date;
EndDate: Date;
begin
Clear(TotalBalance);
Clear(ShareDistributions);
AllocAccountDistribution.SetRange("Allocation Account No.", AllocationAccount."No.");
if AllocAccountDistribution.IsEmpty() then
exit;
if AllocAccountDistribution.FindSet() then
repeat
CalculateDateFilterForDistributionAccount(AllocAccountDistribution, StartDate, EndDate, PostingDate);
GetAccountBalance(AllocAccountDistribution, StartDate, EndDate, AccountBalance, ShareDistributions, TotalBalance);
ShareDistributions.Add(AllocAccountDistribution.SystemId, AccountBalance);
TotalBalance += AccountBalance;
until AllocAccountDistribution.Next() = 0;
end;
internal procedure GetAccountBalance(var AllocAccountDistribution: Record "Alloc. Account Distribution"; StartDate: Date; EndDate: Date; var AccountBalance: Decimal; var ShareDistributions: Dictionary of [Guid, Decimal]; TotalBalance: Decimal)
var
BankAccountLedgerEntry: Record "Bank Account Ledger Entry";
GLEntry: Record "G/L Entry";
Handled: Boolean;
begin
OnGetAccountBalance(AllocAccountDistribution, StartDate, EndDate, AccountBalance, ShareDistributions, TotalBalance, Handled);
if Handled then
exit;
case AllocAccountDistribution."Breakdown Account Type" of
AllocAccountDistribution."Breakdown Account Type"::"Bank Account":
begin
BankAccountLedgerEntry.SetRange("Posting Date", StartDate, EndDate);
BankAccountLedgerEntry.SetRange("Bank Account No.", AllocAccountDistribution."Breakdown Account Number");
if AllocAccountDistribution."Dimension 1 Filter" <> '' then
BankAccountLedgerEntry.SetFilter("Global Dimension 1 Code", AllocAccountDistribution."Dimension 1 Filter");
if AllocAccountDistribution."Dimension 2 Filter" <> '' then
BankAccountLedgerEntry.SetFilter("Global Dimension 2 Code", AllocAccountDistribution."Dimension 2 Filter");
if AllocAccountDistribution."Dimension 3 Filter" <> '' then
BankAccountLedgerEntry.SetFilter("Shortcut Dimension 3 Code", AllocAccountDistribution."Dimension 3 Filter");
if AllocAccountDistribution."Dimension 4 Filter" <> '' then
BankAccountLedgerEntry.SetFilter("Shortcut Dimension 4 Code", AllocAccountDistribution."Dimension 4 Filter");
if AllocAccountDistribution."Dimension 5 Filter" <> '' then
BankAccountLedgerEntry.SetFilter("Shortcut Dimension 5 Code", AllocAccountDistribution."Dimension 5 Filter");
if AllocAccountDistribution."Dimension 6 Filter" <> '' then
BankAccountLedgerEntry.SetFilter("Shortcut Dimension 6 Code", AllocAccountDistribution."Dimension 6 Filter");
if AllocAccountDistribution."Dimension 7 Filter" <> '' then
BankAccountLedgerEntry.SetFilter("Shortcut Dimension 7 Code", AllocAccountDistribution."Dimension 7 Filter");
if AllocAccountDistribution."Dimension 8 Filter" <> '' then
BankAccountLedgerEntry.SetFilter("Shortcut Dimension 8 Code", AllocAccountDistribution."Dimension 8 Filter");
BankAccountLedgerEntry.ReadIsolation := IsolationLevel::ReadCommitted;
BankAccountLedgerEntry.CalcSums("Amount (LCY)");
AccountBalance := BankAccountLedgerEntry."Amount (LCY)";
end;
AllocAccountDistribution."Breakdown Account Type"::"G/L Account":
begin
GLEntry.SetRange("Posting Date", StartDate, EndDate);
GLEntry.SetRange("G/L Account No.", AllocAccountDistribution."Breakdown Account Number");
if AllocAccountDistribution."Business Unit Code Filter" <> '' then
GLEntry.SetFilter("Business Unit Code", AllocAccountDistribution."Business Unit Code Filter");
if AllocAccountDistribution."Dimension 1 Filter" <> '' then
GLEntry.SetFilter("Global Dimension 1 Code", AllocAccountDistribution."Dimension 1 Filter");
if AllocAccountDistribution."Dimension 2 Filter" <> '' then
GLEntry.SetFilter("Global Dimension 2 Code", AllocAccountDistribution."Dimension 2 Filter");
if AllocAccountDistribution."Dimension 3 Filter" <> '' then
GLEntry.SetFilter("Shortcut Dimension 3 Code", AllocAccountDistribution."Dimension 3 Filter");
if AllocAccountDistribution."Dimension 4 Filter" <> '' then
GLEntry.SetFilter("Shortcut Dimension 4 Code", AllocAccountDistribution."Dimension 4 Filter");
if AllocAccountDistribution."Dimension 5 Filter" <> '' then
GLEntry.SetFilter("Shortcut Dimension 5 Code", AllocAccountDistribution."Dimension 5 Filter");
if AllocAccountDistribution."Dimension 6 Filter" <> '' then
GLEntry.SetFilter("Shortcut Dimension 6 Code", AllocAccountDistribution."Dimension 6 Filter");
if AllocAccountDistribution."Dimension 7 Filter" <> '' then
GLEntry.SetFilter("Shortcut Dimension 7 Code", AllocAccountDistribution."Dimension 7 Filter");
if AllocAccountDistribution."Dimension 8 Filter" <> '' then
GLEntry.SetFilter("Shortcut Dimension 8 Code", AllocAccountDistribution."Dimension 8 Filter");
GLEntry.ReadIsolation := IsolationLevel::ReadCommitted;
GLEntry.CalcSums(Amount);
AccountBalance := GLEntry.Amount;
end;
end;
end;
internal procedure CalculateDateFilterForDistributionAccount(AllocAccountDistribution: Record "Alloc. Account Distribution"; var StartDate: Date; var EndDate: Date; PostingDate: Date)
var
AccountingPeriod: Record "Accounting Period";
PeriodStart: Date;
NextPeriodStart: Date;
LastPeriodStart: Date;
LastYearPeriodStart: Date;
LastYearNextPeriodStart: Date;
YearStart: Date;
NextYearStart: Date;
LastYearStart: Date;
begin
if AllocAccountDistribution."Calculation Period" = AllocAccountDistribution."Calculation Period"::"Balance at Date" then begin
StartDate := 0D;
EndDate := PostingDate;
exit;
end;
case AllocAccountDistribution."Calculation Period" of
AllocAccountDistribution."Calculation Period"::Week:
begin
StartDate := CalcDate('<-CW>', PostingDate);
EndDate := CalcDate('<CW>', PostingDate);
exit;
end;
AllocAccountDistribution."Calculation Period"::"Last Week":
begin
StartDate := CalcDate('<-CW-1W>', PostingDate);
EndDate := CalcDate('<CW-1W>', PostingDate);
exit;
end;
AllocAccountDistribution."Calculation Period"::Month:
begin
StartDate := CalcDate('<-CM>', PostingDate);
EndDate := CalcDate('<CM>', PostingDate);
exit;
end;
AllocAccountDistribution."Calculation Period"::"Last Month":
begin
StartDate := CalcDate('<-CM-1M>', PostingDate);
EndDate := CalcDate('<CM>', StartDate);
exit;
end;
AllocAccountDistribution."Calculation Period"::Quarter:
begin
StartDate := CalcDate('<-CQ>', PostingDate);
EndDate := CalcDate('<CQ>', PostingDate);
exit;
end;
AllocAccountDistribution."Calculation Period"::"Last Quarter":
begin
StartDate := CalcDate('<-CQ-1Q>', PostingDate);
EndDate := CalcDate('<CQ>', StartDate);
exit;
end;
AllocAccountDistribution."Calculation Period"::"Month of Last Year":
begin
StartDate := CalcDate('<-CM-1Y>', PostingDate);
EndDate := CalcDate('<CM-1Y>', PostingDate);
exit;
end;
AllocAccountDistribution."Calculation Period"::Year:
begin
StartDate := CalcDate('<-CY>', PostingDate);
EndDate := CalcDate('<CY>', PostingDate);
exit;
end;
AllocAccountDistribution."Calculation Period"::"Last Year":
begin
StartDate := CalcDate('<-CY-1Y>', PostingDate);
EndDate := CalcDate('<CY-1Y>', PostingDate);
exit;
end;
end;
AccountingPeriod.Reset();
StartDate := 0D;
EndDate := DMY2Date(31, 12, 9999);
if AccountingPeriod.IsEmpty() then
exit;
AccountingPeriod.SetFilter("Starting Date", '>%1', PostingDate);
if not AccountingPeriod.FindFirst() then
Error(NoAccountingPeriodDefinedErr, PostingDate);
NextPeriodStart := AccountingPeriod."Starting Date";
AccountingPeriod.SetRange("Starting Date");
AccountingPeriod.Next(-1);
PeriodStart := AccountingPeriod."Starting Date";
AccountingPeriod.Next(-1);
LastPeriodStart := AccountingPeriod."Starting Date";
case AllocAccountDistribution."Calculation Period" of
AllocAccountDistribution."Calculation Period"::Period:
begin
StartDate := PeriodStart;
EndDate := NextPeriodStart - 1;
exit;
end;
AllocAccountDistribution."Calculation Period"::"Last Period":
begin
StartDate := LastPeriodStart;
EndDate := PeriodStart - 1;
exit;
end;
end;
AccountingPeriod.SetFilter("Starting Date", '>%1', CalcDate('<-1Y>', PostingDate));
AccountingPeriod.FindFirst();
LastYearNextPeriodStart := AccountingPeriod."Starting Date";
AccountingPeriod.SetRange("Starting Date");
if AccountingPeriod.Next(-1) = 0 then
if AllocAccountDistribution."Calculation Period" in
[AllocAccountDistribution."Calculation Period"::"Period of Last Year", AllocAccountDistribution."Calculation Period"::"Last Fiscal Year"]
then
Error(PreviousYearIsNotDefinedErr);
LastYearPeriodStart := AccountingPeriod."Starting Date";
AccountingPeriod.SetRange("New Fiscal Year", true);
AccountingPeriod.SetFilter("Starting Date", '>%1', PostingDate);
AccountingPeriod.Find('-');
NextYearStart := AccountingPeriod."Starting Date";
AccountingPeriod.SetRange("Starting Date");
AccountingPeriod.Next(-1);
YearStart := AccountingPeriod."Starting Date";
if AccountingPeriod.Next(-1) = 0 then
if AllocAccountDistribution."Calculation Period" in
[AllocAccountDistribution."Calculation Period"::"Period of Last Year", AllocAccountDistribution."Calculation Period"::"Last Fiscal Year"]
then
Error(PreviousYearIsNotDefinedErr);
LastYearStart := AccountingPeriod."Starting Date";
case AllocAccountDistribution."Calculation Period" of
AllocAccountDistribution."Calculation Period"::"Period of Last Year":
begin
StartDate := LastYearPeriodStart;
EndDate := LastYearNextPeriodStart - 1;
end;
AllocAccountDistribution."Calculation Period"::"Fiscal Year":
begin
StartDate := YearStart;
EndDate := NextYearStart - 1;
end;
AllocAccountDistribution."Calculation Period"::"Last Fiscal Year":
begin
StartDate := LastYearStart;
EndDate := YearStart - 1;
end;
end;
end;
/// <summary>
/// Integration event raised to retrieve account balance for custom account types or calculation methods.
/// </summary>
/// <param name="AllocAccountDistribution">Allocation account distribution record being processed</param>
/// <param name="StartDate">Start date for balance calculation period</param>
/// <param name="EndDate">End date for balance calculation period</param>
/// <param name="AccountBalance">Account balance to be populated by custom logic</param>
/// <param name="ShareDistributions">Dictionary for storing distribution shares</param>
/// <param name="TotalBalance">Total balance across all accounts</param>
/// <param name="Handled">Set to true to indicate custom balance calculation was handled</param>
[IntegrationEvent(false, false)]
local procedure OnGetAccountBalance(var AllocAccountDistribution: Record "Alloc. Account Distribution"; StartDate: Date; EndDate: Date; var AccountBalance: Decimal; var ShareDistributions: Dictionary of [Guid, Decimal]; var TotalBalance: Decimal; var Handled: Boolean);
begin
end;
}