When I throw an error in the FieldVerifying or FieldUpdating event handler for a grid field that’s a lookup field, it’s showing only the ID of the lookup value in red instead of the description.
The error throw is pretty simple (see below). TField is an IBqlField supplied via generic to my method.
throw new PXSetPropertyException<TField>(Message.WarehouseDoesntMatchShipTo, PXErrorLevel.Error);
Is there a way to get it to show the description in red instead?
Try setting the new value to the warehouse CD string value before throwing the exception. Requires a query to INSite in your example.
If you want the old warehouse CD value (value before user tried to change) just change the value used in the PXSelect to row.SiteID
protected virtual void SOLine_SiteID_FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e)
{
var row = (SOLine)e.Row;
if (row == null)
{
return;
}
INSite inSite = PXSelect<INSite, Where<INSite.siteID, Equal<Required<INSite.siteID>>>>.Select(this, e.NewValue);
e.NewValue = inSite?.SiteCD;
throw new PXSetPropertyException($"Invalid Warehouse {inSite?.SiteCD}", PXErrorLevel.Error);
}
Related
I'm customizing the Sales Order screen as follows:
I've added a custom boolean field added to the Order Types screen called 'Require Customer Order Number'.
I've added code to the BLC of the Sales Order screen where I want to conditionally make the CustomerOrderNumber field required, based on whether the 'Require Customer Order Number' field is checked or not.
I'm using the SOOrder_RowSelected event as follows:
protected virtual void SOOrder_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
var soorder = (SOOrder)e.Row;
if (soorder == null) return;
string ordtype = soorder.OrderType;
var soot = (SOOrderType)PXSelect<SOOrderType,
Where<SOOrderType.orderType, Equal<Required<SOOrderType.orderType>>>>.Select(Base, ordtype);
if (soot != null)
{
var sootext = PXCache<SOOrderType>.GetExtension<SOOrderTypeExt>(soot);
if (sootext != null)
{
PXUIFieldAttribute.SetRequired<SOOrder.customerOrderNbr>(sender, sootext.UsrRequireCustOrdNbr == null ? false : (bool)sootext.UsrRequireCustOrdNbr);
}
}
}
This DOES put an asterisk on the CustomerOrderNumber field - but it doesn't spawn an error upon save if that field is empty.
Another issue is my PXSelect to get the record out of SOOrderType ALWAYS returns a null for the check box user field, even if it has a 'True' value in the database (which is why I put the ternary operator on the call). Even if I hard-code a 'true' value in the PXUIFieldAttribute.SetRequired call, it still doesn't spawn the error to prevent a save. The asterisk is there, but it doesn't work.
If I use a Cache_Attached event to add [PXDefault] it works perfectly - but this doesn't help me - I need it conditionally set.
Any ideas?
Required is used only for displaying the asterisk. PXDefault attribute is the one that makes the field mandatory based on PersistingCheck property value.
The issue is that PXUIFieldAttributes like PersistingCheck can only be set once at the time of graph creation. You can set it dynamically in the constructor/Initialize method but if you change the property after that it has no effects.
When I need a field to be mandatory based on a dynamic condition I remove the PXDefault attribute and validate the field manually in event handlers like RowPersisting:
public void PMTimeActivity_RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
{
PMTimeActivity timeActivity = e.Row as PMTimeActivity;
if (timeActivity != null && PMTimeActivity.timeSpent == null)
{
PXUIFieldAttribute.SetError<PMTimeActivity.timeSpent>(sender, timeActivity, "'Time Spent' cannot be empty."));
}
}
Suppose I have a custom field SOLineExt.UsrCustomField that allows text entry, accessible from SO301000. If I'm creating a PO from PO505000 screen based off this SOLine, I want to automatically create a note on the new POLine and insert the value of SOLineExt.UsrCustomField as the NoteText, but only if SOLineExt.UsrCustomField != null.
The custom field is also accessible as POFixedDemandExt.UsrCustomField (it is populated with fixedDemand IEnumerable override in POCreate), so I can feed it through an override of FillPOLineFromDemand() in POOrderEntry if I need to.
1) do I need to create the new Note in FillPOLineFromDemand() or in POLine_RowInserted(), or somewhere else?
2) what code will create a note and insert the value of UsrCustomField into NoteText? Do I need to create and populate a Note DAC?
You can set a text note using the SetNote static method of PXNoteAttribute class.
There are some unexpected issues when calling that method on a POLine object in the context of FillPOLineFromDemand method. Presumably because the POLine object is not properly initialized in cache at that point.
You can use POLine_RowInserted, I tested this solution:
public void POLine_RowInserted(PXCache sender, PXRowInsertedEventArgs e)
{
POLine row = e.Row as POLine;
POLinkSO.DAC.POLineExt rowExt = row != null ? row.GetExtension<POLinkSO.DAC.POLineExt>() : null;
if (rowExt != null)
{
SOLine line = PXSelectReadonly<SOLine,
Where<SOLine.orderNbr, Equal<Required<SOLine.orderNbr>>,
And<SOLine.orderType, Equal<Required<SOLine.orderType>>,
And<SOLine.lineNbr, Equal<Required<SOLine.lineNbr>>,
And<SOLineExt.usrCustomField, IsNotNull>>>>>.Select(Base,
rowExt.UsrPOLinkSOOrderNbr,
rowExt.UsrPOLinkSOOrderType,
rowExt.UsrPOLinkSOLineNbr);
SOLineExt lineExt = line != null ? line.GetExtension<SOLineExt>() : null;
if (lineExt != null)
{
PXNoteAttribute.SetNote(sender, row, lineExt.UsrCustomField);
}
}
}
I tried but the value is getting updated when I selected any filed in the grid then say save.
protected void TSFormulaByProds_DefaultSiteID_FieldSelecting(PXCache cache, PXFieldSelectingEventArgs e)
{
var row = (TSFormulaByProds)e.Row;
TSFormula tSFormula = PXSelect<TSFormula>.Select(this);
if (tSFormula.DefaultSiteID != null)
{
e.ReturnValue = tSFormula.DefaultSiteID;
}
}
I want to update another grid value before save and after selecting the first field from the grid
If you need to initialize a field FieldDefaulting is the appropriate event.
protected void TSFormulaByProds_DefaultSiteID_FieldDefaulting(PXCache cache, PXFieldDefaultingEventArgs e)
{
e.NewValue = yourValueHere;
}
It will be executed when a new DAC row is inserted.
If you need to re-trigger the Defaulting logic from another event you can do:
object newValue;
Cache[typeof(TSFormulaByProds)].RaiseFieldDefaulting<TSFormulaByProds.DefaultSiteID>(yourTSFormulaByProdsDACRow, out newValue);
We have a test DAC called UsrNonRelatedScanField with two fields : OrderNbr and ScanStatus.
Here's our simple query to grab the correct ordernbr and assign it to a SOOrderExt field:
NonRelatedScanField lastScan = PXSelect<NonRelatedScanField,
Where<NonRelatedScanField.orderNbr,
Equal<Required<SOOrder.orderNbr>>>>.Select(Base, row.OrderNbr);
if(lastScan != null)
{
rowExt.UsrNonRelatedScanField = lastScan.ScanStatus;
}
This logic is held in a SOOrder_RowSelecting() method.
Full Method implementation:
protected virtual void SOOrder_RowSelecting(PXCache sender, PXRowSelectingEventArgs e)
{
SOOrder row = (SOOrder)e.Row;
if (row == null) return;
SOOrderExt rowExt = PXCache<SOOrder>.GetExtension<SOOrderExt>(row);
NonRelatedScanField lastScan = PXSelect<NonRelatedScanField,
Where<NonRelatedScanField.orderNbr,
Equal<Required<SOOrder.orderNbr>>>>.Select(Base, row.OrderNbr);
if (lastScan != null)
{
rowExt.UsrNonRelatedScanField = lastScan.ScanStatus;
}
}
Expected Results : Get the current Orders scan status from lastScan DAC
Actual Results: Will populate correctly only on the initial order opened. When selecting other orders the old value is persisting unless I manually refresh the page. When manually refreshed the correct data comes in.
I haven't had any issues in the past with BQL queries, this specific query is not behaving as expected.
Thank you
I have set my invoice to Manual Numbering
And I want to assign the invoice number(RefNbr) - if blank, before saving the invoice (AR301000). I've overridden the RowPersisting event as follow:
public class ARInvoiceEntry_Extension:PXGraphExtension<ARInvoiceEntry>
{
protected void ARInvoice_RowPersisting(PXCache cache, PXRowPersistingEventArgs e, PXRowPersisting InvokeBaseHandler)
{
var row = (ARInvoice)e.Row;
if (row != null)
{
//BB-<timestamp> as inv# for testing only
if (string.IsNullOrEmpty(row.RefNbr))
row.RefNbr = "BB-" + DateTime.Now.ToString("hhmmsstt");
}
if(InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
}
}
If I raised a new invoice w/out any lines and saved it. It correctly assigned the Reference Nbr as expected.
Problem is if I raised a new invoice WITH lines. Its complaining that the RefNbr is blank.
What am I missing ? How do I fix this ? Thanks.
Acumatica's PXDBDefaultAttribute only updates field of the dependent records when both the source field of the parent record and the foreign key field of the dependent record are not empty.
When a dependent record is inserted into the cache, PXDBDefaultAttribute will copy the source field value from the parent record to the field it decorates from the dependent record. While the framework is saving new parent record into the database, it first raises RowPersisting event for the parent record. PXDBDefaultAttribute subscribes to the RowPersisting event of the parent record type to save the original source field value:
to locate the parent record after it was saved to the database
or to use it as a restore point for the dependent field in case of an aborted transaction
Also, PXDBDefaultAttribute subscribes to the RowPersisting event of the dependent record type to update the foreign key field with the actual source field value, that has just been recorded in the database. To update the foreign key field PXDBDefaultAttribute must first locate the parent record using the original source field value obtained earlier in the RowPersisting event handler of the parent record type. In case the foreign key field value appears to be empty, there is no chance for PXDBDefaultAttribute to locate the parent record for it and it simply leaves the dependent field empty. This is what eventually is causing the error "RefNbr cannot be empty".
With all that being said, I believe it won't be possible to achieve the desired results if leaving AR Invoice Reference Number empty until it gets saved to the database. As an alternative, let me suggest to default AR Invoice Reference Number to some constant, like < ENTER >, and at the same time replace it with the actual number within the ARInvoice_RowPersisting handler:
using PX.Data;
using System;
namespace PX.Objects.AR
{
public class ARInvoiceNumberingCstAttribute : ARInvoiceType.NumberingAttribute
{
public const string EnterRefNbr = "<ENTER>";
protected override string GetNewNumber()
{
string newNumber = base.GetNewNumber();
if (string.IsNullOrEmpty(newNumber))
{
newNumber = EnterRefNbr;
}
return newNumber;
}
public override void RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
{
if (GetNewNumber() == EnterRefNbr) return;
base.RowPersisting(sender, e);
}
}
public class ARInvoiceEntry_Extension : PXGraphExtension<ARInvoiceEntry>
{
[PXRemoveBaseAttribute(typeof(ARInvoiceType.NumberingAttribute))]
[PXMergeAttributes(Method = MergeMethod.Append)]
[ARInvoiceNumberingCst]
protected void ARInvoice_RefNbr_CacheAttached(PXCache sender)
{ }
protected void ARInvoice_RowPersisting(PXCache cache, PXRowPersistingEventArgs e,
PXRowPersisting InvokeBaseHandler)
{
if (InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = (ARInvoice)e.Row;
if (row != null)
{
//BB-<timestamp> as inv# for testing only
if (row.RefNbr == ARInvoiceNumberingCstAttribute.EnterRefNbr)
row.RefNbr = "BB-" + DateTime.Now.ToString("hhmmsstt");
}
}
}
}