Skip to main content
Question

How to Dynamically Display and Save Fields of a Selected DAC in Acumatica Customization

  • January 12, 2026
  • 14 replies
  • 136 views

Forum|alt.badge.img+2

I 'm working on a customization where I need to allow users to select a DAC (for example Vendor, Contact, Location, etc.) and then dynamically display the list of available fields for that DAC so the selected field can be saved for later processing (similar to a generic Inquiry screen). The DAC is resolved dynamically at runtime, and the requirement is to list all usable fields (including standard DAC fields and extension Usr* fields) without hardcoding them. I tried to find a way by investigating Generic Inquiry screen, but I was unable to find working solution

    #region FullDACName
[PXDBString(255, IsFixed = true, InputMask = "")]
[PXUIField(DisplayName = "Full DACName")]
[PXStringList(
new[]
{
"PX.Objects.AP.VendorR",
"PX.Objects.CR.Location",
"PX.Objects.CR.Contact",
"PX.Objects.AP.BAccount"
},
new[]
{
"Vendor",
"Location",
"Contact",
"BAccount"
}
)]
public virtual string FullDACName { get; set; }
public abstract class fullDACName : PX.Data.BQL.BqlString.Field<fullDACName> { }
#endregion


#region DACFieldName
[PXDBString(128, IsFixed = true, InputMask = "")]
[PXUIField(DisplayName = "DACField Name")]
[PXStringList(new string[] { null }, new string[] { "" }, ExclusiveValues = false)]
public virtual string DACFieldName { get; set; }
public abstract class dACFieldName : PX.Data.BQL.BqlString.Field<dACFieldName> { }
#endregion

This is My custom DAC fields. I can hardcode the DAC list. That is fine. I just need to List fields of the selected DAC
I tried to set fields using events as bellows. Nothing works yet.
 

protected virtual void _(Events.FieldSelecting<ESEftsureAPIMapper, ESEftsureAPIMapper.dACFieldName> e)
{
if (e.Row == null)
return;

var values = new List<string>();
var labels = new List<string>();

if (!string.IsNullOrWhiteSpace(e.Row.FullDACName))
{
Type dacType = PXBuildManager.GetType(e.Row.FullDACName, false);

if (dacType != null && typeof(IBqlTable).IsAssignableFrom(dacType))
{
PXGraph graph = e.Cache.Graph;


if (!graph.Caches.ContainsKey(dacType))
{
graph.Views.Add(dacType.Name,
new PXView(graph, false, BqlCommand.CreateInstance(dacType)));
}

PXCache cache = graph.Caches[dacType];
cache.CreateInstance();

foreach (string field in cache.Fields)
{
values.Add(field);
labels.Add(field);
}

//PXStringListAttribute.SetList<ESEftsureAPIMapper.dACFieldName>(cache, e.Row, values.ToArray(), labels.ToArray());
}
}


e.ReturnState = PXStringState.CreateInstance(
e.ReturnState,
255,
null,
nameof(ESEftsureAPIMapper.dACFieldName),
false,
1,
null,
values.ToArray(),
labels.ToArray(),
true,
null
);


}

protected virtual void _(Events.RowSelected<ESEftsureAPIMapper> e)
{
if (e.Row == null)
return;

var row = e.Row;
var cache = e.Cache;

if (string.IsNullOrWhiteSpace(row.FullDACName))
return;

Type dacType = PXBuildManager.GetType(row.FullDACName, false);

if (dacType == null || !typeof(IBqlTable).IsAssignableFrom(dacType))
return;

PXCache dacCache = this.Caches[dacType];

List<string> values = new List<string>();
List<string> labels = new List<string>();

foreach (string field in dacCache.Fields)
{
values.Add(field);

PXFieldState state = dacCache.GetStateExt(null, field) as PXFieldState;
labels.Add(state?.DisplayName ?? field);
}

// Optional: add empty
values.Insert(0, string.Empty);
labels.Insert(0, string.Empty);

PXStringListAttribute.SetList<ESEftsureAPIMapper.dACFieldName>(
cache,
row,
values.ToArray(),
labels.ToArray()
);
}

 

14 replies

darylbowman
Captain II
Forum|alt.badge.img+16

Have you debugged to see how far you've gotten?


Forum|alt.badge.img+2
  • Author
  • Semi-Pro I
  • February 25, 2026

HI ​@darylbowman;

Sorry for the late reply. I was stuck with another project.

The code runs through both events completely without any errors.

values.Insert(0, string.Empty);
labels.Insert(0, string.Empty);

When I debugged, I could see that both values and labels lists were properly filled with DAC fields at that stage.

PXStringListAttribute.SetList<ESEftsureAPIMapper.dACFieldName>(
cache, row, values.ToArray(), labels.ToArray());

This line also executed without any errors. However, it did not make any changes in the UI. When checking from the UI, the LOV appears empty.
Thank you.


Forum|alt.badge.img+8
  • Captain II
  • February 25, 2026

Not sure if this will help. I’m doing something similar in a project of mine and it’s working. The two differences are:

  1. I’m extending SOOrderEntry and your screen is a new screen.
  2. The declaration of my field uses the PXStringList() attribute, but I’m not including any parameters:

  [PXStringList()]

whereas you have:

 [PXStringList(new string[] { null }, new string[] { "" }, ExclusiveValues = false)]

Forum|alt.badge.img+2
  • Author
  • Semi-Pro I
  • February 26, 2026

Hi ​@Django 
Thanks for your comment. I tried using [PXStringList()]. but still issue remains same. 


harutyungevorgyan
Jr Varsity I
Forum|alt.badge.img+4

Hi ​@PDharmasena10 ,

Thank you for your interesting question!

Firstly, it's important to note that "FullDacName" will not work if it is just the DAC name without the namespace. For example, it should be "PX.Objects.PO.POLine" instead of just "POLine."

Overall, I haven't used this method to retrieve fields before. It seems unnecessary to involve caching and adding a new dynamic view just to access the properties.

I have developed a method using reflection that will provide you with the information you need without directly modifying the cache. You can use this method to obtain both field names and types, and then utilize that information as needed. For instance, by simply using SetList in the RowSelected event.

Let me know if you have any questions!
 

/// <summary>
/// Retrieves a dictionary containing the names and types of all fields defined in the specified Data Access Class
/// (DAC) including CacheExtensions.
/// </summary>
/// <remarks>This method uses reflection to obtain field information from the DAC. If the specified type
/// cannot be found or does not implement IBqlTable, the result will be an empty dictionary.</remarks>
/// <param name="fullDACName">The fully qualified name of the DAC type from which to retrieve field information. Cannot be null, empty, or
/// whitespace.</param>
/// <returns>A dictionary mapping field names to their corresponding types for the specified DAC. Returns an empty dictionary
/// if the DAC name is invalid or the type does not implement IBqlTable.</returns>
private static Dictionary<string, Type> GetAllFieldsFrom(string fullDACName)
{
Dictionary<string, Type> fieldTypes = new Dictionary<string, Type>();
if (string.IsNullOrWhiteSpace(fullDACName))
{
return fieldTypes;
}
Type dacType = PXBuildManager.GetType(fullDACName, false);

if (dacType == null ||
!typeof(IBqlTable).IsAssignableFrom(dacType))
{
return fieldTypes;
}
var method = typeof(PXCache<>)
.MakeGenericType(dacType)
.GetMethod("GetClassFieldTypes",
BindingFlags.Static | BindingFlags.NonPublic);

fieldTypes = (Dictionary<string, Type>)method?.Invoke(null, null);
return fieldTypes;
}

 


Forum|alt.badge.img+2
  • Author
  • Semi-Pro I
  • February 26, 2026

Hi ​@Django 

I was able to fix the issue where the list was not visible in the UI by setting the grid setting MatrixMode = true. After that, the LOV values started appearing correctly in the UI.

However, I’m now facing another issue. After selecting a value from the LOV, I’m unable to save the record. It throws the error:

“Cannot insert NULL value into NoteID.”
 

NoteID is the standard note field that normally exists in every DAC. Interestingly, if I remove the RowSelected event and the PXStringList attribute from the DAC, I can save the row by manually typing the value.

As I understand, this might be happening because the RowSelected event reinitializes the list repeatedly (this is just my assumption).

Additionally, when I click OK on the error message and then hit Save again, it inserts a new row with the same primary key values, but other fields remain blank.

I tried moving the logic to the FieldUpdated event, but PXStringListAttribute.SetList<>() can only be used inside the RowSelected event.


Forum|alt.badge.img+2
  • Author
  • Semi-Pro I
  • February 26, 2026

Hi ​@harutyungevorgyan 

Thank you for your comment. I will try your solution as well.

However, as I understand, the current issue is how to update the values of the PXStringList programmatically. Fetching the list of fields works fine, even with this approach.

The PXStringListAttribute.SetList<>() method can only be used inside the RowSelected event. From what I’ve seen in the core code, they seem to have implemented it in a similar way on the Generic Inquiry screen.

I believe I may be missing something, but I’m not sure what it is.


harutyungevorgyan
Jr Varsity I
Forum|alt.badge.img+4

@PDharmasena10 You are right. I misunderstood the question, but I still recommend this approach.

 

SetList always works fine, and you are right that you should use it in RowSelected.

The NoteID exception can be related to your dirty cache, especially if you just added some DACs to your cache and created views for them.

Please try this method first, and if the issue is still there, please share the trace so we know exactly where that message comes from.


Forum|alt.badge.img+2
  • Author
  • Semi-Pro I
  • February 26, 2026

Hi ​@harutyungevorgyan 
I tried your approach and it loads the LOV of fields into the UI as expected. Following is my graph code. 
 

  public class EftsureAPIMapperMaint : PXGraph<EftsureAPIMapperMaint, ESEftsureAPIMapper>
{

public PXFilter<FormFilter> FormFilterView;

public SelectFrom<ESEftsureAPIMapper>.View ESEftsureAPIMapperView;

private static Dictionary<string, Type> GetAllFieldsFrom(string fullDACName)
{
Dictionary<string, Type> fieldTypes = new Dictionary<string, Type>();
if (string.IsNullOrWhiteSpace(fullDACName))
{
return fieldTypes;
}
Type dacType = PXBuildManager.GetType(fullDACName, false);
if (dacType == null || !typeof(IBqlTable).IsAssignableFrom(dacType))
{
return fieldTypes;
}
var method = typeof(PXCache<>)
.MakeGenericType(dacType)
.GetMethod("GetClassFieldTypes",
BindingFlags.Static | BindingFlags.NonPublic);
fieldTypes = (Dictionary<string, Type>)method?.Invoke(null, null);
return fieldTypes;
}

protected virtual void _(Events.RowSelected<ESEftsureAPIMapper> e)
{
if (e.Row == null)
return;

if (string.IsNullOrWhiteSpace(e.Row.FullDACName))
return;

var fieldTypes = GetAllFieldsFrom(e.Row.FullDACName.Trim());

if (fieldTypes == null || fieldTypes.Count == 0)
return;

var values = fieldTypes.Keys.ToArray();

PXStringListAttribute.SetList<ESEftsureAPIMapper.dACFieldName>(
e.Cache,
e.Row,
values,
values
);
}

It still gives the same error. As you mentioned, I also think it may be due to a dirty cache issue. It seems like the system is trying to insert a completely new row into the grid instead of updating the row that we intended to modify.

Below is the trace for your reference.

Cannot insert the value NULL into column 'NoteID', table 'Demo25R1.dbo.ESEftsureAPIMapper'; column does not allow nulls. UPDATE fails.The statement has been terminated.

 


harutyungevorgyan
Jr Varsity I
Forum|alt.badge.img+4

@PDharmasena10 ,

Could you please share the ESEftsureAPIMapper DAC with me? The issue doesn't seem to be related to your Graph code. You may have a NoteID field in your SQL, but it's not included in your DAC. Sometimes, the solution can be as simple as that.


Forum|alt.badge.img+2
  • Author
  • Semi-Pro I
  • February 27, 2026

Hi ​@harutyungevorgyan 

I double-checked the code, and it does contain the NoteID field. I have also attached the complete DAC code for your reference.

One more thing to mention: if I remove the StringList attribute and the RowSelected event from the graph, the system allows me to save the record without any issue. In that case, we have to manually type the value for the field.
 

using System;
using PX.Data;

namespace PersifiV03
{
[Serializable]
[PXCacheName("ESEftsureAPIMapper")]
public class ESEftsureAPIMapper : PXBqlTable, IBqlTable
{
#region Formid
[PXDBInt(IsKey = true)]
[PXUIField(DisplayName = "Formid")]
public virtual int? Formid { get; set; }
public abstract class formid : PX.Data.BQL.BqlInt.Field<formid> { }
#endregion

#region FieldID
[PXDBString(128, IsKey = true, InputMask = "")]
[PXUIField(DisplayName = "Field ID")]
public virtual string FieldID { get; set; }
public abstract class fieldID : PX.Data.BQL.BqlString.Field<fieldID> { }
#endregion

#region FieldName
[PXDBString(128, InputMask = "")]
[PXUIField(DisplayName = "Field Name")]
public virtual string FieldName { get; set; }
public abstract class fieldName : PX.Data.BQL.BqlString.Field<fieldName> { }
#endregion

#region FullDACName
[PXDBString(255, InputMask = "")]
[PXUIField(DisplayName = "Full DACName")]
[PXStringList(
new[]
{
"PX.Objects.AP.VendorR",
"PX.Objects.CR.Location",
"PX.Objects.CR.Contact",
"PX.Objects.AP.BAccount"
},
new[]
{
"Vendor",
"Location",
"Contact",
"BAccount"
}
)]
public virtual string FullDACName { get; set; }
public abstract class fullDACName : PX.Data.BQL.BqlString.Field<fullDACName> { }
#endregion

#region Dacname
[PXDBString(128, InputMask = "")]
[PXUIField(DisplayName = "Dacname")]
public virtual string Dacname { get; set; }
public abstract class dacname : PX.Data.BQL.BqlString.Field<dacname> { }
#endregion

#region DACFieldName
[PXDBString(128, InputMask = "")]
[PXUIField(DisplayName = "DACField Name")]
//[PXStringList(new string[] { null }, new string[] { "" }, ExclusiveValues = false)]
[PXStringList()]
public virtual string DACFieldName { get; set; }
public abstract class dACFieldName : PX.Data.BQL.BqlString.Field<dACFieldName> { }
#endregion

#region CreatedDateTime
[PXDBCreatedDateTime()]
public virtual DateTime? CreatedDateTime { get; set; }
public abstract class createdDateTime : PX.Data.BQL.BqlDateTime.Field<createdDateTime> { }
#endregion

#region CreatedByID
[PXDBCreatedByID()]
public virtual Guid? CreatedByID { get; set; }
public abstract class createdByID : PX.Data.BQL.BqlGuid.Field<createdByID> { }
#endregion

#region CreatedByScreenID
[PXDBCreatedByScreenID()]
public virtual string CreatedByScreenID { get; set; }
public abstract class createdByScreenID : PX.Data.BQL.BqlString.Field<createdByScreenID> { }
#endregion

#region LastModifiedDateTime
[PXDBLastModifiedDateTime()]
public virtual DateTime? LastModifiedDateTime { get; set; }
public abstract class lastModifiedDateTime : PX.Data.BQL.BqlDateTime.Field<lastModifiedDateTime> { }
#endregion

#region LastModifiedByID
[PXDBLastModifiedByID()]
public virtual Guid? LastModifiedByID { get; set; }
public abstract class lastModifiedByID : PX.Data.BQL.BqlGuid.Field<lastModifiedByID> { }
#endregion

#region LastModifiedByScreenID
[PXDBLastModifiedByScreenID()]
public virtual string LastModifiedByScreenID { get; set; }
public abstract class lastModifiedByScreenID : PX.Data.BQL.BqlString.Field<lastModifiedByScreenID> { }
#endregion

#region Tstamp
[PXDBTimestamp()]
[PXUIField(DisplayName = "Tstamp")]
public virtual byte[] Tstamp { get; set; }
public abstract class tstamp : PX.Data.BQL.BqlByteArray.Field<tstamp> { }
#endregion

#region Noteid
[PXNote()]
public virtual Guid? Noteid { get; set; }
public abstract class noteid : PX.Data.BQL.BqlGuid.Field<noteid> { }
#endregion
}
}

 


harutyungevorgyan
Jr Varsity I
Forum|alt.badge.img+4

@PDharmasena10 ,

The cause of your significant problem is just a simple typo!
You are using Noteid instead of NoteID; change it, and you will have no issues.
 


I already told you, sometimes, the solution can be as simple as that.​​​​​​ 


Forum|alt.badge.img+2
  • Author
  • Semi-Pro I
  • February 27, 2026

Hi ​@harutyungevorgyan 

I fixed the NoteID issue, but it still gives the same error 😑

Below is the modified DAC code:

#region NoteID
[PXNote()]
public virtual Guid? NoteID { get; set; }
public abstract class noteID : PX.Data.BQL.BqlGuid.Field<noteID> { }
#endregion

In the SQL script, the column is already defined as NoteID. The relevant line is:

[NoteID] [uniqueidentifier] NOT NULL

Despite this, the error still occurs.

Also, thank you very much for your continuous support. I truly appreciate it.

 


harutyungevorgyan
Jr Varsity I
Forum|alt.badge.img+4

Hi ​@harutyungevorgyan 

I fixed the NoteID issue, but it still gives the same error 😑

Below is the modified DAC code:

#region NoteID
[PXNote()]
public virtual Guid? NoteID { get; set; }
public abstract class noteID : PX.Data.BQL.BqlGuid.Field<noteID> { }
#endregion

In the SQL script, the column is already defined as NoteID. The relevant line is:

[NoteID] [uniqueidentifier] NOT NULL

Despite this, the error still occurs.

Also, thank you very much for your continuous support. I truly appreciate it.

 

It's quite puzzling that your issue hasn't been resolved. Could you please run a simple SQL SELECT query on your table to check if any existing records have a null value for NoteID? The SQL error you're encountering is attempting to UPDATE an existing record, so the issue may be related to corrupted data where NoteID is already null.