实现没有持久字段的DAC

时间:2018-12-28 17:49:02

标签: acumatica

我正在寻求实现一个DAC,以便在处理页面中使用该数据不会持久保存到数据库的页面。

此问题的用例是从外部Web服务加载数据,以准备生成AR付款记录。我们希望用户输入一个专用的“处理”页面,然后能够使用现有的处理页面框架来跟踪付款的产生。

我们需要处理页面调出Web服务并请求任何未处理的付款,然后将数据加载到DAC实例中,以在选择要处理的付款之前显示给最终用户。我们看不到将这些数据持久保存到数据库中的任何理由,因此我一直试图仅使用DAC中的非持久属性来使其正常工作。我接近完成此操作的唯一方法是使用[PXDB {type}]属性声明至少一个字段。如果我尝试让它们全部使用非持久性,则会收到如下错误消息:“关键字'FROM'附近的语法不正确。”

我还在寻找一种方法,该方法在第一次实例化图形时就加载处理网格。我可以通过按钮加载数据,但是每当更新处理图时都需要加载结果。我可能会为此创建一个不同的票证,以使该票证专注于非持久DAC。

我已经进行了一次真正的尝试,以仔细阅读文档中有关如何执行此操作的所有线索,但是在此特定用例中,我还没有看到任何内容。我假设我需要使用一个类级别的属性来设置此属性,但没有成功找到需要使用的内容。

[Serializable]
//[PXNonInstantiatedExtension] this looked close
                //to what we are looking for but experimenting 
                //with it did not yield desired results.
public class CtpPayment : IBqlTable
{

    #region Selected
    public abstract class selected : IBqlField{ }
    [PXBool]
    [PXUIField(DisplayName = "Selected")]
    public virtual bool? Selected { get; set; }
    #endregion

    public abstract class id : IBqlField { }
    //todo: find out what size we need 50 is just a guess.
    //[PXDBString(50, IsKey = true)] //We are able to get this to work only if 
                                    //we have at least one persisting field.
                                    //we can live with this but would prefer to 
                                    //have the whole class as non-persistent
    [PXString(50)] //having only non-persisting attributes will result in a 
                    //Incorrect syntax near the keyword 'FROM'. error.
    [PXUIField(DisplayName = "Click To Pay Id")]
    public virtual string Id { get; set; }

    public abstract  class description : IBqlField {}
    [PXString(200)]
    [PXUIField(DisplayName = "Payment Description")]
    public virtual string Description { get; set; }

    public abstract  class amount : IBqlField { }
    [PXDecimal(2)]
    [PXUIField(DisplayName = "Payment Amount")]
    public  virtual decimal? Amount { get; set; }

    public abstract class customerId : IBqlField { }

    [PXInt]
    [PXUIField(DisplayName = "Customer ID")]
    //todo: decorate this with the needed attributes to display friendly key instead of int.

    public virtual int? CustomerID { get; set; }
}


//the graph is defined as follows.

//todo: follow up and determine the most appropriate name for this graph.
public class CtpPaymentProcess : PXGraph<CtpPaymentProcess>
{
    public PXAction<CtpPayment> checkForC2PPayments;

    public PXSetup<CtpSetup> setup;

    public PXProcessing<CtpPayment> Payments;

    private CtpAcumatica _ctpAcumatica;
    public CtpAcumatica CtpAcumatica
    {
        get
        {
            if (_ctpAcumatica == null)
            {
                var graph = PXGraph.CreateInstance<PXGraph>();
                _ctpAcumatica = new CtpAcumatica(setup.Current.CtpUrl,
                    setup.Current.CtpApiKey,
                    "NoLongerNeeded", //todo: refactor this out.
                    "NoLongerNeeded", //todo: refactor this out.
                    graph);
            }
            return _ctpAcumatica;
        }
    }

    public CtpPaymentProcess()
    {
        Payments.SetProcessCaption("Process Payments");
        Payments.SetProcessAllCaption("Process All Payments");
        Payments.SetProcessDelegate<CtpPaymentProcess>(
            delegate(CtpPaymentProcess graph, CtpPayment payment)
            {
                graph.Clear();
                graph.ProcessPayment(payment, true);
            }
        );
        this.Initialized += InitializePayments;

    }

    private void InitializePayments(PXGraph sender)
    {
        //this looked like a candidate to auto populate the 
        //graph with pending payments on initializing the graph.
        //this unfortunately does not get the desired end result.
        //it works fine via the button.
        CreateNonPersistedPaymentRecords();
    }

    private void ProcessPayment(CtpPayment payment, bool massProcess)
    {
        PXTrace.WriteInformation($"Processing {payment}");
        //todo: process Payment
    }


    /// <summary>
    /// This is a temporary method with the purpose of exploring retrieval of payments and rendering 
    /// Payment and application records. this method will not exist in production code and will 
    /// be replaced with a dedicated PXProcessing page.
    /// </summary>
    [PXButton]
    [PXUIField(DisplayName = "Check for Click-to-Pay Payments")]
    protected void CheckForC2PPayments()
    {
        //todo: we need to find a way to do this
        //      without the user needing to hit
        //      this button.
        CreateNonPersistedPaymentRecords();
    }

    public QueryPaymentsResponseViewModel payments { get; internal set; }

    private void CreateNonPersistedPaymentRecords()
    {
        payments = this.CtpAcumatica.CheckForAllNewPayments(100);


        PXTrace.WriteInformation("Processing " + (payments.Payments.Count) + " payments");

        if (payments.Payments != null)
        {
            // Loop processing each payment returned from the gateway, storing the 
            // information into non persisted cache.
            foreach (var payment in payments.Payments)
            {
                if (!payment.IsMarkedRetrieved)
                {

                    createPaymentProcessRecord(payment);
                }
            }
        }
    }

    private void createPaymentProcessRecord(PaymentViewModel payment)
    {
        CtpPayment ctpPayment = new CtpPayment
        {
            Id = payment.Id,
            Description = $"CustID{payment.CustomerId} ApsTransID:{payment.ApsTransactionId} Currency{payment.Currency} {payment.AccountName} {payment.PaymentType} {payment.Date}",
            CustomerID = int.Parse(payment.CustomerId),
            Amount = payment.Amount
        };
        var r = Payments.Cache.Insert(ctpPayment) ?? Payments.Cache.Update(ctpPayment);
    }



}

2 个答案:

答案 0 :(得分:1)

非常感谢Samvel Petrosov和HB_Acumatica向我指出正确的方向。我需要做的是以下事情。

创建一个方法,该方法使用与PXProcessing IdentifierName相同的名称返回非通用IEnumerable。例如,我使用了以下

    public PXProcessing<CtpPayment> Payments;

    public IEnumerable payments()
    {
        paymentsFromCtpServer = CtpAcumatica.CheckForAllNewPayments(100);

        PXTrace.WriteInformation("Processing " + (paymentsFromCtpServer.Payments.Count) + " paymentsFromCtpServer");

        if (paymentsFromCtpServer.Payments != null)
        {
            // Loop processing each payment returned from the gateway, storing the 
            // information into non persisted cache.
            foreach (var payment in paymentsFromCtpServer.Payments)
            {
                if (!payment.IsMarkedRetrieved)
                {

                    yield return new CtpPayment
                    {
                        CustomerID = int.Parse(payment.CustomerId),
                        Amount = payment.Amount,
                        Description = $"Payment:{payment.Id}",
                        Id = payment.Id
                    };
                }
            }
        }
    }

这也满足了我对页面初始化后立即加载新数据的要求。

答案 1 :(得分:0)

当您不想保留数据时,利用DAC的最佳方法是使用如下所示的Projection,将Persistent设置为false可以防止保存数据。

url.pathname