How to use 'Like' in BQL - acumatica

How do I format a BQL query to use the 'like' sql functionality. I want to get all the rows where the first two characters of a particular field match a string variable.
Sql example:
Select item, customer, uom from table where uom like 'CA%'

Somethng like this :
public static class Constants
{
public class likeCA : Constant<string>
{
public likeCA() : base("CA%") { }
}
}
public PXSelect<MyTable,Where<MyTable.uom,Like<Constants.likeCA>>> MySelect;

[PXCacheName("Filter")]
[Serializable]
public class ItemFilter : IBqlTable
{
#region FullDescription
[PXString(IsUnicode = true, InputMask = "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC")]
[PXUIField(DisplayName = "Full Description")]
public virtual string Description { get; set; }
public abstract class fdescription : IBqlField { }
#endregion
#region SearchDescrWildcard
[PXString(IsUnicode = true)]
public virtual string SearchDescrWildcard
{
get
{
return SearchWildcardUtils.CreateWildcard(this.Description);
}
set
{
}
}
public abstract class searchDescrWildcard : IBqlField { }
#endregion
public class SearchWildcardUtils
{
public static bool IsSearchWildcardEmpty(string aSub)
{
return aSub == null || aSub == "" || new Regex("^[_\\?]*$").IsMatch(aSub);
}
public static string CreateWildcard(string text)
{
ISqlDialect sqlDialect = PXDatabase.Provider.SqlDialect;
if (SearchWildcardUtils.IsSearchWildcardEmpty(text))
{
return sqlDialect.WildcardAnything;
}
if (text[text.Length - 1] != '?' && text[text.Length - 1] != ' ')
{
text = $"?{text}?";
}
return Regex.Replace(Regex.Replace(text, "[ \\?]+$", sqlDialect.WildcardAnything), "[ \\?]", sqlDialect.WildcardAnything);
}
}
}
public PXSelect<MyTable, Where<MyTable.description, Like<Current<ItemFilter.searchDescrWildcard>>>> SelectMyTable; // Sql Table select
cmd.WhereAnd<Where<MyTable.description, Like<Current<ItemFilter.searchDescrWildcard>>>>();// BQL query select

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.

Acumatica - Revision

We have 2 DAC - Master and Child
Master DAC
#region MasterID
public abstract class masterID:PX.Data.BQL.BqlInt.Field<masterID> { }
protected int? _MasterID;
[PXDBIdentity()]
[PXUIField(Visibility = PXUIVisibility.Invisible)]
[PXReferentialIntegrityCheck]
public virtual int? MasterID
{
get {return this._MasterID;}
set {this._MasterID = value;}
}
#endregion
#region MasterCD
public abstract class masterRoutingCD:PX.Data.BQL.BqlString.Field<masterCD> { }
protected string _MasterRoutingCD;
[BomID(DisplayName = "Master #", IsKey = true, Required = true,
Visibility = PXUIVisibility.SelectorVisible)]
[PXDefault]
[Rev.Key(typeof(Setup.pMMasterNumberSequenceID),
typeof(Master.masterCD),
typeof(Master.revisionNo),
typeof(Master.masterCD),
typeof(Master.revisionNo)
)]
public virtual string MasterCD
{
get {return this._MasterCD;}
set {this._MasterCD = value;}
}
#endregion
#region RevisionNo
public abstract class revisionNo:PX.Data.IBqlField { }
protected string _RevisionNo;
[RevisionIDField(IsKey = true, Visibility = PXUIVisibility.SelectorVisible,
Required = true)]
[PXDefault(typeof(Master.defaultRevisionNo),
PersistingCheck = PXPersistingCheck.Nothing)]
[Rev.ID(typeof(Master.defaultRevisionNo),
typeof(Master.masterCD),
typeof(Master.revisionNo),
typeof(Master.revisionNo),
typeof(Master.description),
typeof(Master.fromDate),
typeof(Master.toDate))]
public virtual string RevisionNo
{
get {return this._RevisionNo;}
set {this._RevisionNo = value;}
}
#endregion
Child DAC
public abstract class childID:PX.Data.BQL.BqlInt.Field<childID> { }
protected int? _ChildID;
[PXDBIdentity()]
//[PXReferentialIntegrityCheck]
public virtual int? ChildID
{
get {return this._ChildID;}
set {this._ChildID = value;}
}
#endregion
#region MasterID
public abstract class masterID:PX.Data.BQL.BqlInt.Field<masterID> { }
protected int? _MasterID;
[PXDBInt()]
[PXDBDefault(typeof(Master.masterID))]
[PXParent(typeof(Select<Master, Where<Master.masterRoutingCD, Equal<Current<masterCD>>,
And<Master.revisionNo, Equal<Current<revisionNo>>>>>))]
public virtual int? MasterID
{
get {return _MasterID;}
set {_MasterID = value;}
}
#endregion MasterID
#region MasterCD
public abstract class masterCD:PX.Data.BQL.BqlString.Field<masterCD> { }
protected string _MasterCD;
[PXDBDefault(typeof(Master.masterCD))]
[PXDBString(IsKey = true, IsUnicode = true)]
public virtual string MasterCD
{
get {return this._MasterCD;}
set {this._MasterCD = value;}
}
#endregion
#region Revision
public abstract class revisionNo:PX.Data.BQL.BqlString.Field<revisionNo> {}
[PXDBString(15, IsKey = true, IsUnicode = true)]
[PXDBDefault(typeof(Master.revisionNo))]
public virtual string RevisionNo { get; set; }
#endregion Revision
public abstract class stepsID:PX.Data.BQL.BqlInt.Field<stepsID> { }
[OperationCDField(IsKey =true, DisplayName = "Steps ID",
Visibility = PXUIVisibility.SelectorVisible)]
[PXDefault(PersistingCheck = PXPersistingCheck.NullOrBlank)]
//[PXUIField(DisplayName = "Process Steps ID")]
public virtual string StepsID { get; set; }
Graph - MasterMaint
public class MasterMaint:PXRevisionableGraph<MasterMaint, Master,
Master.masterCD, Master.revisionNo>
{
public PXSelect<Child, Where<Child.masterCD, Equal<Current<Master.masterCD>>,
And<Child.revisionNo, Equal<Current<Master.revisionNo>>>>> ChildRecords;
#region Override
public override bool CanClipboardCopyPaste()
{
return false;
}
public override bool CanCreateNewRevision(MasterMaint fromGraph, MasterMaint toGraph,
string keyValue, string revisionValue, out string error)
{
// Always returns true as new revisions can be created at any time
error = string.Empty;
return true;
}
public override void CopyRevision(MasterMaint fromGraph, MasterMaint toGraph,
string keyValue, string revisionValue)
{
if(toGraph?.Documents?.Current == null || fromGraph?.Documents?.Current == null)
{
// api calls should create new revs on their own - this causes issues
// when calling from api so we need to turn the copy rev logic off
return;
}
toGraph.Documents.Cache.SetDefaultExt<EWPMMasterRouting.status>
(toGraph.Documents.Current);
if(SkipAutoCreateNewRevision())
{
return;
}
CopyChildRecords(fromGraph.Documents.Current.MasterRoutingCD,
fromGraph.Documents.Current.RevisionNo, toGraph.Documents.Current.MasterID,
keyValue, revisionValue, false);
}
internal virtual void CopyChildRecords(string sourceID, string sourceRevisionNo,
int? newMasterID, string newMasterCD, string newRevisionID, bool copyNotes)
{
foreach(Child fromRow in PXSelect<Child,
Where<Child.masterCD, Equal<Required<Child.masterCD>>,
And<Child.revisionNo, Equal<Required<Child.revisionNo>>>>>
.Select(this, sourceID, sourceRevisionNo))
{
var toRow = PXCache<Child>.CreateCopy(fromRow);
toRow.MasterID = newMasterRoutingID;
toRow.ChildID = null;
toRow.MasterCD = newMasterCD;
toRow.RevisionNo= newRevisionID;
toRow = ChildRecords.Insert(toRow);
}
}
#endregion
}
Issue - When the value is changed in the Revision the record is displayed with new revision number keeping the CD fields as for previous revision and all the child.
That is all correct, But when the record is saved, there is error "Another process has updated the Record, your changes will be lost"
Why there is a Error
In Acuamtica we have NoteID column unique across the system If you try to create a record with duplicating NoteID field value you'll get "Another process has updated the record" exception because it consider that you are updating the same record.
In you case when you copy revision you do not reset the NoteID value for the record so you actually try to insert another record with the same value.
You'll need to add the following line to CopyRevision method
toGraph.Documents.Cache.SetDefaultExt<EWPMMasterRouting.noteID>(toGraph.Documents.Current);

Unable to update Reason dropdown values in Cases

I am trying to update the dropdown values for Reason field in Cases if Status is set to Open.
{
if (InvokeBaseHandler != null)
InvokeBaseHandler(cache, e);
var row = (CRCase)e.Row;
if (row == null) return;
if (row.Status == CRCaseStatusesAttribute._OPEN)
{
PXStringListAttribute.SetList<CRCase.resolution>(cache, row,
new string[] { "IP", "AD", "ES", "QQ", "CC" },
new string[] { "In Process", "Updated", "In Escalation", "Pending Quote", "Pending Closure"}
);
}
}
But it doesn't seem to be reflecting on the UI.
I checked if anything else is overriding my code in CRCaseMaint graph but couldn't find any which seem to be affecting the List.
I couldn't see anything off in Field definition though.
#region Resolution
public abstract class resolution : PX.Data.BQL.BqlString.Field<resolution> { }
[PXDBString(2, IsFixed = true)]
[CRCaseResolutions]
[PXUIField(DisplayName = "Reason")]
[PXChildUpdatable]
[PXMassUpdatableField]
public virtual String Resolution { get; set; }
#endregion
public sealed class CRCaseResolutionsAttribute : PXStringListAttribute
{
public const string _CUSTOMER_PRECLOSED = "CC";
public const string _CUSTOMER_REPLIED = "AD";
public const string _RESOLVED = "RD";
public const string _ASSIGNED = "AS";
public const string _UNASSIGNED = "UA";
public const string _UPDATED = "AD";
public CRCaseResolutionsAttribute();
public sealed class CustomerPreclosed : BqlType<IBqlString, string>.Constant<CustomerPreclosed>
{
public CustomerPreclosed();
}
public sealed class CustomerReplied : BqlType<IBqlString, string>.Constant<CustomerReplied>
{
public CustomerReplied();
}
public sealed class Resolved : BqlType<IBqlString, string>.Constant<Resolved>
{
public Resolved();
}
public sealed class Assigned : BqlType<IBqlString, string>.Constant<Assigned>
{
public Assigned();
}
public sealed class Unassigned : BqlType<IBqlString, string>.Constant<Unassigned>
{
public Unassigned();
}
public sealed class Updated : BqlType<IBqlString, string>.Constant<Updated>
{
public Updated();
}
}
I think I can definitely use different labels and values than the one listed above. Is there anything else that I am missing and/or need to look into?
Note: Acumatica 2019 R1 (Build 19.112.0045) is the version that I am working on.

Is it possible to dynamically change the PXSelect a view is using programatically?

I have a button in which, when pressed, I want to show or hide rows in a grid based on certain criteria. Is it possible to change the PXSelect a view uses on the fly so that it re-queries the database and retrieves different results? I will, of course, be querying the same table and not changing up the structure of the View or grid.
The code below adds a non-visible field to the header record that is set by the button press this value is then used by the child records view delegate to determine based on the child records criteria (in this case a Boolean on each child) if they are shown.
public sealed class APInvoiceExtension : PXCacheExtension<APInvoice>
{
#region UsrShowAll
public abstract class usrShowAll : IBqlField
{
}
[PXBool]
public bool? UsrShowAll { get; set; }
#endregion
}
public sealed class APTranExtension : PXCacheExtension<APTran>
{
#region UsrHidden
public abstract class usrHidden : IBqlField
{
}
[PXDBBool]
[PXUIField(DisplayName = "Hidden", Enabled = false)]
public bool? UsrHidden { get; set; }
#endregion
}
public class APInvoiceEntryExtension : PXGraphExtension<APInvoiceEntry>
{
public PXAction<APInvoice> SHW;
[PXUIField(DisplayName = "Show All Records", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Update)]
[PXButton]
protected void sHW()
{
if (Base.Document.Current != null)
{
APInvoiceExtension docExt = Base.Document.Current.GetExtension<APInvoiceExtension>();
docExt.UsrShowAll = !(docExt.UsrShowAll ?? false);
}
}
protected virtual IEnumerable transactions()
{
bool showAll = Base.Document.Current != null ? (Base.Document.Current.GetExtension<APInvoiceExtension>().UsrShowAll ?? false) : false;
APTran tran;
foreach (PXResult<APTran, POReceiptLine> res in Base.Transactions.Select())
{
tran = res[0] as APTran;
if (!showAll)
{
if (!(tran.GetExtension<APTranExtension>().UsrHidden ?? false))
{
yield return res;
}
}
else
{
yield return res;
}
}
}
}

Saving a field depending on the Tab selected

I am having trouble to find a solution in how to save a char type field depending on the Tab that the user is positioned. I have "Fringe", "Overhead" and "G&A" tabs where the 3 of them share the same database table. The problem is that I want to save this Type field ("F","O" or "G") depending on the Tab that I am in.
Tabs and Type field in the grid
The solution that I am trying, was to create 3 different DAC Unbounded for each tab and save the information on the datamembers bounded to these tabs (the type field is included on each tab). After doing this, I used the Event Handler RowPersisting to insert manually on database (which it worked correctly) and also I needed to implement the RowDeleting to delete a row if needed. This is where this solution is not working.
Basically the RowDeleting EventHandler is not showing the correct information of the current row in which this event was triggered.
IU Row vs What the event is showing as the current row
I would look into using projections (PXProjection) in Acumatica so you can create additional DACs representing your different types (as they are located on different Tabs but stored in the same table). This way you don not have to use any events to do any complex/tricky logic against the framework as you have described you are doing.
I put together a simple example below. Note the 3 projection DACs that point to the same main dac. This allows the records to be all stored inMyTable in the database but each will have their own cache.
Here I define my different types...
public class MyDacType
{
public const string TypeA = "A";
public const string TypeB = "B";
public const string TypeC = "B";
public class typeA : Constant<string>
{
public typeA() : base(TypeA) { }
}
public class typeB : Constant<string>
{
public typeB() : base(TypeB) { }
}
public class typeC : Constant<string>
{
public typeC() : base(TypeC) { }
}
/// <summary>
/// List attribute for display of user friendly value
/// </summary>
public class ListAttribute : PXStringListAttribute
{
public ListAttribute()
: base(new string[]
{
TypeA,
TypeB,
TypeC
}, new string[]
{
"Type A",
"Type B",
"Type C"
})
{
}
}
}
This is my main table...
/// <summary>
/// Main database DAC
/// </summary>
[Serializable]
public class MyTable : IBqlTable
{
#region SomeKeyField
public abstract class someKeyField : PX.Data.IBqlField
{
}
protected string _SomeKeyField;
[PXDBString(10, IsKey = true, IsUnicode = true)]
[PXDefault]
[PXUIField(DisplayName = "My Key Field", Enabled = false)]
public virtual string SomeKeyField
{
get
{
return this._SomeKeyField;
}
set
{
this._SomeKeyField = value;
}
}
#endregion
#region DacType
public abstract class dacType : PX.Data.IBqlField
{
}
protected string _DacType;
[PXDBString(1)]
[PXDefault]
[MyDacType.List]
[PXUIField(DisplayName = "Dac Type")]
public virtual string DacType
{
get
{
return this._DacType;
}
set
{
this._DacType = value;
}
}
#endregion
#region Description
public abstract class description : PX.Data.IBqlField
{
}
protected string _Description;
[PXDBString(256, IsUnicode = true)]
[PXUIField(DisplayName = "Description")]
public virtual string Description
{
get
{
return this._Description;
}
set
{
this._Description = value;
}
}
#endregion
}
I created 3 projections (one for each tab)...
/// <summary>
/// DAC representing Type A (data stored in MyTable)
/// </summary>
[Serializable]
[PXProjection(typeof(Select<MyTable, Where<MyTable.dacType, Equal<MyDacType.typeA>>>), Persistent = true)]
public class MyTableTypeA : IBqlTable
{
#region SomeKeyField
public abstract class someKeyField : PX.Data.IBqlField
{
}
protected string _SomeKeyField;
[PXDBString(10, IsKey = true, IsUnicode = true, BqlField = typeof(MyTable.someKeyField))]
[PXDefault]
[PXUIField(DisplayName = "My Key Field", Enabled = false)]
public virtual string SomeKeyField
{
get
{
return this._SomeKeyField;
}
set
{
this._SomeKeyField = value;
}
}
#endregion
#region DacType
public abstract class dacType : PX.Data.IBqlField
{
}
protected string _DacType;
[PXDBString(1, BqlField = typeof(MyTable.dacType))]
[PXDefault(MyDacType.TypeA)]
[MyDacType.List]
[PXUIField(DisplayName = "Dac Type", Enabled = false, Visible = false)]
public virtual string DacType
{
get
{
return this._DacType;
}
set
{
this._DacType = value;
}
}
#endregion
#region Description
public abstract class description : PX.Data.IBqlField
{
}
protected string _Description;
[PXDBString(256, IsUnicode = true, BqlField = typeof(MyTable.description))]
[PXUIField(DisplayName = "Description")]
public virtual string Description
{
get
{
return this._Description;
}
set
{
this._Description = value;
}
}
#endregion
}
/// <summary>
/// DAC representing Type B (data stored in MyTable)
/// </summary>
[Serializable]
[PXProjection(typeof(Select<MyTable, Where<MyTable.dacType, Equal<MyDacType.typeB>>>), Persistent = true)]
public class MyTableTypeB : IBqlTable
{
#region SomeKeyField
public abstract class someKeyField : PX.Data.IBqlField
{
}
protected string _SomeKeyField;
[PXDBString(10, IsKey = true, IsUnicode = true, BqlField = typeof(MyTable.someKeyField))]
[PXDefault]
[PXUIField(DisplayName = "My Key Field", Enabled = false)]
public virtual string SomeKeyField
{
get
{
return this._SomeKeyField;
}
set
{
this._SomeKeyField = value;
}
}
#endregion
#region DacType
public abstract class dacType : PX.Data.IBqlField
{
}
protected string _DacType;
[PXDBString(1, BqlField = typeof(MyTable.dacType))]
[PXDefault(MyDacType.TypeB)]
[MyDacType.List]
[PXUIField(DisplayName = "Dac Type", Enabled = false, Visible = false)]
public virtual string DacType
{
get
{
return this._DacType;
}
set
{
this._DacType = value;
}
}
#endregion
#region Description
public abstract class description : PX.Data.IBqlField
{
}
protected string _Description;
[PXDBString(256, IsUnicode = true, BqlField = typeof(MyTable.description))]
[PXUIField(DisplayName = "Description")]
public virtual string Description
{
get
{
return this._Description;
}
set
{
this._Description = value;
}
}
#endregion
}
/// <summary>
/// DAC representing Type C (data stored in MyTable)
/// </summary>
[Serializable]
[PXProjection(typeof(Select<MyTable, Where<MyTable.dacType, Equal<MyDacType.typeC>>>), Persistent = true)]
public class MyTableTypeC : IBqlTable
{
#region SomeKeyField
public abstract class someKeyField : PX.Data.IBqlField
{
}
protected string _SomeKeyField;
[PXDBString(10, IsKey = true, IsUnicode = true, BqlField = typeof(MyTable.someKeyField))]
[PXDefault]
[PXUIField(DisplayName = "My Key Field", Enabled = false)]
public virtual string SomeKeyField
{
get
{
return this._SomeKeyField;
}
set
{
this._SomeKeyField = value;
}
}
#endregion
#region DacType
public abstract class dacType : PX.Data.IBqlField
{
}
protected string _DacType;
[PXDBString(1, BqlField = typeof(MyTable.dacType))]
[PXDefault(MyDacType.TypeC)]
[MyDacType.List]
[PXUIField(DisplayName = "Dac Type", Enabled = false, Visible = false)]
public virtual string DacType
{
get
{
return this._DacType;
}
set
{
this._DacType = value;
}
}
#endregion
#region Description
public abstract class description : PX.Data.IBqlField
{
}
protected string _Description;
[PXDBString(256, IsUnicode = true, BqlField = typeof(MyTable.description))]
[PXUIField(DisplayName = "Description")]
public virtual string Description
{
get
{
return this._Description;
}
set
{
this._Description = value;
}
}
#endregion
}
Now in my graph I create 3 views (1 for each PXProjection dac). I also do not need to display the DacType field since it will automatically default to the correct value.
public class MyGraph : PXGraph<MyGraph>
{
// Some other views...
/// <summary>
/// View for tab 1
/// </summary>
public PXSelect<MyTableTypeA> TypeA;
/// <summary>
/// View for tab 2
/// </summary>
public PXSelect<MyTableTypeB> TypeB;
/// <summary>
/// View for tab 3
/// </summary>
public PXSelect<MyTableTypeC> TypeC;
// When using events and cache attached you use the DAC name. Ex:
// protected virtual void MyTableTypeB_RowDeleting(PXCache cache, RowDeletingEvents e) { }
}

Resources