Using approval for customize screen in Acumatica - acumatica

I added many custom screens to my Acumatica project and I want to add approval on these, so I found and followed this guide:
How to work with Assignment and Approval Maps in Acumatica via Automation Steps?
And this is my code:
My DAC:
[PXEMailSource]
[PXPrimaryGraph(typeof(ProductMaint))]
[Serializable]
public class PSSAProduct : IBqlTable, IAssign
{
#region ProductID
[PXDBIdentity(IsKey = true)]
[PXUIField(DisplayName = "Product ID")]
public virtual int? ProductID { get; set; }
public abstract class productID : IBqlField { }
#endregion
#region ProductCD
[PXDBString(50, IsUnicode = true)]
[PXUIField(DisplayName = "Product ID")]
public virtual string ProductCD { get; set; }
public abstract class productCD : IBqlField { }
#endregion
#region ProductName
[PXDBString(50, IsUnicode = true, InputMask = "")]
[PXUIField(DisplayName = "Product Name")]
public virtual string ProductName { get; set; }
public abstract class productName : IBqlField { }
#endregion
#region Active
[PXDBBool()]
[PXDefault(true, PersistingCheck = PXPersistingCheck.Nothing)]
[PXUIField(DisplayName = "Active")]
public virtual bool? Active { get; set; }
public abstract class active : IBqlField { }
#endregion
#region OwnerID
public abstract class ownerID : IBqlField { }
[PXDBGuid()]
[PX.TM.PXOwnerSelector()]
[PXUIField(DisplayName = "Owner")]
public virtual Guid? OwnerID { get; set; }
#endregion
#region WorkgroupID
public abstract class workgroupID : IBqlField { }
[PXDBInt()]
[PXSelector(typeof(Search<EPCompanyTree.workGroupID>), SubstituteKey = typeof(EPCompanyTree.description))]
[PX.TM.PXCompanyTreeSelector]
[PXUIField(DisplayName = "Workgroup", Enabled = false)]
public virtual int? WorkgroupID { get; set; }
#endregion
#region Hold
[PXDBBool()]
[PXUIField(DisplayName = "Hold", Visibility = PXUIVisibility.Visible)]
[PXDefault(true)]
//[PXNoUpdate] <- Saw this in the PO code, but had to remove so user could save stat of the Hold checkbox
public virtual bool? Hold { get; set; }
public abstract class hold : IBqlField { }
#endregion
#region Approved
public abstract class approved : IBqlField { }
[PXDBBool]
[PXDefault(false, PersistingCheck = PXPersistingCheck.Nothing)]
[PXUIField(DisplayName = "Approved", Visibility = PXUIVisibility.Visible, Enabled = false)]
public virtual bool? Approved { get; set; }
#endregion
#region Rejected
public abstract class rejected : IBqlField { }
[PXBool]
[PXDefault(false, PersistingCheck = PXPersistingCheck.Nothing)]
public bool? Rejected { get; set; }
#endregion
#region Status
[PXDBString(1)]
[PXDefault(PSSAProduct.Statuses.Hold)]
[PXUIField(DisplayName = "Status", Visibility = PXUIVisibility.SelectorVisible, Enabled = false)]
[Statuses.List]
public virtual string Status { get; set; }
public abstract class status : IBqlField { }
#endregion
}
My graph:
public class ProductMaint : PXGraph<ProductMaint, PSSAProduct>
{
public PXSelect<PSSAProduct> PSSAProductView;
public PXSelect<PSSASetupApproval> SetupApproval;
public EPApprovalAutomation<PSSAProduct, PSSAProduct.approved, PSSAProduct.rejected, PSSAProduct.hold, PSSASetupApproval> Approval;
}
My setup class:
[Serializable]
public class PSSASetup : IBqlTable
{
#region SARequestApproval
[EPRequireApproval]
[PXDefault(false, PersistingCheck = PXPersistingCheck.Null)]
[PXUIField(DisplayName = "SARequest Approval")]
public virtual bool? SARequestApproval { get; set; }
public abstract class sARequestApproval : IBqlField { }
#endregion
}
And my setup apprval class:
[Serializable]
public class PSSASetupApproval : IBqlTable, IAssignedMap
{
#region ApprovalID
[PXDBIdentity(IsKey = true)]
[PXUIField(DisplayName = "Approval ID")]
public virtual int? ApprovalID { get; set; }
public abstract class approvalID : IBqlField { }
#endregion
#region AssignmentMapID
[PXDefault()]
[PXDBInt()]
[PXSelector(typeof(Search<EPAssignmentMap.assignmentMapID, Where<EPAssignmentMap.entityType, Equal<AssignmentMapType.AssignmentMapTypeProduct>>>),
DescriptionField = typeof(EPAssignmentMap.name))]
[PXUIField(DisplayName = "Assignment Map ID")]
public virtual int? AssignmentMapID { get; set; }
public abstract class assignmentMapID : IBqlField { }
#endregion
#region AssignmentNotificationID
[PXDBInt()]
[PXSelector(typeof(PX.SM.Notification.notificationID), SubstituteKey = typeof(PX.SM.Notification.name))]
[PXUIField(DisplayName = "Pending Approval Notification")]
public virtual int? AssignmentNotificationID { get; set; }
public abstract class assignmentNotificationID : IBqlField { }
#endregion
#region IsActive
[PXDBBool()]
[PXDefault(typeof(Search<PSSASetup.sARequestApproval>), PersistingCheck = PXPersistingCheck.Nothing)]
[PXUIField(DisplayName = "Is Active")]
public virtual bool? IsActive { get; set; }
public abstract class isActive : IBqlField { }
#endregion
}
public static class AssignmentMapType
{
public class AssignmentMapTypeProduct : Constant<string>
{
public AssignmentMapTypeProduct() : base(typeof(PSSAProduct).FullName) { }
}
}
My problem is if I put Acumatica's DAC as SourceAssign of EPApprovalAutomation, it has no error. But if I use my DAC, it throws "Value cannot be null" inside the framework's code.
I checked carefully what the difference between two DACs but I don't realize anything.
My version Acumatica is 17R2.
If you know, please tell me what am I missing.
Thanks all!

Normaly, this error "Value cannot be null" is related either to table being defined in the DB with NOT NULL fields or to a DAC with a PXDefault attribute that is empty while trying to persist to DB.
If you used the example in your link and you created your DB table with the audit fields:
CreatedByID uniqueidentifier NOT NULL,
CreatedByScreenID char(8) NOT NULL,
CreatedDateTime datetime NOT NULL,
LastModifiedByID uniqueidentifier NOT NULL,
LastModifiedByScreenID char(8) NOT NULL,
LastModifiedDateTime datetime NOT NULL,
Tstamp timestamp NULL,
make sure you add them to your DAC as well:
#region tstamp
public abstract class Tstamp : IBqlField { }
[PXDBTimestamp()]
public virtual byte[] tstamp { get; set; }
#endregion
#region CreatedByID
public abstract class createdByID : IBqlField { }
[PXDBCreatedByID()]
public virtual Guid? CreatedByID { get; set; }
#endregion
#region CreatedByScreenID
public abstract class createdByScreenID : IBqlField { }
[PXDBCreatedByScreenID()]
public virtual string CreatedByScreenID { get; set; }
#endregion
#region CreatedDateTime
public abstract class createdDateTime : IBqlField { }
[PXDBCreatedDateTime()]
public virtual DateTime? CreatedDateTime { get; set; }
#endregion
#region LastModifiedByID
public abstract class lastModifiedByID : IBqlField { }
[PXDBLastModifiedByID()]
public virtual Guid? LastModifiedByID { get; set; }
#endregion
#region LastModifiedByScreenID
public abstract class lastModifiedByScreenID : IBqlField { }
[PXDBLastModifiedByScreenID()]
public virtual string LastModifiedByScreenID { get; set; }
#endregion
#region LastModifiedDateTime
public abstract class lastModifiedDateTime : IBqlField { }
[PXDBLastModifiedDateTime()]
public virtual DateTime? LastModifiedDateTime { get; set; }
#endregion
Also check in your code these two possible candidates for PXDefault:
In the Status field of the PSSAProduct DAC you use [PXDefault(PSSAProduct.Statuses.Hold)]. Check you have defined PSSAProduct.Statuses correctly.
In the AssignmentMapID field of the PSSASetupApproval DAC, the PXSelector [PXSelector(typeof(Search<EPAssignmentMap.assignmentMapID, Where<EPAssignmentMap.entityType, Equal<AssignmentMapType.AssignmentMapTypeProduct>>>), DescriptionField = typeof(EPAssignmentMap.name))] could not be finding any records. You could test with a view to make sure it is working.

I managed to check the different between two class and then I found the answer: I missed NoteID field
#region NoteID
public abstract class noteID : PX.Data.IBqlField
{
}
protected Guid? _NoteID;
[PXNote(new Type[0], ShowInReferenceSelector = true)]
public virtual Guid? NoteID
{
get
{
return this._NoteID;
}
set
{
this._NoteID = value;
}
}
#endregion

Related

blank row being added on insert

I have a very simple Maint page, it has no events
public PXCancel<RCDistributorSalesStat> Cancel;
public PXSave<RCDistributorSalesStat> Save;
public SelectFrom<RCDistributorSalesStat>.View Records;
and everytime I save the first record entered I get an error and it has added a second blank line and throws errors that required fields are missing.
I found a similar question to this on here about having multiple ISKey set, though I only have one.
#region DistributorSalesStatID
[PXDBIdentity(IsKey = true)]
public virtual int? DistributorSalesStatID { get; set; }
public abstract class distributorSalesStatID : PX.Data.BQL.BqlInt.Field<distributorSalesStatID> { }
#endregion
#region InventoryID
[StockItem(Visibility = PXUIVisibility.SelectorVisible, DescriptionField = typeof(InventoryItem.inventoryCD), Enabled = true)]
[PXRestrictor(typeof(Where<InventoryItem.baseUnit.IsEqual<BQLConstants.Straw>>), null)]
[PXForeignReference(typeof(Field<inventoryID>.IsRelatedTo<InventoryItem.inventoryID>))]
public virtual int? InventoryID { get; set; }
public abstract class inventoryID : PX.Data.BQL.BqlInt.Field<inventoryID> { }
#endregion
#region StatisticDate
[PXDBDate()]
[PXUIField(DisplayName = "Statistic Date")]
public virtual DateTime? StatisticDate { get; set; }
public abstract class statisticDate : PX.Data.BQL.BqlDateTime.Field<statisticDate> { }
#endregion
#region CustomerID
[CustomerActive(
//typeof(Search<BAccountR.bAccountID, Where<True, Equal<True>>>),
Visibility = PXUIVisibility.SelectorVisible,
DescriptionField = typeof(Customer.acctName),
Filterable = true) ]
//[CustomerOrOrganizationInNoUpdateDocRestrictor]
[PXForeignReference(typeof(Field<RCDistributorSalesStat.customerID>.IsRelatedTo<PXAccess.BAccount.bAccountID>))]
public virtual int? CustomerID { get; set; }
public abstract class customerID : PX.Data.BQL.BqlInt.Field<customerID> { }
#endregion
#region CustomerLocationID
[LocationActive(typeof(Where<Location.bAccountID, Equal<Current<customerID>>,
And<MatchWithBranch<Location.cBranchID>>>),
DescriptionField = typeof(Location.descr),
Visibility = PXUIVisibility.SelectorVisible)]
[PXDefault(typeof(Coalesce<Search2<BAccountR.defLocationID,
InnerJoin<CRLocation, On<CRLocation.bAccountID, Equal<BAccountR.bAccountID>, And<CRLocation.locationID, Equal<BAccountR.defLocationID>>>>,
Where<BAccountR.bAccountID, Equal<Current<customerID>>,
And<CRLocation.isActive, Equal<True>,
And<MatchWithBranch<CRLocation.cBranchID>>>>>,
Search<CRLocation.locationID,
Where<CRLocation.bAccountID, Equal<Current<customerID>>,
And<CRLocation.isActive, Equal<True>, And<MatchWithBranch<CRLocation.cBranchID>>>>>>))]
[PXForeignReference(
typeof(CompositeKey<
Field<customerID>.IsRelatedTo<Location.bAccountID>,
Field<customerLocationID>.IsRelatedTo<Location.locationID>
>))]
public virtual int? CustomerLocationID { get; set; }
public abstract class customerLocationID : PX.Data.BQL.BqlInt.Field<customerLocationID> { }
#endregion
#region SalesAmount
[PXDBDecimal()]
[PXUIField(DisplayName = "Sales Amount")]
public virtual Decimal? SalesAmount { get; set; }
public abstract class salesAmount : PX.Data.BQL.BqlDecimal.Field<salesAmount> { }
#endregion
#region SalesUnits
[PXDBInt()]
[PXUIField(DisplayName = "Sales Units")]
public virtual int? SalesUnits { get; set; }
public abstract class salesUnits : PX.Data.BQL.BqlInt.Field<salesUnits> { }
#endregion
#region QuantityOnHand
[PXDBInt()]
[PXUIField(DisplayName = "Quantity On Hand")]
public virtual int? QuantityOnHand { get; set; }
public abstract class quantityOnHand : PX.Data.BQL.BqlInt.Field<quantityOnHand> { }
#endregion
I am unsure what is causing the unwanted row to try and persist.
I have changed my keys around, I removed my IsKey on the PXDBIdentity and set two other fields as the key in the DAC and the DB. That seems to have the desired effect as no blank row gets created during Save. Upon further testing it appears that for our listview page I was missing "px:Selector" rows inside of my RowTemplate in my Grid.

How to apply PXForeignReference in a new maintenance screen

I have created maintenance tables and I am applying a "PXForeignReference" at drop time.
what I want is that if I delete a record from the table that is being used in another, it should show a message which indicates that it is being used in another table.
But in my example it is not being fulfilled, for varo I will indicate where my error is.
here I attach some images.
1.first maintenance table
namespace SKPriceIndex
{
[Serializable]
[PXCacheName("PESKPriceIndexCat")]
public class PESKPriceIndexCat : IBqlTable
{
#region CategoryID
[PXDBIdentity]
[PXUIField(DisplayName = "Category ID")]
public virtual int? CategoryID { get; set; }
public abstract class categoryID : PX.Data.BQL.BqlInt.Field<categoryID> { }
#endregion
#region CategoryCD
[PXDBString(10, IsUnicode = true, InputMask = "", IsKey = true)]
[PXUIField(DisplayName = "Category ID")]
public virtual string CategoryCD { get; set; }
public abstract class categoryCD : PX.Data.BQL.BqlString.Field<categoryCD> { }
#endregion
#region Descripcion
[PXDBString(30, IsUnicode = true, InputMask = "")]
[PXUIField(DisplayName = "Category Description")]
public virtual string Descripcion { get; set; }
public abstract class descripcion : PX.Data.BQL.BqlString.Field<descripcion> { }
#endregion
#region State
[PXDBString(100, IsUnicode = true, InputMask = "")]
[PXUIField(DisplayName = "State")]
[PXSelector(typeof(State.stateID),
typeof(State.stateID), typeof(State.name), typeof(State.countryID),
DescriptionField = typeof(State.name))]
[PXMassMergableField]
public virtual string State { get; set; }
public abstract class state : PX.Data.BQL.BqlString.Field<state> { }
#endregion
#region Active
[PXDBBool()]
[PXDefault(false)]
[PXUIField(DisplayName = "Active")]
public virtual bool? Active { get; set; }
public abstract class active : PX.Data.BQL.BqlInt.Field<active> { }
#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 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 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()]
public virtual Guid? LastModifiedByID
{
get
{
return this._LastModifiedByID;
}
set
{
this._LastModifiedByID = 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
#region LastModifiedDateTime
public abstract class lastModifiedDateTime : PX.Data.IBqlField
{
}
protected DateTime? _LastModifiedDateTime;
[PXDBLastModifiedDateTime]
public virtual DateTime? LastModifiedDateTime
{
get
{
return this._LastModifiedDateTime;
}
set
{
this._LastModifiedDateTime = value;
}
}
#endregion
}
}
2.second maintenance table
namespace SKPriceIndexCST
{
[Serializable]
[PXCacheName("PESKPriceIndex")]
public class PESKPriceIndex : IBqlTable
{
#region CategoryID
[PXDBInt(IsKey = true)]
[PXUIField(DisplayName = "Category ID")]
[PXSelector(typeof(PESKPriceIndexCat.categoryID),
typeof(PESKPriceIndexCat.categoryCD),
typeof(PESKPriceIndexCat.descripcion),
DescriptionField = typeof(PESKPriceIndexCat.descripcion),
SubstituteKey = typeof(PESKPriceIndexCat.categoryCD))]
[PXMassMergableField]
[PXRestrictor(typeof(Where<PESKPriceIndexCat.active, Equal<True>>), "", typeof(PESKPriceIndexCat.active))]
public virtual int? CategoryID { get; set; }
public abstract class categoryID : PX.Data.BQL.BqlInt.Field<categoryID> { }
#endregion
#region CreatedDateTime
[PXDBCreatedDateTime()]
public virtual DateTime? CreatedDateTime { get; set; }
public abstract class createdDateTime : PX.Data.BQL.BqlDateTime.Field<createdDateTime> { }
#endregion
#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 LastModifiedDateTime
[PXDBLastModifiedDateTime()]
public virtual DateTime? LastModifiedDateTime { get; set; }
public abstract class lastModifiedDateTime : PX.Data.BQL.BqlDateTime.Field<lastModifiedDateTime> { }
#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 Tstamp
[PXDBTimestamp()]
[PXUIField(DisplayName = "Tstamp")]
public virtual byte[] Tstamp { get; set; }
public abstract class tstamp : PX.Data.BQL.BqlByteArray.Field<tstamp> { }
#endregion
}
}
namespace SKPriceIndexCST
{
[Serializable]
[PXCacheName("PESKPriceIndexDetail")]
public class PESKPriceIndexDetail : IBqlTable
{
#region PriceIndexID
[PXDBIdentity]
public virtual int? PriceIndexID { get; set; }
public abstract class priceIndexID : PX.Data.BQL.BqlInt.Field<priceIndexID> { }
#endregion
#region CategoryID
[PXDBInt(IsKey =true)]
[PXUIField(DisplayName = "Category ID")]
[PXDefault(typeof(PESKPriceIndex.categoryID))]
[PXParent(typeof(Select<PESKPriceIndex, Where<PESKPriceIndex.categoryID, Equal<Current<categoryID>>>>),LeaveChildren =true)] //,LeaveChildren =true
[PXForeignReference(typeof(Field<categoryID>.IsRelatedTo<PESKPriceIndexCat.categoryID>))]
public virtual int? CategoryID { get; set; }
public abstract class categoryID : PX.Data.BQL.BqlInt.Field<categoryID> { }
#endregion
#region EffDate
[PXDBDate(IsKey =true)]
[PXDefault(typeof(AccessInfo.businessDate))]
[PXUIField(DisplayName = "Eff Date")]
public virtual DateTime? EffDate { get; set; }
public abstract class effDate : PX.Data.BQL.BqlDateTime.Field<effDate> { }
#endregion
#region BaseIndex
[PXDBInt()]
[PXUIField(DisplayName = "Base Index")]
public virtual int? BaseIndex { get; set; }
public abstract class baseIndex : PX.Data.BQL.BqlInt.Field<baseIndex> { }
#endregion
#region CreatedDateTime
[PXDBCreatedDateTime()]
public virtual DateTime? CreatedDateTime { get; set; }
public abstract class createdDateTime : PX.Data.BQL.BqlDateTime.Field<createdDateTime> { }
#endregion
#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 LastModifiedDateTime
[PXDBLastModifiedDateTime()]
public virtual DateTime? LastModifiedDateTime { get; set; }
public abstract class lastModifiedDateTime : PX.Data.BQL.BqlDateTime.Field<lastModifiedDateTime> { }
#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 Tstamp
[PXDBTimestamp()]
[PXUIField(DisplayName = "Tstamp")]
public virtual byte[] Tstamp { get; set; }
public abstract class tstamp : PX.Data.BQL.BqlByteArray.Field<tstamp> { }
#endregion
}
}
when I delete the price index record, it doesn't restrict the deletion.
I hope my query is clear.
Thanks in advance.
As far as I know for referential integrity you need to only assign foreign references and when such references are attempted to be deleted they should be prevented from deletion. There is an example of this in the INTran DAC
Taking the INTran example first they defined the FK
public static class FK
{
public class Site : INSite.PK.ForeignKeyOf<INTran>.By<siteID> { }
}
Then they defined the field as follows
#region SiteID
public abstract class siteID : PX.Data.BQL.BqlInt.Field<siteID> { }
[IN.SiteAvail(typeof(INTran.inventoryID), typeof(INTran.subItemID))]
[PXDefault(typeof(INRegister.siteID))]
[PXForeignReference(typeof(FK.Site))]
[InterBranchRestrictor(typeof(Where<SameOrganizationBranch<INSite.branchID, Current<INRegister.branchID>>>))]
public virtual Int32? SiteID {get; set;}
#endregion
On the other hand the PXParent attribute which specifies the master-detail relationship does not prevent deletion. Although it has the LeaveChildren Attribute which defaults to false, and thus deletes the child data record when the parent is deleted.

How do I enable custom attributes? (can assign on class, but not displaying for transaction)

I defined several new screens in Acumatica 2018R2 and need to enable attribute support according the class assigned to the transaction. The class screen appears to allow attaching attributes, but the transactional screen does not display any attribute. I found several examples on Stack Overflow, but the only clue I see seems to be that if the class id cannot be determined then no attributes will show. However, upon debug, I see that the class id is found.
This is my test code where I have tried stripping down to a very basic test.
AAClass
using PX.Data;
using System;
namespace Attributes
{
[Serializable]
public class AAClass : IBqlTable
{
#region ClassID
[PXDBIdentity]
[PXUIField(DisplayName = "Class ID")]
public virtual int? ClassID { get; set; }
public abstract class classID : IBqlField { }
#endregion
#region ClassCD
[PXDBString(15, IsKey = true, IsUnicode = true, InputMask = "")]
[PXUIField(DisplayName = "Class CD")]
public virtual string ClassCD { get; set; }
public abstract class classCD : IBqlField { }
#endregion
#region Descr
[PXDBString(256, IsUnicode = true, InputMask = "")]
[PXUIField(DisplayName = "Descr")]
public virtual string Descr { get; set; }
public abstract class descr : IBqlField { }
#endregion
#region CreatedByID
[PXDBCreatedByID()]
public virtual Guid? CreatedByID { get; set; }
public abstract class createdByID : IBqlField { }
#endregion
#region CreatedByScreenID
[PXDBCreatedByScreenID()]
public virtual string CreatedByScreenID { get; set; }
public abstract class createdByScreenID : IBqlField { }
#endregion
#region CreatedDateTime
[PXDBCreatedDateTime()]
[PXUIField(DisplayName = SSCS.IN.Messages.FldCreatedDateTime)]
public virtual DateTime? CreatedDateTime { get; set; }
public abstract class createdDateTime : IBqlField { }
#endregion
#region LastModifiedByID
[PXDBLastModifiedByID()]
public virtual Guid? LastModifiedByID { get; set; }
public abstract class lastModifiedByID : IBqlField { }
#endregion
#region LastModifiedByScreenID
[PXDBLastModifiedByScreenID()]
public virtual string LastModifiedByScreenID { get; set; }
public abstract class lastModifiedByScreenID : IBqlField { }
#endregion
#region LastModifiedDateTime
[PXDBLastModifiedDateTime()]
[PXUIField(DisplayName = SSCS.IN.Messages.FldLastModifiedDateTime)]
public virtual DateTime? LastModifiedDateTime { get; set; }
public abstract class lastModifiedDateTime : IBqlField { }
#endregion
#region Tstamp
[PXDBTimestamp()]
public virtual byte[] Tstamp { get; set; }
public abstract class tstamp : IBqlField { }
#endregion
#region NoteID
[PXNote]
public virtual Guid? NoteID { get; set; }
public abstract class noteID : IBqlField { }
#endregion
}
}
AAClassMaint
using PX.Data;
using PX.Objects.CR;
namespace Attributes
{
public class AAClassMaint : PXGraph<AAClassMaint, AAClass>
{
#region Data Views
[PXViewName("Classes")]
public PXSelect<AAClass> Classes;
[PXViewName("Attributes")]
public CSAttributeGroupList<AAClass, AATag> Mapping;
#endregion
}
}
AATag
using PX.Data;
using PX.Objects.CR;
using PX.Objects.CS;
using System;
namespace Attributes
{
[Serializable]
public class AATag : IBqlTable
{
#region TagID
[PXDBIdentity]
public virtual int? TagID { get; set; }
public abstract class tagID : IBqlField { }
#endregion
#region TagCD
[PXDBString(15, IsKey = true, IsUnicode = true, InputMask = "")]
[PXUIField(DisplayName = "Tag ID")]
public virtual string TagCD { get; set; }
public abstract class tagCD : IBqlField { }
#endregion
#region MyClassID
[PXDBInt()]
[PXSelector(
typeof(AAClass.classID),
typeof(AAClass.classCD),
typeof(AAClass.descr),
SubstituteKey = typeof(AAClass.classCD)
)]
[PXUIField(DisplayName = "My Class ID")]
public virtual int? MyClassID { get; set; }
public abstract class myClassID : IBqlField { }
#endregion
#region CreatedByID
[PXDBCreatedByID()]
public virtual Guid? CreatedByID { get; set; }
public abstract class createdByID : IBqlField { }
#endregion
#region CreatedByScreenID
[PXDBCreatedByScreenID()]
public virtual string CreatedByScreenID { get; set; }
public abstract class createdByScreenID : IBqlField { }
#endregion
#region CreatedDateTime
[PXDBCreatedDateTime()]
[PXUIField(DisplayName = SSCS.IN.Messages.FldCreatedDateTime)]
public virtual DateTime? CreatedDateTime { get; set; }
public abstract class createdDateTime : IBqlField { }
#endregion
#region LastModifiedByID
[PXDBLastModifiedByID()]
public virtual Guid? LastModifiedByID { get; set; }
public abstract class lastModifiedByID : IBqlField { }
#endregion
#region LastModifiedByScreenID
[PXDBLastModifiedByScreenID()]
public virtual string LastModifiedByScreenID { get; set; }
public abstract class lastModifiedByScreenID : IBqlField { }
#endregion
#region LastModifiedDateTime
[PXDBLastModifiedDateTime()]
[PXUIField(DisplayName = SSCS.IN.Messages.FldLastModifiedDateTime)]
public virtual DateTime? LastModifiedDateTime { get; set; }
public abstract class lastModifiedDateTime : IBqlField { }
#endregion
#region Tstamp
[PXDBTimestamp()]
public virtual byte[] Tstamp { get; set; }
public abstract class tstamp : IBqlField { }
#endregion
#region NoteID
[PXNote]
public virtual Guid? NoteID { get; set; }
public abstract class noteID : IBqlField { }
#endregion
#region Attributes
public abstract class attributes : IBqlField { }
[CRAttributesField(typeof(AATag.myClassID))]
public virtual string[] Attributes { get; set; }
public virtual int? ClassID
{
get { return MyClassID; }
}
#endregion
}
}
AATagEntry
using PX.Data;
using PX.Objects.CR;
namespace Attributes
{
public class AATagEntry : PXGraph<AATagEntry, AATag>
{
#region Data Views
[PXViewName("Classes")]
public PXSelect<AATag> Tags;
[PXViewName("Answers")]
public CRAttributeList<AATag> Answers;
#endregion
}
}
As I understand the various posts for this topic, the key requirements are:
Define MAPPING in the class maintenance screen
[PXViewName("Attributes")]
public CSAttributeGroupList<AAClass, AATag> Mapping;
Define ANSWERS in the transactional entry screen
[PXViewName("Answers")]
public CRAttributeList<AATag> Answers;
Include NoteID in the transactional table
Add ATTRIBUTES to the transactional table, including a field for ClassID
public abstract class attributes : IBqlField { }
[CRAttributesField(typeof(AATag.myClassID))]
public virtual string[] Attributes { get; set; }
public virtual int? ClassID
{
get { return MyClassID; }
}
CRAttributesField attribute will build a request with the generic type (classIdField) provided in it's parameter. In your case that would be AATag.myClassID :
protected static Type GetAttributesSearchCommand(Type classIdField)
{
var cmd = BqlCommand.Compose(typeof (Search2<,,>), typeof (CSAttribute.attributeID),
typeof (InnerJoin<,>), typeof (CSAttributeGroup),
typeof (On<,>), typeof (CSAttributeGroup.attributeID), typeof (Equal<>),
typeof (CSAttribute.attributeID),
typeof(Where<,,>), typeof(CSAttributeGroup.entityType), typeof(Equal<>), typeof(Required<>), typeof(CSAttributeGroup.entityType),
typeof (And<,>), typeof (CSAttributeGroup.entityClassID), typeof (Equal<>), typeof (Current<>),
classIdField);
return cmd;
}
So the key part of the request where clause will be:
And<CSAttributeGroup.entityClassID, Equal<Current<AATag.myClassID>>>
This means you must have a current AATag record in memory:
Caches[typeof(AATag)].Current
I suggest you first trace the value (Help->Trace window) to validate it's non null:
public void AATag_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
if (e.Row is AATag)
{
PXTrace.WriteInformation(((AATag)e.Row).myClassID);
}
}
If it is null, you will have to join AATag in the relevant DataView (preferred) or set it explicitely in some events.
Using Acumatica Request Profiler (SM205070
) can also help to determine why the request is not returning any record.
As HB_Acumatica suggested in the comments, the issue appeared to be with the ClassID fields being defined as integer.
I removed the original ClassID (int) fields and renamed the ClassCD (string) fields in the Class DAC's. Next, I changed the ClassID fields in the transactional DAC's to strings. Problem solved. The attribute tab on the transactional entry screens now display the attributes assigned to the class selected.
Lesson learned: A "Class" DAC should not have a ClassID/ClassCD pair, but rather a simple string ClassID field. (Typically named specific to the type of class, i.e. TagClassID) The Class ID used in the transactional tables will then use the string Class ID.

InventoryItem BasePrice causing "cast not valid"

I am trying to create a processing page to extract data from several tables which will be used to update the EDI service item list. I keep getting an error that states the specified cast for BasePrice is not valid.
This is simply an internal DAC in the BLC. There is no physical table in the database. If I exclude my BasePrice field everything works fine. If I include it, the insert gets the error. See the code below.
public class EDInventoryProcess : PXGraph<EDInventoryProcess>
{
public PXCancel<EDInventoryFilter> Cancel;
public PXFilter<EDInventoryFilter> Filter;
[PXFilterable]
public PXFilteredProcessingOrderBy<EDInventory, EDInventoryFilter,
OrderBy<Asc<EDInventory.partnerID, Asc<EDInventory.inventoryCD>>>> EDItem;
protected virtual IEnumerable eDItem()
{
EDItem.Cache.Clear();
var cmd = new PXSelectJoin<InventoryItem,
InnerJoin<INItemXRef, On<InventoryItem.inventoryID, Equal<INItemXRef.inventoryID>>,
InnerJoin<EDPartnerInfo, On<INItemXRef.bAccountID, Equal<EDPartnerInfo.customerID>>>>,
Where<INItemXRef.alternateType, Equal<INAlternateType.cPN>,
And<InventoryItem.itemStatus, Equal<InventoryItemStatus.active>>>>(this);
cmd.View.Clear();
var ret = cmd.Select();
if (ret != null)
{
EDInventoryFilter filt = (EDInventoryFilter)Filter.Cache.Current;
EDInventory edInv = new EDInventory();
foreach (PXResult<InventoryItem, INItemXRef, EDPartnerInfo> record in ret)
{
edInv = new EDInventory();
InventoryItem item = (InventoryItem)record;
INItemXRef xref = (INItemXRef)record;
EDPartnerInfo partner = (EDPartnerInfo)record;
edInv.PartnerID = partner.PartnerID;
edInv.InventoryID = item.InventoryID;
edInv.InventoryCD = item.InventoryCD;
edInv.ItemDescr = item.Descr;
edInv.ItemStatus = item.ItemStatus;
edInv.BaseUnit = item.BaseUnit;
edInv.SalesUnit = item.SalesUnit;
edInv.PurchaseUnit = item.PurchaseUnit;
edInv.BasePrice = Convert.ToDecimal(item.BasePrice);
//This is the lint that generates the error.
edInv = EDItem.Insert(edInv);
EDItem.Cache.SetStatus(edInv, PXEntryStatus.Held);
yield return edInv;
}
}
EDItem.Cache.IsDirty = false;
}
Here is the DAC definition:
[Serializable]
public partial class EDInventoryFilter : IBqlTable
{
#region TradingPartner
public abstract class tradingPartner : PX.Data.IBqlField
{
}
protected string _TradingPartner;
[PXString(15)]
[PXUIField(DisplayName = "Trading Partner")]
[PXStringList(new string[] { }, new string[] { })]
public virtual String TradingPartner { get; set; }
#endregion
#region Action
public abstract class action : PX.Data.IBqlField { }
protected string _Action;
[PXString(15)]
[PXUIField(DisplayName = "Action")]
[PXStringList(new string[] { "P" }, new string[] { "Push to EDI" })]
public virtual String Action { get; set; }
#endregion
}
[Serializable]
public partial class EDInventory : IBqlTable
{
#region PartnerID
public abstract class partnerID : IBqlField { }
[PXString(30, IsUnicode = true, IsKey = true)]
[PXDefault("")]
[PXUIField(DisplayName = "Partner")]
public virtual string PartnerID { get; set; }
#endregion
#region InventoryID
public abstract class inventoryID : PX.Data.IBqlField { }
protected Int32? _InventoryID;
[PXInt]
[PXUIField(DisplayName = "Inventory ID", Visibility = PXUIVisibility.Visible, Visible = false)]
public virtual Int32? InventoryID { get; set; }
#endregion
#region InventoryCD
public abstract class inventoryCD : PX.Data.IBqlField { }
protected String _InventoryCD;
[PXDefault()]
[InventoryRaw(IsKey = true, DisplayName = "Inventory ID")]
public virtual String InventoryCD { get; set; }
#endregion
#region ItemDescr
public abstract class itemDescr : PX.Data.IBqlField { }
protected String _ItemDescr;
[PXString(255, IsUnicode = true)]
[PXUIField(DisplayName = "Item Description")]
public virtual String ItemDescr { get; set; }
#endregion
#region ItemStatus
public abstract class itemStatus : PX.Data.IBqlField { }
protected String _ItemStatus;
[PXString(2, IsFixed = true)]
[PXDefault("AC")]
[PXUIField(DisplayName = "Item Status", Visibility = PXUIVisibility.SelectorVisible)]
public virtual String ItemStatus { get; set; }
#endregion
#region BaseUnit
public abstract class baseUnit : PX.Data.IBqlField { }
protected String _BaseUnit;
[PXString]
[PXDefault("")]
[PXUIField(DisplayName = "Base Unit", Visibility = PXUIVisibility.SelectorVisible)]
public virtual String BaseUnit { get; set; }
#endregion
#region SalesUnit
public abstract class salesUnit : PX.Data.IBqlField { }
protected String _SalesUnit;
[PXString]
[PXDefault("")]
[PXUIField(DisplayName = "Sales Unit", Visibility = PXUIVisibility.SelectorVisible)]
public virtual String SalesUnit { get; set; }
#endregion
#region PurchaseUnit
public abstract class purchaseUnit : PX.Data.IBqlField { }
protected String _PurchaseUnit;
[PXString]
[PXDefault("")]
[PXUIField(DisplayName = "Purchase Unit", Visibility = PXUIVisibility.SelectorVisible)]
public virtual String PurchaseUnit { get; set; }
#endregion
#region BasePrice
public abstract class basePrice : PX.Data.IBqlField { }
protected Decimal? _BasePrice;
[PXPriceCost()]
[PXDefault(0.0)]
[PXUIField(DisplayName = "Default Price", Visibility = PXUIVisibility.SelectorVisible)]
public virtual Decimal? BasePrice { get; set; }
#endregion
}
I just discovered what might be the answer. I was listing the default value as
[PXDefault(0.0)]
but I found another reference for decimal default as
[PXDefault(TypeCode.Decimal, "0.0")]
which seems to work. I no longer get the error and my processing screen displays as expected. I had assumed the default value drew its type from the object.
Yes for Decimal fields set [PXDefault(TypeCode.Decimal, "0.0")] attribute to avoid casting issues.

How to filter data using custom fields in Process Shipment in acumatica?

I am trying to filter records in the Process Shipment filter using user defined fields in SOOrder table.
I have overridden the entire Order function in Extension of SOShipmentFilter and added my condition to filter data based on SOOrder custom fields.
switch (sAlias)
{
case "EMB": ((PXSelectBase<SOShipment>)cmd).WhereAnd<Where<SOOrderExtNV.usrIsEmbroidery, Equal<True>>>(); break;
case "SNP": ((PXSelectBase<SOShipment>)cmd).WhereAnd<Where<SOOrderExtNV.usrIsScreenPrint, Equal<True>>>(); break;
case "PRO": ((PXSelectBase<SOShipment>)cmd).WhereAnd<Where<SOOrderExtNV.usrIsPromo, Equal<True>>>(); break;
case "FUL": ((PXSelectBase<SOShipment>)cmd).WhereAnd<Where<SOOrderExtNV.usrIsFulfilment, Equal<True>>>(); break;
case "BLK": ((PXSelectBase<SOShipment>)cmd).WhereAnd<Where<SOOrderExtNV.usrIsBlank, Equal<True>>>(); break;
case "SMP": ((PXSelectBase<SOShipment>)cmd).WhereAnd<Where<SOOrderExtNV.usrIsSample, Equal<True>>>(); break;
case "IRN": ((PXSelectBase<SOShipment>)cmd).WhereAnd<Where<SOOrderExtNV.usrIsOronOn, Equal<True>>>(); break;
case "DP": ((PXSelectBase<SOShipment>)cmd).WhereAnd<Where<SOOrderExtNV.usrIsDigitalPrint, Equal<True>>>(); break;
case "BAN": ((PXSelectBase<SOShipment>)cmd).WhereAnd<Where<SOOrderExtNV.usrIsBanners, Equal<True>>>(); break;
case "DCL": ((PXSelectBase<SOShipment>)cmd).WhereAnd<Where<SOOrderExtNV.usrIsDealer, Equal<True>>>(); break;
case "LSR": ((PXSelectBase<SOShipment>)cmd).WhereAnd<Where<SOOrderExtNV.usrIsLaser, Equal<True>>>(); break;
case "SVR": ((PXSelectBase<SOShipment>)cmd).WhereAnd<Where<SOOrderExtNV.usrIsService, Equal<True>>>(); break;
default: break;
}
I have debugged the code and it failes when it Iterate throught recordset
foreach (object res in ((PXSelectBase<SOShipment>)cmd).View.Select(null, null, PXView.Searches, PXView.SortColumns, PXView.Descendings, newFilters.ToArray(), ref startRow, PXView.MaximumRows, ref totalRows))
{
SOShipment order = PXResult.Unwrap<SOShipment>(res);
SOOrder so = PXResult.Unwrap<SOOrder>(res);
if (order.BilledOrderCntr + order.UnbilledOrderCntr + order.ReleasedOrderCntr == 1)
{
order.CustomerOrderNbr = so.CustomerOrderNbr;
}
SOShipment cached = (SOShipment)Base.Orders.Cache.Locate(order);
if (cached != null)
order.Selected = cached.Selected;
yield return order;
}
The trace having following error recorded
Incorrect syntax near '='.
I have profiled the using SQL Profiler and the condition added is
( = CONVERT(BIT, 1))
I have copied the SOOrderExtNV code for reference
using System;
using PX.Data;
using PX.Objects.CM;
using PX.Objects.AR;
using PX.Objects.CS;
using PX.Objects.CR;
using PX.Objects.TX;
using PX.Objects.GL;
using PX.Objects.IN;
using PX.Objects.CA;
using PX.Objects.PM;
using PX.Objects.EP;
using System.Diagnostics;
using System.Collections.Generic;
using PX.Objects;
using PX.Objects.SO;
using PX.Objects.SM;
using KevinCustomNew;
namespace KevinCustomNew
{
[PXKeyValueStorage]
public class SOOrderExtNV: PXCacheExtension<PX.Objects.SO.SOOrder>
{
// Sales Order Header
#region SO Order Header
#region UsrOrderNature
[PXDBString(5)]
[PXUIField(DisplayName = "Order Nature")]
[PXDefault("HA")]
[PXStringList(
new[] { "HA", "CO" },
new[] { "House Account", "Contract order" }
)]
public virtual string UsrOrderNature { get; set; }
public abstract class usrOrderNature : IBqlField { }
#endregion
#region UsrIsNewCustomer
[PXDBBool]
[PXUIField(DisplayName = "New Customer Order")]
public virtual bool? UsrIsNewCustomer { get; set; }
public abstract class usrIsNewCustomer : IBqlField { }
#endregion
#region UsrIsScreenPrint
[PXDBBool]
[PXUIField(DisplayName = "Screen Print")]
[PXDefault(false)]
public virtual bool? UsrIsScreenPrint { get; set; }
public abstract class usrIsScreenPrint : IBqlField { }
#endregion
#region UsrIsEmbroidery
[PXDBBool]
[PXUIField(DisplayName = "Embroidery")]
[PXDefault(false)]
public virtual bool? UsrIsEmbroidery { get; set; }
public abstract class usrIsEmbroidery : IBqlField { }
#endregion
#region UsrIsPromo
[PXDBBool]
[PXUIField(DisplayName = "Promotional")]
[PXDefault(false)]
public virtual bool? UsrIsPromo { get; set; }
public abstract class usrIsPromo : IBqlField { }
#endregion
#region UsrIsBlank
[PXDBBool]
[PXUIField(DisplayName = "Blank")]
[PXDefault(false)]
public virtual bool? UsrIsBlank { get; set; }
public abstract class usrIsBlank : IBqlField { }
#endregion
#region UsrIsService
[PXDBBool]
[PXUIField(DisplayName = "Service")]
[PXDefault(false)]
public virtual bool? UsrIsService { get; set; }
public abstract class usrIsService : IBqlField { }
#endregion
#region UsrIsDealer
[PXDBBool]
[PXUIField(DisplayName = "Decal")]
[PXDefault(false)]
public virtual bool? UsrIsDealer { get; set; }
public abstract class usrIsDealer : IBqlField { }
#endregion
#region UsrIsDigitalPrint
[PXDBBool]
[PXUIField(DisplayName = "Digital Print")]
[PXDefault(false)]
public virtual bool? UsrIsDigitalPrint { get; set; }
public abstract class usrIsDigitalPrint : IBqlField { }
#endregion
#region UsrIsBanners
[PXDBBool]
[PXUIField(DisplayName = "Banners")]
[PXDefault(false)]
public virtual bool? UsrIsBanners { get; set; }
public abstract class usrIsBanners : IBqlField { }
#endregion
#region UsrIsSample
[PXDBBool]
[PXUIField(DisplayName = "Sample")]
[PXDefault(false)]
public virtual bool? UsrIsSample { get; set; }
public abstract class usrIsSample : IBqlField { }
#endregion
#region UsrIsFulfilment
[PXDBBool]
[PXUIField(DisplayName = "Fulfillment")]
[PXDefault(false)]
public virtual bool? UsrIsFulfilment { get; set; }
public abstract class usrIsFulfilment : IBqlField { }
#endregion
#region UsrIsOronOn
[PXDBBool]
[PXUIField(DisplayName = "Iron On")]
[PXDefault(false)]
public virtual bool? UsrIsOronOn { get; set; }
public abstract class usrIsOronOn : IBqlField { }
#endregion
#region UsrIsRushJob
[PXDBBool]
[PXUIField(DisplayName = "Rush Job")]
[PXDefault(false)]
public virtual bool? UsrIsRushJob { get; set; }
public abstract class usrIsRushJob : IBqlField { }
#endregion
#region UsrIsLaser
[PXDBBool]
[PXUIField(DisplayName = "Laser")]
public virtual bool? UsrIsLaser { get; set; }
public abstract class usrIsLaser : IBqlField { }
#endregion
#region UsrIsInHandsDate
[PXDBDate]
[PXUIField(DisplayName = "In-Hands Date")]
public virtual DateTime? UsrIsInHandsDate { get; set; }
public abstract class usrIsInHandsDate : IBqlField { }
#endregion
#region UsrOpportunityID
[PXDBString(20)]
[PXUIField(DisplayName = "Opportunity",Enabled=false)]
[PXSelector(typeof(CROpportunity.opportunityID),
new Type[]
{
typeof(CROpportunity.opportunityID),
typeof(CROpportunity.opportunityName)
},
DescriptionField = typeof(CROpportunity.opportunityName))]
public virtual string UsrOpportunityID { get; set; }
public abstract class usrOpportunityID : IBqlField { }
#endregion
#region UsrHardDate
[PXDBBool]
[PXUIField(DisplayName = "Hard Date")]
[PXDefault(false)]
public virtual bool? UsrHardDate { get; set; }
public abstract class usrHardDate : IBqlField { }
#endregion
#region UsrEventDate
[PXDBDate]
[PXUIField(DisplayName = "Event Date")]
public virtual DateTime? UsrEventDate { get; set; }
public abstract class usrEventDate : IBqlField { }
#endregion
#region UsrEventDescription
[PXDBString(512)]
[PXUIField(DisplayName = "Event Description")]
public virtual string UsrEventDescription { get; set; }
public abstract class usrEventDescription : IBqlField { }
#endregion
#region UsrSoHeaderNoOfPieces
[PXDBInt]
[PXUIField(DisplayName = "No. Of Pieces")]
public virtual int? UsrSoHeaderNoOfPieces { get; set; }
public abstract class usrSoHeaderNoOfPieces : IBqlField { }
#endregion
#region UsrShipDate
[PXDBDate]
[PXUIField(DisplayName = "Ship Date")]
public virtual DateTime? UsrShipDate { get; set; }
public abstract class usrShipDate : IBqlField { }
#endregion
#region UsrHoldUntil
[PXDBDate]
[PXUIField(DisplayName = "Hold Until")]
public virtual DateTime? UsrHoldUntil { get; set; }
public abstract class usrHoldUntil : IBqlField { }
#endregion
#region UsrCustomerContact
[PXDBInt]
[PXUIField(DisplayName = "Contact")]
[PXSelector(
typeof(Search<Contact.contactID,
Where<Contact.bAccountID, Equal<Current<SOOrder.customerID>>,And<Contact.contactType,Equal<DownLoadValueType.CustomerContactType>>>>),
DescriptionField = typeof(Contact.displayName))]
public virtual int? UsrCustomerContact { get; set; }
public abstract class usrCustomerContact : IBqlField { }
#endregion
#region UsrBatchShip
[PXDBBool]
[PXUIField(DisplayName = "Batch Ship")]
public virtual bool? UsrBatchShip { get; set; }
public abstract class usrBatchShip : IBqlField { }
#endregion
#region UsrReadyForProduction
[PXDBBool]
[PXUIField(DisplayName = "Ready for Production")]
public virtual bool? UsrReadyForProduction { get; set; }
public abstract class usrReadyForProduction : IBqlField { }
#endregion
#region UsrEditInstructions
[PXDBString(2000)]
[PXUIField(DisplayName = "Revision Instructions")]
public virtual string UsrEditInstructions { get; set; }
public abstract class usrEditInstructions : IBqlField { }
#endregion
#endregion
}
}
Regards,
R.Muralidharan
Instead of re-declaring entire base code of view delegate in Extension, you should execute the same data view through the base class by using Base.Orders.Select() in extension class and apply your custom filters.
Below code snippet shows you concept to achieve what you are looking for (and feel free to adjust as per your need):
Create DAC Extension for SOShipmentFilter DAC
public class SOShipmentFilterPXExt : PXCacheExtension<SOShipmentFilter>
{
#region UsrFilterByAlias
public abstract class usrFilterByAlias : IBqlField { }
[PXDBString(2)]
[PXStringList(
new[] {"A0", "A1", "A2" },
new[] {"All", "UsrZField1", "UsrZField2" }
)]
[PXDefault("A0")]
public virtual String UsrFilterByAlias { get; set; }
#endregion
}
Create DAC Extension for SOOrder DAC
[PXKeyValueStorage]
public class SOOrderExtNV : PXCacheExtension<PX.Objects.SO.SOOrder>
{
#region UsrZField1
public abstract class usrZField1 : IBqlField { }
[PXDBBool]
[PXUIField(DisplayName = "ZField1")]
public virtual bool? UsrZField1 { get; set; }
#endregion
#region UsrZField2
public abstract class usrZField2 : IBqlField { }
[PXDBBool]
[PXUIField(DisplayName = "ZField2")]
public virtual bool? UsrZField2 { get; set; }
#endregion
}
Create DAC Extension for SOShipment DAC
public class SOShipmentExt : PXCacheExtension<SOShipment>
{
#region UsrZField1
public abstract class usrZField1 : IBqlField { }
[PXBool]
[PXDefault(false, PersistingCheck = PXPersistingCheck.Nothing)]
[PXUIField(DisplayName = "ZField1")]
public virtual bool? UsrZField1 { get; set; }
#endregion
#region UsrZField2
public abstract class usrZField2 : IBqlField { }
[PXBool]
[PXDefault(false, PersistingCheck = PXPersistingCheck.Nothing)]
[PXUIField(DisplayName = "ZField2")]
public virtual bool? UsrZField2 { get; set; }
#endregion
}
Create Graph Extension
public class SOInvoiceShipmentPXExt : PXGraphExtension<SOInvoiceShipment>
{
[PXFilterable]
public PXFilteredProcessing<SOShipment, SOShipmentFilter> Orders;
public virtual void SOShipment_RowSelecting(PXCache sender, PXRowSelectingEventArgs e, PXRowSelecting BaseInvoke)
{
if (BaseInvoke != null)
BaseInvoke(sender, e);
if (e.Row == null) return;
SOShipment shipment = (SOShipment)e.Row;
SOShipmentExt shipmentExt = PXCache<SOShipment>.GetExtension<SOShipmentExt>(shipment);
SOOrder soData = PXSelectJoin<SOOrder,
InnerJoin<SOOrderShipment, On<SOOrderShipment.orderType, Equal<SOOrder.orderType>,
And<SOOrderShipment.orderNbr, Equal<SOOrder.orderNbr>>>>,
Where<SOOrderShipment.shipmentType, Equal<Required<SOOrderShipment.shipmentType>>,
And<SOOrderShipment.shipmentNbr, Equal<Required<SOOrderShipment.shipmentNbr>>>>>
.Select(Base, shipment.ShipmentType, shipment.ShipmentNbr);
if (soData != null)
{
SOOrderExtNV soDataExt = PXCache<SOOrder>.GetExtension<SOOrderExtNV>(soData);
shipmentExt.UsrZField1 = soDataExt.UsrZField1;
shipmentExt.UsrZField2 = soDataExt.UsrZField2;
}
}
protected IEnumerable orders()
{
SOShipmentFilterPXExt filterExt = PXCache<SOShipmentFilter>.GetExtension<SOShipmentFilterPXExt>(Base.Filter.Current);
if (filterExt.UsrFilterByAlias == "A1")
{
var list = new ArrayList();
foreach (PXResult record in Base.Orders.Select())
{
SOShipment shipment = record.GetItem<SOShipment>();
if (shipment != null)
{
SOShipmentExt shipmentExt = PXCache<SOShipment>.GetExtension<SOShipmentExt>(shipment);
if (shipmentExt.UsrZField1.HasValue && shipmentExt.UsrZField1.Value)
list.Add(shipment);
}
}
return list;
}
else if (filterExt.UsrFilterByAlias == "A2")
{
var list = new ArrayList();
foreach (PXResult record in Base.Orders.Select())
{
SOShipment shipment = record.GetItem<SOShipment>();
if (shipment != null)
{
SOShipmentExt shipmentExt = PXCache<SOShipment>.GetExtension<SOShipmentExt>(shipment);
if (shipmentExt.UsrZField2.HasValue && shipmentExt.UsrZField2.Value)
list.Add(shipment);
}
}
return list;
}
else
{
return Base.Orders.Select();
}
}
}

Resources