Acumatica Weirdness with PXCacheExtension/PXGraphExtension - acumatica

I have a Cache Extension:
namespace PX.Objects.SO
{
public class SOLineExt : PXCacheExtension<SOLine>
{
[PXBool]
[PXUIField(DisplayName="Sales Promotion", Enabled = false, IsReadOnly = true)]
public virtual bool? UsrSalesPromotion { get; set; }
public abstract class usrSalesPromotion : IBqlField { }
}
}
and a Graph Extension:
public class SOOrderEntry_Extension:PXGraphExtension<SOOrderEntry>
{
protected void SOLine_RowSelecting(PXCache cache, PXRowSelectingEventArgs e)
{
var salesOrderEntry = (SOLine)e.Row;
if (salesOrderEntry == null)
return;
SOLineExt soLineExt = PXCache<SOLine>.GetExtension<PX.Objects.SO.SOLineExt>(salesOrderEntry);
PXUIFieldAttribute.SetEnabled<soLineExt.usrSalesPromotion>(cache, salesOrderEntry, false);
}
}
}
The problem that I am having is that SOLineExt is not found which I think is because the cache extention was created through Project Customization which puts the resulting .cs file in the Runtime_App folder (How to reference new field if it is DAC Extension, see last comment).
How do I fix this?

So the answer is that I can't read what I type. Iwas using soLineExt instead of SOLineExt. It would be nice to have a way for VS/Resharper to not complain about not finding SOLineExt, but it is not imperative.

Related

Unbound Field in Standalone Projection

I have a Rich Text field attached to the PMQuote and CRQuote DACs. On the CRQuote, the user field is on the standalone class and the regular class, and that is working as desired. However, Acumatica's reporting does not show the HTML formatting so I am creating an unbound Plain Text field to put on the reports and in GI's. This field works fine on the PMQuote, but not on the CRQuote. On the CRQuote, the field is there, but it is always blank. I'm sure it is something to do with the projection, but I'm not sure how it should be created.
Here is the PMQuote field (working properly)
[PXString(IsUnicode = true)]
[PXUIField(Visible = false, DisplayName = "Scope Text")]
public virtual String UsrScopePlainText
{
get
{
return PX.Data.Search.SearchService.Html2PlainText(UsrScope);
}
}
public abstract class usrScopePlainText : PX.Data.BQL.BqlString.Field<usrScopePlainText> { }
And here is what I have on the CRQuote (not working):
public class CRQuoteExt : PXCacheExtension<CRQuote>
{
#region UsrScope
[PXDBText(IsUnicode = true, BqlField = typeof(CRQuoteStandaloneExt.usrScope))]
[PXUIField(DisplayName = "Scope")]
public virtual string UsrScope { get; set; }
public abstract class usrScope : IBqlField { }
#endregion
#region UsrScopePlainText
[PXString(IsUnicode = true)]
[PXUIField(Visible = false, DisplayName = "Scope Text")]
public virtual String UsrScopePlainText
{
get
{
return PX.Data.Search.SearchService.Html2PlainText(UsrScope);
}
}
public abstract class usrScopePlainText : IBqlField { }
#endregion
}
public class CRQuoteStandaloneExt : PXCacheExtension<PX.Objects.CR.Standalone.CRQuote>
{
#region UsrScope
[PXDBText(IsUnicode = true)]
[PXUIField(DisplayName = "Scope")]
public virtual string UsrScope { get; set; }
public abstract class usrScope : IBqlField { }
#endregion
#region UsrScopePlainText
[PXString(IsUnicode = true)]
[PXUIField(Visible = false, DisplayName = "Scope Text")]
public virtual String UsrScopePlainText
{
get
{
return PX.Data.Search.SearchService.Html2PlainText(UsrScope);
}
}
public abstract class usrScopePlainText : IBqlField { }
#endregion
}
Thanks!
An alternative solution could be to have a reusable attribute that you can attach to any field. I think this is cleaner than adding logic directly in the Data Access class...
using PX.Objects.IN;
using PX.Data;
using System;
namespace PX.Objects.IN
{
public class InventoryItemPlainTextBodyExt : InventoryItem
{
[HtmlToText(typeof(InventoryItem.body))]
[PXDependsOnFields(typeof(InventoryItem.body))]
[PXUIField(DisplayName="Body (Plain Text)")]
public virtual string BodyPlainText { get; set; }
public abstract class bodyPlainText : PX.Data.BQL.BqlString.Field<bodyPlainText> { }
}
[PXString]
public class HtmlToTextAttribute : PXEventSubscriberAttribute, IPXFieldSelectingSubscriber
{
protected Type _htmlField;
public HtmlToTextAttribute(Type htmlField) :base()
{
_htmlField = htmlField;
}
public void FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
{
e.ReturnValue = PX.Data.Search.SearchService.Html2PlainText(sender.GetValue(e.Row, _htmlField.Name) as string);
}
}
}
I'm not sure what happened, but I put the original code I posted above back into the customization project and it started working as expected.

unable to access the UsrsubcontractNbr field in the APTran - Acumatica

how do I Enable usrsubcontractNbr in rowselected Event:
I am unable to access the usrSubcontractNbr from neither of the DAC (ApTran and ApTranExt)
UsrSubcontractNbr field was defined in construction feature package. APTran is converted into dll.
How can access this field?
Seems like a similar issue to this post: How to access Custom field,which is defined in Construction feature package- Acumatica
Searching the PX.Objects.CN.dll from the construction install package you will find:
using PX.Data;
using PX.Objects.AP;
using PX.Objects.CS;
namespace PX.Objects.CN.Subcontracts.AP.CacheExtensions
{
public sealed class ApTranExt : PXCacheExtension<APTran>
{
[PXString(15, IsUnicode = true)]
[PXUIField(DisplayName = "Subcontract Nbr.", Enabled = false, IsReadOnly = true)]
public string UsrSubcontractNbr
{
get
{
if (!(this.get_Base().get_POOrderType() == "RS"))
return (string) null;
return this.get_Base().get_PONbr();
}
}
[PXInt]
[PXUIField(DisplayName = "Subcontract Line", Enabled = false, IsReadOnly = true, Visible = false)]
public int? UsrSubcontractLineNbr
{
get
{
if (!(this.get_Base().get_POOrderType() == "RS"))
return new int?();
return this.get_Base().get_POLineNbr();
}
}
public static bool IsActive()
{
return PXAccess.FeatureInstalled<FeaturesSet.construction>();
}
public ApTranExt()
{
base.\u002Ector();
}
public abstract class usrSubcontractNbr : IBqlField, IBqlOperand
{
}
public abstract class usrSubcontractLineNbr : IBqlField, IBqlOperand
{
}
}
}
To access the field you will need to use PX.Objects.CN.Subcontracts.AP.CacheExtensions.ApTranExt
Edit. based on the comment if having an issue using rowselected be sure to use the signature with the PXRowSelected delegate so you can control when you enabled code to run after the base call. you might have a problem where the base call is running after your code which could disable the field again.
Ex:
protected void APTran_RowSelected(PXCache cache, PXRowSelectedEventArgs e, PXRowSelected del)
{
del?.Invoke(cache, e);
var row = (APTran) e.Row;
if (row == null) return;
PXUIFieldAttribute.SetEnabled<PX.Objects.CN.Subcontracts.AP.CacheExtensions.ApTranExt.usrSubcontractNbr>(
cache, row, true);
}

APRegisterExt not saving data on APInvoiceEntry

Build 18.203.0006
Page: AP301000
Good day, I have extended the APRegister Class by adding 2 new checkbox fields. I want to iterate through al the APTran transactions and look for suIDs that start with FBL and GAS. If I find them the new tick boxes should tick.
Currently, the fields(tick boxes) do not save to the database. I am not sure how to tell Acumatica the APRegisterExt has updated.
APInvoiceEntry_Extension:
namespace PX.Objects.AP
{
public class APRegisterExt : PXCacheExtension<PX.Objects.AP.APRegister>
{
#region UsrGroupAEmail
[PXDBBool]
[PXUIField(DisplayName="GroupA Email")]
public virtual bool? UsrGroupAEmail { get; set; }
public abstract class usrGroupAEmail : IBqlField { }
#endregion
#region UsrGroupBEmail
[PXDBBool]
[PXUIField(DisplayName="GroupB Email")]
public virtual bool? UsrGroupBEmail { get; set; }
public abstract class usrGroupBEmail : IBqlField { }
#endregion
}
}
APInvoiceEntry_Extension:
namespace PX.Objects.AP
{
public class APInvoiceEntry_Extension : PXGraphExtension<APInvoiceEntry>
{
#region Event Handlers
protected void APInvoice_RowSelected(PXCache cache, PXRowSelectedEventArgs e)
{
APInvoice invoice = e.Row as APInvoice;
if (invoice == null) return;
var apRX = invoice.GetExtension<APRegisterExt>();
PXResultset<APTran> Tlist = PXSelectJoin<APTran,
LeftJoin<POReceiptLine,
On<POReceiptLine.receiptNbr, Equal<APTran.receiptNbr>,
And<POReceiptLine.lineNbr, Equal<APTran.receiptLineNbr>>>>,
Where<
APTran.tranType, Equal<Current<APInvoice.docType>>,
And<APTran.refNbr, Equal<Current<APInvoice.refNbr>>>>,
OrderBy<
Asc<APTran.tranType,
Asc<APTran.refNbr,
Asc<APTran.lineNbr>>>>>.Select(Base);
apRX.UsrGroupBEmail = false;
apRX.UsrGroupAEmail = false;
foreach (APTran item in Tlist)
{
if (item.SubID.Value.ToString().StartsWith("FBL") || item.SubID.Value.ToString().StartsWith("GAS"))
{
apRX.UsrGroupBEmail = true;
cache.SetValue<APRegisterExt.usrGroupBEmail>(e.Row, true);
}
else
{
apRX.UsrGroupAEmail = true;
}
}
}
#endregion
}
}
Can someone please show me the correct way of saving the data to the new tick boxes so that cache updates.
Consider moving the logic to APRegister_RowPersisting.
Importantly, APTran.subID is an Int dataType.
Read instead the Sub table/DAC during a For Loop of
Base.Transactions.Select() to detect SubCD startsWith FBL or GAS.
foreach(APTran item in Base.Transaction.Select())
{
Sub sub = PXSelect<Sub,
Where<Sub.subID, Equal<Required<Sub.subID>>>>
.Select(graph, item.SubID);
if (item.SubCD.StartsWith("FBL") ||
item.SubCD.StartsWith("GAS"))
{
apRX.UsrGroupBEmail = true;
}
else
{
apRX.UsrGroupAEmail = true;
}
}
To be complete, you may also need to reset both flags during APTran_SubID_FieldUpdated regardless of the changed value:
protected void APTran_SubID_FieldUpdated(PXCache sender,
PXFieldUpdatedEventArgs e, PXFieldUpdated del)
{
del?.Invoke(sender,e);
var apRX = Base.Document.Current.GetExtension<APRegisterExt>();
apRX.UsrGroupBEmail = false;
apRX.UsrGroupAEmail = false;
}

Acumatica Processing Screen Updating ARTran Custom Field Needs To Also Update Custom Table field

We have a custom processing screen that is updating a custom field called UsrDateNotified in the ARTran table where the UsrDateNotified is prior to the RevisionDateReceived in a custom table ItemBaseDocument. We also need to update the UsrDateNotified field in table ItemBaseDocument which is linked to the InventoryID in ARTran. Our current code below validates for updating the ARTran table, but we are struggling with how to also update the related ItemBaseDocument for the selected ARTran records. What is the right approach for this scenario?
using System;
using System.Collections;
using System.Linq;
using PX.Data;
using PX.SM;
using PX.Objects.AR;
using PX.Objects.CR;
using PX.Objects.IN;
namespace DocCenter
{
public class UpdateLastNotified : PXGraph<UpdateLastNotified>
{
public PXFilter<UpdateLastNotifiedFilter> MasterView;
public PXCancel<UpdateLastNotifiedFilter> Cancel;
[PXFilterable]
public PXSelect<ARTran> DetailsView;
public UpdateLastNotified()
{
Cancel.SetCaption("Clear Filter");
this.DetailsView.Cache.AllowInsert = false;
this.DetailsView.Cache.AllowDelete = false;
this.DetailsView.Cache.AllowUpdate = true;
}
protected virtual IEnumerable detailsView()
{
UpdateLastNotifiedFilter filter = MasterView.Current as UpdateLastNotifiedFilter;
PXSelectBase<ARTran> cmd = new PXSelectJoinOrderBy<ARTran,
InnerJoin<InventoryItem, On<ARTran.inventoryID, Equal <InventoryItem.inventoryID>>,
InnerJoin<ItemBaseDocument, On<InventoryItemExt.usrDocumentNumber, Equal<ItemBaseDocument.baseDocumentCode>>,
InnerJoin<Contact, On<ARTranExt.usrContactID, Equal<Contact.contactID>>>>>,
OrderBy<Asc<ARTran.tranDate>>>(this);
cmd.WhereAnd<Where<ContactExt.usrNotificationPriority,
Equal<Current<UpdateLastNotifiedFilter.notificationPriority>>>>();
cmd.WhereAnd<Where<ARTranExt.usrDateNotified,
Less<ItemBaseDocument.revisionDateReceived>>>();
if (filter.BaseDocumentCode != null)
{
cmd.WhereAnd<Where<InventoryItemExt.usrDocumentNumber,
Equal<Current<UpdateLastNotifiedFilter.baseDocumentCode>>>>();
}
return cmd.Select();
}
public PXAction<UpdateLastNotifiedFilter> Process;
[PXProcessButton]
[PXButton(CommitChanges=true)]
[PXUIField(DisplayName = "Process")]
protected virtual IEnumerable process(PXAdapter adapter)
{
PXLongOperation.StartOperation(this, delegate()
{
foreach(ARTran tran in DetailsView.Select())
{
if (tran.Selected==true)
{
ARTranExt tranExt = tran.GetExtension<ARTranExt>();
ARInvoiceEntry tranEntry = new ARInvoiceEntry();
tranExt.UsrDateNotified = MasterView.Current.DateNotified;
tranEntry.Transactions.Update(tran);
tranEntry.Save.PressButton();
}
}
}
);
return adapter.Get();
}
[Serializable]
public class UpdateLastNotifiedFilter : IBqlTable
{
public static class NotificationPriority
{
public const string None = "N";
public const string Alert = "A";
public const string Express = "E";
public const string Shipment = "P";
public const string Subscription = "S";
}
#region NotificationPriority
public abstract class notificationPriority : PX.Data.IBqlField
{
}
[PXDBString(1, IsFixed = true)]
[PXDefault(NotificationPriority.None)]
[PXUIField(DisplayName = "Notification Type")]
[PXStringList(
new string[]
{
NotificationPriority.None,
NotificationPriority.Alert,
NotificationPriority.Express,
NotificationPriority.Shipment,
NotificationPriority.Subscription
},
new string[]
{
"None",
"Alert",
"Express",
"Shipment",
"Subscription"
})]
#endregion
#region BaseDocumentID
public abstract class baseDocumentCode : PX.Data.IBqlField
{
}
[PXString(50)]
[PXUIField(DisplayName="Document Number")]
[PXSelector(typeof(DocCenter.ItemBaseDocument.baseDocumentCode))]
public virtual String BaseDocumentCode
{
get;
set;
}
#endregion
#region DateNotified
public abstract class dateNotified : PX.Data.IBqlField
{
}
[PXDBDate()]
[PXDefault(typeof(AccessInfo.businessDate))]
[PXUIField(DisplayName = "Date Notified")]
public DateTime? DateNotified { get; set; }
#endregion
}
}
}
Your best bet would be to create a view that selects ItemBaseDocument.
PXSelect<ItemBaseDocument> ViewName;
In your for loop of your action button, you will want to create a new ItemBaseDocument object and set it equal to the corresponding ItemBaseDocument row entry. You can then update the date of this object, and when you execute your Save.PressButton() action, that should save the updates to that entry as well.
foreach(ARTran tran in DetailsView.Select())
{
if (tran.Selected==true)
{
ARTranExt tranExt = tran.GetExtension<ARTranExt>();
ARInvoiceEntry tranEntry = new ARInvoiceEntry();
tranExt.UsrDateNotified = MasterView.Current.DateNotified;
tranEntry.Transactions.Update(tran);
tranEntry.Save.PressButton();
//Target Added Code
ItemBaseDocument doc = PXSelect<ItemBaseDocument, Where<ItemBaseDocument.inventoryID,
Equal<Required<ARTran.inventoryID>>>>.Select(tran.InventoryID);
doc.UsrDateNotified = MasterView.Current.DateNotified;
}
}
Disclaimer: There may be a syntax error in the added code above. If there is, please let me know and I will fix it.

Acumatica Add Filter on Create Requisition Screen

May I ask for your guidance here, my problem is I added a Branch Filter on the Create Requisition(RQ504000) screen, I tried to override the RQRequestProcessing Class but it just don't work. I also tried adding a WhereAnd on the Records view upon field updated but it also does not work. Any suggestion is greatly appreciated thanks.
I propose you following solution:
public class RQRequestSelectionExt : PXCacheExtension<RQRequestSelection>
{
#region AllocatedAmount2
public abstract class usrBranchID : IBqlField
{
}
[PXDBInt]
[PXUIField(DisplayName = "Branch ID")]
[PXDefault(2)]
[Branch]
public int? UsrBranchID { get; set; }
#endregion
}
public class RQRequestProcessExt : PXGraphExtension<RQRequestProcess>
{
[PXFilterable(new System.Type[] { })]
public RQRequestProcess.RQRequestProcessing Records;
public IEnumerable records()
{
var currentFilter = Base.Filter.Current;
var filterExt = currentFilter.GetExtension<RQRequestSelectionExt>();
var newList = Base.Records.Select();
ArrayList result = new ArrayList();
foreach (PXResult<RQRequestLineOwned> listItme in newList)
{
var row = listItme.GetItem<RQRequestLineOwned>();
if (filterExt.UsrBranchID != null)
{
if (row.BranchID == filterExt.UsrBranchID)
{
result.Add(row);
}
}
else
{
result.Add(row);
}
}
return result;
}
}
Another one, you can try to play with WhereAnd, but I for myself wasn't very successful with it.

Resources