I need to read automatically-created variants, to get the selection screen parameters and other selection criteria. The names of such variants begin with symbol & (for example, &0000000000425). Such variants are created once you schedule any background job from se80/se38 or any transaction without choosing any existing variant (from the selection screen, menu Program -> Execute in background).
The function module RS_VARIANT_CONTENTS works fine for normal variants (which can be seen via se80/se38), but not for the automatically-created ones (that begin with &). I looked into the FM and found that the VARI table was read by the next code:
IMPORT %_VARI40C TO P_VARI
%_VARI40 TO L_VARI_40
%_VARI TO L_VARI
%_VARIVDAT TO P_VARIVDAT
* %_VARIDYN40 TO P_VARIDYN
%_VARIVDAT_DYN40 TO P_VDATDYN
DYNS_FIELDS TO OLD_DYNSFIELDS
DYNS_TEXPRI TO OLD_TEXPRI
DYNS_EXPR TO OLD_EXPR
DYNS_FIELD_TAB TO DYNS_FIELDS
DYNS_TEXPR TO DYN_SEL-TEXPR
FROM DATABASE VARI(VB) CLIENT L_CLIENT ID P_RKEY
ignoring structure boundaries
IGNORING CONVERSION ERRORS.
However the import returns nothing for the & variants. It looks like the values for & variants are stored within a format which is different from the format used by FM RS_VARIANT_CONTENTS.
Is there any way to find a proper format/data structure for & variants values?
Update: I created ZBC_TEST program and scheduled it as a job. I see a record in VARI table:
MANDT RELID REPORT VARIANT SRTF2
200 VB ZBC_TEST &0000000000425 0
So, the &0000000000425 variant exists in VARI table. VARI-CLUSTD field is not empty for the record. I use this code:
CALL FUNCTION 'RS_VARIANT_CONTENTS'
EXPORTING
REPORT = 'ZBC_TEST'
VARIANT = '&0000000000425'
MOVE_OR_WRITE = 'W'
IMPORTING
SP = lv_sp
TABLES
VALUTAB = lt_valtab.
The FM has been performed without any exception and sy-subrc=0, but lt_valtab table is empty...
The function module is working correctly: Since your program does not have any parameters, the returned value set is empty.
Related
I'm trying to figure out which BAPI/FM I could use to search amounts confirmed based on search criteria of date (+time if possible) and workcenter confirmed where was confirmed...
I would be using BAPI_PRODORDCONF_GETDETAIL which contains these informations, but according to BAPI guide I can only load in the data of confirmation number+confirmation counter.
Therefore the option would be to run BAPI_PRODORDCONF_GETLIST (but I can only input the production order range or confirmation number range), then filter what includes the workcenter and date I need and from those pick up confirmation number+counter and run it through BAPI_PRODORDCONF_GETDETAIL.
but this procedure of getting list of everything without data being filtered on serverside is extemly timeconsuming and out of SAP Gui I have timeout error... therefore I need any BAPI/FM which I could input the workcenter where was confirmed and date, and have the data filtered already...
Any ideas how to do that?
As far as I know there is no such standard FM, so your only choice is custom development.
I would suggest you MCPK transaction were this info is exposed in a handy form, but as I see that your requirement is to receive this info externally this is not appropriate for you.
The confirmations reside in AFRU table and workcenters are in CRHD, so to find confirmed quantities by workcenter you should join these tables, or use a view u_15673 where this info is linked:
TYPES: BEGIN OF prod_orders,
rueck TYPE afru-rueck, "confirmation number
rmzhl TYPE afru-rmzhl," confirmation counter
gmnga TYPE afru-gmnga, " quantity
arbid TYPE crhd-arbpl, " workcenter
END OF prod_orders.
DATA: orders TYPE TABLE OF prod_orders.
SELECT *
FROM u_15673
INTO CORRESPONDING FIELDS OF TABLE orders
WHERE isdd >= '20180101' AND isdz <= '163000'.
To pull this externally, you must create RFC-enabled FM or use RFC_READ_TABLE and fetch this view with parameters, here is the sample.
Another approach is to use RFC_ABAP_INSTALL_AND_RUN. You must create an ABAP program that uses WRITE for output the results as a standard list to screen.
Send the lines of this program to RFC_ABAP_INSTALL_AND_RUN to PROGRAM parameter and the code will be executed on the remote system and this FM will return screen results as the lines of table WRITES.
Possible sample based on MCPK tcode to send to RFC_ABAP_INSTALL_AND_RUN:
CLEAR lwa_selection.
lwa_selection-selname = 'SL_SPTAG'.
lwa_selection-sign = 'I'.
lwa_selection-option = 'BT'.
lwa_selection-low = '20180101'.
lwa_selection-high = '20201231'.
APPEND lwa_selection TO li_selection.
CLEAR lwa_selection.
lwa_selection-selname = 'SL_ARBPL'.
lwa_selection-sign = 'I'.
lwa_selection-option = 'EQ'.
lwa_selection-low = '10400001'.
APPEND lwa_selection TO li_selection.
SUBMIT rmcf0200 WITH SELECTION-TABLE li_selection
with par_stat = abap_true
EXPORTING LIST TO MEMORY
AND RETURN.
DATA: xlist TYPE TABLE OF abaplist.
DATA: xtext TYPE TABLE OF char200.
CALL FUNCTION 'LIST_FROM_MEMORY'
TABLES
listobject = xlist.
CALL FUNCTION 'LIST_TO_TXT'
EXPORTING
list_index = -1
TABLES
listtxt = xtext
listobject = xlist.
IF sy-subrc = 0.
LOOP AT xtext ASSIGNING FIELD-SYMBOL(<text>).
WRITE <xtext>.
ENDLOOP.
ENDIF.
However, this approach is not flexible because MCPK standard layout is a bit different than you want, and is not easy to adjust programmatically.
Because of that I recommend to stick to the RFC_READ_TABLE approach.
I am working with Unidata, and ADO.NET using the U2 .NET Provider. This may be a shot in the dark as there are not many resources for Unidata and .NET these days.
Currently I can only return a single MV record 153926þIþ and parse it using MV_To_DataTable. I'd like to return multiple records like 153926þIþÿ153926þIþÿ. Is there any built in mechanism for doing this? I fear I will have to write the extension to best accomodate me.
I retrieve a single record in a unidata subroutine this way:
SUBROUTINE GETITEMS(results)
EXECUTESQL "SELECT ID, STATUS, DESC FROM ITEMS TO GETITEM_LIST;"
DONE = 0
RECCNT = 0
LOOP
RECCNT += 1
READNEXTTUPLE REC FROM "GETITEM_LIST" ELSE DONE = 1
results := REC
IF RECCNT EQ 1 THEN EXIT
UNTIL DONE
REPEAT
results
CLEARSQL
RETURN
Simple subroutine that returns one record without any record marks. This works when I use the U2Parameter method called MV_To_DataTable to parse it into an existing datatable.
However when I change the subroutine line:
results:= REC to results:= REC : #RM to append the record marks and remove the limit of 1, the MV_To_DataTable no longer is able to parse it correctly. In fact it will throw System.IndexOutOfRangeException: Cannot find column 3.
VB.NET Code:
' ... Open database connection called U2Connection ...
Dim cmd = U2Connection.CreateCommand
cmd.CommandText = "CALL GETITEMS(?)"
cmd.CommandType = CommandType.StoredProcedure
cmd.Parameters.Clear()
cmd.Parameters.Add(New U2Parameter("#arg1", "") With {.Direction = ParameterDirection.InputOutput})
cmd.ExecuteNonQuery()
Dim tb As New DataTable
tb.Columns.Add("ID")
tb.Columns.Add("STATUS")
tb.Columns.Add("DESC")
cmd.Parameters.Item(0).MV_To_DataTable(tb) ' Error happens here
' System.IndexOutOfRangeException: Cannot find column 3.
It appears the method does not separate records. I could be interpretting this incorrectly.
*****UPDATE 2/9/2019
I went ahead and wrote my own extension method to support my return format with the record markers. It populates a datatable with records allowing me to continue as I normally would.
You are kind of straddling the Multivalue/System.Data divide here. If you have not already done so, I would suggest looking into the U2 Toolkit for .NET, which I believe is generally readily available if you are current on maintenance. It comes with some samples of how to do things like this in C# and VB as well some Entity Framework stuff.
But as to what is going on here, You are trying to put a U2Type.DynArray into a System.DataTable is kind of tricky as the DynArray is a Record state, which could contain multiple rows from multiple tables within a DataSet. As #RM is the terminator for a record so it turns DynArray into DynArray[] and you can't have that as a parameter as such.
To fix this with the minimum refactoring, you can still use MV_To_DataTable, but note that it is expecting your data to be tabular and in in a single record. These example assume a newline is an Attribute mark (#FM/#AM)
Here is the contents of what you are returning
Row1Column1
Row1Column2
Row1Column3:#RM
Row2Column1
Row2Column2
Row2Column3:#RM
Row13olumn1
Row13olumn2
Row13olumn3:#RM
And here is what MV_To_DataTable expects
Row1Column1:#VM:Row1Column2:#VM:Row1Column3
Row2Column1:#VM:Row2Column2:#VM:Row2Column3
Row3Column1:#VM:Row3Column2:#VM:Row3Column3
If you adjust your U2 sub to output that, it should work.
Additionally, you could try using your SQL command directly for .net, but that becomes perilous for other reasons depending on your data.
Good Luck!
I need to track down where within a large number of custom sapscripts and smartforms a specific standard text (SO10) is being used.
Apart from the equivalent of "check the code for each print script", I've not found a workable solution online. Any suggestions?
After posting, I found a partial solution. The code below will search for a standard text within sapscripts, but not smartforms.
PARAMETERS: p_sttxt LIKE stxh-tdname.
DATA: BEGIN OF t_stxh OCCURS 0,
tdname LIKE stxh-tdname,
tdspras LIKE stxh-tdspras,
END OF t_stxh.
DATA t_lines LIKE tline OCCURS 0 WITH HEADER LINE.
SELECT tdname tdspras FROM stxh INTO TABLE t_stxh
WHERE tdobject = 'FORM'
AND tdid = 'TXT'
AND tdspras = 'E'.
LOOP AT t_stxh.
REFRESH t_lines.
CALL FUNCTION 'READ_TEXT'
EXPORTING
* CLIENT = SY-MANDT
id = 'TXT'
language = t_stxh-tdspras
name = t_stxh-tdname
object = 'FORM'
TABLES
lines = t_lines
EXCEPTIONS
id = 0
language = 0
name = 0
not_found = 0
object = 0
reference_check = 0
wrong_access_to_archive = 0
OTHERS = 0 .
SEARCH t_lines FOR p_sttxt.
IF sy-subrc EQ 0.
WRITE:/ t_stxh-tdname, t_stxh-tdspras.
ENDIF.
ENDLOOP.
This is a (fixed) version of the code found here: http://scn.sap.com/thread/179142
What concerns SmartForms, you cannot. You cannot just find it like you want it.
Unfortunately, in such ̶g̶o̶o̶d̶ ̶o̶l̶'̶ legacy technology as SmartForms everything is working legacy way, and standard texts are simply hard-coded. Yes, it looks awkward but they are really hard-coded, and these names are written out to SmartForm FM code every time it is re-generated.
So the only workaround here is to analyze the code.
Find all FMs for existing Smart Forms in system
There is a D010INC table containing all forms with their includes. The main point here is that all SmartForm FMs start with /1BCDWB/ prefix.
The main logic is in the includes, so we need to find correspondent INCLUDE for the target form.
Fetch SF include source code
It can be done in a several ways: via CL_RECA_RS_SERVICES class, via table REPOSRC, but the simplest way is ABAP statement READ REPORT.
Search SO10 text element name in the source code
Get Smart Form names for the FMs from hit list. It can be done via STXFADMI table, like in below snippet, but the more correct way is SSF_FUNCTION_MODULE_NAME FM
Bingo!
Sample solution could look like this:
DATA: lt_source TYPE TABLE OF string,
lt_smartforms TYPE TABLE OF d010inc,
so_text TYPE char50,
fs_form TYPE string,
used_in TYPE TABLE OF string,
len TYPE i.
* populating the list of SmartForm FMs
SELECT * FROM d010inc AS d
INTO TABLE lt_smartforms
WHERE master LIKE '/1BCDWB/%'
AND include LIKE '/1BCDWB/%'.
so_text = '85XX_FOOTER'. " <- our SO10 text element name
LOOP AT lt_smartforms ASSIGNING FIELD-SYMBOL(<fs_fm_name>).
* reading FM source code
READ REPORT <fs_fm_name>-include INTO lt_source.
* checking if SO11 exists in source code
FIND FIRST OCCURRENCE OF so_text IN TABLE lt_source.
IF sy-subrc = 0.
len = strlen( <fs_fm_name>-include ) - 7.
* searching for SmartForm related to the target FM
SELECT SINGLE formname
FROM stxfadmi
INTO fs_form
WHERE fmnumb = <fs_fm_name>-include+len(4).
IF sy-subrc = 0.
APPEND fs_form TO used_in.
ENDIF.
ENDIF.
ENDLOOP.
Yes, it is junky, not elegant and awkward, but who said it should be so?
I have the following code:
set output spoole
select * from displays where displayname='dsp020a'
select * from forms where formname in (select formname from displayforms where displayname='dsp020a')
select * from formfields where formname in (select formname from displayforms where displayname='dsp020a')
The third select is crashing ZIM with the following error:
*** ZIM System Error *** The Zim tree pool has overflowed. Type BYE to exit from Zim.
What am I doing wrong and how can I fix it?
When trying to use SQL in ZIM, I also saw problems and unexpected errors. You should always try to use the native ZIM 4GL commands to access data as well as object definitions.
ZIM Data Dictionary contains some predefined internal relations which can help analyse the data model. For example, you could say:
list all Displays DispDispForms DisplayForms where Displays.DisplayName = "dsp020a"
to find out which forms are contained in the given display. Likewise you could do:
list all Forms FormFormFields FormFields where Forms.FormName in ("f020a", "f020b", "f020c")
to list all form fields which belong to the given forms. Unfortunately, there is no relation between DisplayForms and Forms, so you cannot achieve directly what you tried in your example using SQL.
OR (added after your comment):
You can achieve that using a small program. For this example, it would be:
set output output_file
find Displays DispDispForms DisplayForms where Displays.DisplayName = "dsp020a"
while $setcount > 0
let vStr = DisplayForms.FormName
list all Forms FormFormFields FormFields where Forms.FormName = vStr
let $setcount = $setcount - 1
next
endwhile
set output terminal
Now you have all form fields which belong to all forms of the given display listed in the output_file.
There probably already is a relationship between dfs and forms in the db schema but the documentation is poor. However, you can create your own. Note that dfs is a role for displayforms.
add 1 rels let relname = 'dfsforms' relcondition = 'dfs.formname = forms.formname' reltype = 'ZIM' dirname = 'ZIM'
create rel dfsforms
Now you can find forms related to displays using:
find all displays dispdispforms dfs dfsforms forms formformformfields wh displays.displayname = 'dsp020a'
Trying to wrap my head around zoho creator, its not as simple as they make it out to be for building apps… I have an inventory database, and i have four fields that I call to fill a field called Inventory Number (Inv_Num1) –
First Name (First_Name)
Last Name (Last_Name)
Year (Year)
Number (Number)
I have a Custom Function script that I call through a Custom Action in the form report. What I am trying to do is upload a CSV file with 900 entries. Of course, not all of those have those values (first/last/number) so I need to bulk edit all of them. However when I do the bulk edit, the Inv_Num1 field is not updated with the new values. I use the custom action to populate the Inv_Num1 field with the values of the other 4 fields.
Heres is my script:
void onetime.UpdateInv()
{
for each Inventory_Record in Management
{
FN = Inventory_Record.First_Name.subString(0,1);
LN = Inventory_Record.Last_Name.subString(0,1);
YR = Inventory_Record.Year.subString(2,4);
NO = Inventory_Record.Number;
outputstr = FN + LN + YR + NO;
Inventory_Record.Inv_Num1 = outputstr;
}
}
I get this error back when I try to run this function
Error.
Error in executing UpdateInv workflow.
Error in executing For Each Record task.
Error in executing Set Variable task. Unable to update template variable FN.
Error evaluating STRING expression :
Even though there is a First Name for example, it still thinks there is none. This only happens on the fields I changed with Bulk Edit. If I do each one by hand, then the custom action works—but of course then the Inv_Num1 is already updated through my edit on success functions and makes the whole thing moot.
this may be one year late, you might have found the solution but just to highlight, the error u were facing was just due to the null value in first name.
you just have put a null check on each field and u r good to go.
you can generate the inv_number on the time of bulk uploading also by adding null check in the same code on and placing the code on Add> On Submt.( just the part inside the loop )
the Better option would be using a formula field, you just have to put this formula in that formula field and you'll get your inventory_number autogenerated , you can rename the formula_field to Inv Number or whaterver u want.
Since you are using substring directly in year Field, I am assuming the
year field as string.else you would have to user Year.tostring().substring(2,4) & instead of if(Year=="","",...) you have to put if(Year==null , null,...);
so here's the formula
if(First_Name=="","",First_Name.subString(0,1))+if(Last_Name =="","",Last_Name.subString(0,1)) + if(Year=="","",Year.subString(2,4)+Number
Let me know ur response if u implement this.
Without knowing the datatype its difficult to fix, but making the assumption that your Inventory_Record.number is a numeric data item you are adding a string to a number:
The "+" is used for string Concatenation - Joiner but it also adds two numbers together so think "a" + "b" = "ab" for strings but for numbers 1 + 2 = 3.
All good, but when you do "a" + 2 the system doesn't know whether to add or concatenate so gives an error.