I have a custom selector inheriting from PXCustomSelectorAttribute. Whenever I use it in a grid column the field appears blank until clicking into the field as if to change it. Then the existing saved value appears as normal in an editor. Navigating away from the grid cell the value again disappears.
I've prepared a very simplified test that produces this behavior and confirmed the "AutoRefresh" property is set to true as described in a similar post.
As shown in the image above, the data records use the custom selector for their "Option ID" and all three records have values in the DB & DAC. The Selector does show the options returned from the GetRecords() method. But the field values don't show in the grid until clicking into the field.
Example code:
using System;
using System.Collections;
using PX.Data;
namespace CustomSelectorTest
{
// The DAC for the detail records
[Serializable]
public class TestDAC : IBqlTable
{
[PXDBString(IsKey = true)]
[PXUIField(DisplayName = "Option ID")]
[PXDefault]
[CustomSelectorTest]
public virtual string OptionID { get; set; }
public abstract class optionID : IBqlField { }
// An arbitrary data field for testing
[PXDBString]
[PXUIField(DisplayName = "User Data")]
public virtual string UserData { get; set; }
public abstract class userData : IBqlField { }
}
// The DAC for the options presented through the custom selector
[Serializable]
public class TestOption : IBqlTable
{
[PXString(IsKey = true)]
[PXUIField(DisplayName = "Option ID")]
public virtual string OptionID { get; set; }
public abstract class optionID : IBqlField { }
[PXString]
[PXUIField(DisplayName = "Description")]
public virtual string Description { get; set; }
public abstract class description : IBqlField { }
}
// The Custom Selector attribute
public class CustomSelectorTestAttribute : PXCustomSelectorAttribute
{
public CustomSelectorTestAttribute()
: base(typeof(TestOption.optionID)) { }
public IEnumerable GetRecords()
{
// These records would come from an external source
yield return new TestOption() { OptionID = "A", Description = "Alpha" };
yield return new TestOption() { OptionID = "B", Description = "Bravo" };
yield return new TestOption() { OptionID = "C", Description = "Charlie" };
yield return new TestOption() { OptionID = "D", Description = "Delta" };
}
}
// A maintenance screen graph
public class TestMaint : PXGraph<TestMaint, TestDAC>
{
public PXSelect<TestDAC> Records;
}
}
ASPX Page:
<%# Page Language="C#" MasterPageFile="~/MasterPages/ListView.master" AutoEventWireup="true" ValidateRequest="false" CodeFile="XX101000.aspx.cs" Inherits="Page_XX101000" Title="Untitled Page" %>
<%# MasterType VirtualPath="~/MasterPages/ListView.master" %>
<asp:Content ID="cont1" ContentPlaceHolderID="phDS" runat="Server">
<px:PXDataSource ID="ds" runat="server" Visible="True" Width="100%" PrimaryView="Records" TypeName="CustomSelectorTest.TestMaint" />
</asp:Content>
<asp:Content ID="cont2" ContentPlaceHolderID="phL" runat="Server">
<px:PXGrid ID="grid" runat="server" Height="400px" Width="100%" Style="z-index: 100"
AllowPaging="True" AllowSearch="True" AdjustPageSize="Auto" DataSourceID="ds" SkinID="Primary" TabIndex="25000">
<Levels>
<px:PXGridLevel DataKeyNames="OptionID" DataMember="Records">
<RowTemplate>
<px:PXSelector ID="edOptionID" runat="server" AutoRefresh="True" DataField="OptionID" CommitChanges="true" Size="SM" />
<px:PXTextEdit ID="edUserData" runat="server" AlreadyLocalized="False" DataField="UserData" DefaultLocale="" Size="SM" />
</RowTemplate>
<Columns>
<px:PXGridColumn DataField="OptionID" />
<px:PXGridColumn DataField="UserData" />
</Columns>
</px:PXGridLevel>
</Levels>
<AutoSize Container="Window" Enabled="True" MinHeight="200" />
</px:PXGrid>
</asp:Content>
Adding the field size to the [PXDBString] attribute for the field corrected the issue.
[PXDBString(10, IsKey = true)]
...
public virtual string OptionID { get; set; }
Related
I have a processing screen in Acumatica ERP (2020R2). Whenever I run Process All, it executes the process delegate correctly. But whenever I select some of the records and run the Process action, the process delegate doesn't even get called.
I've implemented several processing screens, and I've never had this problem.
I did notice that, when I click the Selected checkbox, a red dot appears beside the row on the grid and doesn't go away. On other processing screens that I've implemented, the red dot appears then goes away.
Here is my graph:
public class StkItemUpdateProcess : PXGraph<StkItemUpdateProcess>
{
#region Fliter DAC
[PXHidden]
public class StkItemFilter : IBqlTable
{
#region UpdateID
[PXString(15, IsUnicode = true, InputMask = ">CCCCCCCCCCCCCCC")]
[PXUIField(DisplayName = "Update ID")]
[PXSelector(
typeof(SearchFor<StkItemUpdateHdr.updateID>),
typeof(StkItemUpdateHdr.updateID),
typeof(StkItemUpdateHdr.descr),
typeof(StkItemUpdateHdr.vendorID),
typeof(StkItemUpdateHdr.effectiveDate)
)]
public virtual string UpdateID { get; set; }
public abstract class updateID : PX.Data.BQL.BqlString.Field<updateID> { }
#endregion
#region VendorID
[PXInt]
[PXUIField(DisplayName = "Vendor")]
[PXSelector(
typeof(
SearchFor<Vendor.bAccountID>.
In<SelectFrom<Vendor>.
InnerJoin<Address>.
On<Address.addressID.IsEqual<Vendor.defAddressID>>.
Where<Vendor.status.IsEqual<BAccount.status.active>>>
),
typeof(Vendor.acctCD),
typeof(Vendor.acctName),
typeof(Address.city),
typeof(Address.state),
SubstituteKey = typeof(Vendor.acctCD),
DescriptionField = typeof(Vendor.acctName)
)]
public virtual int? VendorID { get; set; }
public abstract class vendorID : PX.Data.BQL.BqlInt.Field<vendorID> { }
#endregion
#region InventoryID
[PXInt]
[PXUIField(DisplayName = "Inventory ID")]
[PXSelector(
typeof(
SearchFor<InventoryItem.inventoryID>.
In<SelectFrom<InventoryItem>.
InnerJoin<POVendorInventory>.
On<POVendorInventory.inventoryID.IsEqual<InventoryItem.inventoryID>>.
Where<InventoryItem.stkItem.IsEqual<True>.
And<POVendorInventory.vendorID.IsEqual<StkItemUpdateHdr.vendorID.FromCurrent>>.
And<POVendorInventory.active.IsEqual<True>>>>
),
typeof(InventoryItem.inventoryCD),
typeof(InventoryItem.descr),
typeof(InventoryItem.itemClassID),
typeof(InventoryItem.itemType),
SubstituteKey = typeof(InventoryItem.inventoryCD)
)]
[PXRestrictor(
typeof(Where<InventoryItem.stkItem.IsEqual<True>.
And<POVendorInventory.vendorID.IsEqual<StkItemUpdateHdr.vendorID.FromCurrent>>.
And<POVendorInventory.active.IsEqual<True>>>),
Messages.InvalidVendorItemPair)]
public virtual int? InventoryID { get; set; }
public abstract class inventoryID : PX.Data.BQL.BqlInt.Field<inventoryID> { }
#endregion
#region FromEffectiveDate
[PXDate]
[PXUIField(DisplayName = "From Date")]
[PXUnboundDefault(typeof(AccessInfo.businessDate), PersistingCheck = PXPersistingCheck.Nothing)]
public virtual DateTime? FromEffectiveDate { get; set; }
public abstract class fromEffectiveDate : PX.Data.BQL.BqlDateTime.Field<fromEffectiveDate> { }
#endregion
#region ToEffectiveDate
[PXDate]
[PXUIField(DisplayName = "To Date")]
[PXUnboundDefault(typeof(AccessInfo.businessDate), PersistingCheck = PXPersistingCheck.Nothing)]
public virtual DateTime? ToEffectiveDate { get; set; }
public abstract class toEffectiveDate : PX.Data.BQL.BqlDateTime.Field<toEffectiveDate> { }
#endregion
}
#endregion
#region Selects / Views
public PXCancel<StkItemFilter> Cancel;
public PXFilter<StkItemFilter> Filter;
public PXFilteredProcessingJoin<StkItemUpdateDet, StkItemFilter,
InnerJoin<StkItemUpdateHdr,
On<StkItemUpdateHdr.updateID.IsEqual<StkItemUpdateDet.updateID>>,
InnerJoin<Vendor,
On<Vendor.bAccountID.IsEqual<StkItemUpdateHdr.vendorID>>,
InnerJoin<InventoryItem,
On<InventoryItem.inventoryID.IsEqual<StkItemUpdateDet.inventoryID>>>>>,
Where<Brackets<StkItemFilter.updateID.FromCurrent.IsNull.
Or<StkItemUpdateDet.updateID.IsEqual<StkItemFilter.updateID.FromCurrent>>>.
And<StkItemFilter.vendorID.FromCurrent.IsNull.
Or<StkItemUpdateHdr.vendorID.IsEqual<StkItemFilter.vendorID.FromCurrent>>>.
And<StkItemFilter.inventoryID.FromCurrent.IsNull.
Or<StkItemUpdateDet.inventoryID.IsEqual<StkItemFilter.inventoryID.FromCurrent>>>.
And<StkItemFilter.fromEffectiveDate.FromCurrent.IsNull.
Or<StkItemUpdateHdr.effectiveDate.IsGreaterEqual<StkItemFilter.fromEffectiveDate.FromCurrent>>>.
And<StkItemFilter.toEffectiveDate.FromCurrent.IsNull.
Or<StkItemUpdateHdr.effectiveDate.IsLessEqual<StkItemFilter.toEffectiveDate.FromCurrent>>>>,
OrderBy<Desc<StkItemUpdateHdr.effectiveDate, Asc<StkItemUpdateDet.updateID, Asc<Vendor.acctCD, Asc<InventoryItem.inventoryCD>>>>>>
StkItemUpdates;
#endregion
#region Constructor
public StkItemUpdateProcess()
{
StkItemUpdates.SetProcessCaption("Update");
StkItemUpdates.SetProcessTooltip("Update Selected Stock Items");
StkItemUpdates.SetProcessAllCaption("Update All");
StkItemUpdates.SetProcessAllTooltip("Update All Stock Items");
StkItemUpdates.SetProcessDelegate(UpdateStkItems);
StkItemUpdates.SetSelected<StkItemUpdateDet.selected>();
}
#endregion
#region Overrides
public override bool IsDirty => false;
#endregion
#region Event Handlers
protected virtual void _(Events.FieldUpdated<StkItemFilter, StkItemFilter.updateID> e)
{
if (e.Row == null) return;
StkItemFilter filter = Filter.Current;
if(filter.UpdateID != null)
{
StkItemUpdateHdr hdr =
SelectFrom<StkItemUpdateHdr>.
Where<StkItemUpdateHdr.updateID.IsEqual<#P.AsString>>.
View.ReadOnly.Select(e.Cache.Graph, filter.UpdateID);
e.Cache.SetValueExt<StkItemFilter.vendorID>(filter, hdr?.VendorID);
}
}
protected virtual void _(Events.FieldSelecting<StkItemUpdateDet, StkItemUpdateDet.defaultVendor> e)
{
if (e.Row == null) return;
StkItemUpdateDet det = e.Row;
PXResultset<POVendorInventory> existingVendors =
SelectFrom<POVendorInventory>.
Where<POVendorInventory.inventoryID.IsEqual<#P.AsInt>>.
View.ReadOnly.Select(e.Cache.Graph, det.InventoryID);
//IsDefault cannot be used in BQL statement because it is not bound.
foreach (POVendorInventory vendor in existingVendors)
{
if (vendor.IsDefault == true)
{
e.ReturnValue = vendor.VendorID;
break;
}
}
}
#endregion
#region Actions
public PXAction<StkItemFilter> OpenUpdate;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Open Update")]
public virtual void openUpdate()
{
StkItemUpdateDet det = StkItemUpdates.Current;
StkItemUpdateMaint stkItemUpdateMaint = PXGraph.CreateInstance<StkItemUpdateMaint>();
stkItemUpdateMaint.UpdateHdr.Current = stkItemUpdateMaint.UpdateHdr.Search<StkItemUpdateHdr.updateID>(det.UpdateID);
if (stkItemUpdateMaint.UpdateHdr.Current != null)
{
throw new PXRedirectRequiredException(stkItemUpdateMaint, true, "Stock Item Update")
{
Mode = PXBaseRedirectException.WindowMode.NewWindow
};
}
}
public PXAction<StkItemFilter> OpenStockItem;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Open Stock Item")]
public virtual void openStockItem()
{
StkItemUpdateDet det = StkItemUpdates.Current;
InventoryItemMaint invtItemMaint = PXGraph.CreateInstance<InventoryItemMaint>();
invtItemMaint.Item.Current = invtItemMaint.Item.Search<InventoryItem.inventoryID>(det.InventoryID);
if (invtItemMaint.Item.Current != null)
{
throw new PXRedirectRequiredException(invtItemMaint, true, "Stock Item")
{
Mode = PXBaseRedirectException.WindowMode.NewWindow
};
}
}
#endregion
#region Static Methods
public static void UpdateStkItems(List<StkItemUpdateDet> stkItemUpdateDets)
{
StkItemUpdateMaint stkItemUpdateMaint = PXGraph.CreateInstance<StkItemUpdateMaint>();
InventoryItemMaint invtItemMaint = PXGraph.CreateInstance<InventoryItemMaint>();
foreach (StkItemUpdateDet det in stkItemUpdateDets)
{
stkItemUpdateMaint.Clear();
invtItemMaint.Clear();
StkItemUpdateHdr hdr = stkItemUpdateMaint.UpdateHdr.Current = stkItemUpdateMaint.UpdateHdr.Search<StkItemUpdateHdr.updateID>(det.UpdateID);
InventoryItem invtItem = invtItemMaint.Item.Current = invtItemMaint.Item.Search<InventoryItem.inventoryID>(det.InventoryID);
if (hdr == null || invtItem == null) continue;
BasisPricing.BasisPricing_InventoryItemExt invtItemExt = PXCache<InventoryItem>.GetExtension<BasisPricing.BasisPricing_InventoryItemExt>(invtItem);
bool fieldUpdated = false;
if (invtItemExt.UsrManualCost != det.ManualCost)
{
invtItemMaint.Item.Cache.SetValueExt<BasisPricing.BasisPricing_InventoryItemExt.usrManualCost>(invtItem, det.ManualCost);
fieldUpdated = true;
}
if (invtItem.RecPrice != det.MSRP)
{
invtItemMaint.Item.Cache.SetValueExt<InventoryItem.recPrice>(invtItem, det.MSRP);
fieldUpdated = true;
}
if (fieldUpdated)
{
invtItemMaint.Item.Cache.Update(invtItem);
invtItemMaint.Actions.PressSave();
det.LastUpdatedDateTime = DateTime.Now;
stkItemUpdateMaint.UpdateDets.Cache.Update(det);
stkItemUpdateMaint.Actions.PressSave();
}
}
}
#endregion
}
Here is my ASPX code:
<%# Page Language="C#" MasterPageFile="~/MasterPages/FormView.master" AutoEventWireup="true" ValidateRequest="false" CodeFile="IN507500.aspx.cs" Inherits="Page_IN507500" Title="Untitled Page" %>
<%# MasterType VirtualPath="~/MasterPages/FormView.master" %>
<asp:Content ID="cont1" ContentPlaceHolderID="phDS" Runat="Server">
<px:PXDataSource ID="ds" runat="server" Visible="True" Width="100%"
TypeName="INCostPriceUpdate.StkItemUpdateProcess"
PrimaryView="Filter"
>
<CallbackCommands>
<px:PXDSCallbackCommand Name="OpenUpdate" Visible="false" />
<px:PXDSCallbackCommand Name="OpenStockItem" Visible="false" />
</CallbackCommands>
</px:PXDataSource>
</asp:Content>
<asp:Content ID="cont2" ContentPlaceHolderID="phF" Runat="Server">
<px:PXFormView ID="form" runat="server" DataSourceID="ds" DataMember="Filter" Width="100%" AllowAutoHide="false">
<Template>
<px:PXLayoutRule ID="PXLayoutRule1" runat="server" StartRow="True"/>
<px:PXLayoutRule ID="PXLayoutRule2" runat="server" StartColumn="True" LabelsWidth="85px" ControlSize="XM" />
<px:PXSelector ID="PXSelector1" runat="server" DataField="UpdateID" CommitChanges="true" />
<px:PXSelector ID="FilterVendorID" runat="server" DataField="VendorID" CommitChanges="true" />
<px:PXSelector ID="FilterInventoryID" runat="server" DataField="InventoryID" CommitChanges="true" />
<px:PXLayoutRule ID="PXLayoutRule3" runat="server" StartColumn="True" LabelsWidth="75px" />
<px:PXDateTimeEdit ID="FilterFromDate" runat="server" DataField="FromEffectiveDate" CommitChanges="true" />
<px:PXDateTimeEdit ID="FilterToDate" runat="server" DataField="ToEffectiveDate" CommitChanges="true" />
</Template>
<AutoSize Container="Window" Enabled="True" MinHeight="30" />
</px:PXFormView>
<px:PXGrid ID="StkItemUpdateDetGrid" runat="server" Width="100%" Height="650px" SyncPosition="true" AdjustPageSize="Auto" AllowPaging="false" SkinID="PrimaryInquire" DataSourceID="ds">
<Levels>
<px:PXGridLevel DataMember="StkItemUpdates">
<Columns>
<px:PXGridColumn DataField="Selected" Type="CheckBox" TextAlign="Center" CommitChanges="true" AllowCheckAll="true" AllowMove="false" AllowResize="false" AllowSort="false" AllowShowHide="false"></px:PXGridColumn>
<px:PXGridColumn DataField="StkItemUpdateHdr__VendorID" Width="140" ></px:PXGridColumn>
<px:PXGridColumn DataField="UpdateID" LinkCommand="OpenUpdate" ></px:PXGridColumn>
<px:PXGridColumn DataField="StkItemUpdateHdr__EffectiveDate" Width="90" />
<px:PXGridColumn DataField="InventoryID" CommitChanges="true" Width="150" LinkCommand="OpenStockItem" ></px:PXGridColumn>
<px:PXGridColumn DataField="InventoryDescr" Width="200" ></px:PXGridColumn>
<px:PXGridColumn DataField="ManualCost" Width="100" CommitChanges="true" ></px:PXGridColumn>
<px:PXGridColumn DataField="MSRP" Width="100" CommitChanges="true" ></px:PXGridColumn>
<px:PXGridColumn DataField="CurrManualCost" Width="100" ></px:PXGridColumn>
<px:PXGridColumn DataField="CurrMSRP" Width="100" ></px:PXGridColumn>
<px:PXGridColumn DataField="LastUpdatedDateTime" Width="150" DisplayFormat="g" ></px:PXGridColumn>
<px:PXGridColumn DataField="DefaultVendor" Width="150" ></px:PXGridColumn>
<px:PXGridColumn DataField="AvailVendors" Width="250" ></px:PXGridColumn></Columns>
<RowTemplate>
<px:PXSelector AllowEdit="True" runat="server" ID="CstPXSelector1" DataField="DefaultVendor" ></px:PXSelector>
<px:PXSelector runat="server" ID="CstPXSelector3" DataField="StkItemUpdateHdr__VendorID" AllowEdit="True" ></px:PXSelector></RowTemplate></px:PXGridLevel>
</Levels>
</px:PXGrid>
</asp:Content>
Here is the main DAC used on the Processing Screen:
public class StkItemUpdateDet : IBqlTable
{
#region Selected
[PXBool]
[PXUIField(DisplayName = "Selected")]
public virtual bool? Selected { get; set; }
public abstract class selected : PX.Data.BQL.BqlBool.Field<selected> { }
#endregion
#region UpdateID
[PXDBString(15, IsKey = true, IsUnicode = true, InputMask = ">CCCCCCCCCCCCCCC")]
[PXUIField(DisplayName = "Update ID", Enabled = false)]
[PXDBDefault(typeof(StkItemUpdateHdr.updateID))]
[PXParent(typeof(SelectFrom<StkItemUpdateHdr>.
Where<StkItemUpdateHdr.updateID.IsEqual<StkItemUpdateDet.updateID.FromCurrent>>)
)]
public virtual string UpdateID { get; set; }
public abstract class updateID : PX.Data.BQL.BqlString.Field<updateID> { }
#endregion
#region InventoryID
[PXDBInt(IsKey = true)]
[PXUIField(DisplayName = "Inventory ID")]
[PXDefault]
[PXSelector(
typeof(
SearchFor<InventoryItem.inventoryID>.
In<SelectFrom<InventoryItem>.
InnerJoin<POVendorInventory>.
On<POVendorInventory.inventoryID.IsEqual<InventoryItem.inventoryID>>.
Where<InventoryItem.stkItem.IsEqual<True>.
And<POVendorInventory.vendorID.IsEqual<StkItemUpdateHdr.vendorID.FromCurrent>>.
And<POVendorInventory.active.IsEqual<True>>>>
),
typeof(InventoryItem.inventoryCD),
typeof(InventoryItem.descr),
typeof(InventoryItem.itemClassID),
typeof(InventoryItem.itemType),
SubstituteKey = typeof(InventoryItem.inventoryCD)
)]
[PXRestrictor(
typeof(Where<InventoryItem.stkItem.IsEqual<True>.
And<POVendorInventory.vendorID.IsEqual<StkItemUpdateHdr.vendorID.FromCurrent>>.
And<POVendorInventory.active.IsEqual<True>>>),
Messages.InvalidVendorItemPair)]
public virtual int? InventoryID { get; set; }
public abstract class inventoryID : PX.Data.BQL.BqlInt.Field<inventoryID> { }
#endregion
#region InventoryDesc
[PXString]
[PXUIField(DisplayName = "Descr.", Enabled = false)]
[InventoryDescr]
public virtual string InventoryDescr { get; set; }
public abstract class inventoryDescr : PX.Data.BQL.BqlString.Field<inventoryDescr> { }
#endregion
#region ManualCost
[PXDBDecimal(6)]
[PXUIField(DisplayName = "New Manual Cost")]
[PXDefault(TypeCode.Decimal, "0.000000")]
public virtual decimal? ManualCost { get; set; }
public abstract class manualCost : PX.Data.BQL.BqlDecimal.Field<manualCost> { }
#endregion
#region MSRP
[PXDBDecimal(2)]
[PXUIField(DisplayName = "New MSRP")]
[PXDefault(TypeCode.Decimal, "0.00")]
public virtual decimal? MSRP { get; set; }
public abstract class msrp : PX.Data.BQL.BqlDecimal.Field<msrp> { }
#endregion
#region CurrManualCost
[PXDecimal(6)]
[PXUIField(DisplayName = "Current Manual Cost", Enabled = false)]
[ManualCost]
public virtual decimal? CurrManualCost { get; set; }
public abstract class currManualCost : PX.Data.BQL.BqlDecimal.Field<currManualCost> { }
#endregion
#region CurrMSRP
[PXDecimal(2)]
[PXUIField(DisplayName = "Current MSRP", Enabled = false)]
[MSRP]
public virtual decimal? CurrMSRP { get; set; }
public abstract class currMSRP : PX.Data.BQL.BqlDecimal.Field<currMSRP> { }
#endregion
#region LastUpdatedDateTime
[PXDBDate(PreserveTime = true, UseTimeZone = false)]
[PXUIField(DisplayName = "Update Last Executed", Enabled = false)]
public virtual DateTime? LastUpdatedDateTime { get; set; }
public abstract class lastUpdatedDateTime : PX.Data.BQL.BqlDateTime.Field<lastUpdatedDateTime> { }
#endregion
#region DefaultVendor
[PXInt]
[PXUIField(DisplayName = "Default Vendor", Enabled = false)]
[PXSelector(
typeof(SearchFor<Vendor.bAccountID>),
SubstituteKey = typeof(Vendor.acctCD)
)]
public int? DefaultVendor { get; set; }
public abstract class defaultVendor : PX.Data.BQL.BqlInt.Field<defaultVendor> { }
#endregion
#region AvailVendors
[PXString]
[PXUIField(DisplayName = "Available Vendors", Enabled = false)]
[AvailVendors]
public string AvailVendors { get; set; }
public abstract class availVendors : PX.Data.BQL.BqlString.Field<availVendors> { }
#endregion
#region System Fields
#region CreatedByID
[PXDBCreatedByID()]
public virtual Guid? CreatedByID { get; set; }
public abstract class createdByID : PX.Data.BQL.BqlGuid.Field<createdByID> { }
#endregion
#region CreatedByScreenID
[PXDBCreatedByScreenID()]
public virtual string CreatedByScreenID { get; set; }
public abstract class createdByScreenID : PX.Data.BQL.BqlString.Field<createdByScreenID> { }
#endregion
#region CreatedDateTime
[PXDBCreatedDateTime()]
public virtual DateTime? CreatedDateTime { get; set; }
public abstract class createdDateTime : PX.Data.BQL.BqlDateTime.Field<createdDateTime> { }
#endregion
#region LastModifiedByID
[PXDBLastModifiedByID()]
public virtual Guid? LastModifiedByID { get; set; }
public abstract class lastModifiedByID : PX.Data.BQL.BqlGuid.Field<lastModifiedByID> { }
#endregion
#region LastModifiedByScreenID
[PXDBLastModifiedByScreenID()]
public virtual string LastModifiedByScreenID { get; set; }
public abstract class lastModifiedByScreenID : PX.Data.BQL.BqlString.Field<lastModifiedByScreenID> { }
#endregion
#region LastModifiedDateTime
[PXDBLastModifiedDateTime()]
public virtual DateTime? LastModifiedDateTime { get; set; }
public abstract class lastModifiedDateTime : PX.Data.BQL.BqlDateTime.Field<lastModifiedDateTime> { }
#endregion
#region tstamp
[PXDBTimestamp()]
public virtual byte[] tstamp { get; set; }
public abstract class Tstamp : PX.Data.BQL.BqlByteArray.Field<Tstamp> { }
#endregion
#region Noteid
[PXNote()]
public virtual Guid? Noteid { get; set; }
public abstract class noteid : PX.Data.BQL.BqlGuid.Field<noteid> { }
#endregion
#endregion
}
I have some problems with forming a dynamic selector, below I will imagine what has been done.
I need to dynamically generate a selector depending on the selected AssetId.
The AssedInfo selector must return information depending on the type of information selected in the AssetId. I tried, in steps:
public class NSIAssetLinkInfo: IBqlTable
{
#region AssetId
[PXDBString(500, IsUnicode = true, InputMask = "")]
[PXUIField(DisplayName = "Asset Id")]
[PXStringList(
new string[]
{
"AssedInfo","GLTran"
},
new string[]
{
"AssedInfo","GLTran"
})]
public virtual string AssetId { get; set; }
public new abstract class assetId : PX.Data.BQL.BqlString.Field<assetId> { }
#endregion
#region AssetInfo
[PXUIField(DisplayName = "Asset Information")]
[PXDBString(500, IsUnicode = true, InputMask = "")]
public virtual string AssetInfo { get; set; }
public abstract class assetInfo : PX.Data.BQL.BqlString.Field<assetInfo> { }
#endregion
}
In here, I created a button and an event that should dynamically form our selector.
And it is also necessary that the event that the selector will form returns not all the columns, and only the necessary ones, for example, "AssetID - RecordType," if you are accurate, then you need only those columns that I will indicate, and not all.
added button to panel
public class KNRWAPInvoiceEntryExt : PXGraphExtension<APInvoiceEntry>
{
public SelectFrom<GLTranR>.View GLTrans;
public SelectFrom<FixedAsset>.View FXAsset;
public SelectFrom<NSIAssetLinkInfo>.View NSIAssetLinkInfo;
public PXAction<APInvoice> LinkInfo;
[PXButton]
[PXUIField(DisplayName = "LinkInfo", MapEnableRights = PXCacheRights.Delete, MapViewRights = PXCacheRights.Delete)]
protected void linkInfo()
{
if (TagPanel.AskExt(true) != WebDialogResult.OK) return;
//Do some useful Stuff
}
public PXFilter<NSIAssetLinkInfo> TagPanel;
public virtual void _(Events.FieldSelecting<NSIAssetLinkInfo, NSIAssetLinkInfo.assetId> e)
{
if (e.Row == null)
{
return;
}
var assetId = e.Row.AssetId;
if (assetId == "GLTran")
{
//gltran
//saves TranID
//Displays RefNbr - InventoryID
//not all gltran, but gltran which has accountid
var returnState = PXFieldState.CreateInstance(e.ReturnState, dataType: typeof(FixedAsset), isKey: false, nullable: null,
required: null, precision: null, length: null, defaultValue: null, fieldName: null);
e.ReturnState = returnState;
returnState.Visible = true;
returnState.Visibility = PXUIVisibility.Visible;
returnState.Enabled = true;
returnState.DisplayName = "RefNbr";
returnState.ViewName = "GLTrans";
returnState.DescriptionName = "GLTranR";
returnState.FieldList = new[] { nameof(FixedAsset.AssetID), nameof(FixedAsset.Description), nameof(FixedAsset.Depreciable) };
returnState.HeaderList = new[]
{
PXUIFieldAttribute.GetDisplayName<FixedAsset.assetID>(FXAsset.Cache),
PXUIFieldAttribute.GetDisplayName<FixedAsset.description>(FXAsset.Cache),
PXUIFieldAttribute.GetDisplayName<FixedAsset.depreciable>(FXAsset.Cache),
};
}
if (assetId == "AssedInfo")
{
//Fixed Asset
//saves InventoryID
//InventoryID - Desc
//not all fixed assets, but those, which has class
var returnState = PXFieldState.CreateInstance(e.ReturnState, dataType: typeof(GLTranR), isKey: false, nullable: null,
required: null, precision: null, length: null, defaultValue: null, fieldName: null);
//descriptionName: null, displayName: null, error: null, errorLevel: PXErrorLevel.Undefined,
//enabled: null, visible: null, readOnly: false, visibility: PXUIVisibility.Visible, viewName: null,
//fieldList: null, headerList: null);
e.ReturnState = returnState;
returnState.Visible = true;
returnState.Visibility = PXUIVisibility.Visible;
returnState.Enabled = true;
returnState.DisplayName = "RefNbr";
returnState.ViewName = "GLTrans";
returnState.DescriptionName = "GLTranR";
returnState.FieldList = new[] { nameof(GLTranR.InventoryID), nameof(GLTranR.TranDesc), nameof(GLTranR.RefNbr), nameof(GLTranR.DebitAmt), nameof(GLTranR.CreditAmt) };
returnState.HeaderList = new[]
{
PXUIFieldAttribute.GetDisplayName<GLTranR.inventoryID>(GLTrans.Cache),
PXUIFieldAttribute.GetDisplayName<GLTranR.tranDesc>(GLTrans.Cache),
PXUIFieldAttribute.GetDisplayName<GLTranR.refNbr>(GLTrans.Cache),
PXUIFieldAttribute.GetDisplayName<GLTranR.debitAmt>(GLTrans.Cache),
PXUIFieldAttribute.GetDisplayName<GLTranR.creditAmt>(GLTrans.Cache)
};
}
}
}
Button in View
<CallbackCommands>
<px:PXDSCallbackCommand Name="LinkInfo" Visible="false" />
</CallbackCommands>
And
<px:PXToolBarButton>
<AutoCallBack Command="LinkInfo" Target="ds" />
</px:PXToolBarButton>
And
<px:PXSmartPanel ID="pnlCopyCompany" runat="server" CaptionVisible="True" Caption="My Command" Style="position: static" LoadOnDemand="True" Key="TagPanel" AutoCallBack-Target="frmMyCommand" AutoCallBack-Command="Refresh" DesignView="Content">
<px:PXFormView ID="frmMyCommand" runat="server" SkinID="Transparent" DataMember="TagPanel" DataSourceID="ds" EmailingGraph="">
<Template>
<px:PXGrid runat="server" SyncPosition="true" KeepPosition="true" Height="150px" SkinID="Details" Width="865px" Caption="Caption"
CaptionVisible="false" MatrixMode="True" RepaintColumns="true"
DataSourceID="ds" AllowPaging="True" AdjustPageSize="Auto" NoteIndicator="false" FilesIndicator="false" Style='left: 0px; top: 0px; height: 188px;'>
<AutoSize Enabled="True" MinHeight="200" />
<ActionBar PagerVisible="False" />
<CallbackCommands>
<Refresh CommitChanges="True" />
</CallbackCommands>
<Levels>
<px:PXGridLevel DataMember="NSIAssetLinkInfo" DataKeyNames="TagInfoID">
<RowTemplate>
<px:PXDropDown runat="server" ID="edAssetId" CommitChanges="True" DataField="AssetId" AllowMultiSelect="True" />
<px:PXSelector runat="server" ID="edAssetInfo" DataField="AssetInfo" AutoRefresh="True" CommitChanges="True" />
</RowTemplate>
<Columns>
<px:PXGridColumn DataField="AssetId" CommitChanges="True" />
<px:PXGridColumn DataField="AssetInfo" CommitChanges="True" />
</Columns>
</px:PXGridLevel>
</Levels>
</px:PXGrid>
</Template>
</px:PXFormView>
</px:PXSmartPanel>
I plan what would be like this
I am struggling to follow your example vs. what you are saying you need to do. It looks like you are want to properly set the list of allowed values in AssetID as predefined list. This is much simpler than your example if you use the RowSelected event and predefine your allowed lists. Below is a very trimmed down example showing how I accomplish this with 1 sample list. You can define multiple lists according to your allowed combinations and specify the desired type for listattr according to your conditions.
protected void _(Events.RowSelected<XXMyDAC> e)
{
if(*condition*) // Substitute your conditional logic here
{
SelectableValues listattr = new SelectableValues();
PXStringListAttribute.SetList<XXMyDAC.myField>
(e.Cache, row, listattr.AllowedValues, listattr.AllowedLabels);
}
}
public class SelectableValues : PXStringListAttribute
{
public string[] AllowedLabels = new string[]
{
Messages.MyLabel1,
Messages.MyLabel2
};
public string[] AllowedValues = new string[]
{
MyValue1,
MyValue2
};
}
If you need to limit values being returned from the database rather than from a predefined list, you would use PXRestrictorAttribute on the DAC field either directly in the DAC or via CacheAttached in the graph. Here is a very simple example of that using a custom field called UsrXXMyCondition added to INLocation (via the DAC extension INLocationExt).
[PXMergeAttributes(Method = MergeMethod.Append)]
[PXRestrictor(typeof(
Where<INLocationExt.usrXXMyCondition, NotEqual<ConditionType.condition1>>
), "")]
protected virtual void INTran_ToLocationID_CacheAttached(PXCache sender) { }
I have a smartpanel with two different selectors with data fields called KnownContributor and NewContributor. When I pick a selection from either selector, NewContributor for example, the selector highlights red and I get the error message 'NewContributor cannot be found in the system.' I have the selector clearly defined and this exists on a page with multiple smart panels defined in the same exact manner with selectors also defined in the same exact manner other than the PXSelector's Search syntax.
Here is the before and after a selection is made images:
Here is all of the code:
ASPX:
<px:PXSmartPanel AutoReload="false" runat="server" DesignView="content" Height="225px" Width ="350px" ID="spSetOnlineCheck" LoadOnDemand="true" AutoRepaint="true" ShowAfterLoad="true" CaptionVisible="true" Caption="Set Online Check Contributor" Key="SetOnlineCheckPopup">
<px:PXFormView Height="225px" Width="100%" SkinID="Transparent" DataSourceID="ds" runat="server" ID="fvOnlineCheck" DataMember="SetOnlineCheckPopup">
<Template>
<px:PXLayoutRule runat="server" ID="lrPopupCheck" StartColumn="True"></px:PXLayoutRule>
<px:PXPanel Width="100%" Height="200px" runat="server" ID="pnlOnlineCheck">
<px:PXLabel Width="100%" Height="50px" runat="server" ID="lblOnlineCheck" Text="Pick contributor to be associated with this online check."></px:PXLabel>
<px:PXSelector FilterByAllFields="true" CommitChanges="True" runat="server" ID="selKnown" DataField="KnownContributor"></px:PXSelector>
<px:PXLabel runat="server" ID="CstLabel9" Size="10px" ></px:PXLabel>
<px:PXSelector CommitChanges="True" runat="server" ID="CstPXSelector10" DataField="NewContributor" ></px:PXSelector>
<px:PXLabel runat="server" ID="lblOnlineBankSpace" Height="50px"></px:PXLabel>
<px:PXButton runat="server" ID="AddContributorOK" Text="OK" DialogResult="OK"></px:PXButton>
<px:PXButton runat="server" ID="AddContributorCancel" Text="Cancel" DialogResult="Cancel"></px:PXButton></px:PXPanel>
</Template>
</px:PXFormView>
</px:PXSmartPanel>
DAC:
[Serializable]
public class SetBankCheckPopup : IBqlTable
{
[PXInt()]
[PXUIField(DisplayName = "BankID")]
public int? BankID { get; set; }
public class bankdID : IBqlField { }
[PXInt()]
[PXUIField(DisplayName = "Known Contributors")]
[PXSelector(typeof(Search<CustomerPaymentMethod.bAccountID>),
new Type[]{
typeof(CustomerPaymentMethod.bAccountID)
})]
//[PXSelector(typeof(Search5<CustomerPaymentMethod.bAccountID, InnerJoin<CustomerPaymentMethodDetail,
// On<CustomerPaymentMethod.pMInstanceID, Equal<CustomerPaymentMethodDetail.pMInstanceID>,
// And<CustomerPaymentMethodDetail.detailID, Equal<C1>,
// And<CustomerPaymentMethodDetail.value, Equal<Current<SetBankCheckPopup.routeNbr>>>>>,
// InnerJoin<CFBSCustomerPaymentMethodDetailCopy, On<CFBSCustomerPaymentMethodDetailCopy.pMInstanceID, Equal<CustomerPaymentMethod.pMInstanceID>,
// And<CFBSCustomerPaymentMethodDetailCopy.detailID, Equal<C2>,
// And<CFBSCustomerPaymentMethodDetailCopy.value, Equal<Current<SetBankCheckPopup.acctNbr>>>>>,
// InnerJoin<BAccount, On<BAccount.bAccountID, Equal<CustomerPaymentMethod.bAccountID>>>>>, Aggregate<GroupBy<CustomerPaymentMethod.bAccountID>>>),
// new Type[]
// {
// typeof(BAccount.acctCD),
// typeof(BAccount.acctName)
// },
// ValidateValue =false
// )]
public int? KnownContributor { get; set; }
public class knownContributor : IBqlField { }
[PXInt()]
[PXUIField(DisplayName = "New Contributor")]
//[PXSelector(typeof(Search<CustomerPaymentMethod.bAccountID>),
// new Type[]{
// typeof(CustomerPaymentMethod.bAccountID)
// })]
[PXSelector(typeof(Search5<CustomerPaymentMethod.bAccountID, InnerJoin<CustomerPaymentMethodDetail,
On<CustomerPaymentMethod.pMInstanceID, Equal<CustomerPaymentMethodDetail.pMInstanceID>,
And<CustomerPaymentMethodDetail.detailID, Equal<C1>,
And<CustomerPaymentMethodDetail.value, NotEqual<Current<SetBankCheckPopup.routeNbr>>>>>,
InnerJoin<CFBSCustomerPaymentMethodDetailCopy, On<CFBSCustomerPaymentMethodDetailCopy.pMInstanceID, Equal<CustomerPaymentMethod.pMInstanceID>,
And<CFBSCustomerPaymentMethodDetailCopy.detailID, Equal<C2>,
And<CFBSCustomerPaymentMethodDetailCopy.value, NotEqual<Current<SetBankCheckPopup.acctNbr>>>>>,
InnerJoin<BAccount, On<BAccount.bAccountID, Equal<CustomerPaymentMethod.bAccountID>>>>>, Aggregate<GroupBy<CustomerPaymentMethod.bAccountID>>>),
new Type[]
{
typeof(CustomerPaymentMethod.bAccountID),
typeof(BAccount.acctName)
},
DescriptionField = typeof(BAccount.acctName),
SubstituteKey = typeof(BAccount.bAccountID),
ValidateValue = false
)]
public int? NewContributor { get; set; }
public class newContributor : IBqlField { }
[PXString()]
[PXUIField(DisplayName = "Route Nbr")]
public string RouteNbr { get; set; }
public class routeNbr : IBqlField { }
[PXString()]
[PXUIField(DisplayName = "Acct Nbr")]
public string AcctNbr { get; set; }
public class acctNbr : IBqlField { }
}
Action Button:
public PXAction<CFBSContributionDetail> SetOnlineCheck;
[PXButton(CommitChanges = true)]
[PXUIField(DisplayName = "Set Online Check")]
protected void setOnlineCheck()
{
//NOTE: All online checks come in under the same bank number and routing number. There is no way to distinguish who's check it is without looking at the check.
SetOnlineCheckPopup.Current.RouteNbr = CurrentDocument.Current.RouteNbr;
SetOnlineCheckPopup.Current.AcctNbr = CurrentDocument.Current.AcctNbr;
if (SetOnlineCheckPopup.AskExt() == WebDialogResult.OK)
{
if (SetOnlineCheckPopup.Current.KnownContributor == null && SetOnlineCheckPopup.Current.NewContributor == null)
return;
CFBSContributionDetail row = CurrentDocument.Current;
//Create payment record for customer with bank and routing number
if (SetOnlineCheckPopup.Current.KnownContributor == null)
{
CreateNewCustomerPaymentMethod(row, SetOnlineCheckPopup.Current.NewContributor);
row.ContributorID = SetOnlineCheckPopup.Current.NewContributor;
}
else
{
row.ContributorID = SetOnlineCheckPopup.Current.KnownContributor;
}
}
}
NOTE: In the DAC, the first DAC I just included all cusotmers where in the 2nd DAC I left in the original filtering I'd like to have. I did this just to prove that it isn't the PXSelector(Search) section causing this issue.
I found it was because the selector was based on CustomerPaymentMethod. I switched it to be based on BAccount ([PXSelector(typeof
I'm creating a Process to import items from an external API source into records in Acumatica.
I've created an unbound DAC that is used to represent the entries available from the external API.
[Serializable]
public class ImportItem : IBqlTable
{
[PXBool]
[PXUIField(DisplayName = "Selected")]
public bool? Selected { get; set; }
public abstract class selected : IBqlField { }
[PXString]
[PXUIField(DisplayName = "External Ref Nbr")]
public string RefNbr { get; set; }
public abstract class refNbr : IBqlField { }
}
In a Process graph I implement the delegate of the main view to create and return the Resultset (normally generated from the API data). I then have a screen bound to this graph with a grid view that displays the items to allow the user to select the ones to import. The main Process delegate will then create the records in Acumatica for the selected items.
public class ImportItemsProcess : PXGraph<ImportItemsProcess>
{
public PXProcessing<ImportItem> ImportItems;
public PXCancel<ImportItem> Cancel;
public ImportItemsProcess()
{
ImportItems.SetProcessCaption("Import");
ImportItems.SetProcessAllCaption("Import All");
ImportItems.SetProcessDelegate(ProcessImportItems);
}
protected virtual IEnumerable importItems(PXAdapter adapter)
{
PXResultset<ImportItem> items = new PXResultset<ImportItem>();
/* Would create ImportItems from external API data here */
items.Add(new PXResult<ImportItem>(new ImportItem() { RefNbr = "1" }));
items.Add(new PXResult<ImportItem>(new ImportItem() { RefNbr = "2" }));
items.Add(new PXResult<ImportItem>(new ImportItem() { RefNbr = "3" }));
return items;
}
public static void ProcessImportItems(List<ImportItem> importItems)
{
throw new PXException("ProcessImportItems() has been called");
}
}
And the ASPX page:
<asp:Content ID="cont1" ContentPlaceHolderID="phDS" runat="Server">
<px:PXDataSource ID="ds" runat="server" Visible="True" Width="100%" PrimaryView="ImportItems" TypeName="APIImporter.ImportItemsProcess" >
</px:PXDataSource>
</asp:Content>
<asp:Content ID="cont2" ContentPlaceHolderID="phL" runat="Server">
<px:PXGrid ID="grid" runat="server" Height="400px" Width="100%" Style="z-index: 100"
AllowPaging="True" AllowSearch="True" AdjustPageSize="Auto" DataSourceID="ds" SkinID="Primary" TabIndex="1500" TemporaryFilterCaption="Filter Applied">
<Levels>
<px:PXGridLevel DataMember="ImportItems">
<RowTemplate>
<px:PXCheckBox ID="edSelected" runat="server" AlreadyLocalized="False" DataField="Selected" Text="Selected" CommitChanges="true">
</px:PXCheckBox>
<px:PXTextEdit ID="edRefNbr" runat="server" AlreadyLocalized="False" DataField="RefNbr" DefaultLocale="">
</px:PXTextEdit>
</RowTemplate>
<Columns>
<px:PXGridColumn DataField="Selected" TextAlign="Center" Type="CheckBox" Width="60px" CommitChanges="true">
</px:PXGridColumn>
<px:PXGridColumn DataField="RefNbr">
</px:PXGridColumn>
</Columns>
</px:PXGridLevel>
</Levels>
<AutoSize Container="Window" Enabled="True" MinHeight="200" />
</px:PXGrid>
</asp:Content>
When written as the simplified example here the Process delegate is never invoked. I suspect is has something to do with when the Process button is clicked the callback to the server runs the view's delegate function that re-creates the list of ImportItem objects and that the framework can not relate the newly created objects to the ones in the postback without a Key field. However, if I add an IsKey attribute to the RefNbr of the DAC...
[PXString(IsKey = true)]
[PXUIField(DisplayName = "External Ref Nbr")]
public string RefNbr { get; set; }
...now when selecting an item on the Screen I'm immediately given a line-level error with the message "Error: The argument is out of range. Parameter name: table".
In the data view delegate, you have to add the items to the Cache in addition to returning them. Try below:
protected virtual IEnumerable importItems()
{
int iCachedData = 0;
foreach (var row in ImportItems.Cache.Cached)
{
iCachedData++;
yield return row;
}
if (iCachedData == 0)
{
for (int iCounter = 1; iCounter <= 5; iCounter++)
{
ImportItem item = new ImportItem() { RefNbr = iCounter };
item = ImportItems.Insert(item);
ImportItems.Cache.SetStatus(item, PXEntryStatus.Held);
yield return item;
}
}
}
Good luck!
Try adding abstract classes for your DAC fields first.
If that doesn't resolve your issue please add your ASPX code to your question.
[Serializable]
public class ImportItem : IBqlTable
{
#region Selected
public abstract class selected : IBqlField { }
[PXBool]
[PXUIField(DisplayName = "Selected")]
public bool? Selected { get; set; }
#endregion
#region RefNbr
public abstract class refNbr : IBqlField { }
[PXString]
[PXUIField(DisplayName = "External Ref Nbr")]
public string RefNbr { get; set; }
#endregion
}
I have tried the Processing Graph functionality Using Bound DAC but not Database Table.
This is useful in the case when you want to Access some API and fetch the records in processing screen without saving into the Database. I am not including aspx code here.
The Highlight point in this code in [PXVirtualDAC] and the Key Field in DAC because of this we can achieve this functionality without creating a table. You can say that a Virtual DAC - without bound any database table.
public class TestUnboundProcessing : PXGraph<TestUnboundProcessing>
{
#region Unbound DAC
public class UnboundDAC : IBqlTable
{
#region Selected
[PXDBBool]
[PXUIField(DisplayName = "Selected")]
public virtual bool? Selected { get; set; }
public abstract class selected : PX.Data.IBqlField { }
#endregion
[PXDBString(50, IsUnicode = true,IsKey =true)]
[PXUIField(DisplayName = "Id")]
public string Id { get; set; }
public abstract class id : PX.Data.IBqlField { }
[PXDBString(100, IsUnicode = true)]
[PXUIField(DisplayName = "Author")]
public string Author { get; set; }
public abstract class author : PX.Data.IBqlField { }
[PXDBString(1000, IsUnicode = true)]
[PXUIField(DisplayName = "Body")]
public string Body { get; set; }
public abstract class body : PX.Data.IBqlField { }
}
#endregion
#region Processing Filter DAC
[Serializable]
public partial class TestFilter : PX.Data.IBqlTable
{
#region LastSyncDate
[PXDate()]
[PXDefault(typeof(AccessInfo.businessDate))]
[PXUIField(DisplayName = "Last Sync Date", Visibility = PXUIVisibility.Visible)]
public virtual DateTime? LastSyncDate { get; set; }
public abstract class lastSyncDate : PX.Data.IBqlField { }
#endregion
#region ProjectID
[PXDBString(10, IsUnicode = true)]
[PXUIField(DisplayName = "Project ID")]
public virtual String ProjectID { get; set; }
public abstract class projectID : PX.Data.IBqlField { }
#endregion
#region IssueID
[PXDBString(10, IsUnicode = true)]
[PXUIField(DisplayName = "Issue ID", Visibility = PXUIVisibility.SelectorVisible)]
public virtual String IssueID { get; set; }
public abstract class issueID : PX.Data.IBqlField { }
#endregion
}
#endregion
#region Filter + Delegate Overrides
public PXFilter<TestFilter> Filter;
public PXCancel<TestFilter> Cancel;
[PXVirtualDAC]
[PXFilterable]
public PXFilteredProcessing<UnboundDAC, TestFilter> UnboundView;
protected virtual IEnumerable unboundView()
{
GetUnboundDACList();
foreach (UnboundDAC item in UnboundView.Cache.Cached)
{
yield return item;
}
}
private void GetUnboundDACList()
{
UnboundView.Cache.Insert(new UnboundDAC() { Id = "1", Author = "Test 1", Body = "Comment 1" });
UnboundView.Cache.Insert(new UnboundDAC() { Id = "2", Author = "Test 2", Body = "Comment 2" });
UnboundView.Cache.Insert(new UnboundDAC() { Id = "3", Author = "Test 3", Body = "Comment 3" });
//return UnboundView.Cache;
}
#endregion
#region Constructor + Process
public TestUnboundProcessing()
{
TestFilter filter = Filter.Current;
UnboundView.SetProcessDelegate(delegate (List<UnboundDAC> docList)
{
UnboundProcessing(docList, filter);
}
);
}
#endregion
#region Processing functions
public static void UnboundProcessing(List<UnboundDAC> docList, TestFilter aFilter)
{
TestUnboundProcessing graph = CreateInstance<TestUnboundProcessing>();
graph.SaveRecords(graph, docList, aFilter);
}
public void SaveRecords(TestUnboundProcessing aProcessingGraph, List<UnboundDAC> docList, TestFilter aFilter)
{
bool isErrorOccured = false;
CRActivityMaint graph = PXGraph.CreateInstance<CRActivityMaint>();
foreach (UnboundDAC item in docList)
{
try
{
CRActivity addedActivity = new CRActivity();
addedActivity.UIStatus = ActivityStatusListAttribute.Completed;
addedActivity.Type = "N";
addedActivity.Location = item.Id;
addedActivity.Subject = item.Author;
addedActivity.Body = item.Body;
addedActivity.IsPrivate = true;
graph.Activities.Insert(addedActivity);
}
catch (Exception ex)
{
isErrorOccured = true;
PXProcessing<JRComment>.SetError(docList.IndexOf(item), ex.Message);
}
}
if (graph.Activities.Cache.Cached.Count() > 0)
{
graph.Actions.PressSave();
}
if (isErrorOccured)
{
throw new PXException("One or more record processed unsuccessful");
}
}
#endregion
#region Filter Events
protected virtual void TestFilter_LastSyncDate_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
{
UnboundView.Cache.Clear();
}
protected virtual void TestFilter_ProjectID_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
{
UnboundView.Cache.Clear();
}
protected virtual void TestFilter_IssueID_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e)
{
UnboundView.Cache.Clear();
}
#endregion
I try to create Acumatica processing page.
I have following aspx code:
<%# Page Language="C#" MasterPageFile="~/MasterPages/TabView.master" AutoEventWireup="true"
ValidateRequest="false" CodeFile="SM102000.aspx.cs"
Inherits="Page_SM102000" Title="Untitled Page" %>
<%# MasterType VirtualPath="~/MasterPages/TabView.master" %>
<asp:Content ID="cont1" ContentPlaceHolderID="phDS" runat="Server">
<px:PXDataSource ID="ds" runat="server" Visible="True" Width="100%"
PrimaryView="StackOverflowProcess" TypeName="StackOverflowSync.UsrStackOverflowProcess">
</px:PXDataSource>
</asp:Content>
<asp:Content ID="cont2" ContentPlaceHolderID="phF" runat="server">
<px:PXGrid ID="grid" runat="server"
Height="400px"
Width="100%"
AllowPaging="True"
AdjustPageSize="Auto"
AutoAdjustColumns="True"
AllowSearch="True"
SkinID="Inquire"
DataSourceID="ds"
NoteIndicator="true"
TabIndex="3300"
TemporaryFilterCaption="Filter Applied">
<Levels>
<px:PXGridLevel DataMember="StackOverflowProcess">
<Columns>
<px:PXGridColumn DataField="Selected" TextAlign="Center" Width="20px" Type="CheckBox" AllowCheckAll="True">
</px:PXGridColumn>
<px:PXGridColumn DataField="FailInfo" Width="20px">
</px:PXGridColumn>
<px:PXGridColumn DataField="SynchronizationType" Width="80px">
</px:PXGridColumn>
<px:PXGridColumn DataField="LastFullSync" TextAlign="Right" Width="100px">
</px:PXGridColumn>
<px:PXGridColumn DataField="LastRunCmt" Width="80px">
</px:PXGridColumn>
<px:PXGridColumn DataField="LastRunFld" TextAlign="Right" Width="100px">
</px:PXGridColumn>
</Columns>
</px:PXGridLevel>
</Levels>
<AutoSize Container="Window" Enabled="True" MinHeight="400" />
</px:PXGrid>
</asp:Content>
Following DAC class:
using PX.Data;
using System;
namespace StackOverflowSync.DAC
{
[Serializable()]
public class UsrStackOverflowSettingItem: IBqlTable
{
#region SettingID
public abstract class settingID : PX.Data.IBqlField
{
}
protected int _SettingID;
[PXDBIdentity(IsKey = true)]
public virtual int SettingID
{
get
{
return this._SettingID;
}
set
{
this._SettingID = value;
}
}
#endregion
#region FailInfo
public abstract class failInfo : PX.Data.IBqlField
{
}
protected string _FailInfo;
[PXDBString(255, IsUnicode = true)]
[PXDefault()]
[PXUIField(DisplayName = "")]
public virtual string FailInfo
{
get
{
return this._FailInfo;
}
set
{
this._FailInfo = value;
}
}
#endregion
#region Selected
public abstract class selected : IBqlField
{
}
protected bool? _Selected = false;
/// <summary>
/// Indicates whether the record is selected for mass processing.
/// </summary>
[PXBool]
[PXDefault(false)]
[PXUIField(DisplayName = "Selected")]
public bool? Selected
{
get
{
return _Selected;
}
set
{
_Selected = value;
}
}
#endregion
#region SynchronizationType
public abstract class synchronizationType : PX.Data.IBqlField
{
}
protected string _SynchronizationType;
[PXDBString(255, IsUnicode = true)]
[PXDefault()]
[PXUIField(DisplayName = "Synchronization Type")]
public virtual string SynchronizationType
{
get
{
return this._SynchronizationType;
}
set
{
this._SynchronizationType = value;
}
}
#endregion
#region LastFullSync
public abstract class lastFullSync : PX.Data.IBqlField
{
}
protected DateTime? _LastFullSync;
[PXDBDate()]
[PXDefault()]
[PXUIField(DisplayName = "Last Full Sync")]
public virtual DateTime? LastFullSync
{
get
{
return this._LastFullSync;
}
set
{
this._LastFullSync = value;
}
}
#endregion
#region LastRunCmt
public abstract class lastRunCmt : PX.Data.IBqlField
{
}
protected decimal? _LastRunCmt;
[PXDBDecimal(2)]
[PXDefault(TypeCode.Decimal, "0.0")]
[PXUIField(DisplayName = "Last Run: Records Commited")]
public virtual decimal? LastRunCmt
{
get
{
return this._LastRunCmt;
}
set
{
this._LastRunCmt = value;
}
}
#endregion
#region LastRunFld
public abstract class lastRunFld : PX.Data.IBqlField
{
}
protected decimal? _LastRunFld;
[PXDBDecimal(2)]
[PXDefault(TypeCode.Decimal, "0.0")]
[PXUIField(DisplayName = "Last Run: Records Failed")]
public virtual decimal? LastRunFld
{
get
{
return this._LastRunFld;
}
set
{
this._LastRunFld = value;
}
}
#endregion
#region CreatedByID
public abstract class createdByID : PX.Data.IBqlField
{
}
protected Guid? _CreatedByID;
[PXDBCreatedByID()]
public virtual Guid? CreatedByID
{
get
{
return this._CreatedByID;
}
set
{
this._CreatedByID = value;
}
}
#endregion
#region Tstamp
public abstract class tstamp : PX.Data.IBqlField
{
}
protected byte[] _Tstamp;
[PXDBTimestamp()]
public virtual byte[] Tstamp
{
get
{
return this._Tstamp;
}
set
{
this._Tstamp = value;
}
}
#endregion
#region CreatedByScreenID
public abstract class createdByScreenID : PX.Data.IBqlField
{
}
protected string _CreatedByScreenID;
[PXDBCreatedByScreenID()]
public virtual string CreatedByScreenID
{
get
{
return this._CreatedByScreenID;
}
set
{
this._CreatedByScreenID = value;
}
}
#endregion
#region CreatedDateTime
public abstract class createdDateTime : PX.Data.IBqlField
{
}
protected DateTime? _CreatedDateTime;
[PXDBCreatedDateTime()]
public virtual DateTime? CreatedDateTime
{
get
{
return this._CreatedDateTime;
}
set
{
this._CreatedDateTime = value;
}
}
#endregion
#region LastModifiedByID
public abstract class lastModifiedByID : PX.Data.IBqlField
{
}
protected Guid? _LastModifiedByID;
[PXDBLastModifiedByID()]
[PXUIField(DisplayName = "Last Modified By")]
public virtual Guid? LastModifiedByID
{
get
{
return this._LastModifiedByID;
}
set
{
this._LastModifiedByID = value;
}
}
#endregion
#region LastModifiedDateTime
public abstract class lastModifiedDateTime : PX.Data.IBqlField
{
}
protected DateTime? _LastModifiedDateTime;
[PXDBLastModifiedDateTime()]
[PXUIField(DisplayName = "Modified At")]
public virtual DateTime? LastModifiedDateTime
{
get
{
return this._LastModifiedDateTime;
}
set
{
this._LastModifiedDateTime = value;
}
}
#endregion
#region LastModifiedByScreenID
public abstract class lastModifiedByScreenID : PX.Data.IBqlField
{
}
protected string _LastModifiedByScreenID;
[PXDBLastModifiedByScreenID()]
public virtual string LastModifiedByScreenID
{
get
{
return this._LastModifiedByScreenID;
}
set
{
this._LastModifiedByScreenID = value;
}
}
#endregion
}
}
And following graph:
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using PX.Data;
using PX.SM;
using StackOverflowSync.DAC;
namespace StackOverflowSync
{
public class UsrStackOverflowProcess:PXGraph<UsrStackOverflowProcess>
{
[PXFilterable]
public PXProcessing<UsrStackOverflowSettingItem> StackOverflowProcess;
public PXCancel<UsrStackOverflowSettingItem> Cancel;
public UsrStackOverflowProcess()
{
StackOverflowProcess.SetProcessDelegate(Process);
}
public static void Process(List<UsrStackOverflowSettingItem> syncItems)
{
foreach (UsrStackOverflowSettingItem usrStackOverflowPrcSt in syncItems)
{
//
}
}
public override bool IsDirty => false;
}
}
If I compile and open all of it in my Acumatica instance I see three buttons: Process, and Process All and Cancel, which is expected behavior. When I check "Process All" then method Process receives all items. But if I select few items, and press at Process, method Process doesn't receive any ( I've checked in debugger ). What else should I add/remove to my code in order make button Process work properly?
Reported issue is caused by the non-nullable SettingID field. All DAC fields must be of a nullable type. Your custom screen should operate as expected after you change type of the SettingID property and the _SettingID field to Nullable<int> (int?):
[Serializable()]
public class UsrScanCoSettingItem : IBqlTable
{
#region SettingID
public abstract class settingID : PX.Data.IBqlField
{
}
protected int? _SettingID;
[PXDBIdentity(IsKey = true)]
public virtual int? SettingID
{
get
{
return this._SettingID;
}
set
{
this._SettingID = value;
}
}
#endregion
...
}
The problem is that in aspx you have the first column defined as:
<px:PXGridColumn DataField="Seleted" ...
While the name of the column you want is Selected. Looks like it is as easy as that.
I've been where you are and I think to solve it I had to build the list. Try something like the following.
//Invoices.SetProcessDelegate(SendData);
Invoices.SetProcessDelegate(delegate(List<ARInvoice> list)
{
List<ARInvoice> newlist = new List<ARInvoice>(list.Count);
foreach (ARInvoice doc in list)
{
newlist.Add(doc);
}
SendData(newlist);
});