如何在销售订单成功完成时自定义销售订单流程以触发自动“添加合同”流程
我们希望将我们的网络订阅服务纳入Acumatica,这意味着我们将服务作为订购产品出售,其开始日期和过期日期,我们希望能够通过添加销售订单来输入销售额,然后添加/更改与该产品相关联的额外“合同”以处理订购到期/续约问题。如何在销售订单成功完成时自定义销售订单流程以触发自动“添加合同”流程
我们的想法是以某种方式自定义销售订单流程,以在每次销售订单完成时自动运行某种检查 - 如果订购产品处于该订单,我们希望自动触发流程以添加/根据订单信息更新合同。
是否可以通过定制完成?
只是想提一下,我一直在使用Web服务API来将我们的电子商务与Acumatica集成,我知道我可以通过轮询订单表然后使用Web服务API来添加合同来实现这一点,但是,它在我看来,如果可行的话,通过某种定制方式在Acumatica内执行此操作会更好。
有没有人知道这个定制是否可以完成,以及如果这样做,如何做?
谢谢。
编辑:
说完看着从@Gabriel和@Hybridzz回应,我已经尝试了一段如下代码:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Avalara.AvaTax.Adapter;
using Avalara.AvaTax.Adapter.TaxService;
using PX.CCProcessingBase;
using PX.Common;
using PX.Data;
using PX.Objects.AP;
using PX.Objects.AR;
using PX.Objects.CA;
using PX.Objects.CM;
using PX.Objects.CR;
using PX.Objects.CS;
using PX.Objects.EP;
using PX.Objects.GL;
using PX.Objects.IN;
using PX.Objects.PO;
using PX.Objects.TX;
using AvaMessage = Avalara.AvaTax.Adapter.Message;
using POLine = PX.Objects.PO.POLine;
using POOrder = PX.Objects.PO.POOrder;
using PX.Objects;
using PX.Objects.SO;
using PX.Objects.CT;
namespace PX.Objects.SO
{
public class SOOrderEntry_Extension:PXGraphExtension<SOOrderEntry>
{
public delegate void PersistDelegate();
[PXOverride]
public void Persist(PersistDelegate baseMethod)
{
using (PXTransactionScope ts = new PXTransactionScope())
{
// Create, setup and activate contracts
ContractMaint contractMaint = PXGraph.CreateInstance<ContractMaint>();
CTBillEngine engine = PXGraph.CreateInstance<CTBillEngine>();
//var tranExt = PXCache<ARTran>.GetExtension<ARTranExt>(tran);
string contractCD = "1234567";
DateTime startDate = new DateTime(2015,1,1);
Contract contract = SetupActivateContract(contractMaint, contractCD, startDate , 13128,14330, engine);
}
baseMethod();
}
private Contract SetupActivateContract(ContractMaint contractMaint, string contractCD, DateTime? invoiceDate, int? customerID,
int? customerLocationID, CTBillEngine engine)
{
contractMaint.Clear();
// Initialize new contract
Contract contract = (Contract)contractMaint.Contracts.Cache.CreateInstance();
contract.ContractCD = contractCD;
contract = contractMaint.Contracts.Insert(contract);
// Lookup contract template ID
Contract template = PXSelect<Contract,
Where<Contract.isTemplate, Equal<boolTrue>, And<Contract.contractCD, Equal<Required<Contract.contractCD>>>>>
.Select(Base, "MMS");
if (template == null) throw new PXException("The MMS contract template was not found.");
// Set required fields
contract.TemplateID = template.ContractID;
contract.CustomerID = customerID;
contract = contractMaint.Contracts.Update(contract);
contract.LocationID = customerLocationID;
contract.StartDate = invoiceDate;
contract.ActivationDate = invoiceDate;
ContractMaint.SetExpireDate(contract);
contract = contractMaint.Contracts.Update(contract);
// Save generated contract
contractMaint.Save.Press();
// Setup and activate the contract
engine.SetupAndActivate(contract.ContractID, contract.ActivationDate);
return contract;
}
}
}
的代码进行了验证,并没有任何问题发表,但是,当我试图添加销售订单,但没有看到任何合同被添加到数据库中,正如我所料。我确实添加了一些“抛出异常”的声明,以确保在销售订单过程中实际调用了这段代码,但我不明白为什么不添加合同。
请注意,这是我第一次尝试定制,虽然我在Web服务API方面有一些经验,但可能有一些基本的东西我没有意识到。
任何帮助,将不胜感激。
本主题涵盖了(尚未公布)的定制培训。培训是围绕一个名为“YogiFon”的虚构移动电话公司进行的。在发放发票时,系统将检查发票是否包含库存代码为“SIMCARD”的物料,并且作为发布过程的一部分自动设置合同。作为此定制的一部分,两个自定义字段已添加到发票行中,以便用户输入电话号码和SIM卡ID。这些字段与合同属性一起存储。
需要两个图形扩展,一个用于ARReleaseProcess图形,另一个用于SOInvoiceEntry图形。我写了最初的例子,但学分归Ruslan Devyatko审查。
ARReleaseProcess扩展:
public class ARReleaseProcess_Extension : PXGraphExtension<ARReleaseProcess>
{
public bool SetupContract = false;
public delegate void PersistDelegate();
[PXOverride]
public void Persist(PersistDelegate baseMethod)
{
// use ARDocument.Current
ARRegister invoice = (ARRegister)Base.Caches[typeof(ARRegister)].Current;
List<Contract> setupContracts = new List<Contract>();
if (SetupContract)
{
// Create, setup and activate contracts
ContractMaint contractMaint = PXGraph.CreateInstance<ContractMaint>();
CTBillEngine engine = PXGraph.CreateInstance<CTBillEngine>();
int seq = 1;
//reuse ARTran_TranType_RefNbr from ARReleaseProcess
foreach (ARTran tran in
PXSelect<ARTran,
Where<ARTran.tranType, Equal<Required<ARInvoice.docType>>,
And<ARTran.refNbr, Equal<Required<ARInvoice.refNbr>>,
And<ARTranExt.usrSIMCardID, IsNotNull,
And<ARTranExt.usrContractID, IsNull>>>>,
OrderBy<Asc<ARTran.tranType, Asc<ARTran.refNbr, Asc<ARTran.lineNbr>>>>>.
Select(Base, invoice.DocType, invoice.RefNbr))
{
// Create, setup and activate contract for a particular SOInvoice line
var tranExt = PXCache<ARTran>.GetExtension<ARTranExt>(tran);
string contractCD = String.Format("{0}{1:00}", invoice.RefNbr, seq);
Contract contract = SetupActivateContract(contractMaint, contractCD, invoice.DocDate, invoice.CustomerID,
invoice.CustomerLocationID, tranExt.UsrSIMCardID, tranExt.UsrPhoneNumber, engine);
setupContracts.Add(contract);
// Associate generated contract with the SOInvoice line
tranExt.UsrContractID = contract.ContractID;
Base.ARTran_TranType_RefNbr.Cache.Update(tran);
seq++;
}
}
baseMethod();
}
private Contract SetupActivateContract(ContractMaint contractMaint, string contractCD, DateTime? invoiceDate, int? customerID,
int? customerLocationID, string simCardID, string phoneNumber, CTBillEngine engine)
{
contractMaint.Clear();
// Initialize new contract
Contract contract = (Contract)contractMaint.Contracts.Cache.CreateInstance();
contract.ContractCD = contractCD;
contract = contractMaint.Contracts.Insert(contract);
// Lookup contract template ID
Contract template = PXSelect<Contract,
Where<Contract.isTemplate, Equal<boolTrue>, And<Contract.contractCD, Equal<Required<Contract.contractCD>>>>>
.Select(Base, "SIMCARD");
if (template == null) throw new PXException("The SIMCARD contract template was not found.");
// Set required fields
contract.TemplateID = template.ContractID;
contract.CustomerID = customerID;
contract = contractMaint.Contracts.Update(contract);
contract.LocationID = customerLocationID;
contract.StartDate = invoiceDate;
contract.ActivationDate = invoiceDate;
ContractMaint.SetExpireDate(contract);
contract = contractMaint.Contracts.Update(contract);
// Store SIM/Phone Number into attributes
foreach (CSAnswers attribute in contractMaint.Answers.Select())
{
switch (attribute.AttributeID)
{
case "SIMCARDID":
attribute.Value = simCardID;
contractMaint.Answers.Update(attribute);
break;
case "PHONENUM":
attribute.Value = phoneNumber;
contractMaint.Answers.Update(attribute);
break;
}
}
// Save generated contract
contractMaint.Save.Press();
// Setup and activate the contract
engine.SetupAndActivate(contract.ContractID, contract.ActivationDate);
return contract;
}
}
SOInvoiceEntry扩展:
public class SOInvoiceEntry_Extension : PXGraphExtension<SOInvoiceEntry>
{
#region Event Handlers
protected void ARTran_RowSelected(PXCache cache, PXRowSelectedEventArgs e, PXRowSelected InvokeBaseHandler)
{
if (InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = (ARTran)e.Row;
if (row == null) return;
// The SIM Card ID and the Phone Number fields are only editable when the SIMCARD item is used
// In real life you would have a flag in InventoryItem to indicate that, rather than hardcoding based on InventoryCD
InventoryItem item = (InventoryItem)PXSelectorAttribute.Select<ARTran.inventoryID>(Base.Transactions.Cache, row);
bool enableFields = item != null && item.InventoryCD.StartsWith("SIMCARD");
PXUIFieldAttribute.SetEnabled<ARTranExt.usrSIMCardID>(cache, row, enableFields);
PXUIFieldAttribute.SetEnabled<ARTranExt.usrPhoneNumber>(cache, row, enableFields);
}
#endregion
public PXAction<ARInvoice> release;
[PXUIField(DisplayName = "Release", Visible = false)]
[PXButton()]
public IEnumerable Release(PXAdapter adapter)
{
PXGraph.InstanceCreated.AddHandler<ARReleaseProcess>((graph) =>
{
// Create, setup and activate contracts while releasing SOInvoice
graph.GetExtension<ARReleaseProcess_Extension>().SetupContract = true;
});
return Base.release.Press(adapter);
}
}
您可以覆盖坚持的salesorder图SOOrderEntry
[PXOverride]
public void Persist(Action persit)
{
using (PXTransactionScope ts = new PXTransactionScope())
{
persit(); // this will call base graph Persist();
//If no error the document save is completed, but still wrapped in a transaction and you can do your logic below this
}
}
这的确是可能的,以及即将推出的T300培训包括这个具体的例子。我写了这个例子,并将其检索到这里发布! – Gabriel
谢谢,@ Gabriel!期待你的榜样。 – Gladiator