Acuminator gives warnings when using bql queries in rowselected/Initialize events, where to use them? - acumatica

I'm using the userLoginType and the userRoles to enable/disable some fields in my Row_Selected events, but using Acuminator https://github.com/Acumatica/Acuminator I get the PX1049 warning : https://github.com/Acumatica/Acuminator/blob/dev/docs/diagnostics/PX1049.md
my code is the following :
public class OpportunityMaintExt : PXGraphExtension<OpportunityMaint>
{
public PXSelectJoin<EPLoginType, InnerJoin<Users, On<Users.loginTypeID, Equal<EPLoginType.loginTypeID>>>,
Where<Users.pKID, Equal<Current<AccessInfo.userID>>>> userLoginType;
public PXSelect<Contact, Where<Contact.userID, Equal<Current<AccessInfo.userID>>>> userContact;
public PXSelect<UsersInRoles, Where<UsersInRoles.username, Equal<Current<AccessInfo.userName>>>> userRoles;
public string userLoginTypeName;
protected virtual void CROpportunity_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
CROpportunity o = e.Row as CROpportunity;
CROpportunityExt myOpp = sender.GetExtension<CROpportunityExt>(sender.Current);
userLoginTypeName = TRLoginInfo.getCurrentUserLoginType(userLoginType.SelectSingle(), userRoles);
enabledisablefields();
}
The problem is with the userLoginType.SelectSingle(), the Acuminator tells me that : "In Rowselected Handler, bql and databasequeries should be avoided". But If I place this in Initialize() it gives the same warning.
So, where am I supposed to put these bql/database queries If I want them to be processed when I watch a record ?
Thanks a lot !

Ideally - you should fire your BQL in the RowSelecting Event within a new PXConnectionScope (BQL within RowSelecting Events MUST be done within a new PXConnectionScope to prevent issues), assign non-db-backed fields within your DAC Extension, then use their values in your RowSelected Event to determine whether or not certain fields should be enabled.

Related

Unable to enable a custom field in Cases

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.

How can I filter the records in the ProcessOrders process screen

I need to add a filter to the Process Orders screen grid, so that only orders that have an unpaid balance = 0 will show, based on an additional checkbox to the filter area called 'Must Have Payment' being checked. I thought I had it by adding a where clause to the Orders view, but that didn't work.
[PXFilterable]
public PXFilteredProcessing<SOOrder, SOOrderFilter,
Where<SOOrder.unpaidBalance, NotEqual<Zero>,
Or<Current<SOOrderFilterExt.mustHavePayment>, Equal<False>>>> Orders;
I'm sure I'm doing this incorrectly, as all orders are showing and not just the 'Open' orders as it was before I added this change. I'd like to override the view delegate and modify that to add my filter / condition to the returned rows, but I can't override this method - at least that I can tell.
What's the best way to get this custom filter restriction into the select for that grid?
Thanks much...
Overriding the dataview delegate may not be the best idea on this specific case.
I noticed that AddCommonFilters() method is not private (used in the dataview delegate), so I think you could try to override this method instead, call Base method and then inject your custom code to include your query filter into the main query used on the for each.
Maybe this is something you can use to implement your filtering, see snippet below:
public class SOCreateShipment_Extension : PXGraphExtension<SOCreateShipment>
{
#region Event Handlers
public delegate void AddCommonFiltersDelegate(SOOrderFilter filter,
PXSelectBase<SOOrder> cmd);
[PXOverride]
public void AddCommonFilters(SOOrderFilter filter, PXSelectBase<SOOrder> cmd, AddCommonFiltersDelegate baseMethod)
{
baseMethod(filter,cmd);
//Add your custom code here
if (Yourcondition)
{
cmd.WhereAnd<Where<YOURFILTERINGCondition>>>>();
}
}
........................
Please also review the AlterFilters() method if needed.

Including default Vendor Inventory ID on Multiple Grids

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;
}

Acumatica Warning Message To Only Affect Shipment Screen SO302000

I'm trying to display a warning every time the ShippedQty field is changed to a value < OrigOrderQty on the "Shipment - SO302000" screen, but I only want the code to be active for that specific screen/form.
I added the code below to extend the SOShipmentEntry graph, which accomplishes my original goal, but the issue is that now the code I added is also being used by the "Create Shipment" action in "Sales Orders - SO301000" screen/form.
Create Shipment Action Discussed
namespace PX.Objects.SO
{
public class SOShipmentEntry_Extension : PXGraphExtension<SOShipmentEntry>
{
#region Event Handlers
protected void SOShipLine_ShippedQty_FieldUpdated(PXCache cache,PXFieldUpdatedEventArgs e)
{
var myrow = (SOShipLine)e.Row;
if (myrow == null) return;
if (myrow.ShippedQty >= myrow.OrigOrderQty)
{
}
else
{
throw new PXSetPropertyException("The difference between the shipped-qty and the ordered-qty will be placed on a back-order", PXErrorLevel.Warning);
}
}
#endregion
}
}
While the warning allows the user to save changes to a shipment on the Shipment Screen/form SO302000 (Because the exception is set up as a warning and not an error), I get the following error when I create a shipment using the "Create Shipment" button on the "Sales Orders - SO301000" screen.
Warning works fine for form-mode
Warning becomes error when processed in background by action button
Any ideas to accomplish this? It is my understanding that if I want to make global changes to a field I must make them in the DAC, but if I want to make changes that only affect screens/forms where a graph is used, then I have to make those changes in the graph code itself. I'm guessing the "Create Shipment" action button in the Sales Orders screen is creating an instance of the graph where I added the code, so I'm wondering what are my options here.
Best regards,
-An Acumatica newbie
If you want your event logic to execute only when CreateShipment is invoked from the Shipment screen you can override the other calls to CreateShipment to dynamically remove your event handler.
The event that invokes CreateShipment action from the SalesOrderEntry graph is Action:
public PXAction<SOOrder> action;
[PXUIField(DisplayName = "Actions", MapEnableRights = PXCacheRights.Select)]
[PXButton]
protected virtual IEnumerable Action(PXAdapter adapter,
[PXInt]
[PXIntList(new int[] { 1, 2, 3, 4, 5 }, new string[] { "Create Shipment", "Apply Assignment Rules", "Create Invoice", "Post Invoice to IN", "Create Purchase Order" })]
int? actionID,
[PXDate]
DateTime? shipDate,
[PXSelector(typeof(INSite.siteCD))]
string siteCD,
[SOOperation.List]
string operation,
[PXString()]
string ActionName
)
In that method it creates a SOShipmentEntry graph to create the shipment. You can override Action and remove your handler from that graph instance:
SOShipmentEntry docgraph = PXGraph.CreateInstance<SOShipmentEntry>();
// >> Remove event handler
SOShipmentEntry_Extension docgraphExt = docgraph.GetExtension<SOShipmentEntry_Extension>();
docgraph.FieldUpdated.RemoveHandler<SOShipLine.shippedQuantity>(docgrapExt.SOShipLine_ShippedQty_FieldUpdated);
// << Remove event handler
docgraph.CreateShipment(order, SiteID, filter.ShipDate, adapter.MassProcess, operation, created);
Note that in order to reference SOShipLine_ShippedQty_FieldUpdated method from another graph you'll have to make it public:
public void SOShipLine_ShippedQty_FieldUpdated(PXCache cache,PXFieldUpdatedEventArgs e)
I have described how to do this in that answer too:
Updating custom field is ending into infinite loop
If you want your event logic to execute only when it is modified in the UI or by web service.
You can use the ExternalCall Boolean property of the PXFieldUpdatedEventArgs parameter.
This property value will be true only when the sender field is modified in the UI or by web service.
Usage example:
protected void SOShipLine_ShippedQty_FieldUpdated(PXCache cache,PXFieldUpdatedEventArgs e)
{
// If ShippedQty was updated in the UI or by a web service call
if (e.ExternalCall)
{
// The logic here won't be executed when CreateShipment is invoked
}
}
ExternalCall Property (PXFieldUpdatedEventArgs)
Gets the value specifying whether the new value of the current DAC field has been changed in the UI or through the Web Service API.

Acumatica - Remove RowSelected Event for Service Order Screen

I would like to override the standard method of the RowSelected event on the Service Orders screen. Specifically, the DocDesc field gets populated when you select a row item for the Labor tab. It will set the TranDesc to the DocDesc and I would like to keep this from happening. I am using Acumatica 6.1 which means that the Service Management Module is not standard in Acumatica during this time. I would like the method that populates this field to not run when the labor line is populated, so the DocDesc field would remain null or blank, this way the user can input their own description.
You should be able to customize the ServiceOrderEntry graph like any other graph :
protected virtual void FSServiceOrder_RowSelected(PXCache sender, PXRowSelectedEventArgs e, PXRowSelected bs)
{
...
}
See https://help.acumatica.com/(W(3))/Main?ScreenId=ShowWiki&pageid=4a05d4c2-cd8b-4131-bf3b-d05861de3ae6
You could override the method if it is virtual, like this :
public delegate void PersistDelegate();
[PXOverride]
public void Persist(PersistDelegate baseMethod)
{
...
baseMethod();
...
}
See https://help.acumatica.com/(W(3))/Main?ScreenId=ShowWiki&pageid=635c830e-4617-4d5c-9fa5-035952311aa9
You could also modify the base customization, but since you are not the owner it could get difficult to maintain and track the changes.

Resources