I have a process which is activated by a button on a screen - but I'd like to know how to make it work like a process button, where the spinning wheel happens and the green checkbox appears at the end. I've got the following code, which I had wrapped in a PXLongOperation.StartOperation(...) as follows (the PXLongOperation is commented out here, because it didn't seem to be doing anything):
public PXAction<APInvoice> CreatePOBillings;
// [PXButton(CommitChanges = true)]
[PXProcessButton]
[PXUIField(DisplayName = "Create PO Billings", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Update)]
protected void createPOBillings()
{
int i = 0;
try
{
//This wraps the whole process into a 'PXLongOperation' function that will create the spinning 'busy' wheel at the top toolbar...
//PXLongOperation.StartOperation(this, delegate()
//{
var apinvoice = (APInvoice)Base.Document.Current;
if (apinvoice == null) return;
string RefNbr = apinvoice.RefNbr;
//Run the stored procedure which will get the records to create the Project Transactions. This will populate the table 'xCreatePOBilings':
var pars = new PXSPParameter[] { new PXSPInParameter("#p_RefNbr", RefNbr) }; //, new PXSPOutParameter("p2", outp2) };
var results = PXDatabase.Execute("xspMarketingPOBilling", pars);
//Get the dataset from the xCreatePOBillings table which was populated from the stored procedure above:
PXResultset<xCreatePOBillings> res = PXSelect<xCreatePOBillings,
Where<xCreatePOBillings.ponbr, Equal<Required<xCreatePOBillings.ponbr>>>
,OrderBy<Asc<xCreatePOBillings.ponbr
,Asc<xCreatePOBillings.destProject
,Asc<xCreatePOBillings.startDate>>>>>.Select(Base, RefNbr);
//Create the graph for the Project Transactions screen:
RegisterEntry graph = PXGraph.CreateInstance<RegisterEntry>();
//Create a new cache object for the header of Project Transactions:
PMRegister pmreg = new PMRegister();
pmreg.Module = "PM";
graph.Document.Insert(pmreg);
//Define the cache for the Project Transactions screen's grid records:
PMTran pmtrn;
foreach (PXResult<xCreatePOBillings> rec in res)
{
....
graph.Actions.PressSave();
//});
What is the best way to achieve this, if it's possible?
When the static PXLongOperation.StartOperation method is invoked inside a BLC extension class, as first parameter it can only accept Base property instead of this keyword:
PXLongOperation.StartOperation(Base, delegate()
{
...
}
Related
I have a custom field on an Acumatica Production Detail Operation (UsrEligibleForRoboticFulfillment) that I have created an Action to set based on criteria on the component items in the Materials tab. (code below)
I would like to call this Action to set the field as soon as the Production Order is created, but the split nature of the Production Order is such that there are no events on the Production Detail raised that I can attach to and call the Action. I've tried Row Inserted as well as Persist delegate on the Production Detail graph.
I CAN attach to either the AMProdItem Row Inserted or Persist Delegate on the Production Maint graph, but at this point in time the Operations and Materials have not yet been created.
What's the best way to update this field when a new Production Order is created?
Action code:
public PXAction<AMProdItem> UpdateEligibleForRoboticFulfillment;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Update Robotic Eligibility", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select)]
protected void updateEligibleForRoboticFulfillment()
{
AMProdItem prodDetail = Base.ProdItemRecords.Current;
AMProdOper prodOper = Base.ProdOperRecords.Current;
InventoryItem finishedProduct = PXSelect<InventoryItem,
Where<InventoryItem.inventoryID, Equal<Current<AMProdItem.inventoryID>>>>.Select(Base).FirstOrDefault();
//Only production orders are eligible for robotic fulfillment, not disassemblies
if (prodDetail.OrderType == "MO")
{
bool wasRoboticsEligible = (prodOper.GetExtension<AMProdOperExt>().UsrEligibleForRoboticFulfillment ?? false);
//Get the current branchID
int branchID = (int)Base.Accessinfo.BranchID;
//Get the default site/warehouse for this branch.
INSite site = INTranHelper.GetDefaultSiteForItemBranch(branchID);
//Get the flag indicating whether this site is active for robotics
bool activeRobotics = site.GetExtension<INSiteExt>().UsrActiveRobotics ?? false;
//Get the flags for manual process and component robotics compatible
bool requiresManualProcess = finishedProduct.GetExtension<InventoryItemExt>().UsrManualFinishRequired ?? false;
//Gotta be prepared for the possibility that more than one component is used
//Check for any components that are NOT robotics eligible that have qty required and haven't already been fully allocated
PXResultset<AMProdMatl> components = PXSelectJoin<AMProdMatl,
InnerJoin<InventoryItem, On<InventoryItem.inventoryID, Equal<AMProdMatl.inventoryID>,
And<InventoryItemExt.usrRoboticsCompatible, Equal<False>,
And<AMProdMatl.orderType, Equal<Current<AMProdOper.orderType>>,
And<AMProdMatl.prodOrdID, Equal<Current<AMProdOper.prodOrdID>>,
And<AMProdMatl.operationID, Equal<Current<AMProdOper.operationID>>,
And<AMProdMatl.qtyActual, Less<AMProdMatl.totalQtyRequired>,
And<AMProdMatl.qtyReq, Greater<decimal0>>>>>>>>>>
.Select(Base);
bool roboticsEligible = !requiresManualProcess && activeRobotics;
//If any component is not eligible, the whole operation is not eligible
if (components.Count > 0)
{
roboticsEligible = false;
}
//If the robotics eligible flag should have changed, change it
if (wasRoboticsEligible != roboticsEligible)
{
prodOper.GetExtension<AMProdOperExt>().UsrEligibleForRoboticFulfillment = roboticsEligible;
Base.ProdOperRecords.Update(prodOper);
}
}
}
Had to open a ticket with Acumatica; got a working solution! I had to enclose the persist delegate method in a transaction scope.
Override Persist() method of graph
Call base method first so that Operations and Materials on the Production Order Detail gets created
Enclosed in transaction scope
Something like this:
public delegate void PersistDelegate();
[PXOverride]
public void Persist(PersistDelegate baseMethod)
{
if (/**/)
{
using (var ts = new PXTransactionScope())
{
//Call base method to persist
baseMethod();
/*Custom Logic here*/
ts.Complete();
}
}
else
baseMethod();
}
I have a custom action on screen CR304000 - OpportunityMaint in the Quotes tab of the grid view that marks a field called IsPrimary in the CRQuote DAC as true for the current record in the Quotes view. These project Quotes are associated with the current opportunity as well as with a related PMQoute in the PMQuoteMaint BLC. The PMQuoteMaint BLC has a view called Activties that has all the CRActivities associated with the PMQuote. I created a custom field in the CRActivity called IsPrimary and added it to the Activities view in the PMQuoteMaint BLC grid.
My goal was to override the Action in the OpportunityMaint to update the IsPrimary field in CRActivity to true or false depending on what the Action in OpportunityMaint is toggling. However, my IsPrimary field in PMQuoteMaint is not toggling. My code attempts to get Current Quote OpportunityMain and then create a PMQuoteMaint graph and set the Current record. Then iterate though Activies view and set the IsPrimary field accordingly. Like I said, not having success because I'm not sure that my code is successfully retrieving the correct Activities.
There might be a better way to access CRActivity associated with a PMQuote, but I'm not sure. Any help would be appreciated. Here is code:
public virtual IEnumerable PrimaryQuote(PXAdapter adapter)
{
foreach (CROpportunity opp in adapter.Get())
{
if (Quotes.Current?.IsPrimary != true)
{
var selectExistingPrimary = new PXSelect<CRQuote, Where<CRQuote.quoteID,
Equal<Required<CRQuote.quoteID>>>>(this);
CRQuote primary = selectExistingPrimary.Select(opp.DefQuoteID);
if (primary != null && primary.QuoteID != Quotes.Current.QuoteID && primary.Status ==
PM.PMQuoteStatusAttribute.Closed)
{
throw new PXException(PM.Messages.QuoteIsClosed, opp.OpportunityID,
primary.QuoteNbr);
}
var quoteID = Quotes.Current.QuoteID;
var opportunityID = this.Opportunity.Current.OpportunityID;
this.Persist();
PXDatabase.Update<Standalone.CROpportunity>(
new PXDataFieldAssign<Standalone.CROpportunity.defQuoteID>(quoteID),
new PXDataFieldRestrict<Standalone.CROpportunity.opportunityID>(PXDbType.VarChar,
255, opportunityID, PXComp.EQ)
);
this.Cancel.Press();
CROpportunity rec = this.Opportunity.Search<CROpportunity.opportunityID>
(opportunityID);
yield return rec;
}
yield return opp;
}
``` My OverRide
public PXAction<CROpportunity> primaryQuote;
[PXUIField(DisplayName = Messages.MarkAsPrimary)]
[PXButton]
public virtual IEnumerable PrimaryQuote(PXAdapter adapter)
{
// this is currently selected record in quotes grid
var currQuoteNbr = Base.Quotes.Current.QuoteNbr;
bool isPrimary2;
foreach (CRQuote quote in Base.Quotes.Select())
{
var quoteNbr = quote.QuoteNbr;
if(quoteNbr.Trim() == currQuoteNbr.Trim())
{
isPrimary2 = true;
}
else
{
isPrimary2 = false;
}
PXTrace.WriteInformation(string.Format("Quote: {0} Value:
{1}",quoteNbr.ToString(),isPrimary2.ToString()));
var PMQuoteMaintGraph = PXGraph.CreateInstance<PMQuoteMaint>();
PMQuoteMaintGraph.Quote.Current = PMQuoteMaintGraph.Quote.Search<PMQuote.quoteNbr>
(quoteNbr.Trim()); // this is the current quote
foreach (CRActivity activity in PMQuoteMaintGraph.Activities.Select())
{
CRActivityExt itemExt = PXCache<CRActivity>.GetExtension<CRActivityExt>(activity);
itemExt.UsrPrimary = isPrimary2;
PMQuoteMaintGraph.Activities.Cache.Persist(PXDBOperation.Update);
PXDatabase.Update<CRActivity>(new PXDataFieldAssign<CRActivityExt.usrPrimary>
(isPrimary2));
}
}
return Base.primaryQuote.Press(adapter);
}
I'm trying to create a Sales Order from the PO screen using the SOOrderEntry graph object. I'm selecting the branch using a technique from another Stack Overflow case, and I continuously get the following error:
I can't figure out why this error is coming up, since I'm setting the CustomerID. Here's the code:
public class POOrderEntryExt : PXGraphExtension<POOrderEntry>
{
public override void Initialize()
{
Base.action.AddMenuAction(CreateSO);
}
public PXAction<POOrder> CreateSO;
[PXUIField(DisplayName = "Create Sales Order", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select)]
[PXButton]
protected virtual void createSO()
{
SOOrderEntry sograph = null;
SOOrder soorder = null;
SOLine soline = null;
//Let's get the current data from the screen we're in...
var poorder = (POOrder)Base.Document.Current;
PXResultset<POLine> res = PXSelect<POLine, Where<POLine.orderNbr, Equal<Required<POLine.orderNbr>>>>.Select(Base, poorder.OrderNbr);
using (PXLoginScope ls = new PXLoginScope("admin"))
{
//Create a new instance of the AP Bills screen graph..
sograph = PXGraph.CreateInstance<SOOrderEntry>();
//Get the branch...
var branch = (Branch)PXSelect<Branch, Where<Branch.branchCD, Equal<Required<Branch.branchCD>>>>.Select(Base, "WI-NVC VET");
//soorder.BranchID = branch.BranchID;
//This handler is added per RD from another Stack Overflow case. It's necessary to select the Branch...
sograph.FieldDefaulting.AddHandler<SOOrder.branchID>((s, e) =>
{
e.NewValue = branch.BranchID;
e.Cancel = true;
});
soorder = new SOOrder();
//The OrderType...
soorder.OrderType = SOOrderTypeConstants.SalesOrder;
sograph.Document.Insert(soorder);
soorder.OrderDate = (DateTime?)DateTime.Now;
soorder.RequestDate = (DateTime?)DateTime.Now;
//Get the customer id...
var bacct = (BAccountR)PXSelect<BAccountR, Where<BAccountR.acctCD, Equal<Required<BAccountR.acctCD>>>>.Select(Base, "NE-C003118");
soorder.CustomerID = bacct.BAccountID; // (int?)5454;
sograph.Document.Update(soorder);
sograph.Actions.PressSave();
This is very import to always assign the result of PXCache Insert or Update method invocation to some local variable so further you make changes to the object from the cache, rather than to a legacy record, which has nothing in common with PXCache.
The issue should be resolved by the following tiny change:
soorder = new SOOrder();
soorder.OrderType = SOOrderTypeConstants.SalesOrder;
soorder = sograph.Document.Insert(soorder);
I created a new field, UsrAcknowledged, boolean, checkbox, and placed it on the SOLine details grid. I want every line on the details grid to be checked when the Email Sales Order/Quote process is run under Actions for SOOrder. My code is as follows under the SOOrderEntry_Extension:PXGraphExtension
public CRActivityList<SOOrder> Activity;
public PXAction<SOOrder> notification;
[PXUIField(DisplayName = "Notifications", Visible = false)]
[PXButton(ImageKey = PX.Web.UI.Sprite.Main.DataEntryF)]
protected virtual IEnumerable Notification(PXAdapter adapter,
[PXString]
string notificationCD
)
{
foreach (SOOrder order in adapter.Get<SOOrder>())
{
var parameters = new Dictionary<string, string>();
parameters["SOOrder.OrderType"] = order.OrderType;
parameters["SOOrder.OrderNbr"] = order.OrderNbr;
Activity.SendNotification(ARNotificationSource.Customer, notificationCD, order.BranchID, parameters);
//order.OrderDesc = "Desc";
foreach (SOLine line in PXSelect<SOLine>.Select(Base, order.OrderNbr))
{
SOLineExt rowExt = line.GetExtension<SOLineExt>();
rowExt.UsrAcknowledged = true;
line.OrderQty = 5;
}
yield return order;
}
}
//order.Desc = "Desc" was an initial simple test just to see if my code was achieving the desired results and the proder desciption was changed as planned.
My code compiles and the email process runs as planned with a green check for successful, but neither the Acknowledged check box nor the Order Quantity are changed. I don't really care about the ord qty, it was just another test.
Any suggestions for what I can change to update the Acknowledged checkbox to checked during the Email Sales Order/Quote under Actions on the SO Order Entry screen would be appreciated
I would say you need to call Base.Transactions.Update(line) after you update your row values inside your foreach.
When an item is clicked, a details view controller is opened that makes some webservice calls to save back the data if the user clicks save (this works fine). The issue I have is after saving the data when the user clicks back to go back to the list, the list item (ie if the name was updated) is not updated. So, perhaps the user changes the name and so the list prior should be updated, I'd like to update the value without having to make another webservice call to repopulate.
I am calling this.ReloadData() when the view appears, but this never seems to update the list names. I've checked workouts which is a list of objects, and it has been modified by the detail controller, however the reload data doesn't seem to do anything.
How do I refresh the data in the case that another controller has modified the list of objects.
public override void ViewWillAppear (bool animated)
{
InvokeOnMainThread (delegate {
this.ReloadData();
}); // call reload on main thread
base.ViewWillAppear (animated);
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
workouts = manager.GetWorkouts(Helpers.User.Auth);
var sections = new Section();
var root = new RootElement("Workouts"){sections};
foreach (var wo in workouts) {
var tempwo = wo;
var wodetail = new RootElement(wo.Name, delegate {
var W = new WorkoutViewModel(tempwo);
var p = new WorkoutDetailController(W, tempwo);
return p;
});
sections.Add (wodetail);
}
Root = root;
}
It seems that when you assign the value of an element from a custom object like so:
AccountDetailsSection = new Section ("Details"){
new StringElement ("Code:", _Account.Code),
new StringElement ("Name:", _Account.Name) };
You get the desired result but the element loses the pointer back to the property of the object. So if you change the value of the property later for example:
_Account.Code = "xxx";
Reloading the data doesn't do anything. You should manually update the value like so:
(AccountDetailsSection.Elements [0] as StringElement).Value = "xxx";
this.Root.Reload ( AccountDetailsSection, UITableViewRowAnimation.Fade);
Hope this Helps.