这个答案可能来得有点晚,但我相信它对其他人有用,所以我会分享它。
数据库的新表和字段
XXSetupApproval
为您的模块中的审批设置添加新的设置表(XXSetupApproval 进一步)。您可以在下方查看所有必填字段,但如果您想为不同类型的实体拆分审批,则可能需要添加任何其他参数。
CREATE TABLE XXSetupApproval
(
CompanyID int NOT NULL,
ApprovalID int NOT NULL identity,
AssignmentMapID int NOT NULL,
AssignmentNotificationID int NULL,
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,
IsActive bit NOT NULL
)
GO
ALTER TABLE XXSetupApproval
ADD CONSTRAINT XXSetupApproval_PK PRIMARY KEY CLUSTERED (CompanyID, ApprovalID)
GO
XX注册
修改需要实施审批机制的实体表(进一步XX注册)。您的实体应包含三个必填字段,如下所示。
OwnerID uniqueidentifier NULL,
WorkGroupID int NULL,
Approved bit NOT NULL
XX设置
修改模块中的主要设置表(进一步的 XXSetup)。您的设置应包括一个必填字段,您可以在下面看到。根据您的实体名称命名此标志,因为它将指示是否启用了批准机制(XXRequestApproval 进一步)。
XXRequestApproval bit NULL
数据库的新更新脚本
根据您的条件更新 XXRegister 表并为所有现有记录设置 Approved 标志等于 1。使用您自己的表达式而不是三个点。
EXEC sp_executesql N'UPDATE XXRegister SET Approved = 1 WHERE ...'
代码的新表和字段
AssignmentMapTypeXX
将您的实体添加到 AssignmentMapType 类(进一步是 AssignmentMapTypeXX)。此类型仅用于选择所需类型的分配映射。
public static class AssignmentMapType
{
...
public class AssignmentMapTypeXX : Constant<string>
{
public AssignmentMapTypeXX() : base(typeof(XXRegister).FullName) { }
}
...
}
XX设置
根据数据库中的新字段向 XXSetup DAC 添加新属性和类。如果需要,请使用任何其他属性。
#region XXRequestApproval
public abstract class xXRequestApproval : PX.Data.IBqlField { }
[EPRequireApproval]
[PXDefault(false, PersistingCheck = PXPersistingCheck.Null)]
[PXUIField(DisplayName = "Require Approval")]
public virtual bool? XXRequestApproval { get; set; }
#endregion
XXSetupApproval
根据数据库中的 XXSetupApproval 表将新的 DAC 添加到代码中。 XXSetupApproval DAC 应该实现 IAssignedMap 接口(AssignmentMapID、AssignmentNotificationID、IsActive 字段)。如果需要,请使用任何其他属性。
[Serializable]
public partial class XXSetupApproval : IBqlTable, IAssignedMap
{
#region ApprovalID
public abstract class approvalID : IBqlField { }
[PXDBIdentity(IsKey = true)]
public virtual int? ApprovalID { get; set; }
#endregion
#region AssignmentMapID
public abstract class assignmentMapID : IBqlField { }
[PXDefault]
[PXDBInt]
[PXSelector(typeof(Search<EPAssignmentMap.assignmentMapID, Where<EPAssignmentMap.entityType, Equal<AssignmentMapType.AssignmentMapTypeXX>>>),
DescriptionField = typeof(EPAssignmentMap.name))]
[PXUIField(DisplayName = "Approval Map")]
public virtual int? AssignmentMapID { get; set; }
#endregion
#region AssignmentNotificationID
public abstract class assignmentNotificationID : IBqlField { }
[PXDBInt]
[PXSelector(typeof(PX.SM.Notification.notificationID), SubstituteKey = typeof(PX.SM.Notification.name))]
[PXUIField(DisplayName = "Pending Approval Notification")]
public virtual int? AssignmentNotificationID { get; set; }
#endregion
#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
#region IsActive
public abstract class isActive : IBqlField { }
[PXDBBool()]
[PXDefault(typeof(Search<XXSetup.xXRequestApproval>), PersistingCheck = PXPersistingCheck.Nothing)]
public virtual bool? IsActive { get; set; }
#endregion
}
XX注册
将 PXEmailSource 属性添加到 XXRegister DAC,“Assignment and Approvals Maps”树选择器需要它。
[PXEMailSource]
public partial class XXRegister : IBqlTable, EP.IAssign ...
根据数据库中的新字段向 XXRegister DAC 添加新属性和类。 XXRegister DAC应该实现IAssign接口(OwnerID、WorkgroupID字段)。如果需要,使用任何其他属性。使用您自己的表达式而不是三个点。
#region OwnerID
public abstract class ownerID : IBqlField { }
[PXDBGuid()]
[PXDefault(typeof(...), PersistingCheck = PXPersistingCheck.Nothing)]
[PX.TM.PXOwnerSelector()]
[PXUIField(DisplayName = "Owner")]
public virtual Guid? OwnerID { get; set; }
#endregion
#region WorkgroupID
public abstract class workgroupID : IBqlField { }
[PXDBInt]
[PXDefault(typeof(...), PersistingCheck = PXPersistingCheck.Nothing)]
[PX.TM.PXCompanyTreeSelector]
[PXUIField(DisplayName = "Workgroup", Enabled = false)]
public virtual int? WorkgroupID { get; set; }
#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
状态
为实体添加新的批准状态并在列表中使用它们。如果“P”和“R”已被使用,请使用其他字母。
public const string PendingApproval = "P";
public const string Rejected = "R";
public class ListAttribute : PXStringListAttribute
{
public ListAttribute() : base(
new string[] { ..., PendingApproval, Rejected, ... },
new string[] { ..., EP.Messages.PendingApproval, EP.Messages.Rejected, ... }) { ; }
}
实体图的新代码
在与您的实体一起操作的图中实施 EPApprovalAutomation 助手。使用您自己的参数而不是三个点。
public EPApprovalAutomation<...> Approval;
为 XXSetupApproval DAC 添加视图。使用您自己的表达式而不是三个点。
public PXSelect<XXSetupApproval, Where<...>> SetupApproval;
主设置图表的新代码
为 XXSetupApproval DAC 添加视图。
public PXSelect<APSetupApproval> SetupApproval;
根据 XXRequestApproval 字段的新值更新每个 XXSetupApproval 行。
protected virtual void XXSetup_XXRequestApproval_FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e)
{
PXCache cache = this.Caches[typeof(XXSetupApproval)];
foreach (XXSetupApproval setup in PXSelect<XXSetupApproval>.Select(this))
{
setup.IsActive = (bool?)e.NewValue;
cache.Update(setup);
}
}
网页的新表格和字段
XXSetup.aspx
将带有批准设置的新选项卡添加到设置的主网页(进一步是 XXSetup.aspx)。如果需要,请使用任何其他参数。
<px:PXTabItem Text="Approval">
<Template>
<px:PXPanel ID="panelApproval" runat="server" >
<px:PXLayoutRule runat="server" LabelsWidth="S" ControlSize="XM" />
<px:PXCheckBox ID="chkXXRequestApproval" runat="server" AlignLeft="True" Checked="True" DataField="XXRequestApproval" CommitChanges="True" />
</px:PXPanel>
<px:PXGrid ID="gridApproval" runat="server" DataSourceID="ds" SkinID="Details" Width="100%" >
<AutoSize Enabled="True" />
<Levels>
<px:PXGridLevel DataMember="SetupApproval" >
<RowTemplate>
<px:PXLayoutRule runat="server" StartColumn="True" LabelsWidth="M" ControlSize="XM" />
<px:PXSelector ID="edAssignmentMapID" runat="server" DataField="AssignmentMapID" AllowEdit="True" CommitChanges="True" />
<px:PXSelector ID="edAssignmentNotificationID" runat="server" DataField="AssignmentNotificationID" AllowEdit="True" />
</RowTemplate>
<Columns>
<px:PXGridColumn DataField="AssignmentMapID" Width="250px" RenderEditorText="True" TextField="AssignmentMapID_EPAssignmentMap_Name" />
<px:PXGridColumn DataField="AssignmentNotificationID" Width="250px" RenderEditorText="True" />
</Columns>
</px:PXGridLevel>
</Levels>
</px:PXGrid>
</Template>
</px:PXTabItem>
XXRegister.aspx
将新字段“已批准”添加到实体的主网页(进一步为 XXRegister.aspx)。如果需要,请使用任何其他参数。
<px:PXCheckBox ID="chkApproved" runat="server" DataField="Approved" CommitChanges="True" Enabled="False" />
将带有批准信息的新选项卡添加到 XXRegister.aspx 网页。如果需要,请使用任何其他参数。
<px:PXTabItem Text="Approval Details" BindingContext="form" RepaintOnDemand="false">
<Template>
<px:PXGrid ID="gridApproval" runat="server" DataSourceID="ds" Width="100%" SkinID="DetailsInTab" NoteIndicator="True" Style="left: 0px; top: 0px;">
<AutoSize Enabled="True" />
<Mode AllowAddNew="False" AllowDelete="False" AllowUpdate="False" />
<Levels>
<px:PXGridLevel DataMember="Approval">
<Columns>
<px:PXGridColumn DataField="ApproverEmployee__AcctCD" Width="160px" />
<px:PXGridColumn DataField="ApproverEmployee__AcctName" Width="160px" />
<px:PXGridColumn DataField="ApprovedByEmployee__AcctCD" Width="100px" />
<px:PXGridColumn DataField="ApprovedByEmployee__AcctName" Width="160px" />
<px:PXGridColumn DataField="ApproveDate" Width="90px" />
<px:PXGridColumn DataField="Status" AllowNull="False" AllowUpdate="False" RenderEditorText="True"/>
<px:PXGridColumn DataField="WorkgroupID" Width="150px" />
</Columns>
</px:PXGridLevel>
</Levels>
</px:PXGrid>
</Template>
</px:PXTabItem>
自动化定义的新自动化步骤
这是审批实施过程中最困难的部分,因为应该在实体当前行为的范围内创建一些新的自动化步骤。例如,实体具有相应的下一个状态和自动化步骤:“保留”->“打开”。我们应该在这两个步骤之间实施审批机制。然后应该创建三个新的自动化步骤:“Hold-Open”(如果我们不需要批准文档)、“Hold-Pending Approval”(如果我们需要批准文档)、“Pending Approval”(从实体应该被批准或拒绝)。新的生命周期将如下所示:“Hold” -> “Hold-Open” OR “Hold-Pending Approval” -> “Open” OR “Pending Approval”。因此,“Hold-Open”和“Hold-Pending Approval”自动化步骤只是开关,用于确定在“Hold”之后应该使用哪个自动化步骤。
<Step StepID="Hold-Open" Description="Hold-Open" GraphName="…" ViewName="Document" TimeStampName="tstamp">
<Filter FieldName="Status" Condition="Equals" Value="H" Operator="And" />
<Filter FieldName="Hold" Condition="Equals" Value="False" Value2="False" Operator="And" />
<Filter FieldName="Approved" Condition="Equals" Value="True" Value2="False" Operator="And" />
<Action ActionName="*" IsDefault="1">
<Fill FieldName="Status" Value="N" />
</Action>
</Step>
<Step StepID="Hold-Pending Approval" Description="Hold-Pending Approval" GraphName="…" ViewName="Document" TimeStampName="tstamp">
<Filter OpenBrackets="1" FieldName="Status" Condition="Equals" Value="H" Operator="Or" />
<Filter FieldName="Status" Condition="Equals" Value="N" CloseBrackets="1" Operator="And" />
<Filter FieldName="Hold" Condition="Equals" Value="False" Value2="False" Operator="And" />
<Filter FieldName="Approved" Condition="Equals" Value="False" Value2="False" Operator="And" />
<Action ActionName="*" IsDefault="1">
<Fill FieldName="Status" Value="P" />
</Action>
</Step>
感谢 Acumatica 开发团队的 Evgeny Kralko 所做的工作。