Action upon selecting a line in a grid - acumatica

I'd like to enable or disable a button upon selecting a line in a grid, here's what I tried for now :
public virtual void ARRegister_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
ARRegister row = e.Row as ARRegister;
if (row == null) return;
UnProcessLettering.SetEnabled(row.GetExtension<ARRegisterLeExt>().LettrageCD != null);
}
And I've set the syncposition as true in my grid. But nothing changes when I select a row in which LettrageCD is not null or is null.
Edit : it seems my question is a duplicate : Is there any event triggered when highlighting a row? (didnt find it during my first search :( )

Instead of calling SetEnabled on the PXAction, use the StateColumn property on your button aspx declaration.
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:
#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

Related

How to save a record with two fields disabled on the PM301000 screen

Good afternoon everyone,
I need your help, I want to record one or several records on the PM301000 Projects screen in the detail tab, Cost Budget.
When recording one or more records, two Mark for PO and Vendor ID fields must be disabled. When the condition of the Mark for PO field is equal to true.
I have used the RowPersisting event and it disables it but when I modify or leave the registry the fields are enabled again.
Please help me or tell me how I should do it, my code is as follows.
Thanks in advance.
namespace PX.Objects.PM
{
public class PMBudgetExt : PXCacheExtension<PX.Objects.PM.PMBudget>
{
#region UsrVendorID
[PXDBInt]
[PXUIField(DisplayName = "Vendor ID", Visibility = PXUIVisibility.Visible)]
[PXDimensionSelectorAttribute("VENDOR", typeof(Search<VendorR.bAccountID, Where<VendorR.type, Equal<BAccountType.vendorType>,
And<VendorR.status, Equal<BAccount.status.active>>>>),
typeof(VendorR.acctCD), new Type[] { typeof(VendorR.acctCD), typeof(VendorR.acctName) })]
public virtual int? UsrVendorID { get; set; }
public abstract class usrVendorID : PX.Data.BQL.BqlInt.Field<usrVendorID> { }
#endregion
#region UsrMarkforPO
[PXDBBool()]
[PXDefault(false)]
[PXUIField(DisplayName = "Mark for PO")]
public virtual bool? UsrMarkforPO { get; set; }
public abstract class usrMarkforPO : PX.Data.BQL.BqlBool.Field<usrMarkforPO> { }
#endregion
}
}
namespace PX.Objects.PM
{
public class ProjectEntry_Extension : PXGraphExtension<ProjectEntry>
{
#region Event Handlers
protected void PMCostBudget_RowPersisting(PXCache cache, PXRowPersistingEventArgs e)
{
PMCostBudget newRow = (PMCostBudget)e.Row;
if (newRow == null) return;
PMBudgetExt newRowE = PXCache<PMBudget>.GetExtension<PMBudgetExt>(newRow);
if (Base.CostBudget.Cache.AllowUpdate == true)
{
if (newRowE.UsrMarkforPO == true)
{
PXUIFieldAttribute.SetEnabled<PMBudgetExt.usrMarkforPO>(cache, newRow, false);
PXUIFieldAttribute.SetEnabled<PMBudgetExt.usrVendorID>(cache, newRow, false);
}
}
}
#endregion
}
}
RowPersisting event executes only on save event. Therefore it's not suited for setting the field states. You will get better results with RowSelected event which is executed everytime a record is selected to be displayed on screen. You should set the state on every callback whether it is enabled or disabled. Also, the event should be declared on the same DAC type you are using to set the field state so the cache object match.
public void PMBudget_RowSelected(PXCache sender, PXRowSelectedEventArgs e, PXRowSelected del)
{
if (del != null)
{
del(sender, e);
}
bool isFieldEnabled = [your_condition];
PXUIFieldAttribute.SetEnabled<PMBudget.field>(sender, e.Row, isFieldEnabled);
}

Detecting when an Invoice exceeds the PO amount

I have created a new APTran field called UsrPOTranAmt. I would like to populate it with the PO Line amount when the user either adds a PO or a PO Line to the invoice. Later I can compare the line transaction amount to the UsrPOTranAmt and determine if the user is paying more that the PO amount.
My initial thought was to detect when the PONbr field (PO Type, PO Number, and PO Line Number fields) was updated and then set the UsrPOTranAmt field to the same value as the CuryLineAmt or Amount field that was updated by the PO or PO Line selection. I have tried to detect the field_updated event with a warning message for PONbr and POLineNbr but neither approach has worked.
public class APInvoiceEntry_Extension : PXGraphExtension<APInvoiceEntry>
{
protected void APTran_PONbr_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
{
var row = (APTran)e.Row;
if (row.PONbr != null)
{
cache.RaiseExceptionHanding<APTran.PONbr>(row, row.PONbr,
new PXSetPropertyException("PO Line Number Changed", PXErrorLevel.Warning));
}
}
}
I do not receive the warning message so I don't expect that I can set the UsrPOTranAmt either with this event.
If you have not changed the default behavior of the Grid column of CommitChanges to True, it will not cause the postback and call the event. Give that a try.
The commitChanges is definitely required on a filed updated event, but here is the solution from ACM:
public class APTranExt : PXCacheExtension<PX.Objects.AP.APTran>
{
#region UsrPOTranAmt
[PXDecimal]
[PXUIField(DisplayName = "POTranAmt", Enabled = false)]
[PXDBScalar(typeof(Search<POLine.curyLineAmt,
Where<POLine.orderType, Equal<APTran.pOOrderType>,
And<POLine.orderNbr, Equal<APTran.pONbr>,
And<POLine.lineNbr, Equal<APTran.pOLineNbr>>>>>))]
public virtual Decimal? UsrPOTranAmt { get; set; }
public abstract class usrPOTranAmt : PX.Data.BQL.BqlDecimal.Field<usrPOTranAmt> { }
#endregion
}
public class APInvoiceEntry_Extension : PXGraphExtension<APInvoiceEntry>
{
#region Event Handlers
protected void APTran_RowUpdated(PXCache cache, PXRowUpdatedEventArgs e, PXRowUpdated InvokeBaseHandler)
{
if (InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = (APTran)e.Row;
if(row != null && row.POOrderType != null && row.PONbr != null && row.POLineNbr != null)
{
POLine poline = PXSelect<POLine,
Where<POLine.orderType, Equal<Required<POLine.orderType>>,
And<POLine.orderNbr, Equal<Required<POLine.orderNbr>>,
And<POLine.lineNbr, Equal<Required<POLine.lineNbr>>>>>>.Select(Base, row.POOrderType, row.PONbr, row.POLineNbr);
if(poline != null)
{
cache.SetValue(row, "UsrPOTranAmt", poline.CuryLineAmt);
}
}
}
#endregion
}

Populating unbound field from another bound custom field

I have a requirement to have a field on SalesOrder screen and the same field should appear on Shipment screen also for respective SalesOrder. And the user should be able to update these field on both the screen.
I created a bound field on Sales Order screen which user can save it. Then I created an unbound field on Shipment screen to show the text from Sales Order. For that I have written a SOShipment_RowSelected event and later for user to update it to the database, I have written SOShipment_RowUpdated. However, when I try to edit the field, it fires RowSelected event and it overwrites the editing and bring back in the same original value.
I have tried with SOShipment_ShipmentNbr_FieldUpdated & SOShipment_ShipmentNbr_FieldUpdating event but its not firing everytime.
Here is the code for Cache extension-
public class SOOrderExtension : PXCacheExtension<SOOrder>
{
#region UsrNotesText
[PXDBString(255)]
[PXUIField(DisplayName = "Pick List Notes")]
public virtual string UsrNotesText { get; set; }
public abstract class usrNotesText : IBqlField { }
#endregion
}
public class SOShipmentExtension : PXCacheExtension<SOShipment>
{
#region UsrNotesText
[PXString(255)]
[PXUIField(DisplayName = "Pick List Notes")]
public virtual string UsrNotesText { get; set; }
public abstract class usrNotesText : IBqlField { }
#endregion
}
SOShipmentExtension code-
public class SOShipmentEntryExtension : PXGraphExtension<SOShipmentEntry>
{
PXSelect<SOOrder> soOrder;
protected virtual void SOShipment_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
if (e.Row != null)
{
SOOrder order = PXSelectJoin<SOOrder,
LeftJoin<SOOrderShipment, On<SOOrder.orderNbr, Equal<SOOrderShipment.orderNbr>,
And<SOOrder.orderType, Equal<SOOrderShipment.orderType>>>,
LeftJoin<SOShipment, On<SOOrderShipment.shipmentNbr, Equal<SOShipment.shipmentNbr>>>>,
Where<SOShipment.shipmentNbr, Equal<Current<SOShipment.shipmentNbr>>>>.Select(Base);
if (order != null)
{
SOOrderExtension orderExt = PXCache<SOOrder>.GetExtension<SOOrderExtension>(order);
SOShipment soShipment = Base.Document.Current;
SOShipmentExtension ext = PXCache<SOShipment>.GetExtension<SOShipmentExtension>(soShipment);
ext.UsrNotesText = orderExt.UsrNotesText;
}
}
}
protected virtual void SOShipment_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)
{
SOShipment oldRow = (SOShipment)e.OldRow;
SOShipment newRow = (SOShipment)e.Row;
if (oldRow != null || newRow != null)
{
SOShipmentExtension oldExt = PXCache<SOShipment>.GetExtension<SOShipmentExtension>(oldRow);
SOShipmentExtension newExt = PXCache<SOShipment>.GetExtension<SOShipmentExtension>(newRow);
if (oldExt.UsrNotesText != newExt.UsrNotesText)
{
{
SOOrder order = PXSelectJoin<SOOrder,
LeftJoin<SOOrderShipment, On<SOOrder.orderNbr, Equal<SOOrderShipment.orderNbr>,
And<SOOrder.orderType, Equal<SOOrderShipment.orderType>>>,
LeftJoin<SOShipment, On<SOOrderShipment.shipmentNbr, Equal<SOShipment.shipmentNbr>>>>,
Where<SOShipment.shipmentNbr, Equal<Current<SOShipment.shipmentNbr>>>>.Select(Base);
soOrder.Current = order;
if (order != null)
{
SOOrderExtension orderExt = PXCache<SOOrder>.GetExtension<SOOrderExtension>(order);
orderExt.UsrNotesText = newExt.UsrNotesText;
soOrder.Update(order);
}
}
}
}
}
}
Any suggestions?
The trick is to initialize UsrNotesText elsewhere.
You can use PXDefault attribute:
[PXDefault(typeof(Search<SOOrderExtension.usrNotesText, Where< [...] >>))]
Or FieldDefaulting event handler:
public void SOShipment_UsrNotesText_FieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e)
{
e.NewValue = [...]
}
Sometimes you also want to re-initialize when user changes key fields that are not re-triggering the default.:
public void SOShipment_ShipmentNbr_FieldUpdated(PXCache sender, PXFieldDefaultingEventArgs e)
{
SOShipment shipment = e.Row as SOShipment;
if (shipment != null)
{
SOShipmentExtension shipmentExt = PXCache<SOShipment>.GetExtension<SOShipmentExtension>(shipment);
if (shipmentExt != null)
{
shipmentExt.UsrNotesText = [...];
}
}
}
In such case manually re-triggering FieldDefaulting event with RaiseFieldDefaulting is often a better option.
However method you choose to initialize avoid setting the field value in RowSelected because that event is called at times when you don't want to initialize the custom field.

How can I show QtyOnHand field(in IN402000) to the PO detail grid in PO301000

How can I show QtyOnHand in IN402000 to the PO 301000 detail grid?
The POLine DAC already has a "QtyAvail (Qty on Hand)" field. You would simply need to create a customization project, add the screen, navigate to the Grid: Transactions and add the Control for the field to the screen & publish.
If you need to add the field to PO Line and the Quantity Available from INItemStatus would suffice you could add it with:
using System;
using PX.Data;
using PX.Objects.PO;
using PX.Objects.IN;
public class POLine_Extension : PXCacheExtension<POLine> {
[PXDecimal]
[PXUIField(DisplayName = "Qty Avail", Enabled = false)]
[PXDBScalar(typeof(
Search<INSiteStatus.qtyAvail,
Where<INSiteStatus.inventoryID, Equal<POLine.inventoryID>,
And<INSiteStatus.siteID, Equal<POLine.siteID>>>>))]
public virtual decimal? QtyAvail { get; set; }
public abstract class qtyAvail : IBqlField {}
}
Then the PXDBScalar fields have their values set when the DAC is Selected. There's probably a better way this could be done, but to populate the field as lines are added & updated to the PO within a POOrderEntry graph extension, you could handle the FieldUpdated events for the InventoryID and SiteID to execute a function that retrieves the Qty Available using basically the same BQL and sets that value to the DAC extension field.
public class POOrderEntry_Extension : PXGraphExtension<POOrderEntry>
{
public virtual void POLine_InventoryID_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
{
UpdateQtyAvailable(sender, e);
}
public virtual void POLine_SiteID_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
{
UpdateQtyAvailable(sender, e);
}
private void UpdateQtyAvailable(PXCache sender, PXFieldUpdatedEventArgs e)
{
if (e.Row == null) return;
POLine row = (POLine)e.Row;
foreach(INSiteStatus siteStatus in PXSelect<INSiteStatus,
Where<INSiteStatus.inventoryID, Equal<Required<INSiteStatus.inventoryID>>,
And<INSiteStatus.siteID, Equal<Required<INSiteStatus.siteID>>>>>.Select(sender.Graph, row.InventoryID, row.SiteID))
{
sender.SetValueExt<POLine_Extension.qtyAvail>(row, siteStatus.QtyAvail);
}
}
}

Performance issue with 200+ SO line items with unbound columns

I have added two new unbound columns for SOLine DAC and updating them with in RowSelected handler. However, but it is very slow when there are more number of line items.
Here is the code for unbound columns-
[System.Serializable]
public class SOLineExtension : PXCacheExtension<SOLine>
{
#region UsrQtyAllocated
public abstract class usrQtyAllocated : IBqlField { }
protected decimal? _UsrQtyAllocated;
[PXUIField(DisplayName = "Qty. Allocated")]
public virtual decimal? UsrQtyAllocated { get; set; }
#endregion
#region UsrItemClass
public abstract class usrItemClass : IBqlField { }
protected string _UsrItemClass;
[PXUIField(DisplayName = "Item Class")]
public virtual string UsrItemClass { get; set; }
#endregion
}
Here is the code snippet for RowSelected handler-
protected void SOLine_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
SOLine row = (SOLine)e.Row;
if (row == null) return;
PXUIFieldAttribute.SetEnabled<SOLineExtension.usrQtyAllocated>(sender, row, false);
PXUIFieldAttribute.SetEnabled<SOLineExtension.usrItemClass>(sender, row, false);
INItemClass defItemClass = PXSelectJoin<INItemClass,
InnerJoin<InventoryItem,
On<InventoryItem.itemClassID, Equal<INItemClass.itemClassID>>>,
Where<InventoryItem.inventoryID, Equal<Required<InventoryItem.inventoryID>>>>.Select(Base, row.InventoryID);
if (defItemClass != null)
{
sender.SetValue<SOLineExtension.usrItemClass>(row, defItemClass.Descr);
}
SOLineSplit defSOLine = PXSelectJoin<SOLineSplit,
InnerJoin<SOLine,
On<SOLine.orderType, Equal<SOLineSplit.orderType>,
And<SOLine.orderNbr, Equal<SOLineSplit.orderNbr>,
And<SOLine.inventoryID, Equal<SOLineSplit.inventoryID>>>>>,
Where<SOLineSplit.isAllocated, Equal<Required<SOLineSplit.isAllocated>>,
And<SOLineSplit.orderNbr, Equal<Required<SOLineSplit.orderNbr>>,
And<SOLineSplit.inventoryID, Equal<Required<SOLineSplit.inventoryID>>>>>>.Select(Base, 1, row.OrderNbr, row.InventoryID);
if (defSOLine != null)
{
sender.SetValue<SOLineExtension.usrQtyAllocated>(row, defSOLine.Qty);
}
}
One should never use RowSelected handlers to set values for unbound DAC fields: RowSelected event is intended to execute only UI presentation logic. Instead it’s always recommended to assign values to unbound fields with a RowSelecting handler as shown in API Reference and Step 5.2: Customizing Business Logic for the Sales Orders form the T300 class documentation.
Also for information: none of your custom fields are decorated with PXTypeAttribute, which always a must.

Resources