Reordering header buttons - acumatica

I have added a custom save button in Sales Order screen in place of my current one. How do I reorder the main toolbar so that new save button is in place of the old one? You can do this easily for grid buttons, but for header ones it is not so obvious.

The order of the buttons is based on the order of the PXActions in the graph.
(1) In this example my save button is first, then cancel button second.
public class MyGraph : PXGraph<MyGraph>
{
public PXSave<MyPrimaryDac> Save;
public PXCancel<MyPrimaryDac> Cancel;
}
(2) In this example my cancel button is first, then save button second.
public class MyGraph : PXGraph<MyGraph>
{
public PXCancel<MyPrimaryDac> Cancel;
public PXSave<MyPrimaryDac> Save;
}
Note that PXSave and PXCancel are PXActions.
Edit: from comments and question edits if you are extending another graph you should be able to use a new class inherited from PXSave and set the property name the same ("Save" in the example of sales order). Here is something that should work for an override to the save button and asking the user a question and keeps the save button in the same button location...
public class SOOrderEntryExt : PXGraphExtension<SOOrderEntry>
{
public SalesPXSave<SOOrder> Save;
public class SalesPXSave<TNode> : PXSave<TNode> where TNode : class, IBqlTable, new()
{
public SalesPXSave(PXGraph graph, string name) : base(graph, name)
{
}
public SalesPXSave(PXGraph graph, Delegate handler) : base(graph, handler)
{
}
[PXSaveButton]
[PXUIField(DisplayName = "Save", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Update)]
protected override IEnumerable Handler(PXAdapter adapter)
{
bool someCondition = true;
if (someCondition)
{
if (adapter.View.Ask(adapter.View.Graph.Caches[typeof(TNode)].Current, "Hi User",
MessageButtons.YesNo) != WebDialogResult.Yes)
{
return adapter.Get();
}
}
return base.Handler(adapter);
}
}
}
Edit: For reference here are some quick untested examples of extending RowPersisting or the Persist in an extension if this is preferred vs extending the buttons...
[PXOverride]
public virtual void Persist(Action del)
{
if (Base.Document.Ask(Base.Document.Current, "Question", "Continue?",
MessageButtons.YesNo) != WebDialogResult.Yes)
{
return;
}
del?.Invoke();
}
protected virtual void SOOrder_RowPersisting(PXCache cache, PXRowPersistingEventArgs e, PXRowPersisting del)
{
var row = (SOOrder)e.Row;
if (row == null)
{
return;
}
if ((e.Operation == PXDBOperation.Insert || e.Operation == PXDBOperation.Update || e.Operation == PXDBOperation.Delete) &&
Base.Document.Ask(row, "Question", "Continue ?", MessageButtons.YesNo, true) != WebDialogResult.Yes)
{
e.Cancel = true;
return;
}
del?.Invoke(cache, e);
}

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

APRegisterExt not saving data on APInvoiceEntry

Build 18.203.0006
Page: AP301000
Good day, I have extended the APRegister Class by adding 2 new checkbox fields. I want to iterate through al the APTran transactions and look for suIDs that start with FBL and GAS. If I find them the new tick boxes should tick.
Currently, the fields(tick boxes) do not save to the database. I am not sure how to tell Acumatica the APRegisterExt has updated.
APInvoiceEntry_Extension:
namespace PX.Objects.AP
{
public class APRegisterExt : PXCacheExtension<PX.Objects.AP.APRegister>
{
#region UsrGroupAEmail
[PXDBBool]
[PXUIField(DisplayName="GroupA Email")]
public virtual bool? UsrGroupAEmail { get; set; }
public abstract class usrGroupAEmail : IBqlField { }
#endregion
#region UsrGroupBEmail
[PXDBBool]
[PXUIField(DisplayName="GroupB Email")]
public virtual bool? UsrGroupBEmail { get; set; }
public abstract class usrGroupBEmail : IBqlField { }
#endregion
}
}
APInvoiceEntry_Extension:
namespace PX.Objects.AP
{
public class APInvoiceEntry_Extension : PXGraphExtension<APInvoiceEntry>
{
#region Event Handlers
protected void APInvoice_RowSelected(PXCache cache, PXRowSelectedEventArgs e)
{
APInvoice invoice = e.Row as APInvoice;
if (invoice == null) return;
var apRX = invoice.GetExtension<APRegisterExt>();
PXResultset<APTran> Tlist = PXSelectJoin<APTran,
LeftJoin<POReceiptLine,
On<POReceiptLine.receiptNbr, Equal<APTran.receiptNbr>,
And<POReceiptLine.lineNbr, Equal<APTran.receiptLineNbr>>>>,
Where<
APTran.tranType, Equal<Current<APInvoice.docType>>,
And<APTran.refNbr, Equal<Current<APInvoice.refNbr>>>>,
OrderBy<
Asc<APTran.tranType,
Asc<APTran.refNbr,
Asc<APTran.lineNbr>>>>>.Select(Base);
apRX.UsrGroupBEmail = false;
apRX.UsrGroupAEmail = false;
foreach (APTran item in Tlist)
{
if (item.SubID.Value.ToString().StartsWith("FBL") || item.SubID.Value.ToString().StartsWith("GAS"))
{
apRX.UsrGroupBEmail = true;
cache.SetValue<APRegisterExt.usrGroupBEmail>(e.Row, true);
}
else
{
apRX.UsrGroupAEmail = true;
}
}
}
#endregion
}
}
Can someone please show me the correct way of saving the data to the new tick boxes so that cache updates.
Consider moving the logic to APRegister_RowPersisting.
Importantly, APTran.subID is an Int dataType.
Read instead the Sub table/DAC during a For Loop of
Base.Transactions.Select() to detect SubCD startsWith FBL or GAS.
foreach(APTran item in Base.Transaction.Select())
{
Sub sub = PXSelect<Sub,
Where<Sub.subID, Equal<Required<Sub.subID>>>>
.Select(graph, item.SubID);
if (item.SubCD.StartsWith("FBL") ||
item.SubCD.StartsWith("GAS"))
{
apRX.UsrGroupBEmail = true;
}
else
{
apRX.UsrGroupAEmail = true;
}
}
To be complete, you may also need to reset both flags during APTran_SubID_FieldUpdated regardless of the changed value:
protected void APTran_SubID_FieldUpdated(PXCache sender,
PXFieldUpdatedEventArgs e, PXFieldUpdated del)
{
del?.Invoke(sender,e);
var apRX = Base.Document.Current.GetExtension<APRegisterExt>();
apRX.UsrGroupBEmail = false;
apRX.UsrGroupAEmail = false;
}

Is it possible to dynamically change the PXSelect a view is using programatically?

I have a button in which, when pressed, I want to show or hide rows in a grid based on certain criteria. Is it possible to change the PXSelect a view uses on the fly so that it re-queries the database and retrieves different results? I will, of course, be querying the same table and not changing up the structure of the View or grid.
The code below adds a non-visible field to the header record that is set by the button press this value is then used by the child records view delegate to determine based on the child records criteria (in this case a Boolean on each child) if they are shown.
public sealed class APInvoiceExtension : PXCacheExtension<APInvoice>
{
#region UsrShowAll
public abstract class usrShowAll : IBqlField
{
}
[PXBool]
public bool? UsrShowAll { get; set; }
#endregion
}
public sealed class APTranExtension : PXCacheExtension<APTran>
{
#region UsrHidden
public abstract class usrHidden : IBqlField
{
}
[PXDBBool]
[PXUIField(DisplayName = "Hidden", Enabled = false)]
public bool? UsrHidden { get; set; }
#endregion
}
public class APInvoiceEntryExtension : PXGraphExtension<APInvoiceEntry>
{
public PXAction<APInvoice> SHW;
[PXUIField(DisplayName = "Show All Records", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Update)]
[PXButton]
protected void sHW()
{
if (Base.Document.Current != null)
{
APInvoiceExtension docExt = Base.Document.Current.GetExtension<APInvoiceExtension>();
docExt.UsrShowAll = !(docExt.UsrShowAll ?? false);
}
}
protected virtual IEnumerable transactions()
{
bool showAll = Base.Document.Current != null ? (Base.Document.Current.GetExtension<APInvoiceExtension>().UsrShowAll ?? false) : false;
APTran tran;
foreach (PXResult<APTran, POReceiptLine> res in Base.Transactions.Select())
{
tran = res[0] as APTran;
if (!showAll)
{
if (!(tran.GetExtension<APTranExtension>().UsrHidden ?? false))
{
yield return res;
}
}
else
{
yield return res;
}
}
}
}

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.

Custom logic doesnt trigger before the base action "APPROVE"

When I do a custom logic before the APPROVE in requisition screen, it seems like the logic is triggered after. My code is as below.
public PXAction<RQRequisition> action;
[PXUIField(DisplayName = "Actions")]
[PXButton]
protected virtual IEnumerable Action(PXAdapter adapter,
[PXInt][PXIntList(new int[] { 1, 2 }, new string[] { "Approve", "Reject" })] int? actionID,
[PXBool]bool refresh,
[PXString]string actionName)
{
if (adapter.Menu == "Approve")
{
SIApprovalInfo.updateNextApprover(this.Base);
}
return Base.action.Press(adapter);
}
and I tried this action without calling the return Base.action.Press(adapter);
Sample code:
public PXAction<RQRequisition> action;
[PXUIField(DisplayName = "Actions")]
[PXButton]
protected virtual IEnumerable Action(PXAdapter adapter,
[PXInt][PXIntList(new int[] { 1, 2 }, new string[] { "Approve", "Reject" })]int? actionID,
[PXBool]bool refresh,
[PXString]string actionName)
{
if (adapter.Menu == "Approve")
{
SIApprovalInfo.updateNextApprover(this.Base);
}
return adapter.Get();
}
Still the document is getting approved somehow. So where exactly is this approval getting triggered? How can I add my custom logic before that action?
I suggest a workaround which would be the create your own action, add it to the menu and depending on your business logic, call the Approval. Here is the basic snippet you could use :
using PX.Data;
using System.Collections;
namespace PX.Objects.RQ
{
public class RQRequisitionEntry_Extension : PXGraphExtension<RQRequisitionEntry>
{
public override void Initialize()
{
Base.action.AddMenuAction(CustomApprove);
CustomApprove.SetEnabled(true);
}
public PXAction<RQRequisition> CustomApprove;
[PXButton]
[PXUIField(DisplayName = "Custom Approve")]
protected virtual IEnumerable customApprove(PXAdapter adapter)
{
PXCache cache = Base.Caches[typeof(RQRequisition)];
var requisition = Base.Document.Current;
if (Base.Approval.Approve(requisition))
{
requisition.Approved = true;
yield return Base.Document.Update(requisition); ;
}
else
{
throw new PXInvalidOperationException(PXLocalizer.Localize("Approval failed"));
}
}
}
}

Resources