How to use correctly IN filter in Acumatica Report Designer? - acumatica

A bit new in Acumatica, I've been tasked to update a report which it in on itself is pretty easy, just adding a new value to a filter.
The part of the query I'm modifying is:
( [INRegister].[TransferType] = '1' AND [INTran].[TranDate] BETWEEN CONVERT(DATETIME, '20220101 0:0:0.000') AND CONVERT(DATETIME, '20230112 0:0:0.000') AND ( [INRegister].[DocType] = 'I' OR [INRegister].[DocType] = 'A') AND ( [INTran].[ReasonCode] = 'Null' OR [INTran].[ReasonCode] = 'BAJACADUCA' OR [INTran].[ReasonCode] = 'INISSUED' OR [INTran].[ReasonCode] = 'Null') AND ( [InventoryItem].[PreferredVendorID] = NULL OR NULL IS NULL ))
ORDER BY [InventoryItem].[ItemClassID], [InventoryItem].[PreferredVendorID], [INTran].[TranDate] OPTION(OPTIMIZE FOR UNKNOWN)
I need to use In when ReasonCode is INISSUED because it should have another value (BAJAMERCADERIA) too but everytime I try to use the In filter the second value is ignored or truncated when I check the trace.

The second value is primarily used for the between clause. Have you tried a comma delimited list in value1? i.e. BAJAMERCADERIA,INISSUED

Related

Problems with the BQL "IN<>" statement

The requirement I have is to get a list of all discount codes defined in an instance and which ones a particular customer is currently assigned to, in the case given CustomerID=28. I further have to include only discount codes that naturally will be applicable to customers. There are only 3 of these; "Customer", "Customer and Item", "Customer and Item price Class". These are ARDiscount.ApplicableTo containing "CU", "CP","CI"
Select a.CompanyID, a.DiscountID, a.DiscountSequenceID, b.ApplicableTo, c.CustomerID
From DiscountSequence a
Join ARDiscount b On a.CompanyID = b.CompanyID and a.DiscountID = b.DiscountID
Left Outer Join DiscountCustomer c On a.CompanyID = c.CompanyID
And a.DiscountID = c.DiscountID
And a.DiscountSequenceID = c.DiscountSequenceID
And (IsNull(c.CustomerID,0) = 0 OR c.CustomerID = 72)
Where a.CompanyID = 2
And b.ApplicableTo In ('CU','CP','CI')
Order By a.DiscountID, a.DiscountSequenceID
I created data view delegate to return the 4 columns I need to display and in the view I created
to read the data like the SQL query above I used the BQL "IN<>" statement like this. The method was taken directlty from a blog post found here :
https://asiablog.acumatica.com/2017/11/sql-in-operator-in-bql.html
Object[] applicableTovalues = new String[] { "CP","CI","CU" }; // Customer and Price Class // Customer and Item// Customer
var Results = PXSelectJoin<DiscountSequence
, InnerJoin<ARDiscount, On<DiscountSequence.discountID, Equal<ARDiscount.discountID>>
, LeftJoin<DiscountCustomer, On<DiscountSequence.discountID, Equal<DiscountCustomer.discountID>,
And<DiscountSequence.discountSequenceID, Equal<DiscountCustomer.discountSequenceID>,
And<Where<DiscountCustomer.customerID, Equal<Current<Customer.bAccountID>>,
Or<DiscountCustomer.customerID, IsNull>>>>>>>
, Where<DiscountSequence.discountID, IsNotNull
, And<ARDiscount.applicableTo, In<Required<ARDiscount.applicableTo>>>>
, OrderBy<Asc<DiscountSequence.discountID, Asc<DiscountSequence.discountSequenceID>>>
>.Select(Base, applicableTovalues);
The problem is that the resulting SQL server select statement caught with TRACE only includes the first of the three IN values (''CU'') leaving (CI and CU) out.
I was expecting all three values in the IN statement like this : AND [ARDiscount].[ApplicableTo] IN ( ''CP'', ''CI'', ''CU'')
exec sp_executesql N'SELECT [DiscountSequence].[DiscountID], [DiscountSequence].[DiscountSequenceID], [DiscountSequence].[LineCntr],
<snip>
[DiscountCustomer].[CreatedDateTime], [DiscountCustomer].[LastModifiedByID], [DiscountCustomer].[LastModifiedByScreenID], [DiscountCustomer].[LastModifiedDateTime]
FROM [DiscountSequence] [DiscountSequence] INNER JOIN [ARDiscount] [ARDiscount] ON ( [ARDiscount].[CompanyID] = 2) AND [DiscountSequence].[DiscountID] = [ARDiscount].[DiscountID]
LEFT JOIN [DiscountCustomer] [DiscountCustomer] ON ( [DiscountCustomer].[CompanyID] = 2) AND [DiscountSequence].[DiscountID] = [DiscountCustomer].[DiscountID]
AND [DiscountSequence].[DiscountSequenceID] = [DiscountCustomer].[DiscountSequenceID] AND ( [DiscountCustomer].[CustomerID] = #P0 OR [DiscountCustomer].[CustomerID] IS NULL )
WHERE ( [DiscountSequence].[CompanyID] = 2)
AND ( [DiscountSequence].[DiscountID] IS NOT NULL
AND [ARDiscount].[ApplicableTo] IN ( ''CU''))
ORDER BY [DiscountSequence].[DiscountID], [DiscountSequence].[DiscountSequenceID]
OPTION(OPTIMIZE FOR UNKNOWN) /* AR.30.30.00 */',N'#P0 int',#P0=39
The issue is passing the array into the 'params' parameter. It thinks that you are passing a list of parameters into the bql query instead of a single array as a parameter.
If you cast it as follows it should work:
.Select(Base, (object)applicableTovalues);

Use coalesce in a subquery while creating a view

I am currently trying to make a view in order to show the name of the clients that have purchased a car (new or used) and need to show "NA" when the client name is NULL.
IF OBJECT_ID ('V_PrixSuperieurMoyenne','V') IS NOT NULL
DROP VIEW V_PrixSuperieurMoyenne;
GO
CREATE VIEW V_PrixSuperieurMoyenne
AS
(
SELECT
CS.[State],
CI.ListPrice,
CI.CarInventoryID,
COALESCE(CONCAT(C.Name, ' ', C.FirstName), 'NA') AS 'Nom du Client',
(
SELECT AVG(CI.ListPrice)
FROM CarInventory CI
INNER JOIN CarState CS ON CS.CarStateID = CI.CarStateID
WHERE CS.State = 'New'
) AS 'Prix Moyen Neuf'
FROM
CarInventory CI
INNER JOIN CarState CS ON CS.CarStateID = CI.CarStateID
LEFT JOIN Invoice I ON I.CarInventoryID = CI.CarInventoryID
LEFT JOIN Client C ON C.ClientID = I.ClientID
GROUP BY
CS.State,
CI.ListPrice,
CI.CarInventoryID,
CONCAT(C.Name , ' ', C.FirstName)
)
GO
SELECT *
FROM V_PrixSuperieurMoyenne
ORDER BY 1,2
GO
Here is my code, however it seems like the COALESCE doesn't work as when I try to run the code, when the names are NULL, it only shows an empty string. I need it to show 'NA'.
The CONCAT function will return ' ' when C.Name and C.FirstName are NULL. However, if you replace CONCAT with string additions, you will get NULL as you expect, because a single NULL value in a line of string additions nulls out the entire string.
-- Returns 'NA' when C.Name or C.FirstName are NULL.
COALESCE(C.Name + ' ' + C.FirstName,'NA')
You could also replace COALESCE with ISNULL since you're only checking nullity of a single item:
ISNULL(C.Name +' ' + C.FirstName,'NA')
Be sure to make this change in your GROUP BY, too.
COALESCE is typically used when you want to check whether multiple items are NULL.

Nested subquery in FOR ALL ENTRIES

Consultant sent me this code example, here is something he expects to get
SELECT m1~vbeln_im m1~vbelp_im m1~mblnr smbln
INTO CORRESPONDING FIELDS OF TABLE lt_mseg
FROM mseg AS m1
INNER JOIN mseg AS m2 ON m1~mblnr = m2~smbln
AND m1~mjahr = m2~sjahr
AND m1~zeile = m2~smblp
FOR ALL ENTRIES IN lt_vbfa
WHERE
AND m2~bwart = '102'
AND 0 = ( select SUM( ( CASE
when SHKZG = 'S' THEN 1
when SHKZG = 'H' THEN -1
else 0
END ) *MENGE ) MENGE
into lt_mseg-summ
from mseg
where
VBELN_IM = m1~vbeln_im
and VBELP_IM = m1~vbelp_im
).
The problem is I don't see how that should work in current syntax. I think about deriving internal select and using it as condition to main one, but is there a proper way to write this nested construction?
As i get it, if nested statement = 0, then main query executes. The problem here is the case inside nested statement. Is it even possible in ABAP? And in my opinion this check could be used outside from main SQL query.
Any suggestions are welcome.
the logic that you were given is part of Native/Open SQL and has some shortcomings that you need to be aware of.
the statement you are showing has to be placed between EXEC SQL and ENDEXEC.
the logic is platform dependent.
there is no syntax checking performed between the EXEC and ENDEXEC
the execution of this bypasses the database buffering process, so its slower
To me, I would investigate a better way to capture the data that performs better outside of open/native sql.
If you want to move forward with this type of logic, below are a couple of links which should be helpful. There is an example select using a nested select with a case statement.
Test Program
Example Logic
This is probably what you need, it works at least since ABAP 750.
SELECT vbeln UP TO 100 ROWS
FROM vbfa
INTO TABLE #DATA(lt_vbfa).
DATA(rt_vbeln) = VALUE range_vbeln_va_tab( FOR GROUPS val OF <line> IN lt_vbfa GROUP BY ( low = <line>-vbeln ) WITHOUT MEMBERS ( sign = 'I' option = 'EQ' low = val-low ) ).
SELECT m1~vbeln_im, m1~vbelp_im, m1~mblnr, m2~smbln
INTO TABLE #DATA(lt_mseg)
FROM mseg AS m1
JOIN mseg AS m2
ON m1~mblnr = m2~smbln
AND m1~mjahr = m2~sjahr
AND m1~zeile = m2~smblp
WHERE m2~bwart = '102'
AND m1~vbeln_im IN ( SELECT vbelv FROM vbfa WHERE vbelv IN #rt_vbeln )
GROUP BY m1~vbeln_im, m1~vbelp_im, m1~mblnr, m2~smbln
HAVING SUM( CASE m1~shkzg WHEN 'H' THEN 1 WHEN 'S' THEN -1 ELSE 0 END * m1~menge ) = 0.
Yes, aggregating and FOR ALL ENTRIES is impossible in one SELECT, but you can trick the system with range and subquery. Also you don't need three joins for summarizing reversed docs, your SUM subquery is redundant here.
If you need to select documents not only by delivery number but also by position this will be more complicated for sure.

How do I configure the sort order of results returned by DBSet local?

I want to be able to configure the sort order of a binding source based on DBSet<> local
By passing the number of a field in the resultant query.
i.e order by the nth field in the query.
I am guessing I would be needing to use the .OrderBy() method, but dont know what to pass into it.
I know that I need
Here is the code that sets up the binding source.
var dset = base.Context.Organisations;
if (QuickSearch == null) QuickSearch = "";
var qry = dset.Where(p => p.Name.Contains(QuickSearch));
qry.Load();
bindingSource.DataSource = dset.Local.ToBindingList();
The following code may help-
_bindingSource.Sort = "ColumnName ASC";
OR
_bindingSource.Sort = "ColumnName DESC";
OR
_bindingSource.Sort = "ColumnName1 ASC, ColumnName2 DESC";
and so on......

Creating an associative array using a while loop for select list?

I am dynamically generating a selectlist in Drupal and I want to create an associative array to populate the node ID as the value, and the title of the node as the option.
By default, it makes the value for each option the index of the select list. This is no good because the select list is dynamic, meaning the values won't be in the same order.
I used drupal_map_assoc to make the value the same as the option, but I have queries based on the value stored in this field, so if someone updates the value stored, the queries won't match.
<option value="Addison Reserve Country Club Inc.">Addison Reserve Country Club Inc.</option>
I want to replace the value with the Node ID also pulled with the query.
$sql = 'SELECT DISTINCT title, nid FROM {node} WHERE type = \'accounts\' ';
$result = db_query($sql);
while ($row = db_fetch_array($result)) {
$return[] = $row['title'];
//Trying to do something like 'Addison Reserve Country Club' => '2033' - where 2033 is the nid
}
$return = drupal_map_assoc($return);
I think you just want to do this inside the loop:
$return[$row['nid']] = $row['title'];
Based on your comment, you would also want to do an array_flip() right after the loop, but I think your comment may just have it backwards.

Resources