I would like to filter the records shown on the Projects screen. I've been given the directive to see if limiting the Selector for projects (re-writing the PXSelector for the ProjectID?) would also limit the records that show up on the screen, i.e., a user would not be able to navigate to records that aren't displayed by the Selector. I don't think that's the case, as the screen's view is not limited by what is chosen by the Selector - but I wanted to verify this.
Also - as far as limiting the records that show up in the Selector (possibly re-writing the Selector/BQL using a where clause?) - I looked at the source DAC, and for the life of me I can't figure it out. There is a PXSelector on the ContractID, which doesn't use the SubstituteKey that I'm familiar with, and the ContractCD also has several attributes with which I'm unfamiliar - namely the PXRestrictor AND the PXDimensionSelector.
Bottom line:
1.) What's the best way to limit the records for Project shown in the screen's Selector? Can I just add to the PXRestrictor attribute?
2.) Would limiting the Selector's results also limit what the user can navigate to on the screen using the navigation buttons?
Whenever you need to restrict access to primary records on a data entry screen, it's always required to customize both the lookup DAC key field and the primary data view. By design, in Acumatica key fields in DAC and the primary data view are completely independent, that is why it's required to modify both pieces to achieve the desired result.
For example, to deny access to canceled projects on the Projects screen, you should add PXRestrictorAttribute to PMProject's ContractCD field and also re-declare Project primary data view:
public class ProjectEntryExt : PXGraphExtension<ProjectEntry>
{
public class cancelled : Constant<string>
{
public cancelled() : base(ProjectStatus.Cancelled) {; }
}
[PXViewName(Messages.Project)]
public PXSelect<PMProject, Where<PMProject.baseType, Equal<PMProject.ProjectBaseType>,
And<PMProject.nonProject, Equal<False>, And<PMProject.isTemplate, Equal<False>,
And<PMProject.status, NotEqual<cancelled>,
And<Match<Current<AccessInfo.userName>>>>>>>> Project;
[PXMergeAttributes(Method = MergeMethod.Append)]
[PXRestrictor(typeof(Where<PMProject.status, NotEqual<cancelled>>),
"Given Project/Contract '{0}' is cancelled", typeof(PMProject.contractCD))]
public void PMProject_ContractCD_CacheAttached(PXCache sender) { }
}
Related
I’m trying to get the Attributes per ItemClassId to place them on a grid in the look up dialog box (Sales Order Entry).
enter image description here
I’m using the CSAttributeGroupList method:
public PXFilter<SOSiteStatusFilter> ItemSearchClass;
{
[PXViewName("Attributes")]
CSAttributeGroupList<INItemClass, SOSiteStatusFilter> Attributes;
public PXFilter<SOSiteStatusFilter> ItemSearchClass;
[PXViewName("Attributes")]
CSAttributeGroupList<INItemClass, SOSiteStatusFilter> Attributes;
}
I can't find the proper way to bring the attributes from each ItemClassID.
I have tryed:
public CSAttributeGroupList<INItemClass, SOSiteStatusFilter> Attributes;
But it doesn't work either.
I would first put SyncPosition=True on the grid to the right, to ensure that the current inventory item is selected. From there, you would want to use the code that is found in InventoryItemMaintBase:
public CRAttributeList<InventoryItem> Answers;
This would pull the current inventory item's attributes, and should automatically link to the items answers. You may need to mess with getting the grid on the right to refresh after the row is selected for the inventory item, if it does not do it automatically.
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.
This relates to and extends an existing StackOverflow case that has been answered -
How to place a 'Related Entity' lookup on a field (many thanks to Acumatica support on this as well)
I have a custom screen that adds a 'Related Link' control, like this:
And this works as expected. I've also included several custom screens as entities to link to by adding the NoteID, RefNoteID, RelatedEntity fields to their DACs and the PXRefNoteSelector attribute to their view declarations. Everything works as expected, except that the pencil icon doesn't launch to the entity / screen that's been selected.
Is there something (an attribute, perhaps) that I'm missing in the DACs that needs to be added in order to launch to the selected entity?
You need to decorate the custom entity DAC with PXPrimaryGraph attribute and pass the maintenance page graph type in parameter:
[PXPrimaryGraph(typeof(CustomEntityMaint))]
[Serializable]
public class CustomEntityDAC : PX.Data.IBqlTable
{
}
I have a custom table for historical data. It's a one-time data dump directly into SQL so I'd prefer not to have to create a screen for it.
I have three columns in my table:
CompanyID INT
InvoiceNbr NVARCHAR(40)
Amount DECIMAL(19,4)
I create a new Customization Project and added a new DAC to the Code area that is linked to my custom table:
https://i.imgur.com/6mNjdou.png (Screenshot #1)
https://i.imgur.com/IdNLJkR.png (Screenshot #2)
Then I create a Generic Inquiry, but I don't get the Paper Clip and Note icons. I was hoping to use the Paper Clip to upload documents and attach them to the records in my custom table.
So, I added another column to my custom table:
NoteID UNIQUEIDENTIFIER
And I re-added the new DAC which now generates this code:
https://i.imgur.com/QvpWB5X.png (Screenshot #3)
Now I get the Paper clip and Note icons in my Generic Inquiry:
https://i.imgur.com/olCglBB.png (Screenshot #4)
I can add a note or attach a document and the icons change color which makes it seem like the notes and documents got attached to the records. But when I refresh the page, everything goes away. Also, I can tell that nothing is getting stored in the database.
So the Paper Clip and Note icons don't work.
I'm wondering if it's possible to get the Paper Clip and Note icons to work in my Generic Inquiry without building a custom screen. Is this possible?
Tim, for Notes and Attachments to work properly, your NoteID field should be decorated with the PXNote attribute, instead of the default combination of PXDBGuid- and PXUIFieldAttribute. Will everything work as expected after you replace NoteID field declaration with the code snippet below and republish the customization?
public abstract class noteID : PX.Data.IBqlField
{
}
[PXNote()]
public virtual Guid? NoteID { get; set; }
Currently I work with screen ap303000.aspx. I want to add new attribute to tab "Attributes". This tab is binded to view "Answers" which is declared in following way:
[PXViewName(CR.Messages.Answers)]
public CRAttributeList<Vendor> Answers;
little bit digging with metadata viewer in CRAttributeList shows that CRAttributeList is inherited from PXSelectBase and definitely reads records from CSAnswers table:
public class CRAttributeList<TReference> : PXSelectBase<CSAnswers> where TReference : IBqlTable
which gives me hint, that I need to insert something into table CSAnswers. Table CSAnswers by it's structure also doesn't give me enough information what should I put in table CSAnswers in order to have some attribute as bool and available to all Vendors and to turn it on by default?
You need to specify list of Attributes at Vendor Class (AP201000) level first.
Once Vendor class is specified, Attributes specified at Class level will be listed for which value can be assigned.