我正在寻求实现一个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);
}
}
答案 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