I am trying to add INItemXRef's Alternate ID to the global search for Inventory Item profiles. This is a 1:many relationship since there can be various vendors and units of measure with different Alternate ID's.
Per Acumatica, I have tried replacing the NoteID for Inventory Item with a match with join with the below code:
[PXSearchable(SM.SearchCategory.IN, "{0}: {1}", new Type[] {
typeof(InventoryItem.itemType), typeof(InventoryItem.inventoryCD) },
new Type[] { typeof(InventoryItem.descr) },
NumberFields = new Type[] { typeof(InventoryItem.inventoryCD) },
Line1Format = "{0}{1}{2}", Line1Fields = new Type[] {
typeof(INItemClass.itemClassCD), typeof(INItemClass.descr),
typeof(INItemXRef.alternateID) },
Line2Format = "{0}", Line2Fields = new Type[] {
typeof(InventoryItem.descr)},
MatchWithJoin = typeof(RightJoin<INItemXRef, On<INItemXRef.inventoryID,
Equal<InventoryItem.inventoryID>>>),
WhereConstraint = typeof(Where<Current<InventoryItem.itemStatus>,
NotEqual<InventoryItemStatus.unknown>>)
)]
[PXNote(PopupTextEnabled = true)]
I've also followed the example from question How to include field from a linked entity into Full-Text Entity Index?
My code for this in Inventory Item's DAC extension is:
public partial class INItemXRef: PX.Data.IBqlTable
{
//, IPaymentTypeDetailMaster, ILocation
public abstract class inventoryID: IBqlField { }
[PXDBInt()]
[PXDBChildIdentity(typeof(INItemXRef.inventoryID))]
[PXUIField(DisplayName = "Xref ID", Visibility = PXUIVisibility.Invisible)]
[PXSelector(typeof(Search<INItemXRef.inventoryID>), DirtyRead = true)]
public virtual int? InventoryID{ get; set; }
//public virtual void InventoryItem_InventoryID_CacheAttached(PXCache sender)
//{
//}
public abstract class alternateID: IBqlField { }
[PXDBString()]
[PXUIField(DisplayName = "Alternate ID")]
public virtual int? AlternateID{ get; set; }
}
However, the correct way to do this is still and issue. There has been one suggestion to use CacheAttached but I haven't been able to make that work, either.
Is Cache Attached actually required or am I missing something from including linked entities?
The intention is to search for a specific Inventory Item and get the Items and and Alternate ID(s) they have, meaning an Item may appear more than once in the Global Search. I have also made sure to reset IIS and rebuild full text indexes in case this was the issue.
Related
User requirement is to add universal search for the new screen, added our new screen under Inventory Module also the new screen is not entry screen it is just like user view screen so in that DAC for note id field we have added searchable attribute but it does not work.
Can someone please help me with sample code or correct me where i am doing wrong.
Also let me know is it possible to add searchable attribute for the new screens or not in Acumatica?
Thanks in advance.
#region Noteid
public new abstract class noteid : PX.Data.BQL.BqlGuid.Field<noteid> { }
protected Guid? _Noteid;
[PXSearchable(PX.Objects.SM.SearchCategory.All , "{0}", new Type[] { typeof(KWLotSerialDetails.lotSerialNbr) },
new Type[] { typeof(KWLotSerialDetails.lotSerialNbr), typeof(KWLotSerialDetails.inventoryID)},
NumberFields = new Type[] { typeof(KWLotSerialDetails.lotSerialNbr) },
Line1Format = "{0}{1}", Line1Fields = new Type[] { typeof(KWLotSerialDetails.lotSerialNbr), typeof(KWLotSerialDetails.inventoryID)},
Line2Format = "{1}{2}", Line2Fields = new Type[] { typeof(KWLotSerialDetails.lotSerialNbr), typeof(KWLotSerialDetails.inventoryID) })]
public virtual Guid? Noteid
{
get
{
return this._Noteid;
}
set
{
this._Noteid = value;
}
}
#endregion
You absolutely can add universal search to custom tables. Search is added to a DAC, not a screen, so it doesn't matter about using in a "user view screen". When the full-text index is rebuilt, your NoteID field is processed into the SearchIndex table.
I may be wrong, but I also think you need to convert your Noteid field to NoteID/noteID for this to work properly. C# is case sensitive, and FullIndexRebuild.cs contains:
entity.GetNestedType("noteID")
... so I think it is not finding your Noteid/noteid field because of this.
One of my custom PXSearchable NoteID fields:
#region NoteID
[PXNote]
[PXSearchable(PX.Objects.SM.SearchCategory.IN, "{0}",
new Type[] { typeof(SSINItemManufacturer.manufacturerItem) },
new Type[] { typeof(SSINItemManufacturer.manufacturerItem) },
NumberFields = new Type[] { typeof(SSINItemManufacturer.manufacturerItem) },
Line1Format = "{0}", Line1Fields = new Type[] { typeof(SSINItemManufacturer.manufacturerItem) },
Line2Format = "{0}", Line2Fields = new Type[] { typeof(SSINItemManufacturer.manufacturerItem) },
WhereConstraint = typeof(Where<Current<SSINItemManufacturer.isActive>, NotEqual<False>>),
MatchWithJoin = typeof(InnerJoin<InventoryItem, On<InventoryItem.inventoryID, Equal<SSINItemManufacturer.inventoryID>>>),
SelectForFastIndexing = typeof(Select2<SSINItemManufacturer, InnerJoin<InventoryItem, On<SSINItemManufacturer.inventoryID, Equal<InventoryItem.inventoryID>>>>)
)]
public virtual Guid? NoteID { get; set; }
public abstract class noteID : PX.Data.BQL.BqlGuid.Field<noteID> { }
#endregion
Also check that your DAC is "known" by the "Rebuild Full-Text Entity" screen. Check screen SM209500 to make sure your DAC is listed, and if so, try rebuilding the full-text index on it.
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:
Acumatica - Attempting to add INItemXRef AlternatID to universal search.
I did look at another question that seemed to possibly be a duplicate of my question, but Acumatica gave him a different approach and he has not gotten any responses, hoping to have better luck:
Adding INItemXRef to Global Search with Inventory Item
The goal is a universal search by customer item number. Acumatica advised to create a dummy INItemXRef maintenance screen and add a searchable NoteID to the DAC to reference this screen. Below is my first stab at adding the NoteID, but I'm getting compilation errors:
public class INItemXRefExt : PXCacheExtension<PX.Objects.IN.INItemXRef>
{
#region NoteID
[PXDBGuid]
[PXUIField(DisplayName="NoteID")]
[PXSearchable(SM.SearchCategory.IN,
"{0}: {1}",
new Type[] { typeof(INItemXRef.alternateType), typeof(INItemXRef.inventoryID)},
new Type[] { typeof(INItemXRef.descr), typeof(INItemXRef.alternateID) }, // add fields you want to be searchable
NumberFields = new Type[] { typeof(INItemXRef.inventoryID) },
Line1Format = "{0}{1}", Line1Fields = new Type[] { typeof(INItemClass.itemClassCD), typeof(INItemXRef.alternateID)},
Line2Format = "{0}", Line2Fields = new Type[] { typeof(INItemXRef.descr) },
//WhereConstraint = typeof(Where<Current<InventoryItem.itemStatus>, NotEqual<InventoryItemStatus.unknown>>),
SelectForFastIndexing = typeof(Select<INItemXRef>) // this is required so that search knows how to index your fields
)]
//[PXNote(PopupTextEnabled = true)]
//public Guid? NoteID { get; set; }
public virtual Guid? NoteID { get; set; }
public abstract class NoteID : IBqlField { }
#endregion
}
When compiling I get this validation error:
\App_RuntimeCode\INItemXRef.cs(28): error CS0102: The type 'PX.Objects.IN.INItemXRefExt' already contains a definition for 'NoteID'
I thought at first that the reason is because the field was already created in a previous run (I think it was), and the public abstract class NoteID : IBqlField { } was essentially trying to get it to duplicate the field. So I commented out the line and got this:
The base property is missed or IBqlField is not defined: NoteID in extension PX.Objects.IN.INItemXRefExt
I'm sure there are plenty of problems with my searchable attribute as well and will update the question as it evolves, but for the moment I'm stumped at the compilation step.
Update:
Thanks to the responses below I was able to re-write a bit and get everything to compile. Next step is to figure out why the results are a bit strange. interestingly, I cannot do a universal search and find any item unless it has a cross-reference, and then only displays base item info even though I'm trying to display the alternate code(s) and descriptions(s). New code:
#region NoteID
public abstract class noteID : PX.Data.IBqlField
{
}
protected Guid? _NoteID;
[PXSearchable(SM.SearchCategory.IN,
"{0}: {1}",
new Type[] { typeof(INItemXRef.alternateType), typeof(INItemXRef.inventoryID)},
new Type[] { typeof(INItemXRef.descr), typeof(INItemXRef.alternateID) }, // add fields you want to be searchable
NumberFields = new Type[] { typeof(INItemXRef.inventoryID) },
Line1Format = "{0}{1}", Line1Fields = new Type[] { typeof(INItemClass.itemClassCD), typeof(INItemXRef.alternateID)},
Line2Format = "{0}", Line2Fields = new Type[] { typeof(INItemXRef.descr) },
//WhereConstraint = typeof(Where<Current<InventoryItem.itemStatus>, NotEqual<InventoryItemStatus.unknown>>),
SelectForFastIndexing = typeof(Select<INItemXRef>) // this is required so that search knows how to index your fields
)]
[PXNote(DescriptionField = typeof(INItemXRef.inventoryID),
Selector = typeof(INItemXRef.inventoryID))]
public virtual Guid? NoteID
{
get
{
return this._NoteID;
}
set
{
this._NoteID = value;
}
}
#endregion
You can review one of the out-of-the-box DAC that contains NoteID field and review it for guidance. For example, take a look at the INRegister.NoteID field definition:
#region NoteID
public abstract class noteID : PX.Data.IBqlField
{
}
protected Guid? _NoteID;
[PXSearchable(SM.SearchCategory.IN, "{0}: {1}", new Type[] { typeof(INRegister.docType), typeof(INRegister.refNbr) },
new Type[] { typeof(INRegister.tranDesc), typeof(INRegister.extRefNbr), typeof(INRegister.transferNbr) },
NumberFields = new Type[] { typeof(INRegister.refNbr) },
Line1Format = "{0}{1:d}{2}{3}{4}", Line1Fields = new Type[] { typeof(INRegister.extRefNbr), typeof(INRegister.tranDate), typeof(INRegister.transferType), typeof(INRegister.transferNbr), typeof(INRegister.status) },
Line2Format = "{0}", Line2Fields = new Type[] { typeof(INRegister.tranDesc) },
WhereConstraint = typeof(Where<INRegister.docType, NotEqual<INDocType.production>, And<INRegister.docType, NotEqual<INDocType.disassembly>>>)
)]
[PXNote(DescriptionField = typeof(INRegister.refNbr),
Selector = typeof(INRegister.refNbr))]
public virtual Guid? NoteID
{
get
{
return this._NoteID;
}
set
{
this._NoteID = value;
}
}
#endregion
Notice the use of PXNote attribute(and no use of PXDBGuid attribute).
You can try similar approach and only with PXNote first, to confirm it compiles properly. Then you could add PXSearcheable and start working on it.
This particular question has been answered, but in order to get the search to work the way I need will have to go a different direction creating a brand-new DAC, BLC, and maintenance screen. Thanks for the help!
I've added Customer Location to the Full-Text Entity Index, but cannot figure out how to get Address Line 1 from the Location to be part of the Full-Text Index and be displayed in the result.
To includes fields of linked entities (those which are in one-to-one relationship with the top-level entity on a data entry screen), it's required to specify what top-level entity field should be used along with the PXSelectorAttribute to retrieve the linked entity. Right after the top-level entity field acting as a bridge between the linked entities, you will specify fields of the secondary entity, which should be included into the Full-Text Index and/or be displayed in the result. Keep in mind, that only top-level entity fields decorated with PXSelectorAttribute or PXDimensionSelectorAttribute have the ability to act as a bridge between the linked entities.
For example, to include fields from the Address DAC into the Customer Location Full-Text Entity Index, you must add the DefAddressID field from the Location DAC before listing fields from the Address DAC:
public partial class Location : PX.Data.IBqlTable, IPaymentTypeDetailMaster, ILocation
{
...
public abstract class defAddressID : IBqlField { }
[PXDBInt()]
[PXDBChildIdentity(typeof(Address.addressID))]
[PXUIField(DisplayName = "Default Address", Visibility = PXUIVisibility.Invisible)]
[PXSelector(typeof(Search<Address.addressID>), DirtyRead = true)]
public virtual int? DefAddressID { get; set; }
...
}
The CustomerLocation DAC found in the following code snippet can serve as a perfect example of a custom DAC used to add Customer Location to the Full-Text Entity Index:
[Serializable]
[PXCacheName("Customer Location")]
[PXBreakInheritance]
public partial class CustomerLocation : SelectedCustomerLocation
{
public new abstract class bAccountID : IBqlField { }
[Customer(typeof(Search<Customer.bAccountID,
Where<Customer.type, Equal<BAccountType.customerType>,
Or<Customer.type, Equal<BAccountType.prospectType>,
Or<Customer.type, Equal<BAccountType.combinedType>>>>>),
IsKey = true)]
public override int? BAccountID { get; set; }
public new abstract class locationCD : IBqlField { }
public new abstract class descr : IBqlField { }
public new abstract class defAddressID : IBqlField { }
public new abstract class locType : IBqlField { }
public new abstract class noteID : IBqlField { }
[PXNote()]
[PXSearchable(SM.SearchCategory.CR, "{1} {2}: {3}",
new Type[] {
typeof(CustomerLocation.bAccountID),
typeof(Customer.acctCD),
typeof(CustomerLocation.locationCD),
typeof(CustomerLocation.descr) },
new Type[] {
typeof(CustomerLocation.bAccountID),
typeof(Customer.acctCD),
typeof(CustomerLocation.locationCD),
typeof(CustomerLocation.descr),
typeof(CustomerLocation.defAddressID),
typeof(Address.addressLine1),
typeof(Address.addressLine2),
typeof(Address.city),
typeof(Address.countryID) },
Line1Format = "{0} {2}",
Line1Fields = new Type[] {
typeof(CustomerLocation.descr),
typeof(CustomerLocation.defAddressID),
typeof(Address.addressLine1) },
Line2Format = "{1}",
Line2Fields = new Type[] {
typeof(CustomerLocation.defAddressID),
typeof(Address.addressLine2) },
WhereConstraint =
typeof(Where<CustomerLocation.locType, Equal<LocTypeList.customerLoc>,
Or<CustomerLocation.locType, Equal<LocTypeList.combinedLoc>>>),
MatchWithJoin = typeof(InnerJoin<Customer,
On<Customer.bAccountID, Equal<CustomerLocation.bAccountID>>>),
SelectForFastIndexing = typeof(Select2<CustomerLocation,
InnerJoin<Customer,
On<CustomerLocation.bAccountID, Equal<Customer.bAccountID>>>>)
)]
public override Guid? NoteID { get; set; }
}
Becides the DefAddressID field, which is used to include fields from the Address DAC to the Full-Text Entity Index, CustomerLocation also utilize CustomerAttribute attached to the BAccountID field to include Customer's natural application-wise AcctCD keys instead of the surrogate DB-level BAccountID keys. Last thing to mention is the PXBreakInheritanceAttribute required to prevent initialization of PXCache objects corresponding to base DACs when on Rebuild Full-Text Entity Index screen the system generates list of entities to be used by Full-Text Entity Index.
I have created a Name value pair field for Sales Order table and Search is giving error while using the field from Description field in selector
#region UsrLoader
public abstract class usrLoader : IBqlField { }
[PXDBString(128, IsUnicode = true)]
[PXUIField(DisplayName = "Operator 1")]
[PXSelector(typeof(EPEmployee.acctCD),
new Type[]
{
typeof(EPEmployee.acctCD),
typeof(EPEmployee.acctName)
},
DescriptionField = typeof(EPEmployee.acctName))]
public virtual string UsrLoader { get; set; }
#endregion
#region UsrUnLoader
[PXDBString(128)]
[PXUIField(DisplayName = "Operator 2")]
[PXSelector(typeof(EPEmployee.acctCD),
new Type[]
{
typeof(EPEmployee.acctCD),
typeof(EPEmployee.acctName)
},
DescriptionField = typeof(EPEmployee.acctName))]
public virtual string UsrUnLoader { get; set; }
public abstract class usrUnLoader : IBqlField { }
#endregion
I am getting the following error
Typically I see the Invalid column error when the customization hasn't been published to insert the field in the database table. Did you publish after adding the fields?
Also, the warnings might tell you to join in the parent table. Did you try doing a join to the EPEmployee table to use the description/name field in your GI?