5

I have the following use case:

Acumatica combo box / dropdown, which can have 8 or so values, the selection of which determines the table / DAC used to present in a PXSelector.

e.g.:

-If user select option a, I need to show in PXSelector values from Table A

-If user select option b, I need to show in PXSelector values from Table B

-If user select option c, I need to show in PXSelector value from Table C

I understand that I'd have to use a Custom Selector Attribute, but the details of how to do this are not clear.

billybob
  • 2,859
  • 6
  • 35
  • 55
pmfith
  • 831
  • 6
  • 24

1 Answers1

8

As you said, you need to implement an attribute that inherits from the PXCustomSelectorAttribute class.

1- Create a class PromptType that will have an ID and Description that will hold the types of each table.

public class PromptType
{
    public Type Id { get; set;}
    public Type Description { get; set; }
}

2- Implement the customSelectorAttribute, like this:

public class MyCustomSelector : PXCustomSelectorAttribute
{
    //Class used to display the data into the selector
    [Serializable]
    public class TableDummy : IBqlTable
    {
        #region Id

        [PXInt(IsKey = true)]
        [PXUIField(DisplayName = "Id")]
        public int? Id { get; set; }

        public class id : IBqlField { }

        #endregion


        #region Description

        [PXString(60, IsUnicode = true, InputMask = "")]
        [PXUIField(DisplayName = "Description", Visibility = PXUIVisibility.SelectorVisible)]
        public string Description { get; set; }

        public class description : IBqlField { }

        #endregion
    }

    //Selected table
    private Type _TableSelection;

    //Tables Ids. You can add as much field ID as you want
    private Type _TableFieldA;
    private Type _TableFieldB;
    private Type _TableFieldC;

    //Tables description fields
    private Type _TableAFieldDesc;
    private Type _TableBFieldDesc;
    private Type _TableCFieldDesc;


    public MyCustomSelector(Type tableSelection, Type tableFieldA, Type tableAFieldDesc, Type tableFieldB, Type tableBFieldDesc, Type tableFieldC, Type tableCFieldDesc) : base(typeof(TableDummy.id))
    {
        _TableSelection = tableSelection;
        _TableFieldA = tableFieldA;
        _TableFieldB = tableFieldB;
        _TableFieldC = tableFieldC;
        _TableAFieldDesc = tableAFieldDesc;
        _TableBFieldDesc = tableBFieldDesc;
        _TableCFieldDesc = tableCFieldDesc;
    }

    //Get the name of the selected table by using the private field _TableSelection.
    private string GetSelection()
    {
        var cache = _Graph.Caches[_BqlTable];
        return cache.GetValue(cache.Current, _TableSelection.Name)?.ToString();
    }

    //Return a pompt instance based on the selected table in the dropdown.
    private PromptType GetSelectedTableField(string selectedTable)
    {
        switch (selectedTable)
        {
            case "A":
                return new PromptType() { Id = _TableFieldA, Description = _TableAFieldDesc };
            case "B":
                return new PromptType() { Id = _TableFieldB, Description = _TableBFieldDesc };
            case "C":
                return new PromptType() { Id = _TableFieldC, Description = _TableCFieldDesc };
            default:
                return new PromptType() { Id = _TableFieldA, Description = _TableAFieldDesc };
        }
    }

    //Return the records
    public IEnumerable GetRecords()
    {
        var selectedField = GetSelectedTableField(GetSelection());
        var selectedTable = BqlCommand.GetItemType(selectedField.Id);

        var select = BqlCommand.Compose(
                        typeof(Select<>),
                            selectedTable
                        );

        var cmd = BqlCommand.CreateInstance(select);
        PXView view = new PXView(_Graph, true, cmd);

        foreach (var row in view.SelectMulti())
        {
            var id = (int?)view.Cache.GetValue(row, selectedField.Id.Name);
            var description = view.Cache.GetValue(row, selectedField.Description.Name)?.ToString();
            yield return new TableDummy { Id = id, Description = description };
        }
    }
}

You can change the constructor of the custom attribute, by passing the desired number of tables.


3- Once you have implemented your custom attribute, you can use it in your field like this:

#region DropDown to select a table
[PXDBString(1)]
[PXUIField(DisplayName = "Table Selection")]
[PXStringList(
    new string[]
    {
        "A",
        "B",
        "C"
    },
    new string[]
    {
            "Table A",
            "Table B",
            "Table C"
    })]
public virtual string UsrTableSelection { get; set; }
public abstract class usrTableSelection : IBqlField
{
}
#endregion

#region Selector
[PXDBInt]
[PXUIField(DisplayName = "Table Selector")]
[MyCustomSelector(
    typeof(APRegisterExt.usrTableSelection), 
    typeof(TableA.id),typeof(TableA.description),
    typeof(TableB.id), typeof(TableB.description),
    typeof(PX.Objects.AR.Customer.bAccountID), 
    typeof(PX.Objects.AR.Customer.acctName))]
public virtual int? UsrTableSelector { get; set; }

public abstract class usrTableSelector : IBqlField
{
}
#endregion

4- Also, you should not forget to set the visibility on your tables fields. I'm referring to the tables (DACs) that you want to display in the selector. Let say that you want to display TableA, TableB or TableC depending on your drop down, you need to set the visibility on the fields that you are going to use in the selector.

Here's the implementation of one of the tables that I used during my tests:

[Serializable]
public class TableA : IBqlTable
{
    #region Id

    [PXDBInt(IsKey = true)]
    [PXUIField(DisplayName = "Id")]
    public int? Id { get; set; }

    public class id : IBqlField { }

    #endregion


    #region Description

    [PXDBString(60, IsUnicode = true, InputMask = "")]
    [PXUIField(DisplayName = "Description", Visibility = PXUIVisibility.SelectorVisible)]
    public string Description { get; set; }

    public class description : IBqlField { }

    #endregion


    #region InfoA

    [PXDBString(60, IsUnicode = true, InputMask = "")]
    [PXUIField(DisplayName = "Info A", Visibility = PXUIVisibility.SelectorVisible)]
    public string InfoA { get; set; }

    public class infoA : IBqlField { }

    #endregion
}

By setting the visibility to SelectorVisible, the field will be displayed automatically in the PXSelector.


5- Finally, you need to set CommitChanges to True on your dropdown and set AutoRefresh to True on your form field. Here's an example:

<px:PXDropDown runat="server" ID="CstPXDropDown1" DataField="UsrTableSelection" CommitChanges="True" />
<px:PXSelector runat="server" ID="CstPXSelector2" DataField="UsrTableSelector" AutoRefresh="True" />
billybob
  • 2,859
  • 6
  • 35
  • 55
  • Bilal - Thanks so much for posting this. It worked beautifully. I do have a few questions, though... -Your item #4 isn't clear - is that the PXUIField attribute for the 'TableDummy' DAC? If so, how would I get that to dynamically change the column name for a particular dropdown selection? - Also, the order of the ID, Description fields is reversed for some reason - I can easily change it on the lookup and save it, but why would the description come up first? Anyway, that's it. Thanks again so much... -Peter – pmfith May 06 '16 at 16:10
  • One last thing - and it's not crucial, I'm just curious - how would I use the substitute key parameter in this case so that I get the CD to display instead of the ID? – pmfith May 06 '16 at 16:11
  • @pmfith: I added a more detail example in the step 4. For the column names, it's based on the `DisplayName` that we provided in the `TableDummy`, so it's kind of static. I will check of the order of the columns and get back with an answer. – billybob May 06 '16 at 16:34
  • 1
    I have an issue that's coming up and I don't know why. I've got a description field on the selector whereby I query and place the Description in another field on my page via a FieldVerifying event (e.g., the InventoryItem.InventoryCD is selected and I query the IntenvoryItem DAC for the InventoryItem.Descr - then place that description on a field). This generally works as expected, although now I'm getting a 'Target Value' not found (Target Value is the label of the field). It's there - and it works - but I can't figure where the error is coming from.. Not sure how to post more details – pmfith May 12 '16 at 20:12