Acumatica - NonStockItem Screen on AlloAddNew in Page - acumatica

Good Day!
I have a PXSelector field in DAC that only show non-stock items
public abstract class inventoryID : IBqlField { }
[PXDBInt]
[PXSelector(typeof(Search2<InventoryItem.inventoryID,
LeftJoin<RECOInventoryItem, On<RECOInventoryItem.inventoryID, Equal<InventoryItem.inventoryID>>>,
Where<RECOInventoryItem.inventoryID, IsNull,
And<InventoryItem.itemType, Equal<ItemType.NON_STOCK>,
And<InventoryItem.stkItem, Equal<False>>>>>),
typeof(InventoryItem.inventoryCD),
typeof(InventoryItem.descr),
DescriptionField = typeof(InventoryItem.descr))]
[PXUIField(DisplayName = "Inventory")]
[PXDefault(PersistingCheck = PXPersistingCheck.Nothing)]
public virtual int? InventoryID { get; set; }
I also have a selector in my page that allows edit and allows add new methods
<px:PXSelector ID="edInventoryID" runat="server" DataField="InventoryID" AllowEdit="true" AllowAddNew="true">
</px:PXSelector>
Now, my problem lies with the edit(pencil) button in the User Interface. When I'm clicking the edit button, with the item that i want to edit, I am being redirected to Non-Stock Items page,
But when I want to add another Non-stock Item by using the edit(pencil) button, I am redirected to the Stock Items page.
I already set my PrimaryGraph.
[PXPrimaryGraph(typeof(PropertyMaint))]
public class RECOInventoryItem : Audit, IBqlTable
How do you set the AllowAddNew redirect to Non-Stock Item Page instead of the Stock Item Page.
How do you override the PxPrimaryGraph of the InventoryItem so that the graph that i'm redirecting to is Non-stock Item, instead of Stock Item.
Thank you so much for the advice and suggestions.

PXPrimaryGraph can handle binding 1 DAC to multiple graphs using BQL conditions on the DAC fields as shown on InventoryItem DAC:
[PXPrimaryGraph(new Type[]
{
typeof(NonStockItemMaint),
typeof(InventoryItemMaint)
},
new Type[]
{
typeof(Where<InventoryItem.stkItem, Equal<False>>),
typeof(Where<InventoryItem.stkItem, Equal<True>>)
})]
This works well with AllowEdit functionality because the InventoryItem.stkItem field used for the graph redirection is already populated in the record you want to edit.
When you create a new record with AllowAddNew though I suspect the field InventoryItem.stkItem will be null or always defaulted to the same value by PXDefault attribute. This would have the side effect of always redirecting to the same graph.
As you can probably guess AllowAddNew doesn't have any options or programmatic interface to influence it's behavior so it is pretty limited in what it can do. For that reason I don't think you can override it's behavior.
This leave creating your own action button as the only option I know to achieve your use case. You can style the button to show a + icon and no text like AllowAddNew does. You would still be limited on where you can place that button because Selector popup windows also lack options and programmatic interface.

Related

How do I force user input to commit and save automatically when executing an action?

I have a custom graph, in this case called MyGraph. The page contains a Form Tab in a similar fashion to the Stocked Items page in which the header is a few fields of my DAC (MyDAC), and a form on the 1st tab is a form of my additional fields. The views, for the sake of discussion, are MyView and MyCurrentView.
On the Stocked Items page, I can set a value in the ItemSettings view (General Settings tab) and then execute an action which also happens to save my user input. In my custom page, the user input of the similar "MyCurrentView" tab is lost when I execute an action. I have reviewed the training material once again for T230 Actions, but I cannot seem to overcome this problem. The code sample below shows 2 simplified methods of how I execute the action "ConvertIt". In both cases during a Debug, both myDAC and myDAC2 contain the stale data, indicating that the view was not updated as expected per CommitChanges in the Button attribute and in the DataSource Callback Command. The user input that is not saved is entered via a field presented to the UI only in MyCurrentView.
The T230 training guide indicates that I should set the AutoCallBack Behavior in the action bar, which appears to be possible for a grid, but not for a form. The example from the training guide is:
<ActionBar>
<CustomItems>
<px:PXToolBarButton Text="Complete">
<AutoCallBack Command="Complete" Target="ds">
<Behavior CommitChanges="True" ></Behavior>
</AutoCallBack>
</px:PXToolBarButton>
</CustomItems>
</ActionBar>
Since I am hanging my action off the Action menu via action.AddMenuAction(convertIt);, I do not seem to have a way to specify the behavior as described in the training guide.
Views Defined
public PXSelect<MyDAC> MyView;
public PXSelect<MyDAC, Where<MyDAC.tagID, Equal<Current<MyDAC.tagID>>>> MyCurrentView;
Action Defined - Method 1
#region convertIt Action
public PXAction<MyDAC> convertIt;
[PXUIField(DisplayName = "My DAC Label", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Select)]
[PXButton(CommitChanges = true)]
public virtual void ConvertIt()
{
this.Save.Press(); // Seems to make no difference in saving the unsaved user entry
MyDAC myDAC = MyView.Current;
MyDAC myDAC2 = MyCurrentView.Current;
** DO THE WORK OF THE ACTION HERE **
}
#endregion
Action Defined - Method 2
#region convertIt Action
public PXAction<MyDAC> convertIt;
[PXUIField(DisplayName = "My DAC Label", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Select)]
[PXButton(CommitChanges = true)]
public virtual IEnumerable ConvertIt(PXAdapter adapter)
{
MyDAC myDAC = MyView.Current;
MyDAC myDAC2 = MyCurrentView.Current;
** DO THE WORK OF THE ACTION HERE **
return adapter.Get();
}
#endregion
ASPX Datasource Defined
<px:PXDataSource ID="ds" runat="server" Visible="True" Width="100%"
TypeName="MyGraph"
PrimaryView="MyView">
<CallbackCommands>
<px:PXDSCallbackCommand CommitChanges="True" Name="ConvertIt" ></px:PXDSCallbackCommand>
</CallbackCommands>
</px:PXDataSource>
In short, user input is lost when I complete my action. I have tried including this.Save.Press() at the beginning of my action, but I already save at the end of my action. Regardless, a Debug does not show my user entered values in the view, although I expected the commit changes on the button and in the data source to commit those changes from the client before proceeding with the action.
What have I left out of the C# code or ASPX page definition that is preventing my UI updates to be applied? Interestingly enough, UI changes in the MyView view ARE applied, but not changes in MyCurrentView unless I literally press the save button manually before selecting the action from the menu.
I ran into this problem recently as well. It’s easy to force the page to post back to server when opening a smart panel off of an action, but if there is no panel opening how do you force a post of data that was changed in fields that don’t commit on update?
The only way I’ve found to accomplish this is by editing the ASPX and adding StartNewGroup="true" and CommitChanges="true" to the PXDSCallbackCommand for the Action Folder that the action is added to.
<px:PXDataSource ID="ds" runat="server" Visible="True" Width="100%"
TypeName="MyGraph"
PrimaryView="MyView">
<CallbackCommands>
<px:PXDSCallbackCommand CommitChanges="True" Name="ActionFolder" StartNewGroup="True" />
</CallbackCommands>
</px:PXDataSource>
For the action, you should use the second method.
public PXAction<MyDAC> convertIt;
[PXUIField(DisplayName = "My DAC Label", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Select)]
[PXButton(CommitChanges = true)]
public virtual IEnumerable ConvertIt(PXAdapter adapter)
{
MyDAC myDAC = MyView.Current;
MyDAC myDAC2 = MyCurrentView.Current;
**DO THE WORK OF THE ACTION HERE **
return adapter.Get();
}
public MyGraph()
{
actionFolder.AddMenuAction(convertIt);
}
It is not possible to save the focused field value. Value is saved when the control lose focus. However when the focus goes from an uncommitted field to an action button (clicked by user) the current field value is lost. Not sure this is the issue here but keep in mind that the user has to tab out of a control editor with uncommitted value before clicking on an action button or else the value is lost.

How come PXUIField attribute disables text edit boxes on a processing page in 2019 R1?

I'm trying to setup a processing page that will update a SOShipment document lines with new data entered on the PXFilteredProcessing page. When declaring a PXString (virtual field), if I add the PXUIfield attribute, the field becomes read only.
Here is the DAC declaration showing the actual problem. The page I use is a simple PXFilteredProcessing page with a completely custom page that was made in Visual Studio.
#region StockRow
public abstract class stockRow : IBqlField { }
[PXString()]
[PXUIField(Enabled = true)]
public virtual String StockRow { get; set; }
#endregion
#region StockFlag
public abstract class stockFlag : IBqlField { }
[PXString]
public virtual String StockFlag { get; set; }
#endregion
The page has the fields defined as follow :
<px:PXTextEdit ID="edStockRow" runat="server"
DataField="StockRow" Enabled ="true" >
</px:PXTextEdit>
<px:PXTextEdit ID="edStockFlag" runat="server"
DataField="StockFlag" Enabled ="true">
</px:PXTextEdit>
<px:PXGridColumn DataField="StockRow" Width="200px" >
</px:PXGridColumn>
<px:PXGridColumn DataField="StockFlag" Width="200px">
</px:PXGridColumn>
Should the PXUIField really make the field become read only or is there something I am not getting?
PS: I know I can re-enable the field on the RowSelected even, I'm mostly looking for an explanation as to why this is happening.
if I add the PXUIfield attribute, the field becomes read only
Are you sure that this is the operation making the field read-only?
Typically all processing screen detail fields are disabled except the Selected column. I believe this is a behavior introduced by the use of PXProcessing type data view. Going against that behavior will likely not yield the desired result.
If the screen needs detail fields to be editable (except Selected column) I would advise not to create a processing screen. Using a PXSelect data view instead will provide correct behavior for the editable fields.

How to enable a hyperlink in the form text control?

I have a requirement to accept an URL on a text box and enable hyperlink on the control. Grid columns are allowing to enter a value and mark the column as hyperlink where it is not allowing to do on an editable text control. Is there a way to open the URL by clicking on the text control?
I have examined the Web entry control in Business account aspx source code and used PXLinkEdit Control for accepting the URL and allow to open the page. Even though it is not hyperlinking the text, it allows opening the URL through an action button, which is part of the control
<px:PXLinkEdit ID="edURL" runat="server" DataField="UsrURL" CommitChanges="True" />
Similarly, you could try a PXButton with optional ImageKey and navigate to your URL using Redirect4: (or Redirect0: if the URL is in same domain):
public PXAction<DAC> ViewOnWeb;
[PXUIField(DisplayName = "View On Web",
MapEnableRights = PXCacheRights.Select,
MapViewRights = PXCacheRights.Select)]
[PXButton(ImageKey = PX.Web.UI.Sprite.Main.World)]
protected virtual IEnumerable viewOnWeb;(PXAdapter adapter)
{
if (this.DAC.Current != null)
{
throw new PXException("Redirect4:" +
string.Format("http://my.site.com/search?Id={0}", DAC.code));
}
return adapter.Get();
}

How to place a 'Related Entity' lookup on a field

In the Activity / Task screen (cr306020), there is a 'Related Entity' field with a PXSelector lookup as well as a pencil for opening the screen of the related entity:
I'd like to know if there is a way to do this for a custom field. I've looked at the source code for the field (it's EPActivity.Source in the DAC), but I see nothing that puts these attributes on that field. No PXSelector or anything similar.
The example below shows how to add the Related Entity field on the Opportunities (CR304000) screen. Please be aware, PXRefNoteSelector control used in this sample is not currently supported by the Layout Editor in Acumatica Customization Manager. I used Opportunities to simplify and shorten the example. Unfortunately, for now you can only add the Related Entity field on a custom screen.
Now let's move forward to the sample:
Implement extension for the CROpportunity DAC to declare the database bound UsrRefNoteID and unbound RelatedEntity fields. Related Entity's NoteID will be stored in UsrRefNoteID, and RelatedEntity will be used to display Related Entity's user-friendly description:
public class CROpportunityExt : PXCacheExtension<CROpportunity>
{
#region UsrRefNoteID
public abstract class usrRefNoteID : IBqlField { }
protected Guid? _UsrRefNoteID;
[PXDBGuid]
[PXParent(typeof(Select<CRActivityStatistics,
Where<CRActivityStatistics.noteID, Equal<Current<CROpportunityExt.usrRefNoteID>>>>), LeaveChildren = true)]
public Guid? UsrRefNoteID
{
get
{
return _UsrRefNoteID;
}
set
{
_UsrRefNoteID = value;
}
}
#endregion
#region Source
public abstract class relatedEntity : IBqlField { }
[PXString(IsUnicode = true)]
[PXUIField(DisplayName = "Related Entity Description", Enabled = false)]
[PXFormula(typeof(EntityDescription<CROpportunityExt.usrRefNoteID>))]
public string RelatedEntity { get; set; }
#endregion
}
Create extension for the OpportunityMaint BLC to decorate its primary Opportunity data view with PXRefNoteSelectorAttribute. The PXRefNoteSelectorAttribute is required for the Edit (pencil) and Lookup buttons to work on your custom Related Entity field:
public class OpportunityMaintExt : PXGraphExtension<OpportunityMaint>
{
[PXCopyPasteHiddenFields(typeof(CROpportunity.resolution))]
[PXViewName(Messages.Opportunity)]
[PXRefNoteSelector(typeof(CROpportunity), typeof(CROpportunityExt.usrRefNoteID))]
public PXSelect<CROpportunity> Opportunity;
}
On Aspx page, add PXRefNoteSelector control with the DataField property set to RelatedEntity and NoteIDDataField to UsrRefNoteID.
For the EditButton, LookupButton and LookupPanel tags, use the primary data view name decorated with the PXRefNoteSelector attribute (Opportunity in the code snippet below)
<pxa:PXRefNoteSelector ID="edRefEntity" runat="server" DataField="RelatedEntity" NoteIDDataField="UsrRefNoteID"
MaxValue="0" MinValue="0" ValueType="Guid" CommitChanges="true">
<EditButton CommandName="Opportunity$Navigate_ByRefNote" CommandSourceID="ds" />
<LookupButton CommandName="Opportunity$Select_RefNote" CommandSourceID="ds" />
<LookupPanel DataMember="Opportunity$RefNoteView" DataSourceID="ds" TypeDataField="Type" IDDataField="NoteID" />
</pxa:PXRefNoteSelector>
Hide 3 actions generated by the PXRefNoteSelector attribute from the form toolbar. Use the same primary data view name decorated with the PXRefNoteSelector attribute (Opportunity in the code snippet below) as in the step above:
<CallbackCommands>
...
<px:PXDSCallbackCommand Name="Opportunity$Navigate_ByRefNote" Visible="False" />
<px:PXDSCallbackCommand Name="Opportunity$Select_RefNote" Visible="False" />
<px:PXDSCallbackCommand Name="Opportunity$Attach_RefNote" Visible="False" />
</CallbackCommands>
You might also need to implement your own EntityDescription operator, since at the time this example was created, it had internal access modifier and was not available outside of the PX.Objects.dll:
public class EntityDescription<RefNoteID> : BqlFormulaEvaluator<RefNoteID>, IBqlOperand
where RefNoteID : IBqlField
{
public override object Evaluate(PXCache cache, object item, Dictionary<Type, object> pars)
{
Guid? refNoteID = (Guid?)pars[typeof(RefNoteID)];
return new EntityHelper(cache.Graph).GetEntityDescription(refNoteID, item.GetType());
}
}
And finally... screenshot of the customized Opportunities screen with a brand-new Related Entity field:

WU app XAML binding to Button grid from custom collection

I'm quite new to XAML trying to make grid with Toggle buttons
Something like this:
<GridView ItemSource="{Binding ButtonCollection}">
<GridView.ItemTemplate>
<DataTemplate>
<ToggleButton Content="{Binding Label}" IsEnabled="{Binding BEnabled}" IsChecked="{Binding BChecked, Mode=TwoWay}"/>
<DataTemplate>
</GridView.ItemTemplate>
</GridView>
I have
ObservableCollection<ButtonClass> ButtonCollection
also class for buttons
class ButtonClass
{
public string Label {get; set;}
public bool BEnabled {get; set:}
public bool BChecked {get;set;}
}
binding works when page loads buttons are displayed from ObservableCollection
But I want collection to update when button IsChecked value changes
also is there any way to bind function to click method like:
Click="{Binding DoWhenClicked}"
Now it just results in error I think that is because DoWhenClicked isn't in ItemSource.
Summary:
I want to have Grid of toggle buttons that binds to some sort of list/array/collection of data with label, checked status, enabled status.
When toggle button is checked I want it to reflect in my collection.
Also I want to bind event to Click method so that i can perform operations like disable some Toggle Buttons when other button is checked.
What is good way to do this.
I noticed that you asked several question about Template 10 before, so I supposed that you also used Template 10 here for MVVM pattern.
binding works when page loads buttons are displayed from ObservableCollection But I want collection to update when button IsChecked value changes
In Template 10 project, if you want to get notified when parameter in the data model (here means your ButtonClass), you can derive your class from BindableBase. If you check the BindableBase class you will find that it has done the work of INotifyPropertyChanged, it will be much easier here deriving from BindableBase directly rather than implementing INotifyPropertyChanged by yourself.
also is there any way to bind function to click method
Also I want to bind event to Click method so that i can perform operations like disable some Toggle Buttons when other button is checked.
Instead of Click event, I personally recommend you to using Command in MVVM pattern, and you may want to know which Button is clicked, so you can use CommandParameter here. I created a blank template 10 project and here is my sample:
<GridView x:Name="gridView" RelativePanel.Below="pageHeader" Margin="16,12,0,0" ItemsSource="{x:Bind ViewModel.ButtonCollection}">
<GridView.ItemTemplate>
<DataTemplate>
<ToggleButton Width="100" Content="{Binding Label}" IsEnabled="{Binding BEnabled}"
IsChecked="{Binding BChecked, Mode=TwoWay}" Command="{Binding ToggleBtnClickCommand}"
CommandParameter="{Binding Label}" />
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
The ButtonClass is like this:
public class ButtonClass : BindableBase
{
public string Label { get; set; }
public bool BEnabled { get; set; }
private bool _bChecked;
public bool BChecked
{
get { return _bChecked; }
set
{
_bChecked = value;
RaisePropertyChanged();
}
}
public ICommand ToggleBtnClickCommand { get; set; }
}
And the MainPageViewModel:
public class MainPageViewModel : ViewModelBase
{
public ObservableCollection<ButtonClass> ButtonCollection;
public MainPageViewModel()
{
ButtonCollection = new ObservableCollection<ButtonClass>();
for (int i = 0; i < 20; i++)
{
ButtonCollection.Add(new ButtonClass
{
Label = "TButton " + i,
BChecked = true,
BEnabled = true,
ToggleBtnClickCommand = new DelegateCommand<string>(ToggleBtnClicked)
});
}
}
public void ToggleBtnClicked(string obj)
{
var buttonLable = obj;
}
}
In case you need to check my sample, you can find my demo here.
There are a few concepts that are missing from your code that you need to properly implement for this to work
INotifyPropertyChanged -> Your ButtonClass class acts as a view model for your view. As such, for values to properly update UI and the other way around you need your class to implement it and all your properties to raise the PropertyChanged event when their values change (in the setter)
2-Way bindings -> If you want your ButtonClass instance to update when you click the button and you want your button to change state if you change the property in the ButtonClass, your bindings need to be Mode=TwoWay. You might want to explore the new Bindings {x:Bind} for better performance
Commands -> To bind the click event, you need to use the Command property. You would need to create a ICommand implementation, and create a property in your ButtonClass. The use Command property of the Button to bind to that Command.
Essentially, everything I mentioned are components of an MVVM framework, of which there are many out there. Here are some of the more popular ones: Caliburn, MVVMCross, Prism.
There's also a great starter kit for Windows 10 that will definitely kick start your app and contains a lot of this classes already built - Template10

Resources