向定制Acumatica ERP模块添加标准批准

向定制Acumatica ERP模块添加标准批准,acumatica,Acumatica,我有一个Acumatica ERP的定制模块,需要批准。我想利用Acumatica ERP中的标准批准机制,所以我遵循了以下指南 我想一切都准备好了,但是当我把我的记录取消保留时,除了我写的事件按照我的意图将状态切换到待批准状态之外,什么都没有发生。(也许我应该让审批流程来处理该部分?)我希望我的自动化步骤应该很简单,因为我的状态始终是: 保留->待审批->已批准或已拒绝 我没有收到任何错误,所以我不确定我的自动化步骤是否定义得不对,或者我是否应该在XXApprovalAutomation类中

我有一个Acumatica ERP的定制模块,需要批准。我想利用Acumatica ERP中的标准批准机制,所以我遵循了以下指南

我想一切都准备好了,但是当我把我的记录取消保留时,除了我写的事件按照我的意图将状态切换到待批准状态之外,什么都没有发生。(也许我应该让审批流程来处理该部分?)我希望我的自动化步骤应该很简单,因为我的状态始终是:

保留->待审批->已批准或已拒绝

我没有收到任何错误,所以我不确定我的自动化步骤是否定义得不对,或者我是否应该在XXApprovalAutomation类中编写一些特定的代码,Brendan在上面帖子的答案的步骤3中说要构建这个类

我对Acumatica还是一个新手,所以很多内部工作仍然有点神秘,需要我不断地挖掘代码库。我不确定我的自定义XXApprovalAutomation是否应该在GetAssignedMaps覆盖中有特定的内容,但我会继续挖掘并应用尝试和错误,除非这里有人能指导我


有人能解释一下审批系统如何工作的基本流程,以及我所需要的未在注释帖子中显示的任何相关示例代码(比如GetAssignedMaps的内容,以点击我定义的审批地图)?或者更好的是,告诉我在哪里可以阅读更多信息?

自定义批准似乎有几种方法,但这将坚持自动化步骤。在继续之前,您应该熟悉:

自动化定义-Acumatica ERP 2018r1的此区域允许您在一个地方查看系统给定区域的所有自动化步骤。例如,选择采购订单默认定义id,然后选择采购订单屏幕,可以通过单击显示填充按钮查看以XML格式定义的所有自动化步骤。这个工具对我来说至关重要,因为它指出了我的自动化步骤中遗漏的重要步骤。该系统区域还允许您导出迁移所需的自动化步骤,并将其与发送到下一个系统(即TST->QA->PRD)部署的代码捆绑在一起

自动化步骤-系统的这个区域允许您定义“发生这种情况时,执行那种”类型的操作。例如,我的自动化步骤之一是,当取消选中“保留”按钮且表单当前处于“保留”状态时,将状态切换为“待审批”。通过选择“操作”,然后在菜单文本下命名按钮,确保探索并理解“操作”选项卡和“填充值”(用于设置字段值)的用法,以及在网格中使用操作名称来定义操作菜单上的按钮。这就是我定义Approve和Reject按钮的地方

批准和分配图-正如HB_Acumatica所指出的,该区域有文档说明如何使用它。阅读这篇文章,在尝试进行自己的定制之前,使用类似PO的工具进行练习,以确保您了解使用批准的应用程序方面

虽然我仍在努力牢牢掌握它的工作原理,但以下几点似乎是关键因素:

在需要批准的图形中定义这些。按照Brendan在帖子中的建议定制eApprovaloutomation最终似乎没有必要,至少对我来说是这样。可能是这样做的一个原因,但我从未发现要定制什么或为什么

[PXViewName("My DAC Name")]
public PXSelect<XXRegister, Where<XXRegister.branchID, Equal<Current<AccessInfo.branchID>>>> MyView;

public PXSetup<XXSetup> Setup;

public PXSelect<XXSetupApproval> SetupApproval;

[PXViewName(Messages.Approval)]
public EPApprovalAutomation<XXRegister, XXRegister.approved, XXRegister.rejected, XXRegister.hold, XXSetupApproval> Approval;
我使用它以编程方式切换状态和各个字段,尽管Acumatica ERP在自动化步骤中处理了这一点,但在我目前的知识水平下,这样做对我来说更容易

    #region XXRegister _RowUpdating
    protected void XXRegister _RowUpdating(PXCache sender, PXRowUpdatingEventArgs e)
    {
        XXRegister row = (XXRegister )e.Row;
        XXRegister newRow = (XXRegister )e.NewRow;

        if (row.Hold != newRow.Hold)
        {
            if (newRow.Hold.Equals(true))
            {
                newRow.Status = XXRegister.Statuses.Hold;
                newRow.Approved = false;
            }
            else if (row.Status.Equals(XXRegister.Statuses.Hold))
            {
                newRow.Status = XXRegister.Statuses.PendingApproval;
            }
        }
    }
    #endregion

剩下的部分则是试图让自动化步骤模仿PO左右。我还没做完,但这让我越过了我所问的障碍。完全披露-当我拒绝批准时,我的批准详细信息不会更新以显示谁/何时/状态,但我的数据记录会正确更新。但这并不是我在这里要问的问题,所以这应该包括预期的原始问题。

最终使用标准针灸代码和自动化步骤完成了整个过程。这对我来说尤其痛苦,因为我是Acumatica的新手,在学习系统的其他部分和工具时,我不得不从中休息很多时间。对于那些希望避免我经历的无数个小时的尝试和错误的人,这里是您在2018R1中定制的相关代码。(我知道有些东西可能需要为R2重写,因此,如果您在使其工作时遇到困难,请注意您的版本。)

在你开始之前,请注意,我留下了原始答案,因为它是我学习曲线的一部分,可以帮助你从我开始的地方(可能是你所在的地方)到我结束的地方

MyGraph:

using PX.Data;
using PX.Objects.AP;
using PX.Objects.AR;
using PX.Objects.CR;
using PX.Objects.EP;
using PX.Objects.IN;
using System.Collections;
using System.Collections.Generic;

namespace MyNamespace
{

    public class MyGraph : PXGraph<MyGraph, XXDocument>
    {

        [PXViewName(Messages.MyGraph)]
        public PXSelect<XXDocument, Where<XXDocument.branchID, Equal<Current<AccessInfo.branchID>>>> MyView;

        public PXSetup<XXSetup> MySetup;

        public PXSelect<XXSetupApproval> SetupApproval;

        // THIS WILL USE THE STANDARD APPROVAL CODE AND SUPPORT THE STANDARD APPROVAL SCREEN
        [PXViewName(Messages.Approval)]
        public EPApprovalAutomation<XXDocument, XXDocument.approved, XXDocument.rejected, XXDocument.hold, XXSetupApproval> Approval;


        // RESET REQUESTAPPROVAL FIELD FROM THE SETUP SCREEN SETTING
        protected virtual void XXDocument_RowSelected(PXCache cache, PXRowSelectedEventArgs e)
        {
            XXDocument doc = e.Row as XXDocument;

            if (doc == null)
            {
                return;
            }
            doc.RequestApproval = MySetup.Current.XXRequestApproval;
        }


        public MyGraph()
        {
            XXSetup setup = MySetup.Current;
        }

        // SETS UP THE ACTIONS MENU INCLUDING @actionID = Persist and @refresh FOR AUTOMATION STEPS
        public PXAction<XXDocument> action;
        [PXUIField(DisplayName = "Actions", MapEnableRights = PXCacheRights.Select)]
        [PXButton]
        protected virtual IEnumerable Action(PXAdapter adapter,
            [PXInt] [PXIntList(new int[] { 1, 2 }, new string[] { "Persist", "Update" })] int? actionID,
            [PXBool] bool refresh,
            [PXString] string actionName
        )
        {
            List<XXDocument> result = new List<XXDocument>();
            if (actionName != null)
            {
                PXAction a = this.Actions[actionName];
                if (a != null)
                    foreach (PXResult<XXDocument> e in a.Press(adapter))
                        result.Add(e);
            }
            else
                foreach (XXDocument e in adapter.Get<XXDocument>())
                    result.Add(e);

            if (refresh)
            {
                foreach (XXDocument MyView in result)
                    MyView.Search<XXDocument.refNbr>(MyView.RefNbr);
            }
            switch (actionID)
            {
                case 1:
                    Save.Press();
                    break;
                case 2:
                    break;
            }
            return result;
        }

        public PXAction<XXDocument> hold;

        // QUICK DEFAULT BASED ON WETHER APPROVAL SETUPS ARE DEFINED PROPERLY
        protected virtual void XXDocument_Approved_FieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e)
        {
            e.NewValue = MySetup.Current == null || MySetup.Current.XXRequestApproval != true;
        }

        // THESE (EPApproval_XX_CacheAttached) WILL SET VALUES IN THE GRID FOR THE STANDARD APPROVAL PROCESSING SCREEN
        #region EPApproval Cache Attached
        [PXDBDate()]
        [PXDefault(typeof(XXDocument.docDate), PersistingCheck = PXPersistingCheck.Nothing)]
        protected virtual void EPApproval_DocDate_CacheAttached(PXCache sender)
        {
        }

        [PXDBInt()]
        [PXDefault(typeof(XXDocument.bAccountID), PersistingCheck = PXPersistingCheck.Nothing)]
        protected virtual void EPApproval_BAccountID_CacheAttached(PXCache sender)
        {
        }

        [PXDBString(60, IsUnicode = true)]
        [PXDefault(typeof(XXDocument.description), PersistingCheck = PXPersistingCheck.Nothing)]
        protected virtual void EPApproval_Descr_CacheAttached(PXCache sender)
        {
        }

        [PXDBLong()]
        [CurrencyInfo(typeof(XXDocument.curyInfoID))]
        protected virtual void EPApproval_CuryInfoID_CacheAttached(PXCache sender)
        {
        }

        [PXDBDecimal(4)]
        [PXDefault(typeof(XXDocument.curyTotalAmount), PersistingCheck = PXPersistingCheck.Nothing)]
        protected virtual void EPApproval_CuryTotalAmount_CacheAttached(PXCache sender)
        {
        }

        [PXDBDecimal(4)]
        [PXDefault(typeof(XXDocument.totalAmount), PersistingCheck = PXPersistingCheck.Nothing)]
        protected virtual void EPApproval_TotalAmount_CacheAttached(PXCache sender)
        {
        }
        #endregion

    }
}
使用PX.Data;
使用PX.Objects.AP;
使用PX.Objects.AR;
使用PX.Objects.CR;
使用PX.Objects.EP;
使用PX.Objects.IN;
使用系统集合;
使用System.Collections.Generic;
名称空间MyNamespace
{
公共类MyGraph:PXGraph
{
[PXViewName(Messages.MyGraph)]
公众选择MyView;
公共PXSetup MySetup;
公开审批;
//这将使用标准批准代码并支持标准批准屏幕
[PXViewName(Messages.Approval)]
公共电子审批;
//从设置屏幕设置中重置REQUESTAPPROVAL字段
受保护的虚拟void XXDocument_RowSelected(PXCache缓存,PXRowSelectedEventArgs e)
{
XXDocument doc=e.行为XXDocument;
如果(doc==null)
{
回来
}
doc.RequestApproval=MySetup.Current.XXRequestApproval;
}
    #region XXRegister _RowUpdating
    protected void XXRegister _RowUpdating(PXCache sender, PXRowUpdatingEventArgs e)
    {
        XXRegister row = (XXRegister )e.Row;
        XXRegister newRow = (XXRegister )e.NewRow;

        if (row.Hold != newRow.Hold)
        {
            if (newRow.Hold.Equals(true))
            {
                newRow.Status = XXRegister.Statuses.Hold;
                newRow.Approved = false;
            }
            else if (row.Status.Equals(XXRegister.Statuses.Hold))
            {
                newRow.Status = XXRegister.Statuses.PendingApproval;
            }
        }
    }
    #endregion
using PX.Data;
using PX.Objects.AP;
using PX.Objects.AR;
using PX.Objects.CR;
using PX.Objects.EP;
using PX.Objects.IN;
using System.Collections;
using System.Collections.Generic;

namespace MyNamespace
{

    public class MyGraph : PXGraph<MyGraph, XXDocument>
    {

        [PXViewName(Messages.MyGraph)]
        public PXSelect<XXDocument, Where<XXDocument.branchID, Equal<Current<AccessInfo.branchID>>>> MyView;

        public PXSetup<XXSetup> MySetup;

        public PXSelect<XXSetupApproval> SetupApproval;

        // THIS WILL USE THE STANDARD APPROVAL CODE AND SUPPORT THE STANDARD APPROVAL SCREEN
        [PXViewName(Messages.Approval)]
        public EPApprovalAutomation<XXDocument, XXDocument.approved, XXDocument.rejected, XXDocument.hold, XXSetupApproval> Approval;


        // RESET REQUESTAPPROVAL FIELD FROM THE SETUP SCREEN SETTING
        protected virtual void XXDocument_RowSelected(PXCache cache, PXRowSelectedEventArgs e)
        {
            XXDocument doc = e.Row as XXDocument;

            if (doc == null)
            {
                return;
            }
            doc.RequestApproval = MySetup.Current.XXRequestApproval;
        }


        public MyGraph()
        {
            XXSetup setup = MySetup.Current;
        }

        // SETS UP THE ACTIONS MENU INCLUDING @actionID = Persist and @refresh FOR AUTOMATION STEPS
        public PXAction<XXDocument> action;
        [PXUIField(DisplayName = "Actions", MapEnableRights = PXCacheRights.Select)]
        [PXButton]
        protected virtual IEnumerable Action(PXAdapter adapter,
            [PXInt] [PXIntList(new int[] { 1, 2 }, new string[] { "Persist", "Update" })] int? actionID,
            [PXBool] bool refresh,
            [PXString] string actionName
        )
        {
            List<XXDocument> result = new List<XXDocument>();
            if (actionName != null)
            {
                PXAction a = this.Actions[actionName];
                if (a != null)
                    foreach (PXResult<XXDocument> e in a.Press(adapter))
                        result.Add(e);
            }
            else
                foreach (XXDocument e in adapter.Get<XXDocument>())
                    result.Add(e);

            if (refresh)
            {
                foreach (XXDocument MyView in result)
                    MyView.Search<XXDocument.refNbr>(MyView.RefNbr);
            }
            switch (actionID)
            {
                case 1:
                    Save.Press();
                    break;
                case 2:
                    break;
            }
            return result;
        }

        public PXAction<XXDocument> hold;

        // QUICK DEFAULT BASED ON WETHER APPROVAL SETUPS ARE DEFINED PROPERLY
        protected virtual void XXDocument_Approved_FieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e)
        {
            e.NewValue = MySetup.Current == null || MySetup.Current.XXRequestApproval != true;
        }

        // THESE (EPApproval_XX_CacheAttached) WILL SET VALUES IN THE GRID FOR THE STANDARD APPROVAL PROCESSING SCREEN
        #region EPApproval Cache Attached
        [PXDBDate()]
        [PXDefault(typeof(XXDocument.docDate), PersistingCheck = PXPersistingCheck.Nothing)]
        protected virtual void EPApproval_DocDate_CacheAttached(PXCache sender)
        {
        }

        [PXDBInt()]
        [PXDefault(typeof(XXDocument.bAccountID), PersistingCheck = PXPersistingCheck.Nothing)]
        protected virtual void EPApproval_BAccountID_CacheAttached(PXCache sender)
        {
        }

        [PXDBString(60, IsUnicode = true)]
        [PXDefault(typeof(XXDocument.description), PersistingCheck = PXPersistingCheck.Nothing)]
        protected virtual void EPApproval_Descr_CacheAttached(PXCache sender)
        {
        }

        [PXDBLong()]
        [CurrencyInfo(typeof(XXDocument.curyInfoID))]
        protected virtual void EPApproval_CuryInfoID_CacheAttached(PXCache sender)
        {
        }

        [PXDBDecimal(4)]
        [PXDefault(typeof(XXDocument.curyTotalAmount), PersistingCheck = PXPersistingCheck.Nothing)]
        protected virtual void EPApproval_CuryTotalAmount_CacheAttached(PXCache sender)
        {
        }

        [PXDBDecimal(4)]
        [PXDefault(typeof(XXDocument.totalAmount), PersistingCheck = PXPersistingCheck.Nothing)]
        protected virtual void EPApproval_TotalAmount_CacheAttached(PXCache sender)
        {
        }
        #endregion

    }
}
using PX.Data;
using PX.Data.EP;
using PX.Objects.CS;
using PX.Objects.EP;
using PX.Objects.SM;
using PX.SM;
using PX.TM;
using System;

namespace MyNamespace
{
    [PXEMailSource]

    [Serializable]
    [PXPrimaryGraph(typeof(MyGraph))]
    [PXCacheName(Messages.XXDocument)]
    public partial class XXDocument : IBqlTable, IAssign
    {
        #region Selected
        [PXBool()]
        [PXDefault(false, PersistingCheck = PXPersistingCheck.Nothing)]
        [PXUIField(DisplayName = "Selected")]
        public virtual bool? Selected { get; set; }
        public abstract class selected : IBqlField { }
        #endregion

        #region BranchID
        [PXDBInt()]
        [PXUIField(DisplayName = "Branch ID")]
        public virtual int? BranchID { get; set; }
        public abstract class branchID : IBqlField { }
        #endregion

        #region DocumentID
        [PXDBIdentity]
        public virtual int? DocumentID { get; set; }
        public abstract class documentID : IBqlField { }
        #endregion

        #region RefNbr
        [PXDBString(15, IsKey = true, IsUnicode = true, InputMask = "")]
        [PXUIField(DisplayName = "Ref Nbr", Visibility = PXUIVisibility.SelectorVisible)]
        [AutoNumber(typeof(XXSetup.numberingID), typeof(AccessInfo.businessDate))]
        [PXSelector(typeof(XXDocument.refNbr),
           typeof(XXDocument.refNbr),
           typeof(XXDocument.createdDateTime)
           )]
        public virtual string RefNbr { get; set; }
        public abstract class refNbr : IBqlField { }
        #endregion

        #region Hold
        [PXDBBool()]
        [PXUIField(DisplayName = "Hold", Visibility = PXUIVisibility.Visible)]
        [PXDefault(true)]
        public virtual bool? Hold { get; set; }
        public abstract class hold : IBqlField { }
        #endregion

        #region Approved
        // MAKE THIS PXDBBOOL IF YOU WANT TO SAVE THIS IN THE DATABASE LIKE POORDER.APPROVED FIELD
        // NOT NECESSARY IN MY CODE BUT CAN AFFECT HOW YOU DEFINE AUTOMATION STEPS
        [PXBool()]
        [PXDefault(false, PersistingCheck = PXPersistingCheck.Nothing)]
        // REMEMBER PXUIFIELD IF YOU WANT TO DISPPLAY ON THE SCREEN - I DID NOT WANT THIS ON MY SCREEN
        //[PXUIField(DisplayName = "Approved", Visibility = PXUIVisibility.Visible, Enabled = false)]
        public virtual Boolean? Approved { get; set; }
        public abstract class approved : IBqlField { }
        #endregion

        #region Rejected
        [PXBool]
        [PXDefault(false, PersistingCheck = PXPersistingCheck.Nothing)]
        public bool? Rejected { get; set; }
        public abstract class rejected : IBqlField { }
        #endregion

        #region RequestApproval
        [PXBool()]
        [PXUIField(DisplayName = "Request Approval", Visible = false)]
        public virtual bool? RequestApproval { get; set; }
        public abstract class requestApproval : IBqlField { }
        #endregion

        #region Status
        [PXDBString(1)]
        [PXDefault(XXDocument.Statuses.Hold)]
        [PXUIField(DisplayName = "Status", Visibility = PXUIVisibility.SelectorVisible, Enabled = false)]
        [Statuses.List]
        public virtual string Status { get; set; }
        public abstract class status : IBqlField { }
        #endregion

        #region Description
        [PXDBString(255, IsUnicode = true, InputMask = "")]
        [PXUIField(DisplayName = "Description")]
        public virtual string Description { get; set; }
        public abstract class description : IBqlField { }
        #endregion

        // ADD A VERSION OF AMOUNT FOR CURRENCY AND ALSO A FIELD FOR CURRENCY ID IF YOU WANT IN YOUR APPROVAL SCREEN
        #region Amount
        [PXDBDecimal(2)]
        [PXDefault(TypeCode.Decimal, "0.0")]
        [PXUIField(DisplayName = "Amount", Enabled = false)]
        public virtual decimal? Amount { get; set; }
        public abstract class amount : IBqlField { }
        #endregion

        #region DocDate
        [PXDBDate()]
        [PXUIField(DisplayName = "Date")]
        [PXDefault(typeof(AccessInfo.businessDate))]
        public virtual DateTime? DocDate { get; set; }
        public abstract class docDate : IBqlField { }
        #endregion

        #region BAccountID
        /// <summary>
        /// The ID of the workgroup which was assigned to approve the transaction.
        /// </summary>
        [PXInt]
        [PXDefault(PersistingCheck = PXPersistingCheck.Nothing)]
        public virtual int? BAccountID { get; set; }
        public abstract class bAccountID : IBqlField { }
        #endregion

        #region OwnerID
        [PXDBGuid()]
        [PXDefault(typeof(Search<EPEmployee.userID, Where<EPEmployee.userID, Equal<Current<AccessInfo.userID>>>>), PersistingCheck = PXPersistingCheck.Nothing)]
        [PX.TM.PXOwnerSelector()]
        [PXUIField(DisplayName = "Owner")]
        public virtual Guid? OwnerID { get; set; }
        public abstract class ownerID : IBqlField { }
        #endregion

        #region WorkgroupID
        /// <summary>
        /// The ID of the workgroup which was assigned to approve the transaction.
        /// </summary>
        [PXInt]
        [PXSelector(typeof(Search<EPCompanyTree.workGroupID>), SubstituteKey = typeof(EPCompanyTree.description))]
        [PXUIField(DisplayName = "Approval Workgroup ID", Enabled = false)]
        public virtual int? WorkgroupID { get; set; }
        public abstract class workgroupID : 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 = "Created Date Time")]
        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 = "Last Modified Date Time")]
        public virtual DateTime? LastModifiedDateTime { get; set; }
        public abstract class lastModifiedDateTime : IBqlField { }
        #endregion

        #region Tstamp
        [PXDBTimestamp()]
        [PXUIField(DisplayName = "Tstamp")]
        public virtual byte[] Tstamp { get; set; }
        public abstract class tstamp : IBqlField { }
        #endregion

        #region NoteID  
        [PXSearchable(INSERT YOUR SEARCHABLE CODE HERE OR REMOVE THIS LINE TO NOT BE SEARCHABLE)]
        [PXNote]
        public virtual Guid? NoteID { get; set; }
        public abstract class noteID : IBqlField { }
        #endregion

        #region DeletedDatabaseRecord
        [PXDBBool()]
        [PXDefault(false)]
        [PXUIField(DisplayName = "Deleted Database Record")]
        public virtual bool? DeletedDatabaseRecord { get; set; }
        public abstract class deletedDatabaseRecord : IBqlField { }
        #endregion

        #region IAssign Members
        int? PX.Data.EP.IAssign.WorkgroupID
        {
            get { return WorkgroupID; }
            set { WorkgroupID = value; }
        }

        Guid? PX.Data.EP.IAssign.OwnerID
        {
            get { return OwnerID; }
            set { OwnerID = value; }
        }
        #endregion


        public static class Statuses
        {
            public class ListAttribute : PXStringListAttribute
            {
                public ListAttribute() : base(
                    new[]
                    {
                    Pair(Hold, PX.Objects.EP.Messages.Hold),
                    Pair(PendingApproval, PX.Objects.EP.Messages.PendingApproval),
                    Pair(Approved, PX.Objects.EP.Messages.Approved),
                    Pair(Rejected, PX.Objects.EP.Messages.Rejected),
                    })
                { }
            }

            public const string Hold = "H";
            public const string PendingApproval = "P";
            public const string Approved = "A";
            public const string Rejected = "V";   // V = VOIDED

        }
    }

    public static class AssignmentMapType
    {
        public class AssignmentMapTypeXX : Constant<string>
        {
            public AssignmentMapTypeXX() : base(typeof(XXDocument).FullName) { }
        }
    }
}
using PX.Data;
using PX.SM;
using PX.TM;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using PX.Common;
using PX.Objects;
using PX.Objects.EP;

namespace PX.Objects.EP
{
  public class EPApprovalMapMaint_Extension : PXGraphExtension<EPApprovalMapMaint>
  {
    #region Event Handlers
    public delegate IEnumerable<String> GetEntityTypeScreensDelegate();
    [PXOverride]
    public IEnumerable<String> GetEntityTypeScreens(GetEntityTypeScreensDelegate baseMethod)
    {
      return new string[]
      {
        "AP301000",//Bills and Adjustments
        "AP302000",//Checks and Payments
        "AP304000",//Quick Checks
        "AR302000",//Payments and Applications
        "AR304000",//Cash Sales
        "CA304000",//Cash Transactions
        "EP305000",//Employee Time Card
        "EP308000",//Equipment Time Card
        "EP301000",//Expense Claim
        "EP301020",//Expense Receipt
        "PM301000",//Projects
        "PM307000",//Proforma
        "PM308000",//Change Order
        "PO301000",//Purchase Order
        "RQ301000",//Purchase Request
        "RQ302000",//Purchase Requisition
        "SO301000",//Sales Order
        "CR304500",//Quote
        "XX000000"//My Custom Document Screen
      };

      //return baseMethod();

    }
    #endregion
  }
}
<?xml version="1.0" encoding="utf-8"?>
<Screens>
    <Screen ScreenID="XX000000">
        <Menu ActionName="Action">
            <MenuItem Text="Approve" />
            <MenuItem Text="Reject" />
        </Menu>
        <Step StepID="Approved" GraphName="MyNamespace.MyGraph" ViewName="Savings" TimeStampName="Tstamp">
            <Filter FieldName="Approved" Condition="Equals" Value="True" Value2="False" Operator="And" />
            <Filter FieldName="Status" Condition="Equals" Value="P" Operator="And" />
            <Action ActionName="Action" MenuText="Approve" IsDisabled="1" />
            <Action ActionName="Action" MenuText="Reject" IsDisabled="1" />
            <Action ActionName="*" IsDefault="1">
                <Fill FieldName="Status" Value="A" />
            </Action>
        </Step>
        <Step StepID="Hold" GraphName="MyNamespace.MyGraph" ViewName="Savings" TimeStampName="Tstamp">
            <Filter FieldName="Hold" Condition="Equals" Value="True" Value2="False" Operator="And" />
            <Filter FieldName="Status" Condition="Does Not Equal To" Value="H" Operator="And" />
            <Action ActionName="*" IsDefault="1" AutoSave="4">
                <Fill FieldName="Status" Value="H" />
            </Action>
            <Action ActionName="Action" MenuText="Approve" IsDisabled="1" />
            <Action ActionName="Action" MenuText="Reject" IsDisabled="1" />
        </Step>
        <Step StepID="Hold-Pending Approval" GraphName="MyNamespace.MyGraph" ViewName="Savings" TimeStampName="Tstamp">
            <Filter FieldName="Hold" Condition="Equals" Value="False" Value2="False" Operator="And" />
            <Filter FieldName="Status" Condition="Equals" Value="H" Operator="And" />
            <Action ActionName="*" IsDefault="1" AutoSave="4">
                <Fill FieldName="Status" Value="P" />
            </Action>
        </Step>
        <Step StepID="Pending Approval" GraphName="MyNamespace.MyGraph" ViewName="Savings" TimeStampName="Tstamp">
            <Filter FieldName="Status" Condition="Equals" Value="P" Operator="And" />
            <Action ActionName="Action" MenuText="Approve">
                <Fill FieldName="Approved" Value="True" />
                <Fill FieldName="@actionID" Value="1" />
                <Fill FieldName="@refresh" Value="True" />
            </Action>
            <Action ActionName="Action" MenuText="Reject">
                <Fill FieldName="Rejected" Value="True" />
                <Fill FieldName="Status" Value="V" />
                <Fill FieldName="@actionID" Value="1" />
                <Fill FieldName="@refresh" Value="True" />
            </Action>
        </Step>
    </Screen>
</Screens>