Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect Behavior of Table Relation Filters from FlowFields in AL TestPages #7460

Closed
Drakonian opened this issue Jul 14, 2023 · 18 comments
Closed

Comments

@Drakonian
Copy link

Drakonian commented Jul 14, 2023

I have encountered a bug that's related to the use of TestPage type variables in AL Language within the Business Central Test Framework. Specifically, this issue occurs when using a TestPage type variable to simulate UI behavior.

In my project, I have created a flow field and used it in a table relation filter. I also added CalcFields of that flow field on AfterGetCurrRecord of a page. This implementation works as expected in the Business Central UI, for all cases - when a record is already inserted or not.

However, when trying to simulate this behaviour using a TestPage type variable in a test codeunit, the flow field is always returning an empty value. This inconsistency breaks all possible tests of that filtering within the Business Central test framework.

This behavior is not expected as the TestPage variable in test codeunits should simulate the UI behavior in the same way the page works in Business Central.

Attached is a project with example code demonstrating this issue.

Steps to Reproduce:

  1. Create a flow field and use it in a table relation filter.
  2. Add CalcFields of that flow field on AfterGetCurrRecord of a page.
  3. Verify that the flow field works as expected in the Business Central UI, for all cases - when a record is already inserted or not.
  4. Try to simulate this behaviour using a TestPage type variable in a test codeunit.
  5. Note that the flow field returns an empty value when accessed from a TestPage type variable in the test codeunit.

Expected Result:

The flow field should return the correct value, even when accessed from a TestPage type variable in a test codeunit, replicating the same behavior as in the Business Central UI.

Actual Result:

The flow field always returns an empty value when accessed from a TestPage type variable in the test codeunit.

Please consider investigating this matter. The consistent behavior between TestPage variables and actual UI pages is crucial for accurate testing within the Business Central Test Framework.

AL Code which works as expected:

tableextension 50100 "TST Purchase Line" extends "Purchase Line"
{
    fields
    {
        field(50100; "TST Buy From Vendor No."; Code[20])
        {
            Caption = 'Buy From Vendor No.';
            Editable = false;
            FieldClass = FlowField;
            CalcFormula = lookup("Purchase Header"."Buy-from Vendor No." where("Document Type" = field("Document Type"), "No." = field("Document No.")));
        }

        field(50101; "TST Item No."; Code[20])
        {
            DataClassification = CustomerContent;
            Caption = 'Item No.';
            TableRelation = Item."No." where(Blocked = const(false), "Vendor No." = field("TST Buy From Vendor No."));
        }
    }
}

pageextension 50100 "TST Purchase Order Subform" extends "Purchase Order Subform"
{
    layout
    {
        addafter(Description)
        {
            field("TST Item No."; Rec."TST Item No.")
            {
                ApplicationArea = All;
                ToolTip = 'Specifies the value of the Item No. field.';
            }
        }
    }

    trigger OnAfterGetCurrRecord()
    begin
        Rec.CalcFields("TST Buy From Vendor No.");
    end;
}

AL Test which is always fail, I can't find any way to write this test using test pages.

codeunit 50100 "TST Lookup Filter Test"
{
    Subtype = Test;

    [Test]
    [HandlerFunctions('ItemLookupHandler')]
    procedure POLineLookupFilter()
    var
        PurchaseHeader: Record "Purchase Header";
        Item: Record Item;
        PurchaseOrderCard: TestPage "Purchase Order";
    begin

        //[GIVEN] Create purchase order
        LibraryPurchase.CreatePurchHeader(PurchaseHeader, PurchaseHeader."Document Type"::Order, '');

        //[GIVEN] Create Item
        LibraryInventory.CreateItem(Item);

        //[GIVEN] Set Vendor for current item from Purchase Header Vendor
        Item.Validate("Vendor No.", PurchaseHeader."Buy-from Vendor No.");
        Item.Modify(true);

        //[GIVEN] Save Item No. for item lookup handler
        ItemNo := Item."No.";

        //[GIVEN] Purchase Order with subform is opened
        PurchaseOrderCard.OpenEdit();
        PurchaseOrderCard.GoToRecord(PurchaseHeader);

        //[GIVEN] PO Line is created
        PurchaseOrderCard.PurchLines.New();
        PurchaseOrderCard.PurchLines.Type.Value('Item');

        //[WHEN] Item Lookup page is opened
        PurchaseOrderCard.PurchLines."TST Item No.".Lookup();

        //[THEN] Vendor No filter must have value as in UI....
    end;

    [ModalPageHandler]
    procedure ItemLookupHandler(var ItemLookup: TestPage "Item Lookup")
    begin
        VendorNoFilter := ItemLookup.Filter.GetFilter("Vendor No."); // Wrong, filter return '' = filter to empty vendor, flowField is not evaluated
        BlockedFilter := ItemLookup.Filter.GetFilter(Blocked); // Correct, filter from real field
        ItemLookup.GoToKey(ItemNo); //Wrong, system can't find created item because of empty filter which is must contain value
    end;

    var
        LibraryPurchase: Codeunit "Library - Purchase";
        LibraryInventory: Codeunit "Library - Inventory";
        ItemNo: Code[20];
        VendorNoFilter: Text;
        BlockedFilter: Text;
}

Correct:

image

Wrong:

image

@Drakonian
Copy link
Author

TestPageBug.zip

@NKarolak
Copy link

Unfortunately, this is the wrong place to submit your issue.
If you are a partner with access to Yammer, then here's the audience to be addressed:
https://www.yammer.com/dynamicsnavdev/#/threads/inGroup?type=in_group&feedId=13879726&view=all

If you have no access, let me know, and I will link your issue there.

@Drakonian
Copy link
Author

@NKarolak
I don't have access at the moment, but isn't that AL Language's problem?

@NKarolak
Copy link

I believe it is more for the platform team.
I might be wrong about this. However, the expert on Microsoft's side to decide (or at least: to communicate) whether this will be ever implemented, is active on Yammer only. Then let me inform him on Yammer about this. This will at least speed up matters.

@NKarolak
Copy link

For your reference, I've mentioned your issue here: https://www.yammer.com/dynamicsnavdev/threads/2359402983677952

@pri-kise
Copy link

@Drakonian you should be able to request access to the Yammer via the following shortlink: https://aka.ms/bcyammer There should be some option to request access.
Normally this should be approved within a week, but keep in mind that summer breaks have started and a significant number of people are on vacation and might take a little longer right now.

@Drakonian
Copy link
Author

@pri-kise Thanks for you suggestion. I did it and hope will get access :)

@NKarolak
Copy link

From Yammer:

Screenshot_20230724_190805_Viva Engage

@BazookaMusic
Copy link

BazookaMusic commented Aug 2, 2023

As mentioned before, this issue is not related to the AL language itself but concerns the behavior of the runtime. I have created a bug on the team responsible for it and they will investigate it. If it is by design, there will be a response with the explanation to this issue, else it will be fixed in one of the next releases.

@omnisip
Copy link

omnisip commented Aug 2, 2023 via email

@BazookaMusic
Copy link

@omnisip It is in our internal systems, thus I cannot provide a link for it.

@omnisip
Copy link

omnisip commented Aug 2, 2023 via email

@BazookaMusic
Copy link

Again, this is an issue of the runtime and not an issue in the AL language, thus this is not the correct repository for this issue. The responsible team does not use this repository or follow the issues here.

It is possible to make a support request to have the issue tracked through a support case, if the issue is blocking. See below for how:

https://cloudblogs.microsoft.com/dynamics365/it/2019/09/25/new-process-to-submit-support-requests-for-dynamics-365-business-central/

To be clear, the bug was not closed here to be ignored. I forwarded it to the correct team to speed up the process. Issues on the runtime are currently handled through either support cases or internal tickets.

@navdotnetreqs
Copy link

So you are never going to fix this?

@jehelles
Copy link

jehelles commented Oct 3, 2024

I am looking at this bug, and it does not look like a general issue with CalcField/Filters/TestPages. The following stripped down scenario works just fine - I will try and see if I can find the difference between this working case below and the 'real' non-working case.

table 50101 MyPurchaseHeader
{
    fields
    {
        field(1; "Document Type"; Enum "Purchase Document Type")
        {
        }
        field(2; "Buy-from Vendor No."; Code[20])
        {
            TableRelation = Vendor;
        }
        field(3; "No."; Code[20])
        {
        }
    }

    keys
    {
        key(Key1; "Document Type", "No.")
        {
            Clustered = true;
        }
    }
}


table 50100 MyPurchaseLine
{
    fields
    {
        field(1; "Document Type"; Enum "Purchase Document Type")
        {
        }
        field(3; "Document No."; Code[20])
        {
            TableRelation = "MyPurchaseHeader"."No." where("Document Type" = field("Document Type"));
        }
        field(4; "Line No."; Integer)
        {
        }
        field(5; Type; Enum "Purchase Line Type")
        {
        }
        field(6; "No."; Code[20])
        {
            TableRelation = if (Type = const(" ")) "Standard Text"
            else if (Type = const(Item), "Document Type" = filter(<> "Credit Memo" & <> "Return Order")) Item where(Blocked = const(false), "Purchasing Blocked" = const(false))
            else if (Type = const(Item), "Document Type" = filter("Credit Memo" | "Return Order")) Item where(Blocked = const(false));
            ValidateTableRelation = false;
        }
        field(50100; "TST Buy From Vendor No."; Code[20])
        {
            Editable = false;
            FieldClass = FlowField;
            CalcFormula = lookup("MyPurchaseHeader"."Buy-from Vendor No." where("Document Type" = field("Document Type"), "No." = field("Document No.")));
        }
        field(50101; "TST Item No."; Code[20])
        {
            TableRelation = Item."No." where(Blocked = const(false), "Vendor No." = field("TST Buy From Vendor No."));
        }
    }

    keys
    {
        key(Key1; "Document Type", "Document No.", "Line No.")
        {
            Clustered = true;
        }
    }
}

page 50101 MyPurchaseOrder
{
    PageType = Document;
    RefreshOnActivate = true;
    SourceTable = MyPurchaseHeader;
    SourceTableView = where("Document Type" = filter(Order));

    layout
    {
        area(content)
        {
            group(General)
            {
                field("No."; Rec."No.")
                {
                    ApplicationArea = All;
                }
                field("Buy-from Vendor No."; Rec."Buy-from Vendor No.")
                {
                    ApplicationArea = All;
                    NotBlank = true;
                }
            }
            part(PurchLines; MyPurchaseLineSubform)
            {
                ApplicationArea = Suite;
                SubPageLink = "Document No." = field("No.");
                UpdatePropagation = Both;
            }
        }
    }
}

page 50100 MyPurchaseLineSubform
{
    AutoSplitKey = true;
    DelayedInsert = true;
    LinksAllowed = false;
    MultipleNewLines = true;
    PageType = ListPart;
    SourceTable = "MyPurchaseLine";
    SourceTableView = where("Document Type" = filter(Order));

    layout
    {
        area(Content)
        {
            repeater(GroupName)
            {
                field(Type; Rec.Type)
                {
                }
                field("No."; Rec."No.")
                {
                }
                field("TST Item No."; Rec."TST Item No.")
                {
                }
            }
        }
    }

    trigger OnAfterGetCurrRecord()
    begin
        Rec.CalcFields("TST Buy From Vendor No.");
    end;
}

codeunit 50100 MyRepro
{
    Subtype = Test;

    [Test]
    [HandlerFunctions('ItemLookupHandler')]
    procedure POLineLookupFilter()
    var
        MyPurchaseHeader: Record "MyPurchaseHeader";
        Item: Record Item;
        Vendor: Record Vendor;
        MyPurchaseOrder: TestPage "MyPurchaseOrder";
    begin
        Vendor.FindFirst();

        Item.FindFirst();
        Item.Validate("Vendor No.", Vendor."No.");
        Item.Modify(true);

        ItemNo := Item."No.";

        MyPurchaseHeader."Document Type" := MyPurchaseHeader."Document Type"::Order;
        MyPurchaseHeader."No." := 'JHH100';
        MyPurchaseHeader."Buy-from Vendor No." := vendor."No.";

        if (MyPurchaseHeader.Find('=')) then
            MyPurchaseHeader.Delete();

        MyPurchaseHeader.Insert();

        MyPurchaseOrder.OpenEdit();
        MyPurchaseOrder.GoToRecord(MyPurchaseHeader);

        MyPurchaseOrder.PurchLines.New();
        MyPurchaseOrder.PurchLines.Type.Value('Item');

        MyPurchaseOrder.PurchLines."TST Item No.".Lookup();
    end;

    [ModalPageHandler]
    procedure ItemLookupHandler(var ItemLookup: TestPage "Item Lookup")
    var
        VendorNoFilter: Text;
        BlockedFilter: Text;
    begin
        VendorNoFilter := ItemLookup.Filter.GetFilter("Vendor No.");
        Message('VendorNoFilter: %1', VendorNoFilter);
        BlockedFilter := ItemLookup.Filter.GetFilter(Blocked);
        Message('BlockedFilter: %1', BlockedFilter);
        ItemLookup.GoToKey(ItemNo);
    end;

    var
        ItemNo: Code[20];
}

@jehelles
Copy link

jehelles commented Oct 3, 2024

This is unfortunately not a platform bug, but just the platform doing as requested by the AL code.

In the Validate function for Type in Purchase Line, the Init function is called which clears the calculated fields. In the test code, there is nothing re-calculating this field if you go and invoke the lookup immediately afterwards, leading to blank filters.
The same thing can also be reproduced in the web client if the first thing you do after changing the type is to invoke the lookup.

I suggest you subscribed to one of the events in the Type validate function and re-calculate the field there, as that will make the experience better both in the web client and in tests.

@Drakonian
Copy link
Author

Thanks, it's really not a bug, but rather expected behavior given the original conditions. I was able to reproduce this behavior the same way in the UI.

@Drakonian
Copy link
Author

Drakonian commented Oct 3, 2024

As you mention one of possible solution in this specific scenario will be:

[EventSubscriber(ObjectType::Table, Database::"Purchase Line", 'OnAfterValidateType', '', false, false)]
local procedure PurchaseLine_OnAfterValidateType(var PurchaseLine: Record "Purchase Line")
begin
    PurchaseLine.CalcFields("TST Buy From Vendor No.");
end;

Works perfectly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants