Invoice and Memos screen Email Invoice/Memo Action in Acumatica - acumatica

We have created some customized reports and these will open up based on Sales Order type in details tab. Same way we want to send report based on order type when we use Email Invoice/Memo action from Actions menu.
We tried to override the code but still we are seeing default report is sent in email. How can I fix this? My code is below:
[PXOverride]
public PXAction<ARInvoice> sendARInvoiceMemo;
[PXUIField(DisplayName = "Send AR Invoice/Memo", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select)]
[PXLookupButton]
public IEnumerable SendARInvoiceMemo(PXAdapter adapter, String reportID)
{
PXReportRequiredException ex = null;
foreach (ARInvoice doc in adapter.Get<ARInvoice>())
{
var parameters = new Dictionary<string, string>();
ARTran TranData = PXSelectReadonly<ARTran, Where<ARTran.tranType, Equal<Required<ARTran.tranType>>,
And<ARTran.refNbr, Equal<Required<ARTran.refNbr>>>>>.Select(Base, doc.DocType, doc.RefNbr);
if (TranData != null)
{
if (TranData.SOOrderType == "WS" || TranData.SOOrderType == "WO" || TranData.SOOrderType == "TS" || TranData.SOOrderType == "IM")
{
if (reportID == null) reportID = "KR501011";
Dictionary<string, string> mailParams = new Dictionary<string, string>();
if (reportID == "KR501011")
{
mailParams["DocType"] = doc.DocType;
mailParams["RefNbr"] = doc.RefNbr;
if (!ReportNotificationGenerator.Send(reportID, mailParams).Any())
{
throw new PXException(ErrorMessages.MailSendFailed);
}
}
Base.Clear();
Base.Document.Current = Base.Document.Search<ARInvoice.refNbr>(doc.RefNbr, doc.DocType);
}
if (TranData.SOOrderType == "RS" || TranData.SOOrderType == "RO" || TranData.SOOrderType == "PS" || TranData.SOOrderType == "QT")
{
if (reportID == null) reportID = "KR501012";
Dictionary<string, string> mailParams = new Dictionary<string, string>();
if (reportID == "KR501012")
{
mailParams["DocType"] = doc.DocType;
mailParams["RefNbr"] = doc.RefNbr;
if (!ReportNotificationGenerator.Send(reportID, mailParams).Any())
{
throw new PXException(ErrorMessages.MailSendFailed);
}
}
Base.Clear();
Base.Document.Current = Base.Document.Search<ARInvoice.refNbr>(doc.RefNbr, doc.DocType);
}
}
}
if (ex != null) throw ex;
return adapter.Get();
}

Note, no version was specified so my work was done against 2020r2.
SendARInvoiceMemo isn't called by the "Email Invoice/Memo" action. Rather, Notification is the delegate for that action.
Note that notification isn't actually calling a report ID, rather it relies on notification CD and your mailing settings to determine what report to run. I modified the code to change to a new notificationCD I created called INVOICEALT. This is configured to my alternative Report ID.
[PXOverride()]
[PXUIField(DisplayName = "Notifications", Visible = false)]
[PXButton(ImageKey = PX.Web.UI.Sprite.Main.DataEntryF)]
public virtual IEnumerable Notification(PXAdapter adapter,
[PXString]
string notificationCD)
{
foreach (ARInvoice doc in adapter.Get().RowCast<ARInvoice>())
{
Base.Document.Current = doc;
Dictionary<string, string> parameters = new Dictionary<string, string>
{
["DocType"] = doc.DocType,
["RefNbr"] = doc.RefNbr
};
using (var ts = new PXTransactionScope())
{
if (ProjectDefaultAttribute.IsProject(Base, doc.ProjectID) && Base.Activity.IsProjectSourceActive(doc.ProjectID, notificationCD))
{
Base.Activity.SendNotification(PMNotificationSource.Project, notificationCD, doc.BranchID, parameters);
}
else
{
//Base.Activity.SendNotification(ARNotificationSource.Customer, notificationCD, doc.BranchID, parameters); //This is what was there
//Inserted switch based on Sales Order Type >>
ARTran TranData = PXSelectReadonly<ARTran, Where<ARTran.tranType, Equal<Required<ARTran.tranType>>,
And<ARTran.refNbr, Equal<Required<ARTran.refNbr>>>>>.Select(Base, doc.DocType, doc.RefNbr);
switch (TranData.SOOrderType)
{
case "IN":
Base.Activity.SendNotification(ARNotificationSource.Customer, "INVOICEALT", doc.BranchID, parameters);
break;
default:
Base.Activity.SendNotification(ARNotificationSource.Customer, notificationCD, doc.BranchID, parameters);
break;
}
//<< Inserted switch based on Sales Order Type
}
Base.Save.Press();
ts.Complete();
}
yield return doc;
}
}
As an alternative to the above, if you're just talking about users running the action, you could hide the Email Invoice/Memo action and put a new action in place.
public PXAction<ARInvoice> sendARInvoiceMemoAlt;
[PXUIField(DisplayName = "Alt Email Invoice/Memo", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select)]
[PXLookupButton]
public IEnumerable SendARInvoiceMemoAlt(PXAdapter adapter, String reportID)
{
PXReportRequiredException ex = null;
foreach (ARInvoice doc in adapter.Get<ARInvoice>())
{
var parameters = new Dictionary<string, string>();
ARTran TranData = PXSelectReadonly<ARTran, Where<ARTran.tranType, Equal<Required<ARTran.tranType>>,
And<ARTran.refNbr, Equal<Required<ARTran.refNbr>>>>>.Select(Base, doc.DocType, doc.RefNbr);
if (TranData != null)
{
if (TranData.SOOrderType == "SO")// || TranData.SOOrderType == "WS" || TranData.SOOrderType == "WO" || TranData.SOOrderType == "TS" || TranData.SOOrderType == "IM")
{
if (reportID == null) reportID = "AR641000";
Dictionary<string, string> mailParams = new Dictionary<string, string>();
if (reportID == "AR641000")
{
mailParams["DocType"] = doc.DocType;
mailParams["RefNbr"] = doc.RefNbr;
if (!ReportNotificationGenerator.Send(reportID, mailParams).Any())
{
throw new PXException(ErrorMessages.MailSendFailed);
}
}
Base.Clear();
Base.Document.Current = Base.Document.Search<ARInvoice.refNbr>(doc.RefNbr, doc.DocType);
}
if (TranData.SOOrderType == "IN")// || TranData.SOOrderType == "RS" || TranData.SOOrderType == "RO" || TranData.SOOrderType == "PS" || TranData.SOOrderType == "QT")
{
if (reportID == null) reportID = "AR641001";
Dictionary<string, string> mailParams = new Dictionary<string, string>();
if (reportID == "AR641001")
{
mailParams["DocType"] = doc.DocType;
mailParams["RefNbr"] = doc.RefNbr;
if (!ReportNotificationGenerator.Send(reportID, mailParams).Any())
{
throw new PXException(ErrorMessages.MailSendFailed);
}
}
Base.Clear();
Base.Document.Current = Base.Document.Search<ARInvoice.refNbr>(doc.RefNbr, doc.DocType);
}
}
}
if (ex != null) throw ex;
return adapter.Get();
}
public override void Initialize()
{
base.Initialize();
Base.ActionsMenuItem.AddMenuAction(sendARInvoiceMemoAlt);
}

Related

CRExtensionHelper.UpdateServiceOrderHeader()

got the following error after upgrading Acumatica from 2020r2 to 2022r2
After my research found out UpdateServiceOrderHeader() was removed from CRExtensionHelper.cs
now its SM_CRCaseMaint.cs and SM_SOOrderEntry.cs.
I implemented the following changes but got the following error;
full code:
public PXAction<CRCase> CreateServiceOrders;
[PXButton]
[PXUIField(DisplayName = "Create Service Orders", Visible = true, MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select)]
public virtual void createServiceOrders()
{
CRCase crCaseRow = Base.Case.Current;
FSxCRCase fsxCRCaseRow = Base.Case.Cache.GetExtension<FSxCRCase>(crCaseRow);
if (crCaseRow.CustomerID == null && crCaseRow.ContactID == null)
{
throw new PXException("You can not create proceed." + Environment.NewLine + "The Business Account and Contact are NULL.");
}
if (crCaseRow.CustomerID == null)
{
throw new PXException("You can not create proceed." + Environment.NewLine + "The Business Account is NULL.");
}
if (crCaseRow.ContactID == null)
{
throw new PXException("You can not create proceed." + Environment.NewLine + "The ContactID is NULL.");
}
if (CreateServiceOrderFilter.AskExt() == WebDialogResult.OK)
{
fsxCRCaseRow.SDEnabled = true;
fsxCRCaseRow.BranchLocationID = CreateServiceOrderFilter.Current.BranchLocationID;
fsxCRCaseRow.SrvOrdType = CreateServiceOrderFilter.Current.SrvOrdType;
fsxCRCaseRow.AssignedEmpID = CreateServiceOrderFilter.Current.AssignedEmpID;
fsxCRCaseRow.ProblemID = CreateServiceOrderFilter.Current.ProblemID;
PXLongOperation.StartOperation(Base, delegate ()
{
CreateServiceOrderDocument(Base, crCaseRow, CreateServiceOrderFilter.Current);
});
}
}
private void CreateServiceOrderDocument(CRCaseMaint graphCRCaseMaint, CRCase crCaseRow, FSCreateServiceOrderOnCaseFilter fsCreateServiceOrderOnCaseFilterRow)
{
if (graphCRCaseMaint == null || crCaseRow == null || fsCreateServiceOrderOnCaseFilterRow == null)
{
return;
}
ServiceOrderEntry graphServiceOrderEntry = PXGraph.CreateInstance<ServiceOrderEntry>();
FSServiceOrder newServiceOrderRow = CRExtensionHelper.InitNewServiceOrder(CreateServiceOrderFilter.Current.SrvOrdType, ID.SourceType_ServiceOrder.CASE);
graphServiceOrderEntry.ServiceOrderRecords.Current = graphServiceOrderEntry.ServiceOrderRecords.Insert(newServiceOrderRow);
//CRExtensionHelper.UpdateServiceOrderHeader(
// graphServiceOrderEntry,
// Base.Case.Cache,
// crCaseRow,
// fsCreateServiceOrderOnCaseFilterRow,
// graphServiceOrderEntry.ServiceOrderRecords.Current,
// true);
SM_CRCaseMaint.UpdateServiceOrderHeader(
graphServiceOrderEntry,
Base.Case.Cache,
crCaseRow,
fsCreateServiceOrderOnCaseFilterRow,
graphServiceOrderEntry.ServiceOrderRecords.Current,
true);
//graphServiceOrderEntry.ServiceOrderRecords.Current.SourceID = crCaseRow.CaseCD;
graphServiceOrderEntry.ServiceOrderRecords.Current.SourceRefNbr = crCaseRow.CaseCD;
if (!Base.IsContractBasedAPI)
{
throw new PXRedirectRequiredException(graphServiceOrderEntry, null);
}
}
}
#endregion
}
Error CS0120 means that you need to instantiate the object before calling UpdateServiceOrderHeader(). I would try searching the source code for other instances of this function being called and replicate the setup.

Shipment Confirmation and Update IN through code is not working

I have a requirement to create shipment document, shipment confirmation and update In on a Sales order Transfer document.
The following code is used for shipment confirmation
public string CreateShipment()
{
bool flag = false;
string _retval = string.Empty;
using (IEnumerator<PXResult<SOOrderShipment>> enumerator = Base.shipmentlist.Select(Array.Empty<object>()).GetEnumerator())
{
if (enumerator.MoveNext())
{
SOOrderShipment current = enumerator.Current;
flag = true;
}
}
if (flag)
{
string Mess = "Error: Shipment already created.";
throw new PXException(Mess);
}
SOShipmentEntry sOShipmentEntry = PXGraph.CreateInstance<SOShipmentEntry>();
//SOOrderEntry sOOrderEntry = PXGraph.CreateInstance<SOOrderEntry>();
//sOOrderEntry.Caches.Clear();
SOOrder sOOrder = Base.Document.Current;
int? nullable = new int?(0);
int? customerLocationID = new int?(0);
if (sOOrder != null)
{
nullable = sOOrder.CustomerID;
customerLocationID = sOOrder.CustomerLocationID;
}
int? siteID = new int?(0);
//SOShipmentEntry sOShipmentEntry1 = PXGraph.CreateInstance<SOShipmentEntry>();
SOShipment destinationDocument = sOShipmentEntry.Document.Insert();
destinationDocument.ShipmentType = "T";
destinationDocument = sOShipmentEntry.Document.Update(destinationDocument);
destinationDocument.Operation = "I";
destinationDocument = sOShipmentEntry.Document.Update(destinationDocument);
using (IEnumerator<PXResult<SOLine>> enumerator1 = PXSelectReadonly<SOLine, Where<SOLine.orderType, Equal<Required<SOLine.orderType>>, And<SOLine.orderNbr, Equal<Required<SOLine.orderNbr>>>>>.Select(Base, new object[] { Base.Document.Current.OrderType, Base.Document.Current.OrderNbr }).GetEnumerator())
{
if (enumerator1.MoveNext())
{
SOLine line = (SOLine)enumerator1.Current;
siteID = line.SiteID;
}
}
destinationDocument.SiteID = siteID;
destinationDocument.DestinationSiteID = sOOrder.DestinationSiteID;
destinationDocument = sOShipmentEntry.Document.Update(destinationDocument);
//sOOrderEntry.Document.Current = sOOrder;
if (Base.Document.Current != null)
{
DocumentList<SOShipment> documentList = new DocumentList<SOShipment>(sOShipmentEntry);
sOShipmentEntry.CreateShipment(Base.Document.Current, sOShipmentEntry.Document.Current.SiteID, sOShipmentEntry.Document.Current.ShipDate, new bool?(false), "I", documentList);
if (sOShipmentEntry.Document.Current.ShipmentNbr != null)
{
SOOrderEntry sOOrderEntry1 = PXGraph.CreateInstance<SOOrderEntry>();
sOOrderEntry1.Clear();
sOOrderEntry1.Document.Current = sOOrder;
int? openShipmentCntr = Base.Document.Current.OpenShipmentCntr;
if ((openShipmentCntr.GetValueOrDefault() > 0 ? openShipmentCntr.HasValue : false))
{
sOOrder.Status = SOOrderStatus.Shipping;
sOOrder.Hold = new bool?(false);
sOOrderEntry1.Document.Update(sOOrder);
sOOrderEntry1.Save.Press();
_retval = sOShipmentEntry.Document.Current.ShipmentNbr;
}
}
}
return _retval;
}
The following code confirms the shipment and update IN.
private void ConfirmShipment(string shipmentnbr)
{
SOShipmentEntry sOShipmentEntry = PXGraph.CreateInstance<SOShipmentEntry>();
SOOrderEntry sOOrderEntry = PXGraph.CreateInstance<SOOrderEntry>();
sOShipmentEntry.Clear();
sOOrderEntry.Clear();
sOOrderEntry.Document.Current = PXSelect<SOOrder, Where<SOOrder.orderType, Equal<Required<SOOrder.orderType>>, And<SOOrder.orderNbr, Equal<Required<SOOrder.orderNbr>>>>>.Select(sOOrderEntry, Base.Document.Current.OrderType, Base.Document.Current.OrderNbr);
sOShipmentEntry.Document.Current = PXSelect<SOShipment,Where<SOShipment.shipmentNbr, Equal<Required<SOShipment.shipmentNbr>>>>.Select(sOShipmentEntry,shipmentnbr);
if(sOShipmentEntry.Document.Current!= null && sOOrderEntry.Document.Current != null)
{
sOShipmentEntry.ConfirmShipment(sOOrderEntry, sOShipmentEntry.Document.Current);
sOShipmentEntry.UpdateIN.Press();
}
}
I am able to select sales order in purchase receipt transfer document, but the shipment document status still shows open and not completed.
I have tried the run confirm shipment on the document which is already confirmed through code from the menu of shipment document and I am getting the following error
“Shipment counters are corrupted.”

Sending notification to requester when PO is created in Acumatica

I need to be able to send an email to the original requester when a PO is created from a Requisition in Acumatica 6.1.
Per Acumatica, the Notification screen cannot handle this functionality, so I have this code to extend the POOrder Entry graph, which sends an email to the Customer's contact email from the Requisition when a PO is created (along with the RQRequisitionEntryExt trigger):
public class POOrderEntryExt : PXGraphExtension<POOrderEntry>
{
private bool sendEmailNotification = false;
public bool SendEmailNotification
{
get
{
return sendEmailNotification;
}
set
{
sendEmailNotification = value;
}
}
[PXOverride]
public void Persist(Action del)
{
using (var ts = new PXTransactionScope())
{
if (del != null)
{
del();
}
if (SendEmailNotification)
{
bool sent = false;
string sError = "Failed to send E-mail.";
try
{
Notification rowNotification = PXSelect<Notification,
Where<Notification.name, Equal<Required<Notification.name>>>>
.Select(Base, "PurchaseOrderNotification");
if (rowNotification == null)
throw new PXException("Notification Template was not found.");
var order = Base.Document.Current;
var requisition = (RQRequisition)PXSelect<RQRequisition,
Where<RQRequisition.reqNbr, Equal<Current<POOrder.rQReqNbr>>>>
.SelectSingleBound(Base, new object[] { order });
if (requisition.CustomerID != null)
{
var customer = (BAccountR)PXSelectorAttribute.Select<RQRequisition.customerID>(
Base.Caches[typeof(RQRequisition)], requisition);
if (customer != null)
{
var defCustContact = (Contact)PXSelectorAttribute.Select<BAccountR.defContactID>(
Base.Caches[typeof(BAccountR)], customer);
if (String.IsNullOrEmpty(defCustContact.EMail))
throw new PXException("E-mail is not specified for Customer Contact.");
var sender = TemplateNotificationGenerator.Create(order,
rowNotification.NotificationID.Value);
sender.RefNoteID = order.NoteID;
sender.MailAccountId = rowNotification.NFrom.HasValue ?
rowNotification.NFrom.Value :
PX.Data.EP.MailAccountManager.DefaultMailAccountID;
sender.To = defCustContact.EMail;
sent |= sender.Send().Any();
}
}
}
catch (Exception Err)
{
sent = false;
sError = Err.Message;
}
if (!sent)
throw new PXException(sError);
}
ts.Complete();
}
}
}
And this to modify RQRequisitionEntry:
public class RQRequisitionEntryExt : PXGraphExtension<RQRequisitionEntry>
{
public PXAction<RQRequisition> createPOOrder;
[PXButton(ImageKey = PX.Web.UI.Sprite.Main.DataEntry)]
[PXUIField(DisplayName = Messages.CreateOrders)]
public IEnumerable CreatePOOrder(PXAdapter adapter)
{
PXGraph.InstanceCreated.AddHandler<POOrderEntry>((graph) =>
{
graph.GetExtension<POOrderEntryExt>().SendEmailNotification = true;
});
return Base.createPOOrder.Press(adapter);
}
}
In order to send an email to the Requester's (Employee) contact email from the Request, I modified the POOrderEntryExt to pull the information from the Request object and the Employee's Contact email (I left the RQRequisitionEntryExt the same and in place):
public class POOrderEntryExt : PXGraphExtension<POOrderEntry>
{
private bool sendEmailNotification = false;
public bool SendEmailNotification
{
get
{
return sendEmailNotification;
}
set
{
sendEmailNotification = value;
}
}
[PXOverride]
public void Persist(Action del)
{
using (var ts = new PXTransactionScope())
{
if (del != null)
{
del();
}
if (SendEmailNotification)
{
bool sent = false;
string sError = "Failed to send E-mail.";
try
{
Notification rowNotification = PXSelect<Notification,
Where<Notification.name, Equal<Required<Notification.name>>>>
.Select(Base, "PurchaseOrderNotification");
if (rowNotification == null)
throw new PXException("Notification Template was not found.");
var order = Base.Document.Current;
var requisition = (RQRequisition)PXSelect<RQRequisition,
Where<RQRequisition.reqNbr, Equal<Current<POOrder.rQReqNbr>>>>
.SelectSingleBound(Base, new object[] { order });
var request = (RQRequest)PXSelectJoin<RQRequest,
InnerJoin<RQRequisitionContent,
On<RQRequisitionContent.orderNbr, Equal<RQRequest.orderNbr>>>,
Where<RQRequisitionContent.reqNbr, Equal<POOrder.rQReqNbr>>>
.SelectSingleBound(Base, new object[] { order });
if (request.EmployeeID != null)
{
var employee = (BAccountR)PXSelectorAttribute.Select<RQRequest.employeeID>(
Base.Caches[typeof(RQRequest)], request);
if (employee != null)
{
var defEmpContact = (Contact)PXSelectorAttribute.Select<BAccountR.defContactID>(
Base.Caches[typeof(BAccountR)], employee);
if (String.IsNullOrEmpty(defEmpContact.EMail))
throw new PXException("E-mail is not specified for Employee Contact.");
var sender = TemplateNotificationGenerator.Create(order,
rowNotification.NotificationID.Value);
sender.RefNoteID = order.NoteID;
sender.MailAccountId = rowNotification.NFrom.HasValue ?
rowNotification.NFrom.Value :
PX.Data.EP.MailAccountManager.DefaultMailAccountID;
sender.To = defEmpContact.EMail;
sent |= sender.Send().Any();
}
else
throw new PXException("Customer not found.");
}
else
throw new PXException("Request not found.");
}
catch (Exception Err)
{
sent = false;
sError = Err.Message;
}
if (!sent)
throw new PXException(sError);
}
ts.Complete();
}
}
}
I can get the original code to send an email in my development environment, but my modified code only returns the outer "Failed to send E-mail" error.
Can anyone help point me in the right direction to get my modifications to work?
Because in Acumatica there is one-to-many relationship between RQRequisition and RQRequest, I believe the better approach is to loop through all requests linked to the current requisition and compose a list of requester's emails. After that we can go ahead and send emails to all requesters as part of the Create Orders operation:
public class POOrderEntryExt : PXGraphExtension<POOrderEntry>
{
private bool sendEmailNotification = false;
public bool SendEmailNotification
{
get
{
return sendEmailNotification;
}
set
{
sendEmailNotification = value;
}
}
[PXOverride]
public void Persist(Action del)
{
using (var ts = new PXTransactionScope())
{
if (del != null)
{
del();
}
if (SendEmailNotification)
{
bool sent = false;
string sError = "Failed to send E-mail.";
try
{
Notification rowNotification = PXSelect<Notification,
Where<Notification.name, Equal<Required<Notification.name>>>>
.Select(Base, "PONotification");
if (rowNotification == null)
throw new PXException("Notification Template was not found.");
var order = Base.Document.Current;
var emails = new List<string>();
var requests = PXSelectJoinGroupBy<RQRequest,
InnerJoin<RQRequisitionContent,
On<RQRequest.orderNbr,
Equal<RQRequisitionContent.orderNbr>>>,
Where<RQRequisitionContent.reqNbr,
Equal<Required<RQRequisition.reqNbr>>>,
Aggregate<GroupBy<RQRequest.orderNbr>>>
.Select(Base, order.RQReqNbr);
foreach (RQRequest request in requests)
{
if (request.EmployeeID != null)
{
var requestCache = Base.Caches[typeof(RQRequest)];
requestCache.Current = request;
var emplOrCust = (BAccountR)PXSelectorAttribute
.Select<RQRequest.employeeID>(requestCache, request);
if (emplOrCust != null)
{
var defEmpContact = (Contact)PXSelectorAttribute
.Select<BAccountR.defContactID>(
Base.Caches[typeof(BAccountR)], emplOrCust);
if (!String.IsNullOrEmpty(defEmpContact.EMail) &&
!emails.Contains(defEmpContact.EMail))
{
emails.Add(defEmpContact.EMail);
}
}
}
}
foreach (string email in emails)
{
var sender = TemplateNotificationGenerator.Create(order,
rowNotification.NotificationID.Value);
sender.RefNoteID = order.NoteID;
sender.MailAccountId = rowNotification.NFrom.HasValue ?
rowNotification.NFrom.Value :
PX.Data.EP.MailAccountManager.DefaultMailAccountID;
sender.To = email;
sent |= sender.Send().Any();
}
}
catch (Exception Err)
{
sent = false;
sError = Err.Message;
}
if (!sent)
throw new PXException(sError);
}
ts.Complete();
}
}
}

What's the event for changing Severity on screen Case (ScreenID: CR306000)?

After I customized the code below and I want to update SLA by AssignDateTime. But with the Severity changed then my SLA has changed to get datetime from createdDateTime also. I think it should have other event that need to customize.
protected virtual void CRCase_RowUpdated(PXCache cache, PXRowUpdatedEventArgs e, PXRowUpdated InvokeBaseHandler)
{
if (InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = e.Row as CRCase;
var oldRow = e.OldRow as CRCase;
CRCaseExt rowExt = PXCache<CRCase>.GetExtension<CRCaseExt>(row);
if (row == null || oldRow == null) return;
if (row.OwnerID == null)
{
row.AssignDate = null;
row.SLAETA = null;
}
else if (oldRow.OwnerID == null)
{
row.AssignDate = PXTimeZoneInfo.Now;
if (row == null || row.AssignDate == null) return;
if (row.ClassID != null && row.Severity != null)
{
var severity = (CRClassSeverityTime)PXSelect<CRClassSeverityTime,
Where<CRClassSeverityTime.caseClassID, Equal<Required<CRClassSeverityTime.caseClassID>>,
And<CRClassSeverityTime.severity, Equal<Required<CRClassSeverityTime.severity>>>>>
.Select(Base, row.ClassID, row.Severity);
if (severity != null && severity.TimeReaction != null)
{
row.SLAETA = ((DateTime)row.AssignDate).AddMinutes((int)severity.TimeReaction);
}
}
if (row.Severity != null && row.ContractID != null)
{
var template = (Contract)PXSelect<Contract, Where<Contract.contractID, Equal<Required<CRCase.contractID>>>>.Select(Base, row.ContractID);
if (template == null) return;
var sla = (ContractSLAMapping)PXSelect<ContractSLAMapping,
Where<ContractSLAMapping.severity, Equal<Required<CRCase.severity>>,
And<ContractSLAMapping.contractID, Equal<Required<CRCase.contractID>>>>>
.Select(Base, row.Severity, template.TemplateID);
if (sla != null && sla.Period != null)
{
row.SLAETA = ((DateTime)row.AssignDate).AddMinutes((int)sla.Period);
}
}
}
}
SLAETA field is decorated with PXFormulaAttribute to raise FieldDefaulting event every time change is made to one of the following fields:
CRCase.contractID
CRCase.severity
CRCase.caseClassID
public partial class CRCase : IBqlTable, IAssign, IAttributeSupport, IPXSelectable
{
...
#region SLAETA
public abstract class sLAETA : IBqlField { }
[PXDBDate(PreserveTime = true, DisplayMask = "g")]
[PXUIField(DisplayName = "SLA")]
[PXFormula(typeof(Default<CRCase.contractID, CRCase.severity, CRCase.caseClassID>))]
public virtual DateTime? SLAETA { get; set; }
#endregion
...
}
It’s a way better to only customize CRCase_SLAETA_FieldDefaulting handler in the CRCaseMaint BLC extension instead of implementing CRCase_RowUpdated:
public class CRCaseMaint : PXGraph<CRCaseMaint, CRCase>
{
...
protected virtual void CRCase_SLAETA_FieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e)
{
CRCase row = e.Row as CRCase;
if (row == null || row.CreatedDateTime == null) return;
if (row.ClassID != null && row.Severity != null)
{
var severity = (CRClassSeverityTime)PXSelect<CRClassSeverityTime,
Where<CRClassSeverityTime.caseClassID, Equal<Required<CRClassSeverityTime.caseClassID>>,
And<CRClassSeverityTime.severity, Equal<Required<CRClassSeverityTime.severity>>>>>.
Select(this, row.ClassID, row.Severity);
if (severity != null && severity.TimeReaction != null)
{
e.NewValue = ((DateTime)row.CreatedDateTime).AddMinutes((int)severity.TimeReaction);
e.Cancel = true;
}
}
if (row.Severity != null && row.ContractID != null)
{
var template = (Contract)PXSelect<Contract, Where<Contract.contractID, Equal<Required<CRCase.contractID>>>>.Select(this, row.ContractID);
if (template == null) return;
var sla = (ContractSLAMapping)PXSelect<ContractSLAMapping,
Where<ContractSLAMapping.severity, Equal<Required<CRCase.severity>>,
And<ContractSLAMapping.contractID, Equal<Required<CRCase.contractID>>>>>.
Select(this, row.Severity, template.TemplateID);
if (sla != null && sla.Period != null)
{
e.NewValue = ((DateTime)row.CreatedDateTime).AddMinutes((int)sla.Period);
e.Cancel = true;
}
}
}
...
}
You can either use the FieldUpdated Event or in the row updated event you can look for the change of your field.
Eg: row.Severity != oldRow.Severity

Can we save jaxb context bindings to oxm metadata file?

We have the opportunity to init jaxb context from external oxm file
Map<String, Object> props = new HashMap<String, Object>(1);
props.put(JAXBContextProperties.OXM_METADATA_SOURCE, "oxm.xml");
JAXBContext jc = JAXBContextFactory.createContext(new Class[0], props, <ClassLoader>);
Can we safe current jaxb context xml bindings to xml? From context inited from jaxb annotated classes (I have jaxb.properties)
JAXBContext jc = JAXBContext.newInstance(new Class[]{...});
Like I save generated schema to file
jc.generateSchema(new SchemaOutputResolver(){...});
I need to write oxm file of my schema with only difference in date/time representation.
I create function to create oxm from package. It is not complete, but do everything that I needed, and good as starting point...
public static void createOXM(Package pack, String filename)
throws Exception
{
/*
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="eclipselink_oxm_2_4.xsd"
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" package-name="<name>"
xml-mapping-metadata-complete="true" xml-accessor-type="NONE" xml-accessor-order="UNDEFINED">
*/
org.eclipse.persistence.jaxb.xmlmodel.XmlBindings bindings =
new org.eclipse.persistence.jaxb.xmlmodel.XmlBindings();
bindings.setPackageName(pack.getName());
bindings.setXmlMappingMetadataComplete(true);
bindings.setXmlAccessorType(org.eclipse.persistence.jaxb.xmlmodel.XmlAccessType.NONE);
bindings.setXmlAccessorOrder(org.eclipse.persistence.jaxb.xmlmodel.XmlAccessOrder.UNDEFINED);
List<Class<?>> classes = getClassesForPackage(pack);
for (Class<?> cls: classes)
{
int clsMod = cls.getModifiers();
String clsName = cls.getName().replaceAll("[^\\.]+\\.", "");
if (cls.isAnonymousClass() || !Modifier.isPublic(clsMod)) // Class$1 etc...
continue;
if (cls.getAnnotation(XmlTransient.class) != null)
continue;
org.eclipse.persistence.jaxb.xmlmodel.XmlBindings.JavaTypes javaTypes = bindings.getJavaTypes();
if (javaTypes == null)
{
javaTypes = new org.eclipse.persistence.jaxb.xmlmodel.XmlBindings.JavaTypes();
bindings.setJavaTypes(javaTypes);
}
org.eclipse.persistence.jaxb.xmlmodel.JavaType javaType = new org.eclipse.persistence.jaxb.xmlmodel.JavaType();
javaTypes.getJavaType().add(javaType);
javaType.setName(clsName);
XmlRootElement xmlRoot = cls.getAnnotation(XmlRootElement.class);
if (xmlRoot != null)
{
org.eclipse.persistence.jaxb.xmlmodel.XmlRootElement root =
new org.eclipse.persistence.jaxb.xmlmodel.XmlRootElement();
javaType.setXmlRootElement(root);
if (!"##default".equals(xmlRoot.name()))
root.setName(xmlRoot.name());
if (!"##default".equals(xmlRoot.namespace()))
root.setNamespace(xmlRoot.namespace());
}
XmlType xmlType = cls.getAnnotation(XmlType.class);
if (xmlType != null)
{
org.eclipse.persistence.jaxb.xmlmodel.XmlType type = new org.eclipse.persistence.jaxb.xmlmodel.XmlType();
javaType.setXmlType(type);
if (!"##default".equals(xmlType.name()))
type.setName(xmlType.name());
if (!"##default".equals(xmlType.namespace()))
type.setNamespace(xmlType.namespace());
String[] props = xmlType.propOrder();
if (props != null && props.length > 0)
{
for (String prop: props)
if (!prop.trim().isEmpty())
type.getPropOrder().add(prop.trim());
}
}
XmlAccessorType xmlAccType = cls.getAnnotation(XmlAccessorType.class);
if (xmlAccType != null)
{
String accType = xmlAccType.value().name();
javaType.setXmlAccessorType(org.eclipse.persistence.jaxb.xmlmodel.XmlAccessType.valueOf(accType));
}
if (cls.isEnum())
{
org.eclipse.persistence.jaxb.xmlmodel.XmlBindings.XmlEnums enums = bindings.getXmlEnums();
if (enums == null)
{
enums = new org.eclipse.persistence.jaxb.xmlmodel.XmlBindings.XmlEnums();
bindings.setXmlEnums(enums);
}
org.eclipse.persistence.jaxb.xmlmodel.XmlEnum en = new org.eclipse.persistence.jaxb.xmlmodel.XmlEnum();
en.setJavaEnum(clsName);
enums.getXmlEnum().add(en);
XmlEnum xmlEnum = cls.getAnnotation(XmlEnum.class);
if (xmlEnum != null)
{
Class<?> xmlClass = xmlEnum.value();
if (xmlClass != String.class)
en.setValue(xmlClass.getName());
}
for (Field field: cls.getDeclaredFields())
if (field.isEnumConstant())
{
org.eclipse.persistence.jaxb.xmlmodel.XmlEnumValue value =
new org.eclipse.persistence.jaxb.xmlmodel.XmlEnumValue();
en.getXmlEnumValue().add(value);
value.setJavaEnumValue(field.getName());
value.setValue(field.getName());
XmlEnumValue enumValue = field.getAnnotation(XmlEnumValue.class);
if (enumValue != null)
value.setValue(enumValue.value());
}
continue;
}
Class clsSuper = cls.getSuperclass();
if (clsSuper.getPackage() == pack)
javaType.setSuperType(clsSuper.getName());
for (Field field: cls.getDeclaredFields())
{
org.eclipse.persistence.jaxb.xmlmodel.JavaType.JavaAttributes javaAttrs = javaType.getJavaAttributes();
if (javaAttrs == null)
{
javaAttrs = new org.eclipse.persistence.jaxb.xmlmodel.JavaType.JavaAttributes();
javaType.setJavaAttributes(javaAttrs);
}
XmlTransient xmlTrans = field.getAnnotation(XmlTransient.class);
if (xmlTrans != null)
{
org.eclipse.persistence.jaxb.xmlmodel.XmlTransient attr =
new org.eclipse.persistence.jaxb.xmlmodel.XmlTransient();
attr.setJavaAttribute(field.getName());
javaAttrs.getJavaAttribute().add(new JAXBElement(new QName("", "xml-transient"), attr.getClass(), attr));
continue;
}
XmlValue xmlValue = field.getAnnotation(XmlValue.class);
if (xmlValue != null)
{
org.eclipse.persistence.jaxb.xmlmodel.XmlValue attr = new org.eclipse.persistence.jaxb.xmlmodel.XmlValue();
attr.setJavaAttribute(field.getName());
javaAttrs.getJavaAttribute().add(new JAXBElement(new QName("", "xml-value"), attr.getClass(), attr));
continue;
}
XmlAttribute xmlAttr = field.getAnnotation(XmlAttribute.class);
if (xmlAttr != null)
{
org.eclipse.persistence.jaxb.xmlmodel.XmlAttribute attr =
new org.eclipse.persistence.jaxb.xmlmodel.XmlAttribute();
attr.setJavaAttribute(field.getName());
if (!"##default".equals(xmlAttr.name()) && !field.getName().equals(xmlAttr.name()))
attr.setName(xmlAttr.name());
if (!"##default".equals(xmlAttr.namespace()))
attr.setNamespace(xmlAttr.namespace());
if (xmlAttr.required())
attr.setRequired(true);
XmlSchemaType xmlSchemaType = field.getAnnotation(XmlSchemaType.class);
if (xmlSchemaType != null)
{
org.eclipse.persistence.jaxb.xmlmodel.XmlSchemaType schemaType =
new org.eclipse.persistence.jaxb.xmlmodel.XmlSchemaType();
attr.setXmlSchemaType(schemaType);
schemaType.setName(xmlSchemaType.name());
if (!"http://www.w3.org/2001/XMLSchema".equals(xmlSchemaType.namespace()))
schemaType.setName(xmlSchemaType.namespace());
if (xmlSchemaType.type() != XmlSchemaType.DEFAULT.class)
schemaType.setType(xmlSchemaType.type().getName());
}
XmlJavaTypeAdapter xmlJavaTypeAdapter = field.getAnnotation(XmlJavaTypeAdapter.class);
if (xmlJavaTypeAdapter != null)
{
org.eclipse.persistence.jaxb.xmlmodel.XmlJavaTypeAdapter javaTypeAdapter =
new org.eclipse.persistence.jaxb.xmlmodel.XmlJavaTypeAdapter();
attr.setXmlJavaTypeAdapter(javaTypeAdapter);
if (xmlJavaTypeAdapter.value() != null)
javaTypeAdapter.setValue(xmlJavaTypeAdapter.value().getName());
if (xmlJavaTypeAdapter.type() != XmlJavaTypeAdapter.DEFAULT.class)
javaTypeAdapter.setValue(xmlJavaTypeAdapter.type().getName());
}
javaAttrs.getJavaAttribute().add(new JAXBElement(new QName("", "xml-attribute"), attr.getClass(), attr));
continue;
}
XmlElement xmlElem = field.getAnnotation(XmlElement.class);
if (xmlElem != null && xmlAttr != null)
throw new RuntimeException("XmlAttribute and XmlElement can be both");
org.eclipse.persistence.jaxb.xmlmodel.XmlElement attr = new org.eclipse.persistence.jaxb.xmlmodel.XmlElement();
attr.setJavaAttribute(field.getName());
if (xmlElem != null)
{
if (!"##default".equals(xmlElem.name()) && !field.getName().equals(xmlElem.name()))
attr.setName(xmlElem.name());
if (!"##default".equals(xmlElem.namespace()))
attr.setNamespace(xmlElem.namespace());
if (!"\u0000".equals(xmlElem.defaultValue()))
attr.setDefaultValue(xmlElem.defaultValue());
if (xmlElem.required())
attr.setRequired(true);
if (xmlElem.nillable())
attr.setNillable(true);
}
XmlElementWrapper xmlWrapper = field.getAnnotation(XmlElementWrapper.class);
if (xmlWrapper != null)
{
org.eclipse.persistence.jaxb.xmlmodel.XmlElementWrapper elemWrapper =
new org.eclipse.persistence.jaxb.xmlmodel.XmlElementWrapper();
attr.setXmlElementWrapper(elemWrapper);
if (!"##default".equals(xmlWrapper.name()))
elemWrapper.setName(xmlWrapper.name());
if (!"##default".equals(xmlWrapper.namespace()))
elemWrapper.setNamespace(xmlWrapper.namespace());
if (xmlWrapper.required())
elemWrapper.setRequired(true);
if (xmlWrapper.nillable())
elemWrapper.setNillable(true);
}
XmlSchemaType xmlSchemaType = field.getAnnotation(XmlSchemaType.class);
if (xmlSchemaType != null)
{
org.eclipse.persistence.jaxb.xmlmodel.XmlSchemaType schemaType =
new org.eclipse.persistence.jaxb.xmlmodel.XmlSchemaType();
attr.setXmlSchemaType(schemaType);
schemaType.setName(xmlSchemaType.name());
if (!"http://www.w3.org/2001/XMLSchema".equals(xmlSchemaType.namespace()))
schemaType.setName(xmlSchemaType.namespace());
if (xmlSchemaType.type() != XmlSchemaType.DEFAULT.class)
schemaType.setType(xmlSchemaType.type().getName());
}
XmlJavaTypeAdapter xmlJavaTypeAdapter = field.getAnnotation(XmlJavaTypeAdapter.class);
if (xmlJavaTypeAdapter != null)
{
org.eclipse.persistence.jaxb.xmlmodel.XmlJavaTypeAdapter javaTypeAdapter =
new org.eclipse.persistence.jaxb.xmlmodel.XmlJavaTypeAdapter();
attr.setXmlJavaTypeAdapter(javaTypeAdapter);
if (xmlJavaTypeAdapter.value() != null)
javaTypeAdapter.setValue(xmlJavaTypeAdapter.value().getName());
if (xmlJavaTypeAdapter.type() != XmlJavaTypeAdapter.DEFAULT.class)
javaTypeAdapter.setValue(xmlJavaTypeAdapter.type().getName());
}
javaAttrs.getJavaAttribute().add(new JAXBElement(new QName("", "xml-element"), attr.getClass(), attr));
}
}
JAXBContext jc = JAXBContext.newInstance(bindings.getClass());
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
m.setProperty(Marshaller.JAXB_FRAGMENT, false);
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.marshal(bindings, new File(filename));
}
Have fun!

Resources