如何定制销售订单过程以触发自动“添加合同”。成功完成销售订单的过程 [英] How to customize the sales order process to trigger an automatic "adding contract" process when sales order is successfully completed

查看:85
本文介绍了如何定制销售订单过程以触发自动“添加合同”。成功完成销售订单的过程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们希望将我们的Web订阅服务容纳到Acumatica中,这意味着我们将服务作为具有开始日期和到期日期的订阅产品进行销售,并且我们希望能够通过添加销售订单然后添加/更改与该产品相关的额外合同以处理订阅到期/续订问题。



我们的想法是以某种方式自定义销售订单流程以运行某种检查每次完成销售订单时都会自动执行-如果订购产品处于该订单中,我们希望根据该订单信息自动触发流程以添加/更新合同。



可以通过定制完成吗?



我只想提一下,我一直在使用Web Service API将我们的电子商务与Acumatica集成在一起,我知道我可以通过轮询订单表,然后使用Web服务API添加合同来实现这一点,但是,在我看来,这将是bett

有人知道这种定制是否可以完成,如果可以的话怎么做? / p>

谢谢。



编辑:



已查找来自@Gabriel和@Hybridzz的回复,我尝试了如下代码:

  using System; 
使用System.Collections;
使用System.Collections.Generic;
使用System.Diagnostics;
使用System.Linq;
使用System.Text;
使用Avalara.AvaTax.Adapter;
使用Avalara.AvaTax.Adapter.TaxService;
使用PX.CCProcessingBase;
使用PX.Common;
使用PX.Data;
使用PX.Objects.AP;
使用PX.Objects.AR;
使用PX.Objects.CA;
使用PX.Objects.CM;
使用PX.Objects.CR;
使用PX.Objects.CS;
使用PX.Objects.EP;
使用PX.Objects.GL;
使用PX.Objects.IN;
使用PX.Objects.PO;
使用PX.Objects.TX;
使用AvaMessage = Avalara.AvaTax.Adapter.Message;
使用POLine = PX.Objects.PO.POLine;
使用POOrder = PX.Objects.PO.POOrder;
使用PX.Objects;
使用PX.Objects.SO;
使用PX.Objects.CT;

命名空间PX.Objects.SO
{

公共类SOOrderEntry_Extension:PXGraphExtension< SOOrderEntry>
{
公共代表void PersistDelegate();
[PXOverride]
public void Persist(PersistDelegate baseMethod)
{
使用(PXTransactionScope ts = new PXTransactionScope())
{

//创建,设置和激活合同
ContractMaint contractMaint = PXGraph.CreateInstance< ContractMaint>();
CTBillEngine引擎= PXGraph.CreateInstance< CTBillEngine>();
// var tranExt = PXCache< ARTran> .GetExtension< ARTranExt>(tran);
string contractCD = 1234567;
DateTime startDate = new DateTime(2015,1,1);
合约合约= SetupActivateContract(contractMaint,contractCD,startDate,13128,14330,engine);
}
baseMethod();
}

private Contract SetupActivateContract(ContractMaint contractMaint,string contractCD,DateTime?invoiceDate,int?customerID,
int?customerLocationID,CTBillEngine引擎)
{
contractMaint.Clear();

//初始化新合约
合约合约=(Contract)contractMaint.Contracts.Cache.CreateInstance();
contract.ContractCD = contractCD;
contract = contractMaint.Contracts.Insert(contract);

//查找合同模板ID
合同模板= PXSelect< Contract,
where< Contract.isTemplate,Equal< boolTrue> ;, And< Contract.contractCD,Equal< Required< Contract.contractCD>>>>
.Select(Base, MMS);
如果(template == null)抛出新的PXException(未找到MMS合同模板。);

//设置必填字段
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);

//保存生成的合同
contractMaint.Save.Press();
//设置并激活合同
engine.SetupAndActivate(contract.ContractID,contract.ActivationDate);

返还合同;
}
}
}

代码已验证并发布没问题,但是,当我尝试添加销售订单时,没有看到我期望的任何合同被添加到数据库中。我确实添加了一些抛出异常语句,以确保在销售订单过程中确实调用了这段代码,但是我只是不明白为什么未添加合同。



请注意,这是我第一次尝试进行自定义,尽管我在Web服务API方面有一些经验,但可能有些基本的知识我并没有意识到



任何帮助将不胜感激。

解决方案

(尚未发布)定制培训中介绍了此内容。培训围绕一家名为 YogiFon的虚拟手机公司进行。下达发票时,系统将检查发票是否包含库存代码为 SIMCARD的项目,并在发布过程中自动设置合同。作为此自定义的一部分,两个自定义字段已添加到发票行,以使用户输入电话号码和SIM卡ID。这些字段与合同属性一起存储。



需要两个图扩展,一个用于ARReleaseProcess图,另一个用于SOInvoiceEntry图。我写了最初的示例,但要感谢Ruslan Devyatko进行审查。



ARReleaseProcess扩展:

 公共类ARReleaseProcess_Extension:PXGraphExtension< ARReleaseProcess> 
{
public bool SetupContract = false;

公共代表void PersistDelegate();
[PXOverride]
public void Persist(PersistDelegate baseMethod)
{
//使用ARDocument.Current
ARRegister发票=(ARRegister)Base.Caches [typeof(ARRegister )]。当前;
List< Contract> setupContracts = new List< Contract>();

if(SetupContract)
{
//创建,设置和激活合同
ContractMaint contractMaint = PXGraph.CreateInstance< ContractMaint>();
CTBillEngine引擎= PXGraph.CreateInstance< CTBillEngine>();

int seq = 1;

//从ARReleaseProcess
foreach重复使用ARTran_TranType_RefNbr(ARTran tran in
PXSelect< ARTran,
where< ARTran.tranType,Equal< Required< ARInvoice.docType>> ;,
和< ARTran.refNbr,等于< Required< ARInvoice.refNbr>>,
和< ARTranExt.usrSIMCardID,IsNotNull,
和< ARTranExt.usrContractID,IsNull>>> ;>,
OrderBy< Asc< ARTran.tranType,Asc< ARTran.refNbr,Asc< ARTran.lineNbr>>>> ;.
Select(Base,invoice.DocType,invoice .RefNbr))
{
//为特定的SOInvoice行
创建,设置和激活合同var tranExt = PXCache< ARTran> .GetExtension< ARTranExt>(tran);
string contractCD = String.Format( {0} {1:00},invoice.RefNbr,seq);
合约合同= SetupActivateContract(contractMaint,contractCD,invoice.DocDate,invoice.CustomerID,
invoice.CustomerLocationID,tranExt.UsrSIMCardID,tranExt.UsrPhoneNumber,engine);
setupContracts.Add(contract);

//将生成的合同与SOInvoice行
关联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,字符串simCardID,字符串phoneNumber,CTBillEngine引擎)
{
contractMaint.Clear();

//初始化新合约
合约合约=(Contract)contractMaint.Contracts.Cache.CreateInstance();
contract.ContractCD = contractCD;
contract = contractMaint.Contracts.Insert(contract);

//查找合同模板ID
合同模板= PXSelect< Contract,
where< Contract.isTemplate,Equal< boolTrue> ;, And< Contract.contractCD,Equal< Required< Contract.contractCD>>>>
.Select(Base, SIMCARD);
如果(template == null)抛出新的PXException(找不到SIMCARD合同模板。);

//设置必填字段
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);

//将SIM /电话号码存储到属性
foreach(contractMaint.Answers.Select()中的CSAnswers属性)
{
switch(attribute.AttributeID)
{
case SIMCARDID:
属性。值= simCardID;
contractMaint.Answers.Update(attribute);
休息时间;
案例 PHONENUM:
属性。值= phoneNumber;
contractMaint.Answers.Update(attribute);
休息时间;
}
}
//保存生成的合同
contractMaint.Save.Press();
//设置并激活合同
engine.SetupAndActivate(contract.ContractID,contract.ActivationDate);

返还合同;
}
}

SOInvoiceEntry扩展名:

 公共类SOInvoiceEntry_Extension:PXGraphExtension< SOInvoiceEntry> 
{
#region事件处理程序
保护无效ARTran_RowSelected(PXCache缓存,PXRowSelectedEventArgs e,PXRowSelected InvokeBaseHandler)
{
如果(InvokeBaseHandler!= null)
InvokeBaseHandler(cache,e);
var row =(ARTran)e.Row;

if(row == null)返回;

//仅当使用SIMCARD项时,SIM卡ID和电话号码字段才可编辑
//在现实生活中,InventoryItem中会有一个标志来表明这一点,而不是基于InventoryCD
InventoryItem item =(InventoryItem)PXSelectorAttribute.Select< ARTran.inventoryID>(Base.Transactions.Cache,row)的硬编码;
bool enableFields = item!= null&& item.InventoryCD.StartsWith( SIMCARD);
PXUIFieldAttribute.SetEnabled< ARTranExt.usrSIMCardID>(缓存,行,enableFields);
PXUIFieldAttribute.SetEnabled< ARTranExt.usrPhoneNumber>(缓存,行,enableFields);
}
#endregion

public PXAction< ARInvoice>释放;
[PXUIField(DisplayName = Release,Visible = false)]
[PXButton()]
public IEnumerable Release(PXAdapter adapter)
{
PXGraph.InstanceCreated .AddHandler< ARReleaseProcess>((graph)=>
{
//创建,设置和激活合同,同时释放SOInvoice
图。GetExtension< ARReleaseProcess_Extension>()。SetupContract = true;
});
返回Base.release.Press(adapter);
}
}


We want to accommodate our web subscription service into Acumatica, which means we sell a service as a subscription product that has starting date and expiration date, and we want to be able to enter the sale by adding sales order and then adding/changing an extra "contract" associated to that product to handle the subscription expiration/renewal issues.

Our idea is to somehow customize the sales order process to run some kind of check automatically every time when a sales order is completed - if a subscription product is in that order, we want a process to be triggered automatically to add/update a contract based on the order information.

Could it be done through customization?

Just want to mention, I have been working with Web Service API to integrate our e-commerce with Acumatica and I know I could implement this by polling the order table and then using web service API to add contract, however, it looks to me it would be better to do this inside Acumatica through some kind of customization if it is doable.

Does anybody know if this customization could be done and how to do it if it does?

Thanks.

Edited:

Having looked responses from @Gabriel and @Hybridzz, I have tried a piece of code as below:

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;
   }
 }
}

The code was validated and published without any problem, however, when I tried to add a sales order, I didn't see any contract being added into database as I expected. I did add some "throw exception" statements to make sure this piece of code was actually called during the sales order process, but I just don't understand why the contract wasn't added.

Please note this is the first time I tried to do customization, although I have some experiences in web service API, there could be something basic that I wasn't aware of.

Any help would be appreciated.

解决方案

This topic is covered in the (yet to be published) customization training. The training is centered around a fictitious mobile phone company called "YogiFon". When release an invoice, system will check whether invoice contains an item with inventory code "SIMCARD", and setup the contract automatically as part of the release process. As part of this customization, two custom fields were added to the invoice lines, to have user input the phone number and SIM Card ID. These fields are stored with the contract attributes.

There are two graph extensions needed, one for the ARReleaseProcess graph, and another one for the SOInvoiceEntry graph. I wrote the original example, but credits goes to Ruslan Devyatko for reviewing it.

ARReleaseProcess extension:

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 extension:

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);
    }
}

这篇关于如何定制销售订单过程以触发自动“添加合同”。成功完成销售订单的过程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆