Modify default customerPayments api to allow passing in Bal. Account No

I am trying to copy the default customerPayments api page to make a custom AL api that supports passing in Bal. Account Type and Bal. Account No.
I have tried just about everything I can think of and am currently trying to pass in Balance Account Id and then looking up the number and type. I also am trying to repopulate those values in the OnInsertRecord trigger. This is all mostly from ideas from chatgpt so they seem to make sense but still I get an error saying this balance account id is not found in G/L Account list and I am passing in.

[journalId] => 133d60ba-142c-f011-9a4a-002248327837
[accountType] => Customer
[customerId] => 7881713c-914b-f011-be59-6045bdd776d3
[amount] => 251.93
[balanceAccountId] => ff6bd92b-68ee-ed11-8848-002248281f66

the balanceAccountId is definitely a bank account id from the Bank Accounts list. If I read other manually entered journal lines that select that account, they use that id.
Is there somewhere that is defaulting this account type to G/L and then failing before my onInsertRecord code runs? How should I make this work to allow me to set the Balance Account number via a custom api on insert of new record? Thanks so much.

page 83708 "SF_API - CustomerPayments"
{
    PageType = API;
    APIPublisher = 'XXXXXX';
    APIGroup = 'XXXXXX';
    APIVersion = 'v2.0';
    EntityName = 'my_customerPayment';
    EntitySetName = 'my_customerPayments';
    EntityCaption = 'MY Customer Payment';
    EntitySetCaption = 'MY Customer Payments';
    SourceTable = "Gen. Journal Line";
    ODataKeyFields = SystemId;
    DelayedInsert = true;
    Extensible = false;


    layout
    {
        area(content)
        {
            repeater(Group)
            {
                field(id; Rec.SystemId)
                {
                    Caption = 'Id';
                    Editable = false;
                }
                field(journalId; Rec."Journal Batch Id")
                {
                    Caption = 'Journal Id';

                    trigger OnValidate()
                    begin
                        if (not IsNullGuid(xRec."Journal Batch Id")) and (xRec."Journal Batch Id" <> Rec."Journal Batch Id") then
                            Error(CannotEditJournalIdErr);
                    end;
                }
                field(journalDisplayName; Rec."Journal Batch Name")
                {
                    Caption = 'Journal Display Name';

                    trigger OnValidate()
                    begin
                        if (xRec."Journal Batch Name" <> '') and (xRec."Journal Batch Name" <> Rec."Journal Batch Name") then
                            Error(CannotEditBatchNameErr);
                    end;
                }
                field(lineNumber; Rec."Line No.")
                {
                    Caption = 'Line No.';
                }

                field(customerId; Rec."Customer Id")
                {
                    Caption = 'Customer Id';

                    trigger OnValidate()
                    begin
                        if Rec."Customer Id" = BlankGUID then begin
                            Rec."Account No." := '';
                            exit;
                        end;

                        if not Customer.GetBySystemId(Rec."Customer Id") then
                            Error(CustomerIdDoesNotMatchACustomerErr);

                        Rec."Account No." := Customer."No.";
                    end;
                }
                field(customerNumber; Rec."Account No.")
                {
                    Caption = 'Customer No.';
                    TableRelation = Customer;

                    trigger OnValidate()
                    begin
                        if Customer."No." <> '' then begin
                            if Customer."No." <> Rec."Account No." then
                                Error(CustomerValuesDontMatchErr);
                            exit;
                        end;

                        if Rec."Account No." = '' then begin
                            Rec."Customer Id" := BlankGUID;
                            exit;
                        end;

                        if not Customer.Get(Rec."Account No.") then
                            Error(CustomerNumberDoesNotMatchACustomerErr);

                        Rec."Customer Id" := Customer.SystemId;
                    end;
                }
                field(postingDate; Rec."Posting Date")
                {
                    Caption = 'Posting Date';
                }
                field(documentNumber; Rec."Document No.")
                {
                    Caption = 'Document No.';
                }
                field(externalDocumentNumber; Rec."External Document No.")
                {
                    Caption = 'External Document No.';
                }
                field(amount; Rec.Amount)
                {
                    Caption = 'Amount';
                }
                field(appliesToInvoiceId; AppliesToInvoiceIdText)
                {
                    Caption = 'Applies To Invoice Id';

                    trigger OnValidate()
                    var
                        SalesInvoiceAggregator: Codeunit "Sales Invoice Aggregator";
                    begin
                        Rec."Applies-to Invoice Id" := AppliesToInvoiceIdText;
                        if Rec."Applies-to Invoice Id" = BlankGUID then begin
                            AppliesToInvoiceNumberText := '';
                            exit;
                        end;

                        SalesInvoiceHeader.Reset();
                        if not SalesInvoiceAggregator.GetSalesInvoiceHeaderFromId(Format(AppliesToInvoiceIdText), SalesInvoiceHeader) then
                            Error(AppliesToInvoiceIdDoesNotMatchAnInvoiceErr);

                        AppliesToInvoiceNumberText := SalesInvoiceHeader."No.";

                        if Rec."Account No." = '' then
                            if SalesInvoiceHeader."Bill-to Customer No." <> '' then
                                Rec."Account No." := SalesInvoiceHeader."Bill-to Customer No."
                            else
                                Rec."Account No." := SalesInvoiceHeader."Sell-to Customer No.";
                    end;
                }
                field(appliesToInvoiceNumber; AppliesToInvoiceNumberText)
                {
                    Caption = 'Applies To Invoice No.';

                    trigger OnValidate()
                    var
                        SalesInvoiceAggregator: Codeunit "Sales Invoice Aggregator";
                        BlankGUID: Guid;
                    begin
                        Rec."Applies-to Doc. No." := AppliesToInvoiceNumberText;

                        if SalesInvoiceHeader."No." <> '' then begin
                            if SalesInvoiceHeader."No." <> AppliesToInvoiceNumberText then
                                Error(AppliesToDocValuesDontMatchErr);
                            exit;
                        end;

                        if SalesInvoiceHeader.Get(AppliesToInvoiceNumberText) then begin
                            AppliesToInvoiceIdText := SalesInvoiceAggregator.GetSalesInvoiceHeaderId(SalesInvoiceHeader);
                            if Rec."Account No." = '' then
                                if SalesInvoiceHeader."Bill-to Customer No." <> '' then
                                    Rec."Account No." := SalesInvoiceHeader."Bill-to Customer No."
                                else
                                    Rec."Account No." := SalesInvoiceHeader."Sell-to Customer No.";
                        end else
                            AppliesToInvoiceIdText := BlankGUID;
                    end;
                }
                field(description; Rec.Description)
                {
                    Caption = 'Description';
                }
                field(comment; Rec.Comment)
                {
                    Caption = 'Comment';
                }
                field(lastModifiedDateTime; Rec.SystemModifiedAt)
                {
                    Caption = 'Last Modified Date';
                    Editable = false;
                }

                // *** begin Carlisle additions to standard api
                field(balanceAccountId; Rec."Balance Account Id")
                {
                    Caption = 'Balance Account Id';

                    trigger OnValidate()
                    var
                        GLAcc: Record "G/L Account";
                        BankAcc: Record "Bank Account";
                        Vendor: Record Vendor;
                        Customer: Record Customer;
                        Employee: Record Employee;
                    begin
                        // Reset fields first
                        Rec."Bal. Account No." := '';
                        Rec."Bal. Account Type" := Rec."Bal. Account Type"::"G/L Account"; // default

                        if GLAcc.GetBySystemId(Rec."Balance Account Id") then begin
                            Rec."Bal. Account No." := GLAcc."No.";
                            Rec."Bal. Account Type" := Rec."Bal. Account Type"::"G/L Account";
                        end else if BankAcc.GetBySystemId(Rec."Balance Account Id") then begin
                            Rec."Bal. Account No." := BankAcc."No.";
                            Rec."Bal. Account Type" := Rec."Bal. Account Type"::"Bank Account";
                        end else if Customer.GetBySystemId(Rec."Balance Account Id") then begin
                            Rec."Bal. Account No." := Customer."No.";
                            Rec."Bal. Account Type" := Rec."Bal. Account Type"::Customer;
                        end else if Vendor.GetBySystemId(Rec."Balance Account Id") then begin
                            Rec."Bal. Account No." := Vendor."No.";
                            Rec."Bal. Account Type" := Rec."Bal. Account Type"::Vendor;
                        end else if Employee.GetBySystemId(Rec."Balance Account Id") then begin
                            Rec."Bal. Account No." := Employee."No.";
                            Rec."Bal. Account Type" := Rec."Bal. Account Type"::Employee;
                        end else
                            Error('The balanceAccountId does not match any supported record (G/L, Bank, Customer, Vendor, Employee).');
                    end;
                }
                // *** end Carlisle additions to standard api

                part(dimensionSetLines; "APIV2 - Dimension Set Lines")
                {
                    Caption = 'Dimension Set Lines';
                    EntityName = 'dimensionSetLine';
                    EntitySetName = 'dimensionSetLines';
                    SubPageLink = "Parent Id" = field(SystemId), "Parent Type" = const("Journal Line");
                }
            }
        }
    }

    actions
    {
    }

    trigger OnAfterGetCurrRecord()
    begin
        if not FiltersChecked then begin
            CheckFilters();
            FiltersChecked := true;
        end;
    end;

    trigger OnAfterGetRecord()
    begin
        SetCalculatedFields();
    end;

    trigger OnInsertRecord(BelowxRec: Boolean): Boolean
    var
        TempGenJournalLine: Record "Gen. Journal Line" temporary;
        JournalBatchId: Guid;
        JournalBatchIdFilter: Text;
    begin
        if IsNullGuid(Rec."Journal Batch Id") then begin
            JournalBatchIdFilter := Rec.GetFilter("Journal Batch Id");
            if JournalBatchIdFilter = '' then
                Error(FiltersNotSpecifiedErr);
            JournalBatchId := JournalBatchIdFilter;
        end else begin
            JournalBatchIdFilter := Rec.GetFilter("Journal Batch Id");
            if (JournalBatchIdFilter <> '') then begin
                JournalBatchId := JournalBatchIdFilter;
                if (JournalBatchId <> Rec."Journal Batch Id") then
                    Error(JournalBatchIdNameNotMatchErr)
            end else
                JournalBatchId := Rec."Journal Batch Id";
        end;

        ProcessAppliesToInvoiceNumberAndId();

        TempGenJournalLine.Reset();
        TempGenJournalLine.Copy(Rec);


        Clear(Rec);
        GraphMgtCustomerPayments.SetCustomerPaymentsTemplateAndBatch(
          Rec, LibraryAPIGeneralJournal.GetBatchNameFromId(JournalBatchId));
        LibraryAPIGeneralJournal.InitializeLine(
          Rec, TempGenJournalLine."Line No.", TempGenJournalLine."Document No.", TempGenJournalLine."External Document No.");
        TransferGeneratedFieldsFromInitializeLine(TempGenJournalLine);

        GraphMgtCustomerPayments.SetCustomerPaymentsValues(Rec, TempGenJournalLine);
        // *** begin carlisle edits *** //
        // also copy our new account fields back to Rec from Temp.
        Rec."Bal. Account Type" := TempGenJournalLine."Bal. Account Type";
        Rec."Bal. Account No." := TempGenJournalLine."Bal. Account No.";
        Rec."Balance Account Id" := TempGenJournalLine."Balance Account Id";
        // *** end carlisle edits *** //

        SetCalculatedFields();
    end;

    trigger OnModifyRecord(): Boolean
    var
        GenJournalLine: Record "Gen. Journal Line";
    begin
        ProcessAppliesToInvoiceNumberAndId();

        GenJournalLine.GetBySystemId(Rec.SystemId);

        if Rec."Line No." = GenJournalLine."Line No." then
            Rec.Modify(true)
        else begin
            GenJournalLine.TransferFields(Rec, false);
            GenJournalLine.Rename(Rec."Journal Template Name", Rec."Journal Batch Name", Rec."Line No.");
            Rec.TransferFields(GenJournalLine, true);
        end;

        SetCalculatedFields();

        exit(false);
    end;

    trigger OnNewRecord(BelowxRec: Boolean)
    begin
        ClearCalculatedFields();

        Rec."Document Type" := Rec."Document Type"::Payment;
        Rec."Account Type" := Rec."Account Type"::Customer;
        Rec."Applies-to Doc. Type" := Rec."Applies-to Doc. Type"::Invoice;
    end;

    trigger OnOpenPage()
    begin
        GraphMgtCustomerPayments.SetCustomerPaymentsFilters(Rec);
    end;

    var
        Customer: Record Customer;
        SalesInvoiceHeader: Record "Sales Invoice Header";
        GraphMgtCustomerPayments: Codeunit "Graph Mgt - Customer Payments";
        LibraryAPIGeneralJournal: Codeunit "Library API - General Journal";
        AppliesToInvoiceNumberText: Code[20];
        AppliesToInvoiceIdText: Guid;
        FiltersNotSpecifiedErr: Label 'You must specify a journal batch ID or a journal ID to get a journal line.';
        JournalBatchIdNameNotMatchErr: Label 'The Journal Id and Journal Display Name do not match.';
        CannotEditBatchNameErr: Label 'The Journal Batch Display Name cannot be changed.';
        CannotEditJournalIdErr: Label 'The Journal Id cannot be changed.';
        CustomerValuesDontMatchErr: Label 'The customer values do not match to a specific Customer.';
        CustomerIdDoesNotMatchACustomerErr: Label 'The "customerId" does not match to a Customer.', Comment = 'customerId is a field name and should not be translated.';
        CustomerNumberDoesNotMatchACustomerErr: Label 'The "customerNumber" does not match to a Customer.', Comment = 'customerNumber is a field name and should not be translated.';
        AppliesToDocValuesDontMatchErr: Label 'The Applies To Invoice values do not match to the same Invoice.';
        AppliesToInvoiceIdDoesNotMatchAnInvoiceErr: Label 'The "appliesToInvoiceId" should be the ID of an Open, Paid, Corrective, or Canceled Invoice.', Comment = 'appliesToInvoiceId is a field name and should not be translated.';
        FiltersChecked: Boolean;
        BlankGUID: Guid;

    local procedure TransferGeneratedFieldsFromInitializeLine(var GenJournalLine: Record "Gen. Journal Line")
    begin
        if GenJournalLine."Document No." = '' then
            GenJournalLine."Document No." := Rec."Document No.";
    end;

    local procedure SetCalculatedFields()
    begin
        AppliesToInvoiceNumberText := Rec."Applies-to Doc. No.";
        AppliesToInvoiceIdText := Rec."Applies-to Invoice Id";
    end;

    local procedure ClearCalculatedFields()
    begin
        Clear(AppliesToInvoiceIdText);
        Clear(AppliesToInvoiceNumberText);
    end;

    local procedure ProcessAppliesToInvoiceNumberAndId()
    begin
        if AppliesToInvoiceNumberText <> '' then
            Rec."Applies-to Doc. No." := AppliesToInvoiceNumberText;
        Rec."Applies-to Invoice Id" := AppliesToInvoiceIdText;
    end;

    local procedure CheckFilters()
    begin
        if (Rec.GetFilter("Journal Batch Id") = '') and
           (Rec.GetFilter(SystemId) = '')
        then
            Error(FiltersNotSpecifiedErr);
    end;
}
1 Like

Maybe it matters if the Journal Template has G/L account selected? I assumed I could just change that. If this matters, then I need a different template for each account type which seems wrong since I can change the account type in the UI just fine, so it should be allowed.

1 Like

More info:
If I pass garbage Id for balanceAccountId, I get the same error, so my onValidate() handler is never getting called because I don’t see the custom error that should be being thrown from there. So, the error I am seeing is getting thrown before that correct? Which would mean the Bal. Account Type and Bal Account No fields are not being set in my onValidate() function.

I can turn on all 3 fields, Bal. Account Type, Bal. Account No., and Balance Account Id and just pass all of them in on my API request. I think that was still throwing the same error about G/L account ids rather than Bank Account.

Any issue using the standard API?

1 Like

Yeah, I can’t change this to push to different bank accounts based on payment methods. I ended up switching to /journalLines api instead. That seems to allow pushing the bal. account type and bal. account id fields ok. I tried adding those fields to the customerPayments api and they would just keep resetting and not following what I tried to post. journalLines api already has those fields and does do what I post.

1 Like

@Dustin_Yoder Exactly, it’s always good to hit the standard API first before locking yourself with the customizations and all, MS takes care of something we might end up loosing hrs.

1 Like