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 sowie den Upload 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:
ABAPprogram zzequi00.
class equi_detail definition inheriting from /s10/any.
public section.
* Geräteigenschaften
data:
equnr type v_equi-equnr, " Gerätenummer
tplnr type v_equi-tplnr, " Technischer Platz
eqtyp type v_equi-eqtyp, " Gerätetyp
inbdt type v_equi-inbdt, " Inbetriebnahmedatum
sernr type v_equi-sernr. " Seriennummer
* Anhänge
data:
attachment type ref to /s10/attachment,
attachments type table of ref to /s10/attachment.
* Name der Datenbanktabelle
constants:
dbtablename type string value 'v_equi'.
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 ).
* Anhänge sortieren
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.
* Tabellenfelder für Detailansicht sowie Schlüsselfelder
data:
equnr type v_equi-equnr,
tplnr type v_equi-tplnr.
* Name der Datenbanktabelle
constants:
dbtablename type string value 'v_equi'.
endclass.
class equi_short implementation.
endclass.
class equi_manager definition inheriting from /s10/any.
public section.
* Tabellenfelder für Listenansicht sowie Schlüsselfelder
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,
* Methode zum Aufbau von 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.
* Listbildschirm anzeigen
method list.
s10nextscreen( 'list' ).
endmethod.
* Datenbankwerte selektieren und Tabelle tabequi füllen
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.
* Daten lesen
s10databaseselect(
exporting
dbtablename = join
condition = condition
maxrows = maxrows
distinct = 'X'
fieldlist = 'v_equi~equnr,v_equi~tplnr'
changing
folder = tabequi ).
* Tabelle sortieren
s10sort(
foldername = 'tabequi'
columns = 'equnr,tplnr'
userformat = 'X' ).
endmethod.
* Details in der Liste anzeigen (Zeilenauswahl)
method on_detail_tabequi.
* Aktuelle Tabellenzeile lesen
data: tabindex type i.
tabindex = s10actionparameter( ).
read table tabequi
index tabindex
assigning field-symbol(<row>).
* Tabellenschlüssel setzen und Detailattribute lesen
create object myequi.
myequi->equnr = <row>->equnr.
myequi->tplnr = <row>->tplnr.
myequi->s10databaseread(
keylist = 'equnr,tplnr' ).
endmethod.
endclass.
* Hauptklasse für diese Anwendung
class main definition inheriting from /s10/any.
public section.
* Manager-Objekt
data:
my_equi_manager type ref to equi_manager.
methods:
logon.
endclass.
class main implementation.
* Benutzer anmelden
method logon.
* S10-Lizenz setzen
s10setlicense(
'Synactive GmbH demo license number=100
role=s10demo_role maxusers=10
signature=821.126.87.7' ).
* Manager-Objekt erzeugen
create object my_equi_manager.
* Listenanzeige starten
my_equi_manager->list( ).
endmethod.
endclass.
Zur Erläuterung ist die Verbindung zur HTML-Seite an einigen Stellen erforderlich:
HTML<!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;">
<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">
<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>
<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);">
<div class='outputcelldiv'
style="width: 100px;
max-width: 100px;"
name="equnr">
</div>
<div class='outputcelldiv'
style="width: 260px;"
name="equnr@text">
</div>
<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'>
<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>
<div class="infoblock">
<label class='label output'
name="myequi.inbdt">
</label>
<br />
<span class='output'
name='myequi.inbdt'>
</span>
</div>
<div class="infoblock">
<label class='label output'
name="myequi.sernr">
</label>
<br />
<span class='output'
name='myequi.sernr'>
</span>
</div>
<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();">
<div class='outputcelldiv linkkey'
name="dockey">
</div>
<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>
<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
ABAP
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
ABAP* 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:
ABAPmethod 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:
ABAP/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
ABAPclass 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
HTML<form class='table' name='tabequi'>
Ab Zeile A119
ABAPclass 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:
HTML<!-- 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
HTML<div class="tablerow" onclick="S10ToggleDetail();">
definiert ist in Zeile H188.
Es wird zunächst die ABAP-Methode
ABAP
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.
Upload von Dokumenten
Nun zum Hochladen von Dokumenten und Abspeichern als Attachment. Das Vorgehen hierzu ist wie folgt:
Schritt 1: In HTML die Auswahl der gewünschten hochzuladenden Datei aufnehmen
<label class='label'>Titel für neues Dokument</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)">
Dokument auswählen und hochladen
<span class="processingmessage">Upload läuft</span>
</button>
Erläuterungen
Zeilen 4, 8, 15: data-locid=
In unserer Anwendung wird die Upload-Möglichkeit unterhalb der Attachment-Liste innerhalb des Detailbereichs der Tabellenzeile angezeigt. Es kann also, wenn der Benutzer für mehrere Zeilen nacheinander den Detailbereich öffnet, mehrfach erscheinen. Aus diesem Grund können wir zur Identifizierung der HTML-Elemente aus JavaScript nicht das gebräuchliche id= verwenden, da in HTML die vergebene id in der HTML-Seite eindeutig sein muss, um mit document.getElementById() zu funktionieren. Das Attribut data-locid gestattet uns, ein Element innerhalb des geöffneten Detailbereichs mit der S10-Farmework-Funktion S10 ElementByLocid() zu adressieren (siehe JavaScript in Schritt 2), es ist sozusagen eine lokal verwendbare Id.
Zeile 7: <input type=file>
Hierduch wird in HTML die Auswahl einer Datei durch den Benutzer ermöglicht. Mit accept=... geben wir die Dateikennungen an, die bei der Auwahl möglich sein sollen. In Zeile 10 wird das Element mit style="display:none" versteckt, da wir nicht die HTML-Standarddarstellung nutzen wollen, sonden im Anschluss in Zeile 14 einen eigenen Button zur Dateiauswahl vorsehen.
Der oben angegebene HTML-Teil kommt in den Detaibereich der Tabellenzeile. Zusätzlich benötigen wir ausserhalb des Dateilbereichs ein einzelnes verstecktes <input>-Element für die S10 Variable, mit der wir den Dateiinhalt in unser ABAP-Programm transportieren lassen:
HTML<input type="hidden"
class="input"
name="data_url"
id="data_url" />
In unserem ABAP-Programm wird hierzu (Schritt 3) eine Variable "data_url" definiert, in der wir den Dateiinhalt erhalten:data:
data_url type string.
Schritt 2: Mit Javscript-Mitteln den Inhalt der ausgewählten Datei in eine versteckte S10 Variable übertragen
Fügen Sie folgendes JavaScrpt-Coding in der HTML-Seite ein. Wo es sich befindet, spielt keine Rolle, also in <head> oder <body>. Falls Sie das Hochladen von Dokumenten in mehreren HTML-Seite benötigen, am besten als eigene Datei ablegen und mit <script src="..."> </script> referieren.
HTML<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(
'Bitte geben Sie zunächst einen Titel ein'
);
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) {
return;
};
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: Bitte nur Dateien < 1MB hochladen"
);
return;
};
document.getElementById(
'data_url'
).value = result;
S10Apply(
'save_attachment',
attachment_filename + '\t' +
attachment_title,
attachment_button
);
};
</script>
Erläuterungen
Zeile 9 Funktion "start_upload_attachment"
Die Funktion wird von den Upload-Button aufgerufen. Sie prüft, ob der Benutzer einen Titel eingegeben hat, setzt einige globale Variablen und ruft dann die Dateiauswahl auf, indem das versteckte <input type='file'> Element geklickt wird.
Zeile 32 Funktion "upload_attachment"
Diese Funktion wird aus dem <input type='file'> Element durch onchange= aufgerufen, siehe HTML-Ausschnitt oben Zeile 11, sobald der Benutzer eine Datei ausgewählt hat. WIr besorgen uns das "File object" der ausgewählten Datei und lesen die Datei im Format "Data URL" in Zeile 47 ein. Das Dateilesen führt das Javascript "File Object" asynchron aus. Wir geben deshalb in Zeile 45 davor die JavaScript-Funktion an, die aufgerufen wird, wenn die Datei vollständig eingelesen ist und wir auf den gesamten Inhalt zugreifen können.
Zeile 52 Funktion "complete_upload_attachment"
Diese Funktion wird aufgerufen, sobald die Datei vollständig eingelesen ist. Wir können auf den Inhalt der Datei über die Komponente "result" des JavaScript File Readers zugreifen. In Zeile 59 prüfen wir die Dateigrösse und geben bei zu grosser Datei eine Fehlermeldung aus.
Hinweis: In mobilen Geräten steht bei der Dateiauswahl meist auch die Möglichkeit zur Verfügung, ein Foto zu machen und hochzuladen. Durch die grosse Auflösung heutiger Geräte entstehen dabei unter Umständen sehr grosse jpeg-Dateien. Sie können derartige Bilddateien zunächst intern stärker komprimieren und dann hochladen (canvas-Element in HTML verwenden).
<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(
'Bitte zunächst einen Titel eingeben'
);
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 (multiple 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: Bitte nur Dateien < 1MB hochladen"
);
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: Bitte nur Bilder < 1MB (komprimiert) hochladen"
);
return;
};
// upload content as data url
document.getElementById(
'data_url'
).value = data_uri;
S10Apply(
'save_attachment',
attachment_filename + '\t' +
attachment_title,
attachment_button
);
};
</script>
Den Inhalt der Datei setzen wir in Zeile 66 in die versteckte Variable "data_url". Anschliessen rufen wir in Zeile 68 die ABAP-Funktion "save_attachment" auf, der wir den Dateinamen und, durch ein TAB-Zeichen getrennt, den vom Benutzer eingegebenen TItel mitgeben. Nun kann auf ABAP-Ebene das Abspeichern der Datei als Attachment erfolgen.
Schritt 3: In ABAP den Dateinhalt aus der S10 Variablen übernehmen und mit der Klasse "attachment" des S10 Frameworks als Attachment zum Equipment abspeichern
Ergänzen Sie Ihr ABAP-Programm um folgendes Coding:
ABAP* Variable für Upload-Dateiinhalt
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.
Erläuterungen
Zeilen 3 und 4
Die Variable "data_url" in der "public section" der Klasse anlegen.
Zeile 17
Den in JavaScript zusammengesetzten Parameter zerlegen wir wieder in die Bestandteile Dateiname und Titel. Alternativ hätten wir in JavaScript wie beim Dateiinhalt vorgehen können und zwei weitere versteckte <input> Element füllen, die jeweils ABAP Attributen zugeordnet sind.
Zeile 32
Wir benötigen die aktuelle Equipmentnummer, die wir der aktiven Tabellenzeile entnehmen.
Zeile 37
Nun wird das Attachment hinzugefügt. Der Objekttyp, hier "EQUI", und der Aufbau des Schlüssels, hier nur die Equipmentnummer, hängt von dem jeweiligen SAP Objekt ab.
Zeilen 46-53
Wir lesen die Equipmentdetails neu und sorgen dafür, dass der Detailbereich aktualisiert wird (Zeile 47). Damit erscheint das gerade hinzugefügte Attachment nun in der Attachmentliste.