Im SAP System werden Dokumente zu einem Objekt meistens über die "Objekt Services" als "Attachments" abgelegt. In Transaktion IE02 (Ändern Equipment) sieht das zum Beispiel wie folgt aus:
Zahlreiche Dateitypen sind dabei möglich; gebräuchlich sind PDFs, Word- und Excel-Dateien sowie Bilddateien im .jpg oder .png Format.
In diesem Tutorial integrieren wir die Dokumentenanzeige in eine S10 Anwendung. Unser Startpunkt ist eine Liste von Equipments, die wir uns mit den S10 Utilities, Transaktion /s10/util, generiert haben:
In diese Basisliste bauen wir die Dokumentenanzeige ein. Bei Klick auf eine Equipmentzeile erscheinen einige Details zum jeweiligen Equipment sowie die Anlagenliste:
In der Liste der Dokumente wird bei einem Klick das Dokument entweder extern angezeigt (Word- und Exceldateien) oder inline in die Liste eingebettet (Bilddateien, PDF, Video). Der Browser bietet dabei in der Regel die Möglichkeit, das Dokument nachträglich statt in der Liste als eigenständige Anwendung anzuzeigen. Bei Word und Excel ist zur Zeit in den gängigen Browsern keine eingebettete Anzeige unterstützt.
Hier einige Beispiele:
Die Dokumente können jeweils einzeln auf- und zugeklappt werden. Bei Excel und Word wird die entsprechende Anwendung auf dem mobilen Gerät oder auf dem Desktop geöffnet:
Nun zur Implementierung. Wir nutzen die globale Klasse /s10/attachment, die Teil des S10 Demo-Pakets ist; sie stellt einige generelle Services bereit, um Dokumente zu SAP Objekten zu lesen. Damit wird das ABAP-Programm recht übersichtlich:
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. |
Zur Erläuterung ist die Verbindung zur HTML-Seite an einigen Stellen erforderlich:
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">Equipmenttext</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">Technischer Platz </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">Serialnummer </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'>Maximale Anzahl</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'>Anzahl selektiert</label><br /> <div class='output' style="font-size: 14px; padding-top: 6px; font-weight: bold;" name='tabequi@rowcount'> </div> </div> <div class="processingmessage">Daten wurden selektiert ...</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;"> Es wurde nichts selektiert </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'>Dokumente</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">Keine Dokumente abgelegt</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> |
Erläuterungen
Die Zeilennummern A... beziehen sich auf das ABAP-Coding, H... auf die HTML-Datei.
Zeile A3
class equi_detail definition inheriting from /s10/any.
Die Klasse "equi_detail" beschreibt die Detailsicht des Equipments, nach Selektion einer Zeile in der Liste. Die Attribute (Zeilen A9 bis A13) werden in HTML ab H155 angezeigt. Wenn Sie weitere Infos zu dem Equipment anzeigen wollen, die in dem Datenbank-View V_EQUI enthalten sind, genügt es, diese in der Klasse noch dazuzunehmen und in der HTML-Datei auszugeben. Das Lesen aller Attribute geschieht durch s10databaseread( ) in Zeile A192.
Zeile A15
* attachments data: attachment type ref to /s10/attachment, attachments type table of ref to /s10/attachment.
Definiert ein attachment-Objekt und die Tabelle der Attachments. Die Ausgabe der Tabelle erfolgt in HTML ab Zeile H181 bis H209. Die Spalten doctitle, doctype und docsize_out sind als Attribute der Klasse /s10/attachment definiert.
Neben den sichtbaren Spalten wird der Dokumentenschlüssel unsichtbar in der Tabelle vermerkt: H191 <div class='outputcelldiv linkkey' name="dockey"></div> Bei Klick auf eine Attachmentzeile wird in ABAP dieser Schlüssel durch s10contextinfo() gelesen:
method on_detail_attachments. create object attachment. attachment->dockey = s10contextinfo( )->getvalue( 'dockey'). endmethod.
Zeile A37
Die Tabelle der Attachments pro Equipment ist als build-Methode realisiert. Zur Impelmentierung wird die Methode "list_attachments" der Klasse /s10/attachment aufgerufen:
/s10/attachment=>list_attachments( exporting instid = equnr typeid = 'EQUI' catid = 'BO' importing attachments = attachments ).
Für andere Objekte müssen Sie hier die richtige "typeid" mitgeben, das ist der "Business Object Type" (Transaktion SWO2). Für eine Bestellanforderung wäre es zum Beispiel "BUS2105". Die Katalog-Id ist immer "BO" für "Business Object".
Zeile A64
class equi_short definition inheriting from /s10/any.
Die Klasse "equi_short" beschreibt die Zeile der Equipemntliste. WIr benötigen hier nur die Equipementnummer und den Technischen Platz. Die entsprechenden Texte werden durch das S10 Framework in der Ausgabe durch die Notation "...@text" ergänzt. In der HTML Datei finden Sie die Ausgabe der Tabelle ab Zeile H125, beginnend mit
<form class='table' name='tabequi'>
Ab Zeile A119
class equi_manager implementation.
Der Rest des Programms ist unabhängig von der Anzeige der Dokumente; er ist durch die S10 Utilities generiert.
Der zentrale Abschnitt in der HTML-Datei ist die Anzeige des Dokuments ab Zeile H211:
<!-- Document display area --> <div class='tabledetail' id="myequi.attachments_detail"> <div class="outputhtml" name="myequi.attachment.display_html"> </div> </div>
Diese Abschnitt wird automatisch hinter die ausgewählte Zeile der Attachment-Liste eingeblendet, sobald der Benutzer auf eine Attachment-Zeile klickt, da
<div class="tablerow" onclick="S10ToggleDetail();">
definiert ist in Zeile H188.
Es wird zunächst die ABAP-Methode
method on_detail_attachments.
create object attachment.
attachment->dockey = s10contextinfo( )->getvalue( 'dockey').
endmethod.
des Objekts "myequi" gerufen, da die Tabelle "attachments" im Objekt "myequi" definiert ist. Allerdings passt das Objekt "myequi" im ABAP Programm immer zur letzten ausgewählten Equipmentzeile; es ist ja nur einmal vorhanden. Wenn der Benutzer zunächst einige Equipments aufklappt und dann in einem der aufgeklappten Bereiche ein Attachment anzeigt, nutzt uns die Zeilennummer aus HTML im ABAP Programm nichts, da die aktuelle attachment-Tabelle zu einem anderen Equipment gehören kann. Diese Schwierigkeit entsteht durch die geschachtelte Struktur der Liste, also Liste der Equipments und pro Equipment eine Liste der Attachments.
Aus diesem Grund nutzen wir nicht die Zeilennummer und die aktuelle Tabelle "attachments", sondern greifen durch
attachment->dockey = s10contextinfo( )->getvalue( 'dockey').
auf den in der HTML-Zeile versteckten Dokumentenkey zu und setzen ihn in das attachment-Objekt. Da in der HTML-Seite das Attribut "display_html" ausgegeben wird:
<div class="outputhtml" name="myequi.attachment.display_html">
läuft nun die build-Methode des Attributs "display_html" der globalen Klasse "/s10/attachment" ab. Die build-Methode liest das Dokument ein und baut den richtigen HTML-Code zur Anzeige des Objekts auf. In unserer HTML-Datei ist es hier wichtig, mit class="outputhtml"statt mit class="output" zu arbeiten, da andernfalls der HTML-Code angezeigt würde, statt ihn als HTML zu interpretieren.
Unsere Dokumentenanzeige wäre einfacher zu implementieren, wenn wir die Informationen pro Equipment und pro Attachment nicht in die Liste integrieren würden, sonden jeweils in separat aufgerufenen Seiten anzeigen würden, aus denen der Benutzer dann zur Liste zurückgeht. Der Vorteil der gewählten Darstellung ist, dass die einmal aufgeklappten Teile, zum Beispiel zwei Fotos, in der Liste simultan sichtbar bleiben oder zumindest durch Blättern leicht erreichbar sind. Zum Beispiel wären zwei Fotos zu einer Schadensmeldung dann unmittelbar untereinander sichtbar.