Skip to main content
Answer

Customize UDF so it is conditional on SOLine

  • October 7, 2025
  • 4 replies
  • 70 views

Forum|alt.badge.img

Hello! I am no expert in C# in customization projects so all my knowledge is based on ChatGPT, which has been super helpful so far. 

We have a UDF currently made available on the SOLine on Details tab. It is a string data type and is a drop-down combo box. The goal: this UDF should only be editable when the item on this SO is in a specific item class (CUSTOMNS). If the item is not in this item class, then the field should be locked and not editable.

I have pasted the codes below, open to any suggestions. I am able to publish the project, but with what I have, the field is locked and not editable regardless of the item class. Thanks in advance!

 

The UDF code is:

[PXMergeAttributes(Method = MergeMethod.Merge)]
[PXStringList(
    new string[] { "MCC", "FORMENS", "JTex", "HongKong" },
    new string[] { "MCC", "Formens", "JTex", "HongKong" }
)]
[PXUIField(DisplayName = "Factory ID", Enabled = false)]

I also manually added a SOOrderEntry Graph Extension: 

using System;
using PX.Data;
using PX.Objects.SO;
using PX.Objects.IN;

namespace PX.Objects.SO
{
    public class SOOrderEntry_Extension : PXGraphExtension<SOOrderEntry>
    {
        protected void _(Events.RowSelected<SOLine> e)
        {
            if (e.Row == null) return;

            var line = (SOLine)e.Row;
            bool editable = IsCustomNSItem(e.Cache, line);

            PXUIFieldAttribute.SetReadOnly<SOLineExt.usrFactoryID>(e.Cache, null, false);
            PXUIFieldAttribute.SetEnabled<SOLineExt.usrFactoryID>(e.Cache, line, editable);
        }

        protected void _(Events.FieldUpdated<SOLine.inventoryID> e)
        {
            if (e.Row == null) return;

            var line = (SOLine)e.Row;
            bool editable = IsCustomNSItem(e.Cache, line);

            PXUIFieldAttribute.SetReadOnly<SOLineExt.usrFactoryID>(e.Cache, null, false);
            PXUIFieldAttribute.SetEnabled<SOLineExt.usrFactoryID>(e.Cache, line, editable);

            if (!editable)
                e.Cache.SetValueExt<SOLineExt.usrFactoryID>(line, null);
        }

        protected void _(Events.RowInserted<SOLine> e)
        {
            if (e.Row == null) return;

            var line = (SOLine)e.Row;
            bool editable = IsCustomNSItem(e.Cache, line);

            PXUIFieldAttribute.SetReadOnly<SOLineExt.usrFactoryID>(e.Cache, null, false);
            PXUIFieldAttribute.SetEnabled<SOLineExt.usrFactoryID>(e.Cache, line, editable);
        }

        protected void _(Events.RowUpdated<SOLine> e)
        {
            if (e.Row == null) return;

            var line = (SOLine)e.Row;
            bool editable = IsCustomNSItem(e.Cache, line);

            PXUIFieldAttribute.SetReadOnly<SOLineExt.usrFactoryID>(e.Cache, null, false);
            PXUIFieldAttribute.SetEnabled<SOLineExt.usrFactoryID>(e.Cache, line, editable);

            if (!editable)
                e.Cache.SetValueExt<SOLineExt.usrFactoryID>(line, null);
        }

        private bool IsCustomNSItem(PXCache cache, SOLine line)
        {
            if (line?.InventoryID == null)
                return false;

            InventoryItem item =
                PXSelectorAttribute.Select<SOLine.inventoryID>(cache, line) as InventoryItem;

            if (item?.ItemClassID == null)
                return false;

            INItemClass itemClass = INItemClass.PK.Find(Base, item.ItemClassID);
            return string.Equals(itemClass?.ItemClassCD, "CUSTOMNS", StringComparison.OrdinalIgnoreCase);
        }
    }
}

 

 

Best answer by darylbowman

First off I’d recommend going through the developer series training materials.

This.

ChatGPT is helpful, but it does not understand all the mechanics of the Acumatica framework.

For instance, you should only change the state of controls in a RowSelected event handler. Rip out the rest.

And try something like this:

public class SOOrderEntry_Extension : PXGraphExtension<SOOrderEntry>
{
protected void _(Events.RowSelected<SOLine> e, PXRowSelected b)
{
b?.Invoke(e.Cache, e.Args);

SOLine row = e.Row;
if (row is null) return;

bool editable = IsCustomNSItem(e.Cache, line);
PXTrace.WriteInformation($"Editable: {editable}");
PXUIFieldAttribute.SetEnabled<SOLineExt.usrFactoryID>(e.Cache, line, editable);
}
}

Load a record who’s row SHOULD be enabled and check the trace to make sure ‘editable’ is actually returning the correct value.

I suspect this line is returning ‘null’:

InventoryItem item = PXSelectorAttribute.Select<SOLine.inventoryID>(cache, line) as InventoryItem;

 

4 replies

Forum|alt.badge.img+7
  • Captain II
  • October 8, 2025

First off I’d recommend going through the developer series training materials. They’re super helpful and they’ll help you to understand the why. AI is getting better at this stuff but you’ll still be better than AI.

The other thing is that this probably belongs over in the Development forum as I’m pretty sure you’re beyond “low code” at this point even though it might not feel like it. ;)

The RowSelected and FieldUpdated events should be sufficient. Technically UI changes aren’t supposed to be field FieldUpdated but I’ve used it to good effect in the past.

I’d be tempted to have you remove the Enabled = false property of the DAC field declaration since you know you plan to control that in the events.

I’d also be tempted to have you remove the lines of code calling .SetReadOnly.

Remove the two lines of code starting with if (!isEditable) because it looks like if the field is editable that you’re going to clear the value in that field anytime that the row is updated and your “I am allowed to edit the field” condition is true.

Lastly, I’ll suggest that you move this project into Visual Studio (assuming you haven’t) because it will allow you to debug your code and confirm that everything is running as expected.


S1AW
Freshman II
  • Freshman II
  • October 8, 2025

@Django you are the best, thank you for the intuition, advice and ideas!


Forum|alt.badge.img
  • Author
  • Semi-Pro III
  • October 8, 2025

@Django Thanks for the suggestions! I tried removing the lines you have recommended and no luck there. Will try the developer training materials next :)


darylbowman
Captain II
Forum|alt.badge.img+15
  • Answer
  • October 9, 2025

First off I’d recommend going through the developer series training materials.

This.

ChatGPT is helpful, but it does not understand all the mechanics of the Acumatica framework.

For instance, you should only change the state of controls in a RowSelected event handler. Rip out the rest.

And try something like this:

public class SOOrderEntry_Extension : PXGraphExtension<SOOrderEntry>
{
protected void _(Events.RowSelected<SOLine> e, PXRowSelected b)
{
b?.Invoke(e.Cache, e.Args);

SOLine row = e.Row;
if (row is null) return;

bool editable = IsCustomNSItem(e.Cache, line);
PXTrace.WriteInformation($"Editable: {editable}");
PXUIFieldAttribute.SetEnabled<SOLineExt.usrFactoryID>(e.Cache, line, editable);
}
}

Load a record who’s row SHOULD be enabled and check the trace to make sure ‘editable’ is actually returning the correct value.

I suspect this line is returning ‘null’:

InventoryItem item = PXSelectorAttribute.Select<SOLine.inventoryID>(cache, line) as InventoryItem;