Ein IDoc ist das Standardformat für den Datenaustausch zwischen SAP-ERP-Systemen (kurz SAP-System). In einem häufig genutzten Szenario werden IDocs über das SAP PI/PO verschickt, um dort in ein Format umgesetzt zu werden, welches von einem nicht-SAP-Produkt verarbeitet wird.
Doch wie funktioniert die Kommunikation zwischen dem SAP-System und der SAP PI/PO? Das folgende Szenario beschreibt beispielhaft das Versenden eines IDoc aus dem SAP-System an die SAP PI/PO. Die Betrachtungen beziehen sich hauptsächlich auf den Sendervorgang im SAP-System und sind daher auch auf ein Szenario mit der SAP Cloud Platform Integration (CPI) anwendbar.
Grundlegendes
IDocs zwischen SAP-Systemen (oder der SAP PI/PO) werden über einen Remote Function Call (RFC) ausgetauscht, bei dem ein Funktionsbaustein im Zielsystem aufgerufen wird. Die einfachste Möglichkeit eines RFC-Aufrufs ist der synchrone RFC-Aufruf (sRFC). Dabei geschieht der Aufruf im Zielsystem sofort und der Aufrufer wartet auf die Verarbeitung. Der Vorteil ist, dass auf die Antwort der Gegenseite direkt reagiert werden kann. Der Nachteil ist, dass es zu Fehlern kommt, wenn die Gegenseite nicht er riechbar ist (z.B. durch hohe Auslastungen).
Für größere Datenmengen (wie dem Stammdatenaustausch) eigene sich der sRFC-Aufruf daher nicht. IDocs werden daher über einen transaktionalen RFC (tRFC) verschickt. Hierbei werden die ausgehenden Nachrichten zunächst in eine Warteschlange (Queue) gestellt und dann verarbeitet. Ist das Zielsystem einmal nicht erreichbar, können fehlgeschlagenen Nachrichten noch einmal verarbeitet werden.
Für einen solchen tRFC-Aufruf beim IDoc-Versand zwischen SAP-System und SAP PI/PO sind prinzipiell zwei Voraussetzungen zu erfüllen. Zum einen muss die grundlegende Netzwerkverbindung aufgebaut werden. Dies geschieht in der Transaktion SM59. Dort wird eine Verbindung vom Typ T („TCP/IP-Verbindung“) erstellt. Die SAP PI/PO muss sich dabei als registriertes Serverprogramm anmelden (Details dazu folgen in einem weiteren Blog-Beitrag). Die zweite Voraussetzung ist ein logischer Port (Transaktion WE21). Dort muss ein Port vom Typ „Transaktionaler RFC“ angelegt werden, entsprechend muss hier die eben erstellte RFC-Verbindung hinterlegt sein.
Sind diese beiden Voraussetzungen erfüllt, kann der Port in den Partnervereinbarungen (WE20) verwendet werden. In den Partnervereinbarungen müssen dann pro Empfänger (hier in diesem Szenario die SAP PI/PO) und Nachrichtenart die entsprechenden Versand-Einstellungen vorgenommen werden.
Es sei noch erwähnt, dass mithilfe von Verteilungsmodellen (Transaktion BD64) die Verbindungen zentral verwaltet werden können. Zusätzlich ist es möglich, die Einstellungen z.B. für Partnervereinbarungen generieren zu lassen.
Der Unterschied von tRFC und qRFC
Für die Verarbeitung von IDoc gibt es prinzipiell zwei Möglichkeiten. Die bereits vorgestellt Variante per tRFC zeichnet sich dadurch aus, dass die Abarbeitung der Queue nicht davon abhängt, in welcher Reihenfolge die IDoc erstellt werden. Das heißt, dass die Daten in einer beliebigen Reihenfolge im Zielsystem ankommen können. Gibt es Fehler bei der Verarbeitung einer Nachricht, hat das keine Auswirkung auf folgende Nachrichten.
Ist es nötig, dass die Reichenfolge der Verarbeitung eingehalten wird (z.B. Anlage von Stammdaten, anschließend Update von Datensätzen), kann die Verarbeitung auch auf queued RFC (qRFC) umgestellt werden. Auch in diesem Fall gibt eine Verarbeitungs-Queue, die allerdings immer in der Reihenfolge der Nachrichtenerstellung abgearbeitet wird. Kommt es zu einem Fehler bei der Verarbeitung, werden nachfolgende Nachrichten nicht abgearbeitet, sondern bleiben in der Warteschlange stehen.
Wie stellt man das Verfahren von tRFC auf qRFC um? Zunächst muss im Port (Transaktion WE21) das Kennzeichen „Queueverarbeitung wird unterstützt“ gesetzt werden. Ist das Empfangssystem eine SAP PI/PO oder ein anderes SAP-System, ist diese Unterstützung gegeben.
Anschließend kann die qRFC-Verarbeitung in der Partnervereinbarung pro Nachrichtentyp aktiviert werden.
Story vom verlorenem IDoc
Um die Analyse im Fehlerfall besser zu verdeutlichen, soll folgendes Szenario dienen. Ein SAP ERP-System verteilt Kostenstellen (IDoc COSMAS) an ein Fremdsystem. Dazu werden die IDoc an die SAP PI/PO geschickt, damit die Nachrichten dort für das nicht-SAP-Zielsystem gewandelt werden. Die IDocs werden per Standard-Report RBDSECOS erstellt und sind im IDoc-Monitor (WE02) auch im Status 03 („Datenübergabe an Port OK“) zu sehen. In der SAP PI/PO tauchen die Nachrichten allerdings nicht auf (Message Monitor), auch der Sender-Kanal weist keine Fehler auf (Channel Monitor). Die IDocs scheinen verschwunden zu sein.
Wenn der Message Monitor keine Nachrichten anzeigt (auch keine fehlerhaften), sollte die Suche im Sendersystem (SAP ERP) beginnen. Der Status 03 („Datenübergabe an Port OK“) ist tatsächlich nicht der End-Status, indem sich ein IDoc befinden kann. Dieser Status zeigt nur an, dass die Nachricht an die Queue (entweder tRFC oder qRFC) übergeben wurde, nicht, dass diese wirklich versandt wurde. Mit der Transaktion BD75 kann der Queue-Status überprüft werden und der eigentliche End-Status 12 („Versand OK“) gesetzt werden. Setzt man das Kennzeichen „Nicht versandte IDocs anzeigen“, erhält man zusätzlich eine Ausgabelist mit IDocs, die noch in der Queue hängen geblieben sind.
Gibt es IDocs, die noch in der Warteschlange stehen, kann man die eigentlichen Queues betrachten, um dort weiter Hinweise auf den Fehler zu finden. Für die tRFC-Queue gibt es Transaktion SM58, für die qRFC-Queue SMQ1.
Hier in diesem Beispiel sieht man eine fehlgeschlagene Nachricht im qRFC-Monitor (SMQ1). Obwohl nur eine Nachricht einen Fehler hat, bleiben auch alle folgenden Nachrichten hängen.
Jetzt kommt es auf den individuellen Fehler an, wie die Lösung aussieht. Handelt es sich zum Beispiel um eine Fehlerkonfiguration in der SAP PI/PO, muss der Fehler dort behoben werden. Ist der Fehler behoben und das IDoc neu erstellt, muss die Verarbeitung der Queue fortgesetzt werden. Dazu platziert man den Cursor in der entsprechenden Fehlerzeile und lösche die Zeile aus der Queue (Mülleimer-Icon). Erst jetzt können die anderen Nachrichten nachverarbeitet werden, entweder einzeln über das „Ausführen“-Icon oder auf dem vorherigen Bildschirm über die Funktion „Queue aktivieren“. [...]
Read more...
In some situations, it is useful to be able to manually export and save IDocs as files. A typical use case is before a QA-System refresh. You don’t want to fully lose all your hard work Test-IDocs. Then, you can export and save them as file for reference, and you can reimport them later with the WE19 test-tool after the system refresh.
We present three different ways of exporting IDocs with different outcomes:
Export as an Excel list of segment fields and values. This format is not the official IDoc protocol format and thus you cannot “import” it as an IDoc in a target system
Export as XML file IDoc – requires use of developer tools (SE37)
Export as IDoc file or XML-IDoc with the IDoc test-Tool WE19
The last two ways produce files that can be re-imported in a SAP system.
1. Export as an Excel list of segment fields and values
Start from the IDoc List transaction WE02, open an IDoc to display, and then use the somewhat less known menu function “Print IDoc”.
This will display the IDoc as a flat list of segments with field-names, descriptions and values. The list can then simply be saved as a local file or exported as a Spreadsheet with the standard ALV functionality.
2. Export as XML-IDoc file by using an ABAP function
Start the ABAP function workbench SE37 and test-execute the function module IDOC_XML_TRANSFORM. Enter the IDoc number to be exported in parameter DOCNUM and execute. You will get something looking like IDoc-XML. Unfortunately it has additional spaces added in the XML tags, which makes it actually not real XML.
To get the correct XML, you can copy-paste the displayed output to a text editor and just remove the spaces in the XML tags as needed.
Alternatively, the ABAP debugger can be used to display the correct XML-IDoc, before it is being transformed with the additional spaces: The breakpoint must be put after the method call idoc->get_xmldata_as_string and then you can just display the variable “str” with the XML browser debugger view:
3. Export as XML-IDoc file or “standard” IDoc-file with WE19 and CG3Y
This is a somewhat tangled way of proceeding – but it produces standard IDoc-file or XML-IDoc files that are in the IDoc protocol format and can be re-imported in any target SAP system
Prerequisites:
There is a File (or XML-File) port available. This can be checked in transaction WE21. Additionally, the Port Outbound-File directory must be accessible. You can test this with the button “access test”.
One is allowed to add dummy IDoc Partner-agreement to a dummy receiver system for example the SAP dev system.
Authorization for WE19
Authorization for CG3Y. By the way, CG3Y is only needed to fetch the IDoc-XML files from the SAP server. You can use any other (maybe non standard) other tool for that purpose.
Steps:
Add a dummy partner agreement in WE20 for example to the SAP-Dev system for the given message and IDoc type and the file port.
Call WE19 with the IDoc number to be exported.
Adjust the control record.
Receiver data to match the partner-agreement created in previous step
Also remove and clear all control record “reference” and date fields
If it’s an inbound IDoc, also adjust the sender partner data to be the current SAP logical system.
Push button “standard outbound processing“.
Normally, the IDoc file is created and saved on he server directory as specified by the IDoc File port.
Call CG3Y to fetch the server file and download it to you desktop. [...]
Read more...
ERPNext stellt Mengeneinheiten (UOMs) wie „Stunde“ und „Stück“ in Druckformaten exakt so dar, wie sie im System hinterlegt sind – im Regelfall also in der Einzahl (Singular), auch wenn die Anzahl mehr als eins beträgt. Für deutsche Dokumente ist es jedoch sprachlich korrekt – und sieht professioneller aus – bei Mengen >1 z. B. „Stunden“, an Stelle von „Stunde“ anzuzeigen.
In diesem Blogbeitrag zeigen wir, wie Sie in in Ihren ERPNext.Druckdokumenten auch die Pluralformen von UOMs dynamisch – also passend zur Menge – anzeigen lassen können… mithilfe eines benutzerdefinierten Feldes und etwas Jinja-Logik.
Schritt 1: Benutzerdefiniertes Feld im UOM-Doctype hinzufügen
Zuerst erstellen wir ein benutzerdefiniertes Feld im UOM/Einheit-Doctype:Feldname z. B.: custom_plural_uomBezeichnung: Plural UOMFeldTyp: Data
Hier können die Anwender:innen später „Stunden“, „Tage“, „Wochen“ etc. eintragen.
Im Ergebnis sieht die Übersicht mit den Pluralformen dann so aus:
Schritt 2: Feld im Druckformat verwenden
Für Mengenangaben prüfen wir die Anzahl. Ist diese größer als 1, wird die neu eingeführte Pluralform verwendet.
Ergebnis:
Mit dieser kleinen Erweiterung wirken Ihre ERPNext-Dokumente deutlich professioneller. Ob „Stunden“, „Tage“ oder andere Einheiten – mit korrekten Pluralformen sorgen Sie für mehr Sprachqualität und Lesbarkeit. Und natürlich können Sie die Pluralformen mit der internen Übersetzungstechnik auch in den Zielsprachen Ihrer Dokumente korrekt anzeigen.
Gern unterstützen wir Sie bei der Einführung dieser Erweiterung in Ihrem ERPNext-System… [...]
Read more...
Business Use Case
In vielen Unternehmen werden Kundenaufträge automatisiert per IDoc in das SAP-System eingespielt – etwa durch EDI-Schnittstellen mit Großkunden. Was zunächst wie ein effizienter Prozess klingt, birgt jedoch Risiken: Falsch befüllte Daten, unvollständige Informationen oder fehlerhafte Preisfindungen können zu Problemen in der weiteren Auftragsabwicklung führen. Ohne eine manuelle Kontrolle schleichen sich solche Fehler leicht unbemerkt ins System ein. Besonders in komplexen Vertriebsprozessen oder bei sensiblen Kundenbeziehungen ist es daher sinnvoll, eine Qualitätssicherung vorzuschalten. Hier kommen SAP-Workflows ins Spiel: Sie ermöglichen es, eingehende Aufträge automatisiert an definierte Bearbeiter zur Prüfung und Freigabe weiterzuleiten. So lassen sich Fehler frühzeitig erkennen und beheben – bevor sie kostspielige Auswirkungen haben. Der Workflow kann dabei flexibel an verschiedene Prüfregeln und Verantwortlichkeiten angepasst werden. In diesem Blogbeitrag zeige ich, wie ein solcher Prozess in SAP umgesetzt werden kann.
Lösungsansatz
Kundenaufträge, die aus IDocs entstehen, werden mit einer entsprechenden Fakturasperre – in unserem Fall 02 („Rückmeldung fehlt“), es können auch neue Sperren für diesen Fall definiert werden. Diese Fakturasperre kann im Kunden bereits gesetzt oder – was aus prozessualer Sicht empfohlen wird – in einer Middleware oder bei der IDoc-Eingangsverarbeitung im ERP-System gesetzt werden. Beim Anlegen eines Kundenauftrags wird automatisch ein Workflow gestartet, der die Prüfung und weitere Bearbeitung des Auftrags regelt. Die Erkennung von Aufträgen aus IDocs ist über die o. g. Fakturasperre (Kennzeichen) möglich.
Nach Prüfung und Erkennung der Sperre wird der Kundenauftrag als Workitem der*dem zuständigen Mitarbeiter*in zur Weiterverarbeitung zugewiesen. Dazu erfolgt eine Benachrichtigung per Expressnachricht (SAP Business Workplace). Die Annahme und Bearbeitung des Workitems führt zu einem Absprung in die Auftragsbearbeitung (VA02). Dort prüft die*der Anwender*in die Auftragsdaten, führt gegebenenfalls Korrekturen durch und entfernt schließlich die Fakturasperre. Damit ist der Auftrag zur weiteren Bearbeitung freigegeben.
Mit dem Sichern wird die Bearbeitungs-Aktivität abgeschlossen und der Workflow beendet.
Umsetzung
Um Kundenaufträge per Workflow bearbeiten zu können, wurde dem Workflow-Container ein Element vom Business-Objekttyp „BUS2032“ zugewiesen. Dieser Objekttyp beinhaltet bereits sämtliche Attribute, Ereignisse und Methoden, die für die Analyse und Bearbeitung von Kundenaufträgen nötig sind, soweit es das hier dargestellte Szenario betrifft. Sollten doch weitere benötigt werden, ließen diese sich auf Basis dieses Objekttyps anlegen, ohne bei Null anfangen zu müssen.
Schritt 1:Prüfen der oben definierten Bedingung, ob eine Bearbeitung des Kundenauftrags notwendig ist, bevor er freigegeben werden kann. Hierzu wird ein Schritt vom Typ „Bedingung“ angelegt.
Wie bereits angesprochen, wurde für dieses Beispiel das Vorhandensein einer Fakturasperre als Bedingung gewählt. Da der Fall, dass die Bedingung nicht erfüllt ist, keine weiteren Schritte beinhaltet, wird dieser hier auch nicht weiter dargestellt.
Schritt 2:Es wird dem Bearbeiter (üblicherweise bestimmt über eine Organisationseinheit) eine Nachricht geschickt (Schritttyp „Mail versenden“), um ihn über das Vorhandensein des Auftrags sowie die Notwendigkeit der Bearbeitung zu informieren. Diese Nachricht erhält der Mitarbeiter als Express System-Nachricht im System.
Schritt 3:Der Auftrag wird dem Bearbeiter als Workitem zugewiesen und eine Möglichkeit geboten, direkt in die Bearbeitung des Auftrags abzuspringen. Hierfür wird ein Schritt vom Typ „Aktivität“ verwendet, der eine Aufgabe mit Verweis auf die „Change“-Methode des verwendeten Objekttyps beinhaltet.
Schritt 4:Nachdem der Bearbeiter die Transaktion VA02 wieder verlassen hat, wird der Workflow beendet. Für den Umfang des Beispiel-Szenarios wurden keine weiteren Prüfungen/Aktivitäten nachgeschaltet. Inwieweit diese evtl. noch notwendig wären, hängt natürlich vom konkreten Anwendungsfall ab. [...]
Read more...
Als Unternehmen, das inzwischen intensiv mit ERPNext arbeitet, suchen wir ständig nach Möglichkeiten, das System an unsere individuellen Anforderungen und die Anforderungen unserer Kunden anzupassen. Oft besteht der Wunsch, benutzerdefinierte Felder in Dokumenttypen (Standard-Doctypes) hinzuzufügen, sodass die Daten nahtlos in andere Dokumentarten und Druckformate übertragen werden.
Anwendungsbeispiel (Use Case): Sie wollen bei der Anlage von Aufträgen einen individuellen Text eingeben können, zum Beispiel ein Hinweis auf laufende Aktionen oder die Ankündigung von Preisanpassungen etc. Dieser Text soll nach Bearbeitung des Auftrags in die Rechnung übernommen (kopiert) und auf dem Rechnungsformular ausgegeben werden.
Im Folgenden erklären wir Schritt für Schritt, wie wir diese Lösung in unser ERPNext-System integriert haben:
Schritt 1: Hinzufügen eines benutzerdefinierten Feldes zum Kundenauftrag
Mit der Funktion „Formular anpassen“ (Customize Form) können eigene Felder für Dokumenttypen definiert werden. Diese Felder können verschiedene Typen haben, Mussfelder sein etc. Im Layout-Editor können die Elemente relativ frei platziert werden. Für die weitere Verwendung muss ein technischer Name vergeben werden – in unserem Fall lautet dieser custom_additional_text.
Das neue Feld muss sowohl für Kundenaufträge als auch für Ausgangsrechnungen angelegt werden, damit es in beiden Dokumenttypen verwendet werden kann.
Schritt 2: Kopieren/Übernahme in das Rechnungsdokument
Für die Übernahme des Textinhalts in neue Rechnungen verwenden wir ein Clientskript. Dieses binden wir an die Ausgangsrechnung und setzen damit für neue Dokumente den Feldwert für custom_additional_text, den wir aus dem Vorgänger (Auftrag) übernehmen.
frappe.ui.form.on('Sales Invoice', {
onload: function (frm) {
if (frm.doc.custom_additional_text && frm.is_new) {
frm.set_value('custom_additional_text', frm.doc.custom_additional_text);
}
}
});
Schritt 3: Hinzufügen zur Druckausgabe
Zuletzt fügen wir das neue Feld zur Druckausgabe für Rechnungen hinzu. Dies können wir an beliebiger Stelle durch Ausgabe der Variable doc.custom_additional_text erreichen. In unserem Fall soll der Text direkt unter die Tabelle der Rechnungspositionen gedruckt werden. Das Ergebnis sieht so aus:
Diese kleine Anpassung eröffnet viele Möglichkeiten. Egal, ob personalisierte Notizen, Richtlinien-Updates oder spezielle Anweisungen hinzugefügt werden sollen – ein Feld „Zusätzlicher Text“ bietet die Flexibilität, Rechnungen individuell an den jeweiligen Auftrag anzupassen. Natürlich sind auch komplexere Logiken denk- und realisierbar. Zum Beispiel können Texte aus dem Kundenstamm gezogen oder dynamisch generiert werden. Haben Sie daran und an unseren Lösungsideen Interesse? Dann kontaktieren Sie uns gern: info@informatik-dv.com [...]
Read more...
Als IT-Dienstleister mit Schwerpunkt auf Beratung und Entwicklung im Bereich der SAP-Produkte haben wir uns stets darauf konzentriert, unseren Kunden optimale Lösungen für ihre Geschäftsprozesse zu bieten. Angesichts der ab dem Jahr 2025 schrittweise eingeführten Pflicht zur E-Rechnung haben wir uns entschieden, unseren eigenen Prozess zu modernisieren und das Open-Source-System ERPNext einzuführen. Dieses leistungsfähige ERP-System hat uns nicht nur überzeugt, sondern auch inspiriert, unsere Expertise auf ein neues Feld auszuweiten.
Im neuen Jahr wagen wir nun einen spannenden Schritt: Wir begleiten unseren ersten Kunden bei der Einführung von ERPNext als Ersatz für das bisher genutzte SAP ERP-System. Unser Angebot umfasst die vollständige Unterstützung – von der Installation über das Customizing und die Anpassung von Druckformularen bis hin zur Integration mit DATEV für die Finanzbuchhaltung und das Personalwesen (HR). Sollte sich eine Anforderung nicht durch Standard-Customizing lösen lassen, entwickeln wir maßgeschneiderte Erweiterungen, um die individuellen Bedürfnisse des Kunden zu erfüllen.
Mit diesem Projekt öffnen wir nicht nur ein neues Kapitel in unserer Unternehmensgeschichte, sondern möchten auch zeigen, dass ERPNext eine flexible, kosteneffiziente und leistungsfähige Alternative für Unternehmen darstellt. Unser Ziel ist es, unsere Kunden weiterhin bei der Digitalisierung ihrer Geschäftsprozesse kompetent zu begleiten – unabhängig davon, ob sie sich für SAP, ERPNext oder eine andere Lösung entscheiden. Wir freuen uns auf diese neue Herausforderung und darauf, gemeinsam mit unseren Kunden die Zukunft zu gestalten. [...]
Read more...
Motivation
In a modern business environment, managing communication efficiently is crucial. Email remains a vital medium for various types of communication, including inquiries, support requests, and transactional updates. However, categorizing and processing a large volume of emails manually is not only time-consuming but also prone to errors. Automating this process can lead to significant improvements in efficiency and accuracy.
Use Case
Our client uses SAP Solution Manager as central helpdesk and ticketing system. Since every IT problem is handled with a ticket, more than 100 tickets are created per day. Each ticket belongs to a certain category, with more than 350 categories to choose from. E-Mails to the central helpdesk address are created as unclassified tickets and manually classified as the next step. It is obvious that an automation of the ticket classification process can save hundreds of working hours and, thus, Euros per year.
Approach
To address this challenge, we aim to develop and deploy an AI model capable of automatically categorizing incoming emails based on their content. By leveraging machine learning, specifically Natural Language Processing (NLP), the model can learn from historical data and predict the correct category for new emails. This automation allows for faster response times and more streamlined workflows, ultimately enhancing the overall productivity of the team.
Workflow
The core of this project is the development of a machine learning model trained on a dataset of previously categorized emails. The model is designed to predict the correct category for new incoming emails, allowing for automatic sorting and routing to the appropriate department or workflow.
Preprocessing Data
To train the model, we started by gathering a large dataset of emails that had already been categorized by experts. This dataset was then cleaned and preprocessed to ensure the model could learn effectively. The preprocessing steps included:
Text Cleaning: Removing unnecessary characters, stopwords, and normalizing the text to ensure consistency.
Vectorization: Converting the email content into numerical features using techniques such as TF-IDF (Term Frequency-Inverse Document Frequency) to represent the importance of terms relative to the dataset.
Handling Imbalanced Data: Utilizing techniques like SMOTE (Synthetic Minority Over-sampling Technique) to balance the categories in the training data, ensuring the model does not favor more common categories over rarer ones.
Model Selection
For this project, we selected Logistic Regression as the machine learning model to classify the emails into predefined categories. Logistic Regression is a well-established, interpretable model that performs particularly well for binary and multi-class classification tasks. Its ability to output probabilities for class predictions allows for a clear understanding of the model’s confidence in its predictions, making it a reliable choice for this type of classification problem.
Why Use Logistic Regression?
Simplicity and Interpretability: Logistic Regression is simple to implement and interpret. The model’s coefficients provide insights into how each feature impacts the prediction, which can be particularly useful for understanding which words or phrases are most indicative of certain categories.
Performance: Despite its simplicity, Logistic Regression often performs competitively with more complex models, especially when the features are well-prepared, as they are with the TF-IDF vectorization used in this project.
Handling Imbalanced Data: Logistic Regression can be configured to handle imbalanced datasets through the use of class weights. In this project, we utilized the class_weight='balanced' option, ensuring that the model paid equal attention to all categories, regardless of their frequency in the dataset.
Training the Model
After preprocessing the data and vectorizing the text, the next step was to train the Logistic Regression model. Given the potential for imbalanced data, we first addressed this by applying SMOTE (Synthetic Minority Over-sampling Technique) or RandomOverSampler depending on the specific characteristics of the training data.
SMOTE was applied when the dataset had enough samples per category, allowing the generation of synthetic examples for minority classes.
RandomOverSampler was used as a fallback when any category had too few examples for SMOTE to function correctly.
The model was then trained using the resampled data to ensure balanced learning across all categories. We also set the maximum number of iterations (max_iter=1000) to give the model sufficient time to converge, particularly given the size and complexity of the dataset.
Evaluation
After training, the model was evaluated using a separate test set. The performance was measured through metrics such as precision, recall, and F1-score, which are particularly useful for understanding the model’s effectiveness in the context of imbalanced data.
The trained model showed promising results in accurately categorizing emails across the different predefined categories. The detailed classification report highlighted the model’s strengths and identified areas for potential improvement.
Save the Model
To facilitate easy deployment and future use, the trained Logistic Regression model, along with the TF-IDF vectorizer, was saved using joblib. This allows for quick loading and application of the model on new, unseen data without the need for retraining.
Results
After evaluating the model on the test data, we achieved an accuracy of 88%, meaning that the model correctly categorized 88% of incoming emails. However, it’s important to note that the remaining 18% were not entirely incorrect. In some cases, the model predicted a more general category than the specific one originally assigned by human operators. This indicates that while the prediction might not have matched the exact category, it still provided a valid classification that could be considered relevant for broader purposes. This flexibility suggests that the system can still save time and effort in many cases by offering general, yet useful, categorization. With these results, the model is already highly suitable for practical use and can be seamlessly integrated into the client’s workflow.
Next Steps
Moving forward, we plan to make this model available as an internal service that can be easily accessed by other teams within the organization. The AI-based ticket classification can be integrated into the existing SAP Solution Manager or any similar helpdesk systems, allowing for automated, scalable, and accurate ticket categorization. By transforming this solution into a centralized service, it can be reused across multiple departments, enhancing the overall efficiency of the ticketing process.
In the future, we aim to further improve the model by expanding the training data, ensuring it becomes even more accurate and reliable over time. As more tickets are processed and categorized, we will continue to add new categories and refine existing ones. This will allow the system to handle a broader range of inquiries, including rare cases.
Are you interested in our classification system or need support for your own AI endeavors? Please don’t hesitate to get in touch. We look forward to tackling new challenges in the field of AI. [...]
Read more...
Problem
In SAP Solution Manager, the start page can be customized to display different widgets, known as the „My Messages“ widgets.
In the setting, different filter, such as status or message type filter, can be configured. But one filter often behaves not as expected. Let’s talk about the Service Team filter.
The service team filter searches for messages, in which the service team, your business partner is assigned to, contributes. The problem is, how you define the „contribute“ part. For example, take a look into service requests. In service requests, usually a checklist is maintained. In this checklist, several steps can be assigned to responsible business partner, or even service teams.
Due to this, all messages are found as „assigned to my team“, in which the service team has a step in the checklist. This is independent of the step status, so you see messages where your step is not relevant. This behavior is often unwanted, but cannot be changed via customizing.
The desired outcome would be a list of messages (service request), where the support team is directly assigned (on the header level).
I will present a solution with only a few extensions to the coding. No SAP standard coding needs to be changed.
Background: BOL-Queries
To understand the search better, we can perform the search manually, similar to the automatic search by the widget.
In transaction GENIL_BOL_BROWSER, the search can be reproduced. Enter the component BT and select the query object BTQAICSearch. In the example here, a search with status, message type and service team is simulated. The search parameter SERVICE_TEAM is used by the widget and filled with the assigned service teams of your business partner. So the actual search behavior is determined by the search parameter.
A different search can be achieved, if we could change the search parameter. One step closer to our desired output, brings us an exchange of the search parameter SERVICE_TEAM with BU_PARTNER. The search parameter BU_PARTNER works together with the partner function. This search parameter is called /AICRM/PARTNER_FCT. Let’s try an adjusted search.
The partner function SLFN0003 corresponds to the service team, but depends on your configuration. Different partner functions can also be used.
But who can the standard search be influenced without massively changing the SAP standard coding?
Solution
The solution comes with extensibility of the WUI components. With transaction BSP_WD_CMPWB you can extend the coding of the different components. The component for the start page widgets is called AIC_IM_MYINCDNT. The trick here is, that the filters are dynamically created from the customizing in the component controller.
At first, create an enhancement for the component controller, which generates all the necessary classes in the customer namespace for you. You should always use the assistant to generate the enhancements.
Now you can freely redefine the methods in your z-class, the assistant took care, that your enhanced controller is called correctly. Open the new class and search for the function GET_FILTER_KEYS and redefine this method.
In the coding, start by calling the original (parent) method. With that, we do not lose any standard logic.
CALL METHOD super->get_filter_keys
EXPORTING
it_filter = it_filter
IMPORTING
et_statuskey_values = et_statuskey_values
et_transaction_types = et_transaction_types
et_team_values = et_team_values
et_processor_values = et_processor_values
et_reporter_values = et_reporter_values
EXCEPTIONS
no_team_assigned_to_bp = 1
no_bp_assigned_to_user = 2
no_valid_predefined_proctype = 3
no_transaction_types_found = 4
OTHERS = 5.
CHECK sy-subrc = 0.
CHECK et_team_values IS NOT INITIAL.
The following code is only executed, if the table ET_TEAM_VALUES is not empty. This prevents us from adding the search parameter for the partner function, if there are now searches for the team at all.
We can then simply replace the parameter SERVICE_TEAM with BU_PARTNER in the table.
LOOP AT et_team_values
ASSIGNING FIELD-SYMBOL(<fs_team>)
WHERE attr_name = ‚SERVICE_TEAM‘
AND option = ‚EQ‘.
<fs_team>-attr_name = ‚BU_PARTNER‘.
ENDLOOP.
CHECK sy-subrc = 0.
APPEND VALUE ltype_query_item(
attr_name = ‚/AICRM/PARTNER_FCT‘
sign = ‚I‘
option = ‚EQ‘
low = ‚SLFN0003‘ ) TO et_team_values.
One small detail: The loop goes only over entries with search option ‚EQ‘. In case you filter for „not assigned“, the generated search query contains „SERVICE_TEAM LE 1“, and we do not want to break this search.
Now, whenever you search with filter for the service team, the BOL query is replaced with the more specific search for the head partner function.
Bonus tip
This new search logic can be what most of your users want. Be maybe, some prefer the standard logic. You can make the logic switch dependent on a user parameter, and skip your adjustments, if the parameter is not set by the user. User parameter can easily be distributed via mass change (transaction SU10). Also, individual users can change their own parameter in transaction SU3.
In the coding, the read can be done with a standard function call. Just use the following code right before you adjust the team value table.
CALL FUNCTION ‚SUSR_USER_PARAMETERS_GET‘
EXPORTING
user_name = sy-uname
with_text = abap_true
TABLES
user_parameters = it_user_params
EXCEPTIONS
user_name_not_exist = 1
OTHERS = 2.
CHECK sy-subrc = 0.
DATA(lv_switch) = VALUE #( it_user_params-PARVA OPTIONAL ).
CHECK lv_switch IS NOT INITIAL. [...]
Read more...
Der Solution Manager im täglichen Einsatz
Mit dem SAP Solution Manager lassen sich Geschäfts- und IT-Prozesse abbilden, dokumentieren und verwalten. Die folgenden Bereiche werden durch den Solution Manager abgedeckt:
Requirements management
Project management
Process management
Test suite
Change request management (ChaRM)
IT service management (ITSM)
Landscape management
Von all diesen Anwendungsbereichen wird nach unserer Erfahrung das Change Request Management (ChaRM) am häufigsten eingesetzt. Die Vorteile, gerade für diesen Bereich, den Solution Manager zu benutzen, zeigen sich schnell, wenn man andere SAP Produkte in der Systemlandschaft einsetzt. Durch die direkte Integration des Solution Managers mit den anderen Systemen (insbesondere ABAP-Applikationsserver), lassen sich Transporte direkt über die ChaRM-Vorgänge steuern.
Aktuelle Entwicklungen
Moderne Softwareentwicklung folgt häufig dem agilen Projektmanagement. Damit die flexiblen Zyklen effizient verwaltet werden können, biete SAP mit Focus Build und Focus Insight Add-ons für den Solution Manager, welche speziell für die agile Softwareentwicklung gedacht sind. Weitere Informationen zu Focus Build sind auf den SAP-Support-Seiten zu finden.
Ein weiterer wichtiger Punkt ist, dass immer mehr Systeme von on-premise Lösungen in die Cloud umziehen. Auch nach dem Umzug von SAP R3 nach SAP S/4HANA kann man die Entwicklungen im Solution Manager verwalten. Darüber hinaus kann auch der Solution Manager selber in die SAP Cloud umziehen. Die WebUI wird dabei durch die moderne Fiori-Oberfläche ersetzt. Es sei allerdings erwähnt, dass der Umzug des Solution Manager nach SAP S/4HANA – je nach Umfang der Kundenanpassungen – ein zeitaufwendiges Projekt ist. Hier muss man abwägen, ob sich ein Umzug lohnt, insbesondere mit Hinblick auf den nächsten Abschnitt.
Die Zukunft des Solution Managers
SAP hat offiziell angekündigt, dass der Solution Manager nur noch bis 2027 (mit erweiterter Maintainance Lizenz bis 2030) unterstützt wird. Damit stellt sich automatisch die Frage nach einem Nachfolger. Wir stellen vor: SAP Cloud ALM (Application Lifecycle Management).
Parallelbetrieb und Migration: SAP Solution Manager und SAP Cloud ALM
SAP Cloud ALM soll die meisten Funktionen des Solution Managers übernehmen. Ein Blick auf die Möglichkeiten im Bereich ChaRM und die Verwaltung von angeschlossenen Systemen verrät aber, dass nur SAP Cloud Produkte unterstützt werden. Hat man also eine Hybridlandschaft aus Cloud und on-premise Systemen, muss man auch weiterhin den Solution Manager für die on-premise System verwenden (Parallelbetrieb).
So kann eine Migration nach SAP Cloud ALM gelingen
Die Migration vom Solution Manager zur Cloud ALM muss immer im Zusammenhang mit der restlichen Systemlandschaft gesehen werden. Die SAP Cloud ALM kann parallel zum Solution Manager aufgesetzt werden. Sobald ein verwaltetes System in die SAP Cloud umgezogen ist, kann auch dessen Landschaftsmanagement nach Cloud ALM umziehen.
Wichtig ist: Es gibt keine synchrone Datenhaltung zwischen dem Solution Manager und der Cloud ALM. Das heißt, ab der Migration kann man das verwaltete System nur noch über die Cloud ALM steuern.
Für die eigentliche Migration hat SAP geplant, Reports zur Verfügung zu stellen, um die Daten aus dem Solution Manager zu extrahieren. Damit ist allerdings die Migration eine einmalige Aufgabe, nicht eine regelmäßige Synchronisation. Nicht alle Daten (z.B. Geschäftspartner) können per Programm automatisch übernommen werden.
Der Migration gleicht daher mehr eine Neueinführung, die allerdings schrittweise erfolgen kann. Die Geschäfts- und IT-Prozesse müssen neu geplant und eingerichtet werden.
Deckt SAP Cloud ALM alle Bereiche des Solution Managers ab?
Tatsächlich werden die meisten Anwendungsbereiche aus dem Solution Manager durch den SAP Cloud ALM (eventuell mit Erweiterungen wie SAP Signavio Process Manager) abgedeckt. Auf den SAP Supportseiten lässt sich eine Matrix finden, die die entsprechenden Nachfolgeprodukte aufzeigt.
Ein Bereich fällt dabei jedoch raus: IT Service Management (ITSM).
Für den Bereich ITSM wird es von SAP keinen Nachfolger zum SAP Solution Manager geben. Wie auch im SAP Blog genannt, muss man sich hier bei Drittanbietern wie ServiceNow umsehen. Auch wenn die Einheit zwischen ITSM und ChaRM dann durch einen Systemwechsel gebrochen ist, bieten doch einige Anbieter direkte Integrationen an. Hier wird sich zeigen, inwieweit eine solche Integration in der Praxis funktioniert. [...]
Read more...
Motivation
Die Validierung postalischer Adressen ist sehr wichtig für die Datenqualität Ihrer Geschäftspartner-Adressen.
ERP Softwarehersteller wie SAP bieten in ihrer Software eine integrierte Adressvalidierung . Allerdings basieren diese auf regionalen Referenzdaten, die nicht mitgeliefert werden, sondern eingespielt und regelmäßig aktualisiert werden müssen. Die Funktionalität dieser integrierten Adressvalidierung ist außerdem eher beschränkt.
Alternativ kann auf spezialisierte Validierungs-Dienste zugegriffen werden.
Mittels solche Dienste können Adress-Komponenten (Straße, Stadt, PLZ etc.) identifiziert, validiert, automatisch korrigiert und ergänzt werden. Bei Eingabe von ungenauen oder unvollständigen Adressen, kann eine Liste von möglichen zutreffenden Adressen ausgegeben werden, aus der der Anwender die richtige Adresse auswählen kann. Auch „autocomplete“-Funktionen können damit implementiert werden.
Beispiele von Adress-Validierungs-Diensten sind :
Uniserv Address Validation
Melissa Address Check
Google Address Validation
Informatica Address Verification (Ehemals Addressdoctor)
Länderabhängige Adress-Validierung ist ein komplexes Thema, deshalb können die Validierungs-Ergebnissen durchaus nicht trivial zu interpretieren sein. Unterschiedliche Länder haben Ihre postalische Adresse Besonderheiten, die sowohl beim Aufbau einer Validierungs-Anfrage als auch beim Interpretieren der Ergebnisse berücksichtigt werden müssen.
Beispiele von Länderabhängigen postalische Adresse Besonderheiten:
In den Ländern GB oder CA gibt es die Adress-Komponente „Organisationsname“. Diese spielt bei Firmen oder Organisations-Adressen eine Zentrale Rolle
unterschiedliche Postleitzahl-Länge und -Formatierung, mit optionalen Teilen wie z. B. ZIP+4 in den USA
unterschiedliche Handhabung und Komponenten für „Hausnummer 1 und 2″: in den USA und CA: Benutzung von Appt/Unit/Suite
CEDEX – prioritized delivery address in FR
Falls die Benutzung des externen Validierung Dienstes direkt in jedem unterschiedlichen System vorgenommen wird, entsteht ein hoher Abstimmungsbedarf, um die Dienst-Benutzung und Mapping-Regeln konsistent zu halten. Falls dies nicht oder unzureichend gemacht wird, ergeben sich aus den Unterschieden inkonsistente Adressdaten. Außerdem erfordert die Anbindung in diesem Fall, dass die Entwickler-Teams in den unterschiedlichen Systemen tiefe Kenntnisse im Bereich internationale Adressvalidierung und die eingesetzte Validierung Software aufbauen müssen.
Einsatz von Adressvalidierung ohne zentralen Wrapper: Hohes Risiko von Inkonsistenzenund erhöhter Implementierungsaufwand
Es ist aus diesem Grund sehr empfehlenswert, einen Zentralen Validierungsdienst-Wrapper anzubieten, der zum einen die Benutzung des externen Dienstes einfacher macht, dessen Ergebnissen gleich Benutzerfreundlich und einsetzbar sind, und zum anderen einheitliche Mapping Regeln und Datenstrukturen einsetzt.
Der Implementierungsaufwand reduziert sich außerdem in jedem Zielsystem und die Adressen werden alle mit gleichen Regeln und Ergebnissen validiert.
Weitere Vorteile von einem Zentralen Validierungsdienst-Wrapper sind:
Möglichkeit, zusätzliche Funktionalität einzubauen
Logging von Validierungs-Aufrufe
Caching von Validierungs-Aufrufen, damit können ggf. die Kosten reduziert werden
Möglichkeit, Varianten des Dienstes anzubieten, mit unterschiedlich ausgeprägter Funktionalität: z. B.
Minimal, sagt nur ob die Adresse ok ist oder nicht, ohne Validierungs-Fehlermeldungen
Standard mit Validierungs-Fehlermeldungen, ggf. Adresskorrekturen und -ergänzungen, aber ohne Rückgabe einer Adress-Treffer-Liste bei ungenaue Adressen
Volle Funktionalität: mit Validierungs-Fehlermeldungen, Adresskorrekturen und -ergänzungen und mit Rückgabe einer Adress-Treffer-Liste bei ungenaue Adressen
Länderabhängige Validierungsfunktionen können zentral gesteuert werden . Zum Beispiel kann eingestellt werden in Welche Länder die Validierung von Bundesstaaten / Regionen aktiviert sein soll und in welche Länder nicht
Einsatz von Adressvalidierung mit zentralem Wrapper
Implementierung
Für die Implementierung des Validierungs-Dienst-Wrappers als REST API gibt es viele Möglichkeiten. Zum Beispiel kann es mit Node.js + Express oder beliebige ähnliche API Server Software gebaut werden. Für einen reibungslosen produktiven Einsatz parallel zur Weiterentwicklung und Tests wird der Dienst auf Cloud-Foundry oder equivalent (Heroku, Kubernetes ) verwaltet und „deployed“.
Aufbau des Validierungs-Wrappers als REST Dienst
Mit einer Swagger API-Beschreibung kann die Implementierung der Benutzung von dem Dienst auf Zielsysteme schon vor der Finalisierung des Wrappers selbst angefangen werden.
API-Beschreibung mit Swagger
Während der Entwicklung und Tests des Wrappers können bestimmte Adressdaten mit bekannten Validierungsergebnissen gesammelt und in einer Test-Suite aufgenommen werden. Diese Testsuite kann dann später bei Fehlerkorrekturen oder Weiterentwicklung des Wrappers als CI/CD eingesetzt werden und garantiert die Stabilität und Qualität der Adressvalidierung.
Test-Definition im YAML Format
Adressvalidierung im SAP
Der REST-Validierungsdienst muss im SAP aus einer implementierung des BadI ADDRESS_CHECK aufgerufen werden. Die Aufbereitung der REST-Anfrage mit der Adresse in der Payload im JSON Format und die Bearbeitung der Antwort auch im JSON Format, ist mit ABAP unproblematisch.
Validierungsmeldungen können direkt als Ergebnis in der BadI-Implementierung zurückgegeben werden, und werden in der Adress-Management- (z. B. BP- oder Debitor Ändern) -applications angezeigt.
Beispiel mit Adresskorrektur und -ergänzung
Adresseingabe
Validierungsmeldungen und Ergebnis
Beispiel mit Trefferliste
Im Fall ungenauer Adressen mit Rückgabe einer Adress-Trefferliste kann diese in einem zusätzlichen Popup zur Auswahl angezeigt werden: [...]
Read more...
Motivation
Will man Nutzer regelmäßig über bestimmte Prozesse im SAP System informieren, so kann es sehr nützlich sein direkt aus einem Report eine E-Mail zu versenden. Einfache Text-E-Mails sind allerdings nicht mehr zeitgemäß… das Pflegen von HTML-Inhalten direkt im Quellcode aber sehr umständlich.
Wir stellen die Möglichkeit der Verwendung von Templates vor, sodass man die Datenbeschaffung und die Formatierung der Ausgabe sauber trennen kann.
Das HTML-Template verwalten
Im SAP System kann man Dateien für die Wiederverwendung ablegen, im sogenannten SAP Web Repository (Transaktion SMW0). Dort kann man neben binären Daten (wie zum Beispiel Bilder oder PDFs) auch HTML-Templates („HTML Schablonen“) hinterlegen. Mithilfe der Suche (einschränkbar nach Paket, Name und Beschreibung) kann man sich vorhandene Objekte ansehen. Ein neues Objekt lässt sich über die Funktion „Anlegen“ direkt vom Client hochladen.
Die auf diese Weise bereitgestellten Templates sind Teil der Transportverwaltung und können daher über die Systemlandschaft verteilt werden.
Ein einfaches Beispiel für ein Template könnte wie folgt aussehen.<!DOCTYPE HTML PUBLIC „-//W3C//DTD HTML 4.0 Transitional//EN“>
<HTML>
<HEAD>
<META http-equiv=Content-Type content=“text/html; charset=iso-8859-1″ />
</HEAD>
<BODY>
<FONT face=“Verdana,Arial,Helvetica“ size=2>
<div>
<span style=“font-family: Verdana; font-size: medium;“>
<strong>Ein HTML-Template in SAP
<br />
</strong>
</span>
</div>
<div>
</div>
<div>Ein kleines Beispiel für ein Template.
</div>
<div>
</div>
<div>Die E-Mail wurde von Nutzer !USER! erstellt.
</div>
<div>
</div>
</FONT>
</BODY>
</HTML>
Platzhalter im Template ersetzen
Um ein Template richtig zu benutzen, braucht man die Möglichkeit, Platzhalter korrekt zu ersetzen. Im obigen Beispiel gibt einen Platzhalter für den Nutzernamen !USER!. Dieser Text ist prinzipiell frei wählbar, man sollte aber darauf achten, dass diese Zeichenkette nicht zufällig an einer anderen Stelle auftaucht. Dazu empfiehlt sich eine einheitliche Markierung, zum Beispiel durch Einfassen der Variablen in „!“-Zeichen.
Wie verwendet man nun das Template im Coding?
Mithilfe des Funktionsbausteins WWW_HTML_MERGER kann man sowohl das Template (Parameter TEMPLATE) laden, als auch die Platzhalter ersetzen. Um die Ersetzung zu realisieren, wird eine „merge_table“ übergeben, die wie folgt aufgebaut ist:
Name: der Platzhaltername, der gesucht und ersetzt werden soll
Command: hier stehen verschiedene Optionen zur Auswahl, die gängigste ist wohl „R“ (replace = ersetzen)
HTML: eine Tabelle mit Zeilen, die statt des Platzhalters eingefügt werden sollen
CALL FUNCTION ‚WWW_HTML_MERGER‘
EXPORTING
template = ‚ZMY_TEMPLATE_NAME‘
IMPORTING
html_table = lt_html_table[]
CHANGING
merge_table = lt_merge_table[].
Dieser Baustein liefert eine Tabelle mit Zeilen der Länge 255 zurück, die den HTML-Inhalt repräsentieren. Dieses Format eignet sich für die direkte Übergabe an den Funktionsbaustein für den E-Mail-Versand.
Achtung: Die für den Aufruf benötigten Strukturen/Tabellen befinden sich im Typenpool SWWW.
E-Mail versenden
Der eigentliche E-Mail-Versand erfolgt über den Standard-Funktionsbaustein SO_NEW_DOCUMENT_SEND_API1.
Für den Versand muss man mindestens folgende Parameter übergeben:
DOCUMENT_DATA: eine Struktur, die die Kopfinformationen für die E-Mail bereitstellt. Unter anderem zählt das der E-Mail-Betreff (DOCUMENT_DATA-OBJ_DESC).
DOCUMENT_TYPE: dieser optionale Parameter ist standardmäßig auf „RAW“ gesetzt, was das Versenden des HTML-Inhaltes unmöglich macht. Daher muss dieser Parameter auf „HTM“ gesetzt werden.
OBJECT_CONTENT: die Tabelle mit den Zeilen, die im E-Mail-Body gesendet werden sollen. Hier kann die HTML_TABLE aus dem vorherigen Funktionsaufruf verwendet werden.
RECEIVERS: eine Tabelle mit den Empfängern der E-Mail. Hierbei können z. B. Nutzer oder E-Mail-Adressen gesetzt werden. Welche Art von Empfänger übergeben wird, wird im Feld REC_TYPE festgelegt (Wert „U“ steht für eine E-Mail-Adresse). Weiterhin kann man definieren, ob der Empfänger die E-Mail als Kopie oder Blindkopie erhalten soll.
Im folgenden Beispiel-Coding werden zunächst für Geschäftspartner, die im Selektionsbild definiert werden können, die E-Mail-Adressen bestimmt. Anschließend wird noch der E-Mail-Betreff gesetzt. SELECT
smtp_addr as receiver,
‚U‘ as rec_type,
‚X‘ as express
FROM ADR6
INNER JOIN BUT020 ON but020~addrnumber = adr6~addrnumber
INTO TABLE @lt_receiver
WHERE but020~partner IN @s_bp.
CHECK sy-subrc = 0.
ls_doc_data-obj_descr = |Test-E-Mail am { sy-datum }|.
CALL FUNCTION ‚SO_NEW_DOCUMENT_SEND_API1‘
EXPORTING
DOCUMENT_DATA = ls_DOC_DATA
DOCUMENT_TYPE = ‚HTM‘
IMPORTING
NEW_OBJECT_ID = lv_OBJECT_ID
TABLES
OBJECT_CONTENT = lt_OBJCONT
RECEIVERS = lt_RECEIVER
EXCEPTIONS
TOO_MANY_RECEIVERS = 1
DOCUMENT_NOT_SENT = 2
DOCUMENT_TYPE_NOT_EXIST = 3
OPERATION_NO_AUTHORIZATION = 4
PARAMETER_ERROR = 5
X_ERROR = 6
ENQUEUE_ERROR = 7
OTHERS = 8.
IF sy-subrc NE 0.
WRITE: / ‚Error‘, sy-subrc, ‚while sending!‘.
EXIT.
ENDIF.
CALL FUNCTION ‚BAPI_TRANSACTION_COMMIT‘.
Der Funktionsbaustein liefert die ID des angelegten Nachrichtenobjektes zurück, falls man nachträglich noch das Objekt modifizieren muss.
Wichtig: das Einstellen der E-Mail in die Warteschlange erfolgt erst nach einem erfolgreichen Commit, was mit dem Funktionsbaustein BAPI_TRANSACTION_COMMIT erreicht werden kann.
Hat alles geklappt, kann man nun die erstellte E-Mail in der Transaktion SOST sehen. Je nach Einstellungen im SAP Connect wird die E-Mail jetzt auch versendet. [...]
Read more...
Last year, one of our clients asked us to connect their SAP ERP system to the DB eSchenker web service interface. Purpose of this project was to submit delivery data (addresses, sizes and volumes, handling units, dates) in order to book transports automatically instead of doing so manually in the DB Schnenker portal. Since our client was already using our EDI converting framework Contux, we decided to use it (again) as middleware between the ERP system and the eSchenker web service.
As you can see in the image above, we used standard shipment IDocs to export the data from SAP ERP. Much like with other interfaces, we worked with the IDoc XML file output, since the number of messages per day is rather small. Nothing was extended in or added to the ERP system’s output. Only the message definition and the conditions records had to be customized directly in SAP. Mappings (custom incoterms, handling unit types, format conversions) where completely moved to Contux.
In Contux, we build a simple message transformation to map the IDoc data to the input structure of the DB eSchenker service and to map certain fields (values used in SAP to values using in eSchenker). We also added a new adapter to call a SOAP web service from Contux. This was implemented using basic Java libraries.
After only a few days of implementation and testing, our clients were able to move the new interface to the production environment. Since then it has served them well and reduced the costs for transport registration dramatically. We also have a few ideas for extending the current solution. For example, it would be possible to directly print the registration papers from the Contux system.
If you would like to take advantage of our expertise in the design and implementation of EDI interfaces, maybe even with setting up an interface to DB eSchenker in your environment, please don’t hesitate to get in touch. [...]
Read more...
Ausgangssituation
In diesem Beitrag soll der Fall betrachtet werden, dass eine Virtuelle Maschine (VM) komplett ausfällt. Solch ein Szenario ist zum Beispiel denkbar bei einem Schaden im Rechenzentrum.
Als Voraussetzung für eine erfolgreiche Wiederherstellung müssen folgende Objekte Teil eines regelmäßigen Backups sein:
Datenbank: Zum einen wird ein komplettes Datenbank-Abbild benötigt (online oder offline). Außerdem empfiehlt es sich regelmäßig (am besten täglich) die Logs und Archive (inkrementelle Sicherung) zu speichern. Für dieses Backup werden die von SAP bereitgestellten BRTools verwendet. Eine einfache Kopie der Partition speichert keine konsistenten Abbilder der Datenbank.
Betriebssystem und SAP-Installation: Hiermit sind die Partitionen gemeint, auf denen sich die Betriebssystemdaten (z.B. „C:“) und die SAP-Installation befindet. Üblicherweise (aber nicht zwangsläufig) sind diese getrennt. Eine Anmerkung hierzu: diese Sicherung ist nicht zwingend erforderlich, erlaubt aber eine schnellere Wiederherstellung, und damit eine geringe Ausfallzeit.
Konfigurationsdateien: Lokale Einstellungen, die von der Standardkonfiguration abweichen, sollten dokumentiert und gesichert werden. Das können z.B. Datenbankverbindungseinstellungen oder SAP-Profildateien sein.
Wichtig ist, dass sowohl die Datenbank- als auch die Betriebssystemsicherungen räumlich getrennt an einem anderen Standort aufbewahrt werden, sodass im Falle eines physischen Schadens nicht auch die Backups betroffen sind.
Vorgehen zur Wiederherstellung
Zunächst muss einmal eine funktionierende VM wiederhergestellt werden. Dies kann ein frisch aufgesetztes System sein, inklusive einer neuen Betriebssysteminstallation. Dies kann der passende Weg sein, wenn das Ausgangsbetriebssystem beschädigt ist (z.B. durch Malware). Dafür wird das oben beschrieben Backup des Betriebssystems und der SAP-Installation nicht benötigt. Stattdessen wird ein SAP-System mit äquivalenten Release-Status – passend zum Backup – installiert.
In dem hier beschriebenen Szenario soll allerdings davon ausgegangen werden, dass ein Hardware-Schaden die Wiederherstellung notwendig gemacht hat, man also das Backup des Betriebssystems verwenden kann. Dies hat den Vorteil, dass der Schritt der SAP-Installation wegfällt, womit eine beträchtliche Zeitersparnis erreicht wird. Die Ausgangssituation ist nun eine VM, bei der zwar die Systempartition und die Partition der SAP-Installation dem Original entsprechen, die Datenbankpartition aber leer sind. Jetzt kann das Backen mithilfe der BRTools eingespielt werden.
Details zur Datenbank-Wiederherstellung
Vorbereitungen zum Backup
Zunächst einmal muss sichergestellt werden, dass die neue VM Zugriff auf die Backups hat. Dazu bindet man am einfachsten die Backups vom Backup-Server als Netzwerklaufwerk ein. Dort sollte sich auch die wichtigste Konfiguration für die Wiederherstellung befinden: die Datei init{SID}.sap. Diese Datei sollte identisch mit der Konfiguration sein, die für das Backup verwendet wird. Diese Konfiguration kann manuell in das Standardverzeichnis kopiert werden, in dem die BRTools die Konfiguration suchen (Festplatte der Oracle-Installation, typischerweise gleich SAP-Installation, Pfad „…oralce{SID}{Version}databaseinit{SID}.sap).
Prüfung der Backup-Konfiguration
In der eben erwähnten Konfiguration sollte zunächst geprüft werden, ob die darin erwähnten Verzeichnisse existieren. Das betrifft zum einen den Speicherort der Backups (siehe oben: Einhängen des Netzlaufwerks), aber auch die lokale Ordnerstruktur. Sollten lokale Ordner fehlen, weil Partition für z.B. Archive nicht Teil der VM-Wiederherstellung sind, müssen diese von Hand gelegt werden. Dabei ist darauf zu achten, dass der SAP-Nutzer ({SID}adm) Lesen- und Schreibberechtigungen hat.
Ausführung der BRTools
In den BRTools ist ein Programm BRRESTORE enthalten, welches mit dem SAP-Nutzer {SID}adm ausgeführt werden muss. Die Konfiguration liest dieses Programm aus der oben erwähnten Konfiguration automatisch, solange diese im Standardpfad vorhanden ist. Dieses Tool stellt nun aus dem Backupverzeichnis die entsprechenden Datenbank-Dateien lokal wieder her. Dieser Vorgang kann mehrere Stunden in Anspruch nehmen, je nach Datenbankgröße.
Kommt es bei der Ausführung zu Fehlern, müssen die Fehlerausgabe genauer analysiert werden. Typische Fehler sind, dass Ordnerstrukturen noch nicht vollständig wieder angelegt wurden, oder dass der SAP-Nutzer keine Schreibberechtigungen auf dieses Verzeichnis hat.
Nach dem Durchlauf von BRRESTORE kann man jetzt beginnen, die Datenbank und das SAP-System wieder hochzufahren.
Datenbank starten
Für den ersten Start der Datenbank wird empfohlen, den Dienst in einem Terminal (z.B. PowerShell) zu starten, um dort eventuell ausführlichere Fehlermeldungen zu erhalten.
Der SQL-Befehl „startup“ führt häufig zunächst einmal zu einem Fehler, zusammen mit dem Hinweis, den „alert log“ zu überprüfen. Diesen finden man (abhängig von der Installation) üblicherweise auf der Partition der Datenbank in einem Verzeichnis der Art „…oracle{SID}saptracediagrdbms{sid}{sid}trace“, wobei {SID} jeweils mit der Datenbank-ID zu ersetzen ist (Achtung: die IDs der CDB und PDB können sich unterscheiden).
Typische Fehlerursachen sind hier fehlende Control-Files. Diese müssen manuell aus dem Backup kopiert werden. Eine Liste der erwarteten Control-Files kann man mit dem Datenbankparameter „control_files“ auslesen.
Ist die Datenbank gestartet und ein Verbindungstest war erfolgreich, kann man mit dem Start des SAP-Systems fortfahren.
SAP-System starten
Das SAP-System kann man wie gewohnt über die SAP Management Console starten. Etwaige Fehler werden dort protokolliert und dargestellt. Sofern die Erreichbarkeit der Datenbank sichergestellt ist, treten bei diesem Schritt selten Probleme auf.
Nun steht ein ausführlicher Test der Wiederherstellung an, bevor man das System wieder an die Nutzer übergibt. [...]
Read more...
… where we discuss manual and automated testing and present a simplistic approach for the creation of an automated test suite for ABAP legacy code.
What Testing Can Do
Testing cannot be isolated from the development process. Be it waterfall or agile or the V-model to build a bazaar or a cathedral everyone wants high quality code without defects. Tests can
reveal unknown bugs in the code or
verify the code will perform according to the specification or.
help understand how existing code work
Tests validate a specification. This should not come as a surprise to anyone using the Given-When-Then routine for test design.
In a though experiment, let us assume a fictional design surface exist where all processes in the problem domain can be represented. The specification and implementation could then be visualized as diagrams:
Manual Debugging
An ABAP report displays the wrong output. How can I proceed?
set break-points, step through the code while observing the program state in the debugger until the fault can be consistently reproduced
i.e. I create a test case and reveal a bug.
step through the code while observing the program state with the debugger the part of the logic that triggers the wrong behavior is known.
i.e. I locate a bug.
identify the error and implement a correction. Then set break-points and step through the code while checking the system state with the debugger to make sure your code is now correct.
i.e. I verify that code performs according to expectations.
In all steps I used used break-points, the debugger and some expectation about the correct code behavior.
But debugging is a manual process that does not scale. The proposition of this blog is to create test code that will harness productive code and reduce reliance on Debugging for test purposes.
A typical test will run in 1/100th of a second, so they are run all the time, specially after each change. Test failures are then localized in the code that was changed last. We can check it and fix the code (or the test). With a good test coverage and this leads to a new workflow with much less manual testing / debugging.
ABAP Unit Recap
If you understand this diagram, then we are good to go:
If not, check e.g.
Suketu Dave introducing the fundementals
James E. McDonough 10 episodes long series on ABAP Unit testing
You could also check the literature:
TOOS: Binder, Robert; Testing Object-Oriented Sytems: Models, Patterns, and Tools, 2006
GOOS: Steve Freeman and Nat Pryce: Growing Object-Oriented Software, Guided by Tests, 2010
WELC: Michael Feathers, Working Effectively with Legacy Code, 2005
xTP: Gerard Meszaros, xUnit Test Patterns: Refactoring TestCode, 2007
POOD: Sandi Metz, Practical Object-Oriented Design in Ruby, An Agile Primer, 2007.
But note none of those books use ABAP as a reference.
The Feedback Loop
In Working with ABAP Unit Tests I claimed anyone could try retrofiting test into production code (test last) because writing test code before production code (test first) requires so much more discipline that nobody is doing it.
Since then I learned to appreciate how Test Driven Development could be done in ABAP (cf. openSAP course: while working on legacy code we should gradually extract functionality to an island of happiness were the new code is completely covered by tests). I am now convinced the TDD feedback loop is the most effective way to achieve a good tests coverage at all times.
You have to see the units fail before your fix the production code make them pass. This leads to feedback loop informing the specification from the tests after validating specification by the tests. This guide the software development with changes having an observable impact on the system behavior.
Characterization Testing
So what is the normal way to add tests to an existing code base? The approach is called characterization testing, as is discussed here.
When the code does not have known errors, the we still identify units of processing, create tests that invoke those unit and write assertions that accurately describe how the code currently works.
In this phase, we aim at creating a test suite with a good coverage of the existing code base. The focus is to achieve a good code coverage to enable refactoring with pervasive design changes in the next phase.
This might already be technically difficult because some operations would be highly tangled (high coupling). Implicit interfaces might have to be introduced without the safety net of a good test suite. But the goal in this phase is simple: write more tests, increase test coverage.
Code Coverage Demo
I have been working on an interpreter for the Scheme language in ABAP based on R7RS small, a specification that is many decades old and seven revisions strong. Get ABAP Scheme from github and install it using abapGit.
Many examples of the expected behavior can directly be converted to tests, so ABAP Scheme has over 600 ABAP unit tests that execute in around 5 seconds, depending on the environment.
In the SAP GUI, open Report ZZ_LISP_IDE and execute the unit tests
Execute_Coverage
Coverage Metrics SE80
Coverage Result SE80
In the ADT for Eclipse, select the Report ZZ_LISP_IDE and run as ABAP unit test.
ADT Enable Coverage
ADT ABAP Unit
ADT Coverage
Both ADT and the SAP GUI editor can display statement coverage if you execute the unit tests with coverage. You can now create ABAP unit test cases that invokes the paths of the code that are still red.
For the By Display in coverage in your the editor, to see with code is still red. If you are not sure about the expected value, that use the values returned in the debugger as expected value.
With this, you can expand your test coverage. Your code paths will be green. But please do not obsess on achieving 100% coverage. All this is preparation for the next phase where you can now safely improve the code without change its external behavior: the refactoring phase. [...]
Read more...
In this blog post, we will demonstrate a simple and cost effective way to send digitally signed PDF documents, for example invoices or business orders, from your SAP ERP system to your partners – customers as well as suppliers. While the use case described in this article focuses on SAP, we can apply the same techniques to other systems.
Many companies, esp. small and mid-sized companies that do not have the required resources to implement complex EDI interfaces, send business documents as PDF file via simple email. This process can be implemented easily, does not require expensive software and can be used without the need for complex interface designs. The output can be used by anybody with a mail client as well as directly imported into third-party software.
The figure above shows this simple process. While we focus on the business parts required, we will not talk about the communication protocols (SMTP etc.) involved in this blog post.
The process is very simple and can be implemented using SAP standard tools and means of output. However, it cannot be considered secure since everybody can send an email with a PDF document attached to your customers. Hence, it is very easy to fake invoice and bank account data and ask your customers to submit money to the wrong accounts.
In order to make sure that your customers know whether or not a digital invoice (or any other business document) really has been created by you, you need to digitally sign your PDF documents. This is something that cannot be done easily with SAP ERP or many other business software. The standard solution that SAP offers facilitates the Adobe Document Server (ADS), however, an additional license is required making the solution quite expensive.
Thus, we thought of a way of adding digital signatures to PDF documents using our free EDI development framework Contux (which we will officially introduce in another blog post). Contux is a Java-based EDI transformation/mapping and transmission framework designed for processing asynchronous interfaces. We considered PDF files to be just another B2B message with the mail data as meta data and SMTP as the transport protocol.
How does it work?
We created a special version of the SAP printing program which writes the PDF document (SAPScript, Smartform, or ADS Interactive form) alongside with email (subject, body) and receiver information to the file system. Contux then picks up the file, creates a digital signature using a PKCS 12 certificate and attaches the signed PDF document to an email. The email is sent to the business partners using the companies default mail server (SMTP).
Using this simple approach, we can enable our customers to send digitally signed business documents to their business partners in very little development time. Since the meta data format is very simple, the same process can be used by other tools as well. Finally, our customer’s business partners can easily check whether or not the document they receive are authentic, make the interface much more trustworthy than a simple email interface.
If you’re interested in our simple and cost effective way to enable you to send electronically signed documents (PDF etc.), please get in touch. We will be happy to provide you with our knowledge and advice. [...]
Read more...
How to evaluate the Order of Growth
Let us assume we have a process that takes more time with a larger input (e.g. higher number of “rows”). Actually, here is a concrete example in the context of ABAP development.
I suggest to compute the order-of-growth and make sure your run-time is acceptable for large input. We cannot avoid some maths (power law, log) here, but in those times of Covid19, you should have seen nice looking animations explaining why exponential growth can be so bad.
If the order of growth is 2 (quadratic) or higher, you have a potential disaster at hand. You must then keep the input size small at all costs or find another solution.
Now if have have at least two different measurements of your run-time as a function of the input size, then you can already estimate an order-of-growth.
From the vantage of software, the running time T is described as a function T(N) of an input size N (e.g. number of “rows”). We model this dependency as power law
T(N) = c N^b
where c is a constant.
From this formula, the order-of-growth b can be easily estimated by comparing T(N) with T(2N) based on the doubling hypothesis, so b is equal to
log( T(2N) / T(N) ) / log( 2 ) .
I implemented this logic in an unit test, using the fact that b is also equal to
log( T(10 N) / T(N) ).
METHOD profile.
CLEAR rs_time.
rs_time-size = iv_size.
DATA(lv_start) = mi_timer->get_runtime( ).
run_data( iv_size ). " <---- The Code under Test
rs_time-time = mi_timer->get_runtime( ) - lv_start.
IF iv_time GT 0.
rs_time-lg_ratio = log10( rs_time-time / iv_time ).
ENDIF.
ENDMETHOD. "profile
METHOD performance.
* empirical analysis
CONSTANTS c_precision TYPE f VALUE '1e-2'.
DATA lv_size TYPE syindex VALUE 1.
mi_timer = cl_abap_runtime=>create_hr_timer( ).
DATA(ls_time) = profile( iv_size = 0
iv_time = 0 ).
DO 6 TIMES.
ls_time = profile( iv_size = lv_size
iv_time = ls_time-time ).
lv_size = lv_size * 10.
ENDDO.
IF ( ls_time-lg_ratio - c_precision ) > 1 .
cl_abap_unit_assert=>fail( msg = | Performance: { ls_time-lg_ratio } | ).
ENDIF.
ENDMETHOD.
cf. Introduction to Programming in Java by R. Sedgewick, K. Wayne [...]
Read more...
Einige Worte zu PGP
Das Programm PGP („Pretty Good Privacy“) erlaubt das Erstellen von verschlüsselten Inhalten, die nur von einer bestimmten Zielperson gelesen werden kann. Dazu wird ein asymmetrisches Verfahren verwendet. In der Praxis heißt das, dass man immer ein paar von Schlüsseln braucht: einen öffentlichen und einen privaten Schlüssel.
Der öffentliche Schlüssel kann bedenkenlos geteilt werden (z.B. über einen Schlüsselserver).
Der private Schlüssel darf niemals weitergegeben werden. Dieser ist zusätzlich durch ein Passwort geschützt.
Wie funktioniert nun die Verschlüsselung? Der Sender benutzt den öffentlichen Schlüssel des Empfängers, um die Nachricht zu verschlüsseln. Diese Nachricht kann anschließend nur durch den passenden privaten Schlüssel entschlüsselt werden (asymmetrisch).
Was funktioniert eine Signatur mit PGP?
PGP kann nicht nur zu Verschlüsselung verwendet werden, sondern auch um Nachrichten zu signieren. Diese Signatur funktioniert wie eine analoge Unterschrift. Es ist davon auszugehen, dass die Signatur nur von einer Person erzeugt werden kann, und damit sichergestellt ist, dass die Nachricht auch wirklich von dieser Person kommt.
Technisch wird dazu eine Prüfsumme der gesendeten Nachricht erstellt, sodass sichergestellt ist, dass der Inhalt nach der Signatur nicht mehr geändert werden kann. Mit dieser Prüfsumme und dem privaten Schlüssel des Senders wird eine Signatur errechnet und der Nachricht angehängt.
Der Empfänger der Nachricht kann nun mit Hilfe der öffentlichen Schlüssels prüfen, ob die Nachricht unverändert von erwarteten Sender geschickt wurde.
Integration in Gmail (Web-Mailer)
Die Nutzung eines Webmailers, also eine Web-Applikation, und die Verwendung eines privaten Schlüssels widerspricht sich. Um das Problem zu lösen, benötigt man ein Browser-Plugin, welches Zugriff auf die zu sendende Nachricht als auch auf lokale Dateien hat. Beispiele für solch ein Plugin sind FlowCrypt oder Mailvelope. Die hier gezeigten Scrennshots beziehen sich auch FlowCrypt in Chrome, lassen sich aber analog auf andere Plugins anwenden.
Initial Setup
Auf der verlinkten Webseite einfach den großen „Get Extension“-Button verwenden. Nach dem Hinzufügen gibt es ein kleines Icon , mit dessen Hilfe man auf die Einrichtungsseite des Plugins gelangt.
Jetzt versucht das Plugin sich mit einem Google-Account zu verbinden und erfragt die Zugriffsrecht auf die E-Mails. Wir ober erwähnt, muss man diesen Zugriff erlauben.
Auf der nun folgenden Startseite kann man nun einen eigenen PGP-Schlüssel hochladen, oder einen neuen Schlüssel erstellen.
Um einen neuen Schlüssel zu erstellen muss man lediglich ein Passwort vergeben. Das Plugin erstellt anschließend das Schlüsselpaar mit den empfohlenen Sicherheitseinstellungen.
Möchte man diesen Schlüssel auch in anderen Anwendungen verwenden (z.B. Thunderbird), kann man in den erweiterten Einstellungen (umschalten um unteren Bildschirmrand) den Schlüssel exportieren – sowohl den öffentlichen als auch den privaten Schlüssel.
Benutzung in Gmail
Hat man das Plugin eingerichtet, erscheint über dem „Schreiben“-Button von Gmail ein zusätzliche Button „Secure Compose“. Dieser öffnet den Mail-Editor des Plugin anstatt den Standard-Editor.
Dieser Editor verhält sich ähnlich, aber nicht identisch wie der Standard-Editor. Um unteren Rand kann man nun entscheiden, ob die Nachricht verschlüsselt, signiert oder mit beiden Optionen versendet werden soll. Da es in diesem Beitrag um die Signatur gehen soll, wird auch nur diese Option ausgewählt.
Die Empfänger-Seite
Beim Senden der Nachricht wird nun der eigentliche Inhalt in eine Signatur eingebettet. Damit die Darstellung korrekt ist, sollte die Gegenseite also auch eine Tool besitzen, welches die Darstellung von signierten Mails beherrscht. Benutzt man das Besprochene Plugin, wird die Signatur automatisch mit Hilfe eines Schlüsselservers geprüft und die Darstellung entsprechend angepasst.
Mobile Apps mit dieser Fähigkeit sind z.B. K9-Mail. Wenn die Software die Darstellung nicht unterstützt, dann kann man den Inhalt der Mail trotzdem lesen (weil ja nicht verschlüsselt), aber die Anzeige ist „gewöhnungsbedürftig“.
PGP/inline vs. PGP/MIME
Um das eben gezeigt merkwürdige Verhalten zu beheben, kann man statt der standardmäßigen PGP/inline-Methode umstellen auf PGP/MIME. Bei PGP/inline wird lediglich der Textinhalt der E-Mail angepasst, sodass die Signatur (wie oben gezeigt) einfach als Text dargestellt wird. Verwendet man hingegen die PGP/MIME-Methode, werden auch die nicht sichtbaren Teile der E-Mail angepasst. Das Ergebnis ist, dass die Signatur lediglich als Anhang dargestellt wird.
In FlowCrypt wird diese Option aktuell nur im Firefox-Plugin unterstützt. Möchte man PGP/MIME verwenden, kann man dieses Feature in der Auswahlliste des Mail-Editors auswählen.
Zusätzliche Anmerkungen
Wir wird die Identität geprüft?
Erstellt man einen Schlüssel, so kann man prinzipiell irgendeinen Namen zu diesem Schlüssel hinterlegen. Um nun zu prüfen, ob der Schlüssel auch wirklich dem Absender gehört, setzt PGP auf ein dezentrales Vertrauensmodell. Einfach gesagt, zeigt man seinen Schlüssel einer anderen Person, die dann nach Prüfung der Identität (z.B. mittels Personalausweis) den eigenen Schlüssel wiederum mit ihren Schlüssel signiert. Es entsteht dabei ein Vertrauensnetzwerk, wobei ein Schlüssel mit mehr Signaturen vertrauenswürdiger ist (auch mehrstufig).
Ich habe meinen privaten Schlüssel versendet. Was nun?
Ist mein privater Schlüssel in andere Hände gefallen, muss man diesen Schlüssel widerrufen. Dazu wird ein Widerrufs-Zertifikat dem Schlüssel angehängt und über die Schlüsselserver verteilt. Solchen Schlüsseln wird nicht mehr getraut.
Einschränkungen
Durch die Verschlüsselung wird die Suche nach Mails im Web-Mailer eingeschränkt, da verschlüsselte Mails nicht gefunden werden. Auch die Benutzung von Gmail an anderen Rechnern und – ohne zusätzliche Werkzeuge – am Handy wird erschwert, da verschlüsselte E-Mails nicht lesbar sind. Bis zur komplett verschlüsselten E-Mail als Standard ist also noch ein weiter Weg zu gehen. [...]
Read more...
Scenario
Image you have a standard 3-system landscape (development, quality assurance, production). However, in the productive environment you have to maintain multiple clients. This can be the case, eg. if you have several companies hosted in one system.
Example TMS configuration with multiple target clients in productive system.
Using ChaRM in Solution Manager requires you to select one specific target system:client combination for your change document. As consequence: Will you have to create a change document for every client, even if the changes are exactly the same!?
Solution Manager Configuration
For the next steps, several assumptions are made:
Solution Manager version is 7.2 (7.1 should also work).
ChaRM and cCTS is setup, so that transports for system line DEV -> QAS -> PRD work with client 100.
Change document used is based on an urgent correction.
With the TMS configuration shown in the first picture (using transport group /PROD/), transports are automatically cloned for every target client. The challenge is, that during the processes in ChaRM, the transport will be imported into the client specified as target, but other transports remain in the queue of system PRD. At the end of the blog post a background job will be installed which imports the objects into remaining clients.
Adjusting the Solution
Every system:client combination has to be part of the solution. Therefore, we need to assign PRD:110 to the production branch of a new component group. Start transaction SLAN in your Solution Manager, add a new component group, and assign only a production system.
Solution configuration containing the new target client 110.
Cluster Assignment
Open the TMS configuration in your domain controller of your solution manager landscape. Change to edit mode and open the details of your production cluster. There you have to enter client 110 as new target.
Client 110 added as target in TMS cluster configuration
Central CTS configuration
It is mandatory to add the system PRD:110 to the cCTS configuration of your Solution Manager. All checks should be passed. Go to your Solution Manager and start transaction SZENCONFIG. For instructions on cCTS configuration, you can follow the official How-To guide by SAP.
System PRD:110 added to cCTS configuration and successfully validated
Setup of Change Cycle and Background Job
After the basic setup is done, the client 110 can be used in a change cycle. Even if we do not want to use the target PRD:110 directly for an change document, we want to use the task list of the change cycle to setup a background job. Therefore, this system needs to be part of the system landscape. Create a new change cycle or edit an existing one and (re-)define system landscape. If the solution configuration in Solution Manager and TMS configuration of the satellite system are maintained properly, Solution Manager should be able to calculate transport routes including PRD:110.
System landscape of Change Cylce with both target clients.
Do not to forget to turn on the usage of cCTS. In the next steps check the cluster assignments of each system:client.
Background Job
What happens currently?
Create an RFC and assign the change cycle.
In the scope of this RFC add an urgent change for the system PRD:100.
Now perform the normal steps to develop/test/deploy your change. This will transport your changes as usual from DEV:100 to QAS:100 and finally PRD:100. The transport for PRD:110 will remain in the queue of production system.
How can you import the remaining transports of PRD queue automatically? For this we need to go back to the change cycle and open the task list. There you will find tasks for both systems: PRD:100 and PRD:110.
Task list for system PRD:110. Selected task used to synchonize transports to multiple cliens.
If you click the task, the Synchronize Urgent Change a popup will be shown. There you can execute the task immediately with the transports shown in the upper part of the popup, or you can schedule a job. We want to choose the second option. For example you can plan the job every 15 minutes. The background user must have sufficient roles in the satellite system to perform remotely transports.
Schedule the synchronization as background job.
This job will find relevant transports using a special attribute of the transport object: SAP_CTS_PROJECT contains the CTS project name and SAP_TMWFLOW contains task list id of corresponding RFC.
Extra: You can also configure multiple test clients. There is an additional task for target system called Synchronize Urgent Changes from other Test Systems, which also can be scheduled as background job.
How to change or delete Background Jobs
Background jobs for task list items cannot be changed using standard transaction SM37 (here you can see only completed executions). To maintain task list jobs use CCM Administration Cockpit (transaction CM_ADM_COCKPIT). In tab Scheduled Import Jobs you can redefine or delete your background jobs. [...]
Read more...
Gliederung:
BOPF Einführung
Mit BOPF arbeiten
Next Generation BO in RAP
Referenzen
Building Apps with the ABAP RESTful Application Programming Model
Referenzen: ABAP RESTful Application Programming Model (RAP) at openSAP
Die bisherige BOPF Daten (Struktur, Hierarchie, Queries) und Verhalten (Aktionen, Ermittlungen, Validierung) werden nun durch
Die Struktur (Datenteil) wird durch eine Hierarchie von Core Data Services (CDS) views oder view Entities mit einem Wurzelknoten (root CDS Entity). Damit können Read-Only Anwendungen (Queries) generiert werden.
Für Datenänderungen werden das Verhalten durch die Standard-Operationen Create, Create by Association, Update, Delete geprägt. Für kundendefiniertes Verhalten müssen die Operationen Actions, Validations, Determinations, Functions, Locks, Messages in Behavior Definitions (CDS BDEF) und Behavior implementation mit nativer ABAP Unterstützung implementiert werden.
Bei der Laufzeit haben wir die Szenarien managed, managed with draft, unmanaged.
Datenmodellierung
Core Data Services (CDS Views, Data Definitions) werden für Beschreibung der Datenstruktur und Abfragen (Queries) verwendet.
Bemerkung:
Bis S/4 HANA 1909 (e.g. Entwickler Demo) muss mit CDS views gearbeitet werden.
ab S/4 HANA 2020 (e.g. Cloud Version) können CDS View Entities verwendet werden.
Für ein Business Objekt für gesetztliche Feiertage wird in der Eclipse-Umgebung wird eine Datenbank-Tabelle mit 5 Spalten definiert:
@EndUserText.label : ‚Public Holiday Table‘
@AbapCatalog.enhancementCategory : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #C
@AbapCatalog.dataMaintenance : #LIMITED
define table ycalholidayeyjfc {
key client : mandt not null;
key holiday_id : ycal_holiday_id_eyjfc not null;
month_of_holiday : ycal_month_eyjfc;
day_of_holiday : ycal_day_eyjfc;
changedat : timestampl;
}
Eine darauf aufbauende CDS View sieht so aus
@AbapCatalog.sqlViewName: ‚YCAL_IHOLIDEYJFC‘
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: ‚CDS View for Public Holidays‘
define root view ycal_i_holiday_eyjfc
as select from ycalholidayeyjfc
composition of ycal_i_holitxt_eyjfc as _HolidayTxt
association to ycal_i_holitxt_eyjfc as _DefaultText
on _DefaultText.holiday_id = $projection.holiday_id
and _DefaultText.spras = $session.system_language
{
//ycalholidayeyjfc
@Semantics.user.createdBy: true
key holiday_id,
@Semantics.user.lastChangedBy: true
month_of_holiday,
@Semantics.systemDateTime.lastChangedAt: true
day_of_holiday,
_DefaultText.fcal_description as HolidayDescription,
@Semantics.systemDateTime.lastChangedAt: true
changedat,
_HolidayTxt
}
Bei einer Read-Only-Anwendung können ausgehend vom CDS-View ein oData-Entry Point und eine UI5 App generiert werden. Ein Business Object (BO) wird in RAP für Datenänderungen benötigt.
ABAP Behavior Definition Language
Das Verhalten wird in CDS Behavior Definition (BDEF)-Projektionen deklariert und in einer behavior implementation als ABAP Code beschrieben. Dabei wird EML verwendet, eine Erweiterung der ABAP .
Im managed Szenario sind die Operationen CREATE, READ, UPDATE, DELETE ohne Aufwand unterstützt. Andere Operationen (Actions) werden in Klassen implementiert.
managed implementation in class ybp_cal_i_holiday_eyjfc unique;
define behavior for ycal_i_holiday_eyjfc alias HolidayRoot
persistent table YCALHOLIDAYEYJFC
lock master
with additional save
//authorization master ( instance )
//etag master <field_name>
{
create;
update;
delete;
determination det_create_and_change_texts on save
{ field HolidayDescription; }
}
define behavior for YCAL_I_HOLITXT_EYJFC alias HolidayText
persistent table ycalholitxteyjfc
lock dependent ( holiday_id = holiday_id )
{
update; delete;
field( readonly ) holiday_id;
}
Die Implementation unterscheidet strikt die Interaktion-Phase mit lokale Handler Klassen und die Verbuchungsphase mit einer saver Klasse.
CLASS ybp_cal_i_holiday_eyjfc DEFINITION PUBLIC ABSTRACT FINAL
FOR BEHAVIOR OF ycal_i_holiday_eyjfc.
ENDCLASS.
Beispiel: Lokaler Handler Definition
CLASS lhc_HolidayRoot DEFINITION INHERITING
FROM cl_abap_behavior_handler.
Die Definition von Methoden kann nun so aussehen
METHODS det_create_and_change_texts
FOR DETERMINATION HolidayRoot~det_create_and_change_texts
IMPORTING keys FOR HolidayRoot.
oder
METHODS modify_create FOR MODIFY IMPORTING it_travel_create FOR CREATE travel.
Eine Saver-Klasse Definition muss von der Klasse CL_ABAP_BEHAVIOR_SAVER abgeleitet werden.
In den Händler werden abgeleitete Datentypen verwendet, die mit % anfangen
%CID, %KEY, %PID, %CONTROL, %DATA, %FAIL…
ABAP Entity Manipulation Language
ABAP wurde mit Statements erweitert, um mit CDS Entities zu arbeiten.
Lesen: READ ENTITIES, READ ENTITY und READ ENTITIES OPERATIONS.
Ändern: MODIFY ENTITIES, MODIFY ENTITY and MODIFY ENTITIES OPERATIONS.
Verbuchung starten: COMMIT ENTITIES und COMMIT ENTITIES RESPONSES.
Sperren: SET LOCKS OF und SET LOCKS ENTITY.
In diesem Blog werden Beispiele kommentiert: [...]
Read more...
This article is a repost of Michael’s blog post on https://mrentzsch.wordpress.com/2015/10/12/creating-pdf-documents-in-openui5-apps/
I have been a fan of OpenUI5 (or UI5 in general) ever since my first encounters with it in late 2013. Thus, I have been trying to do some of my work using OpenUI5, and, hence, I have decided to write a few blog posts about UI5 and about how certain things can be achieved. I have mainly been focusing on new apps for our company and myself, since most of our customers have not yet switched to using mobile enabled applications heavily or rather rely on the existing apps from SAP itself. I usually have a PHP-based back-end using JSON as data model provider, since a full SAP stack is an overkill in most cases.
When implementing business apps, creating PDF reports/documents that can be printed, sent via email or stored in an electronic archive often is a required feature. This can be achieved with two different approaches.
It is possible to create the PDF using JavaScript libraries right on the (mobile) device. A common library for generating PDFs in JavaScript is jsPDF. This approach doesn’t require extra communication with the back-end or any back-end at all. On the downside, jsPDF doesn’t seem to be very robust when working with images. Thus, this approach is mainly fitting for small apps with simple output.
The second option is producing the PDF document on the back-end. For PHP-driven services, fpdf is a very good tool for that. Just like jsPDF, fpdf is free software. In my tests, it proved much more reliable when producing complex output using images etc. For this approach, a new script/service has to be called. I found it to be a convenient solution to pass the required data (from the UI5 data model) as a JSON string (JSON.stringify) to the PHP service.
Below, you will find an example for generating PDFs directly on the mobile or desktop device using JavaScript and jsPDF and for creating the documents in the back-end with PDF and fpdf.
jsPDF: Document generation in the (mobile) app
Let us create a small app with input fields and a button for generating a PDF document. The PDF doc will contain the input value to show you, how you can connect your app data to the output process.
The UI5 coding for our apps looks like this:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv='X-UA-Compatible' content='IE=edge' />
<meta http-equiv='Content-Type' content='text/html;charset=UTF-8'/>
<script id='sap-ui-bootstrap' type='text/javascript' src='https://openui5.hana.ondemand.com/resources/sap-ui-core.js' data-sap-ui-theme='sap_bluecrystal' data-sap-ui-libs='sap.m,sap.ui.commons'></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.1.135/jspdf.min.js"></script>
<script>
var oModel = new sap.ui.model.json.JSONModel({ name: "Name", age: 30 });
var p = new sap.m.Page( {
title: "Generate PDF",
content: [
new sap.m.Label({ text: "Name:" }),
new sap.m.Input({ value: "{/name}" } ),
new sap.m.Label({ text: "Age:" }),
new sap.m.Input({ value: "{/age}" } ),
new sap.m.Button({ text: "Generate PDF", press: function(evt) {
var n = evt.getSource().getModel().getProperty("/name");
var a = evt.getSource().getModel().getProperty("/age");
var doc = new jsPDF();
doc.text(20, 20, "This is " + n);
doc.text(20, 30, "She/he is " + a + " years old.");
doc.save('sheet.pdf');
}})
]
} );
var app = new sap.m.App({ pages: }).placeAt("content");
app.setModel(oModel);
</script>
</head>
<body class='sapUiBody'>
<div id='content'></div>
</body>
</html>
As you can see, pushing the button “Generate PDF” will trigger the document output directly in the app. Therefore, a new object of type jsPDF has to be instantiated providing methods for adding content to the PDF document. jsPDF comes with a lot of examples showing you how to create bigger and more complex documents.
You can give the app a try on jsBin.
fpdf: Document generation in the back-end
Let us take the same app from above, but this time the PDF document will be generated in a PHP-based service.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv='X-UA-Compatible' content='IE=edge' />
<meta http-equiv='Content-Type' content='text/html;charset=UTF-8'/>
<script id='sap-ui-bootstrap' type='text/javascript' src='https://openui5.hana.ondemand.com/resources/sap-ui-core.js' data-sap-ui-theme='sap_bluecrystal' data-sap-ui-libs='sap.m,sap.ui.commons'></script>
<script>
var oModel = new sap.ui.model.json.JSONModel({ name: "Name", age: 30 });
var p = new sap.m.Page( {
title: "Generate PDF",
content: [
new sap.m.Label({ text: "Name:" }),
new sap.m.Input({ value: "{/name}" } ),
new sap.m.Label({ text: "Age:" }),
new sap.m.Input({ value: "{/age}" } ),
new sap.m.Button({ text: "Generate PDF", press: function(evt) {
var n = evt.getSource().getModel().getProperty("/name");
var a = evt.getSource().getModel().getProperty("/age");
window.open("http://some.where/?name=" + n + "&age=" + a, "_blank");
}})
]
} );
var app = new sap.m.App({ pages: }).placeAt("content");
app.setModel(oModel);
</script>
</head>
<body class='sapUiBody'>
<div id='content'></div>
</body>
</html>
Pushing the button “Generate PDF” this time opens a new window/tab with the specified location passing the form data as HTTP GET parameters. Usually, you will pass bigger amounts of data to the output processing and might consider using HTTP POST. So, what does the back-end script look like:
<?php
require('fpdf.php');
$name = $_GET;
$age = $_GET;
$pdf = new FPDF();
$pdf->AddPage();
$pdf->SetFont('Arial', 'B', 12);
$pdf->Cell(40, 10, 'This is ' . $name . ' aged ' . $age);
$pdf->Output();
?>
The PHP script simply fetches the two parameters (data), creates a new object of type FPDF and uses a few – easy to understand – methods to put some content into the document. Again, fpdf comes with a variety of examples and tutorials to show you how to use it. It takes some time to get used to the positioning of elements etc. But once you have got acquainted to fpdf, it’s quite easy to create large document with different types of content.
In this post, I have shown two different approaches on where to create PDF in (mobile) apps that are based on UI5. Both example were kept as simple as possible ignoring some best practices for UI5. Of course, there are different libraries for PDF output and the same approaches apply for word documents etc. If you know better tools for the job – other than jsPDF and fpdf – feel free to leave a comment. Also, if you’re trying to implement PDF as part of (mobile) apps and require help, please do not hesitate to get in touch. [...]
Read more...
Kalender 2020
Zusammenfassung
In unserem Kalender für das Jahr 2020 stellen wir die durchschnittliche jährliche Änderung der Bevölkerungszahlen grafisch dar. Zur Entwicklung des Kalenders verbanden wir die auf der Webseite https://www.citypopulation.de/ veröffentlichten Census-Daten mit Kartenmaterial des Projektes NaturalEarth. Wir verwendeten den Download Natural Earth quick start, der Geoinformationen im Maßstab 1.10.000.000 (1 cm = 100 km) enthält und erstellten daraus in dem Open-Source-Geo-Informationssystem QGIS eine Weltkarte. Den eigentlichen Kalender setzten wir mit dem Schriftsatzsystem LaTeX und seiner Grafikbibliothek PGF/Tikz.
Census-Daten
Zur grafischen Darstellung wollten wir uns nicht auf Datenebene der Nationalstaaten begrenzen. Das hätte dazu geführt, dass die großen Flächenstaaten, wie Russland, China, die USA oder Indien jeweils nur in einer Farbe dargestellt worden wären. Deshalb verwendeten wir regionale Daten und rechneten die Differenz aus den letzten beiden Erhebungen unter Annahme einer gleichmäßigen Entwicklung auf ein Jahr herunter. Das folgende Bild verdeutlicht das am Beispiel der Tabelle für Taiwan.
Insgesamt betrachteten wir 3.654 Regionen aus 168 Ländern, die im Archiv Censusdata.zip abgelegt sind.
Kartenmaterial
Für die grafische Darstellung benötigt man Kartenmaterial, in dem die Gebiete, auf die sich die Census-Daten beziehen, geografisch abgegrenzt sind. Diese Voraussetzung erfüllt der auf NaturalEartth angebotene Datensatz „Large scale“, den wir als Quelle verwendeten. Er enthält Vektor- und Metadaten im Maßstab 1:10.000.000 (1 cm = 100 km) und ist für großflächige Karten geeignet. Der Layer 10_admin_1_states_provinces_shp dieses Datensatzes enthält Flächeninformationen für Länder und Provinzen, die weitestgehend mit den in den Census-Daten verwendeten Regionen übereinstimmen.
QGIS
Zur Erstellung „unserer“ Karte verwendeten wir das freie Geo-Informationssystem QGIS, in das wir das oben beschriebene Kartenmaterial importierten. Nun bestand die Aufgabe, die 3.654 Regionen in Abhängigkeit der Bevölkerungsentwicklung farblich darzustellen. Dazu erweiterten wir die Tabelle 10_admin_1_states_provinces_shp um die Spalte pop_Growth, in die wir die ermittelten Werte eintrugen:
Die Farbwerte wurden in den Layer-Eigenschaften zugewiesen:
Mit diesen Einstellungen erhält man folgende Weltkarte:
QGIS unterstützt etwa 2700 bekannte Koordinatenbezugssysteme. Die Kreisform der Karte erreichten wir durch Transformation in North_Pole_Azimuthal_Equidistant.
Regionale Ausschnitte
Europa
Nord- und Mittelamerika
Südamerika
Afrika
Asien
Australien und Ozeanien
Gebiete, die sich in dem von uns verwendeten Kartenmaterial keinem Land zuordnen ließen oder zu denen keine Census-Daten vorliegen, blieben weiß. Neben der Antarktis sind das im Wesentlichen kleinere Inseln und Überseegebiete europäischer Staaten, zu denen kein eigener Census geführt wird.
Augenscheinlich ist das weiß dargestellte Gebiet der West-Sahara, das ohne internationale Anerkennung von Marokko beansprucht wird. Auch für diese Region gibt es keinen Census.
Bei genauem Hinsehen findet man auf dem gedruckten Kalender einen kleinen weißen Fleck in Kasachstan. Das ist der russische Weltraumbahnhof Baikonur, der bis 2050 an Russland vermietet ist und im Kartenmaterial gesondert aufgeführt wird aber keine eigene Bevölkerung hat.
LaTeX
Zum Satz des Kalenders verwendeten wir das freie Schriftsatzsystem LaTeX, das besonders im wissenschaftlichen Bereich verbreitet ist. Mit ihm lassen sich qualitativ sehr hochwertige Dokumente erstellen. PGF/Tikz ist eine in LaTeX integrierte Makrosprache zur Erstellung von Vektorgrafiken. Anders als bei WYSIWIG-Systemen (What You See Is What You Get), bei denen die Formatierung direkt bei der Erstellung des Dokumentes erfolgt, schreibt man bei LaTeX und PGF/Tikz zunächst einen Seitenbeschreibungstext (ähnlich wie bei HTML), den man durch den LaTeX-Übersetzer in das gewünschte Ausgabeformat – zum Beispiel PDF – umwandeln lässt.
Den Quelltext zu unserem Kalender veröffentlichten wir in dem Online-Übersetzer Overleaf. Er ist dort unter der Projektnummer 5dea2f83faee3100014068fa abgelegt. Zur Vermeidung langer Ladezeiten skalierten wir die mit QGIS erstellte Weltkarte, die im Original eine Dateigröße von ca. 129 MB hat, auf 1 MB herunter.
Einladung zum Bearbeiten
Dieses Overleaf-Projekt ist nur lesbar. Versuchen Sie es mit einer eigenen Kopie. Tragen Sie Ihren Geburtstag ein oder legen Sie ein anderes Bild – zum Beispiel ein Foto – in die Mitte. [...]
Read more...
In SAPUI5 oder OpenUI5 sind Modelle eine einfache Möglichkeit, um die Datenhaltung von der Darstellung zu entkoppeln (nach dem MVC Muster). Dafür übernimmt das Modell die Kommunikation mit der Datenquelle, während die grafischen Elemente (Controlls) über das Binding mit dem Modell verknüpft sind. Die Aktualisierung der Daten/Eigenschaften der Controlls erfolgt event-basiert und damit automatisch.
Die Datenquelle für das Modell ist dabei üblicherweise ein Webservice, zum Beispiel ein OData-Service. Auch lokale Dateien können verwendet werden, allerdings nur lesend. Diese Einschränkung resultiert daraus, dass eine UI5-Anwendung mittels Javascript im Browser läuft, und damit keine Daten auf den Server schreiben kann.
Für eine kleine (interne) Anwendung kann es aber nützlich sein, dass die Daten lokal in Dateien gespeichert werden können. Dazu soll folgendes eingerichtet werden:
Die Hauptkomponente (Component.js) der App erhält die Funktionen zum Lesen und Schreiben des Modells, inklusive eines Caches, um die Datenmenge zu reduzieren.
Ein lokales PHP-Skript, das über Ajax aufgerufen wird, übernimmt das Speichern der Dateien.
Eine .htaccess-Datei soll den Zugriff auf das Dateisystem einschränken.
Die Hauptkomponente
Die Component.js erweitern wie um den folgenden Code:init: function() {
UIComponent.prototype.init.apply(this, arguments);
this._oDataCache = {};
},
loadFile: function(sFilename, oModel) {
if (this._oDataCache) {
oModel.setJSON(this._oDataCache.Data);
return;
} else {
oModel.loadData(sFilename, "", false, "Get", false, false);
this._oDataCache = {
"Data": oModel.getJSON()
};
return;
}
},
_startQueue: function() {
this._queueRunning = true;
this._saveNextFile();
},
saveFile: function(sData, sFilename) {
this._oDataCache = {
"Data": sData,
"Updkz": "X"
}
if (!this._queueRunning) {
this._startQueue();
}
},
_saveNextFile: function(self = this) {
if (!self._oDataCache) {
self._queueRunning = false;
return;
}
self._foundFileWithUpdate = false;
for (var element in self._oDataCache) {
if (self._oDataCache.Updkz == "X") {
self._oDataCache.Updkz = "";
self._sendFile(
self._oDataCache.Data,
element
);
self._foundFileWithUpdate = true;
break;
}
}
if (!self._foundFileWithUpdate) {
self._queueRunning = false;
}
},
_sendFile: function(sData, sFilename) {
var data = {
"data": sData,
"filename": sFilename
};
parent = this;
$.ajax({
type: "POST",
url: "./savefile.php",
data: data,
error: function(e, s) {
console.warn(s);
},
success: this._saveNextFile(parent),
});
},
Die Funktionen, die aus den Controllern heraus aufgerufen werden, lauten nun
this.getOwnerComponent().loadFile(sFilename, oModel)
hierbei ist sFilename ein String mit dem Dateinamen und
oModel ein sap.ui.model.json.JSONModel() Objekt
this.getOwnerComponent().saveFile(sData, sFilename)
sData sind die Daten als valider JSON String (kann über oModel.getJSON() erhalten werden)
sFilename ist der String mit dem Dateinamen
In der Funktion „loadFile“ wird:
zunächst geschaut, ob Daten im Cache vorhanden sind.
Falls keine Daten im Cache sind, dann werden die Daten vom Dateisystem mit der UI5-Funktion oModel.loadData() gelesen. Die Parameter sind:
Dateiname als String
Parameter (leerer String, da keine weiteren Parameter mitgegeben werden müssen)
Das Laden erfolgt synchron (aSync = false), damit die Daten wirklich zur Verfügung stehen, bevor eine weitere Verarbeitung stattfindet.
Der Typ ist “GET” (“POST” ist auch möglich)
Merge = false (Daten werden ersetzt)
Cache = false (forciert das Laden ohne UI5-Model Cache, da eigener Cache verwendet wird)
Anschließend werden die Daten zum eigenen Cache hinzugefügt. Der eigene Cache kann einfach zurückgesetzt werden, sodass ein Nachladen erzwungen wird.
In der Funktion „saveFile“ werden:
die Daten dem Cache hinzugefügt und vermerkt, dass diese auf dem Dateisystem aktualisiert werden müssen (Updkz = ‘X’).
Anschließend wird die Verarbeitung als Queue angestoßen, falls die Queue aktuell nicht läuft. Eine Verarbeitungsqueue sorgt dafür, dass nicht mehrere Vorgänge gleichzeitig auf das System schreiben.
Solange noch Daten im Cache sind, die das Updatekennzeichen besitzen, ruft die Queue rekursiv die Funktion “_saveNextFile” auf. Um zu verhindern, dass gleichzeitig mehrere Zugriffe auf die selbe Datei erfolgen, erfolgt der nächste Aufruf erst in dem “success”-Hook des Ajax-Requests.
Der Ajax-Aufruf erfolgt für die URL “./savefile.php”,
die Daten (JSON-Inhalt und Dateiname) werden per POST-Methode an das Skript übergeben
Minimales Server-Skript zum Speichern
In der savefile.php könne nun noch diverse Berechtigungsprüfungen auftauchen, sodass ein nicht-autorisiertes Schreiben verhindert werden kann. Hier wird davon ausgegangen, dass es sich um eine interne Anwendung handelt, deren Zugriff per htaccess beschränkt wird.<?php
$data = $_POST;
$filename = $_POST;
if ($data == '') {
echo "No Data";
} else {
if ($filename == '') {
$filename = 'localService/test.json';
}
$dirname = dirname($filename);
if (!is_dir($dirname)) {
mkdir($dirname, 0755, true);
}
$f = fopen($filename, 'w+');
if ($f) {
fwrite($f, $data);
fclose($f);
echo "Write to $filenamen" . $data;
} else {
echo "Fehler beim oeffnen der Datei $filename";
}
}
?>
Das Skript legt neben der Datei auch das Verzeichnis an, falls dieses noch nicht existiert.
Zugriffsbeschränkung auf das interne Netz
Um den Zugriff zu beschränken legt man eine .htaccess-Datei mit dem folgenden Inhalt an:SetEnvIf remote_addr ^192.168.*.* allowedip=1
Order deny,allow
deny from all
allow from env=allowedip
Diese Datei erlaubt den Zugriff auf die Webseite nur, wenn man eine lokale IP aus dem Bereich “192.168.*.*” besitzt. [...]
Read more...
Gliederung:
BOPF Einführung
Mit BOPF arbeiten
Next Generation BO im RAP
Referenzen
Modellierung – Design Time Tools
Wir unterscheiden
Design Time: das Business Objekt (BO) wird modelliert. Festgelegt werden Datenstrukturen in Knoten (Nodes), Schlüssel und Aktionen die das BO Verhalten steuern. Die Dictionary-Objekte und die Konstantenschnittstelle werden generiert.
Run Time: ein Verbraucher (Anwendung) erzeugt BOs über einen Service Manager. Aktionen werden in der Interaction Phase ausgeführt. Mit dem Transaktionsmanager wird die Save/Cleanup Phase gestartet.
In der Design Time Tools sind
Transaktion BOB – Business Object Builder
Transaktion BOBX – Business Object Builder Expert
Transaktion BOBF
BOPF Eclipse Plug-In
Transaktion BOBT zum Testen von BOs.
Ich empfehle Transaktion BOBX zur Bearbeitung eines BOPF Modells. Aus dem Menüpunkt Springen kann die erweiterte Sicht aktiviert werden, womit die Knoten der im System vorhandene BOs angezeigt werden.
Referenzen:
Step by Step Creation of BO: https://sapyard.com/bopf-for-beginners-part-1-introduction/
Freie Kapitel in https://www.rheinwerk-verlag.de/bopf-business-objekte-mit-abap-entwickeln_4520/
Für die Fehleranalyse leisten Debugger Scripts wertvolle Hilfe.
Nachrichten
Validierungen, Ermittlungen und Aktionen erzeugen Nachrichten, die das Interface /BOBF/IF_FRW_MESSAGE implementieren. Eine BOPF-Nachricht ist immer nur für den Enduser gedacht. Sie spielt in der Verarbeitung keine Rolle. Die Nachrichten lassen sich in sehr unterschiedlichen Anwendungen (User Interfaces) einblenden.
Für jedes Projekt wird empfohlen, eine eigene Hilfsklasse mit der Oberklasse /BOBF/CM_FRW anzulegen. Diese kann neue Nachrichtenobjekte anlegen und mit ADD_MESSAGE( ) hinzufügen.
Knoten
Die Knoten haben eine baumartigen Knotenstruktur mit einem Wurzelknoten meistens ROOT und Unterknoten, z.B. eine Belegkopf und einen Knoten für die Positionen. In den Knoten wird das Verhalten modelliert.
Bemerkung: zur Laufzeit ist ein Knoten ist ein Container, vergleichbar mit einer ABAP internen Tabelle. Ein Objekt ist eine Zeile im Container und erhält eine global eindeutige Kennung (GUID). Der Verbraucher nutzt den Servicemanager um Aktionen über das aufzurufen. Diese Aufrufe haben für beliebigen BOs dieselbe Struktur, dank dem Command Pattern .
Knotenstruktur
Die Knoten können sowohl persistente Attribute enthalten, die auf der Datenbank abgelegt werden als auch transiente Attribute, die immer berechnet werden.
Technischer Schlüssel
Das technische Include /BOBF/S_FRW_KEY_INCL für Schlüssel ist für alle BO gleich (GUID für den Primärschlüssel KEY, GUID für den übergeordneter BO PARENT_KEY und GUID für die Wurzel der Knotenstruktur ROOT_KEY:
Alternative Schlüssel
Das BO Datenmodell wird in den Attribute mit dem Gruppenname NODE_DATA gepflegt.. Ein klassischer lesbarer Primärschlüssel wird oft noch definiert. In diesem Fall sollte ein Datenbank-Index definiert werden.
Die Datenstruktur mit dem Gruppenname TRANSIENT_NODE_DATA enthält die transiente Daten, die nur zur Laufzeit über Ermittlungen bestimmt werden.
Die Methode CONVERT_ALTERN_KEY( ) wird benutzt, um alternative Schlüssel in GUID umzusetzen.
Knotenelemente
Jeder Knoten hat Knotenelemente und optionale Konsistenzgruppen. Die Gruppe darf ein Fehlerstatus definieren, wo kein Speichern möglich ist.
Die generierte Konstantenschnittstelle definiert lesbare Namen für die GUID. Berechtigungen können auf Knotenebene geprüft werden.
An den Knoten werden Funktionen für Aktionen, Ermittlungen, Abfragen, … angehängt. Dazu legt man ABAP Klassen an, die nach dem Command Pattern das Interface implementieren.
Ansicht eines Business Object in Transaktion BOBX:
Assoziationen
Eine Assoziation definert eine Beziehung zwischen BO-Knoten und prägt damit das Datenmodell. Am häufigsten wird im Read API Methode RETRIEVE_BY_ASSOCIATION( ) verwendet, um auf einfach auf abhängige Objekte zuzugreifen.
Abfragen
Eine BOPF Abfrage liefert ähnlich einer SQL-Abfrage nur persistente Daten. Abfragen werden machen Sinn, wenn sie vom Verbraucher zu Beginn einer Anwendung ausgeführt werden, um die BOPF GUIDs zu holen. Innerhalb benutzt man die READ API-Methoden retrieve() oder retrieve_by_association() des Service-Managers. RETRIEVE( ) kann auch die Datenbank lesen, falls es vom Service Manager mit dem Parameter IV_BEFORE_IMAGE = ‚X‘ aufgerufen wird.
Die Abfragen
SELECT_ALL und
SELECT_BY_ELEMENTS
werden vom BOPF bei Bedarf generiert. Andere Abfrage wie SELECT_BY_ATTRIBUTES müssen das Interface /BOBF/IF_FRW_QUERY implementieren.
Aktionen
Aktionen sind vergleichbar mit öffentlichen Klassenmethoden, die Attribute verändern. Eine Aktion bearbeiten immer eine Liste von BO Schlüsselfelder. Technisch wird sie als Methode EXECUTE( ) des Interfaces /BOBF/IF_FRW_ACTION implementiert.
Innerhalb der Aktion werden über das READ API retrieve( ) aus den Schlüsseln die kompletten Datensätze gelesen und verändert. Die Änderungen werden ins Puffer über das MODIFY API update( ) geschrieben. Im Beispiel wird dann eine Nachricht erzeugt.
Das BOPF API ist untypisiert und arbeitet mit Datenreferenzen. Daher werden oft Referenzen auf lokale Daten angelegt, verändert und zum Speichern weitergegeben.
METHOD /bobf/if_frw_action~execute.
DATA ls_textid LIKE if_t100_message=>t100key.
DATA lv_severity TYPE zcm_proddevord=>ty_message_severity.
io_read->retrieve( EXPORTING iv_node = zif_proddevord_c=>sc_node-root
it_key = it_key
IMPORTING eo_message = eo_message
et_data = lt_root
et_failed_key = et_failed_key ).
LOOP AT lt_root REFERENCE INTO DATA(lr_head).
lr_head->notify_loop = zcl_pu_global=>c_loop_initial.
lr_head->qstatus = zcl_pu_global=>c_qstatus_first_step.
CLEAR lr_head->wait_until.
CLEAR lr_head->wait_for_action.
io_modify->update( iv_node = is_ctx-node_key
iv_key = lr_head->key
iv_root_key = lr_head->root_key
is_data = lr_head
it_changed_fields = VALUE #(
( zif_proddevord_c=>sc_node_attribute-root-qstatus )
( zif_proddevord_c=>sc_node_attribute-root-notify_loop )
( zif_proddevord_c=>sc_node_attribute-root-wait_until )
( zif_proddevord_c=>sc_node_attribute-root-wait_for_action ) ) ).
io_modify->end_modify( EXPORTING iv_process_immediately = abap_true
IMPORTING eo_message = DATA(lo_message) ).
IF lo_message IS BOUND AND lo_message->check( ).
ls_textid = zcm_proddevord=>reset_counter_error.
lv_severity = /bobf/cm_frw=>co_severity_error.
ELSE.
ls_textid = zcm_proddevord=>reset_counter_success.
lv_severity = /bobf/cm_frw=>co_severity_info.
ENDIF.
zcm_proddevord=>add( EXPORTING textid = ls_textid
severity = lv_severity
node_key = is_ctx-node_key
ir_pu = lr_head
CHANGING co_message = eo_message ).
ENDLOOP.
ENDMETHOD.
Validierungen
im Objektmodell würde man Validierung als Methoden abbilden. Im BOPF sind diese so weil die eine UI unabhängige Validierung von Attributen erst ermöglichen. Im BOPF gibt es
Aktionsvalidierungen: diese werden vor der verbundenen Aktion automatisch durchlaufen. Eine Validierung liefert Failed Keys zurück, d.h. BO für die Validierung fehlgeschlagen ist. Für diese BO findet keine Ausführung der verbundenen Aktion.
Konsistenzvalidierungen: diese werden nach einer Attributänderung durchlaufen und können den Status einer Konsistenzgruppe ändern. Damit kann u.U. vermieden werden, dass die Daten gesichert werden. Es wird auf jeden Fall die Aktion ausgeführt.
Das Attribut Failed Keys macht nur bei Validierung Sinn. Eine Aktion die Failed Key liefert wurde bereits ausgeführt. Daher sollte wirklich inhaltlich Aktionen von Validierungen getrennt werden. Ermittlungen die Failed Key zurückliefern werden vom BOPF später wieder mit demselben Schlüssel (für dasselbe BO) wiederholt, bis kein Failed Key zurückgeliefert wird. Dann erst ist Speichern möglich. BOPF nimmt an, Eingabewerte würden fehlen.
METHOD /bobf/if_frw_validation~execute.
DATA lt_root TYPE zpu_t_root.
io_read->retrieve( EXPORTING iv_node = zif_proddevord_c=>sc_node-root
it_key = it_key
IMPORTING eo_message = eo_message
et_data = lt_root
et_failed_key = et_failed_key ).
et_failed_key = VALUE #( FOR ls_root IN lt_root
WHERE ( NOT ( zprstk EQ zcl_pu_global=>c_pu_status_waiting_for_order
AND qstatus EQ zcl_pu_global=>c_qstatus_idle ) )
( key = ls_root-key ) ).
ENDMETHOD.
Ermittlungen
Transiente Attribute werden vom BOPF automatisch aktualisiert, mit einer eigene Kategorie von Routinen, die sog. Determinations Ermittlungen. In BOPF werden Ermittlungen automatisch bei Änderungen aufgerufen und implementieren so das Konzept der reaktiven Programmierung, wie bei Datenbank-Trigger.
Beispiel: a = 1. b = 2. Eine Ermittlung berechnet das transiente Attribut s = a + b und s hat zunächst den Wert 3. Setzt man a = 2, dann wird die Ermittlung im Hintergrund aufgerufen und s auf 4 gesetzt.
Ermittlungen können z.B. transiente Attribute nachladen. Der Wizard in der Transaktion BOB führt durch die verschiedene Anwendunsfälle und setzt die Auswertungszeitpunkte, die bei Transaktion BOBX selbst eingestellt werden müssen.
Transaktionsmodel
Der Transaktionsmanager verwaltet Lese- und Schreibzugriffe auf BO Attribute mit einem Puffer.
In sog. Interaction Phase werden die Operationen
Anlegen
Lesen
Ändern
Löschen
Aktion ausführen
Finalize
Check before Save
Adjust numbers
Save
After Save
schreibt den Puffer dann in konsistenten Datenbank-Objekte oder nimmt dies Änderungen zurück (Cleanup Phase).
Um BOPF mit existieren Verbuchungsfunktionsbausteinen zu kombinieren kann es notwendig werden, spezielle Klasse für den Datenzugriff zu benutzen. Zwei solche Data Access Classes (DAC) sind verfügbar: /BOBF/CL_DAC_TABLE und /BOBF/CL_DAC_LEGACY_TABLE).
Zusätzlich kann es notwendig sein, die Kontrolle in der Save Phase abzugeben, indem ein Slave Transaktionsmanager benutzt wird. Bei komplexen Transaktion kann mit Sync Points einen Teil-Rollback realisiert werden. [...]
Read more...
Business Object Processing Framework
Entwickler sind daran gewöhnt, Funktionen aus einer Library in Ihren Programmen aufzurufen. Im Gegensatz dazu implementieren Sie bei einem Framework Erweiterungen, die zu gegebenen Zeiten aufgerufen werden. Mit diesem Ansatz (Inversion of Control) verwalten Frameworks wie BOPF komplexe Aspekte einer Anwendung eigenständig.
Das Ergebnis ist zweischneidig: das BOPF ist mächtig und übernimmt sehr viele Aufgaben. Es schränkt jedoch mit starren Abläufen ein. Der Entwickler muss von vornherein dessen Arbeitsweise und Grenzen begreifen, um es effektiv einzusetzen.
Im Folgenden versuche ich, dem interessierten ABAP-Entwickler ein Verständnis für das BOPF vermitteln.
Was ist ein Business Object?
Ein Geschäftsobjekt (Business Object, BO) ist lediglich ein Baustein, eine Komponente in einer Anwendung, die technische Details der Anwendungslogik versteckt und in Begriffe aus der Welt der Anwender (Geschäftswelt) übersetzt.
Ein Objekt wird von Anwendungsentwickler als Abstraktion für eine Entität (Produkt, Route, Kunde, Auftrag, Bestellung, Reisekosten…) mit Zustand, eigener Identität und definiertem Verhalten. Bei einem gelungenen Design sprechen Anwender und Entwickler die gleiche Sprache.
Formal hat ein Business Object
eine Struktur im Sinne eines Datenmodells. Vergleichbar mit den Attributen einer ABAP Klasse sind beim BOPF die Knoten einer Baumstruktur.
ein Verhalten: Das BO kapselt die Geschäftslogik und stellt es den Verbrauchern als Dienst oder Protokoll zur Verfügung. In ABAP OO wäre das die öffentliche Schnittstelle einer Klasse.
eine Implementation: die Entwicklersicht, wo die Komplexität dem Verbraucher möglichst verborgen sein sollte, um Wiederverwendbarkeit zu erreichen.
Historie
Über die Jahre hat SAP hat mehrere BO Konzepte implementiert. Vorläufer waren BO im Workflow und die standardisierte Schnittstelle BAPI. CRM benutzt die Frameworks Business Object Layer (BOL) und Generic Interaction Layer(GenIL). Das Business Object Processing Framework (BOPF) wird seit Jahren in Produkten wie Supplier Relationship Management, Transport Management, Change Management, Supplier LifeCycle Management, Environment, Health and Safety, Quality Issue Management verwendet.
Seit Netweaver 7.31 können SAP Kunden mit der Komponente SAP_BS_FND (Business Suite Foundation) die alten BOPF Transaktionen /BOBF/CONF_UI, /BOBF/CUST_UI, /BOBF/TEST_UI nutzen. Allgemeinen freigegeben ist das BOPF ab Netweaver 7.40 Basis mit den neuen Transaktionen BOB, BOBX, BOBT und später das BOPF Eclipse Plug-In. CDS Views können BOPF Objekte generieren.
Die Cloud Version von ABAP enthält bereits die Weiterentwicklung von BOPF: BOs werden als Core Data Services modelliert.
Die BOPF Modellierung wurde in das RESTfull Programming Model (RAP) übernommen, mit einer nativen Unterstützung in der Programmiersprache ABAP mit der Entity Manipulation Language (EML, Create/Update/Delete Interface, Aktionen und Ermittlungen und zusätzlich Funktionen ohne Seiteneffekten) und Tools (Integration in Eclipse).
BOPF Architektur
Transaktion /BOBF/CONF_UI generiert zu jedem BO eine Visualisierung und Entwurfsdokument. Darin findet man diese Bild der BOPF Architektur:
Der Verbraucher (Consumer, z.B. die UI) nutzt Dienste aus dem Transaktionsschicht. Diese enthält den Transaktionsmanager, der Änderungen verwaltet, den Service Manager, mit dem BOs gelesen und Aktionen aufgerufen werden, und ein Konfigurationsmanager, der Meta-Daten auslesen kann. Dabei wird das BOPF Datenmodell verwendet, das zur Entwurfszeit gepflegt wurde.
Die BO Laufzeit führt die Dienste aus, d.h. Anwendung starten, bei Aufruf von Aktionen Objekt zu definierten Klassen instanziieren und deren Methoden aufrufen, Ergebnisse dem Verbraucher bereitstellen.
BOPF Meta Data Model
Sowie ABAP OO Klassen anbietet, um Daten und Routinen als Objekt zu modellieren, bietet BOPF ein Meta Model, um Geschäftsobjekte zu modellieren.https://archive.sap.com/documents/docs/DOC-45425
Das BOPF zwingt zur einer strikten Klassifizierung nach Verhalten (Eigenschaften, Validierung und Änderungen werden unterschiedlich definiert) um ein UI-unabhängiges Datenmodell zu erreichen.
Der BOPF Entwickler definiert das Verhalten seine BOs in ABAP Object Klassen, die nach dem Command Pattern eine vorgegebene Schnittstelle implementieren.
Command Pattern
Über ein Kommando kann ein Aufrufer eine Operation ausführen, ohne die konkrete Logik oder den Befehlsempfänger zu kennen. BOPF arbeitet mit GUID für die Aktionen, die über ein Konstanten-Definition zu lesbaren Namen umgesetzt werden.
Falls der Verbraucher (z.B. die GUI) eine Aktion über das DO_ACTION Service aufruft, wird das BO zur Laufzeit ein Objekt der Klasse zur Aktion anlegen und Ihre EXECUTE( ) Method ausführen, mit dem aktuellen BO Schlüsseln als Parameter.
Falls diese Aktion validiert werden muss, werden vorher die Validierung instanziiert und die EXECUTE( ) Methode ausgeführt. Die BOPF Laufzeit entscheidet aus dem Ergebnis ob die Aktion ausgeführt werden kann. Falls ja, wird das Ergebnis dem Verbraucher zur Verfügung gestellt.
Vor- und Nachteile
Die Einarbeitung kann langwierig werden falls Konzepte aus Design und Modellierung neu sind, vor allem wenn kein erfahrener Kollege im Team ist. Der Entwickler gibt die Kontrolle und damit Flexibilität an das Framework ab. Es bedarf ein Verständnis der BOPF Möglichkeiten, um möglich wenig selbst zu implementieren.
Nach der Eingewöhnung die Entwicklungszeit reduziert und die Arbeit lässt sich besser parallelisieren. Hat man einmal ein Business Object in implementiert, dann kann man in fremden Programmen BOPF Verbraucher warten.
Es stehen sehr unterschiedliche BOPF Verbraucher zur Verfügung, sei es SAP GUI Reports oder Web Dynpro Anwendungen die mit dem Floor Plan Manager generiert werden BRF+ Business Rules oder UI5 Fiori Apps. Viele diese Verbraucher sind kein Teil der Toolbox des Durchschnitts-ABAP-Entwickler.
Ein Szenario wird oft besonders hervorgehoben: die Erstellung des Datenmodells mit dem BOPF und das generieren der WebDynpro-Oberfläche mit dem Floor Plan Manager. Nach jeder Änderung kann man neu generieren und hat eine lauffähige Anwendung.
Beim BOPF ist auch das Transaktionsmanagement hervorzuheben: es gibt z.B. keine Überraschung mit implizite COMMITs, Sperren, Entsperren, Pufferverwaltung und sicheres Schreiben sind kein Problem. Es wird wahrscheinlich der Grund, warum dieses an sich alte Framework jetzt wieder in neuen Entwicklungen aufgenommen wird (CDS + BOPF, RAP).
Fortsetzung folgt…
BOPF Einführung
Mit BOPF arbeiten
Next Generation BO in RAP
Referenzen [...]
Read more...
reposted from https://blogs.sap.com/2014/05/25/paper-was-always-mobile-sap-codejam-leipzig/
A SAP CodeJam introduces a new SAP technology to developers. Participants learn to solve a set of problems at their own pace using advice from an expert. There is food, they are drinks, registration is free. After attending twice I was persuaded of the learning impact of this event format and I gathered support to bring it home.
On May 16th 2014, Informatik DV GmbH hosted its first SAP CodeJam – SAP Mobile – at our office in Leipzig. We used our website, posters and postcards to bring up local interest and were happy to greet 26 attendees.
Thanks to Craig Cmehil who invited Martin Grasshoff, SAP Mobile Platform Product manager, as our speaker. Martin cuts through the what do you want to call it today game and explain concepts clearly. He presented the architecture of SMP 3.0 and we had a peek preview in the future of Rapid Development Experience (River RDE). It felt like having the best trainer for the topic and trying to be a good student to get the most out of it.
Some of the insights I got (of course, YMMV):
Paper was always mobile. it is not mobile is you cannot work offline, it is just a web app. A mobile app can work offline with automatic conflict resolution
the challenge is to enable mobile scenarios rapidly with low total cost of ownership.
HTML5 is now good enough for mobile apps, but you want access the device gimmicks. So there are the Kapsel plugins.
OData is just a protocol, but it is a terrific innovation because it is a standard, like SQL for DB access
with the SMP 3.0 architecture, the developer does not care about being on- or offline.
In the hands-on session, Martin guided us through the steps needed to build a mobile applications in a scenario with SAP NetWeaver Gateway as a OData service, SAP Mobile Platform on SAP HANA Cloud Platform and AppBuilder.
Thanks to all participants, I hope you all enjoyed the event. If you missed it, get in touch to be informed in time for next episode in Leipzig.
Check SAP CodeJam Facebook for more pictures
Interested in working in the SAP/ABAP/HANA environment?
Please follow up on our career page. [...]
Read more...
Can you read ABAP code and understand its aim? You will expect some variation of a given pattern if you understand the solution. And if you have experience writing code in the domain you will look for idioms and you will be well aware of some pitfalls the implementation may have avoided. From your experience, you can tell whether the code is best practice or not.
But how do you teach this knowledge, how do you give advice to the original developer (my younger self), how do you teach experience? While discussing with my younger self, I would avoid saying this is good, that is bad mojo, but saying it helps me in this context and it causes trouble in this other context would let me avoid religious war. The main point here is to provide the context. This is how patterns are written. I would like to contribute to a pattern language for ABAP refactoring.
A pattern language is developed to pass knowledge. We try to say: I had this problem, I used this solution because it helped me in this context. With this information in the open, another voice can say: this lead to trouble is this other context. A well written pattern is an enlightening discussion of contexts for solution to a given problem.
Smart UI vs. MVC
Eric Evans uses the term Smart UI pattern (cf. Domain Driven Design, Chapter 4: Isolating the Domain – The Smart UI Anti Pattern) to describes the less sophisticated but wildly successful method of adding logic to the UI code. The rest of the his book advocates Domain Driven Design with a Layered Architecture approach (e.g. Model View Controller MVC) that will reduce complexity in a large project. The point is made that additional complexity and the costs of learning object modeling can make the SmartUI a pragmatic move for a given team.
So If the circumstance warrant: Put all the business logic into the user interface. Further chop the application in small functions and implement them as separate user interfaces. Use the most automated UI building tools available.
Advantage: Productivity is high and immediate for simple applications.
I want to stress the importance of the context here: even if you are shunning the Smart UI Pattern, let us recognize it is highly successful in some contexts. The problem is that the Smart UI is an Anti-Pattern in the context of layered architecture that is the prefered pattern for larger systems. So a pattern description does not just say this is good, this is bad. It tries provide context so that one can get an insight about the limits of a given method.
Category: Objects Discovery for ABAP Reports
ABAP Report Model View Controller
Name: ABAP Report Model View Controller
Problem: We have a data-driven report in Input / Processing / Output style and want to convert it to objects. How shoud an ABAP report be structured? How can we partition a report in an object oriented way?
Approach: The general design pattern is layered architecture. We define distinct layers which interact only through defined interfaces with each other. The most popular pattern is MVC.
How it works: Create an application class that will be the CONTROLLER. It has an entry point for the program that will be called at the START-OF-SELECTION event and other possibly static method of events like AT-SELECTION-SCREEN-OUTPUT.
Create an view class that will be used to implement the output, e.g. ALV.
Create a model class that will contains the business logic, e.g. how to retrieve the data.
When to use it: while converting classical reports to ABAP Objects. While refactoring existing reports.
ABAP Report Parameter Object
The suggested refactoring process for ABAP reports resonates with me. For some time now I have been extracting selection screen parameters into an object as one of the first steps. If anyone else has done the same, then I would suggest to elevate this practice to a pattern:
Name: ABAP Report Parameter Object
Problem: How do we handle the global selection screen parameters in an ABAP Objects program?
Approach: We create a new parameter object. The parameter object is passed to all routines that need access to the global variables. It can be generated with specific values in unit tests.
How it works: Create a class with a private method GET_SELECTION( ) that is called in the constructor. With this, a VALUE OBJECT is created with all current selection parameters as member variables as public read-only members of the class.
When to use it: while converting classical reports to ABAP Objects
Further Notes: The parameter object is a dumb object (data only) with minimal behavior. After refactoring, more behavior could be moved to its class.
If we have this selection-screen definition:
SELECT-OPTIONS s_class FOR vseoclass-clsname.
PARAMETERS: p_dtls AS CHECKBOX DEFAULT ‚X‘,
p_subco AS CHECKBOX.
then we can create this class
TYPES tt_r_clsname TYPE RANGE OF vseoclass-clsname.
CLASS lcl_param DEFINITION.
PUBLIC SECTION.
DATA r_clsname TYPE tt_r_clsname READ-ONLY.
DATA dtls TYPE flag READ-ONLY.
DATA subco TYPE flag READ-ONLY.
METHODS constructor.
PRIVATE SECTION.
METHODS get_selection.
ENDCLASS.
CLASS lcl_param IMPLEMENTATION.
METHOD constructor.
get_selection( ).
ENDMETHOD.
METHOD get_selection.
r_clsname[] = s_class[].
dtls = p_dtls.
subco = p_subco.
ENDMETHOD.
ENDCLASS.
If the developer does not use the global program variables, they can be renamed/removed later.
The intent is to remove any reference to global selection variables in the code an replace it by access to an attribute of the PARAMETER object. The attribute are READ-ONLY to avoid having them changed everywhere. The PARAMETER class can offer friendship to a test class to support test data creation. I think this creates a path for further refactoring.
I usually define a CONTROLLER class, that interacts with a MODEL and a VIEW class. The controller passes the PARAMETER object to all other classes.
ABAP Report User Object
In ABAP reports we see authorization checks all over the place, but they are usually not well tested. I rember the menacing words of a (friendly) colleague asking who this report was for and which roles were needed. And I remember how often my code failed to perform correctly in production because some parts where not being executed due to missing authorization, so I suggest this practice:
Name: ABAP Report User Object
Problem: How do make sure the all authorization checks are correctly handled without creating a set of test users specific for each report?
Approach: We create a new user object. The user is passed to all routines that need authorization checks. It can be replaced by mock users in unit tests.
How it works: Create a user class and move all authorization checks as public methods of the user class. The CONTROLLER class can create an object of this class and pass it around
When to use it: while converting classical reports to ABAP Objects, while refactoring ABAP reports
ABAP Report Log Object
Name: ABAP Report Log Object
Problem: How do we collect all errors and messages in an application? Can we avoid passing a message table parameter to all routines?
Approach: We create a new log object. The log object could be initialized with a parameter object. It is passed to all routines who can generate errors/messages.
How it works: Create a class with a private attribute MT_MESSAGES and some public method that can add system messages, exceptions and other messages to the message log. For system messages, the MESSAGE statement is executed with an INTO variant and the system variables SY-MSGV1 to SY-MSGV4 and other SY-MSGTY are evaluated.
When to use it: while converting classical reports to ABAP Objects, while refactoring.
Further Notes: The log object can include additional behavior like displaying the list of messages. But it should be foremost a message collector. Adding too much behavior here could hide application specific objects that should be extracted to a new class.
Further Patterns
There are 3 other patterns I would like to discuss so there could will be a next episode
Using RAISING parameter to propagate ABAP Objects Exceptions instead of Error code.
The NULL Object pattern (example)
The iterator pattern (ALV )
but the general idea ist that the pattern language is a good way to present concepts and the discussion is part of the pattern.
Interested in discussing Patterns with us?
Please follow up on our career page. [...]
Read more...
There is a difference between searching and finding, so implementing a plain text search in ABAP is more challenging than querying a table attribute. The user-specified search pattern cannot be matched using the simple test for equality used for attributes. Sophisticated plain text matching algorithms are needed to make it attractive for users attuned to Google-search:
the Full Text Search (FTS) matches a pattern exactly.
the Fuzzy search performs approximate string matching according to a distance algorithm tuned to a problem domain that finds both exact matches and deviations.
the Acoustic search is an approximate String matching according to a phonetic algorithm.
Such an user friendly, fault-tolerant search can be made fast efficiently: the approach is pre-process text and store the result in an index table. This is done behind the scene in the SAP Enterprise Search Tool. But what other options do the ABAP developer have to search for text in tables?
ABAP SQL
A simple pattern matching is enabled by the SQL LIKE statementSELECT product FROM catalog WHERE name LIKE '%_agento%'.
Long texts saved in tables STXH and STXL are accessed with function modules like READ_TEXT that can also read the text buffer and the archive. So this approach does not allow searching for long text. Fuzzy search is not possible in ABAP SQL.
If you do not have TREX of SAP Enterprise Search enabled, then generic text search might not be high on your agenda.
HANA DB
The HANA DB (HDB) supports FTS, Fuzzy and Acoustic search (cf. search, analysis and mining).
I discovered CONTAINS( ) predicate in HANA DB as I wanted to implement a text search in ABAP. Note the prerequisite is to create a full-text index. This can be done in the dictionary for HDB only. The alternative is to define the column as TEXT at HDB table definition level.
My approach for long text search was to duplicate the long text content into a HDB TEXT column. This in effect creates an index, enabling queries using CONTAINS( ).
CONTAINS( ) queries were implemented using ABAP Database Connectivity (ADBC). I came to realize Fuzzy Text search could be a Killer App for ADBC. It works! (and yes, you can also use AMDP.
The ADBC methods can be used to
create and manage connections to all database maintained in transaction DBACOCKPIT (table DBCON).
execute DB specific statements
process the query results
Known Problems:
No automatic client handling
Security risk: SQL Injection.
Important ADBC Classes
CL_SQL_CONNECTION
CL_SQL_STATEMENT
execute_ddl( ) – CREATE TABLE, DROP TABLE
execute_query( ) – SELECT FROM WHERE
execute_update( ) – INSERT INTO VALUES ( )
CL_SQL_PREPARED_STATEMENT
prepare_statement( )
CL_SQL_RESULT_SET – Result set of an SQL query
set_param_table( )
next_package( )
close( )
CX_SQL_EXCEPTION
CL_SHDB_SELTAB method sql_where_condition( ) and the static method COMBINE_SELTABS( ) shall be used to convert SELECT-OPTIONS into Native SQL.
Demo Report DEMO_ADBC_DDL_DML
Sample Code
generates SQL statement for fuzzy text search for the pattern TEST OR PALLETSELECT TOP 200 DISTINCT SCORE() AS FUZZY_SCORE, K.EBELN, P.EBELP
FROM EKKO AS K INNER JOIN EKPO AS P ON K.EBELN = P.EBELN
AND K.MANDT = P.MANDT
INNER JOIN LFA1 AS L ON K.LIFNR = L.LIFNR AND K.MANDT = L.MANDT
LEFT OUTER JOIN ADRC AS A ON L.ADRNR = A.ADDRNUMBER
AND L.MANDT = A.CLIENT
WHERE K.MANDT = '140' AND A.NATION = ''
AND CONTAINS(P.TXZ01, 'TEST OR PALLET',
FUZZY(0.8, 'textSearch=compare,similarCalculationMode=search' ))
AND K.EKORG = 'GLOB'
AND K.KDATB <= '20190213' AND K.KDATE >= '20190424'
AND K.LOEKZ = '' AND P.LOEKZ = ''
AND SCORE() >= 0.20000000
ORDER BY FUZZY_SCORE DESC
Sybase
After this successful endeavour, I searched the web and realized the CONTAINS( ) predicate is also available on other platforms. The SAP Netweaver demo system NPL runs on Sybase. ABAP does not support full text index for other DBs.
Index ZTOAD-Y01 is of type Full Text. This is not implemented for DB SYB
Message no. DT558
But Sybase also support the CONTAINS( ) statement for exact and fuzzy text search.
So how to enable the indexing? The help says “NGRAM TEXT index search is mainly useful when words are misspelled. Sybase IQ does not support searches like synonyms and antonyms.” for Sybase (SYB).
Oracle DB
Oracle supports fuzzy text search with a variants of the CONTAINS( ) statement.
MS SLQ
MS SQL Server supports full-text searching with the CONTAINS( ) statement.
Other DBs
On other systems, you could try to use a custom of the string distance functions as a DB procedure if not already available.
Here is a T-SQL (MS SQL server) implementation of the Levenshtein distance.
MySQL has Full Text Search WHERE MATCH(column) AGAINST('Rose', 'Crown') and the SOUNDEX( ) function for acoustic search
SELECT * FROM `author_bios`
WHERE SOUNDEX(`auth_last_name`) = SOUNDEX('Williams')
Summary
The CONTAINS( ) statement is very helpful for text search, but it is not supported in Open SQL. However, variants of this statements are supported by various databases. With this, Fuzzy Text search could be implemented using ABAP DB Connectivity (ADBC). It works well for SAP HANA and I think it can be made to work for other platforms.
So ABAP Text Search seems to be the poster use case for ADBC.
Interested in working in the SAP/ABAP/HANA environment?
Please follow up on our career page. [...]
Read more...