Table 232 Gen. Journal Batch, source in 29

Source29

src/Layers/W1/BaseApp/Finance/GeneralLedger/Journal/GenJournalBatch.Table.al620 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.GeneralLedger.Journal;

using Microsoft.Bank.BankAccount;
using Microsoft.Bank.Setup;
using Microsoft.Finance.GeneralLedger.Account;
using Microsoft.FixedAssets.FixedAsset;
using Microsoft.Foundation.AuditCodes;
using Microsoft.Foundation.NoSeries;
using Microsoft.Intercompany.Partner;
using Microsoft.Purchases.Vendor;
using Microsoft.Sales.Customer;
using System.Automation;

/// <summary>
/// Stores journal batch configurations that group journal lines for processing and provide default settings.
/// Batches inherit behavior from journal templates and enable organized transaction entry with shared defaults.
/// </summary>
/// <remarks>
/// Primary container for journal lines with batch-level validation and processing controls.
/// Key relationships: Links to Gen. Journal Template for configuration and Gen. Journal Line for transactions.
/// Extensibility: Supports approval workflows and batch-level customization through events and configuration fields.
/// </remarks>
table 232 "Gen. Journal Batch"
{
    Caption = 'Gen. Journal Batch';
    DataCaptionFields = Name, Description;
    LookupPageID = "General Journal Batches";
    DataClassification = CustomerContent;

    fields
    {
        /// <summary>
        /// References the journal template that defines behavior and configuration for this batch.
        /// </summary>
        field(1; "Journal Template Name"; Code[10])
        {
            Caption = 'Journal Template Name';
            NotBlank = true;
            TableRelation = "Gen. Journal Template";
        }
        /// <summary>
        /// Unique identifier for the journal batch within the template scope.
        /// </summary>
        field(2; Name; Code[10])
        {
            Caption = 'Name';
            ToolTip = 'Specifies the name of the journal you are creating.';
            NotBlank = true;
        }
        /// <summary>
        /// Descriptive name for the journal batch providing user-friendly identification.
        /// </summary>
        field(3; Description; Text[100])
        {
            Caption = 'Description';
            ToolTip = 'Specifies a brief description of the journal batch you are creating.';
        }
        /// <summary>
        /// Reason code applied to all journal lines in this batch for audit and reporting purposes.
        /// </summary>
        field(4; "Reason Code"; Code[10])
        {
            Caption = 'Reason Code';
            ToolTip = 'Specifies the reason code, a supplementary source code that enables you to trace the entry.';
            TableRelation = "Reason Code";

            trigger OnValidate()
            begin
                if "Reason Code" <> xRec."Reason Code" then begin
                    ModifyLines(FieldNo("Reason Code"));
                    Modify();
                end;
            end;
        }
        /// <summary>
        /// Default balancing account type applied to journal lines in this batch when no specific balancing account is specified.
        /// </summary>
        field(5; "Bal. Account Type"; Enum "Gen. Journal Account Type")
        {
            Caption = 'Bal. Account Type';
            ToolTip = 'Specifies the type of account that a balancing entry is posted to, such as BANK for a cash account.';

            trigger OnValidate()
            begin
                "Bal. Account No." := '';
                Clear(BalAccountId);
                if "Bal. Account Type" <> "Bal. Account Type"::"G/L Account" then
                    "Bank Statement Import Format" := '';
            end;
        }
        /// <summary>
        /// Default balancing account number applied to journal lines when no specific balancing account is specified.
        /// </summary>
        field(6; "Bal. Account No."; Code[20])
        {
            Caption = 'Bal. Account No.';
            ToolTip = 'Specifies the number of the general ledger, customer, vendor, or bank account that the balancing entry is posted to, such as a cash account for cash purchases.';
            TableRelation = if ("Bal. Account Type" = const("G/L Account")) "G/L Account"
            else
            if ("Bal. Account Type" = const(Customer)) Customer
            else
            if ("Bal. Account Type" = const(Vendor)) Vendor
            else
            if ("Bal. Account Type" = const("Bank Account")) "Bank Account"
            else
            if ("Bal. Account Type" = const("Fixed Asset")) "Fixed Asset"
            else
            if ("Bal. Account Type" = const("IC Partner")) "IC Partner";

            trigger OnValidate()
            begin
                if "Bal. Account Type" = "Bal. Account Type"::"G/L Account" then begin
                    CheckGLAcc("Bal. Account No.");
                    UpdateBalAccountId();
                end;
                CheckJnlIsNotRecurring();
            end;
        }
        /// <summary>
        /// Number series used for automatic document number assignment in journal lines within this batch.
        /// </summary>
        field(7; "No. Series"; Code[20])
        {
            Caption = 'No. Series';
            ToolTip = 'Specifies the number series from which entry or record numbers are assigned to new entries or records.';
            TableRelation = "No. Series";

            trigger OnValidate()
            var
                IsHandled: Boolean;
            begin
                IsHandled := false;
                OnBeforeNoSeriesValidate(Rec, IsHandled);
                if IsHandled then
                    exit;

                if "No. Series" <> '' then begin
                    GenJnlTemplate.Get("Journal Template Name");
                    if GenJnlTemplate.Recurring then
                        Error(
                          Text000,
                          FieldCaption("Posting No. Series"));
                    if "No. Series" = "Posting No. Series" then
                        Validate("Posting No. Series", '');
                end;
            end;
        }
        /// <summary>
        /// Number series used for automatic posting document number assignment when journal lines are posted.
        /// </summary>
        field(8; "Posting No. Series"; Code[20])
        {
            Caption = 'Posting No. Series';
            ToolTip = 'Specifies the code for the number series that will be used to assign document numbers to ledger entries that are posted from this journal batch.';
            TableRelation = "No. Series";

            trigger OnValidate()
            begin
                if ("Posting No. Series" = "No. Series") and ("Posting No. Series" <> '') then
                    FieldError("Posting No. Series", StrSubstNo(Text001, "Posting No. Series"));
                ModifyLines(FieldNo("Posting No. Series"));
                Modify();
            end;
        }
        /// <summary>
        /// Controls automatic copying of VAT posting group setup from accounts to journal lines for VAT compliance.
        /// </summary>
        field(9; "Copy VAT Setup to Jnl. Lines"; Boolean)
        {
            Caption = 'Copy VAT Setup to Jnl. Lines';
            ToolTip = 'Specifies whether the program to calculate VAT for accounts and balancing accounts on the journal line of the selected journal batch.';
            InitValue = true;
        }
        /// <summary>
        /// Enables VAT difference handling and manual VAT adjustments for journal lines in this batch.
        /// </summary>
        field(10; "Allow VAT Difference"; Boolean)
        {
            Caption = 'Allow VAT Difference';
            ToolTip = 'Specifies whether to allow the manual adjustment of VAT amounts in journal templates.';

            trigger OnValidate()
            begin
                if "Allow VAT Difference" then begin
                    GenJnlTemplate.Get("Journal Template Name");
                    GenJnlTemplate.TestField("Allow VAT Difference", true);
                end;
            end;
        }
        /// <summary>
        /// Controls whether journal lines in this batch can be exported for electronic payment processing.
        /// </summary>
        field(11; "Allow Payment Export"; Boolean)
        {
            Caption = 'Allow Payment Export';
            ToolTip = 'Specifies if you can export bank payment files from payment journal lines using this general journal batch.';
        }
        /// <summary>
        /// Specifies the import format used for processing bank statements related to this journal batch.
        /// </summary>
        field(12; "Bank Statement Import Format"; Code[20])
        {
            Caption = 'Bank Statement Import Format';
            ToolTip = 'Specifies the format of the bank statement file that can be imported into this general journal batch.';
            TableRelation = "Bank Export/Import Setup".Code where(Direction = const(Import));

            trigger OnValidate()
            begin
                if ("Bank Statement Import Format" <> '') and ("Bal. Account Type" <> "Bal. Account Type"::"G/L Account") then
                    FieldError("Bank Statement Import Format", BankStmtImpFormatBalAccErr);
            end;
        }
        /// <summary>
        /// Journal template type inherited from the associated journal template for workflow and validation control.
        /// </summary>
        field(21; "Template Type"; Enum "Gen. Journal Template Type")
        {
            CalcFormula = lookup("Gen. Journal Template".Type where(Name = field("Journal Template Name")));
            Caption = 'Template Type';
            Editable = false;
            FieldClass = FlowField;
        }
        /// <summary>
        /// Indicates whether this journal batch supports recurring journal entries with automatic regeneration capabilities.
        /// </summary>
        field(22; Recurring; Boolean)
        {
            CalcFormula = lookup("Gen. Journal Template".Recurring where(Name = field("Journal Template Name")));
            Caption = 'Recurring';
            Editable = false;
            FieldClass = FlowField;
        }
        /// <summary>
        /// Controls whether the system automatically suggests balancing amounts when creating journal lines in this batch.
        /// </summary>
        field(23; "Suggest Balancing Amount"; Boolean)
        {
            Caption = 'Suggest Balancing Amount';
            ToolTip = 'Specifies if the Amount field on journal lines for the same document number is automatically prefilled with the value that is required to balance the document.';
        }
        /// <summary>
        /// Indicates whether this journal batch is currently pending approval in the approval workflow system.
        /// </summary>
        field(28; "Pending Approval"; Boolean)
        {
            Caption = 'Pending Approval';
            Editable = false;
        }
        /// <summary>
        /// Controls whether posted journal entries are automatically copied to posted journal lines for archival and audit purposes.
        /// </summary>
        field(31; "Copy to Posted Jnl. Lines"; Boolean)
        {
            Caption = 'Copy to Posted Jnl. Lines';
            ToolTip = 'Specifies whether the journal lines to be copied to posted journal lines of the selected journal batch.';

            trigger OnValidate()
            begin
                if "Copy to Posted Jnl. Lines" then begin
                    GenJnlTemplate.Get("Journal Template Name");
                    GenJnlTemplate.TestField("Copy to Posted Jnl. Lines", true);
                end;
            end;
        }
        field(40; "No. of Lines"; Integer)
        {
            CalcFormula = count("Gen. Journal Line" where("Journal Template Name" = field("Journal Template Name"), "Journal Batch Name" = field(Name)));
            Caption = 'No. of Lines';
            Editable = false;
            FieldClass = FlowField;
            ToolTip = 'Specifies the number of lines in this journal batch.';
        }

        /// <summary>
        /// System-maintained timestamp indicating when this journal batch was last modified for change tracking.
        /// </summary>
        field(8001; "Last Modified DateTime"; DateTime)
        {
            Caption = 'Last Modified DateTime';
        }
        /// <summary>
        /// System identifier linking this batch to the associated G/L Account for API integration and balancing account resolution.
        /// </summary>
        field(8002; BalAccountId; Guid)
        {
            Caption = 'BalAccountId';
            DataClassification = SystemMetadata;

            trigger OnValidate()
            var
                GLAccount: Record "G/L Account";
            begin
                if not IsNullGuid(BalAccountId) then begin
                    if not GLAccount.GetBySystemId(BalAccountId) then
                        Error(BalAccountIdDoesNotMatchAGLAccountErr);

                    CheckGLAcc(GLAccount."No.");
                end;

                Validate("Bal. Account Type", "Bal. Account Type"::"G/L Account");
                Validate("Bal. Account No.", GLAccount."No.");
            end;
        }
    }

    keys
    {
        key(Key1; "Journal Template Name", Name)
        {
            Clustered = true;
        }
    }

    fieldgroups
    {
        fieldgroup(Brick; Name, "Journal Template Name", Description, "Bal. Account Type", "Bal. Account No.")
        {
        }
    }

    trigger OnDelete()
    begin
        ApprovalsMgmt.PreventDeletingRecordWithOpenApprovalEntry(Rec);

        GenJnlAlloc.SetRange("Journal Template Name", "Journal Template Name");
        GenJnlAlloc.SetRange("Journal Batch Name", Name);
        GenJnlAlloc.DeleteAll();
        GenJnlLine.SetRange("Journal Template Name", "Journal Template Name");
        GenJnlLine.SetRange("Journal Batch Name", Name);
        GenJnlLine.DeleteAll(true);
    end;

    trigger OnInsert()
    begin
        LockTable();
        GenJnlTemplate.Get("Journal Template Name");
        if not GenJnlTemplate."Copy VAT Setup to Jnl. Lines" then
            "Copy VAT Setup to Jnl. Lines" := false;
        "Allow Payment Export" := GenJnlTemplate.Type = GenJnlTemplate.Type::Payments;

        SetLastModifiedDateTime();
    end;

    trigger OnModify()
    begin
        ApprovalsMgmt.PreventModifyRecIfOpenApprovalEntryExistForCurrentUser(Rec);
        SetLastModifiedDateTime();
    end;

    trigger OnRename()
    begin
        ApprovalsMgmt.OnRenameRecordInApprovalRequest(xRec.RecordId, RecordId);

        SetLastModifiedDateTime();
    end;

    var
        GenJnlTemplate: Record "Gen. Journal Template";
        GenJnlLine: Record "Gen. Journal Line";
        GenJnlAlloc: Record "Gen. Jnl. Allocation";
        ApprovalsMgmt: Codeunit "Approvals Mgmt.";

#pragma warning disable AA0074
#pragma warning disable AA0470
        Text000: Label 'Only the %1 field can be filled in on recurring journals.';
        Text001: Label 'must not be %1';
#pragma warning restore AA0470
#pragma warning restore AA0074
        BankStmtImpFormatBalAccErr: Label 'must be blank. When Bal. Account Type = Bank Account, then Bank Statement Import Format on the Bank Account card will be used', Comment = 'FIELDERROR ex: Bank Statement Import Format must be blank. When Bal. Account Type = Bank Account, then Bank Statement Import Format on the Bank Account card will be used in Gen. Journal Batch Journal Template Name=''GENERAL'',Name=''CASH''.';
        CannotBeSpecifiedForRecurrJnlErr: Label 'cannot be specified when using recurring journals';
        BalAccountIdDoesNotMatchAGLAccountErr: Label 'The "balancingAccountNumber" does not match to a G/L Account.', Locked = true;

    /// <summary>
    /// Sets up a new journal batch with default values from the journal template.
    /// Copies configuration settings like balancing account, number series, and reason codes from the template.
    /// </summary>
    procedure SetupNewBatch()
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeSetupNewBatch(Rec, IsHandled);
        if IsHandled then
            exit;

        GenJnlTemplate.Get("Journal Template Name");
        "Bal. Account Type" := GenJnlTemplate."Bal. Account Type";
        "Bal. Account No." := GenJnlTemplate."Bal. Account No.";
        "No. Series" := GenJnlTemplate."No. Series";
        "Posting No. Series" := GenJnlTemplate."Posting No. Series";
        "Reason Code" := GenJnlTemplate."Reason Code";
        "Copy VAT Setup to Jnl. Lines" := GenJnlTemplate."Copy VAT Setup to Jnl. Lines";
        "Allow VAT Difference" := GenJnlTemplate."Allow VAT Difference";
        "Copy to Posted Jnl. Lines" := GenJnlTemplate."Copy to Posted Jnl. Lines";

        OnAfterSetupNewBatch(Rec);
    end;

    local procedure CheckGLAcc(AccNo: Code[20])
    var
        GLAcc: Record "G/L Account";
    begin
        if AccNo <> '' then begin
            GLAcc.Get(AccNo);
            GLAcc.CheckGLAcc();
            GLAcc.TestField("Direct Posting", true);
        end;
    end;

    local procedure CheckJnlIsNotRecurring()
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCheckJnlIsNotRecurring(Rec, IsHandled);
        if IsHandled then
            exit;

        if "Bal. Account No." = '' then
            exit;

        GenJnlTemplate.Get("Journal Template Name");
        if GenJnlTemplate.Recurring then
            FieldError("Bal. Account No.", CannotBeSpecifiedForRecurrJnlErr);
    end;

    local procedure ModifyLines(i: Integer)
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeModifyLines(Rec, i, IsHandled);
        if IsHandled then
            exit;

        GenJnlLine.LockTable();
        GenJnlLine.SetRange("Journal Template Name", "Journal Template Name");
        GenJnlLine.SetRange("Journal Batch Name", Name);
        if GenJnlLine.Find('-') then
            repeat
                case i of
                    FieldNo("Reason Code"):
                        GenJnlLine.Validate("Reason Code", "Reason Code");
                    FieldNo("Posting No. Series"):
                        GenJnlLine.Validate("Posting No. Series", "Posting No. Series");
                end;
                GenJnlLine.Modify(true);
            until GenJnlLine.Next() = 0;
    end;

    /// <summary>
    /// Checks whether any journal lines exist for this journal batch.
    /// Used for validation before allowing batch deletion or modification.
    /// </summary>
    /// <returns>True if journal lines exist for this batch, false otherwise.</returns>
    procedure LinesExist(): Boolean
    var
        GenJournalLine: Record "Gen. Journal Line";
    begin
        GenJournalLine.SetRange("Journal Template Name", "Journal Template Name");
        GenJournalLine.SetRange("Journal Batch Name", Name);
        exit(not GenJournalLine.IsEmpty);
    end;

    /// <summary>
    /// Calculates the total balance of all journal lines in this batch.
    /// Returns the sum of Balance (LCY) field for all lines in the batch.
    /// </summary>
    /// <returns>The total balance of all journal lines in local currency.</returns>
    procedure GetBalance(): Decimal
    var
        GenJournalLine: Record "Gen. Journal Line";
    begin
        GenJournalLine.SetRange("Journal Template Name", "Journal Template Name");
        GenJournalLine.SetRange("Journal Batch Name", Name);
        OnGetBalanceOnAfterSetGenJournalLineFilters(GenJournalLine);
        GenJournalLine.CalcSums("Balance (LCY)");
        exit(GenJournalLine."Balance (LCY)");
    end;

    /// <summary>
    /// Checks the balance of the journal batch and triggers appropriate events.
    /// Calls OnGeneralJournalBatchBalanced if balanced, OnGeneralJournalBatchNotBalanced if not.
    /// </summary>
    /// <returns>The current balance of the journal batch in local currency.</returns>
    procedure CheckBalance() Balance: Decimal
    begin
        Balance := GetBalance();

        if Balance = 0 then
            OnGeneralJournalBatchBalanced()
        else
            OnGeneralJournalBatchNotBalanced();
    end;

    /// <summary>
    /// Integration event that occurs when a general journal batch is balanced (total balance equals zero).
    /// Allows customization of behavior when journal batches achieve balance validation.
    /// </summary>
    [IntegrationEvent(true, false)]
    local procedure OnGeneralJournalBatchBalanced()
    begin
    end;

    /// <summary>
    /// Integration event that occurs when a general journal batch is not balanced (total balance is not zero).
    /// Allows customization of error handling or warnings when journal batches fail balance validation.
    /// </summary>
    [IntegrationEvent(true, false)]
    local procedure OnGeneralJournalBatchNotBalanced()
    begin
    end;

    [IntegrationEvent(true, false)]
    [Scope('OnPrem')]
    procedure OnCheckGenJournalLineExportRestrictions()
    begin
    end;

    [IntegrationEvent(true, false)]
    [Scope('OnPrem')]
    procedure OnMoveGenJournalBatch(ToRecordID: RecordID)
    begin
    end;

    local procedure SetLastModifiedDateTime()
    begin
        "Last Modified DateTime" := CurrentDateTime;
    end;

    /// <summary>
    /// Updates the Bal. Account Id field based on the current balancing account number and type.
    /// Synchronizes the system ID field with the referenced G/L Account record.
    /// </summary>
    procedure UpdateBalAccountId()
    var
        GLAccount: Record "G/L Account";
    begin
        if "Bal. Account No." = '' then begin
            Clear(BalAccountId);
            exit;
        end;

        if not GLAccount.Get("Bal. Account No.") then
            exit;

        BalAccountId := GLAccount.SystemId;
    end;

    /// <summary>
    /// Integration event raised after setting up a new journal batch with default configuration.
    /// Enables custom initialization of batch settings and default values.
    /// </summary>
    /// <param name="GenJnlBatch">Newly created journal batch for customization</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterSetupNewBatch(var GenJnlBatch: Record "Gen. Journal Batch")
    begin
    end;

    /// <summary>
    /// Integration event raised before checking if journal batch is not recurring type.
    /// Enables custom validation logic for recurring journal restrictions.
    /// </summary>
    /// <param name="GenJournalBatch">Journal batch to validate for recurring type</param>
    /// <param name="IsHandled">Set to true to skip standard recurring journal validation</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeCheckJnlIsNotRecurring(var GenJournalBatch: Record "Gen. Journal Batch"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before modifying journal lines when batch settings change.
    /// Enables custom handling of line updates during batch modification.
    /// </summary>
    /// <param name="GenJournalBatch">Journal batch being modified</param>
    /// <param name="i">Iterator value during batch processing</param>
    /// <param name="IsHandled">Set to true to skip standard line modification logic</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeModifyLines(var GenJournalBatch: Record "Gen. Journal Batch"; i: Integer; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before validating number series assignment on journal batch.
    /// Enables custom number series validation logic and assignment rules.
    /// </summary>
    /// <param name="GenJournalBatch">Journal batch with number series to validate</param>
    /// <param name="IsHandled">Set to true to skip standard number series validation</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeNoSeriesValidate(var GenJournalBatch: Record "Gen. Journal Batch"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before setting up a new journal batch configuration.
    /// Enables custom batch creation logic and default value assignment.
    /// </summary>
    /// <param name="GenJournalBatch">Journal batch being set up</param>
    /// <param name="IsHandled">Set to true to skip standard batch setup logic</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeSetupNewBatch(GenJournalBatch: Record "Gen. Journal Batch"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised after setting filters on journal lines during balance calculation.
    /// Enables additional filtering criteria for balance computation.
    /// </summary>
    /// <param name="GenJournalLine">Journal line record with applied filters for balance calculation</param>
    [IntegrationEvent(false, false)]
    local procedure OnGetBalanceOnAfterSetGenJournalLineFilters(var GenJournalLine: Record "Gen. Journal Line")
    begin
    end;

}