Execute code on confirmation of shipment - acumatica

I want to execute some code (to change the shipment date) upon the 'Confirm Shipment' action on the Shipments screen (SO302000).
I was thinking that this would be the way to do it:
public class SOShipmentEntryExt : PXGraphExtension<SOShipmentEntry>
{
[PXOverride]
public virtual void ConfirmShipment(SOOrderEntry docgraph, SOShipment shiporder)
{
Base.ConfirmShipment(docgraph, shiporder);
//Add my code to do something here...
}
}
When I try this, I get a shipment counter error. Is there a better way to do this?

From a similar case:
How to add custom business logic to Acumatica framework's Actions?
public class SOShipmentEntryExt : PXGraphExtension<SOShipmentEntry>
{
public PXAction<SOShipment> action;
[PXButton]
[PXUIField(DisplayName = "Actions", MapEnableRights = PXCacheRights.Select)]
protected IEnumerable Action(PXAdapter adapter
,[PXIntList(new int[] { 1 }
,new string[] { "Confirm Shipment" })
,PXInt] int? actionID)
{
//actionID = 1 means the Confirm Shipment action was the one invoked
if (actionID == 1)
{
Base.Document.Current.ShipDate = Base.Accessinfo.BusinessDate;
Base.Document.Update(Base.Document.Current);
}
//calls the basic action that was invoked
return Base.action.Press(adapter);
}
}

Related

Adding custom action menu to Acumatica Customer form

I think this is probably just something stupid, but I'm trying to add a custom menu to the Customer form. I've used this code:
public override void Initialize()
{
base.Initialize();
this.SpeedyActions.AddMenuAction(BtnCreateMenu1);
this.SpeedyActions.AddMenuAction(BtnCreateMenu2);
}
public PXAction<PX.Objects.AR.Customer> BtnCreateMenu1;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Menu Item 1")]
protected void btnCreateMenu1()
{
// Logic
}
public PXAction<PX.Objects.AR.Customer> BtnCreateMenu2;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Menu Item 2")]
protected void btnCreateMenu2()
{
// Logic
}
public PXAction<PX.Objects.AR.Customer> SpeedyActions;
[PXButton(SpecialType = PXSpecialButtonType.Report, MenuAutoOpen =true)]
[PXUIField(DisplayName = "Speedy Actions")]
protected void speedyActions()
{
//code logic here
}
I've got basically the same code on the sales order form and it works perfectly. I suspect that I'm maybe using the wrong code here PX.Objects.AR.Customer and I've also tried CR.BAccount, but can't seem to make it work. Anyone have some thoughts?
I would try
Base.report.AddMenuAction(BtnCreateMenu1)

Reordering header buttons

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

How to add custom business logic to Acumatica framework's Actions?

I added a custom field to SOShipment and I want to set its value when the CreateShipment action is invoked on Order Entry or through Process Orders screens. How do I do that ?
Create a graph extension for SOOrderEntry and add an Action method like this :
using PX.Data;
using System.Collections;
namespace PX.Objects.SO
{
public class SOOrderEntry_Extension : PXGraphExtension<SOOrderEntry>
{
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)
{
//actionID = 1 means the CreateShipment action was the one invoked
if (actionID == 1)
{
PXGraph.InstanceCreated.AddHandler<SOShipmentEntry>((graph) =>
{
graph.RowInserting.AddHandler<SOShipment>((sender, e) =>
{
//Custom logic goes here
var shipment = (SOShipment)e.Row;
var shipmentExt = PXCache<SOShipment>.GetExtension<SOShipmentExt>(shipment);
if (Base.Document.Current != null && shipmentExt != null)
{
shipmentExt.UsrPriority = Base.Document.Current.Priority;
}
});
});
}
//calls the basic action that was invoked
return Base.action.Press(adapter);
}
}
}
When any of SOOrderEntry's actions is run (even through the Process Orders screen), this method is invoked. We verify that the action really is CreateShipment with the actionID == 1 and add events handler for SOShipmentEntry graph creation and SOShipment RowInserting. Our custom logic is added inside the RowInserting event.

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

How to pass custom field vales from Opportunity to sales Order?

I have to copy the custom field values from opportunity to sales order while converting the opportunity to sales order.
I have come across a sample code to pass custom field from sales order to shipment and I have tried to use the code for overriding the “create sales order” action.
The following code snippet I have used in OpportunityMaint extension class
public PXAction action;
[PXButton]
[PXUIField(DisplayName="Actions",MapEnableRights=PXCacheRights.Select,MapViewRights=PXCacheRights.Select)]
protected IEnumerable Action( PXAdapter adapter,
[PXIntList(new int[] {1,2,3}, new string[] {"Create Account","Create Sales order","Create Invoice"}),PXInt]
int? actionId,
[PXString]
string ActionName)
{
if(actionId == 2)
{
// Implement So Order row insert handler
}
return Base.Action.Press(adapter);
}
The piece of code is not triggering.
Looking forward for better solution to implement this option
Regards,
R.Muralidharan
You will need to override the CreateSalesOrder action. Below is a snippet of code where I had to push the opportunity down to the sales order.
public class OpportunityMaint_Extension : PXGraphExtension<OpportunityMaint>
{
public PXAction<CROpportunity> createSalesOrder;
[PXUIField(DisplayName = Messages.CreateSalesOrder, MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Select)]
[PXButton(ImageKey = PX.Web.UI.Sprite.Main.DataEntry)]
public virtual IEnumerable CreateSalesOrder(PXAdapter adapter)
{
PXGraph.InstanceCreated.AddHandler<SOOrderEntry>((graph) =>
{
graph.RowInserted.AddHandler<SOOrder>((cache, args) =>
{
var soOrder = (SOOrder)args.Row;
var soOrderExt = PXCache<SOOrder>.GetExtension<SOOrderExt>(soOrder);
foreach (CROpportunity opportunity in adapter.Get())
{
soOrderExt.UsrOpportunityID = opportunity.OpportunityID;
}
});
});
return Base.createSalesOrder.Press(adapter);
}
}

Resources