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);
}
Related
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 :
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);
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"
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.
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.