How to add a memo / formatted text field ala Cases - acumatica

I'd like to add a tab to a screen which contains a memo / formatted text area the way the Cases screen does, e.g.:
Adding a tab is straightforward, no help necessary there, but I don't remember seeing anything about how to add this type of text area in the training courses. If there's an example I'd appreciate a point in the right direction.

You can add the RichTextEditor manually to your aspx file.
<px:PXTabItem Text="Test">
<Template>
<px:PXRichTextEdit runat="server" AllowLoadTemplate="false"
AllowAttached="true" AllowSearch="true" AllowMacros="true"
AllowSourceMode="true" DataField="YOURFIELD" ID="edDescription"
Style='width:100%;'>
<AutoSize Enabled="True" />
</px:PXRichTextEdit>
</Template>
</px:PXTabItem>
Make sure you Tab has the correct Datamember where your field used on the RichTextEditor is located.
<px:PXTab DataMember="Document" ID="tab" runat="server" Height="540px" Style="z-index: 100;" Width="100%">
Also you could mark your Field used on the RichTextEditor as PXDBText.
#region YourField
public abstract class yourField : IBqlField { }
protected String _yourField;
[PXDBText(IsUnicode = true)]
[PXUIField(DisplayName = "YOURFIELD")]
public virtual String YourField
{
get
{
return this._yourField;
}
set
{
this._yourField = value;
}
}
#endregion

Related

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.

Displaying GI in tab on any screen

I want to display a GI on one of the tab in any screen.
For example, there is a new custom GI for OrderMargin which I want to display on SO screen on a new tab that will show the Order Margin for a particular order only.
OrderMargin is simple GI with SOOrder, SOLine and InventoryItem table joins and few columns required columns with margin calculations.
Can anyone suggest?
Let's say you've created a GI called SalesOrderMargin with 2 hidden parameters:
To embed this GI into the Sales Orders page, you should follow the steps below:
Declare new unbound field for SOOrder to return absolute URL for the SalesOrderMargin GI:
public class SOOrderExt : PXCacheExtension<SOOrder>
{
public abstract class marginGiUrl : IBqlField { }
[PXString]
[PXUIField(Visible = false)]
public string MarginGiUrl
{
get
{
if (string.IsNullOrEmpty(Base.OrderType) ||
string.IsNullOrEmpty(Base.OrderNbr)) return string.Empty;
string inqName = "SalesOrderMargin";
var url = new StringBuilder(PXGenericInqGrph.INQUIRY_URL)
.Append("?name=").Append(inqName)
.Append("&SOOrderType=").Append(Base.OrderType);
.Append("&SOOrderNbr=").Append(Base.OrderNbr);
.Append("&hidePageTitle=true");
return PX.Common.PXUrl.SiteUrlWithPath().TrimEnd('/') +
url.ToString().Remove(0, 1);
}
}
}
On the Sales Orders screen, add new tab with a PXSmartPanel container set up to render as an iframe:
<px:PXTabItem Text="Margins" >
<Template>
<px:PXSmartPanel runat="server" ID="panelMarginGI" RenderIFrame="True"
AutoSize-Enabled="true" SkinID="Frame" LoadOnDemand="true"/>
</Template>
</px:PXTabItem>
Place input control for the custom SOOrder unbound field declared in step 1 somewhere in the Sales Orders' top level PXFormView container (input control will always be hidden from the users and is only required to assign source URL for the PXSmartPanel):
<px:PXFormView ID="form" runat="server" DataSourceID="ds" Width="100%"
DataMember="Document" Caption="Order Summary"...>
<Template>
...
<px:PXTextEdit ID="edMarginGiUrl" runat="server" DataField="MarginGiUrl" />
</Template>
</px:PXFormView>
In SO301000.aspx insert JavaScript code to assign source URL for the PXSmartPanel:
<script type="text/javascript" language="javascript">
function commandResult(ds, context) {
var commands = ["ReloadPage", "Save", "Cancel", "Insert", "First", "Previous", "Next", "Last"];
if (commands.indexOf(context.command) >= 0) {
var marginGiUrl = px_alls["edMarginGiUrl"];
var smartpanel = px_alls["panelMarginGI"];
if (marginGiUrl || smartpanel) {
var url = marginGiUrl.getValue();
smartpanel.setPageUrl(url);
smartpanel.repaint();
}
}
}
</script>
Subscribe to the CommandPerformed event of PXDataSource to invoke the commandResult JavaScript function:
<px:PXDataSource ID="ds" runat="server" Visible="True" Width="100%" TypeName="PX.Objects.SO.SOOrderEntry" PrimaryView="Document">
<ClientEvents CommandPerformed="commandResult" />
...
</px:PXDataSource>
And this is how your SalesOrderMargin GI should appear on the Sales Orders screen:

Acumatica Processing Screen with Parameter Doesn't Refresh

Issue Summary:
The processing logic on my new screen is working but the page doesn't show any feedback to the user (e.g. Timer doesn't show, No Red/Green checkboxes, checkboxes aren't disabled)
Issue Detail:
I'm creating a processing screen that requires separate information from the user to be used by the processing delegate. The business logic works, but the user experience isn't like other processing screens. When you click Process there is nothing shown to the user. Normally the page refreshes the grid by disabling and showing only the selected items being processed, in addition to the long operation timer being added (then replaced by a green check or red x based on whether the process was successful or failed). When I click process all the entire grid's selected column is checked, but again nothing else changes (e.g. no timer, status, nor is the grid disabled). Ultimately both process and process all perform the business logic but the user doesn't see anything to indicate said success/failure.
The screen shows all customer locations because the process is updating some statistics that we are keeping for each location based on orders that exist for each location.
My Graph
public class CalculateLocationStatsProcess : PXGraph<CalculateLocationStatsProcess>
{
public PXCancel<LocationStatsFilter> Cancel;
public PXFilter<LocationStatsFilter> filterLocStat;
public PXFilteredProcessingJoin<Location,
LocationStatsFilter,
InnerJoin<Customer, On<Customer.bAccountID, Equal<Location.bAccountID>>>,
Where<True, Equal<True>>,
OrderBy<Asc<Customer.acctCD, Asc<Location.locationCD>>>> processLocations;
public CalculateLocationStatsProcess()
{
}
public virtual void LocationStatsFilter_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
processLocations.SetProcessDelegate(
delegate (List<Location> list)
{
var newList = new List<Location>();
foreach (Location locLp in list)
{
newList.Add(locLp);
}
CalculateLocationStatsProcess.updateLocationStats(newList, filterLocStat.Current);
}
);
}
public static void updateLocationStats(List<Location> locations, LocationStatsFilter filter)
{
var graph = new PXGraph();
var locStats = new StatsHelper(graph, filter.TargetDate);
bool erred = false;
for (int iLp = 0; iLp < locations.Count; iLp++)
{
Location locationLp = locations[iLp];
PXProcessing<Location>.SetCurrentItem(locationLp);
try
{
locStats.setCommStats(locationLp);
}
catch (Exception ex)
{
erred = true;
PXProcessing<Location>.SetError(iLp, ex.Message);
}
}
locStats.StatCache.Persist(PXDBOperation.Insert);
locStats.StatCache.Persist(PXDBOperation.Update);
if (erred)
{
throw new PXException("Location(s) failed during recalculation process. View individual lines to see their specific error.");
}
}
}
My Page
<%# Page Language="C#" MasterPageFile="~/MasterPages/FormDetail.master" AutoEventWireup="true" ValidateRequest="false" CodeFile="SO509503.aspx.cs" Inherits="Page_SO509503" Title="Calculate Location Stats" %>
<%# MasterType VirtualPath="~/MasterPages/FormDetail.master" %>
<asp:Content ID="cont1" ContentPlaceHolderID="phDS" runat="Server">
<px:PXDataSource ID="ds" runat="server" Visible="True" Width="100%" PrimaryView="filterLocStat" TypeName="exampleNS.CalculateLocationStatsProcess">
<CallbackCommands>
</CallbackCommands>
</px:PXDataSource>
</asp:Content>
<asp:Content ID="cont2" ContentPlaceHolderID="phF" runat="Server">
<px:PXFormView ID="formFilter" runat="server" DataMember="filterLocStat" DataSourceID="ds" Style="z-index: 100" Width="100%" >
<Template>
<px:PXLayoutRule runat="server" ID="PXLayoutRule1" ControlSize="M" LabelsWidth="M" StartRow="true" />
<px:PXDateTimeEdit runat="server" ID="edTargetDate" DataField="TargetDate" />
</Template>
</px:PXFormView>
</asp:Content>
<asp:Content ID="cont3" ContentPlaceHolderID="phG" runat="Server">
<px:PXGrid ID="gridLocations" runat="server"
AdjustPageSize="Auto" AllowPaging="True" AllowSearch="true" DataSourceID="ds" FilesIndicator="false"
Height="400px" NoteIndicator="false" SkinID="Inquire" Style="z-index: 100" SyncPosition="true" Width="100%" >
<AutoSize Container="Window" Enabled="True" MinHeight="200" />
<Levels>
<px:PXGridLevel DataMember="processLocations">
<Columns>
<px:PXGridColumn DataField="Selected" Type="CheckBox" AllowCheckAll="true" Width="40px" />
<px:PXGridColumn DataField="Customer__AcctCD" Width="125px" />
<px:PXGridColumn DataField="LocationCD" Width="75px" />
<px:PXGridColumn DataField="Descr" Width="175px" />
</Columns>
</px:PXGridLevel>
</Levels>
</px:PXGrid>
</asp:Content>
My Filter DAC
public class LocationStatsFilter : IBqlTable
{
#region TargetDate
public abstract class targetDate : IBqlField { }
[PXDate()]
[PXUIField(DisplayName = "Target Month and Year")]
public virtual DateTime? TargetDate { get; set; }
#endregion
}
My Location Extension DAC
[PXTable(typeof(Location.locationID), typeof(Location.bAccountID), IsOptional = false)]
public class LocationExt : PXCacheExtension<Location>
{
#region Selected
public abstract class selected : IBqlField { }
[PXBool()]
[PXDefault(false)]
[PXUIField(DisplayName = "Selected")]
public virtual bool? Selected { get; set; }
#endregion
#region DateFirstService
public abstract class dateFirstService : IBqlField { }
[PXDBDate()]
[PXUIField(DisplayName = "Date of First Service")]
public virtual DateTime? DateFirstService { get; set; }
#endregion
}
I modelled my solution after several processing screens that I found, but I've looked at so many I couldn't say which ones I used as examples. I've moved the SetProcessDelegate call between the RowSelected event and the Constructor with no luck. I've attempted making the updateLocationStats function static versus not static (using the existing graph instead) with no success.
UPDATE:
Calling the updateLocationStats method directly instead of creating a
copy of the List didn't change the result.
Adding PXFilterable to the PXFilteredProcessingJoin didn't change the result
Removing the calls to locStats (creation, setCommStats(locationLp), Persist) didn't change the result
Added missing Location DAC
Attempted moving Selected from LocationExt DAC to a new LocationAlt : Location DAC with no change in result.
Added DataType="Boolean" to the selected field in the page. No change in behavior
Added BatchUpdate="True" to the PXGrid tag. No change in behavior.
Added PXProcessing.SetProcessed(); after locStats.setCommStats(locationLp); no change in behavior.
Alternate Test
public class LocationAlt : Location
{
#region Selected
public abstract class selected : IBqlField { }
[PXBool()]
[PXDefault(false)]
[PXUIField(DisplayName = "Selected")]
public virtual bool? Selected { get; set; }
#endregion
}
public class CalculateLocationStatsProcess : PXGraph<CalculateLocationStatsProcess>
{
public PXCancel<LocationStatsFilter> Cancel;
public PXFilter<LocationStatsFilter> filterLocStat;
[PXFilterable()]
public PXFilteredProcessingJoin<LocationAlt,
LocationStatsFilter,
InnerJoin<Customer, On<Customer.bAccountID, Equal<LocationAlt.bAccountID>>>,
Where<True, Equal<True>>,
OrderBy<Asc<Customer.acctCD, Asc<LocationAlt.locationCD>>>> processLocations;
public CalculateLocationStatsProcess()
{
}
public virtual void LocationStatsFilter_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
processLocations.SetProcessDelegate(
delegate (List<LocationAlt> list)
{
CalculateLocationStatsProcess.updateLocationStats(list, filterLocStat.Current);
}
);
}
public static void updateLocationStats(List<LocationAlt> locations, LocationStatsFilter filter)
{
var graph = new PXGraph();
var locStats = new CommStatsHelper(graph, filter.TargetDate);
bool erred = false;
for (int iLp = 0; iLp < locations.Count; iLp++)
{
LocationAlt locationLp = locations[iLp];
PXProcessing<LocationAlt>.SetCurrentItem(locationLp);
try
{
locStats.setCommStats(locationLp);
}
catch (Exception ex)
{
erred = true;
PXProcessing<LocationAlt>.SetError(iLp, ex.Message);
}
}
locStats.StatCache.Persist(PXDBOperation.Insert);
locStats.StatCache.Persist(PXDBOperation.Update);
if (erred)
{
throw new PXException("Location(s) failed during recalculation process. View individual lines to see their specific error.");
}
}
}
Note that the alternate test didn't work either.
The problem being experienced is because the originating graph instance (that is the instance of the graph for which you are in when you first enter the screen) isn't maintaining scope to the execution graph instance (that is the instance of the graph which is doing the logic).
For Acumatica to put the processing status logic (e.g. Timer, Green/Red Status circles) it appears the originating graph needs an link to the graph performing the actions. This appears to be handled for you just fine when you point SetProcessDelegate to a method delegate.
In the issue here, you are creating an ad-hoc delegate (by using the delegate keyword in the constructor/rowselected event). The variable declaration (not instantiation) needs to be outside of the delegate.
Corrected Setting of Delegate
CalculateLocationStatsProcess graph = null;
processLocations.SetProcessDelegate((List<Location> list) =>
{
graph = PXGraph.CreateInstance<CalculateLocationStatsProcess>();
CalculateLocationStatsProcess.updateLocationStats(list, filterLocStat.Current);
}
);
Note the first line is before the SetProcessDelegate call. Even though the graph isn't instantiated outside the delegate, the graph pointer is created. Thus it appears when assigned a link is made and the UI updates as desired.
Additional Notes
The selected field still exists as part of the extension
I left the function static but it did work as desired if converted to non static, you simply change CalculateLocationStatsProcess.updateLocationStats(list, filterLocStat.Current); to graph.updateLocationStats(list, filterLocStat.Current);
The delegate declaration can occur in either the Constructor public CalculateLocationStatsProcess() or in the Filter's Row Selected Event Handler public virtual void LocationStatsFilter_RowSelected(PXCache sender, PXRowSelectedEventArgs e) both worked for me.
BatchUpdate="true" isn't necessary.
DataType="Boolean" isn't necessary.
Lastly, the linked Customer__AcctCD clears when the process is finished (the join doesn't appear to keep the fields populated) so some code for displaying the AcctCD on the graph will need changing. Since this wasn't the focus of the question presented, I do not plan to include that code here.
I believe you are missing the Selected data field in your DAC:
#region Selected
public abstract class selected : IBqlField
{
}
protected bool? _Selected = false;
[PXBool]
[PXDefault(false)]
[PXUIField(DisplayName = "Selected")]
public bool? Selected
{
get
{
return _Selected;
}
set
{
_Selected = value;
}
}
#endregion
Instead of using Location directly, create a DAC extension for Location (LocationStat?) where you will add the Selected field. Don't use Location in your processing screen, use the extension containing the selected field.

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:

Showing smartpanel after field value changes - grid remains empty

I have the need in one of my customization to show a popup directly after the user modifies the value of one of the controls (in this case, a custom field in the SOLine of the Sales Order Entry screen). This popup shows some additional values in a grid that the user must select before completing the row.
Using the standard process a SmartPanel was added to the screen.
If I call this from an action / PXLookupButton, the popup shows and the grid is populated correctly.
If I move this to either the "FieldUpdated" or "RowSelected" event, the smartpanel is displayed however the grid is always empty. Once more, if I then click on the button the grid stays empty till I cancel the modifications and re-enter using only the button.
I tried calling the action's press method in these events as well but the same result occurs.
Watching SQL profiler and the debugger events I can see that the BQL statement is being executed and returning the correct rows it's just not displaying in the smartpanel's grid.
Is it possible to handle this type of request? I'm assuming I need to either move this to a different method and/or pass some additional values but haven't found the right combination.
This holds true on Acumatica 5.3 / 6.1
Any input would be appreciated.
RowUpdated handler allowed me to achieve requested behavior and show SmartPanel after field value change.
Example below relies on custom unbound Trigger Dialog field declared for the SOLine DAC. When a user checks or uncheckes Trigger Dialog flag, the system will show Item Quantity dialog to update Quantity for selected SOLine record:
public class SOLineExt : PXCacheExtension<SOLine>
{
#region TriggerDialog
public abstract class triggerDialog : PX.Data.IBqlField
{
}
[PXBool]
[PXUIField(DisplayName = "Trigger Dialog")]
public virtual bool? TriggerDialog { get; set; }
#endregion
}
Very basic SmartPanel declaration in Aspx:
<px:PXSmartPanel runat="server" ID="CstSmartPanel2" Key="SOLineParam" Caption="Item Quantity" AutoRepaint="True"
CaptionVisible="True" AcceptButtonID="CstButton6" AutoReload="true" >
<px:PXFormView runat="server" ID="CstFormView3" DataMember="SOLineParam" SkinID="Transparent" >
<Template>
<px:PXLayoutRule runat="server" StartColumn="True" />
<px:PXNumberEdit runat="server" ID="CstPXNumberEdit10" DataField="OrderQty" />
</Template>
</px:PXFormView>
<px:PXLayoutRule runat="server" StartRow="True" />
<px:PXPanel runat="server" ID="CstPanel5" SkinID="Buttons">
<px:PXButton runat="server" ID="CstButton6" DialogResult="OK" CommandName="ChangeOk" CommandSourceID="ds" />
<px:PXButton runat="server" ID="CstButton7" DialogResult="Cancel" Text="Cancel" />
</px:PXPanel>
</px:PXSmartPanel>
Accomplished with the SOOrderEntry BLC extension subscribing to RowUpdated handler for the SOLine DAC to show Item Quantity dialog to a user:
public class SOOrderEntryExt : PXGraphExtension<SOOrderEntry>
{
[Serializable]
public class SOLineParams : IBqlTable
{
#region OrderQty
public abstract class orderQty : PX.Data.IBqlField
{
}
[PXDBDecimal]
[PXDefault(TypeCode.Decimal, "0.0")]
[PXUIField(DisplayName = "Quantity")]
public virtual decimal? OrderQty { get; set; }
#endregion
}
public PXFilter<SOLineParams> SOLineParam;
public PXAction<SOOrder> ChangeOk;
[PXUIField(DisplayName = "OK")]
[PXButton(CommitChanges = true)]
protected void changeOk()
{
var lineParams = SOLineParam.Current;
Base.Transactions.Cache.SetValue<SOLine.orderQty>(Base.Transactions.Current, lineParams.OrderQty);
SOLineParam.Cache.Clear();
}
public void SOLine_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)
{
if (!sender.ObjectsEqual<SOLineExt.triggerDialog>(e.Row, e.OldRow) && e.ExternalCall == true)
{
SOLineParam.AskExt();
}
}
}
Another part of the extension class is ChangeOk action invoked by SmartPanel to update Quantity for selected record in the Document Details grid. To hide ChangeOk action from screen toolbar, it's also necessary to add the following command into PXDataSource.CallbackCommands collection:
<px:PXDSCallbackCommand Name="ChangeOk" Visible="False" />

Resources