ABAP Entwicklung – Fallstricke und Lösungen
Verwendung: Vor Projektstart komplett durchlesen, bei Fehlern Symptome nachschlagen. Dieser Skill enthält nur projektübergreifend nutzbare Erfahrungen. Umgebungsadressen, Transportaufträge und Objektlisten sind im übergeordneten Projekt-Handbuch (z. B. sap-abap-project-ops) zu finden.
Die vollständigen 120 Einträge (mit Code-Ausschnitten und Quellen) befinden sich in references/casebook.md.
Verwendung: Neues Projekt → direkt §11 Checkliste (chronologisch abarbeiten); laufendes Projekt mit Fehler → §10 Symptom-Suche → zur entsprechenden Sektion; für vollständigen Code und Quellen eines Eintrags → references/casebook.md.
Drei Meta-Regeln (wichtiger als jeder einzelne Fallstrick):
- Fehlermeldung stimmt oft nicht mit der Ursache überein – Der ABAP-Compiler zeigt meist die Stelle an, wo er den Fehler erkennt, nicht die Fehlerstelle. Gehen Sie zuerst von Reihenfolge/Schlüsselwortreihenfolge/Typkonflikt aus, nicht vom Syntax selbst.
- Das Gefährlichste in SAP ist nicht der Fehler, sondern das stille Versagen – Die dreifache Täuschung „Rückgabe S, Aktivierung 200, Kompilierung OK“ verbirgt stille Datenfehler. Bei BAPI,
SELECT INTO TABLE,EXTENSIONIN, Textelementen und Enhancement-Feldern ist ein Datenabgleich nach Datenbankupdate zwingend erforderlich – vertrauen Sie nicht auf den Rückgabewert. - Bei Fehlern sofort ST22 prüfen, nicht raten – Der Kurzdump
Error analysisliefert direkt dieprocedure "XXX" "(FORM)"und dieCX_SY_*Ausnahmeklasse – das ist eine Größenordnung schneller als das Testen einzelner FORM.
0. Zehn eiserne Regeln (Verstoß bedeutet sofortige Nacharbeit)
- Vor jeder Änderung eines existierenden Objekts, Schritt 1: Rücklesen aus dem aktuellen System. Benutzer ändern oft direkt in SE80 Produktivobjekte, ohne das Archiv zu aktualisieren. Das lokale Archiv dient nur zum Vergleich, nicht als Basis.
get_source→ Vergleich mit Backup (0 Differenz) → Patch → Rücklesen + erneuter Vergleich → Rückwärts ins Archiv. - Textelemente gehören ebenfalls zur Basis,
get_sourceenthält kein TEXTPOOL. Vor dem Überschreiben von Texten mitpush_textelementsmussread_textelementsden gesamten aktivierten Text archivieren, mit dem lokalen JSON vergleichen, manuell zusammenführen und dann pushen. Sonst werden Änderungen des Benutzers in SE38 spurlos überschrieben – der Quellcode-Diff zeigt nichts. - NIEMALS direkt UPDATE/MODIFY auf Standardtabellen. Änderungen an gebuchten FI-Belegen erfolgen über
FI_DOCUMENT_CHANGE(GruppeFACG,T_ACCHGnur mitFDNAME+NEWVAL, Kopfzeilen ohneI_BUZEI, nur Positionen mit). - Transportauftrag nicht selbst erzeugen. Fehlt
CORR_NRbeim Push eines transportierbaren Objekts → Abbruch mit Meldung, Benutzer auffordern, die Nummer zu geben; vor dem Anlegen eines neuen TADIR-Objekts zuerst „Typ+Vorschlagsname+TADIR-Doppelcheck+Nummernvergabe-Begründung“ melden, bestätigen lassen, dann anlegen. Bereits einem Transport zugeordnete Objekte verwenden die vorhandene Nummer weiter – keine erneute Rückfrage. - NIEMALS
Update*-Klassen für testweise Aufrufe von Schnittstellen verwenden. Sie schreiben wirklich – es gab einen Fall, woUpdateFunctionModulemit nur einem Funktionsbaustein zum Testen den inaktiven Quellcode auf 620 Zeichen verstümmelt hat. Sonden nur lesend. - Vier Schritte zur Auslieferung: Generieren → Quellcode pushen → Textelemente pushen (
PROG/PXmuss separat aktiviert werden) → GUI-Status generieren (CUA). Ohne Aktivierung der Textelemente sindTEXT-xxxzur Laufzeit immer leer. - Produktivquellcode-Kommentare: Gerüst behalten, Erläuterungen entfernen. Programm-Kopf (Firma/Autor/Datum/Zusammenfassung/Änderungshistorie) +
*&---Rahmenlinien + FORM-Banner +-->/<--Parameter-Dokumentation + kurze Kommentare zu Bedingungen. Entfernen: Entwurfsbegründungen, Berechnungslogik, Tests, TODO-Begründungen,scripts/work/ADT/ZTEST_*Referenzen. Nach dem Strippen muss ein Diff belegen, dass der Code unverändert ist. - Test-/Sondenobjekte nach Test sofort löschen. Zuerst
get_sourceinsbackup/→ Löschen →TADIRauf Null prüfen. Nur selbst erstellte Objekte löschen; von anderen erstellte, einem Paket/Transport zugeordnete, oder aktive DEMOs unberührt lassen. - Vor dem Schreiben in SAP: erst Sperre prüfen, dann Backup. Credentials nicht teilen, nicht anzeigen, nicht in Auslieferungspakete aufnehmen.
- Wenn der Benutzer sagt „Daten sind in Ordnung“, umso mehr vom Programmfehler ausgehen. Anhand der Fehler-„Betrag+Belegnummer“ die Eingabe rekonstruieren, eine Sonde schreiben, die den Fehler exakt reproduziert; Reproduktion = Ursache gefunden.
1. Aktivierung und Schreibkanäle (ADT / MCP)
- HTTP 200 ≠ Aktivierung erfolgreich – Erfolg/Misserfolg steht im Body: explizit auf
activationExecuted="false"prüfen und Fehler werfen. Der einzige zuverlässige Weg, die Aktivierung zu verifizieren, ist der Rücklesevergleich von include oder?version=activemit der inaktiven Version.TRDIR-RSTATwird in S/4 nicht geschrieben. - Scheinerfolg am schwersten zu erkennen – Alte Skripte suchten mit Regex nach
<chklst:shortText>/<msg>text</msg>. Die echte Struktur istchkl:Präfix +<shortText><txt>+ msg mit Attributen => Keine einzige Meldung wird gefunden, selbst nach dreimaliger Meldung „Aktivierung erfolgreich“ ist es ein 131-Zeichen-Leergehäuse. Wird keine Meldung gefunden, muss als Fehler behandelt werden. - Textelemente sind eigenständig aktivierbare Objekte
PROG/PX– Aktivierung des Elternprogramms aktiviert sie nicht mit;push_textelementsohneGET→LOCK→PUT→UNLOCKund ohne ACTIVATE schreibt nur die inaktive Version.PROG/Psenden = SAP sieht nichts Aktivierbares, retourniertactivationExecuted="false"und keine einzige Meldung => stille Wirkungslosigkeit. Der Aktivierungsendpunkt akzeptiert keincorrNr(sonst 400). push_textelementsPUT überschreibt komplett, Reihenfolge darf nicht vertauscht werden – Neue Symbole: erst Textelemente, dann Quellcode; Löschen von Symbolen: erst Quellcode, dann Textelemente.TE_FILEmuss zusammen mit allen vorhandenen selections vollständig übergeben werden.- Bei nur inaktiver Version von Textelementen: Programm muss neu generiert werden, um Änderungen zu aktivieren – Nur Textelemente pushen + dann Quellcode mit identischem Inhalt PUT => keine Aktualisierung. Eine Codezeile ändern (auch Kommentar) und dann pushen oder Programm neu anlegen.
- Drei Arten von „Pseudo-Sperren“ – 1) Meldung „Bereits in Transportauftrag yyy von Benutzer xxx gesperrt“ ist eigentlich fehlender
transport_request/corrNr; 2)UpdateDataElementmeldetlocked by another user– tatsächlich 400 „bereits vorhanden“ – HandlerisAlreadyExistsError()erkennt nur englische Meldungen, nicht chinesische, fällt auf 403-Fallback mit Übersetzung „gesperrt“ (⇒ in chinesischer Umgebung können vorhandene Datenelemente nicht geändert werden,UpdateDomaindagegen hat diese Einschränkung nicht); 3)403 … not specified for this client= falscher Mandant. - Echte Sperren nur über SM12 lösbar – Die drei Sperrtypen
ESDICT/ESRDIRE/SEOCLSENQhängen an ADT-Sessions.DEQUEUE_ALL,_scope='3',DEQUEUE_EESDICTalle wirkungslos. Nach Bestätigung viaENQUEUE_READBenutzer bitten, die Sperre in SM12 manuell zu löschen und sofort neu zu starten; nicht auf Session-Timeout warten. - Zwei 403-Meldungen haben völlig unterschiedliche Bedeutung –
User XXX is currently editing ZFIDxxx= echter Benutzer mit SE80 offen, warten;Object ... locked in task DEVK90011x not specified for this client= falscher Mandant, mit dem Transportauftrag des richtigen Mandanten neu pushen. Verwechslung führt zu sinnlosen Suchschleifen. - ADT LOCK
Accept-Header muss Datentyp enthalten – Nurapplication/xmlsenden ⇒ SAP sperrt erfolgreich, bekommt aber 406 und kannLOCK_HANDLEnicht auslesen ⇒ Sperrleck, alle folgenden Requests melden „Benutzer X bearbeitet gerade“ und täuschen vor, ein anderer sei schuld. Richtig:application/vnd.sap.as+xml;dataname=com.sap.ad.lock.result. - Skript
fail()mitprocess.exit()überspringtfinally⇒ hängende Enqueue-Sperre – Nach abnormalem Beenden eines Push-Skripts bleibtSEOCLSENQ40 Minuten lang, alle folgenden LOCK-Requests erhalten 403 wie „Benutzer mit SE80 offen“. Skripte schreiben: zuerst UNLOCK, dann beenden, oderprocess.exitCode = 1verwenden. - Niemals Editier-Sperren für Benutzer gewaltsam löschen – ADT-Sperre löschen würde ungespeicherte Änderungen des Benutzers vernichten.
- Includes sind eigenständige ADT-Ressourcen – POST an
/programs/programserzeugt immer ein ausführbares Programm (TRDIR-SUBC=1), später nicht änderbar; Include-POST an/sap/bc/adt/programs/includes(Root-Element<include:abapInclude>). Hauptprogramm und Include müssen mit derselben activation-Request aktiviert werden, sonst Fehler wegen undefinierter Referenzen. INSERT REPORT ... STATE 'A'erzeugte Includes schreiben Quellcode als aktive Version – Spätere Änderung der inaktiven Version → Aktivierung des Hauptprogramms kompiliert mit der alten aktiven Version, Fehler verweisen auf alten Code. Nach Änderung mussINSERT REPORT 'X' FROM lt_src STATE 'A' PROGRAM TYPE 'I'.(STATE vor PROGRAM TYPE) die aktive Version aktualisieren.- Lokaler Proxy kann ADT-Requests blockieren – Symptom identisch mit „Verbindung zu SAP verloren“ (
socket hang up/ CSRF-Fehler). Vor spawnen des MCP-Prozesses*_proxylöschen undNO_PROXY=*setzen; schreibende Requests immer mitproxy: false. - ADT hat keine dynpro / GUI Status / Menu Ressourcen – CUA kann aber codiert werden (
RS_CUA_INTERNAL_FETCH+_WRITE,state='A', sofort wirksam).D020Sist der dynpro-Screen-Header, nicht die GUI-Status-Tabelle;RS_CUA_GET_STATUSist nicht zur Beurteilung geeignet, nurRS_CUA_INTERNAL_FETCH(alle 10 Pflicht-TABLES angeben,ORDER BY ppositiongegen 25-Zeilen-Abschneidung).
2. Syntax-Zwänge (einmal durch den Compiler)
- FORM-Parameter vier Tabus – 1) Kein
TYPE c LENGTH n(meldetNumeric literals cannot be used as field names) ⇒TYPE char<n>oder DDIC-Typ; 2) Feste Länge empfängt unterschiedlich lange Parameter – Compiler warnt inkompatibel, Textelemente ausgenommen, aber zur Laufzeit dennoch 500Type conflict when calling a FORM⇒ generisch mitTYPE c; 3) Kein InlineTYPE STANDARD TABLE OF(Parameter wird als 8 gezählt, meldetDifferent number of parameters) ⇒ vorherTYPESdefinieren; 4)FIELD-SYMBOLin FORM lokal – für Form-übergreifende Verwendung global deklarieren. FORM ... TABLESmuss vorUSING/CHANGINGstehen – SonstField "TABLES" is unknown.(Schlüsselwort als Parametername interpretiert).PERFORM-Aufruf analog.CALL FUNCTIONParameter-Reihenfolge ist zwingend –EXPORTING → IMPORTING → CHANGING → TABLES → EXCEPTIONS.TABLESvorIMPORTING⇒Unable to interpret "EV_OBJNR", Ursache (Reihenfolge) nicht ersichtlich.EXPORTINGdes gerufenen Moduls muss im aufrufendenIMPORTING-Abschnitt stehen – sonstCALL_FUNCTION_PARM_UNKNOWN– leicht fälschlich als „FM-Schnittstelle nicht generiert“ interpretiert.- Mehrere Felder in SELECT durch Komma trennen,
@für Host-Variablen zwingend –elements in the "SELECT LIST" list must be separated using commas.UPDATE ... SETMehrfachzuweisung analog; Konstanten (CONSTANTS) inWHEREebenfalls mit@. - Position von
INTOundUP TO n ROWS: wie vom Aktivator gemeldet. Bei JOIN/Unterabfragen/SQL-Ausdrücken istINTOam Ende zwingend (SELECT 563);UP TO n ROWSvorWHERE. Verschiedene Statements verhalten sich unterschiedlich – auswendig lernen führt zu Fehlern. ASSIGN ... CASTING TYPE c LENGTH <Variable>nicht erlaubt – Länge muss Literal sein; Weglassen vonLENGTHführt bei Index-Überschreitung zu DUMPInvalid subfield access. Korrekt:cl_abap_container_utilities=>fill_container_c( )(invers zur StandardfunktionREAD_CONTAINER_C).DESCRIBE FIELD–TYPEvorCOMPONENTS– Auch wenn nur die Anzahl der Komponenten benötigt wird:DESCRIBE FIELD cs_data TYPE lv_type COMPONENTS lv_count.CONSTANTSbenötigtVALUE–CONSTANTS: c_x TYPE flag.⇒"VALUE ..." is not specified.MESSAGE TEXT-xxx TYPE 'S' WITH nungültig – Textsymbol kann nicht als Nachrichtentext mitWITHverwendet werden. Hilfs-FORM:lv_text = pv_text → REPLACE '&' IN lv_text WITH lv_num → MESSAGE lv_text TYPE 'S'.- Feste Längen haben keinen Typnamen
char<n>–TYPE char1000/char55/char220alle illegal, meldenType "X" is unknown. ImmerTYPE c LENGTH n. Falschen Typnamen erraten = eine Aktivierungsrunde verschwendet. Im ZweifelDD04Loderget_sourceprüfen. class=>methodnur für statische Methoden – Instanzmethoden:CREATE OBJECT lo. lo->method( ), sonstcan only be specified with static methods.- Stringvorlage nicht als
PERFORM ... USING-Argument zulässig –Field "|" is unknown; erst Variable zuweisen, dann übergeben. CALL TRANSACTION ... WITH f1 v1ist keine gültige Syntax –WITHnur beiAUTHORITY-CHECK. Parameterübergabe nur überUSING bdcdata; SM30-Startbildschirm hat nur das Tabellenfeld (VIMDYNFLDSohne Update-Modus-Feld), Änderungsmodus mitVIEW_MAINTENANCE_CALL+action = 'U'.REPLACE ALL OCCURRENCES OF ... WITH ''führt bei diesem Typsystem zur Endlosschleife – Leerzeichen/Zeichen löschen: zeichenweiseDO strlen TIMES+lv_txt+lv_i(1)Filtern.CONCATENATEakzeptiert keine numerischen Werte –lv_cnt TYPE i⇒ muss character sein; eingebaute Funktionen (strlen/lines) nicht direkt in&&(meldetThe syntax for a method specification is objref->method, was fälschlich auf Pfeilsyntax hindeutet). Numerische Werte immer mit Stringvorlage|...{ lv_cnt }...|.- In
$TMPSonden: kein FORM, Kommentare außerhalb von Methoden werden nicht gespeichert – Sonde aktivieren meldetStatement is not accessible(CheckProgramdagegen nicht); Kommentare zwischenENDMETHODund nächstemMETHODin Klasse meldenunknown comments which can't be stored. SELECT-OPTIONSName ≤ 8 Zeichen – 9 Zeichen: erste Aktivierung wird abgelehnt. Kürzer umbenennen erfordert gleichzeitige Änderung des Eintrags im Textelementselections(alte Variante mit altem Namen wird still ignoriert, nur Hinweis, kein Fehler).
3. Stille Fehlerliste (die teuerste Sektion)
Alles, was „S / Aktivierung 200 / Kompilierung OK“ meldet, aber nicht richtig funktioniert – zuerst hier checken.
3.1 Datenverschiebung und -verlust
SELECTmit expliziter FeldlisteINTO TABLE <vorher deklarierte Tabelle mit vollem Typ>– Zuordnung nach Position – Bei passendem Typ kein Fehler, keine Warnung:DD07T-DOMVALlandet inDOMNAME,COEP-WKGBTRinMEGBTR(Betrag immer leer),ZTEPIC3Ameldetcomponent "ZJK" not compatible with "ZJYRQ"(Position mit_act_dbgermittelt). Ein Fallstrick tritt in 5 Berichten wiederholt auf. Drei Lösungen:SELECT */INTO TABLE @DATA(...)inline (Struktur durch Liste bestimmt) / eigene schmale Struktur in Spaltenreihenfolge; oderINTO CORRESPONDING FIELDS OF TABLE.LEFT JOINrechte Tabellenfelder inWHERE⇒ semantische Degradation zuINNER JOIN– Bei fehlendem Match der rechten Tabelle = NULL, Bedingung nicht erfüllt. Filterbedingungen inONverschieben.DELETE ADJACENT DUPLICATES ... COMPARING <nicht Schlüsselfeld>löscht Daten – Beispiel A017: Primärschlüssel enthältDATBI,KNUMHist kein Schlüssel.COMPARING knumhlöscht alle nachfolgenden Gültigkeitszeiträume mit gleichem KNUMH. Vor dem LöschenSORT BYüber alle Schlüssel, dannCOMPARINGüber alle Schlüssel.AT NEW/AT END OFbasiert auf physischer Nachbarschaft, nach ALV-Rückschreiben ungültig –REUSE_ALV_*sortiertt_outtabum (Benutzer sortiert nach einer Spalte), dann nicht mehr benachbart,AT END OFwird mehrfach ausgelöst. Gruppierung ersetzen durch „äußere Schleife über die eindeutigen Schlüssel × innere Schleife, die alle zugehörigen Einträge sammelt“ – Gruppengrenze nur über Geschäftsschlüssel.APPENDüberschreibtsy-tabix– NachAPPEND INITIAL LINE ... ASSIGNING <fs>istsy-tabixdie Abschnittsnummer, nicht die Zeilennummer ⇒ Intervall wird vergrößert, 128 Zeilen werden zu 558 (derselbe Eintrag 43 mal wiederholt). In der Schleife zuerstlv_tabix = sy-tabix.sichern.READ TABLE ... WITH KEYohne Treffer setztsy-tabixauf 0 – AnschließendAPPEND sy-tabix TO lt_delfügt 0 in Löscheliste,DELETE ... INDEX 0führt sofort zu DUMPTABLE_INVALID_INDEX. In Schleife Zeilennummer speichern + vor LöschenIF idx >= 1 AND idx <= lines( ).DATA(...)inline inIF-Zweig deklariert – Bei nicht erfüllter Bedingung wurde die interne Tabelle nie erzeugt ⇒ folgenderLOOPverursacht Kurzdump. Deklaration an den Methodenkopf verschieben oder vor Datenzugriff auf Leerheit prüfen.
3.2 BAPI / Standard-FMs „Rückgabe S oder kein Fehler, aber nicht geschrieben“
- BAPI
x-Struktur muss zusammen mit der Ursprungsstruktur übergeben werden – Nurdepreciationareas, keindepreciationareasx⇒ AFASL gefüllt, FehlerNo change parameter given for depreciation area 01. Fehlende x-Struktur: kein Syntaxfehler, nur ausgelöst, wenn Datenbedingungen erfüllt sind. - Daten-Element von SD-Erweiterungsfeld-ID-Struktur muss
CHAR1sein –SD_SALESDOCUMENT_CHANGEüberGET_CHANGEABLE_FIELDSlistet nur Felder mitrollname = 'CHAR1' OR 'CHAR01'auf. Felder mitBAPIUPDATEwerden komplett ausgeschlossen ⇒ Rückgabe S, Wert unverändert, kein Fehler – im Erstellungsfall wird diese Liste nicht verwendet ⇒ typisch: „Anlegen geht, Ändern nicht“. Nicht aufBAPIUPDATEumstellbar. BAPIPAREX-VALUEPART1nur 240 Zeichen – Standard-FM liestVALUEPART1~4(960 Zeichen) fortlaufend über Offset. Nur erstes Segment füllen ⇒ restliche still abgeschnitten (BAPE_VBAP271 Zeichen, letzte 3 Felder leer, kein Fehler). Nach 240 aufteilen, 4 Segmente füllen.- Gutschrift-/Belastungs-/Auftragsarten ohne Planzeilen: Menge nur in
TARGET_QTY–BAPISDITMhat kein Feld mit Referenz aufKWMENG; Positionskategorie hat keinen Eintrag inTVEPZ⇒ keine Planzeilen generiert, übergebeneBAPISCHDLwerden still verworfen, Rückgabe S, aberKWMENG=0,NETWR=0,VBEP0 Zeilen. Bestimmung: Eintrag inTVEPZvorhanden?, nichtTVAP-LFREL(LFRELsowohl fürZ001als auchZ014leer), nichtVBTYP. Lesen derselbe Fallstrick:VBAP-KWMENGimmer 0, Menge inVBAP-ZMENG⇒ Logik: „KWMENGnicht leer bevorzugen, sonst aufZMENGzurückfallen“. BAPE_*erstes Feld ist Schlüssel, bei Änderungsmodus nicht übergeben → Belegnummer gelöscht – Standard-FM kopiertEXTENSIONINmitmove-correspondingnachE_VBAKKOM, leeresVBELNüberschreibt die Nummer ⇒V1 045gefolgt vonV4 219(Ursache in der E-Meldung vorV4 219suchen).- SD-Referenzbelege: Dreier-Set –
REF_DOC+REF_DOC_IT+REF_DOC_CA(Referenzbelegkategorie); nur die ersten beiden ⇒V2 068 ohne Belegkategorie, aber mit C(&2leer). Zeilennummer 0 oder Beleg nicht vorhanden = gleiche Meldung, aber&3leer. Kategorie =VBAK-VBTYPdes referenzierten Belegs, nicht KopfzeileVGTYP. SD_SALESDOCUMENT_CHANGE– Eingangstabellen jeweils sortieren – Zu löschende Zeilen müssen inITEM_INundITEM_INXbeideITM_NUMBERenthalten; nurITEM_INXführt zu falscher Zuordnung mit völlig unzusammenhängender MeldungT184 Keine Positionskategorie verfügbar.BAPI_USER_EXISTENCE_CHECKliefert in diesem System für beliebige Benutzer 0 zurück (auchZZZZZZ999gilt als vorhanden) ⇒ Prüfung wirkungslos, Belege an ungültige Benutzer werden von beiden Oberflächen nicht gesehen. DirektUSR02prüfen + Längenbeschränkung (0 < len <= 12).DELETE_TEXTfür nicht vorhandenen Text wirftRAISING NOT_FOUND– Standard-FM fügt die E-Meldung anBAPIRET2, vonfrm_check_returnals Fehler gewertet ⇒ „Löschen“ führt zu Schnittstellenfehler. Vor dem LöschenSTXHprüfen (TDOBJECT+TDNAME+TDID+TDSPRAS). Falls nicht vorhanden, überspringen (Operation wird damit idempotent). Zusätzlich: InSD_SALES_DOCU_MAINTAINist der Funktionscode für Löschen003(DELETE_TEXT);&&/005schreibt nur eine leere Zeile.- Neue Konditionen: Schrittnummer nicht selbst vergeben – Hartcodiert
COND_ST_NO='001', aberZP01hatSTUNR=010⇒ Meldung „Konditionsart nicht in Schrittnummer 001“. Leer lassen, automatische Positionierung durch Konditionsverarbeitung; FlagUPDATEFLAG=I/Ubasierend auf Existenz der Kondition. - Bei Schnittstellen-Umbuchung gibt
OBJ_KEYnur eine Belegnummer zurück ⇒ Anzahl Belege zu niedrig, Rückschreibung auf falschen Beleg. Layout:Belegnummer(10)+Buchungskreis(4)+Geschäftsjahr(4), das Jahr steht ab+14(4)(häufig fälschlich+10(4)verwendet, was den Buchungskreis ergibt). BSEG-BUZEIistNUMC(3), physisches Maximum 999 Zeilen pro Beleg, BAPI akzeptiert nur 990 – Aufteilung: max.989 Details + 1 Saldo; Grenzfälle 999/1000/1997 müssen abgedeckt werden (1997 wird bei Prüfung>999in 1000 Zeilen geschnitten, was überläuft).BAPIACHE09hat kein FeldEXCH_RATE– Bei Doppelwährung (00+10 Zeilen): nur 00er Zeile mit Kurs → System übernimmt Kurs aus OB08 für den Kopf (Eingabe 7, Ergebnis 8.28). Auch dieCURR_TYPE='10'-Zeile mussexch_rateenthalten, sowohl für Detail- als auch Saldozeilen an zwei Stellen.- Doppelwährungsbeleg: Jeder
ITEMNO_ACCbenötigt zweiCURRENCYAMOUNT-Einträge, und jede Währungssumme muss 0 sein – Fehlende10-Zeile ⇒Inconsistent currency information; auch die durch Aufteilung entstandenen Saldozeilen müssen ergänzt werden. UMSKZhat in keiner der drei BAPI-Strukturen ein Feld – Bei Wechsel (W/S) über Standard-BAPI wird es still verworfen, Konto falsch. Nur überEXTENSION2als Enhancement in die Datenbank schreiben oder komplett überPOSTING_INTERFACE_*(FB01 Batch-Input).READ_TEXT– ParameterNAMEistTDOBNAME(70), ArgumentTDNAME(16) verursacht Laufzeit-Typkonflikt – Kompilierung OK, mit leerem Eingabeparameter funktioniert es, aber bei vorhandenen Daten: 500Type conflict during a function module call. Parameter mitTYPE tdobnamedeklarieren oder direkt die Cluster-TabelleSTXLlesen.
3.3 Texte / Beschreibungen / Spaltenüberschriften werden leer
- Feld mit anonymem Inline-Typ ⇒
DD03T-Text leer ⇒ ALV-Spaltenüberschrift komplett leer – Feld muss ein Datenelement haben (danach istDD03Tmit 0 Zeilen korrekt, Text wandert nach oben). Strukturfeldtexte nur über DDL mit@EndUserText.labelpro Feld; verschachtelteSTRU/TTYP-Felder haben nie einenDD03T-Text (SAP-Standardstrukturen auch nicht).CreateStructurefields[].descriptionlandet nicht inDD03T. - Textelemente nicht aktiviert / nur inaktive Version ⇒ zur Laufzeit
TEXT-xxximmer leer – ALV-Spaltenüberschrift fällt auf DDIC-Kurztext zurück, Statusspalte leer. - Domain nur Typ definiert, keine Werte gepflegt ⇒ F4 auf Selektionsbild öffnet keine Liste –
DD07TbenötigtAS4LOCAL='A'(sonst inaktive Zeilen),ORDER BY VALPOS(sonst lexikografisch 1,10,11,2,3…), Sprachfallback auf Domain-HauptspracheDD01L-DOMMASTER(Feldname nichtMASTERLANG). MESSAGE ... WITHhat eine Obergrenze von ca. 70 Zeichen für den gesamten Text ⇒ Lange BAPI-Fehler werden still abgeschnitten (z.B.Field Reas...), lenkt die Fehlersuche in die falsche Richtung. Stattdessen kurzes Präfix + Stringvorlage mit Originaltext (≤220 Zeichen, passend zuBAPI_MSG).BAPIACGL09-ITEM_TEXT(50) /BAPIACHE09-HEADER_TXT(25) still rechts abgeschnitten – Beim Zusammensetzen von „Präfix+Suffix“ zuerst das Präfix aufLänge - Suffixlängekürzen.- DDIC-Komponentenname (30 Zeichen) still abgeschnitten – JSON-Schlüsselzuordnung verliert dadurch unerwartet Schlüssel.
3.4 Berechtigungen und Filter
AUTHORITY-CHECKmit leerem Wert ist meistens „keine Berechtigung“ ⇒ zugehörige Datensätze dauerhaft unsichtbar – In Rollen wirdEKGRP = spacenormalerweise nicht vergeben. Organisationsstufen-Felder leer: Prüfung überspringen und direkt zulassen –IF lv_ekgrp IS NOT INITIAL. AUTHORITY-CHECK ... ENDIF.. Statt kompletter Bildschirmsperre zeilenweise filtern, sonst können Benutzer ohne Berechtigung den Transaktionscode gar nicht erst ausführen.MODIF IDsteuert nur Sichtbarkeit, ausgefüllte Werte in versteckten Feldern nehmen trotzdem am Filter teil – Verstecken-Szenario: auf der Datenseite explizit mit RANGE abfangen.
4. Transaktionen und LUW
EXPORT ... TO DATABASE(Cluster-Log) unterliegt der Transaktionssteuerung – „Datenbanktabelle schreiben → Beleg anlegen → Fehler: Rollback“ ⇒ Log und Tabelle im selben LUW,ROLLBACKlöscht auch die Snapshot-Eingabe vonindx=01(indx=02bleibt). Zudem führt die unterlagerte Buchungs-FM internBAPI_TRANSACTION_ROLLBACKdurch – daher wirkt weder das Entfernen des eigenenROLLBACKnoch das Ändern der Reihenfolge. ⇒ Lösung: Vor dem einzigen Schreibzugriff und vor dem Aufruf der BAPI ein separatesCOMMIT WORKeinfügen –ROLLBACKbleibt erhalten. Zusatznutzen: Der eigene LUW wird vor dem BAPI-Aufruf konsolidiert – ohnehin empfohlene Vorgehensweise.- Atomarität erfordert gemeinsamen Commit; unabhängige Commits erfordern Rückleseprüfung –
SD_SALESDOCUMENT_CHANGEmitCALL_FROM_BAPI='X'hängt das Update in die Update-Warteschlange – ohne Commit nicht sichtbar. Entfernt manCOMMIT WORK AND WAIT, führt die nachfolgendeSELECT-Rückleseprüfung fälschlich zu „Flag nicht aktualisiert“. Bei Fehler im Buchungszweig mussROLLBACK WORKergänzt werden, sonst bleibt das ausstehende SD-Update hängen und wird beim nächsten Commit fälschlich gespeichert. - Plattform-Snapshot bindet nur FM-Parameter, lokale Variablen der Funktion nicht – Nachrichten in
lv_req/lv_respsind von der Plattform aus nicht sichtbar; Monitorserializeohnename_mappingsgibt DDIC-Großbuchstaben-Namen aus, nicht den Vertrags-JSON. Geschäftsseitig eine eigene Nachrichten-Log-Tabelle +save( )-Methode an der einzigen Stelle, wo die Nachricht entsteht (Ende vonfrm_send) – ein separater Commit schließt genau die Lücke, die das Plattform-Rollback im Log hinterlässt.
5. Dialogfenster / F4 / ALV / Selektionsbild
POPUP_GET_VALUESRETURNCODESemantik kontraintuitiv –'A'= Abbrechen; Leerzeichen = Bestätigen undFIELDSsind zurückgeschrieben (LSPO4U01:IF ok_code_save = 'CANC'. returncode = 'A'. ELSE. fill_values. returncode = space.).IF lv_rc <> 'A'behandelt „Bestätigen“ als „Abbrechen“ ⇒ Symptom: Nach Klick auf Bestätigen im Dialog passiert nichts (keine Meldung, keine Aktion, kein Dump).POPUP_GET_VALUESöffnet F4 nur, wenn das Feld an eine DDIC-Suche Hilfe hängt – Prüfung:F4IF_DETERMINE_SEARCHHELP(0=vorhanden / 2=Feld vorhanden, keine Hilfe / 1=Feld nicht vorhanden). Nicht aufSKA1-SAKNRausweichen – das fügt ALPHA-Nullen hinzu,'8000'wird zu'000008000', Prüfung schlägt fehl und Speicherformat ändert sich. ⇒ StattdessenPOPUP_GET_VALUES_USER_HELP+F4_FORMNAME+F4_PROGRAMNAME = sy-repidverwenden, Feldeigenschaften bleiben unverändert. Das exit-FORM wird vom FM mit 5USINGaufgerufen (tabname/fieldname/display_flag/returncode/value). Den ausgewählten Wert in den 5. Parameter schreiben undreturncodeauf'F'setzen – dann aktualisiert der Dialog (keinsy-stepl, daher nicht über dynprofield zurückschreiben).POPUP_GET_VALUESunterstützt kein bedingtes Ausblenden von Feldern –SVALkann nurFIELD_ATTRsteuern (Pflicht/Lesen). Typabhängig nur den passenden Feldsatz laden; bei Typwechsel des Benutzers Dialog neu öffnen (bei gleichbleibendem Typ sofort beenden, keine Endlosschleife).F4IF_INT_TABLE_VALUE_REQUESTmitFIELD_TAB– jede Zeile mussINTTYPEableitbar haben – Ableitungskette:TABNAME → INTTYPE → DATATYPE → DOMNAME → ROLLNAME.
...