I'm trying to create a new Contact field on the Stock Item screen that has a selector pointing to Contacts but it doesn't work:
Here are the attributes of the Contact field:
[PXDBInt]
[PXUIField(DisplayName="Contact",Visibility = PXUIVisibility.SelectorVisible)]
[PXSelector(typeof(
Search<CRContact.contactID>
)
, new Type[] { typeof(CRContact.contactID), typeof(CRContact.firstName), typeof(CRContact.lastName), typeof(CRContact.fullName), typeof(CRContact.email) }
, SubstituteKey = typeof(CRContact.contactID)
, DescriptionField = typeof(CRContact.fullName)
, CacheGlobal = true)]
Am I missing an attribute or something?
You assigned substitute key as CRContact.contactID. That is the reason that you see 0 displayed. Recommend to assign CRContact.fullName as the substitute key, since this DAC has no natural keys....ie ContactCD.
Related
I have to ask this question, because the solution in this case (using ValidateValue = false) didn't work for me:
Acumatica PXselector, how to type new value into selector control
Here's my selector
public abstract class sourceAccount : PX.Data.BQL.BqlString.Field<sourceAccount> { }
[PXDBInt]
[PXUIField(DisplayName = "Source Account")]
[PXSelector(typeof(Search<Account.accountID,
Where<Account.active, Equal<True>>>),
typeof(Account.accountCD),
typeof(Account.description),
SubstituteKey = typeof(Account.accountCD),
DescriptionField = typeof(Account.description),
ValidateValue = false)]
public virtual Int32? SourceAccount { get; set; }
When this is compiled and deployed, I still get this:
Any idea why this wouldn't work for me?
The trace of the error:
First simplify selector logic:
[PXSelector(typeof(Search<Account.accountID>), ValidateValue = false)]
Then lookup all references to sourceAccount / SourceAccount to check for additional validations.
And make sure the value entered matches at DAC and DB side.
You are entering a string value when the DAC field is an integer, that will not work.
I have a customization where I'm adding a Selector to a grid field that has a description field as follows:
[PXSelector(typeof(Search<EPEmployee.bAccountID,
Where<EPEmployee.status, Equal<SetupTypes.active>>>),
typeof(EPEmployee.acctCD),
typeof(EPEmployee.acctName),
SubstituteKey = typeof(EPEmployee.acctCD),
DescriptionField = typeof(EPEmployee.acctName))]
My problem is that the field has a description (in this case, 'Employee Name') field that is automatically added, but I can't find a way to change the display name of that field.
Since I have another field that uses the same employee lookup, they both have the same 'Employee Name' description field - so I have no way of knowing which description goes with which Selector field unless I choose a value and see the description show up in its associated 'Employee Name' field.
Is there a way to change the display name of that description field? I've tried CacheAttached and the RowSelected event with a PXUIFieldAttribute.SetDisplayName, but nothing seems to work.. seems that 'field_description' is an automatically added field that doesn't exist anywhere in the DAC to be able to change the display name.
Per Acumatica support, the only way that this can be done is as follows:
The only way that seems to work is to have two different DACs that are projections on the EPEmployee DAC and use each in the separate selector attribute.
For instance:
[PXProjection(typeof(EPEmployee))]
public class EPEmployeeTest : IBqlTable
{
#region BAccountID
...
#endregion
#region AcctCD
...
#endregion
public abstract class acctName : PX.Data.BQL.BqlString.Field<acctName> { }
[PXDBString(60, IsUnicode = true, BqlField=typeof(EPEmployee.acctName))]
[PXUIField(DisplayName = "Employee Name New Label", Enabled = false, Visibility = PXUIVisibility.SelectorVisible)]
public override string AcctName{get; set;}
}
Good Day!
I have a field in my DAC where I need to change the Selector attribute depending on the setup that I set on my preferences. As you may know, there is an existing LeadSelector Attribute and CustomerSelector Attribute on acumatica. I wish to change the selector attribute of that given field if I set Customer on my preferences, and vice versa.
Is there any available resources right here now?
I've been thinking of creating an Extended Selector attribute on which I will check what is the preference setup then inherit the LeadSelector or CustomerSelector on the Extended Selector. But I think it might not be possible.
The other thing that I've been thinking, is to add both selectors on the attribute and remove them from the graph level whenever which preference is set up.
I'm also thinking of creating 2 selectors, on which I will hide the other depending on the preference setup. But the problem is, the selector is being used not only on one page, and it's a hassle if I create 2 selectors just to solve that issue. And also in the future it might not just lead and customer selectors.
I hope you can help me, I'm out of ideas. Thank you so much.
UPDATE 09-24-2019
I created a custom selector attribute for Lead and Customer Selector attributes. And it's working just I want it to be, but now my problem is, the description field won't show on the text box or on that field, also, there are error such as 'Investor name cannot be found in the system'.
Investor Selector Attribute
public class InvestorSelectorAttribute : PXCustomSelectorAttribute
{
public InvestorSelectorAttribute() : base(typeof(REInvestor.accountID))
{
DescriptionField = typeof(REInvestor.acctName);
SubstituteKey = typeof(REInvestor.acctName);
}
protected IEnumerable GetRecords()
{
var leads = new PXSelect<Contact,
Where<Contact.contactType, Equal<ContactTypesAttribute.lead>,
Or<Where<Contact.contactType, Equal<ContactTypesAttribute.person>,
And<Contact.status, Equal<LeadStatusesAttribute.converted>>>>>>(this._Graph);
var contacts = new PXSelect<BAccountR>(this._Graph);
REFeature setup = PXSelect<REFeature>.Select(this._Graph);
if (setup.InvestorType == InvestorTypesAttribute.LeadVal)
{
foreach (Contact lead in leads.Select())
{
yield return new REInvestor { AccountID = lead.ContactID, AcctName = lead.DisplayName };
}
}
else
{
foreach (BAccountR contact in contacts.Select())
{
yield return new REInvestor { AccountID = contact.BAccountID, AcctName = contact.AcctName, AcctCD = contact.AcctCD };
}
}
}
}
Unbound REInvestor DAC
[Serializable]
[PXCacheName("Investor")]
public class REInvestor : IBqlTable
{
public abstract class accountID : BqlInt.Field<accountID> { }
[PXDBInt(IsKey = true)]
[PXUIField(DisplayName = REMessages.DisplayNames.AccountID, Visibility = PXUIVisibility.SelectorVisible)]
public virtual int? AccountID { get; set; }
public abstract class acctName : BqlString.Field<acctName> { }
[PXDBString(128, InputMask = "", IsUnicode = true)]
[PXUIField(DisplayName = REMessages.DisplayNames.AccountName, Visibility = PXUIVisibility.SelectorVisible)]
public virtual string AcctName { get; set; }
public abstract class acctCD : BqlString.Field<acctCD> { }
[PXDBString(128, InputMask = "", IsUnicode = true)]
[PXUIField(DisplayName = REMessages.DisplayNames.AcctCD, Visibility = PXUIVisibility.SelectorVisible)]
public virtual string AcctCD { get; set; }
}
** DAC Integration **
[PXDBInt]
[PXUIField(DisplayName = REMessages.DisplayNames.InvestorsName, Required = true)]
[InvestorSelector()]
[PXDefault(PersistingCheck = PXPersistingCheck.NullOrBlank)]
public virtual int? ContactID { get; set; }
I really need your help and suggestions. Thank you so much.
Is ok to have 2 fields defined and one displayed.
Each one with his own selector, and PXUIField Description.
You set visibility for one of them base on the setup field at graph level (RowSelected event)
In case you need to merge them on persist (persist both fields in one bound field), you can simply use 2 unbound fields for collecting/displaying data.
on updating/persisting event, you can update the values from unbound fields to unique database field
on retrieve you can make an Data View - Delegate to populate the unbound fields base on configuration;
When you plan to move on multiple pages (graphs) you move the code from the graph to an PXEventSubscriberAttribute
You add the new attribute at the DAC level.
This way you have access on all Graph events that may need (persisting, selecting, updating, ...). All the code stays in one place.
For multiple DACs you still need to create the fields; add them the new attribute.
We have a custom field with a lookup/selector for Inventory Item that I had restricted to only a certain item class. With 2018R1, the ItemClassID field is now an int and I need to compare to the ItemClassCD field.
My PXRestrictor can only access fields from the original DAC. How best should I rewrite this to accommodate the change to the item class?
#region ParentInventoryID
[Inventory( IsKey = true, Visibility = PXUIVisibility.SelectorVisible, DisplayName = "Parent Inventory ID")]
[PXRestrictor(typeof(Where<InventoryItem.itemClassID, Equal<ItemClass.cabledTransceiverFinishedProduct>>), "Parent is not a cabled transceiver finished product.")]
[PXDefault()]
[PX.Data.EP.PXFieldDescription]
[PXParent(typeof(Select<InventoryItem, Where<InventoryItem.inventoryID, Equal<Current<parentInventoryID>>>>))]
public Int32? ParentInventoryID { get; set; }
public abstract class parentInventoryID : IBqlField { }
#endregion
A new table INItemClass has been created to store the Inventory Item Class.
InventoryItem.INItemClassID key field points to a INItemClass record.
I think that the Inventory attribute doesn't join the INItemClass by default so you would have to add that join in the attribute type parameter.
With the INItemClass join you can restrict on the text field INItemClass.itemClassCD:
[Inventory(typeof(Search2<InventoryItem.inventoryID,
InnerJoin<INItemClass, On<INItemClass.itemClassID, Equal<InventoryItem.itemClassID>>>>),
typeof(InventoryItem.inventoryCD),
typeof(InventoryItem.descr),
DisplayName = "Parent Inventory ID")]
[PXRestrictor(typeof(Where<INItemClass.itemClassCD, Equal<ItemClass.cabledTransceiverFinishedProduct>>), "Parent is not a cabled transceiver finished product.")]
I have a custom grid using a custom dac that I have declared. Originally i had a PXselector attribute set to POOrder.orderNbr
With this selector it grabs all POOrders in my grid selector
[PXDBString(50, IsKey = true, IsUnicode = true, InputMask = "")]
[PXSelector(typeof(POOrder.orderNbr))]
[PXUIField(DisplayName = "Po#")]
public string Po { get; set; }
public class po : IBqlField { }
But i want the selector to filter POOrders that appear on the Document details->POReceiptLine. I attempted to filter using the Search but only get the lowest value POOrder.ordnbr on the receipt. The images below should illustrate what I mean. I want it to show all POOrder.orderNbr but it's only retrieving the first lowest value order.
[PXDBString(50, IsKey = true, IsUnicode = true, InputMask = "")]
[PXSelector(typeof(Search<POOrder.orderNbr,Where<POOrder.orderNbr,Equal<Current2<POReceiptLine.pONbr>>>>))]
[PXUIField(DisplayName = "Po#")]
public string Po { get; set; }
public class po : IBqlField { }
I believe it would be much better if you inner join POReceiptLine DAC, and then aggregate associated Purchase Orders to eliminate possible duplicates:
[PXSelector(typeof(Search5<POOrder.orderNbr,
InnerJoin<POReceiptLine, On<POReceiptLine.pOType, Equal<POOrder.orderType>,
And<POReceiptLine.pONbr, Equal<POOrder.orderNbr>,
And<POReceiptLine.receiptNbr, Equal<Current<POReceipt.receiptNbr>>>>>>,
Aggregate<GroupBy<POOrder.orderType,
GroupBy<POOrder.orderNbr>>>>))]
The main issue in the selector is that you are looking for POOrder where the order Number equals the current Receipt Line. That means that depending the line selected, the value will change but will always yield only one result. The solution would then be to change the condition to be on the Purchase Receipt document instead.
To do so you have two options. Either you search directly POReceiptLine.poNbr, but then your selector will display columns from that table, or you join POOrder to POReceiptLine.
With search on POReceiptLine
[PXSelector(typeof(Search<POReceiptLine.pONbr,
Where<POReceiptLine.receiptType,
Equal<Optional<POReceipt.receiptType>>,
And<POReceiptLine.receiptNbr,
Equal<Optional<POReceipt.receiptNbr>>>>>), DirtyRead = true)]
With search on POOrder
[PXSelector(typeof(Search2<POOrder.orderNbr,
InnerJoin<POReceiptLine, On<POReceiptLine.pOType,
Equal<POOrder.orderType>,
And<POReceiptLine.pONbr,
Equal<POOrder.orderNbr>>>>,
Where<POReceiptLine.receiptType,
Equal<Optional<POReceipt.receiptType>>,
And<POReceiptLine.receiptNbr,
Equal<Optional<POReceipt.receiptNbr>>>>>))]
Also notice the parameter DirtyRead = true. This parameter will tell the selector to get it's information in the cache and not only from the Database. Because you are selecting something from the same page, you would need to save the whole document before being able to select the record otherwise. Unfortunatly it would not work with the second option because the Join is done at the database level.