0
votes

I have added a custom field called "UsrSubStatus" to the BAccount DAC. It is defined as follows:

[PXDBString(25)]
[PXDefault]
[PXStringList(
new string[]
{ "A1", "A2", "A3", "I1", "I2", "I3" },
new string[]
{ "On-Plan", "Off-Plan", "Services Only", "ROR - Same Product", "ROR - New Product", "Out of Business" })]
[PXUIField(DisplayName="Sub-Status", Required = true)]

This adds an asterisk to the field on the Business Accounts screen but does not make it required. Since I am going to have to validate the values in selected in the UsrSubStatus field based on the status of the BAccount record I tried to raise an exception if the field is left blank or is null in the BusinessAccountMaint_Extension.

protected void BAccount_RowUpdating(PXCache cache, PXRowUpdatingEventArgs e)
{
  var baccount = e.Row as BAccount;
  BAccountExt baccountExt = PXCache<BAccount>.GetExtension<BAccountExt>(baccount);
  
  // Test for UsrSubStatus not entered
  //if (baccountExt.UsrSubStatus == null || baccountExt.UsrSubStatus == "")
  //{
    cache.RaiseExceptionHandling<BAccountExt.usrSubStatus>(baccountExt, baccountExt.UsrSubStatus,
      new PXSetPropertyException("Sub-Status may not be blank.", PXErrorLevel.Error));
  //}
}

This gives me an error that it is unable to cast object of type 'PX.Objects.CR.BAccountExt' to type 'PX.Objects.CR.BAccount'.

2
In the trace, if the line with the error is the RaiseExceptionHandling one, try changing the first parameter from baccountExt to just baccount. I'm not sure, but I think you need the base object here. i.e. cache.RaiseExceptionHandling<BAccountExt.usrSubStatus>(baccount, baccountExt.UsrSubStatus... - Brian Stevens
Well, I don't get the error but it still allows me to save the record without raising the exception. - John Wiles
I have the same issue on a FieldVerifying event handler. My solution was to follow up with throw new PXException(Message.MyErrorMsg); - Brian Stevens
Also, when looking in the source code for Events on PX.Data, I see an example on a RowUpdating event that says to use e.Cancel = true; after the RaiseExceptionHandling, which based on the example likely is the proper way to do it. setting e.Cancel "cancels the update operation, and shows the warning or error indication near the input control for one field or multiple fields". - Brian Stevens

2 Answers

0
votes

Mostly noted in the comments, but consolidating to an answer and correcting previous answer...

For PXDefault, add PersistingCheck = PXPersistingCheck.NullOrBlank as that will check to ensure the value is not null or a blank string.

[PXDefault(PersistingCheck = PXPersistingCheck.NullOrBlank)]

The RaiseExceptionHandling method should be applied to the baccount object rather than the baccountExt object.

Also, it took me a bit to realize that you are using RowUpdating instead of RowPersisting, and I ran my tests the same way. The proper way to do that is on the RowPersisting event.

For my example, I used BAccount.ownerID since I don't have your DAC extension, but the principles should be the same.

To validate a field that is changed, you would use the FieldVerifying event and RaiseExceptionHandling. If you want this validation to occur before the user presses the save button or updates another field set to commit changes, you would also update CommitChanges = true on the screen definition of the field.

protected void _(Events.FieldVerifying<BAccount.ownerID> e)
{
    var baccount = e.Row as BAccount;

    Base.BAccount.Cache.RaiseExceptionHandling<BAccount.ownerID>(baccount, null,
        new PXSetPropertyException("Failed at Field Updating", PXErrorLevel.Error));
    e.Cancel = true;
}

However, in this case, you are trying to validate something in your data when the user presses save. You want to validate in the RowPersisting event because that is what is triggered by the save button. The RowUpdating event fires anytime there is an update to the row, but this may happen frequently as the user navigates the form or grid. Interestingly enough, per my testing, RaiseExceptionHandling does not seem to work in that event. I was able to successfully test with RowPersisting, and it seems to have the result you wanted.

protected void _(Events.RowPersisting<BAccount> e)
{
    var baccount = e.Row as BAccount;

    e.Cache.RaiseExceptionHandling<BAccount.ownerID>(baccount, null,
        new PXSetPropertyException("Failed at Row Persisting", PXErrorLevel.Error));
    e.Cancel = true;
}

While your DAC should handle ensuring the field is not blank or empty (with Persisting Check as noted above), you sometimes need to revalidate upon save. You would use the RowPersisting example above to perform your tests, present the error with RaiseExceptionHanlding, and terminate the Persisting (save) with e.Cancel = true;

Using the RowPersisting event, I was able to put the indicator on the OwnerID field and stop the save action.

0
votes

I think my original problem was using the RowUpdating instead of the RowUpdated event and when the dynamic row changed it was hiding the previously selected value not deleting it. Here is the DAC:

[PXDBString(25, IsUnicode=true)]
[PXUIField(DisplayName="Sub-Status", Required = true)]
[PXDefault()]
[PXDependsOnFields(typeof(BAccount.status))]
[PXStringList(
new string[]
{ "A1", "A2", "A3", "I1", "I2", "I3" },
new string[]
{ "On-Plan", "Off-Plan", "Services Only", "ROR - Same Product", "ROR - New Product", "Out of Business" })]

I ended up having to do a lot of testing in the BusinessAccountMaint_Extension:

private string[] Values2 = { "A1", "A2", "A3" };
private string[] Labels2 = { "On-Plan", "Off-Plan", "Services Only" };
private string[] Values3 = { "I1", "I2", "I3" };
private string[] Labels3 = { "ROR - Same Product", "ROR - New Product", "Out of Business" };

protected void BAccount_Status_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e, PXFieldUpdated del)
{
  if(del != null)
    del(cache, e);

  var row = (BAccount)e.Row;
  if(row == null) return;

  BAccountExt rowExt = PXCache<BAccount>.GetExtension<BAccountExt>(row);
  if (row.Status != "I")
      PXStringListAttribute.SetList<BAccountExt.usrSubStatus>(cache, row, Values2, Labels2);
  else
      PXStringListAttribute.SetList<BAccountExt.usrSubStatus>(cache, row, Values3, Labels3 );

  if (string.IsNullOrWhiteSpace(rowExt.UsrSubStatus))
  {
    cache.RaiseExceptionHandling<BAccountExt.usrSubStatus>(row, rowExt.UsrSubStatus,
      new PXSetPropertyException("Sub-Status cannot be blank.", PXErrorLevel.Error));
  }
}

protected void BAccount_RowSelected(PXCache cache, PXRowSelectedEventArgs e, PXRowSelected del)
{
  if (del != null)
    del(cache, e);

  var row = (BAccount)e.Row;
  if (row == null) return;

  if (row.Status != "I")
    PXStringListAttribute.SetList<BAccountExt.usrSubStatus>(cache, row, Values2, Labels2);
  else
    PXStringListAttribute.SetList<BAccountExt.usrSubStatus>(cache, row, Values3, Labels3);
}

protected void BAccount_RowUpdated(PXCache cache, PXRowUpdatedEventArgs e, PXRowUpdated del)
{
  if (del != null)
    del(cache, e);

  var row = (BAccount)e.Row;
  if (row == null) return;
  BAccountExt rowExt = PXCache<BAccount>.GetExtension<BAccountExt>(row);

  if (row.Status != "I" && (rowExt.UsrSubStatus == "I1" || rowExt.UsrSubStatus == "I2" || rowExt.UsrSubStatus == "I3")) 
      cache.RaiseExceptionHandling<BAccountExt.usrSubStatus>(row, rowExt.UsrSubStatus,
        new PXSetPropertyException("Sub-Status cannot be blank.", PXErrorLevel.Error));

  if (row.Status == "I" && (rowExt.UsrSubStatus == "A1" || rowExt.UsrSubStatus == "A2" || rowExt.UsrSubStatus == "A3")) 
      cache.RaiseExceptionHandling<BAccountExt.usrSubStatus>(row, rowExt.UsrSubStatus,
        new PXSetPropertyException("Sub-Status cannot be blank.", PXErrorLevel.Error));

  if (string.IsNullOrWhiteSpace(rowExt.UsrSubStatus))
    cache.RaiseExceptionHandling<BAccountExt.usrSubStatus>(row, rowExt.UsrSubStatus,
      new PXSetPropertyException("Sub-Status cannot be blank.", PXErrorLevel.Error));
}

Thanks for everyone's help.