0
votes

I have two custom tables (Header / Detail) which have multiple fields as the key for both. I've created all the PXParent and PXDefault attributes in the detail select from the Header, just as shown in the T200 course.

The problem is that when I have the key fields on the header section and I go to try to create a new record, the form doesn't clear the values I've selected for those key fields.

I've gone on to just creating one auto-incrementing int identity field as the key, with the other former key fields assembled into a unique index instead. I'm using the PXDBIdentity(IsKey = true) attribute in my DAC - and it works well except for one problem: When I create a new record, the field shows -2147483647 until I save, when it then shows the newly created identity value.

So I guess my question is this:

What's the standard protocol / best practice for creating a key field for a custom table when the unique identity of that table consists of 6 fields? If I'm doing it correctly, how do I eliminate that -2147483647 from showing in the ID field?

2

2 Answers

2
votes

There is only one major rule when it comes to defining key fields in a DAC:

  • only a field bound to an identity column can be the only key field in a DAC

  • you can define several key fields in a DAC as long as none of the defined key fields is bound to an identity column

The standard PXInsert action provided by the framework preserves values of all key fields, except the last one. If you want the Insert button to clear values of all key field, it's possible to inherit from the PXInsert<TNode> class and clear Searches array before base logic gets executed:

public class MyGraph : PXGraph<MyGraph>
{
    public class PXInsertCst<TNode> : PXInsert<TNode>
        where TNode : class, IBqlTable, new()
    {
        public PXInsertCst(PXGraph graph, string name)
        : base(graph, name)
        {
        }
        public PXInsertCst(PXGraph graph, Delegate handler)
            : base(graph, handler)
        {
        }

        [PXUIField(DisplayName = ActionsMessages.Insert, 
             MapEnableRights = PXCacheRights.Insert, 
             MapViewRights = PXCacheRights.Insert)]
        [PXInsertButton]
        protected override IEnumerable Handler(PXAdapter adapter)
        {
            adapter.Searches = null;
            return base.Handler(adapter);
        }
    }

    public PXSave<MyPrimaryDAC> Save;
    public PXCancel<MyPrimaryDAC> Cancel;

    // The standard PXInsert type was replaced with the custom PXInsertCst type
    public PXInsertCst<MyPrimaryDAC> Insert;

    public PXDelete<MyPrimaryDAC> Delete;
    public PXCopyPasteAction<MyPrimaryDAC> CopyPaste;
    public PXFirst<MyPrimaryDAC> First;
    public PXPrevious<MyPrimaryDAC> Previous;
    public PXNext<MyPrimaryDAC> Next;
    public PXLast<MyPrimaryDAC> Last;
}

In case it's required to show the only DAC key field bound an identity column in the UI, but users don't want to see a temporary negative value generated for a new record, you should implement FieldSelecting and FieldUpdating event handlers for the DAC's identity column mapped field following the code sample below:

public class MyGraph : PXGraph<MyGraph>
{
    protected void MyDAC_IdentityField_FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
    {
        if (e.Row != null && e.ReturnValue is int?)
        {
            if ((e.ReturnValue as int?).GetValueOrDefault() < 0)
            {
                e.ReturnValue = null;
            }
        }
    }

    protected void MyDAC_IdentityField_FieldUpdating(PXCache sender, PXFieldUpdatingEventArgs e)
    {
        if (e.Row != null && e.NewValue == null && sender.Inserted.Count() == 1)
        {
            var defaultValue = sender.GetValue<MyDAC.identityField>(sender.Inserted.FirstOrDefault_());
            if (defaultValue != null)
            {
                e.NewValue = defaultValue;
            }
        }
    }
}

For a more generic approach on the DAC level, you can implement a custom attribute inherited from PXDBIdentityAttribute and override FieldSelecting and FieldUpdating event handlers following the code sample below:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter | 
     AttributeTargets.Class | AttributeTargets.Method)]
public class PXDBNewIdentityAttribute : PXDBIdentityAttribute
{
    public override void FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
    {
        base.FieldSelecting(sender, e);

        if (e.Row != null && e.ReturnValue is int?)
        {
            if ((e.ReturnValue as int?).GetValueOrDefault() < 0)
            {
                e.ReturnValue = null;
            }
        }
    }

    public override void FieldUpdating(PXCache sender, PXFieldUpdatingEventArgs e)
    {
        if (e.Row != null && e.NewValue == null && sender.Inserted.Count() == 1)
        {
            var defaultValue = sender.GetValue(sender.Inserted.FirstOrDefault_(), FieldOrdinal);
            if (defaultValue != null)
            {
                e.NewValue = defaultValue;
            }
        }

        base.FieldUpdating(sender, e);
    }
}
0
votes

The problem with an identity field is that the number is generated by the Database and not by the ORM which generally holds new Inserted rows in the Cache before the record persists. My advice would be to either drop the identity from the UI altogether or figure out a different key.