I have a user field which displays the ARRegister.RefNbr. This user field is contained in the APTran grid. The user actually creates an AR invoice with a custom action, and the new AR document ref nbr is saved to the APTran grid. I wish to craft the user field as a hyperlink (similar to the inventory receipt reference number, in the SO shipment order tab). Should I use a PXSelector control? What are the proper attributes? The goal is to open the AR invoice screen, when the user click on the user field.
There is a generic approach that allows you to add links to the grid cells and isn't based on selectors or anything else. To implement it you have to do the following steps:
1.Define an action in your graph that handles redirects. Something like this:
public PXAction<YourMainDAC> ViewInvoice;
[PXButton]
protected virtual void viewInvoice()
{
ARTran row = Transactions.Current;
string docType = //get Doc Type from the tran record
string refNbr = //get Ref Nbr from the tran record
ARInvoice invoice = PXSelect<ARInvoice,
Where<ARInvoice.docType, Equal<Required<ARInvoice.docType>>,
And<ARInvoice.refNbr, Equal<Required<ARInvoice.refNbr>>>>>
.Select(this, row.YourDocTypeField, row.YourRefNbrField);
// Create the instance of the destination graph
ARInvoiceEntry graph = PXGraph.CreateInstance<ARInvoiceEntry>();
graph.Document.Current = invoice;
// If the invoice is found, throw an exception to open
// a new window (tab) in the browser
if (graph.Document.Current != null)
{
throw new PXRedirectRequiredException(graph, true, "AR Invoice");
}
}
2.In the .aspx page definition add a callback command that corresponds to the new action (substitute grid with the ID of the ARTrans grid on your page):
<px:PXDataSource ID="ds" ... >
<CallbackCommands>
<px:PXDSCallbackCommand Name="ViewInvoice"
Visible="False"
DependOnGrid="grid">
</px:PXDSCallbackCommand>
</CallbackCommands>
</px:PXDataSource>
3.In the grid column where you want to add a link specify link command to point to the above PXDSCallbackCommand:
<px:PXGridColumn DataField="InvoiceNbrOrSomething"
LinkCommand="ViewInvoice">
</px:PXGridColumn>
This is a bit lengthy way of defining a link but, first, it does not impose any constraints on the field where you add a link and it also gives you full control over which graph to open and what to show there.
Note: you may also need to set SyncPosition="true" on the grid control in the aspx.
The example is adapted from the Example 3.4 in the Acumatica T200 training guide. You may want to check it out for some thorough explanations and more info.
If you have a selector linked to a standard Acumatica table such as adding a custom field that contains a selector against InventoryItem or ARInvoice you can set the AllowEdit=True on your field in the page containing your custom field. This will automatically add the hyperlink. If your field does not contain the selector it will not work unless maybe setup for segments.
We have custom tables we added to our project where we wanted hyperlinks. As long as you add the PXPrimaryGraph attribute on your DAC you should be able to do the same for a complete custom page/dac.
We started off using the LinkCommand but the AllowEdit approach keeps the code simple without the need for custom logic to support the link. More complex logic than going to the fields primary graph would require a linkcommand.
Related
I'm trying to add a non-bound (display only) field to the grid in the Approvals screen. This field's value comes from a bound user field in an APInvoice DAC extension class. My problem is that in the RowSelected event for the Approvals screen grid (which is the EPOwned DAC), I'm trying to link back to the Bills and Adjustments screen records via the RefNbr on the Approvals screen grid.
Although the Approvals grid shows it as the RefNbr, the actual field in the EPOwned DAC is a GUID. By some magic (I've pored over the source code for this screen and I cannot find where it creates an APInvoiceEntry Graph to open that screen - even though it does, somehow), it knows to link the clicked-on record in the Approvals grid to the Bills and Adjustments RefNbr / record.
My hunch is that it all has some link with the RefNoteID / NoteID in EPOwned (EPApproval ) - but I cannot find any link between the EPOwned records and the APInvoice / APRegister records.
Does anyone know how to link, through BQL, the EPOwned (EPApproval) record and the APInvoice record that is related to the Refnbr shown in the Approvals grid?
After further research, it is found that the EPOwned DAC (which contains EPApproval) is linked back to the APInvoice DAC via the following:
EPOwned.RefNoteID = APInvoice.NoteID
This will provide the necessary link to find the APInvoice.RefNbr
I'm using Acumatica 2020R1 and I'm adding side panels. Is there anyway, for example on the PO form (PO301000), that I could link a side panel to update when clicking on the Document Details?
I actually discovered a way to do this on Acumatica 2020R1. Here's how I did it:
Edit: I did this for the SOOrderEntry screen, creating a side panel that looks at the SOOrder but uses parameters from the SOLine. This method will work for any two tables with a master-detail relationship by replacing SOOrder & SOLine accordingly.
Part A:
Create virtual fields on the SOOrder DAC that correlate to the SOLine Parameters you need
Update these as lines are selecting/updated using event handlers
pass these fields as the params to your side panel actions as you normally would
While It seems like this alone would work, the side panel actually doesn’t refresh when a new SOLine is selected. We’re going to have to get creative to resolve this issue.
Part B:
Create an additional virtual field on the SOOrder DAC to keep track of which side panel was last opened (I will explain how to track this later). I use a string field to do so. This is especially important if you have more than one side panel on your screen.
Next you want to define a standard graph action that will update this field. Make it Visible = false, since you don’t actually want this button to show. We will call if from behind the scenes later. This is the method I use:
public PXAction<SOOrder> SidePanelCallback;
[PXButton]
[PXUIField(DisplayName = "SidePanelCallback", Visible = false)]
public virtual IEnumerable sidePanelCallback(PXAdapter adapter)
{
SOOrder doc = Base.Document.Current;
if (doc == null) return adapter.Get();
var docExt = doc.GetExtension<SOOrderExt>();
docExt.UsrSidePanelDetailParamsRefresh = adapter.CommandArguments;
return adapter.Get();
}
*Note: docExt.UsrSidePanelDetailParamRefresh is the variable I for the previous step
Now you need a way to trigger your code when your side panel actions are called. To do this we’ll use the following JavaScript
function commandResult(ds, context) {
var ds = px_alls['ds'];
switch (context.command)
{
case ('JustAction'):
ds.executeCallback('GraphAction', 'parameter value');
break;
}
}
You want to replace JustAction with the internal name of your side panel action. it should look like this: NavigateToLayer$DB000028
where DB000028 is the target ScreenID for your sidepanel. If you want to make sure your actions follow the same naming convention, you can check it by adding breakpoints and checking with the debugger.
You should replace GraphAction with the name of the graph action you created in the previous step. In our case SidePanelCallback
Then finally for the parameter value pass whatever you’d like to use to identify the opened side panel. This will be stored in UsrSidePanelDetailParamRefresh, or your equivalent.
You can create a case statement for each side panel you want this functionality for, so you only have to do all this once per graph!
Then finally, attach your script to the client event ‘commandPerformed’ on your data source.
Next we will define another graph action to do the actual refresh of the side panel. Mine looks like this:
public PXAction<SOOrder> SidePanelRefresh;
[PXButton]
[PXUIField(DisplayName = "SidePanelRefresh", Visible = false)]
public virtual IEnumerable sidePanelRefresh(PXAdapter adapter)
{
SOOrder doc = Base.Document.Current;
if (doc == null) return adapter.Get();
var docExt = doc.GetExtension<SOOrderExt>();
Dictionary<string, string> parameters = new Dictionary<string, string>();
string[] args = adapter.CommandArguments.Split('*');
switch (docExt.UsrSidePanelDetailParamsRefresh)
{
case ("Component Inventory"):
parameters.Add("Kit", args[0]);
parameters.Add("SiteID", args[1]);
throw new PXRedirectToGIRequiredException("Component Inventory", parameters) { Mode = PXBaseRedirectException.WindowMode.Layer };
}
return adapter.Get();
}
Note that for my use case the parameters I am passing cannot contain the character ‘*’, so I use it as a delimiter
And again, you can define as many case statements as you need for each of your dynamic side panels.
And now finally we need javascript again to call this action. Using event handlers are too slow for this type of refresh, they will always pass the old SOLine parameters. If you know a way to avoid javascript here, I’d love to hear it, as doing callbacks like this does have a heavy overhead. Because of this, be careful where you attach this script to, I actually attach it to the grid’s RowSelectorClick client event rather than AfterRowChange for this reason, and train my users to select rows in this way (by clicking the arrow to the left of each row) so we don’t impact the rest of the company for the minority that use this feature. I’d recommend this if you're working on a high traffic screen such as SO301000.
Anyway, here’s the javascript:
function Side_Panel_Refresh() {
px_alls["ds"].executeCallback("SidePanelRefresh", this.func.caller.arguments[2].row.getCell("InventoryID").getValue() + "*" + this.func.caller.arguments[2].row.getCell("SiteID").getValue());
}
SidePanelRefresh: Name of our graph action from the previous step.
InventoryID & SiteID are my parameters.
And again, using ‘*’ as a delimiter.
Anyway, I hope this helps anyone looking to make dynamic side panels. If anyone has improvements on this method I’d love to hear constructive feedback.
Happy development!
It is a new feature. Side panel were restricted to generic inquiry. In version 2020 you still need a generic inquiry to configure the side panel but they can be viewed in data entry forms too.
The feature is implemented with wizards so it's pretty limited in what it can do. Currently you are limited to the choices available in navigation parameter. It seems like it's not possible to add details entity there. Maybe there's a way but I'm not aware of it.
Check documentation reference link for User Interface: Side Panels for Data Entry Forms (content copied below):
https://help-2020r1.acumatica.com/Help?ScreenId=ShowWiki&pageid=a11aa71b-5f18-4c8f-9c69-350f2c7a89f4
Adding a Side Panel to the Data Entry Form
A customizer can add multiple side panel navigation paths to a data entry form.
To add a side panel, the customizer navigates to the Customization Projects (SM204505) form and creates a new project.
In the Customization Project Editor, the customizer selects the screen for which the side panel is planned to be added.
On the Screens > Form ID > Actions, the customizer adds an action of the Navigation:
Side panel type (1), specifies general settings (2), adds destination screen (3), and specifies parameters (4), if needed (for instance, in the screenshot below, Business Account was selected as a parameter so that the side panel displayed the cases of the specific customer selected on the data entry form).
Example of a side panel on the Customers form
To apply the changes, the customizer must publish the created customization project.
I didn't find any documentation on advanced customization.
I need to customize Sales Order in a more advanced way than basic customization offered in UI Customization Menu.
For example, add some sublists under Item section with a relation one to many between Item and Messages (1), or add a new button to display a new page to create a messages record page linked with the selected item (2).
Thank you!
Explicative Screenshot
NetSuite's current API does not accommodate the use of custom buttons inside sublists (unless you are using the native REFRESH or MARKALL button within a custom Suitelet).
An alternative solution to bridge the gap between the item record and the message record you want to create, why not do the following:
First, create a SUITELET which opens as a CHILD window of the current window on FIELDCHANGE of the item.
Second, add a basic TEXTAREA field and include the SUBMIT button to the suitelet.
On SUBMIT of the Suitelet, ccreate a new message record, attaching it to the current sales order.
Lastly - ensure the Suitelet closes itself.
Note: This solution will only work on EDIT of a sales order. It will not work on CREATE because no order ID would exist at the time you submit a message. (You need the order ID to attach the message to).
I hope this suggestion is clear.
You can do your customization on BeforeLoad in a UserEvent script : you can access the current form from the context and do somethings like adding buttons, sublists, hiding fields...
You can not add a button, but you can add a link pointing to a Suitelet where you can execute your actions. As an example, I created a sublist with a View link that points out to the line's custom record:
To define the field:
var idField = appStepsSublist.addField({
id: 'id',
type: serverWidget.FieldType.URL,
label: 'View'
});
idField.linkText = 'View';
To set Value :
var viewUrl = url.resolveRecord({
recordType: 'customrecord_nab_approval_step',
recordId: appStep.id,
isEditMode: false
});
usedSublist.setSublistValue({
id: 'id',
line: index,
value: viewUrl
});
In your case, you can use the URL module to get a Suitelet link and append to it the needed data from your current line.
I'm new to Acumatica custom development and I'm trying to do something that I would assume is very simple. I have a Selector control (DataClass: FSServiceOrder, DataField: BranchLocationID) in the Sales Order header that allows user to set the Branch Location. Below, in an Inventory grid, I merely want to set the Warehouse field in the new row equal to the value of the above-mentioned selector. I can set Warehouse with a hard-coded value, but I have no idea how to reference the selector or get it's value, as it seems to be outside the scope of the passed PXCache object:
protected void FSSODetPart_RowSelected(PXCache cache, PXRowSelectedEventArgs e)
{
string BranchLocationID = "" // Not sure how to get this value
var row = (FSSODetPart)e.Row;
cache.SetValueExt(row, "SiteID", BranchLocationID);
}
I was hoping I could simply reference all UI controls similar to ASP.NET, but that doesn't seem to be the case. Any help is appreciated. Getting a value from the screen seems to be fundamental but I cannot find any help in the documentation. Thanks.
In Acumatica screen controls are bound to DataViews.
DataViews contains DAC records. Common practice is to get the value from the current DAC record in the bound DataView.
Use the current object of the DataView holding FSServiceOrder DAC records:
string BranchLocationID = myDataview.Current.BranchLocationID;
If you don't know the DataView name, on the WebSite hold Ctl+Alt and click on the BranchLocationID UI field. A popup will appear showing the DataView name.
Getting the current object from the DAC collections should work too but it's preferable to use the DataView:
string BranchLocationID = Base.Caches[typeof(FSServiceOrder)].Current.BranchLocationID;
Also make sure you set the CommitChanges attribute to true on BranchLocationID form field in the Aspx file. This ensures that current object will fire events in the back-end when it's value is changed.
<px:PXSelector ID="edBranchLocationID" runat="server"
DataField="BranchLocationID" CommitChanges="True" />
I have created a custom field type as it is there in sharepoint OOTB, the difference is only that the end user does not need to check the name i.e I have replaced it with DropDownList. The dropdownlist suggest the no. of users available in the web site for that I have created a FieldClass which inherits from SPFieldUser and a FieldControlClass which inherits from UserField. It is working fine in all conditions i.e when I create a List or Document Libarary it shows me the DropDownList
with respective users after saying OK it creates an item for me. I have overriden a Value property in FieldControlClass as follows,
public override object Value
{
get
{
SPUserCollection userscollection = rootWeb.SiteUsers;
//ddlInfoBox is a DropDownList to which I have Binded the collection of users in the form of string
SPUser user = userscollection.Web.EnsureUser(this.ddlInfoBox.SelectedValue);
SPFieldUserValue userval = new SPFieldUserValue(user.ParentWeb, user.ID, user.LoginName);
return userval;
}
set
{
SPFieldUserValue userval = (SPFieldUserValue) this.ItemFieldValue;
this.ddlInfoBox.SelectedValue = userval.Lookupvalue; //Here look up value is nothing but a Login name e.g In-Wai-Svr2\tjagtap
}
}
Due to above property the Custom Field's Value for this current ListItem will be stored as SPFieldUserValue e.g 27#;In-Wai-Svr2\tjagtap.
The main problem is here, when this particular ListItem is shown in the list page views e.g on AllItems.aspx or the custom view pages associated with it, it shows the
number as 27 as a FieldValue insted of HyperLink with text as "In-Wai-Svr2\tjagtap" and PostBackURL as "/_layouts/userdisp.aspx?ID=27".
When I edit this Item it makes the respective value selected in the dropdownlist, also while viewing this item i.e on DispForm.aspx it also shows the hyperlink. I have
acheived it by writting a custom logic in createchildcontrol() method i.e by using ControlMode if it is New or Edit then fill the dropdown list, if it is Display then get the ItemFieldValue Type Cast it into SPFieldUserValue and get corresponding lookupid and value for making the URL and showing Text of the HyperLink.
I have spent a lot of time on searching and bringing the HyperLink as the user name with navigation insted of UserID (27) as a string on the list view pages e.g AllItem.aspx but to no avail, then after a lot of research I found that there might be a way of achieving such kind of functionality by using field type definition xml file where there is a provision to define a DisplayPatteren as you wish by specifying the html code. But here is a problem How can I get the UserID (27) with respective UserName e.g In-Wai-Svr2\tjagtap inorder to make an anchor tag like In-Wai-Svr2\tjagtap which will solve my problem. I have hard coded this anchor tag within the Default case statement of a switch under DisplayPatteren but it shows me the field value on AllItems.aspx as
In-Wai-Svr2\tjagtap27 i.e the value defined in xml file is concatenating with the string value (27).
Please help me to resolve the above mentioned 2 issue. I am really in need of solving this problem ASAP.
Thanks & Regards,
Tejas Jagtap
Have u tried to override the GetFieldValueAsHtml() method in the the custom field class or maybe the RenderFieldForDisplay() method in the custom field control class.
Can you use the DisplayPattern CAML from the User field type?