我有一个Acumatica ERP的自定义模块,需要获得批准。我想利用Acumatica ERP中的标准批准机制,因此我遵循了本指南...
我认为一切都准备就绪,但是当我取消记录时什么都不会发生,除了我写的事件将按预期将状态切换为“待批准”。 (也许我应该让审批流程来处理那部分?)我希望自动化步骤应该很简单,因为我的状态始终为:
保留->待批准->批准-或-拒绝
我没有收到任何错误,所以不确定不确定自动化步骤定义是否正确,还是应该将特定的内容编码到Brendan在其答案的第3步中说要构建的XXApprovalAutomation类中以上帖子。
我对Acumatica还是很陌生,因此很多内部工作仍然是个谜,这要求我不断地研究CodeRepository。我不确定我的自定义XXApprovalAutomation是否应该在GetAssignedMaps覆盖中具有特定的内容,但除非有人在这里指导,否则我将继续挖掘和尝试尝试。
有人可以解释批准系统如何工作的基本流程,以及我需要的,未在我所注明的帖子中显示的任何相关示例代码(例如GetAssignedMaps的内容可以打到我定义的批准图上)吗?甚至更好,将我指向可以在其中阅读更多内容的地方?
答案 0 :(得分:1)
出现自定义批准有两种方法,但这将遵循“自动化步骤”。在继续之前,您应该熟悉以下内容:
自动化定义-Acumatica ERP 2018r1的这一区域使您可以在一处查看系统给定区域的所有自动化步骤。例如,选择“采购订单默认定义ID”,然后选择“采购订单”屏幕,可以通过单击“显示填充的”按钮来查看以XML格式定义的所有自动化步骤。该工具对我来说至关重要,因为它指出了我的自动化步骤中遗漏的重要步骤。该系统区域还允许您导出迁移所需的自动化步骤,并将其与为在下一个系统上部署而发送的代码捆绑在一起(例如,TST-> QA-> PRD)。
自动化步骤-系统的这一区域使您可以定义“发生这种情况时,执行此操作”类型的操作。例如,我的自动化步骤之一是,当取消选中保留按钮并且表单当前处于保留状态时,将状态切换为“待批准”。请务必探索并了解“操作”标签,并使用“用值填充”(用于设置字段值),以及在网格中使用“操作名称”来定义“操作”菜单上的按钮,方法是选择“操作”,然后命名菜单文本下的按钮。这是我定义“批准”和“拒绝”按钮的地方。
批准和分配图-正如HB_Acumatica指出的那样,该区域有说明如何使用它的文档。阅读相关内容,并尝试使用PO进行类似操作,以确保在尝试进行自己的定制之前,您了解使用批准的应用程序方面。
尽管我仍在努力掌握使其工作所需的扎实经验,但以下因素似乎是关键因素:
在图形中要批准的位置定义它们。就像Brendan在他的帖子中所建议的那样,自定义EPApprovalAutomation似乎最终没有必要,至少对我而言。可能是这样做的原因,但我从未发现要自定义的内容或原因。
[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;
MyView可以是您想调用的任何视图。它将包含您的自定义数据。设置是首选项DAC的视图,您可以在其中通知系统为自定义数据启用批准。 SetupApproval是启用批准时在自定义首选项屏幕中定义的批准图的视图。 (这会将批准地图与通知相关联,以便定义的人可以获取指定的批准通知。)批准就是魔术……这是一些超级秘密的特殊调味料,显然不适合普通大众理解。但是,传入的参数定义(1)哪个DAC描述您要批准的数据记录;(2)DAC中的哪个字段与“已批准”,“已拒绝”和“保持”相关;以及(3)此魔术框应在哪里查找批准图和通知-即XXSetupApproval DAC的名称。
未经证实的事实...就是我所观察到的... 名为EPApprovalAutomation的超级机密特殊魔术框似乎在查看“保留”字段的状态,如果未选中,则使用XXSetupApproval数据导航“批准图”。当选择批准和拒绝操作时,该魔术框似乎会将适当的值应用于批准和拒绝的字段。自动化步骤监视字段的更改(无论您如何定义)并应用自动化,例如将状态字段设置为“已批准”或“已拒绝”。 EPApprovalAutomation似乎要做的最后一件事是将“批准人”用户,日期和状态应用于应该显示在自定义屏幕的“批准细节”选项卡中的EPApproval记录。
现在,从推测开始,回到我所做的事情,以进一步发展……
在需要自定义批准的数据的DAC中,请确保按以下方式定义“状态”,“保留”,“已批准”和“拒绝”。注意它何时在数据库中和何时不在数据库中。
#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
[PXDBBool()]
[PXDefault(false, PersistingCheck = PXPersistingCheck.Nothing)]
[PXUIField(DisplayName = "Approved", Visibility = PXUIVisibility.Visible, Enabled = false)]
public virtual bool? Approved { get; set; }
public abstract class approved : IBqlField { }
#endregion
#region Rejected
[PXBool]
[PXDefault(false, PersistingCheck = PXPersistingCheck.Nothing)]
public abstract class rejected : IBqlField { }
#endregion
#region Status
[PXDBString(1)]
[PXDefault(XXRegister.Statuses.Hold)]
[PXUIField(DisplayName = "Status", Visibility = PXUIVisibility.SelectorVisible, Enabled = false)]
[Statuses.List]
public virtual string Status { get; set; }
public abstract class status : IBqlField { }
#endregion
请注意,已拒绝是PXBool,而不是PXDBBool-因此,请勿将其添加到数据库的表中。
在图中,添加事件处理程序。我希望使用此选项来设置当前记录的分支,因为我希望最终用户仅查看其分支中的记录。
#region XXRegister_RowInserting
protected void XXRegister_RowInserting(PXCache sender, PXRowInsertingEventArgs e)
{
XXRegister row = (XXRegister)e.Row;
row.BranchID = PXAccess.GetBranchID();
}
#endregion
尽管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或SO。我还没有完成,但这使我摆脱了我一直在问的障碍。完全公开-当我拒绝批准时,我的批准详细信息不会更新为显示谁/何时/状态,但是我的数据记录已正确更新。但这不是我在这里问的真正问题,因此这应该涵盖了原来的预期问题。
答案 1 :(得分:0)
最后使用标准Acumatica代码和自动化步骤完成了整个过程。这对于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
}
}
我的文档DAC:
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) { }
}
}
}
覆盖EPApprovalMapMaint屏幕类型列表:
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格式的,但最好使用它来学习阅读此格式并在“自动化步骤”屏幕中复制适当的步骤。就我而言,我利用一个非数据库字段来批准,以帮助触发批准的进展(EPApprovalAutomation),但是我的状态为:保留->待批准-> [已批准|已拒绝],我实际上不需要存储在数据库中“批准”。
<?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>
代码的其他部分(包括设置屏幕)很容易创建,以遵循原始帖子中提到的Brendan的帖子。这些代码示例应指导您完成我反复尝试的所有步骤,并尝试遵循面包屑进行遍历(许多跟踪仅以元数据结尾,而不是以不完整的成熟代码结尾)实际上在我的CodeRepository中。)
答案就在我眼前,这一切看起来都很简单。但是现在知道系统如何在代码级别处理批准,这对我来说找到方法非常困难。
从根本上来说,请了解EPApprovalAutomation是标准的批准钩子。它管理EPApproval记录,这些记录是实际的批准。您需要为其设置一个批准图,以了解要遵循的批准树,但是Brendan再次出色地解释了他的帖子中的设置。
您告诉EPApprovalAutomation您要批准的文件/记录是什么,并为它提供管理批准所需的相关字段名称。我在自动化步骤中将字段设置为批准或拒绝,并且EPApprovalAutomation注意到将字段设置为true以处理批准步骤。请注意,它似乎仅根据输入来真正管理EPApproval记录。它将查找适当的批准图,并保留批准完成或查找下一步以开始该批准。
必须以某种方式编写自动化步骤,以便在满足正确条件时操纵诸如状态之类的字段。在我的情况下,当其余代码响应“批准”按钮运行后仍设置了“批准”标志时,就会发生批准。由于我没有保存到数据库,因此我只能查找DAC中的字段何时仍设置为true,然后将状态切换为“已批准”。在“拒绝”上,只需继续并设置“拒绝”即可触发拒绝批准,然后在我的文档中将状态权限设置为“拒绝”。
我对Acumatica还是很陌生,因此可能会有更好的方法来执行其中的一些操作。如果您碰巧更有经验并且有一些见识可以帮助我们所有人提高技能,请发表评论。