这个想法是,如果客户默认要在发票上打印,则可以在库存级别指定。它可以在服务订单级别上覆盖,并在约会级别再次覆盖。 我的工作良好。
问题是您打印发票的时候。在约会中,当点击“运行计费”按钮时,它运行了一种称为InvoIceapointment()的方法,该方法将约会细节转移到Artran数据库,然后运行Arinvoiceentry图。
但它正在创建来自库存默认值的记录,而忽略了约会设置。我在某个事件中无法做到,因为当它发送到refnbr.
中的Arinvoiceentry时。没有refnbr,我无法获得约会的FSODET线。 我需要查找相关的约会,但是我看不到Arinvoiceentry Graph的Cache中的任何地方。
我们曾经在此处使用fsxartran作为交叉参考,这将使我们能够获取链接到fsartran的FSService订单信息,但现在已经消失了。没有人知道我如何覆盖InvoIceAppointment()方法,抓住约会细节,然后将其设置为将其带给用户的arinvoiceentry图表?
如果有助于这是源代码中的InvoIceAppointment()方法,该方法在单击按钮时被调用。public PXAction<FSAppointment> invoiceAppointment;
[PXButton]
[PXUIField(DisplayName = "Run Billing", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select)]
public IEnumerable InvoiceAppointment(PXAdapter adapter)
{
List<FSAppointment> list = adapter.Get<FSAppointment>().ToList();
List<AppointmentToPost> rows = new List<AppointmentToPost>();
if (!adapter.MassProcess)
{
SaveWithRecalculateExternalTaxesSync();
}
if (ServiceOrderTypeSelected.Current != null && ServiceOrderRelated.Current != null
&& ServiceOrderTypeSelected.Current.PostTo == ID.Batch_PostTo.SO)
{
ValidateContact(ServiceOrderRelated.Current);
}
foreach (FSAppointment fsAppointmentRow in list)
{
// Acuminator disable once PX1008 LongOperationDelegateSynchronousExecution [compatibility with legacy code]
PXLongOperation.StartOperation(
this,
delegate ()
{
SetServiceOrderStatusFromAppointment(ServiceOrderRelated.Current, fsAppointmentRow, ActionButton.InvoiceAppointment);
CreateInvoiceByAppointmentPost graphCreateInvoiceByAppointmentPost = PXGraph.CreateInstance<CreateInvoiceByAppointmentPost>();
graphCreateInvoiceByAppointmentPost.Filter.Current.PostTo = ServiceOrderTypeSelected.Current.PostTo == ID.SrvOrdType_PostTo.ACCOUNTS_RECEIVABLE_MODULE ? ID.Batch_PostTo.AR_AP : ServiceOrderTypeSelected.Current.PostTo;
graphCreateInvoiceByAppointmentPost.Filter.Current.IgnoreBillingCycles = true;
graphCreateInvoiceByAppointmentPost.Filter.Current.BranchID = fsAppointmentRow.BranchID;
graphCreateInvoiceByAppointmentPost.Filter.Current.LoadData = true;
if (fsAppointmentRow.ActualDateTimeEnd > Accessinfo.BusinessDate)
{
graphCreateInvoiceByAppointmentPost.Filter.Current.UpToDate = fsAppointmentRow.ActualDateTimeEnd;
graphCreateInvoiceByAppointmentPost.Filter.Current.InvoiceDate = fsAppointmentRow.ActualDateTimeEnd;
}
graphCreateInvoiceByAppointmentPost.Filter.Insert(graphCreateInvoiceByAppointmentPost.Filter.Current);
AppointmentToPost appointmentToPostRow = graphCreateInvoiceByAppointmentPost.PostLines.Current =
graphCreateInvoiceByAppointmentPost.PostLines.Search<AppointmentToPost.refNbr>(fsAppointmentRow.RefNbr, fsAppointmentRow.SrvOrdType);
if (appointmentToPostRow == null)
{
throw new PXSetPropertyException(TX.Error.DocumentCannotBeInvoiced, fsAppointmentRow.SrvOrdType, fsAppointmentRow.RefNbr);
}
rows = new List<AppointmentToPost>
{
appointmentToPostRow
};
Guid currentProcessID = graphCreateInvoiceByAppointmentPost.CreateInvoices(graphCreateInvoiceByAppointmentPost, rows, graphCreateInvoiceByAppointmentPost.Filter.Current, adapter.QuickProcessFlow, false);
if (graphCreateInvoiceByAppointmentPost.Filter.Current.PostTo == ID.SrvOrdType_PostTo.SALES_ORDER_MODULE
|| graphCreateInvoiceByAppointmentPost.Filter.Current.PostTo == ID.SrvOrdType_PostTo.SALES_ORDER_INVOICE)
{
foreach (PXResult<FSPostBatch> result in SharedFunctions.GetPostBachByProcessID(this, currentProcessID))
{
FSPostBatch fSPostBatchRow = (FSPostBatch)result;
graphCreateInvoiceByAppointmentPost.ApplyPrepayments(fSPostBatchRow);
}
}
AppointmentEntry apptGraph = PXGraph.CreateInstance<AppointmentEntry>();
apptGraph.AppointmentRecords.Current =
apptGraph.AppointmentRecords.Search<FSAppointment.refNbr>
(fsAppointmentRow.RefNbr, fsAppointmentRow.SrvOrdType);
if (!adapter.MassProcess || this.IsMobile == true)
{
using (new PXTimeStampScope(null))
{
apptGraph.AppointmentPostedIn.Current = apptGraph.AppointmentPostedIn.SelectWindowed(0, 1);
apptGraph.openPostingDocument();
}
}
});
}
return list;
}
有人建议覆盖这样的方法:
public PXAction<FSAppointment> invoiceAppointment;
[PXButton]
[PXUIField(DisplayName = "Run Billing", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select)]
public IEnumerable InvoiceAppointment(PXAdapter adapter)
{
PXGraph.InstanceCreated.AddHandler<ARInvoiceEntry>((g) =>
{
g.RowPersisting.AddHandler<ARInvoice>((cache, e) =>
{
var invoiceGraph = cache.Graph as ARInvoiceEntry;
var transactions = invoiceGraph.Transactions.Select()?.FirstTableItems;
ARInvoice invoice = (ARInvoice)e.Row;
foreach (ARTran tran in transactions)
{
// Can use SetValueExt just like regular event handlers
cache.SetValueExt<ARInvoiceExt.usrPrintFlag>(tran, false);
}
});
});
var result = Base.InvoiceAppointment(adapter);
}
但是,以这种方式添加的处理程序永远不会执行,因此我没有位置可以放置代码来检查服务订单主。
关于我从这里去哪里的任何建议?
我无法弄清楚如何实施此解决方案(这令人沮丧,因为我认为有办法。)
我回到了用例:
从约会中选择“运行计费”措施时,它会创建一个 发票是Arinvoice的主记录,并带有详细记录 从约会DET复制到Artran Records。有一个自定义 约会记录的字段称为usrprintflag,用户 可能已经设置了。我需要确保约会的价值 Record的Usrprintflag被复制到Artran的USR领域。 名称
挑战是,当约会DET记录首次复制时,发票没有参考号,并且Artran记录没有大多数字段。预约不是Arinvoiceentry图的一部分,并且没有缓存或链接可用于找到创建Arinvoice的应用程序。因此,覆盖Rowinsert的方法是没有用的。 (以前有一个名为FSXARTRAN的投影,它传达了约会信息,但已被贬低并删除了几个发行版。))
我的解决方案是放置逻辑IN
Events.FieldUpdated<ARTran, ARTran.inventoryID> e
我可以提出一些理论上可能失败的情况,但是在现实生活中这些情况似乎并不合理。
在其他任何人都遇到的情况下,这里有一些示例代码:
protected virtual void _(Events.FieldUpdated<ARTran, ARTran.inventoryID> e)
{
if (e is null) return;
if (!IsPrintFeatureEnabledForFS()) return; // if not active, just leave
var tran = e.Row as ARTran;
// Best I can determine, if an Invoice is created for a SALES ORDER
// the SOOrderType in ARTran is "SO" -- if it is a SERVICE ORDER
// SOOrderType will == NULL
if (tran.SOOrderType != null)
return;
ARTranExt rowExt = e.Row.GetExtension<ARTranExt>();
if (rowExt == null) return;
if (e.OldValue is null)
{
if (IsPrintFeatureEnabledForFS())
{
// Add Check to see what ScreenID created this record. We only care about FS300200
if (tran.CreatedByScreenID == "FS300200")
{
var ApptTransaction = FindMatchingAppointmentDetail(tran.CustomerID, tran.LineNbr, tran.InventoryID);
if (ApptTransaction != null)
{
FSAppointmentDetExt apDetExt = ApptTransaction.GetExtension<FSAppointmentDetExt>();
rowExt.UsrASGPrintCheck = apDetExt?.UsrASGPrintCheck;
}
}
}
}
}
魔术功能可以找到约会:
private FSAppointmentDet FindMatchingAppointmentDetail(int? CustomerID, int? LineNbr, int? InventoryID) { // We want to see the Apppointment closed since that is where they "Run Billing" From var appointments = SelectFrom<FSAppointment> .Where<FSAppointment.customerID.IsEqual<@P.AsInt> .And<FSAppointment.status.IsEqual<@P.AsString>>> .OrderBy<Desc<FSAppointment.actualDateTimeEnd>> .View.Select(Base, CustomerID, "Z"); // "Z" for closed status, Billing would be 'B' foreach (PXResult<FSAppointment> result in appointments) { FSAppointment fsAppointment = result; PXTrace.WriteInformation($"Checking Appointment: {fsAppointment.AppointmentID} (Date: {fsAppointment.ActualDateTimeEnd})"); // Get all appointment details for this appointment var appointmentDetails = PXSelect<FSAppointmentDet, Where<FSAppointmentDet.appointmentID, Equal<Required<FSAppointment.appointmentID>>, And<FSAppointmentDet.lineNbr, Equal<Required<FSAppointmentDet.lineNbr>>>>> .Select(Base, fsAppointment.AppointmentID, LineNbr); foreach (FSAppointmentDet fsAppointmentDet in appointmentDetails) { // Check if LineNbr matches and InventoryID does, we have a match. This could possibly // give us a false positive, but it seems HIGHLY unlikely. if (fsAppointmentDet.InventoryID == InventoryID) { PXTrace.WriteInformation($"Match found! AppointmentID: {fsAppointment.AppointmentID}, LineNbr: {fsAppointmentDet.LineNbr}"); return fsAppointmentDet; // Return the first match (newest first) } } } PXTrace.WriteWarning("No matching appointment detail found."); return null; // No match found }
至少现在正在工作。