Adding Customer Class to GLTran \ GLTranR to see in screen GL404000 (Account Details) - acumatica

I am currently trying to add the customer account class to the viewing area of the GL404000 screen with the by extending the GLTran DAC.
The AccountID, AccountCD and Account name are already available so I thought it would be an easy task.
In GLTranR, I saw where they were pulling in the account data:
public new abstract class referenceID : PX.Data.BQL.BqlInt.Field<referenceID> { }
[PXDBInt()]
[PXDimensionSelector("BIZACCT", typeof(Search<BAccountR.bAccountID>), typeof(BAccountR.acctCD), DescriptionField = typeof(BAccountR.acctName), DirtyRead = true)]
[PXUIField(DisplayName = CR.Messages.BAccountCD, Enabled = false, Visible = false)]
public override Int32? ReferenceID
{
get
{
return this._ReferenceID;
}
set
{
this._ReferenceID = value;
}
}
The line that I attempted to change to my need was the [PXDimensionSelector()] however I cannot get this to pull in the class data. Even when I dont change the code at all it will not populate the column.
public new abstract class usrBusinessAccountClass : PX.Data.BQL.BqlInt.Field<usrBusinessAccountClass> { }
protected Int32? _UsrBusinessAccountClass;
[PXDBInt()]
[PXDimensionSelector("BIZACCT", typeof(Search<BAccountR.bAccountID>), typeof(BAccountR.acctCD), DescriptionField = typeof(BAccountR.acctClass), DirtyRead = true)]
[PXUIField(DisplayName = "Business Account Class", Enabled = false, Visible = false)]
public virtual Int32? UsrBusinessAccountClass
{
get {return _UsrBusinessAccountClass;}
set{ _UsrBusinessAccountClass = value;} // set does work but value does not???
}
just for a test I changed the setter to:
set { _UsrBusinessAccountClass = 1234; }
And that populated the column with the value 1234, so that is why I think my issue is just with selecting the class.
I would show this but I need 10 rep to post images.

You are on the proper track. The following code shows how to retrieve additional data to display within the screen.
Non-Database backed field on the DAC you wish to display data on :
public sealed class GLTranRExtension : PXCacheExtension<GLTranR>
{
public abstract class usrClassID : BqlString.Field<usrClassID>
{
}
[PXString(10, IsUnicode = true, InputMask = ">aaaaaaaaaa")]
[PXSelector(typeof(CRCustomerClass.cRCustomerClassID), DescriptionField = typeof(CRCustomerClass.description))]
[PXUIField(DisplayName = "Business Account Class")]
public string UsrClassID { get; set; }
}
Graph extension in which the extension field is then populated with data :
public class AccountByPeriodEnqExtension : PXGraphExtension<AccountByPeriodEnq>
{
protected virtual void __(Events.RowSelecting<GLTranR> e)
{
if(e.Row is GLTranR row)
{
GLTranRExtension rowExt = row.GetExtension<GLTranRExtension>();
using(new PXConnectionScope())
{
BAccount bAccount = PXSelectReadonly<BAccount, Where<BAccount.bAccountID, Equal<Required<BAccount.bAccountID>>>>.Select(this.Base, row.ReferenceID);
if(bAccount != null)
{
rowExt.UsrClassID = bAccount.ClassID;
}
}
}
}
}
You will also need to add within the GL404000 the UI elements for your new extension fields. The result will look as follows :

Related

Dynamic Input mask using the county DAC on the customer screen

Good day
I am trying to set my Input mask at runtime according to the country that is selected.
I created the following DAC to save the Input mask:
namespace PX.Objects.CS
{
public class CountryExt : PXCacheExtension<PX.Objects.CS.Country>
{
#region UsrPhoneMask
[PXDBString(50)]
[PXUIField(DisplayName="Phone Mask")]
public virtual string UsrPhoneMask { get; set; }
public abstract class usrPhoneMask : PX.Data.BQL.BqlString.Field<usrPhoneMask> { }
#endregion
}
}
The part I am struggling with is when I Override the attribute on-screen level, this is the Phone1 field on the Customer screen:
namespace PX.Objects.AR
{
public class CustomerMaint_Extension : PXGraphExtension<CustomerMaint>
{
#region Event Handlers
[PXDBString(50, IsUnicode = true,
InputMask = Search<CountryExt.usrPhoneMask, Where<Country.countryID,Equal<Current<Country.countryID>>>>]
[PXUIField(DisplayName = "Phone 1", Visibility = PXUIVisibility.SelectorVisible)]
// [PhoneValidation()]
[PXMassMergableField]
[PXPersonalDataField]
protected virtual void Contact_Phone1_CacheAttached(PXCache cache){ }
}
}
I know it is the search I am implementing wrong just don't know how to fix it.
I have also tried setting it using a const
public const string Masknum = PXSelect<CountryExt.usrPhoneMask, Where<Country.countryID, Equal<Current<Country.countryID>>>>;
public class masknum : PX.Data.BQL.BqlString.Constant<masknum>
{
public masknum() : base(Masknum) {; }
}
PXSelect<CountryExt.usrPhoneMask, Where<Country.countryID,Equal<Current<Country.countryID>>>>;
[PXDBString(50, IsUnicode = true, InputMask = Masknum)]
[PXUIField(DisplayName = "Phone 1", Visibility = PXUIVisibility.SelectorVisible)]
// If you do not remove below attribute the mask will not be set
// [PhoneValidation()]
// [PXMassMergableField]
[PXPersonalDataField]
protected virtual void Contact_Phone1_CacheAttached(PXCache cache)
{
}
Property values declared in DAC attribute are not dynamic because they are required to be constant values by .NET Framework compilers. To change the mask at runtime you should investigate the ReturnState property of CacheAttached and FieldSelecting events.
From the Source Code page (SM204570) you can search for the OrderSiteSelectorAttribute which implements a dynamic mask using these methods.
Other examples shipped in the product source code might be more useful for your use case so you can also do a broad search for ReturnState. Here's the relevant code shown in the screenshot above:
public override void CacheAttached(PXCache sender)
{
base.CacheAttached(sender);
PXDimensionAttribute attr = new PXDimensionAttribute(SiteAttribute.DimensionName);
attr.CacheAttached(sender);
attr.FieldName = _FieldName;
PXFieldSelectingEventArgs e = new PXFieldSelectingEventArgs(null, null, true, false);
attr.FieldSelecting(sender, e);
_InputMask = ((PXSegmentedState)e.ReturnState).InputMask;
}
public override void SubstituteKeyFieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
{
base.SubstituteKeyFieldSelecting(sender, e);
if (_AttributeLevel == PXAttributeLevel.Item || e.IsAltered)
{
e.ReturnState = PXStringState.CreateInstance(e.ReturnState, null, null, null, null, null, _InputMask, null, null, null, null);
}
}
EDIT:
From the matching DAC RowSelected event, try calling the SetInputMask method.
If that is producing the expected results it is the most straightforward way to achieve dynamic input mask:
PXStringAttribute.SetInputMask<DAC.field>(sender, inputMaskValue);

Need help in selector control

I created a selector control which displays a list of all serial #s from INItemLotSerial table, it works fine, the problem is the description field is showing InventoryID, how to show InventoryCD. Please have a look at below sample code.
[PXSelector(typeof(Search<INItemLotSerial.lotSerialNbr>),
new Type[] { typeof(INItemLotSerial.lotSerialNbr), typeof(INItemLotSerial.inventoryID) },
SubstituteKey = typeof(INItemLotSerial.lotSerialNbr), DescriptionField = typeof(INItemLotSerial.inventoryID))]
// i also joined with InventoryItem but this doesn't works.
[PXSelector(typeof(Search2<INItemLotSerial.lotSerialNbr,
LeftJoinSingleTable<InventoryItem, On<InventoryItem.inventoryID,Equal<INItemLotSerial.inventoryID>>>>),
new Type[] { typeof(INItemLotSerial.lotSerialNbr), typeof(INItemLotSerial.inventoryID) },
SubstituteKey = typeof(INItemLotSerial.lotSerialNbr), DescriptionField = typeof(InventoryItem.inventoryCD))]
The main problem with DescriptionField property is that it is waiting for getting the field from the same table for which Selector is written. But in the case of ID/CD usually, the CD is not present in the table where ID is present, except the main table.
UPDATED I have removed previous code (implementation using custom attributes and FieldSelecting event handler) because of the performance issues which it is bringing with it. The code below is resulting with the same lookup but getting the data with one inner join instead of all the requests which the previous code was doing.
You can do the following to get this lookup with description:
Create a PXProjection on INItemLotSerial and InventoryItem tables like below:
[PXCacheName("Lot Serials with Inventory CD")]
[PXProjection(typeof(Select2<INItemLotSerial,
InnerJoin<InventoryItem,
On<INItemLotSerial.inventoryID, Equal<InventoryItem.inventoryID>>>>))]
public class INItemLotSerialWithInventoryItem : IBqlTable
{
[PXDBInt(BqlField = typeof(INItemLotSerial.inventoryID))]
[PXUIField(DisplayName = "Inventory ID", Visibility = PXUIVisibility.Visible, Visible = false)]
public virtual int? InventoryID { get; set; }
public abstract class inventoryID : IBqlField { }
[PXDBString(InputMask = "", IsUnicode = true, BqlField = typeof(InventoryItem.inventoryCD))]
[PXUIField(DisplayName = "Inventory ID")]
public virtual string InventoryCD { get; set; }
public abstract class inventoryCD : IBqlField { }
[PXDBString(InputMask = "", IsUnicode = true, BqlField = typeof(INItemLotSerial.lotSerialNbr))]
[PXUIField(DisplayName = "Lot/Serial Nbr")]
public virtual string LotSerialNbr { get; set; }
public abstract class lotSerialNbr : IBqlField { }
}
Set your selector to use this PXProjection like below:
[PXSelector(typeof(Search<INItemLotSerialWithInventoryItem.lotSerialNbr>),
new Type[] { typeof(INItemLotSerialWithInventoryItem.lotSerialNbr) },
SubstituteKey = typeof(INItemLotSerialWithInventoryItem.lotSerialNbr),
DescriptionField = typeof(INItemLotSerialWithInventoryItem.inventoryCD))]
As a result, you will get lookup like below:

Change label name of default fields

I want to show which user created Invoice, for this i have added default Acumatica field, but the label is showing as Created By, how can i change that label name to "Invoice Created By". Please have a look at below screenshot for field am referring to.
You could use PXUIFieldAttribute.SetDisplayName static method to change DAC field’s display name. This change will be applicable only for Sales Invoice Entry Graph (SO303000 screen)
public class SOInvoiceEntryPXDemoExt : PXGraphExtension<SOInvoiceEntry>
{
public override void Initialize()
{
PXUIFieldAttribute.SetDisplayName<ARInvoice.createdByID>(Base.Document.Cache, "Invoice Created By");
}
}
If you need display name changed for this field in all screens, you need to have DAC Extension as below:
With this, attributes specified in an extension DAC apply to DAC class in every Graph of the application unless a Graph replaces them with other attributes.
public class ARInvoicePXDemoExt : PXCacheExtension<ARInvoice>
{
[PXMergeAttributes(Method = MergeMethod.Append)]
[PXUIField(DisplayName = "Invoice Created By", Enabled = false, IsReadOnly = true)]
public virtual Guid? CreatedByID { get; set; }
}
You need to add CreatedByID field on screen SO303000
And set DisplayMode property to Text.
The 'CreatedByID' I believe is an audit field and therefore cannot easily change the ui of the additional data fields available through the control. The resolution I would suggest is a non database backed UI field that is populated during row selecting. Example can be found below :
public class SOOrderEntryExtension : PXGraphExtension<SOOrderEntry>
{
public virtual void SOOrder_RowSelecting(PXCache sender, PXRowSelectingEventArgs e)
{
SOOrder row = e.Row as SOOrder;
if(row != null)
{
SOOrderExtension rowExt = PXCache<SOOrder>.GetExtension<SOOrderExtension>(row);
Users user = PXSelectReadonly<Users, Where<Users.pKID, Equal<Required<Users.pKID>>>>.Select(this.Base, row.CreatedByID);
if(user != null)
{
rowExt.InvoiceCreatedBy = user.DisplayName;
}
}
}
}
public class SOOrderExtension : PXCacheExtension<SOOrder>
{
public abstract class invoiceCreatedBy : PX.Data.IBqlField
{
}
[PXString]
[PXUIField(DisplayName = "Invoice Created By")]
public virtual string InvoiceCreatedBy { get; set; }
}

An unhandled exception has occurred in the function 'MoveNext'. Please see the trace log for more details

I have a customization Project that requires me to only show data (like Opportunities) that have been setup in added customization tab in Business Account. I've manage to comply these using data view delegate and a custom selector, The error occurs when:
1.) clicking an opportunity ID where the customization restricts such data in the generic inquiry.
2.) clicking next or previous button in the opportunities module
Here is the Code
protected virtual IEnumerable opportunity()
{
var output = new PXSelect<CROpportunity>(Base);
foreach (CROpportunity _output in output.Select())
{
bool returnOutput = false;
var memberships = PXSelectJoinGroupBy<EPEmployee, RightJoin<EPCompanyTreeMember, On<EPCompanyTreeMember.userID, Equal<EPEmployee.userID>>>, Where<EPEmployee.userID, Equal<Current<AccessInfo.userID>>>, Aggregate<GroupBy<EPCompanyTreeMember.workGroupID>>>.Select(Base);
foreach (PXResult<EPEmployee, EPCompanyTreeMember> member in memberships)
{
EPCompanyTreeMember _member = (EPCompanyTreeMember)member;
BAccountRestriction2 visible = PXSelect<BAccountRestriction2, Where<BAccountRestriction2.account, Equal<Required<BAccount.bAccountID>>, And<BAccountRestriction2.child, Equal<Required<BAccountRestriction2.child>>>>>.Select(Base, ((CROpportunity)_output).BAccountID, _member.WorkGroupID);
if (visible != null || ((CROpportunity)_output).OpportunityID == " <NEW>")
{
returnOutput = true;
break;
}
}
if (returnOutput)
{
yield return _output;
}
}
}
Here is the Custom Selector
public class OpportunityMaintExtension : PXGraphExtension<OpportunityMaint>
{
#region custom selector
public class PXCustomSelectorOpportunityAttribute : PXCustomSelectorAttribute
{
public PXCustomSelectorOpportunityAttribute()
: base(typeof(CROpportunity.opportunityID)
, new[] { typeof(CROpportunity.opportunityID),
typeof(CROpportunity.opportunityName),
typeof(CROpportunity.status),
typeof(CROpportunity.curyAmount),
typeof(CROpportunity.curyID),
typeof(CROpportunity.closeDate),
typeof(CROpportunity.stageID),
typeof(CROpportunity.cROpportunityClassID),
typeof(BAccount.acctName),
typeof(Contact.displayName) }
)
{
//this.DescriptionField = typeof(CQHRISLeave.refNbr);
}
protected virtual IEnumerable GetRecords()
{
foreach (var pc in this._Graph.GetExtension<OpportunityMaintExtension>().opportunity())
{
if (((CROpportunity)pc).OpportunityID != " <NEW>")
yield return pc as CROpportunity;
}
}
}
#endregion
And then the implementation of the custom selector attribute is here:
public class CROpportunityExtension : PXCacheExtension<CROpportunity>
{
#region OpportunityID
public abstract class opportunityID : PX.Data.IBqlField { }
public const int OpportunityIDLength = 10;
[PXDBString(OpportunityIDLength, IsUnicode = true, IsKey = true, InputMask = ">CCCCCCCCCCCCCCC")]
[PXUIField(DisplayName = "Opportunity ID", Visibility = PXUIVisibility.SelectorVisible)]
[AutoNumber(typeof(CRSetup.opportunityNumberingID), typeof(AccessInfo.businessDate))]
[NORDE.OpportunityMaintExtension.PXCustomSelectorOpportunity]
[PXFieldDescription]
public virtual String OpportunityID { get; set; }
#endregion
}
I've managed to "resolve" this issue by disabling the generic inquiry on this page. Initially, Opportunities page is a generic inquiry, for some reason, it may have affected the cache or the data view delegate and throws an error.
I simply: Edit Generic inquiry -> Entry Point Tab -> Uncheck "Replace Entry Screen with this Inuiry in Menu"

Populating unbound column with fix value reference to other table

I have extended a APTran DAC for Bills and Adjustments screen (ID - AP301000).
I am trying to populate a value from different table based on the current line item.
The value I need is from CrossReference based on current Inventory on line item and VendorID of current Bill.
Below is the code. Please let me know if I am missing anything.
public class string_VendorType : Constant<string>
{
public string_VendorType() : base("0VPN")
{ }
}
protected string _UsrVendorPartNum;
[PXString(50)]
[PXUIField(DisplayName = "Vendor Part Number", Enabled = false, IsReadOnly = true)]
[PXDefault(typeof(Search2<INItemXRef.alternateID,
LeftJoin<InventoryItem, On<INItemXRef.inventoryID, Equal<InventoryItem.inventoryID>>,
LeftJoin<APTran, On<InventoryItem.inventoryID, Equal<APTran.inventoryID>>,
LeftJoin<APInvoice, On<APInvoice.refNbr, Equal<APTran.refNbr>,
And<APInvoice.vendorID, Equal<INItemXRef.bAccountID>>>>>>,
Where<InventoryItem.inventoryID, Equal<Current<APTran.inventoryID>>,
And<INItemXRef.alternateType, Equal<string_VendorType>,
And<APInvoice.refNbr, Equal<Current<APTran.refNbr>>>>>>))]
public virtual string UsrVendorPartNum
{
get
{
return _UsrVendorPartNum;
}
set
{
_UsrVendorPartNum = value;
}
}
public abstract class usrVendorPartNum : IBqlField { }
However, the value isn't populating. Please advise.
I got the following working (using PXUnboundDefault). The query you have can be simplified to the following working example:
public class APTranExt : PXCacheExtension<PX.Objects.AP.APTran>
{
protected string _UsrVendorPartNum;
[PXString(50)]
[PXUIField(DisplayName = "Vendor Part Number", Enabled = false, IsReadOnly = true)]
[PXUnboundDefault(typeof(Search<INItemXRef.alternateID,
Where<INItemXRef.inventoryID, Equal<Current<APTran.inventoryID>>,
And<INItemXRef.alternateType, Equal<INAlternateType.vPN>,
And<INItemXRef.bAccountID, Equal<Current<APTran.vendorID>>>>>>))]
public virtual string UsrVendorPartNum
{
get
{
return _UsrVendorPartNum;
}
set
{
_UsrVendorPartNum = value;
}
}
public abstract class usrVendorPartNum : IBqlField { }
}
Note that you do not need to create your own constant. You can reuse INAlternateType and the vPN constant.
Simply for reference...
I would say if this was a DB field you could look into using the AlternativeItemAttribute, however it requires a subitem field which oddly APTran does not have in it.
Example usage of AlternativeItemAttribute on POLine.AlternateID:
public abstract class alternateID : PX.Data.IBqlField
{
}
protected String _AlternateID;
[AlternativeItem(INPrimaryAlternateType.VPN, typeof(POLine.vendorID), typeof(POLine.inventoryID), typeof(POLine.subItemID))]
public virtual String AlternateID
{
get
{
return this._AlternateID;
}
set
{
this._AlternateID = value;
}
}

Resources