Reputation: 17
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!
Upvotes: 0
Views: 592
Reputation: 17
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
Upvotes: 0
Reputation: 498
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.
Upvotes: 1