Passing a range table to an ABAP class/method causes a commit - methods

I want to pass a range table for OBJNR, e.g.
call method ME->ME_GET_STATUS
exporting
I_OBJNR = <FS_DATA>-OBJNR_NTF
I_AEDAT = LV_AEDAT
I_AEZEIT = LV_AEZEIT
IT_OBJNR = LR_OBJNR
importing ...
In the PUBLIC section of the super class I have:
RT_OBJNR type range of JSTO-OBJNR . (this is inherited by the calling class)
which is used by both calling & called methods.
The method ME_GET_STATUS has a parameter:
IT_OBJNR Importing Type RT_OBJNR Range table for OBJNR
and code
,WA_OBJNR like line of IT_OBJNR
,LR_OBJNR type RT_OBJNR
LR_OBJNR[] = IT_OBJNR[].
The range table is only used to buffer an itab - LT_JCDS.
select S~OBJNR, S~STAT, S~CHGNR, S~UDATE, S~UTIME,
S~INACT, O~OBTYP, O~STSMA
from JCDS as S
join JSTO as O
on O~OBJNR = S~OBJNR
into table #IT_JCDS
where S~OBJNR in #LR_OBJNR
order by S~OBJNR, S~UDATE, S~UTIME, S~STAT, S~CHGNR
loop at IT_JCDS into data(LT_JCDS)
where OBJNR = I_OBJNR
group by ( OBJNR = LT_JCDS-OBJNR STAT = LT_JCDS-STAT
GS = group size GI = group index )
ascending
reference into data(OBJNR_REF)
It all works perfectly if there is just 1 record in the range table.
The issue is that if I pass more than 1 record it still works fine but seems to cause a commit (?) which closes the cursor causing a dump in MCEX_BW_LO_API. This occurs when calling the macro "sel" for the 2nd data package.
The idea is to pass multiple records of 'EQ' and 'BT' selections resulting in fewer records being returned from the database.
I've tried changing to a standard table and using = LR_OBJNR[]

Related

Sorting in an cl_gui_alv_grid by column?

I'm generating an internal table with cl_gui_alv_grid that has five columns and I can't seem to figure out how to sort by column 1 once the table is loaded. I managed to do it with cl_salv_table, however not with the grid. Any idea please? I suppose it's done somehow with the CALL METHOD go_alv->set_sort_criteria however I'm getting a crash when triggering the table view.
DATA: lt_sort TYPE lvc_t_sort,
ls_sort TYPE lvc_s_sort.
FORM sort_data.
REFRESH: lt_sort.
CLEAR: ls_sort.
ls_sort-spos = '1'.
ls_sort-fieldname = 'Column1'.
ls_sort-up = abap_true.
APPEND ls_sort TO lt_sort.
ENDFORM.
FORM first_display.
PERFORM sort_data.
CALL METHOD go_alv->set_table_for_first_display
EXPORTING
i_structure_name = 'TABLE_STRUCTURE'
is_layout = gs_layout
CHANGING
it_outtab = gt_salv_table
ENDFORM.
CALL METHOD go_alv->set_sort_criteria
EXPORTING
it_sort = lt_sort
* EXCEPTIONS
* no_fieldcatalog_available = 1
* others = 2.
Do you call the set_sort_criteria before the set_table_for_first_display? It looks like there is no fieldcatalouge yet, this is why it does not work.
I think more straighforward is: The set_table_for_first_display method has a changing parameter it_sort, just use that one for importing the sort table into the class, no need to call set_sort_criteria:
CALL METHOD go_alv->set_table_for_first_display
EXPORTING
i_structure_name = 'TABLE_STRUCTURE'
is_layout = gs_layout
CHANGING
it_outtab = gt_salv_table
it_sort = lt_sort
EXCEPTIONS
...

Oracle: Update from within procedure not working

In my Oracle PL/SQL procedure I am trying to update a row like this:
UPDATE personal p
SET p.surname = surname, p.name = name, p."alter" = alter, p.sex = sex, p.jobcode = jobcode, p.year_wage = month_wage * 12
WHERE p.personalnr = personalnr;
COMMIT;
I have added these two statements right after the commit to confirm the code is reached and executed with the right arguments (e.g. here I want to change the name):
DBMS_OUTPUT.put_line('updated ' || name);
DBMS_OUTPUT.put_line('personalnr ' || personalnr);
Now this update-statement is part of a procedure that is called from within another procedure.
However, the changes are not applied and the name will remain the same even tho the update was executed. I have tried to use an exception-handler as well and there doesn't seem to be any exception happening. I can confirm that the WHERE-clause is as intendet. There is one record that matches the predicate.
Now the strange thing:
When I change the code to the example below, an update happens. However it updates every record and not only the one with the right personalnr. Again: the routine is called only once with one personalnr that matches only one entry in the table.
UPDATE personal p
SET p.name = 'test'
WHERE p.personalnr = personalnr;
COMMIT;
It is working, but it's updating all rows in the table (or at least, those where personalnr is not null), not just the one you expect.
From the documentation:
If a SQL statement references a name that belongs to both a column and either a local variable or formal parameter, then the column name takes precedence.
You have a PL/SQL variable that has the same name as a column. When you do
where p.personalnr = personalnr
you are really doing:
where p.personalnr = p.personalnr
and the same thing happens in the set part; SET p.surname = surname updates the column value to whatever value it had before, not the PL/SQL variable's value. So it looks like the update didn't happen- it actually did, but because everything was set to the same as it's original value it doesn't look like anything happened. (Except - all rows will now have the same year_wage value...)
You can either prefix your variables with the procedure name:
where p.personalnr = my_proc.personalnr
or change the variable names so they don't conflict; it's common to use a short prefix, e.g. l_ for a local variable, or p_ for a passed-in parameter, etc.
where p.personalnr = l_personalnr
Remember to do that for the set part too, or your update still won't appear to do anything.
UPDATE personal p
SET p.surname = l_surname, p.name = l_name, p."alter" = l_alter,
p.sex = l_sex, p.jobcode = l_jobcode, p.year_wage = l_month_wage * 12
WHERE p.personalnr = l_personalnr;
You need to change the parameter name something other than the table's column name.
UPDATE personal p
SET p.name = 'test'
WHERE p.personalnr = personally;
-- here condition is column_name = column_name
-- This will be true for all the records of the table
Change personalnr --> p_personalnr and it will work for you

LINQ select column(s) of table before doing multiple joins

After running the query below and hovering over "usersToWork" when debugging, I can view all of the properties of the single entry that I get returned to me in addition to the other tables that have relations to this value. What I need to display to the user is the "Lines.Id" (Lines being the table and Id being the column in the Lines table) value, however that value gets lost from the SelectMany() statements. Is there anyway to select that "Lines.Id" value to include in the final value that I get from all of my joins? In the code below, I commented out what I want but I can't place that there otherwise I get error on the first SelectMany statement saying 'int' does not contain a definition for 'Shifts' and no extension method 'Shifts' accepting a first argument of type 'int' could be found.'
Correct me if I'm wrong but SelectMany() selects all of the columns from what you want to join on. In this case, in the first SelectMany() I get only values from the "Shifts" table and in the second SelectMany() I get only values from the "Users" table. Why is this different from the SQL join? When joining in SQL you can get every column as you join them together, SelectMany() yields only the values of the second table that you are joining on. Is it even possible to get that value in the "Lines" table or will I have to do another query? Any help would be great.
int idEnteredByUser = 123;
var usersToWork = entityDataModel.Lines
//....NOT IN MY CODE NOW....
// .Select(line => line.Id)//THIS IS WHAT I NEED.
// .Select(line => line.Description, line.Id//OR THIS TO RETURN TWO VALUES IF POSSIBLE
//This is my current code, I need to include on of the select lines above.
.SelectMany(line => line.Shifts) //Join lines on shifts.
.Where(shift => shift.EndTime >= DateTime.Now) //Join restricted times.
.SelectMany(user => user.Users) //Join the restricted shift times on users.
.Where(user => user.UserId == idEnteredByUser ); //Only look for the specific user
This works much easier using LINQ query syntax.
I'm assuming that you made a typo in your posted code and that user is a property of shift.
var idEnteredByUser = 123;
var usersToWork =
from line in entityDataModel.Lines
from shift in line.Shifts
where shift.EndTime >= DateTime.Now
from user in shift.Users
where user.UserId == idEnteredByUser
select new
{
Description = line.Description,
Id = line.Id
};

Unable to create a constant value - only primitive types or Enumeration types allowed

I have seen some questions related to this Exception here but none made me understand the root cause of the problem. So here we have one more...
var testquery =
((from le in context.LoanEMIs.Include("LoanPmnt")
join lp in context.LoanPmnts on le.Id equals lp.LoanEMIId
where lp.PmntDtTm < date && lp.IsPaid == false
&& le.IsActive == true && lp.Amount > 0
select new ObjGetAllPendingPmntDetails
{
Id = lp.Id,
Table = "LoanEMI",
loanEMIId = lp.LoanEMIId,
Name = le.AcHead,
Ref = SqlFunctions.StringConvert((double)le.FreqId),
PmntDtTm = lp.PmntDtTm,
Amount = lp.Amount,
IsDiscard = lp.IsDiscarded,
DiscardRemarks = lp.DiscardRemarks
}).DefaultIfEmpty(ObjNull));
List<ObjGetAllPendingPmntDetails> test = testquery.ToList();
This query gives the following Exception Message -
Unable to create a constant value of type CashVitae.ObjGetAllPendingPmntDetails. Only primitive types or enumeration types are supported in this context.
I got this Exception after I added the SQL function statement to convert le.FreqId which is a byte to a string as ToString() is not recognized in the LINQ Expression Store.
ObjGetAllPendingPmntDetails is a partial class in my model which is added as it is used too many times in the code to bind data to tables.
It has both IDs as long, 'Amount' as decimal, PmntDtTm as Datetime,IsDiscard as bool and remaining all are string including 'Ref'.
I get no results as currently no data satisfies the condition. While trying to handle null, I added DefaultIfEmpty(ObjNull) and ObjNull has all properties initialized as follows.
ObjGetAllPendingPmntDetails ObjNull = new ObjGetAllPendingPmntDetails()
{ Id = 0, Table = "-", loanEMIId = 0, Name = "-", Ref = "-",
PmntDtTm = Convert.ToDateTime("01-01-1900"),
Amount = 0, IsDiscard = false, DiscardRemarks = "" };
I need this query to work fine as it has Union() called on it with 5 other queries. All returning the same ObjGetAllPendingPmntDetails columns. But there is some problem as this query has no data satisfying the conditions and the Exception Shared Above.
Any suggestions are appreciated as I am unable to understand the root cause of the problem.
#AndrewCoonce is right, the .DefaultIfEmpty(ObjNull) is the culprit here. Entity Framework turns DefaultIfEmpty into something like...
CASE WHEN ([Project1].[C1] IS NULL) THEN #param ELSE [Project1].[Value] END AS [C1]
...but there's no way to coerce an instance of ObjGetAllPendingPmntDetails into something that can take the place of #param, so you get an exception.
If you move the DefaultIfEmpty call to after the ToList it should work correctly (although you'll need to call ToList again after that if you really want a concrete list instance).

Lotus Notes - multiple IF formula statements

I'm new in Lotus Notes programming and I need your advices and your help.
My main form contains a table with 8 rows and 2 columns. ( there are 16 cells ) Every each cell has a numeric field. My fields name are :
txt_n1 and txt_i1 ( for 1st row )
txt_n2 and txt_i2 ( for 2nd row )
....
txt_n8 and txt_i8 ( for 8th row )
What I want to do is:
I have a view called vwMarketing with just one column. I want this view to display only those docs. in which there is at least one or more rows which its cells contains equal values.
So, if let say txt_n4 = txt_i4 => OK
row(k) (where k=1..8) : if cell 1 value is 5 and cell 2 value is 5 => OK.
There could be more than one row with this property, important is to exist at least one, and the values not to be null.
I hoped i was pretty clear, thanks !
PS: Actually, the formula statement i want to be in the column, so if it is OK => "A" and if not => "" ( in the view property, I checked : Don't show empty categories )
if you have small amount of documents in the view, then as u were suggested use Selection Formula to exclude documents with wrong condition.
you can add computed item/flag into your documents, field will compute if the document should be displayed in the view or not. then you will not have performance issue. i.e.
but code you need should look like that (that will check if documents is fine to be displayed in view or not), if you use it in view - just put after all Select _res = 1 otherwise if you decide to use flag into document (to increase performance) then Select youritem = 1
_res := 0;
#For(i:=1;i<=8;i:=i+1;
_post := #Text(i);
_txt_n := #GetField("txt_n"+_post);
_txt_i := #Text(#GetField("txt_i"+_post));
#If( (_txt_n=_txt_i) & (_txt_n!="");
#Do( _res := 1; i:=9);
0
)
);
_res
I would solve it slightly different:
Create a hidden text field on your form called 'DisplayInView' (or similar).
Modify the view selection: SELECT DisplayInView="Yes"
Add the code below to the PostSave event of your form:
Dim thisdoc As NotesDocument
Dim isSame As Boolean
isSame = False
Set thisdoc = source.Document
'*** Loop through all fields in document and compare the field pairs
Forall i In thisdoc.Items
If Left(i.Name,5) = "txt_n" Then
If i.Text = thisdoc.GetItemValue( Replace(i.Name,"txt_n","txt_i") )(0) Then
isSame = True
Exit Forall
End If
End If
End Forall
If isSame Then
Call doc.ReplaceItemValue("DisplayInView","Yes")
Call doc.Save(True,False)
End If
I haven't tested it, but I believe it should work.

Resources