I am in the process of upgrading from Acumatica 2018 R2 to 2019 R1. In quite a few of my customizations, I have code, triggered by a button click or an event handler, which sets/changes the value of a custom field. That was working as expected in 2018 R2. However, in 2019 R1 the values on the custom fields are not being updated. Here's a simple example.
public class SOOrderEntry_SOOpenPOsGILink_Extension : PXGraphExtension<SOOrderEntry>
{
protected virtual void SOLine_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
SOLine row = (SOLine)e.Row;
if (row != null)
{
bool isEmpty = true;
SOLineExt ext = row.GetExtension<SOLineExt>();
// logic determining value of isEmpty
ext.UsrEnableOpenPOs = isEmpty;
}
}
}
Where UsrEnableOpenPOs is defined as folows.
public class SOLineExt : PXCacheExtension<PX.Objects.SO.SOLine>
{
#region UsrEnableOpenPOs
[PXBool]
[PXUIField(DisplayName="EnableOpenPOs", Enabled = false, Visible=false)]
[PXUnboundDefault(false, PersistingCheck = PXPersistingCheck.Nothing)]
public virtual bool? UsrEnableOpenPOs { get; set; }
public abstract class usrEnableOpenPOs : PX.Data.BQL.BqlBool.Field<usrEnableOpenPOs> { }
#endregion
}
In this example, when a SOLine is selected on the SO Order Entry screen, the value of SOLineExt.UsrEnableOpenPOs should be set to the value of isEmpty. This code works in 2018 R2 and correctly updates UsrEnableOpenPOs. However, in 2019 R1, the code is triggered correctly and runs, but the value on the screen is not updated.
As I mentioned, we have quite a few instances where we are running into this problem. In some cases the code is triggered by a button click and in other cases by different events like RowSelected, RowInserting, FieldUpdated and RowUpdated.
I would appreciate some insight on why this code is no longer working and what I can do to fix it.
Should always try to set the value in cache vs the row instance.
ex:
sender.SetValueExt<SOLineExt.usrEnableOpenPOs>(row, isEmptry);
Unbound fields should be set in the RowSelecting or FieldSelecting events. The RowSelected event should be for UI changes such as disabled, visible, etc. fields.
Related
I am trying to enable the custom field in Case when the Status is in Closed State. I am working on a customization for Acumatica version 20.114.0020 (2020 R1).
I have created a custom field usrIsNotBillable in CRCase DAC.
[PXDBBool]
[PXUIField(DisplayName="Confirmed Not Billable", Enabled = true)]
public virtual bool? UsrIsNotBillable { get; set; }
public abstract class usrIsNotBillable : PX.Data.BQL.BqlBool.Field<usrIsNotBillable> { }
It is totally working fine when the Case is in other states than Closed. But when the case is closed, every other property gets disabled. But I want this field to be set enabled. So, I override the Row Selected method for CRCaseMaint graph as below:
protected void CRCase_RowSelected(PXCache cache, PXRowSelectedEventArgs e, PXRowSelected InvokeBaseHandler)
{
InvokeBaseHandler?.Invoke(cache, e);
CRCase row = (CRCase) e.Row;
if (row == null) return;
Base.CaseCurrent.Cache.AllowUpdate = true;
Base.CaseCurrent.AllowUpdate = true;
PXUIFieldAttribute.SetEnabled<CRCaseExt.usrIsNotBillable>(cache, row, true);
}
If I use other DAC fields such as IsBillable like this:
PXUIFieldAttribute.SetEnabled<CRCase.isBillable>(cache, row, true);
It just works fine.
I checked other examples too and the implementation is similar to this. I am just not sure why it is not working in this case.
I have also checked if this screen has any existing workflows and it doesn't.
Any help would be appreciated.
Thanks.
Besides writing code to enable the field in RowSelected event, it is also important to add the field in Closed state in the Workflow.
However, if this is also not working deleting contents of CstDesigner of project does the job.
I've been tasked with adding the default vendor inventory ID to grids in multiple screens in Acumatica. The field does not need to be bound and is disabled. I've gotten it as far as showing the field correctly, but only on saved records. Adding a new line and even refreshing the grid will not display the ID, I have to close the screen or switch to another record and come back before the vendor ID will display, even clicking the save button and refreshing will not cause it show. The client is using this field as a reference point so it's important it shows as soon as they select an item.
Below is the code I have for the Kit Specification screen, I need to figure out a way to make it a little more reactive, at least show properly on a refresh. I have tried using Current<> in the where statement, but that just breaks it entirely and always shows nothing.
public class INKitSpecStkDetExt : PXCacheExtension<PX.Objects.IN.INKitSpecStkDet>
{
#region VendorInventoryCode
public abstract class vendorInventoryCode: IBqlField { }
[PXDBScalar(typeof(Search2<PO.POVendorInventory.vendorInventoryID,
InnerJoin<IN.InventoryItem,
On<PO.POVendorInventory.vendorID, Equal<IN.InventoryItem.preferredVendorID>, And<PO.POVendorInventory.inventoryID, Equal<IN.InventoryItem.inventoryID>>>>,
Where<IN.InventoryItem.inventoryID,Equal<IN.INKitSpecStkDet.compInventoryID>>,
OrderBy<Desc<PO.POVendorInventory.vendorInventoryID>>>))]
[PXString(40, IsUnicode = true)]
[PXUIField(DisplayName = "Vendor Inventory Code", Enabled=false)]
public string VendorInventoryCode{ get; set; }
#endregion
}
Once I have the code nailed down it will be used in several other places. Help very much appreciated! Frustrating to have it so close and not be able to cross the finish line...
Follow up based on feedback from HB_Acumatica, working code is below for reference:
public void INKitSpecStkDet_VendorInventoryCode_FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
{
var row = e.Row as INKitSpecStkDet;
if (e.Row != null)
{
PO.POVendorInventory vendorInventory = PXSelectReadonly2<PO.POVendorInventory,
InnerJoin<IN.InventoryItem,
On<PO.POVendorInventory.vendorID, Equal<IN.InventoryItem.preferredVendorID>, And<PO.POVendorInventory.inventoryID, Equal<IN.InventoryItem.inventoryID>>>>,
Where<IN.InventoryItem.inventoryID, Equal<Required<IN.INKitSpecStkDet.compInventoryID>>>,
OrderBy<Desc<PO.POVendorInventory.vendorInventoryID>>>.Select(Base, row.CompInventoryID);
e.ReturnValue = vendorInventory != null ? vendorInventory.VendorInventoryID : null;
}
}
PXDBScalar attribute doesn't refresh by itself. Maybe explicitly calling RaiseFieldDefaulting method will refresh it:
object newValue = null;
base.Caches[typeof(INKitSpecStkDet)].RaiseFieldDefaulting<INKitSpecStkDetExt.vendorInventoryCode>(rowINKitSpecStkDet, out newValue);
Using PXFormula instead of PXDBScalar if possible will yield better automatic refresh behavior but has it's own set of limitation as well.
If you're looking for the simplest way that works in most contexts (except when no graph is used like in reports and generic inquiry) that would be the FieldSelecting event.
You can execute BQL and return any desired value from the event. It will be called each time the field is referenced so it should update by itself.
public void INKitSpecStkDet_VendorInventoryCode_FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
{
PO.POVendorInventory vendorInventory = PXSelectReadonly2<Po.POVendorInventory […]>.Select(Base);
e.ReturnValue = vendorInventory != null ? vendorInventory.VendorInventoryCode : null;
}
I have a customization to the Release Time Activities screen (EP507020) where I add a user field. This user field will contain the result of fetching the Appointment status from the Appointments screen (FS300200) based on the Appointment ID that I've also added to the Release Time Activities screen grid.
This was done so that the process grid could be filtered for appointment status that were a certain value. The Appointment Status User field I've added contains the same attributes that the Status field contains on the Appointments screen, with the Cache extension looking as follows:
#region UsrApptStatus
public abstract class usrApptStatus : IBqlField
{
}
[PXDBString(1)]
[FSAppointment.status.ListAtrribute]
[PXUIField(DisplayName = "Appt Status",Enabled = false)]
public virtual string UsrApptStatus { get; set; }
#endregion
This works fine when I fetch the status as follows in a Graph extension:
protected virtual void EPActivityApprove_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
var epactivityapprove = (EPActivityApprove)e.Row;
if (epactivityapprove != null)
{
FSxPMTimeActivity rowExt = epactivityapprove.GetExtension<FSxPMTimeActivity>();
if (rowExt != null)
{
var appointmentID = rowExt.AppointmentID;
var fsappt = (FSAppointment)PXSelect<FSAppointment,
Where<FSAppointment.appointmentID, Equal<Required<FSAppointment.appointmentID>>>>.Select(Base, appointmentID);
var epactivityapproveext = PXCache<EPActivityApprove>.GetExtension<EPActivityApproveExt>(epactivityapprove);
epactivityapproveext.UsrApptStatus = fsappt.Status;
}
}
}
The problem is that when I go to filter the Status column, no matter what I choose from the list of options, it clears all rows. I have no idea why this wouldn't work, but I'm sure I'm missing something.
Grid before filtering:
Filter:
Grid after filter:
It seems to me that changing the value in the RowUpdated event is interfering with the filter. If you wrote both extensions, it would help to have both custom fields in the same extension and use the PXFormula attribute to set the value of your status based on the AppointmentID field. This way you wouldn't have to rely on the event:
#region UsrApptStatus
public abstract class usrApptStatus : IBqlField
{
}
[PXDBString(1)]
[FSAppointment.status.ListAtrribute]
[PXFormula(typeof(Selector<appointmentID, FSAppointment.status>))]
[PXUIField(DisplayName = "Appt Status",Enabled = false)]
public virtual string UsrApptStatus { get; set; }
#endregion
I created a ListView to show the list of documents, then created a button "Button A" to do some actions, my requirement is I would like the button status may be changed with the selected document changes.
Fox example: there are three documents in the following graphic, I want the button is enabled when I click Order-00001 or Order-00002, and it is disabled for Order-00003 due to no money in it.
I appreciate if you could give me a hint if there is any event to be raised when I click a row. Thanks a lot.
To reduce callback to the server there isn't a row selected event. Instead there is PXToolbarButton StateColumn property to control the button enabled state.
When you declare your button, you specify a Boolean DAC field that will enable/disable the button based on it's value. Note that the button needs the DependOnGrid property set to the ID of the grid to get the selected row:
<px:PXToolBarButton Text="Button A" DependOnGrid="grid" StateColumn="IsButtonVisible">
IsButtonVisible is a custom unbound Boolean DAC field (you may choose any name you want except isSelected/Selected which is reserved for checkbox):
#region IsButtonVisible
public abstract class isButtonVisible : IBqlField
{
}
protected bool? _IsButtonVisible;
[PXBool]
[PXUIField(DisplayName = "Is Button Visible", Enabled = false, Visible = false)]
public virtual bool? IsButtonVisible
{
get
{
return _IsButtonVisible;
}
set
{
_IsButtonVisible = value;
}
}
#endregion
You can set the value of IsButtonVisible in the RowSelected event based on your business logic:
protected virtual void DAC_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
DAC row = e.Row as DAC;
if (row != null)
{
bool yourCondition = ???;
row.IsButtonVisible = yourCondition;
}
}
Source:
Enable disable button of grid or PXToolBarButton, which depends from value of column in Acumatica
I want to hide or update a field on the UI based on conditions of another field.
For example, if I have a field called Color:
[PXUIField(DisplayName="Color")]
[PXStringList("Red,Blue,Other")]
[PXDefault("Red")]
And text field for comments only shown when "Other" is selected, how is this accomplished?
The requested behavior can either be accomplished either with a series of event handlers or with a bunch of attributes. You can find several examples on how to subscribe to the RowSelected and FieldUpdated events in the T200 training course, available at Acumatica University and Acumatica Open University
Going with field attributes is a more convenient and way easier option for your particular scenario. I would recommend setting CommitChanges to True for the drop-down, so the Comments field is cleared and disabled/enabled immediately after the user updates Color. Also, it's very to have your Color declared after Comments, so the framework will process Comments field first and always clear the current Comments value after the Color field got updated.
public class Other : Constant<string>
{
public Other() : base("Other") { }
}
public abstract class comments : IBqlField { }
[PXDBString(255, IsUnicode = true)]
[PXUIField(DisplayName = "Comments")]
[PXUIEnabled(typeof(Where<color, Equal<Other>>))]
[PXFormula(typeof(Default<color>))]
[PXDefault(PersistingCheck = PXPersistingCheck.Nothing)]
public string Comments { get; set; }
public abstract class color : IBqlField { }
[PXDBString(10, IsUnicode = true)]
[PXUIField(DisplayName = "Color")]
[PXStringList("Red,Blue,Other")]
[PXDefault("Red")]
public string Color { get; set; }
The only way to conditionally hide/show editor on a form is though the RowSelected event handler:
public void YourDAC_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
YourDAC row = e.Row as YourDAC;
if (row == null) return;
PXUIFieldAttribute.SetVisible<YourDAC.comments>(sender, row, row.Color == "Other");
}
I believe, in the T200 training course, there are several examples on the PXUIFieldAttribute.SetVisible method.