Jade.B
Jade.B

Reputation: 17

Using approval for customize screen in 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!

Upvotes: 0

Views: 592

Answers (2)

Jade.B
Jade.B

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

Marco Villase&#241;or
Marco Villase&#241;or

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:

  1. In the Status field of the PSSAProduct DAC you use [PXDefault(PSSAProduct.Statuses.Hold)]. Check you have defined PSSAProduct.Statuses correctly.

  2. 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

Related Questions