How to download data with conversion exit? - download

I try to download an internal table to my PC and the download should use the conversion exits.
An example:
Table T002 contains a language key with one character (T0002-SPRAS).
When I WRITE T0002-SPRAS. the conversion routine ISOLA is used and I get a two character language key (E becomes EN...)
This conversion routine should be used for my export.
My test report:
REPORT Y_MY_DOWNLOAD_TEST.
CONSTANTS: c_filename type string VALUE 'C:\temp\test.txt'.
data: it_table type table of t002.
start-of-selection.
SELECT * from t002 into table it_table.
* Start file download
CALL METHOD cl_gui_frontend_services=>gui_download
EXPORTING
filename = c_filename
filetype = 'ASC' "or DAT
WRITE_FIELD_SEPARATOR = 'X'
WRITE_LF = 'X'
* DAT_MODE = SPACE
codepage = '4110' "UNICODE
SHOW_TRANSFER_STATUS = 'X'
WRITE_LF_AFTER_LAST_LINE = 'X'
CHANGING
data_tab = it_table
EXCEPTIONS
OTHERS = 99.
write: 'end'.
The result is a file without the usage of the conversion exit (English keeps E).
The SAP documentation mention the parameter dat_mode:
If this flag is set, .... Conversion exits are not executed.
I don't set the flag,so I would expect, the conversions are done.
I tried different combinations (dat_mode on/off, filetype ASC and DAT), but I found never a conversion.
Remarks:
I use SAP Release 7.01, Support Package SAPKB70107. It is a unicode system.
T002 is only an example, my real data are other data, containing language key.
I'm looking for a solution with gui_download (or another standard method/function module).
I don't want to build my own export file like this:
data:
tmp type string,
targetline type string,
targettable type table of string.
loop at it_table into sourceline.
"This could be done dynamic with field symbols and ASSIGN COMPONENT
write sourceline-field1 to tmp.
CONCATENATE targetline ';' tmp into targetline.
"...
APPEND targetline to targettable.
endloop.
This will be a possible solution, but in this case it would be easier for me to adapt the consumer of the export file.

I don't think it's possible. You could however join the LAISO value (which is the value the SPRAS output conversion function returns) in your queries which include an SPRAS type of field, and use a custom type for the query in which you replace the SPRAS type field with the LAISO type.
Here's an example using the T003P table:
types: begin of ty_t003p,
client type mandt,
spras type laiso,
auart type aufart,
txt type auarttext,
end of ty_t003p.
data ta_t003p type standard table of ty_t003p.
select t003p~client t002~laiso t003p~auart t003p~txt into table ta_t003p from t003p inner join t002 on t002~spras = t003p~spras.
cl_gui_frontend_services=>gui_download(
exporting
filename = 'C:\temp\test.txt'
filetype = 'DAT'
changing
data_tab = ta_t003p ).

Okay,
here goes, use SE11, go to the table, double click the data element with the conversion routine to display the data element. Then double click the domain to display the domain then double click the convers. routine name. ( ISOLA in this case ) Since you want the output value ( the input value is in the db ) you want to execute CONVERSION_EXIT_ISOLA_INPUT on the spras field for each table entry.
Something like
data: wa_table type t002.
loop at it_table into wa_table.
CALL FUNCTION 'CONVERSION_EXIT_ISOLA_OUTPUT'
EXPORTING
input = wa_table-spras
IMPORTING
OUTPUT = wa_table-spras.
modify it_table from wa_table index sy-tabix.
endloop.
At this point you can just continue using cl_gui_frontend_services=>gui_download on it_table.
I realize this is close to using your WRITE statement, except that the WRITE statement would get you in trouble.
What we have done at my work is write a program that uses the data dictionary to generate an upload download program.
Table DD04L contains the conversion exit for each table field, and then we do something likes this :
CONCATENATE 'wa_db-' wa_field-fieldname INTO g_string.
SELECT SINGLE * FROM dd03l INTO wa_dd03l WHERE tabname EQ p_tab AND fieldname EQ wa_field-fieldname.
SELECT SINGLE * FROM dd04l INTO wa_dd04l WHERE rollname EQ wa_dd03l-rollname.
IF wa_dd04l-lowercase IS INITIAL.
_repl 'translate wa_field to upper case.' g_string.
ENDIF.
_add 'if g_oops is initial.'.
IF wa_dd04l-convexit IS NOT INITIAL.
_add 'try.'.
_repl 'move wa_field to &.' g_string.
_add 'CATCH CX_DYNAMIC_CHECK into gcl_dynamic_check.'.
_add 'l_error = gcl_dynamic_check->get_text( ).'.
_add 'l_long_error = gcl_dynamic_check->GET_LONGTEXT( ).'.
_repl 'concatenate ''Conversion error'' wa_field ''into & ->'' l_error into l_error separated by space.' g_string.
_add 'condense l_error.' .
_add 'write l_error. new-line.' .
_add 'write l_long_error. new-line.' .
_add 'ENDTRY.'.
CONCATENATE 'CONVERSION_EXIT_' wa_dd04l-convexit '_INPUT' INTO g_fm.
_repl ' CALL FUNCTION ''&''' g_fm.
_add ' EXPORTING'.
_repl ' input = &' g_string.
_add ' IMPORTING'.
_repl ' output = &' g_string.
_add ' EXCEPTIONS'.
_add ' length_error = 1'.
_add ' OTHERS = 2.'.
IF sy-subrc <> 0.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
ENDIF.
with some defines which add code to the generated ABAP
DEFINE _repl.
wa_prog = &1.
replace all occurrences of '&' in wa_prog with &2.
append wa_prog to it_prog.
END-OF-DEFINITION.
DEFINE _add.
append &1 to it_prog.
END-OF-DEFINITION.
It's a ton of fun to write..

As of ABAP 7.52, I have verified that the conversion exits are executed only when:
The parameters are either filetype = 'DAT'
or filetype = 'ASC' and dat_mode = 'X'
(not really what the documentation says)
And only for fields whose data types are 'N', 'D', 'F' (except value 0) or 'T' (but not 'C' which is the most frequent case, especially concerning the conversion exits ALPHA, ISOLA and CUNIT)
(you may verify these rules on data types in the subroutine Put_Char_LineBuffer of the function group SFES, and more specifically in the subroutine ConvertAsc)
Note that the documentation embedded in the method gui_download of cl_gui_frontend_services says:
DAT mode: No longer supported. Use ASC instead.
So you cannot rely on the conversions done by the method.

Related

How can I use 'update where' select in FoxPro?

I am totally new to FoxPro (and quite fluent with MySQL).
I am trying to execute this query in FoxPro:
update expertcorr_memoinv.dbf set 'Memo' = (select 'Memo' from expertcorr_memoinv.dbf WHERE Keymemo='10045223') WHERE Keydoc like "UBOA"
I got the error:
function name is missing )
How can I fix it?
In FoxPro SQL statements you would not 'single-quote' column names. In Visual FoxPro version 9 the following sequence would run without errors:
CREATE TABLE expertcorr_memoinv (keydoc Char(20), keymemo M, Memo M)
Update expertcorr_memoinv.dbf set Memo = (select Memo from expertcorr_memoinv.dbf WHERE Keymemo='10045223') WHERE Keydoc like "UBOA"
If you would provide a few sample data and an expected result, we could see whether the line you posted would do what you want after correcting the single-quoted 'Memo' names.
NB 1: "Memo" is a reserved word in FoxPro.
NB 2: As you know, the ";" semicolon is a line-continuation in Visual FoxPro, so that a longer SQL statement can be full; of; those;
So that the Update one-liner could be written as:
Update expertcorr_memoinv ;
Set Memo = (Select Memo From expertcorr_memoinv ;
WHERE Keymemo='10045223') ;
WHERE Keydoc Like "UBOA"
NB 3: Alternatively, you can SQL Update .... From... in Visual FoxPro, similar to the Microsoft SQL Server feature. See How do I UPDATE from a SELECT in SQL Server?
I would do that just as Stefan showed.
In VFP, you also have a chance to use non-SQL statements which make it easier to express yourself. From your code it feels like KeyMemo is a unique field:
* Get the Memo value into an array
* where KeyMemo = '10045223'
* or use that as a variable also
local lcKey
lcKey = '10045223'
Select Memo From expertcorr_memoinv ;
WHERE Keymemo=m.lcKey ;
into array laMemo
* Update with that value
Update expertcorr_memoinv ;
Set Memo = laMemo[1] ;
WHERE Keydoc Like "UBOA"
This is only for divide & conquer strategy that one may find easier to follow. Other than that writing it with a single SQL is just fine.
PS: In VFP you don't use backticks at all.
Single quotes, double quotes and opening closing square brackets are not used as identifiers but all those three are used for string literals.
'This is a string literal'
"This is a string literal"
[This is a string literal]
"My name is John O'hara"
'We need 3.5" disk'
[Put 3.5" disk into John's computer]
There are subtle differences between them, which I think is an advanced topic and that you may never need to know.
Also [] is used for array indexer.
Any one of them could also be used for things like table name, alias name, file name ... (name expression) - still they are string literals, parentheses make it a name expression. ie:
select * from ('MyTable') ...
copy to ("c:\my folder\my file.txt") type delimited

Oracle PL/SQL Query With Dynamic Parameters in Where Clause

I'm trying to write a dynamic query that could have a different amount of parameters of different type. The only issue I'm having is handling if the value is a string therefore needing single quotes around it. I am using the value of a field called key_ref_ to determine what my where clause will look like. Some examples are:
LINE_NO=1^ORDER_NO=P6002277^RECEIPT_NO=1^RELEASE_NO=1^
PART_NO=221091^PART_REV=R02^
At the moment I am replacing the '^' with ' and ' like this:
REPLACE( key_ref_, '^' ,' and ' );
Then I'm trying to create the dynamic query like this:
EXECUTE IMMEDIATE
'select '||column_name_||' into column_ from '||base_table_||' where '||
key_ref_ || 'rownum = 1';
This won't work in cases where the value is not a number.
Also I only added "rownum = 1" to handle the extra 'and' at the end instead of removing the last occurence.
If the input will not have the tild symbol(~) then you can try the below code.
if the input has tild, you can replace it with some other value which should not be there in input
considering the input provided in the example..
LINE_NO=1^ORDER_NO=P6002277^RECEIPT_NO=1^RELEASE_NO=1^PART_NO=221091^PART_REV=R02^
use the below code
replace(replace(replace('LINE_NO=1^ORDER_NO=P6002277^RECEIPT_NO=1^RELEASE_NO=1^PART_NO=221091^PART_REV=R02^','^','~ and '),'=','=~'),'~',q'[']')
and the result would be
LINE_NO='1' and ORDER_NO='P6002277' and RECEIPT_NO='1' and RELEASE_NO='1' and PART_NO='221091' and PART_REV='R02' and
System will type cast the number fields so, there would not be any issue.

Creating a copy of a recordset in a Session variable

This is a follow on from a question I asked here > Converting an HTML table to JSON to pass to an AJAX call for downloading as a CSV
I have a report page that outputs a number of recordsets as graphs and tables but also buttons to "download as a CSV file" I have a generic function that will take any number of recordsets (as the stored proc returns multiple recordsets) and outputs a CSV so thats fine.
The issue is I want to set the output of my stored proc into a Session("DATA") variable and then create a "copy" of the data in memory so that whenever the "download" button is pressed I can just look for a Session("DATA") variable and output it if it exists.
The problem is that when I set a recordset object to point at the Session it is referential so that once it has looped through all the recordsets outputting them on the page the Session is then empty (or at the end of all the recordsets - so it's an object with no data in it)
How can I Create a "copy" of the recordset instead of a pointer so that the Session always has the full recordset in it e.g
Set Session("DATA") = objCon.Execute(strSQL) '* returns multiple recordsets
Set objRS = Session("DATA")
Do While Not objRS.EOF......
'* BUT now when I want to access Session("DATA") it is at the start of all the recordsets and not a spent, EOF of the last recordset due to me looping through objRS
I could have a function that loops through the recordsets and makes a duplicate but then that seems like a lot of effort and performance and I thought there must be a way to copy the recordsets for the session somehow without looping through it multiple times.
If I have to create a "Copy" object function then I suppose I will have to but is there not an easier way in ASP CLASSIC to create a copy of an object and not a reference pointer?
You can read the entire recordset into an array, using GetRows:
'GetDataSet
' Returns a table of data based on the supplied SQL statement and connection string.
'Parameters:
' sqlString (string) - The SQL string to be sent. This can be either a valid SQL string or an Application setting
' specified using the '#' prefix (e.g. #GET_USERNAME)
' connString (string) - The database connection string. Either a valid connection string, an Application setting
' (using the '#' prefix, e.g. #CONN_STRING) or an AMC (AppModeConnection string).
'Usage:
' dataSet = GetDataSet(sqlString, connString)
'Description:
' This function generates a table of information in a 2 dimensional array. The first dimension represents the columns
' and the second the rows. If an error occurs while the routine is executing the array and the base index (0,0) is set
' to C_ERROR, (0,1) to the VBScript error index, and (0,2) to the VBScript error description.
'Notes:
' Updated this function to take advantage of the AppModeConnection feature.
'Revisions:
' 30/09/2015 1.1 Added facility to allow recovery of Application settings as Query and connection strings using
' '#', (e.g.: ds = GetDataSet("#GET_USER_DETAIL", "#CONN_DATABASE")
' 25/09/2015 1.0 Added AMC support for Classic ASP. The system will test to see if there is a valid connection
' string based on the current application mode and the connection string provided (e.g. if the
' connection string is 'CONN_DATABASE' and the application mode is 'DEV' then the final connection
' string will be 'CONN_DATABASE_DEV'. A connection string should be present to cover this.
' < 25/09/2015 0.1 Bug ironed out that prevented closing of the database.
' < 25/09/2015 0.0 Initial version.
function GetDataSet(ByVal sqlString, ByVal connString)
'Test to see if there's an application connection string first...
If Left(connString, 1) = "#" Then
connString = Application(Mid(connString, 2))
Else
Dim amc
amc = AppModeConnection(connString)
If amc <> "" then connString = amc
End If
'Test the SQL string to see if it's stored as an Application setting...
If Left(sqlString, 1) = "#" Then sqlString = Application(Mid(sqlString, 2))
'Define the initial output...
dim rV, rs
If (Application("APP_MODE") = Application("MODE_DEV") And Application("DEV_TRAP_ERRORS")) Or _
(Application("APP_MODE") <> Application("MODE_DEV")) Then On Error Resume Next
'Define and open the recordset object...
set rs = Server.CreateObject("ADODB.RecordSet")
rs.Open sqlString, connString, 0, 1, 1
'Initialise an empty value for the containing array...
redim rV(0,0)
rV(0,0) = C_NO_DATA
'Deal with any errors...
if not rs.EOF and not rs.BOF then
'Store the data...
rV = rs.GetRows()
'Tidy up...
rs.close
set rs = nothing
select case err.number
case 3021 'No data returned
'Do nothing as the initial value will still exist (C_NO_DATA)
case 0 'No error
'Do nothing as data has been returned
case else
redim rV(4,0)
rV(C_COL_IDENTIFIER,0) = C_ERROR
rV(C_COL_ERROR_ID,0) = err.number
rV(C_COL_ERROR_MESSAGE,0) = err.description
rV(C_COL_SQL,0) = sqlString
rV(C_COL_CONNECTION,0) = "Withheld"
end select
end if
on error goto 0
'Return the array...
GetDataSet = rV
end function
This is my own in depth version which does some funky stuff with connection strings etc, so feel free to use it, but note that you'll have to set-up the handling for the connection strings etc. Within the code, though, is the core element - the GetRows, that you require.
You shouldn't need to set any Session variables, simply process all in the same page, as per marekful's answer to your post. You can do this using a simple For...Next loop using an array.
To use the function above simply declare your SQL and call it like so...
Dim ds, sql
sql = "EXEC prc_get_report_data "
ds = GetDataSet(sql, "#my_conn")
(Note: read the code comments about the connection strings).
The array returned from this is obviously two dimensional zero based, where x = columns, y = rows:
ds(x, y)
What I tend to do is define constants to cover the column names, matching them to the equivalents in the database...
Const COL_ID = 0 'Column 0 is the ID field (note zero based)
Const COL_TITLE = 1 'Title field
Const COL_DESCRIPTION = 2 'Description field
...and so on.
Then you can reference them eaasily:
If ds(COL_ID, row) = 14 Then
Use the UBound function to get the extents of the array...
Dim row, rows
For rows = 0 To UBound(ds, 2) '2 is the second dimension of the array (note not zero based
If ds(COL_ID, row) = avalue Then
You get the idea.

VB 6.0 - Formating Textbox "dd/MM/yy"

I've made a question previously about this but I didn't express really what I wanted. I have a textbox that requires the user to write a Date in it. Then he can press a button and generate a report based on the entries on some other textboxes. It is really important that the date in the generated report is in this format "dd/MM/yy".
I use this code :
Dim a as String
a = Format(Textbox1.text, "dd/MM/yy")
Everything is working fine except when the user types a date in this format "dd.MM.yy" e.g 15.05.15 . When this happens, the date in the generated report is always "30/12/99". Can some1 help me understand why this is happening ?
EDIT: Regarding the Duplicate Question. In the previous question I asked how I can extract characters from a textbox and form a date in the format I want. This question is about a specific problem that happens when a specific type of format is used in the textbox (dd.MM.yy) that prints a specific date always (30/12/99).
30/12/1899 is the Date equivalent of 0, this is what you get when you try to convert a String which is not in a correct date format to a Date.
To verify this, type any old rubbish into your text box and you will get 30/12/99 as output.
Regarding using a DateTimePicker control, see my answer to your other question here
you can chose which keys you allow in the textbox.
for example as follows:
'1 form with:
' 1 textbox : name=Text1
Option Explicit
Private Sub Text1_KeyPress(KeyAscii As Integer)
' Caption = CStr(KeyAscii)
KeyAscii = DateOnly(KeyAscii)
End Sub
Private Function DateOnly(intKey As Integer) As Integer
Dim intResult As Integer
intResult = intKey
Select Case intKey
Case vbKeyBack 'allow backspace
Case vbKey0 To vbKey9 'allow numbers
Case 45 'allow -
' Case 46 'change . into /
' intResult = 47
Case 47 'allow /
Case Else 'dont allow anything else
intResult = 0
End Select
DateOnly = intResult
End Function
this just limits which keys the user can enter, you will still have to pay attention to other invalid inputs
[EDIT]
I added Case 45 to the code above.
In the select case you specify what happens to each key input:
you allow the key unaltered by specifying nothing, as i did with the / (ascii value 47)
you can change the key to another key, as i did with the . (ascii value 46)
you can reject the key input by setting it to 0, as i did with all other keys (case else)
You can find out which ascii value a specific key has by uncommenting the first line in Text1_KeyPress so the ascii value will show in the form caption
The IsDate Function is also useful. This will check if the date entered is valid, and return True if it is.
IsDate ("21/05/2015") 'returns 'True'
IsDate ("21.05.2015") 'returns 'False'
You could use the Replace function to change "." to "/"
mydate = Replace("21.05.2015", ".", "/") ' gives "21/05/2015"

How to Find Record Set Value is Number or String?

Original:
Using VB6
If rsCardEvent(4).Value = Str Then
TimOut = rsCardEvent(4)
Else
TimeOut = Left(TimOut, 2) & ":" & Mid(TimOut, 3, 2) & ":" & Right(TimOut, 2)
End If
Getting Type MisMatch Error.
How To Find Record Set Value is String or Number
Exactly i need
If Number means print number like Time Format (HH:MM:SS)
else
print string value
Coding Help for the above condition
Edited Version:
I'm working with an ADO.Recordset object and am trying to determine the data type of a column at run-time. I need to handle the column value differently in my code depending on its underlying data type. If the column value is a string, I want to work with the value as-is. If it is a number, I want to treat the number as an packed time and convert it to HH:MM:SS format (i.e. the number 120537 would be converted to the string "12:05:37").
Below is some example code that demonstrates what I want to achieve. However, when I run this code I get a "Type Mismatch" error:
If rsCardEvent(4).Value = Str Then
TimOut = rsCardEvent(4)
Else
TimeOut = Left(TimOut, 2) & ":" & Mid(TimOut, 3, 2) & ":" & Right(TimOut, 2)
End If
Have a look at the Visual Basic 6 function library. There are functions that can help you determine the underlying type of a value.
There are quite a few but you might find these useful:
IsNumeric
IsDate
Based on this article, if rsCardEvent is an ADO recordset, you could check the Type property. Something like this:
Select Case rsCardEvent(4).Type
Case adBSTR, adChar, adVarChar, adWChar, _
adVarWChar, adLongVarChar, adLongVarWChar
' It is a string '
Case Else
' It is not a string '
End Select
You can use the IsNumeric function available in VB6.
How about:
If TypeName(rsCardEvent(4).Value) = "String" then
http://msdn.microsoft.com/en-us/library/5422sfdf.aspx

Resources