Skip to main content
Answer

PXDefault Attribute is Updating Existing Records. Why?

  • July 15, 2025
  • 5 replies
  • 61 views

MichaelShirk
Captain II
Forum|alt.badge.img+5

I have a requirement to add an “Item Condition” field to the INItemLotSerial record. 

We need this because we offer Rent-To-Own contracts on our portable buildings. When the customer returns a building rather than completing the contract to ownership, we create a return order to ship it back and receive it back into our inventory as a “Used” building. 

Since all buildings are manufactured by us, all new records should have the “New” condition. I’ve added the below field to a DAC extension of INItemLotSerial, using the PXDefault attribute to set the value to “New” for new lot serial records. The SWItemCondition.List attribute is a custom string list attribute to provide options for “New” and “Used” conditions.

 

Now; this value is only changed to used when a return shipment is completed for this item. 
I update it using the following code. 

 

This “SetItemCondition()” method is called in an override of the “SOShipmentEntry.updateIN()” method, for return shipments only. 

This all works, and if I put a break point right after this in the code, I can query the Database and see that the Item Condition was changed to “Used”. 

However, at some point after this, the PXDefault attribute is triggered and the value is changed back to “New”. I know it’s the PXDefault attribute because I can control the behavior by changing the default value in the attribute. If I remove the default, or change the default value to “Used”, a record with the “New” condition will correctly be changed to “Used”.

I will note that the base UpdateIN method is called BEFORE I call the SetItemCondition method, so it’s not the base method that’s setting it back to the default value. Also, we do have additional custom code after the call to SetItemCondition, but it doesn’t touch the itemLotSerial record.

Is there ever a valid case where an existing value should be overridden by the default from the PXDefault attribute? Or, when creating a return shipment, is it possible that the existing INItemLotSerial record is being deleted and copied to a new record during the return process?

 

Any help is appreciated!

 

Best answer by Keith Richardson

Here is an example that I use, to default the IP address a payment was created from. This uses an EventSubscriberAttribute and then subscribes the field defaulting to a custom function.

 

First is the attribute:

 


public class IPAddressAttribute: PXEventSubscriberAttribute
{
public Type IPField;

public override void CacheAttached(PXCache sender)
{
base.CacheAttached(sender);
sender.Graph.FieldDefaulting.AddHandler(BqlTable, sender.GetField(IPField), OnIPFieldDefaulting);
}
public IPAddressAttribute(Type iPField, Type registerField)
{
IPField = iPField;

}
public void OnIPFieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e)
{
string IPAddress = "";
var context = System.Web.HttpContext.Current;
if (context != null)
{
var Request = context.Request;
if (Request != null)
IPAddress = Request.UserHostAddress;
}

e.NewValue = IPAddress;
PXTrace.WriteInformation("IP recieved as " + IPAddress);
}


}

then on the custom field:

 


#region UsrHWIPAddress
[PXDBString]
[PXUIField(DisplayName = "IP Address")]
[IPAddress(typeof(usrHWIPAddress))]
public virtual string UsrHWIPAddress { get; set; }
public abstract class usrHWIPAddress : PX.Data.BQL.BqlString.Field<usrHWIPAddress> { }
#endregion

 

5 replies

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

Is there ever a valid case where an existing value should be overridden by the default from the PXDefault attribute?

Yes, you can cause this to happen in several ways:

  • [PXFormula(typeof(Default<anotherField>))] (calls ‘defaulting’ when anotherField is updated)
  • e.Cache.SetDefaultExt<thisField>(row)  (manually calls ‘defaulting’ on thisField)

Keith Richardson
Semi-Pro I
Forum|alt.badge.img+2

What version are you running? I had a hunch, and there are some attributes that loop through the code and attempt to set field defaulting on all fields… maybe there is something in the version you are running that is causing it?


Example in 2025r1… in AP PaymentRefAttribute..

 


protected virtual void InsertCheck(PXCache sender, APRegister payment, int CashAccountID, string PaymentMethodID, string CheckNbr)
{
PXCache<CashAccountCheck> cache = sender.Graph.Caches<CashAccountCheck>();
CashAccountCheck check = new CashAccountCheck();

List<PXDataFieldAssign> fields = new List<PXDataFieldAssign>();

Dictionary<string, object> foreign_values = new Dictionary<string, object>
{
{typeof(CashAccountCheck.cashAccountID).Name.ToLower(), CashAccountID},
{typeof(CashAccountCheck.paymentMethodID).Name.ToLower(), PaymentMethodID},
{typeof(CashAccountCheck.checkNbr).Name.ToLower(), CheckNbr},
{typeof(CashAccountCheck.docType).Name.ToLower(), payment.DocType},
{typeof(CashAccountCheck.refNbr).Name.ToLower(), payment.RefNbr},
{typeof(CashAccountCheck.finPeriodID).Name.ToLower(), payment.FinPeriodID},
{typeof(CashAccountCheck.docDate).Name.ToLower(), payment.DocDate},
{typeof(CashAccountCheck.vendorID).Name.ToLower(), payment.VendorID},
{typeof(CashAccountCheck.Tstamp).Name.ToLower(), PXCache.NotSetValue},
};

foreach (string field in cache.Fields)
{
object NewValue;
if (!foreign_values.TryGetValue(field.ToLower(), out NewValue))
{
cache.RaiseFieldDefaulting(field, check, out NewValue);
if (NewValue == null)
{
cache.RaiseRowInserting(check);
NewValue = cache.GetValue(check, field);
}
}
if (NewValue != PXCache.NotSetValue)
{
PXCommandPreparingEventArgs.FieldDescription descr;
cache.RaiseCommandPreparing(field, check, NewValue, PXDBOperation.Insert, typeof(CashAccountCheck), out descr);

if (descr?.Expr != null)
{
fields.Add(new PXDataFieldAssign((Column)descr.Expr, descr.DataType, descr.DataLength, descr.DataValue));
}
}
}
PXDatabase.Insert<CashAccountCheck>(fields.ToArray());
}


Theres the foreach for field and then Raises Field Defaulting. I assume some functionality like that may be hidden somewhere that is being called?


The other thought, is having a fielddefaulting event attribute, rather than the PXDefault, that checks the current value and then only sets if it currently is blank?


MichaelShirk
Captain II
Forum|alt.badge.img+5
  • Author
  • Captain II
  • July 16, 2025


The other thought, is having a fielddefaulting event attribute, rather than the PXDefault, that checks the current value and then only sets if it currently is blank?

 

Yeah I’ll need to go this route. 


The problem I’m running into is, where do I define the event handler for a field of a record that doesn’t have a primary graph associated with it? 


Keith Richardson
Semi-Pro I
Forum|alt.badge.img+2

Here is an example that I use, to default the IP address a payment was created from. This uses an EventSubscriberAttribute and then subscribes the field defaulting to a custom function.

 

First is the attribute:

 


public class IPAddressAttribute: PXEventSubscriberAttribute
{
public Type IPField;

public override void CacheAttached(PXCache sender)
{
base.CacheAttached(sender);
sender.Graph.FieldDefaulting.AddHandler(BqlTable, sender.GetField(IPField), OnIPFieldDefaulting);
}
public IPAddressAttribute(Type iPField, Type registerField)
{
IPField = iPField;

}
public void OnIPFieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e)
{
string IPAddress = "";
var context = System.Web.HttpContext.Current;
if (context != null)
{
var Request = context.Request;
if (Request != null)
IPAddress = Request.UserHostAddress;
}

e.NewValue = IPAddress;
PXTrace.WriteInformation("IP recieved as " + IPAddress);
}


}

then on the custom field:

 


#region UsrHWIPAddress
[PXDBString]
[PXUIField(DisplayName = "IP Address")]
[IPAddress(typeof(usrHWIPAddress))]
public virtual string UsrHWIPAddress { get; set; }
public abstract class usrHWIPAddress : PX.Data.BQL.BqlString.Field<usrHWIPAddress> { }
#endregion

 


MichaelShirk
Captain II
Forum|alt.badge.img+5
  • Author
  • Captain II
  • July 16, 2025

@Keith Richardson Man, you’re a life-saver! 

Here’s the code that ended up working. Just a slight adaptation. 
 

    public class SWItemConditionDefaultAttribute : PXEventSubscriberAttribute
{
public override void CacheAttached(PXCache sender)
{
base.CacheAttached(sender);
sender.Graph.FieldDefaulting.AddHandler(
BqlTable,
_FieldName,
OnDefaultItemCondition
);
}

protected virtual void OnDefaultItemCondition(PXCache sender, PXFieldDefaultingEventArgs e)
{
if (e.Row is INItemLotSerial row)
{
var ext = PXCache<INItemLotSerial>.GetExtension<SWINItemLotSerialExt>(row);
if (ext != null && string.IsNullOrEmpty(ext.UsrSWItemCondition))
{
e.NewValue = SWItemCondition.NewItem;
}
}
}
}

And on the DAC. 
 

        #region UsrSWItemCondition
[PXDBString(2, IsFixed = true)]
[SWItemConditionDefault]
[SWItemCondition.List]
[PXUIField(DisplayName = "Item Condition", Visibility = PXUIVisibility.SelectorVisible)]
public string UsrSWItemCondition { get; set; }
public abstract class usrSWItemCondition : PX.Data.BQL.BqlString.Field<usrSWItemCondition> { }
#endregion

 

I’m still confused about what’s actually happening with the INItemLotSerial record, because before attempting this override of the CacheAttached event, I attempted to implement a custom Field Defaulting event attribute that implemented IPXFieldDefaultingSubscriber. In it, I also added the check to make sure the value was null in the custom field before defaulting it to New. However it was still overriding the “Used” value. I did a bunch of debugging and somehow the custom field value was null in the cache, when the event was called, so it was always updating it to New.

Thanks again for the help. I owe you one!