Реализация ЦАП без сохраняемых полей - PullRequest
0 голосов
/ 28 декабря 2018

Я хочу реализовать ЦАП для использования на странице обработки, где данные не будут сохранены в базе данных.

Вариант использования этого вопроса - загрузка данных из внешнего веб-сервиса вподготовка к созданию платежных записей AR.Мы бы хотели, чтобы пользователь зашел на выделенную страницу обработки, а затем смог бы использовать существующую платформу обработки страниц, чтобы выполнить генерацию платежа.

Нам нужна страница обработки, чтобы обратиться к веб-службеи запрашивать любые необработанные платежи, затем загружать данные в экземпляры DAC для отображения конечному пользователю, прежде чем платежи будут выбраны для обработки.Мы не видим причин для сохранения этих данных в базе данных, поэтому я пытался заставить их работать, используя только непостоянные атрибуты в ЦАП.Единственный способ приблизиться к этому - это объявить хотя бы одно поле с помощью атрибута [PXDB {type}].Если я пытаюсь заставить их всех использовать непостоянные данные, я получаю сообщение об ошибке «Неверный синтаксис рядом с ключевым словом« FROM. »»

Я также ищу способ загрузить сетку обработки, когда графикпервый экземпляр.Я могу загрузить данные с помощью кнопки, но результат должен загружаться всякий раз, когда обновляется график обработки.Я, вероятно, создам для этого отдельный билет, чтобы этот фокус был сфокусирован на непостоянном ЦАП.

Я дал искреннюю попытку просмотреть документацию по любым подсказкам, как это сделать, но я этого не сделалвидел что-нибудь еще в этом конкретном случае использования.Я предполагаю, что мне нужно использовать атрибут уровня класса, чтобы установить это, но не удалось найти то, что нужно использовать.

[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 голосов
/ 28 января 2019

Лучший способ использовать ЦАП, если вы не хотите сохранять данные, - это использовать проекцию, как показано ниже, если для параметра «Постоянный» задано значение «ложь», данные не сохраняются.

[PXProjection(typeof(Select<MyOriginalTable, 
        Where<MyOriginalTable.myField1, Equal<True>>>), Persistent = false)]
public class MyOriginalTableProjection : PX.Data.IBqlTable
{
    public abstract class myField1: PX.Data.IBqlField
    {
    }
    protected bool? _MyField1;
    [PXDBBool(BqlField = typeof(MyOriginalTable.myField1)]
    [PXDefault(true)]
    public virtual bool? MyField1
    {
        get
        {
            return this._MyField1;
        }
        set
        {
            this._MyField1 = value;
        }
    }
    public abstract class myField2 : PX.Data.IBqlField
    {
    }
    protected String _MyField2;
    [PXDBString(1, IsKey = true, BqlField = typeof(MyOriginalTable.myField2))]
    public virtual String MyField2
    {
        get
        {
            return this._MyField2;
        }
        set
        {
            this._MyField2 = value;
        }
    }

    public abstract class myField3: PX.Data.IBqlField
    {
    }
    protected String _MyField3;
    [PXDBString(15, IsKey = true, BqlField = typeof(MyOriginalTable.myField3))]
    public virtual String MyField3
    {
        get
        {
            return this._MyField3;
        }
        set
        {
            this._MyField3 = value;
        }
    }
}
0 голосов
/ 28 декабря 2018

Большое спасибо Самвелу Петросову и HB_Acumatica за то, что они указали мне правильное направление.Мне нужно было сделать следующее:

Создать метод, который возвращает неуниверсальный IEnumerable, используя то же имя, что и PXProcessing IdentifierName.например, я использовал следующее

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

Это также соответствовало моему требованию иметь эту загрузку с новыми данными, как только страница инициализируется.

...