In the SAP system, documents for an object are usually stored as "attachments" via the "Object Services". In transaction IE02 (Change Equipment), for example, this looks like this:
Numerous file types are possible; common are PDFs, Word and Excel files as well as image files in .jpg or .png format.
In this tutorial we will integrate the document display into an S10 application. Our starting point is a list of equipment that we have generated with the S10 utilities, transaction /s10/util:
We build the document display into this basic list. When you click on an equipment line, some details about the respective equipment and the list of installations appear:
In the list of documents, a click either displays the document externally (Word and Excel files) or embeds it inline in the list (image files, PDF, video). The browser usually offers the option of subsequently displaying the document as an independent application instead of in the list. In the case of Word and Excel, embedded display is not currently supported in the current browsers.
Here are a few examples:
The documents can each be opened and closed individually. For Excel and Word, the corresponding application is opened on the mobile device or on the desktop:
Now to the implementation. We use the global class /s10/attachment, which is part of the S10 demo package; it provides some general services to read documents for SAP objects. This makes the ABAP programme quite clear:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
program zzequi00. class equi_detail definition inheriting from /s10/any. public section. * Equipment properties data: equnr type v_equi-equnr, " Equipment Number tplnr type v_equi-tplnr, " Functional Location eqtyp type v_equi-eqtyp, " Equipment type inbdt type v_equi-inbdt, " Start-up Date sernr type v_equi-sernr. " Serial Number * attachments data: attachment type ref to /s10/attachment, attachments type table of ref to /s10/attachment. * database table name constants: dbtablename type string value 'v_equi'. " equipments with functional locations methods: build_attachments importing equnr type equnr exporting attachments type table, on_detail_attachments. endclass. class equi_detail implementation. method build_attachments. /s10/attachment=>list_attachments( exporting instid = equnr typeid = 'EQUI' catid = 'BO' importing attachments = attachments ). * sort attachments s10sort( exporting foldername = 'attachments' columns = 'doctitle' ). endmethod. method on_detail_attachments. create object attachment. attachment->dockey = s10contextinfo( )->getvalue( 'dockey'). endmethod. endclass. class equi_short definition inheriting from /s10/any. public section. * table fields for detail view, plus key fields data: equnr type v_equi-equnr, " Equipment number tplnr type v_equi-tplnr. " Functional location * database table name constants: dbtablename type string value 'v_equi'. " for select endclass. class equi_short implementation. endclass. class equi_manager definition inheriting from /s10/any. public section. * table fields for list view, plus key fields data: search_equnr type string, search_equnr_upper type string, search_tplnr type v_equi-tplnr, search_tplnr_upper type string, search_sernr type v_equi-sernr, " search_sernr_upper type string, search_maxrowcount type string value '100'. data: tabequi type table of ref to equi_short, myequi type ref to equi_detail. methods: list, on_detail_tabequi, * method to build up tabequi build_tabequi importing search_equnr type string search_tplnr type v_equi-tplnr search_sernr type v_equi-sernr search_maxrowcount type string exporting tabequi type table. endclass. class equi_manager implementation. * display list screen method list. s10nextscreen( 'list'). endmethod. * select database values and fill table tabequi method build_tabequi. data: condition type string, join type string, search type string. search_equnr_upper = to_upper( search_equnr ). search_tplnr_upper = to_upper( search_tplnr ). search_sernr_upper = to_upper( search_sernr ). if search_equnr is not initial. search = search_equnr. if join is initial. join = |v_equi |. endif. join = join && | join eqkt on eqkt~EQUNR = v_equi~equnr and eqkt~SPRAS = @sy-langu and upper( eqkt~EQKTX ) like '%| && search_equnr_upper && |%'|. endif. if search_tplnr is not initial. search = search_tplnr. if condition is not initial. condition = condition && | and |. endif. condition = condition && |v_equi~TPLNR EQ '| && search_tplnr && |'|. endif. if search_sernr is not initial. search = search_sernr. if condition is not initial. condition = condition && | and |. endif. condition = condition && |v_equi~SERNR EQ '| && search_sernr && |'|. endif. data: maxrows type i. maxrows = search_maxrowcount. * read data s10databaseselect( exporting dbtablename = join condition = condition maxrows = maxrows distinct = 'X' fieldlist = 'v_equi~equnr,v_equi~tplnr' changing folder = tabequi ). s10sort( foldername = 'tabequi' columns = 'equnr,tplnr' userformat = 'X' ). endmethod. * show details in list (line selection) method on_detail_tabequi. * read current table row. data: tabindex type i. tabindex = s10actionparameter( ). read table tabequi index tabindex assigning field-symbol(<row>). * set table key and read detail attributes create object myequi. myequi->equnr = <row>->equnr. myequi->tplnr = <row>->tplnr. myequi->s10databaseread( keylist = 'equnr,tplnr' ). endmethod. endclass. * main class for this application class main definition inheriting from /s10/any. public section. * manager object data: my_equi_manager type ref to equi_manager. methods: logon. endclass. class main implementation. * logon user method logon. * set S10 license s10setlicense( 'Synactive GmbH demo license number=100 role=s10demo_role maxusers=10 signature=821.126.87.7' ). * create manager object create object my_equi_manager. * start list display my_equi_manager->list( ). endmethod. endclass. |
For explanation, the connection to the HTML page is required in some places:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta name="viewport" content="width=400"> <link rel='stylesheet' type='text/css' href='../../../style/s10.style.css'> <link rel='stylesheet' type='text/css' href='../../../style/custom.style.css'> <script src='../../synactiveS10/synactiveS10.java.js'></script> <title>Equipments</title> </head> <body style="width: 100%; margin: 0px; padding: 0px;" onload='init();' class="colorscheme9"> <div class="headerarea" style="width: 100%; padding: 10px;"> <!-- title from data dictionary --> <b>Equipments</b> </div> <div class="toolbar"> <button type="button" class="toolbarbutton" onclick="S10Enter();"> Start </button> <button type="button" class="toolbarbutton" style="float: right;" onclick="S10Logoff();"> End </button> </div> <!-- filter --> <div class="tablefilteractive"> <div class="infoblock" style="width: 150px; height: 50px;"> <label class="label">Equipment text</label><br> <input type="search" class="input" name="search_equnr" style="width: 140px;"> </div> <div class="infoblock" style="width: 150px; height: 50px;"> <label class="label">Functional location </label> <br> <input type="search" class="input" name="search_tplnr" style="width: 140px;"> </div> <div class="infoblock" style="width: 150px; height: 50px;"> <label class="label">Serial number </label> <br> <input type="search" class="input" name="search_sernr" style="width: 140px;"> </div> <br /> <div class="infoblock" style="width: 150px; height: 50px;"> <label class='label'>Max count</label> <br /> <select class="inputselect" name="search_maxrowcount" size="1" style='width: 120px;' onchange="S10Enter();"> <option value="100">100 </option> <option value="400">400 </option> <option value="1000">1000 </option> <option value="4000">4000 </option> <option value="10000">10000 </option> <option value="40000">40000 </option> <option value="100000">100000 </option> </select> </div> <div class="infoblock" style="width: 150px; height: 50px;"> <label class='label'>Selected</label><br /> <div class='output' style="font-size: 14px; padding-top: 6px; font-weight: bold;" name='tabequi@rowcount'> </div> </div> <div class="processingmessage">Reading data...</div> </div> <!-- column headers --> <div class="colheaders"> <!-- Equipment --> <div class='colhead colheadup output ' style="width: 100px; max-width: 100px;" name="equnr"> </div> <div class='colhead output' style="width: 260px;" name="equnr@text"> </div> <!-- Functional location --> <div class='colhead output landscape' style="width: 300px;" name="tplnr@text"> </div> <div class="colhead" style="width: 20px; float: right; margin-right: 4px;" onclick="S10FilterTable(this);"> <img src="../../../icons/filter.png" style="width: 18px; height: 18px;"> </div> </div> <!-- list rows --> <form class='table' name='tabequi'> <div class="tablerow" onclick="S10ToggleDetail(this);"> <!-- Equipment --> <div class='outputcelldiv ' style="width: 100px; max-width: 100px;" name="equnr"> </div> <div class='outputcelldiv' style="width: 260px;" name="equnr@text"> </div> <!-- Functional location --> <div class='outputcelldiv landscape' style="width: 300px;" name="tplnr@text"> </div> <div class='tablerequestdetail' style='float: right;'></div> </div> </form> <!-- nothing selected --> <div class="tablenocontent" style="display: block;"> Nothing selected </div> <!-- detail view --> <div id="tabequi_detail" class='tabledetail'> <!-- Equipment type--> <div class="infoblock"> <label class='label output' name="myequi.eqtyp"></label> <br /> <span class='output' name='myequi.eqtyp'></span> <span class='output' name='myequi.eqtyp@text'></span> </div> <!-- In operation from --> <div class="infoblock"> <label class='label output' name="myequi.inbdt"></label> <br /> <span class='output' name='myequi.inbdt'></span> </div> <!-- Serial number --> <div class="infoblock"> <label class='label output' name="myequi.sernr"></label> <br /> <span class='output' name='myequi.sernr'></span> </div> <!-- Attachments --> <div class="infoblock" style="width: 100%; max-width: 800px; height: auto; display: block;"> <label class='label'>Documents</label><br /> <form class='table' name='myequi.attachments' style="width: 360px; --landscape-width: 480px;"> <div class="tablerow" onclick="S10ToggleDetail();"> <!-- additional keys --> <div class='outputcelldiv linkkey' name="dockey"></div> <!-- columns --> <div class='outputcelldiv' style="width: 220px; --landscape-width: 320px;" name="doctitle"> </div> <div class='outputcelldiv' style="width: 40px;" name="doctype"> </div> <div class='outputcelldiv' style="width: 80px;" name="docsize_out"> </div> </div> </form> <div class="tablenocontent">No documents attached</div> <br /> </div> <!-- Document display area --> <div class='tabledetail' id="myequi.attachments_detail"> <div class="outputhtml" name="myequi.attachment.display_html"> </div> </div> </body> </html> |
Explanations
The line numbers A... refer to the ABAP code, H... to the HTML file.
Line A3
class equi_detail definition inheriting from /s10/any.
The class "equi_detail" describes the detailed view of the equipment after selecting a line in the list. The attributes (lines A9 to A13) are displayed in HTML from H155. If you want to display further information on the equipment that is contained in the database view V_EQUI, it is sufficient to add these to the class and output them in the HTML file. The reading of all attributes is done by s10databaseread( ) in line A192.
Line A15
* attachments data: attachment type ref to /s10/attachment, attachments type table of ref to /s10/attachment.
Defines an attachment object and the table of attachments. The table is output in HTML from line H181 to H209. The columns doctitle, doctype and docsize_out are defined as attributes of the class /s10/attachment.
In addition to the visible columns, the document key is noted invisibly in the table: H191 <div class='outputcelldiv linkkey' name="dockey"></div> When clicking on an attachment line, this key is read in ABAP by s10contextinfo():
method on_detail_attachments. create object attachment. attachment->dockey = s10contextinfo( )->getvalue( 'dockey'). endmethod.
Line A37
The table of attachments per equipment is realised as a build method. For implementation, the method "list_attachments" of the class /s10/attachment is called:
/s10/attachment=>list_attachments( exporting instid = equnr typeid = 'EQUI' catid = 'BO' importing attachments = attachments ).
For other objects, you must enter the correct "typeid" here, which is the "Business Object Type" (transaction SWO2). For example, for a purchase requisition it would be "BUS2105". The catalogue ID is always "BO" for "Business Object".
Line A64
class equi_short definition inheriting from /s10/any.
The class "equi_short" describes the line of the equipment list. We only need the equipment number and the functional location here. The corresponding texts are supplemented in the output by the S10 Framework with the notation "...@text". In the HTML file, you will find the output of the table from line H125, beginning with
<form class='table' name='tabequi'>
From line A119
class equi_manager implementation.
The rest of the programme is independent of the document display; it is generated by the S10 Utilities.
The central section in the HTML file is the display of the document from line H211:
<!-- Document display area --> <div class='tabledetail' id="myequi.attachments_detail"> <div class="outputhtml" name="myequi.attachment.display_html"> </div> </div>
This section is automatically displayed behind the selected line of the attachment list as soon as the user clicks on an attachment line, as
<div class="tablerow" onclick="S10ToggleDetail();">
is defined in line H188.
First, the ABAP method
method on_detail_attachments.
create object attachment.
attachment->dockey = s10contextinfo( )->getvalue( 'dockey').
endmethod.
of the object "myequi" is called, since the table "attachments" is defined in the object "myequi". However, the object "myequi" in the ABAP programme always matches the last selected equipment line; after all, it only exists once. If the user first expands some equipment and then displays an attachment in one of the expanded areas, the line number from HTML in the ABAP programme is of no use to us, because the current attachment table can belong to another piece of equipment. This difficulty arises from the nested structure of the list, i.e. list of equipment and a list of attachments for each piece of equipment.
For this reason, we do not use the line number and the current table "attachments", but reach through
attachment->dockey = s10contextinfo( )->getvalue( 'dockey').
to the document key hidden in the HTML line and set it in the attachment object. Since the attribute "display_html" is output in the HTML page:
<div class="outputhtml" name="myequi.attachment.display_html">
the build method of the attribute "display_html" of the global class "/s10/attachment" will now run. The build method reads the document and builds the correct HTML code to display the object. In our HTML file it is important here to work with class="outputhtml "instead of class="output", otherwise the HTML code would be displayed instead of interpreted as HTML.
Our document display would be easier to implement if we did not integrate the information per piece of equipment and per attachment into the list, but instead displayed them in separate pages from which the user then returns to the list. The advantage of the chosen display is that the parts that have been expanded once, for example two photos, remain visible simultaneously in the list or are at least easily accessible by scrolling. For example, two photos of a damage report would then be visible directly below each other.
Uploading documents
Now for uploading documents and saving them as attachments. The procedure for this is as follows:
Step 1: Add the selection of the desired file to be uploaded in HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<label class='label'>Title for new docuement</label> <br /> <input class="input" data-locid="attachment_title_input" style="width: 380px;"> <input type="file" data-locid="attachment_input" accept=".jpg,.jpeg,.png,.pdf,.docx,.xlsx,.txt" style="display: none" onchange="upload_attachment(this)"> <br /> <button type="button" class="button" data-locid="attachment_button" style="margin-top: 5px;" onclick="return start_upload_attachment(this)"> Select and upload a document <span class="processingmessage">Uploading...</span> </button> |
Remarks
Lines 4, 8, 15: data-locid=
In our application, the upload option is displayed below the attachment list within the detail area of the table line. So it may appear multiple times if the user opens the detail area for multiple lines in succession. For this reason we cannot use the common id= to identify the HTML elements from JavaScript, because in HTML the assigned id must be unique in the HTML page to work with document.getElementById(). The attribute data-locid allows us to address an element within the opened detail area with the S10 Framework function S10 ElementByLocid() (see JavaScript in Step 2), it is a locally usable id, so to speak.
Line 7: <input type=file>
This allows the user to select a file in HTML. With accept=... we specify the file extensions that should be possible for the selection. In line 10 the element is hidden with style="display:none", because we do not want to use the HTML standard display, but rather provide a separate button for file selection in line 14.
The HTML part specified above goes into the detail area of the table row. In addition, we need a single hidden <input> element outside the file area for the S10 variable, which we use to transport the file contents into our ABAP program:
<input type="hidden" class="input" name="data_url" id="data_url" />
data: data_url type string.
Step 2: Transfer the content of the selected file into a hidden S10 variable using Javscript means
Insert the following JavaScrpt code in the HTML page. Whether it is located in <head> or in <body> does not matter. If you need to upload documents in multiple HTML page, you may put it into a separate file and reference it with <script src="..."> </script>.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
<script> var attachment_reader = null; var attachment_input = null; var attachment_button = null; var attachment_title = ""; var attachment_filename = ""; function start_upload_attachment(f) { attachment_title = S10ElementByLocid(f, 'attachment_title_input').value; if (!attachment_title) { alert('Please first enter a title'); return; }; attachment_input = S10ElementByLocid(f, 'attachment_input'); attachment_button = S10ElementByLocid(f, 'attachment_button'); // reset value, otherwise onchange // is not triggered for same filename attachment_input.value = ''; attachment_input.click(); return false; }; function upload_attachment(f) { var files = f.files; // anything selected? if (files) { // 1st file (nultiple files not supported) var fileobject = files[0]; attachment_filename = fileobject.name; attachment_reader = new FileReader(); attachment_reader.addEventListener("load", complete_upload_attachment); attachment_reader.readAsDataURL(fileobject); }; } function complete_upload_attachment() { var result = attachment_reader.result; attachment_reader = null; // check file size if (result.length > 1024 * 1024) { S10ErrorMessage(attachment_button, "Synactive demo system: Please upload only files < 1MB"); return; }; // upload content as data url document.getElementById('data_url').value = result; S10Apply('save_attachment', attachment_filename + '\t' + attachment_title, attachment_button); }; </script> |
Remarks
Line 9 Function "start_upload_attachment"
The function is called from the upload button. It checks if the user has entered a title, sets some global variables and then calls the file selection by clicking the hidden <input type='file'> element.
Line 32 Function "upload_attachment"
This function is called from the <input type='file'> element by onchange=, see HTML snippet above line 11, as soon as the user has selected a file. We get the "file object" of the selected file and read the file in the format "data URL" in Line 47. The file reading is done asynchronously by the javascript "File Object". Therefore, we specify in line 45 the JavaScript function that will be called when the file is completely read and we can access the entire content.
Line 52 Function "complete_upload_attachment"
This function is called as soon as the file is completely read. We can access the contents of the file via the "result" component of the JavaScript File Reader. In line 59 we check the file size and give an error message if the file is too large.
Note: In mobile devices, the file selection usually also provides the option to take a photo and upload it. Due to the large resolution of today's devices, this may result in very large jpeg files. You can first compress such image files internally and then upload them (use canvas element in HTML).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
<script> var attachment_reader = null; var attachment_input = null; var attachment_button = null; var attachment_title = ""; var attachment_filename = ""; function start_upload_attachment(f) { attachment_title = S10ElementByLocid(f, 'attachment_title_input').value; if (!attachment_title) { alert('Please first enter a title'); return; }; attachment_input = S10ElementByLocid(f, 'attachment_input'); attachment_button = S10ElementByLocid(f, 'attachment_button'); // reset value, otherwise onchange is not triggered for same filename attachment_input.value = ''; attachment_input.click(); return false; }; function upload_attachment(f) { var files = f.files; // anything selected? if (files) { // 1st file (nultiple files not supported) var fileobject = files[0]; attachment_filename = fileobject.name; attachment_reader = new FileReader(); attachment_reader.addEventListener("load", complete_upload_attachment); attachment_reader.readAsDataURL(fileobject); }; } function complete_upload_attachment() { var result = attachment_reader.result; attachment_reader = null; // compress large jpeg images (camera) if (result.startsWith('data:image/jpeg') && result.length > 400 * 1024) { var image = new Image(); image.src = result; image.onload = function () { upload_image(image); }; return; }; // check file size if (result.length > 1024 * 1024) { S10ErrorMessage(attachment_button, "Synactive demo system: Please upload only files < 1MB"); return; }; // upload content as data url document.getElementById('data_url').value = result; S10Apply('save_attachment', attachment_filename + '\t' + attachment_title, attachment_button); }; function compressImage(image, compression) { var canvas = document.createElement("canvas"); canvas.width = image.width; canvas.height = image.height; var ctx = canvas.getContext("2d"); ctx.drawImage(image, 0, 0); return canvas.toDataURL("image/jpeg", compression); } function upload_image(image) { var data_uri = compressImage(image, 0.7); var k = 0.5; while (k > 0 && data_uri.length > 1024 * 1024) { data_uri = compressImage(image, k); k = k - 0.1; }; // check file size if (data_uri.length > 1024 * 1024) { S10ErrorMessage(attachment_button, "Synactive demo system: Please upload only images < 1MB"); return; }; // upload content as data url document.getElementById('data_url').value = data_uri; S10Apply('save_attachment', attachment_filename + '\t' + attachment_title, attachment_button); }; </script> |
In line 66 we put the content of the file into the hidden variable "data_url". Then we call the ABAP function "save_attachment" in Line 68, giving it the file name and, separated by a TAB character, the title entered by the user. Now the file can be saved as an attachment on the ABAP level.
Step 3: In ABAP, transfer the file content from the S10 variable and save it as an attachment to the equipment using the "attachment" class of the S10 framework
Add the following code to your ABAP program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
* Variable for file content public section. data: data_url type string. * In "class equi_manager implementation": method save_attachment. data: parms type string. parms = s10actionparameter( ). data: docfilename type string, doctitle type string, doctype type string. split parms at cl_abap_char_utilities=>horizontal_tab into docfilename doctitle. * doctype: file extension if docfilename ca '.'. data: strs type standard table of string, str type string. split docfilename at '.' into table strs. read table strs index lines( strs ) into str. doctype = to_lower( str ). endif. * current equipment data: rownumber type i. rownumber = s10contextinfo( )->rownumber. read table tabequi index rownumber assigning field-symbol(<equi>). call method /s10/attachment=>create_attachment exporting objtype = 'EQUI' objkey = <equi>->equnr docfilename = docfilename doctitle = doctitle doctype = doctype data_url = data_url. * refresh detail view <equi>->s10detailview = 'X'. * set table key and read detail attributes create object myequi. myequi->equnr = <equi>->equnr. myequi->tplnr = <equi>->tplnr. myequi->s10databaseread( keylist = 'equnr,tplnr' ). * clear data clear: data_url. endmethod. |
Remarks
Lines 3 und 4
Create the "data_url" variable in the "public section" of the class.
Line 17
We split the parameter composed in JavaScript again into the components filename and title. Alternatively, we could have proceeded in JavaScript as with the file content and filled two hidden <input> element, each assigned to ABAP attributes.
Line 32
We need the current equipment number, which we take from the active table row.
Line 37
Now the attachment is added. The object type, here "EQUI", and the structure of the key, here only the equipment number, depends on the respective SAP object.
Lines 46-53
We re-read the equipment details and make sure that the details area is updated (line 47). This means that the attachment just added now appears in the attachment list.