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.