Hopefully this will be an easy question!
I've created a new report for the Acumatica Opportunity screen, and I'd like to add it to the screen itself. I've previously added a new report to the existing REPORTS dropdown, but I have not played with Automation Steps enough to create the REPORTS dropdown if it does not already exist.
Can someone provide instructions or point me in the direction of the appropriate documentation?
EDIT: So, what I'm running into is that the Opportunity doesn't have a Reports section already there to add to; I have to create it.
What I have so far gets a REPORTS button on the screen but it's not a dropdown and it doesn't appear to do anything.
public class OpportunityMaint_Extension : PXGraphExtension<OpportunityMaint>
{
#region Event Handlers
#endregion
#region Actions
public PXAction<CROpportunity> report;
[PXUIField(DisplayName = "Reports", MapEnableRights = PXCacheRights.Select)]
[PXButton(SpecialType = PXSpecialButtonType.Report)]
protected virtual IEnumerable Report(PXAdapter adapter,
[PXString(8, InputMask = "CC.CC.CC.CC")]
[PXStringList(new string[] { "IOCR6410" }, new string[] { "Quote" })]
string reportID)
{
List<CROpportunity> list = adapter.Get<CROpportunity>().ToList();
if (!String.IsNullOrEmpty(reportID))
{
Base.Save.Press();
int i = 0;
Dictionary<string, string> parameters = new Dictionary<string, string>();
foreach (CROpportunity opp in list)
{
if (reportID == "IOCR6410")
{
parameters["CROpportunity.OpportunityID" + i.ToString()] = opp.OpportunityID;
}
i++;
}
if (i > 0)
{
throw new PXReportRequiredException(parameters, reportID, string.Format("Report {0}", reportID));
}
}
return list;
}
#endregion
}
For this type of changes, it's better to not make changes in automation step(s), but follow the approach suggested in How to add report to Inventory Transfers dropdown menu for reports
Below is a slightly updated version of your code snippet, which should lead to the following result:
public class OpportunityMaint_Extension : PXGraphExtension<OpportunityMaint>
{
public override void Initialize()
{
Report.AddMenuAction(QuoteReport);
Report.MenuAutoOpen = true;
}
public PXAction<CROpportunity> Report;
[PXButton]
[PXUIField(DisplayName = "Reports", MapEnableRights = PXCacheRights.Select)]
protected void report()
{ }
public PXAction<CROpportunity> QuoteReport;
[PXButton]
[PXUIField(DisplayName = "Quote", MapEnableRights = PXCacheRights.Select)]
protected IEnumerable quoteReport(PXAdapter adapter)
{
var reportID = "IOCR6410";
List<CROpportunity> list = adapter.Get<CROpportunity>().ToList();
Base.Save.Press();
int i = 0;
Dictionary<string, string> parameters = new Dictionary<string, string>();
foreach (CROpportunity opp in list)
{
parameters["CROpportunity.OpportunityID" + i.ToString()] = opp.OpportunityID;
i++;
}
if (i > 0)
{
throw new PXReportRequiredException(parameters, reportID, string.Format("Report {0}", reportID));
}
return list;
}
}
Related
I have a customization Project that requires me to only show data (like Opportunities) that have been setup in added customization tab in Business Account. I've manage to comply these using data view delegate and a custom selector, The error occurs when:
1.) clicking an opportunity ID where the customization restricts such data in the generic inquiry.
2.) clicking next or previous button in the opportunities module
Here is the Code
protected virtual IEnumerable opportunity()
{
var output = new PXSelect<CROpportunity>(Base);
foreach (CROpportunity _output in output.Select())
{
bool returnOutput = false;
var memberships = PXSelectJoinGroupBy<EPEmployee, RightJoin<EPCompanyTreeMember, On<EPCompanyTreeMember.userID, Equal<EPEmployee.userID>>>, Where<EPEmployee.userID, Equal<Current<AccessInfo.userID>>>, Aggregate<GroupBy<EPCompanyTreeMember.workGroupID>>>.Select(Base);
foreach (PXResult<EPEmployee, EPCompanyTreeMember> member in memberships)
{
EPCompanyTreeMember _member = (EPCompanyTreeMember)member;
BAccountRestriction2 visible = PXSelect<BAccountRestriction2, Where<BAccountRestriction2.account, Equal<Required<BAccount.bAccountID>>, And<BAccountRestriction2.child, Equal<Required<BAccountRestriction2.child>>>>>.Select(Base, ((CROpportunity)_output).BAccountID, _member.WorkGroupID);
if (visible != null || ((CROpportunity)_output).OpportunityID == " <NEW>")
{
returnOutput = true;
break;
}
}
if (returnOutput)
{
yield return _output;
}
}
}
Here is the Custom Selector
public class OpportunityMaintExtension : PXGraphExtension<OpportunityMaint>
{
#region custom selector
public class PXCustomSelectorOpportunityAttribute : PXCustomSelectorAttribute
{
public PXCustomSelectorOpportunityAttribute()
: base(typeof(CROpportunity.opportunityID)
, new[] { typeof(CROpportunity.opportunityID),
typeof(CROpportunity.opportunityName),
typeof(CROpportunity.status),
typeof(CROpportunity.curyAmount),
typeof(CROpportunity.curyID),
typeof(CROpportunity.closeDate),
typeof(CROpportunity.stageID),
typeof(CROpportunity.cROpportunityClassID),
typeof(BAccount.acctName),
typeof(Contact.displayName) }
)
{
//this.DescriptionField = typeof(CQHRISLeave.refNbr);
}
protected virtual IEnumerable GetRecords()
{
foreach (var pc in this._Graph.GetExtension<OpportunityMaintExtension>().opportunity())
{
if (((CROpportunity)pc).OpportunityID != " <NEW>")
yield return pc as CROpportunity;
}
}
}
#endregion
And then the implementation of the custom selector attribute is here:
public class CROpportunityExtension : PXCacheExtension<CROpportunity>
{
#region OpportunityID
public abstract class opportunityID : PX.Data.IBqlField { }
public const int OpportunityIDLength = 10;
[PXDBString(OpportunityIDLength, IsUnicode = true, IsKey = true, InputMask = ">CCCCCCCCCCCCCCC")]
[PXUIField(DisplayName = "Opportunity ID", Visibility = PXUIVisibility.SelectorVisible)]
[AutoNumber(typeof(CRSetup.opportunityNumberingID), typeof(AccessInfo.businessDate))]
[NORDE.OpportunityMaintExtension.PXCustomSelectorOpportunity]
[PXFieldDescription]
public virtual String OpportunityID { get; set; }
#endregion
}
I've managed to "resolve" this issue by disabling the generic inquiry on this page. Initially, Opportunities page is a generic inquiry, for some reason, it may have affected the cache or the data view delegate and throws an error.
I simply: Edit Generic inquiry -> Entry Point Tab -> Uncheck "Replace Entry Screen with this Inuiry in Menu"
We have a custom processing screen that is updating a custom field called UsrDateNotified in the ARTran table where the UsrDateNotified is prior to the RevisionDateReceived in a custom table ItemBaseDocument. We also need to update the UsrDateNotified field in table ItemBaseDocument which is linked to the InventoryID in ARTran. Our current code below validates for updating the ARTran table, but we are struggling with how to also update the related ItemBaseDocument for the selected ARTran records. What is the right approach for this scenario?
using System;
using System.Collections;
using System.Linq;
using PX.Data;
using PX.SM;
using PX.Objects.AR;
using PX.Objects.CR;
using PX.Objects.IN;
namespace DocCenter
{
public class UpdateLastNotified : PXGraph<UpdateLastNotified>
{
public PXFilter<UpdateLastNotifiedFilter> MasterView;
public PXCancel<UpdateLastNotifiedFilter> Cancel;
[PXFilterable]
public PXSelect<ARTran> DetailsView;
public UpdateLastNotified()
{
Cancel.SetCaption("Clear Filter");
this.DetailsView.Cache.AllowInsert = false;
this.DetailsView.Cache.AllowDelete = false;
this.DetailsView.Cache.AllowUpdate = true;
}
protected virtual IEnumerable detailsView()
{
UpdateLastNotifiedFilter filter = MasterView.Current as UpdateLastNotifiedFilter;
PXSelectBase<ARTran> cmd = new PXSelectJoinOrderBy<ARTran,
InnerJoin<InventoryItem, On<ARTran.inventoryID, Equal <InventoryItem.inventoryID>>,
InnerJoin<ItemBaseDocument, On<InventoryItemExt.usrDocumentNumber, Equal<ItemBaseDocument.baseDocumentCode>>,
InnerJoin<Contact, On<ARTranExt.usrContactID, Equal<Contact.contactID>>>>>,
OrderBy<Asc<ARTran.tranDate>>>(this);
cmd.WhereAnd<Where<ContactExt.usrNotificationPriority,
Equal<Current<UpdateLastNotifiedFilter.notificationPriority>>>>();
cmd.WhereAnd<Where<ARTranExt.usrDateNotified,
Less<ItemBaseDocument.revisionDateReceived>>>();
if (filter.BaseDocumentCode != null)
{
cmd.WhereAnd<Where<InventoryItemExt.usrDocumentNumber,
Equal<Current<UpdateLastNotifiedFilter.baseDocumentCode>>>>();
}
return cmd.Select();
}
public PXAction<UpdateLastNotifiedFilter> Process;
[PXProcessButton]
[PXButton(CommitChanges=true)]
[PXUIField(DisplayName = "Process")]
protected virtual IEnumerable process(PXAdapter adapter)
{
PXLongOperation.StartOperation(this, delegate()
{
foreach(ARTran tran in DetailsView.Select())
{
if (tran.Selected==true)
{
ARTranExt tranExt = tran.GetExtension<ARTranExt>();
ARInvoiceEntry tranEntry = new ARInvoiceEntry();
tranExt.UsrDateNotified = MasterView.Current.DateNotified;
tranEntry.Transactions.Update(tran);
tranEntry.Save.PressButton();
}
}
}
);
return adapter.Get();
}
[Serializable]
public class UpdateLastNotifiedFilter : IBqlTable
{
public static class NotificationPriority
{
public const string None = "N";
public const string Alert = "A";
public const string Express = "E";
public const string Shipment = "P";
public const string Subscription = "S";
}
#region NotificationPriority
public abstract class notificationPriority : PX.Data.IBqlField
{
}
[PXDBString(1, IsFixed = true)]
[PXDefault(NotificationPriority.None)]
[PXUIField(DisplayName = "Notification Type")]
[PXStringList(
new string[]
{
NotificationPriority.None,
NotificationPriority.Alert,
NotificationPriority.Express,
NotificationPriority.Shipment,
NotificationPriority.Subscription
},
new string[]
{
"None",
"Alert",
"Express",
"Shipment",
"Subscription"
})]
#endregion
#region BaseDocumentID
public abstract class baseDocumentCode : PX.Data.IBqlField
{
}
[PXString(50)]
[PXUIField(DisplayName="Document Number")]
[PXSelector(typeof(DocCenter.ItemBaseDocument.baseDocumentCode))]
public virtual String BaseDocumentCode
{
get;
set;
}
#endregion
#region DateNotified
public abstract class dateNotified : PX.Data.IBqlField
{
}
[PXDBDate()]
[PXDefault(typeof(AccessInfo.businessDate))]
[PXUIField(DisplayName = "Date Notified")]
public DateTime? DateNotified { get; set; }
#endregion
}
}
}
Your best bet would be to create a view that selects ItemBaseDocument.
PXSelect<ItemBaseDocument> ViewName;
In your for loop of your action button, you will want to create a new ItemBaseDocument object and set it equal to the corresponding ItemBaseDocument row entry. You can then update the date of this object, and when you execute your Save.PressButton() action, that should save the updates to that entry as well.
foreach(ARTran tran in DetailsView.Select())
{
if (tran.Selected==true)
{
ARTranExt tranExt = tran.GetExtension<ARTranExt>();
ARInvoiceEntry tranEntry = new ARInvoiceEntry();
tranExt.UsrDateNotified = MasterView.Current.DateNotified;
tranEntry.Transactions.Update(tran);
tranEntry.Save.PressButton();
//Target Added Code
ItemBaseDocument doc = PXSelect<ItemBaseDocument, Where<ItemBaseDocument.inventoryID,
Equal<Required<ARTran.inventoryID>>>>.Select(tran.InventoryID);
doc.UsrDateNotified = MasterView.Current.DateNotified;
}
}
Disclaimer: There may be a syntax error in the added code above. If there is, please let me know and I will fix it.
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"));
}
}
}
}
I am trying to add Add Items functionality in Opportunity like in Sales Order. I have gone through the code in SOOrderEntry and I have tried to replicate the same functionality.
The Add Item menu brings the smart panel with filter options, but it does not populate the data. I have compared the setting with Sales Order Add Item Smart panel settings and I did not miss anything.
I have replicated the table structure and events as in sales order with changes required for Opportunity.
#region SiteStatus Lookup
public PXFilter<OpportunitySiteStatusFilter> oppsitestatusfilter;
[PXFilterable]
[PXCopyPasteHiddenView]
public OpportunityStatusLookup<OpportunitySiteStatusSelected, OpportunitySiteStatusFilter> opportunitysitestatus;
public PXAction<CROpportunity> addOppBySite;
[PXUIField(DisplayName = "Add Stock Item", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select)]
[PXLookupButton]
public virtual IEnumerable AddOppBySite(PXAdapter adapter)
{
oppsitestatusfilter.Cache.Clear();
if (opportunitysitestatus.AskExt() == WebDialogResult.OK)
{
return AddOppSelBySite(adapter);
}
oppsitestatusfilter.Cache.Clear();
opportunitysitestatus.Cache.Clear();
return adapter.Get();
}
public PXAction<CROpportunity> addOppSelBySite;
[PXUIField(DisplayName = "Add", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select, Visible = false)]
[PXLookupButton]
public virtual IEnumerable AddOppSelBySite(PXAdapter adapter)
{
foreach (OpportunitySiteStatusSelected line in opportunitysitestatus.Cache.Cached)
{
if (line.Selected == true && line.QtySelected > 0)
{
CROpportunityProducts newline = PXCache<CROpportunityProducts>.CreateCopy(Base.Products.Insert(new CROpportunityProducts()));
newline.SiteID = line.SiteID;
newline.InventoryID = line.InventoryID;
newline.SubItemID = line.SubItemID;
newline.UOM = line.SalesUnit;
//newline.AlternateID = line.AlternateID;
//newline = PXCache<SOLine>.CreateCopy(Transactions.Update(newline));
//if (newline.RequireLocation != true || PXAccess.FeatureInstalled<FeaturesSet.warehouseLocation>())
// newline.LocationID = null;
newline = PXCache<CROpportunityProducts>.CreateCopy(Base.Products.Update(newline));
//newline.Qty = line.QtySelected;
cnt = 0;
Base.Products.Update(newline);
}
}
opportunitysitestatus.Cache.Clear();
return adapter.Get();
}
protected virtual void OpportunitySiteStatusFilter_RowInserted(PXCache cache, PXRowInsertedEventArgs e)
{
OpportunitySiteStatusFilter row = (OpportunitySiteStatusFilter)e.Row;
if (row != null && Base.Products.Current != null)
row.SiteID = Base.Products.Current.SiteID;
}
int cnt;
public IEnumerable<PXDataRecord> ProviderSelect(BqlCommand command, int topCount, params PXDataValue[] pars)
{
cnt++;
return Base.ProviderSelect(command, topCount, pars);
}
#endregion
Regards,
R. Muralidharan
You may want to compare your custom SmartPanel aspx with out-of-box Add Stock Item SmartPanel of SalesOrderEntry screen. As #Hybridzz mentioned, most likely you haven’t set AutoSize property. You need to set AutoSize to True when Grid pagination is Enabled.
Hi I am trying to extend the ARPaymentEntry graph so I can do some extra stuff when the user releases the payment.
I have extended the paymententry graph and copied the Release action over like so
public PXAction<ARPayment> release;
[PXUIField(DisplayName = "Release", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Update)]
[PXProcessButton]
public virtual IEnumerable Release(PXAdapter adapter)
{
PXCache cache = Base.Document.Cache;
List<ARRegister> list = new List<ARRegister>();
foreach (ARPayment ardoc in adapter.Get<ARPayment>())
{
if (!(bool)ardoc.Hold)
{
cache.Update(ardoc);
list.Add(ardoc);
}
}
if (list.Count == 0)
{
throw new PXException(Messages.Document_Status_Invalid);
}
Base.Save.Press();
PXLongOperation.StartOperation(this, delegate()
{
if (SyncPaymentToRex(list))
{
ARDocumentRelease.ReleaseDoc(list, false);
}
});
return list;
}
If you look at the PXLongOperation I have a my own method I want to pass before it goes and releases the document.
Now this works for me but there is no user feedback on the screen (e.g. the controls arent disabled, no processing icon appears while its performing the operation etc) and also the screen doesnt reload, I have to manually reload the page before I can see the payment has been release etc
Can I get some help so I can get the page updating and reacting like it usually does on release but with my code in there as well?
Try this
public PXAction<ARPayment> release;
[PXUIField(DisplayName = "Release", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Update)]
[PXProcessButton]
[PXOverride]
public virtual IEnumerable Release(PXAdapter adapter)
{
PXCache cache = Base.Document.Cache;
List<ARRegister> list = new List<ARRegister>();
foreach (ARPayment ardoc in adapter.Get<ARPayment>())
{
if (!(bool)ardoc.Hold)
{
cache.Update(ardoc);
list.Add(ardoc);
}
}
if (list.Count == 0)
{
throw new PXException(Messages.Document_Status_Invalid);
}
Base.Save.Press();
PXLongOperation.StartOperation(this.Base, delegate()
{
if (SyncPaymentToRex(list))
{
ARDocumentRelease.ReleaseDoc(list, false);
}
});
return list;
}