I have a BQL resultset consisting of several left joins, and one of the tables (ARCstRptControl) is joined multiple times under an 'alias' by way of inheriting the original DAC The code is shown below:
public class ARInv : PXGraph<ARInv, ARInvoice>
{
[Serializable]
public class ARCstRptControl1 : ARCstRptControl { }
[Serializable]
public class ARCstRptControl2 : ARCstRptControl { }
foreach (PXResult<ARTran
,PMTran
,ARCstRptControl
,ARCstRptControl1
,PMTask
,Account
,ARCstRptControl2> thistran in PXSelectJoin<ARTran,
LeftJoin<PMTran,
On<ARTran.pMTranID, Equal<PMTran.tranID>>,
LeftJoin<ARCstRptControl,
On<PMTran.origAccountGroupID, Equal<ARCstRptControl.accountID>>,
LeftJoin<ARCstRptControl1,
On<PMTran.accountGroupID, Equal<ARCstRptControl1.accountID>>,
LeftJoin<PMTask,
On<PMTask.projectID, Equal<PMTran.projectID>,
And<PMTask.taskID,Equal<PMTran.taskID>>>,
LeftJoin<Account,
On<ARTran.accountID, Equal<Account.accountID>>,
LeftJoin<ARCstRptControl2,
On<Account.accountGroupID, Equal<ARCstRptControl2.accountID>>>>>>>>,
Where<ARTran.tranType, Equal<Current<ARInvoice.docType>>,
And<ARTran.refNbr, Equal<Current<ARInvoice.refNbr>>>>>.Select(this))
{
ARTran artran = (ARTran)thistran;
PMTran pmtran = (PMTran)thistran;
ARCstRptControl srcgrp = (ARCstRptControl)thistran;
ARCstRptControl1 destgrp = (ARCstRptControl1)thistran;
PMTask pmtask = (PMTask)thistran;
Account account = (Account)thistran;
ARCstRptControl2 destgrp2 = (ARCstRptControl2)thistran;
All the table / DAC variables have the expected results, except ARCstRptControl2. I've moved this entire query into SQL Server and it gives me the results I want, including ARCstRptControl2 - but I must be doing something wrong with the join here in the BQL. Any ideas?
Since I'm using aliased DACs (ARCstRptControl1 and 2), and since I'm using two fields from those aliased DACs, I needed to have those fields (and any others that would be used) from that alias declared in the DAC declaration, as follows (i.e., I'm using accountID and columnID in the join):
[Serializable]
public class ARCstRptControl1 : ARCstRptControl
{
public abstract new class accountID : IBqlField { }
public abstract new class columnID : IBqlField { }
}
Related
I have an inquiry screen in which we filter historical AP records from a custom DAC by a VendorID or FinPeriodID from the filter.
I've created a View Delegate to handle the dynamic filtering logic, and through debugging have confirmed that it's being hit correctly and is appending the proper WhereAnd statement with the current value of the Filter. Running the SQL statement equivalent directly in the database is returning the correct records, however the View Delegate ends up returning no records to the screen.
The base view is just defined as: public PXSelectReadonly<AAAPDoc> Docs;
The View Delegate is defined as:
protected virtual IEnumerable<AAAPDoc> docs()
{
AAAPHistoricalFilter filter = Filter.Current;
PXSelectBase<AAAPDoc> cmd = new PXSelectReadonly<AAAPDoc>(this);
if (filter.VendorID.HasValue)
{
cmd.WhereAnd<Where<AAAPDoc.vendorID, Equal<Current<AAAPHistoricalFilter.vendorID>>>>();
}
if (filter.FinPeriodID.HasValue)
{
cmd.WhereAnd<Where<AAAPDoc.finPeriodID, Equal<Current<AAAPHistoricalFilter.finPeriodID>>>>();
}
foreach (AAAPDoc record in cmd.Select())
{
yield return record;
}
}
Filter DAC
[Serializable]
[PXHidden]
public class AAAPHistoricalFilter : IBqlTable
{
#region VendorID
public abstract class vendorID : BqlInt.Field<vendorID>
{
}
[Vendor(IsDBField = false, DisplayName = "Vendor ID")]
public virtual int? VendorID { get; set; }
#endregion
...
Edit: Updated with original partial Filter DAC to give context to solution
Turns out, it was a problem with my filter DAC. I used the [Vendor] attribute with the property IsDBField = false. Removing the IsDbField property altogether from the attribute gave me the expected results.
I have a requirement to write a BQL statement for below SQL query
SELECT * FROM CSATTRIBUTEGROUP INNER JOIN INVENTORYITEM ON CSATTRIBUTEGROUP.ENTITYCLASSID=CAST(INVENTORYITEM.ITEMCLASSID AS NVARCHAR(10))
LEFT JOIN CSANSWERS ON INVENTORYITEM.NOTEID=CSANSWERS.REFNOTEID WHERE INVENTORYCD='CPU'
The EntityClassID in CSAttribute & ItemClassID in inventory are different type. How to join the table using BQL.
One would be tempted to create a custom view representing the INVENTORYITEM table with a cast of ITEMCLASSID. However custom views are not recommended. Instead try creating a PXProjection to represent the INVENTORYITEM table as usrINVENTORYITEM. Then use a PXString to convert the Int? to a string. Once exposed you can work out your BQL as shown below.
#region InventoryItem - projection
[PXProjection(typeof(Select<InventoryItem>), Persistent = false)]
public partial class usrInventoryItem : InventoryItem
{
#region ItemClassIDStr
[PXString(10, IsUnicode = true)]
[PXUIField(DisplayName = "ItemClassIDStr", Visibility = PXUIVisibility.SelectorVisible)]
public virtual string ItemClassIDStr
{
get
{
return $"{ItemClassID}";
}
set
{
this.ItemClassIDStr = value;
}
}
public abstract class itemClassIDStr : IBqlField { }
#endregion
}
#endregion
then the BQL statement:
// basic example of your join.
public PXSelectJoin<CSAttributeGroup, LeftJoin<usrInventoryItem,
On<usrInventoryItem.itemClassIDStr, Equal<CSAttributeGroup.entityClassID>>>> ExampleJoin;
I have a selector defined as follows:
[PXSelector(typeof(Search2<xTACTaxDocument.iD,
InnerJoin<xTACEntityMappingEIN,
On<xTACTaxDocument.clientEINID, Equal<xTACEntityMappingEIN.iD>>,
InnerJoin<xTACEntityMappingEIN1,
On<xTACTaxDocument.investmentEINID, Equal<xTACEntityMappingEIN1.iD>>>>>),
typeof(xTACTaxDocument.iD),
typeof(xTACTaxDocument.formID),
typeof(xTACTaxDocument.year),
typeof(xTACEntityMappingEIN.eIN),
typeof(xTACEntityMappingEIN1.eIN))]
Where I define an alias DAC as follows(redefining the fields I need to use) :
[Serializable]
public class xTACEntityMappingEIN1 : xTACEntityMappingEIN
{
public abstract new class iD : IBqlField { }
public abstract new class eIN : IBqlField { }
}
My question is - since the original ein and aliased DAC ein fields have the same name - is it possible - purely in the displayed grid - to rename the second one? Or, ideally, rename both of them? Didn't see that as an option anywhere in the intellisense...
This is kind of what I'm looking to do (see the aliased fields):
select xTACTaxDocument.iD
,xTACTaxDocument.FormID
,xTACTaxDocument.Year
,xTACEntityMappingEIN.EIN as 'ClientEIN'
,xTACEntityMappingEIN1.EIN as 'InvestmentEIN'
from xTACTaxDocument
Inner Join xTACEntityMappingEIN
On xTACTaxDocument.clientEINID = xTACEntityMappingEIN.iD
Inner Join xTACEntityMappingEIN xTACEntityMappingEIN1
On xTACTaxDocument.investmentEINID = xTACEntityMappingEIN1.iD
The only option would be to additionally override the EIN property in the xTACEntityMappingEIN1 DAC to use a different DisplayName in PXUIFieldAttribute:
[Serializable]
public class xTACEntityMappingEIN1 : xTACEntityMappingEIN
{
public abstract new class iD : IBqlField { }
public abstract new class eIN : IBqlField { }
[PXDBString(50, IsUnicode = true, IsKey = true)]
[PXUIField(DisplayName = "Investment EIN")]
public override string EIN { get; set; }
}
Please note, in the code snippet above I randomly chose string type for the EIN field. Ideally EIN field attributes should be close to identical in both xTACEntityMappingEIN and xTACEntityMappingEIN1, except the DisplayName property value for PXUIFieldAttribute.
The DAC Names need to be unique to "alias" a table. You cannot set an alias like you might use in SQL, but you can declare a new class inheriting the source class to give it a new "name" for the query. I had a similar Q&A here: Acumatica BQL Query with the same table more than once
In the inherited class you can change the display name of the fields as needed to "alias" a field name that repeats.
Here is a quick untested sample:
[Serializable]
public class xTACEntityMappingEINClient : xTACEntityMappingEIN
{
//Override field to set display name = "ClientEIN"
//[PXUIField(DisplayName = "ClientEIN")]
}
[Serializable]
public class xTACEntityMappingEINInvestment : xTACEntityMappingEIN
{
//Override field to set display name = "InvestmentEIN"
//[PXUIField(DisplayName = "InvestmentEIN")]
}
[PXSelector(typeof(Search2<xTACTaxDocument.iD,
InnerJoin<xTACEntityMappingEINClient,
On<xTACTaxDocument.clientEINID, Equal<xTACEntityMappingEINClient.iD>>,
InnerJoin<xTACEntityMappingEINInvestment ,
On<xTACTaxDocument.investmentEINID, Equal<xTACEntityMappingEINInvestment.iD>>>>>),
typeof(xTACTaxDocument.iD),
typeof(xTACTaxDocument.formID),
typeof(xTACTaxDocument.year),
typeof(xTACEntityMappingEINClient.eIN),
typeof(xTACEntityMappingEINInvestment .eIN))]
Hi I am having trouble some BQL syntax what I want to achieve is a BQL statement like the below SQL with nested sub query in the where clause.
SELECT * FROM ARInvoice I
WHERE (SELECT COUNT(*) FROM ARAdjust A WHERE I.RefNbr = A.AdjdRefNbr) > 0
Is this possible in BQL if so how would I write this?
Below is what I have got at the moment but this isn't correct I'm getting syntax errors
PXSelect<PX.Objects.AR.ARInvoice,
Where<PXSelectGroupBy<PX.Objects.AR.ARAdjust, Where<PX.Objects.AR.ARAdjust.adjdRefNbr, Equal<PX.Objects.AR.ARInvoice.refNbr>, Aggregate<Count>>, Greater<Zero>>>>.Select(new PXGraph());
thanks
You have two options to implement this.
Using Sub Queries:
You could add an unbound calculated field (PXDBScalar) in your ARInvoice
To add Sub Queries in BQL, you must do it at the the attribute level. Because you want to query another table, PXDBScalar, would be the best option. If you wanted to query other fields of the same record, PXDBCalced would be more adequate. For more informations about Advanced SQL Attributes please refer to the T200 under Using Advanced SQL Attributes and Acumatica’s help under Help -> Acumatica Framework -> API Reference -> Attributes -> Adhoc SQL for Fields.
Extend the ARInvoice (V5.1 and below)
public class ARInvoiceExtension : PXCacheExtension<ARInvoice>
{
public abstract class lastPaymentOrderNbr : IBqlField
{
}
#region LastPaymentOrderNbr
[PXString]
[PXUIField(DisplayName = "Last Payment Order Nbr.")]
[PXDBScalar(typeof(Search<ARAdjust.adjdOrderNbr,
Where<ARAdjust.adjdDocType, Equal<ARInvoice.docType>,
And<ARAdjust.adjdRefNbr, Equal<ARInvoice.refNbr>>>,
OrderBy<Desc<ARAdjust.adjgDocDate>>>))]
public string LastPaymentOrderNbr { get; set; }
#endregion
}
A new field has been added to ARInvoice in V5.2 to get the last payment date so you don't have to add another one:
public abstract class lastPaymentDate : PX.Data.IBqlField
{
}
protected DateTime? _LastPaymentDate;
/// <summary>
/// The date of the most recent payment associated with this document.
/// </summary>
[PXDate()]
[PXDBScalar(typeof(Search<ARAdjust.adjgDocDate,
Where<ARAdjust.adjdDocType, Equal<ARInvoice.docType>,
And<ARAdjust.adjdRefNbr , Equal<ARInvoice.refNbr>>>,
OrderBy<Desc<ARAdjust.adjgDocDate>>>))]
[PXUIField(DisplayName = "Last Payment Date")]
public virtual DateTime? LastPaymentDate
{
get
{
return this._LastPaymentDate;
}
set
{
this._LastPaymentDate = value;
}
}
Your PXSelect would then look like this:
V5.1 and below
public PXSelect<ARInvoice, Where<ARInvoiceExtension.lastPaymentOrderNbr, IsNotNull>> InvoicesTest;
V5.2
public PXSelect<ARInvoice, Where<ARInvoice.lastPaymentDate, IsNotNull>> InvoicesTest;
Inner join on table
Instead of sub-querying it you could simply add an inner join and filter record that do not have an ARAdjust. You then group by your key fields to avoid duplicates.
public PXSelectJoinGroupBy<ARInvoice,
InnerJoin<ARAdjust, On<ARAdjust.adjdRefNbr, Equal<ARInvoice.refNbr>,
And<ARAdjust.adjdDocType, Equal<ARInvoice.docType>>>>,
Where<ARAdjust.adjdOrderNbr, IsNotNull>,
Aggregate<GroupBy<ARInvoice.docType,
GroupBy<ARInvoice.refNbr>>>> InvoicesTest;
I have a WinTree object which consists of three WinTreeItem objects. The problem I have is that I need to parameterize the first WinTreeItem(root) so I can select 1st, 2nd, or 3rd WinTreeItem.
Here is the code where I have the WinTree object which has the WitTreeItem name as a property.
public class UITree1Tree : WinTree
{
public UITree1Tree(UITestControl searchLimitContainer) :
base(searchLimitContainer)
{
#region Search Criteria
this.SearchProperties[WinTree.PropertyNames.Name] = "Tree Lists:";
this.WindowTitles.Add("Insert Symbol List");
#endregion
}
#region Properties
public UITrSymbolLiTreeItem UITradeStationSymbolLiTreeItem
{
get
{
if ((this.mUITrSymbolLiTreeItem == null))
{
this.mUITrSymbolLiTreeItem = new UITrSymbolLiTreeItem(this);
}
return this.mUITrSymbolLiTreeItem;
}
}
#endregion
#region Fields
private UITrSymbolLiTreeItem mUITrSymbolLiTreeItem;
#endregion
}
public class UITrSymbolLiTreeItem: WinTreeItem
{
public UITrSymbolLiTreeItem (UITestControl searchLimitContainer) :
base(searchLimitContainer)
{
#region Search Criteria
this.SearchProperties[WinTreeItem.PropertyNames.Name] = "Tr Symbol Lists";
this.SearchProperties["Value"] = "0";
this.WindowTitles.Add("Insert Tr List");
#endregion
}
}
You can use the Constructor of the class to pass the parameter.
OR
Add a Property to its parent and set this Property while initializing. The Parent testcontrol will be available inside child and hence your parameter also available. You can use this parameter inside the child wherever you want.
I did in this way through out my project and it works fine.