Base de conocimientos de dificultades y soluciones en desarrollo ABAP
Uso: Leer antes de comenzar un nuevo proyecto; para solucionar problemas, buscar por síntoma. Este skill solo recoge experiencias «reutilizables entre proyectos». Las direcciones de entorno, números de solicitud y listas de objetos se encuentran en el resumen general del proyecto (por ejemplo, sap-abap-project-ops).
Las 120 entradas completas (con fragmentos de código y referencias) están en references/casebook.md.
Cómo usar: Nuevo proyecto → ir directamente a §11 Checklist de inicio (ejecutar en orden cronológico); proyecto ya iniciado con problemas → §10 Búsqueda rápida por síntoma para localizar la sección; para profundizar en alguna entrada con código completo y referencias → references/casebook.md.
Tres meta-reglas (más importantes que cualquier error individual):
- El texto del error a menudo no está relacionado con la causa real — El punto donde el compilador ABAP reporta el error es donde «encuentra la inconsistencia», no donde «escribiste mal». Asume primero que es un problema de orden de sentencias/orden de palabras clave/tipo no coincidente, no de sintaxis en sí.
- Lo más peligroso en SAP no son los errores, sino los fallos silenciosos — Los datos se corrompen silenciosamente bajo la triple ilusión de «retorno S, activación 200, compilación exitosa». Siempre que se trate con BAPI,
SELECT INTO TABLE,EXTENSIONIN, elementos de texto o campos de mejora, haz una conciliación con la base de datos, no confíes en el valor de retorno. - Ante un fallo, consulta ST22, no adivines — El
Error analysisde un volcado corto te da directamenteprocedure "XXX" "(FORM)"y la clase de excepciónCX_SY_*, lo que es un orden de magnitud más rápido que probar FORM por FORM.
0. Diez reglas de oro (infringirlas implica rehacer)
- Al modificar cualquier objeto existente, el primer paso es leer la versión activa de producción. Los usuarios a menudo modifican objetos de producción directamente en SE80 sin devolverlos al archivo; el archivo local solo sirve para comparación, no como línea base.
get_source→ diff con copia de seguridad para confirmar 0 diferencias → aplicar parche → leer de nuevo y diff → sobrescribir archivo en sentido inverso. - Los elementos de texto también son parte de la línea base;
get_sourceno incluye TEXTPOOL. Antes de sobrescribir el texto, debesread_textelementspara obtener el texto completo de la versión activa, hacer diff con el JSON local, fusionar manualmente y luego subirlo. De lo contrario, las modificaciones de texto que el usuario haga en el frontend de SE38 se sobrescribirán sin dejar rastro, y el diff del código fuente no mostrará ninguna diferencia. - Nunca uses UPDATE / MODIFY directamente en tablas estándar. Para modificar documentos FI ya contabilizados, usa
FI_DOCUMENT_CHANGE(grupoFACG,T_ACCHGsolo llenaFDNAME+NEWVAL, el encabezado no pasaI_BUZEI, solo las partidas individuales). - No te inventes números de solicitud. Si falta
CORR_NRal subir un objeto transportable ⇒ detente e informa, espera a que el usuario dé el número; antes de crear un nuevo objetoTADIR, primero informa de «tipo + nombre sugerido + resultado de comprobación de duplicados en TADIR + motivo para continuar la numeración», espera confirmación para crearlo. Las modificaciones posteriores a objetos existentes que ya tienen una solicitud asignada reutilizan el número original, no preguntes de nuevo. - No uses clases
Update*para hacer llamadas de prueba a interfaces. Realmente escriben — se usóUpdateFunctionModulepara pasar solo el cuerpo de la función «para probar», sobrescribiendo el código fuente inactivo con una versión truncada de 620 caracteres. Las sondas solo usan interfaces de solo lectura. - Cuatro pasos de entrega: Generar → Subir código fuente → Subir elementos de texto (
PROG/PXdeben activarse por separado) → Generar estado de GUI (CUA). Si los elementos de texto no están activos, en tiempo de ejecuciónTEXT-xxxestará siempre vacío. - Comentarios en código fuente de producción: deja el esqueleto, elimina la narrativa. Conserva el encabezano del programa (unidad/autor/fecha/resumen/registro de cambios) + líneas de marco
*&---+ pancartas de FORM + documentación de parámetros-->/<--+ comentarios cortos de criterios; elimina la justificación del diseño, descripción de criterios, resultados de pruebas conjuntas, razones de TODO, referencias ascripts/work/ADT/ZTEST_*. Después de la separación, debes hacer diff para demostrar que el código en sí no ha cambiado. - Los objetos de prueba/sonda deben eliminarse inmediatamente después de la prueba. Primero
get_sourcepara archivar enbackup/→ eliminar → verificar queTADIRse ha limpiado. Solo elimina los que creaste tú en esta ronda; los creados por otros, los que ya tienen paquete/solicitud asignados y las DEMO en uso no se tocan. - Primero verifica bloqueos, luego haz copia de seguridad, antes de escribir en SAP. Los archivos de credenciales no se transmiten a terceros, no se muestran ni se escriben en los entregables.
- Cuando el usuario dice «los datos están bien», más razón para asumir que el programa tiene un problema. Invierte el «importe del error + número de documento» para deducir las características de entrada, escribe una sonda que reproduzca el error carácter por carácter; si la reproducción es exitosa, la causa raíz está identificada.
1. Canales de activación y escritura (ADT / MCP)
- HTTP 200 ≠ activación exitosa — El éxito o fracaso está en el body: debes comprobar explícitamente
activationExecuted="false"y lanzar un error; la única forma fiable de verificar la activación es leer el include o comparar las versiones?version=activee inactive.TRDIR-RSTATno escribe valores en S/4. - El falso éxito es el más difícil de detectar — Los scripts antiguos capturaban mensajes con expresiones regulares como
<chklst:shortText>/<msg>text</msg>, pero la estructura real es prefijochkl:+<shortText><txt>+ msg con atributos ⇒ no se capturaba ni un mensaje, reportando «activación exitosa» tres veces seguidas cuando en realidad era una cáscara vacía de 131 caracteres. Si no se capturan mensajes, debe tratarse como fallo. - Los elementos de texto son objetos activables independientes
PROG/PX— Activar el programa padre no los activa automáticamente; sipush_textelementssolo tieneGET→LOCK→PUT→UNLOCKsin ACTIVATE, PUT solo escribe la versión inactiva. Al pasarPROG/P, SAP considera que no hay contenido activable y devuelveactivationExecuted="false"sin ningún mensaje ⇒ inactividad silenciosa. La interfaz de activación no aceptacorrNr(si se envía da 400). - El PUT de
push_textelementssobrescribe todo el bloque, el orden de envío no puede invertirse — Nuevos símbolos: primero elementos de texto luego código fuente; eliminación de símbolos: primero código fuente luego elementos de texto.TE_FILEdebe incluir todas las selecciones existentes completas. - Cuando los elementos de texto solo tienen versión inactiva, el programa debe regenerarse para que surtan efecto — Simplemente subir los elementos de texto + volver a PUT el mismo código fuente no los refrescará. Modifica una línea del código fuente (aunque sea un comentario) y luego envíalo de nuevo, o reconstruye el programa.
- Tres tipos de «falso bloqueo»: ① El mensaje «bloqueado en la solicitud yyy del usuario xxx» en realidad es falta de envío de
transport_request/corrNr; ②UpdateDataElementreportalocked by another userpero en realidad es un 400 «ya existe» — elisAlreadyExistsError()del handler solo reconoce inglés, el mensaje en chino no se detecta, y al caer en el caso por defecto 403 se traduce como «bloqueado» (⇒ en entornos ZH, MCP no puede modificar elementos de datos existentes;UpdateDomainno tiene esta limitación); ③403 … not specified for this clientes sociedad incorrecta. - El único bloqueo real que se puede eliminar es a través de SM12 — Los tres tipos de bloqueo
ESDICT/ESRDIRE/SEOCLSENQestán vinculados a sesiones ADT.DEQUEUE_ALL,_scope='3'yDEQUEUE_EESDICTson todos ineficaces. Después de confirmar conENQUEUE_READ, pide al usuario que los elimine manualmente en SM12 y vuelve a ejecutar inmediatamente; no esperes a que expire la sesión. - Dos mensajes 403 tienen significados completamente diferentes —
User XXX is currently editing ZFIDxxx= un usuario real tiene abierto SE80, espera;Object ... locked in task DEVK90011x not specified for this client= sociedad incorrecta, cambia a la sociedad de la solicitud y reintenta. Confundirlos te hará dar un rodeo innecesario. - El encabezado
Acceptdel LOCK de ADT debe incluir el datatype — Si solo envíasapplication/xml, SAP ya ha bloqueado con éxito pero responde 406 sinLOCK_HANDLE⇒ fuga de bloqueo; todas las solicitudes posteriores reportan «el usuario X está editando actualmente», engañándote para que creas que otro lo tiene ocupado. Valor correcto:application/vnd.sap.as+xml;dataname=com.sap.ad.lock.result. process.exit()en elfail()del script saltafinally⇒ el bloqueo de cola permanece — Tras una salida anormal de un script de tipo push, el bloqueoSEOCLSENQpuede persistir hasta 40 minutos, haciendo que todos los LOCK posteriores reporten 403 como si «el usuario tuviera SE80 abierto». Al escribir scripts, primero haz UNLOCK y luego sal, o cambia aprocess.exitCode = 1.- No elimines por el usuario los bloqueos de edición por la fuerza — Eliminar bloqueos ADT podría perder las modificaciones no guardadas de la otra parte.
- Los includes son recursos ADT independientes — Un POST a
/programs/programssiempre crea un programa ejecutable (TRDIR-SUBC=1), que no se puede cambiar después; los includes se envían a/sap/bc/adt/programs/includes(elemento raíz<include:abapInclude>). El programa principal y el include deben estar en la misma solicitud de activación, de lo contrario se reportarán mutuamente como no definidos. - El include creado por
INSERT REPORT ... STATE 'A'escribe el código fuente en versión activa — Si luego modificas la versión inactiva, la activación del programa principal aún compilará con la versión activa antigua y los errores apuntarán al código antiguo. Después de la modificación, debes ejecutarINSERT REPORT 'X' FROM lt_src STATE 'A' PROGRAM TYPE 'I'.(STATEdebe estar antes dePROGRAM TYPE) para refrescar la versión activa. - El proxy local puede colgar las solicitudes ADT — Se manifiesta exactamente igual que «no se puede conectar a SAP» (
socket hang up/ error CSRF). Antes de generar el proceso hijo del MCP, elimina*_proxyy estableceNO_PROXY=*; todas las solicitudes de escritura deben usarproxy: false. - ADT no tiene recursos de dynpro / GUI Status / Menu — Pero CUA se puede manejar mediante código (
RS_CUA_INTERNAL_FETCH+_WRITE,state='A', escribir de vuelta es efectivo).D020Ses el encabezado de la pantalla dynpro, no es la tabla de estados de GUI;RS_CUA_GET_STATUSno se puede usar para determinar, solo se reconoceRS_CUA_INTERNAL_FETCH(los 10 TABLES obligatorios deben darse completos,ORDER BY ppositionpara evitar el truncamiento de 25 líneas).
2. Restricciones sintácticas duras (compilar a la primera)
- Cuatro tabúes para parámetros FORM: ① No se puede escribir
TYPE c LENGTH n(reportaNumeric literals cannot be used as field names) ⇒ usaTYPE char<n>o tipo DDIC; ② Un parámetro de longitud fija que recibe una longitud diferente causa un error de incompatibilidad en tiempo de compilación, excepto para símbolos de texto, pero en tiempo de ejecución sigue siendo 500Type conflict when calling a FORM⇒ para longitudes variables usa el genéricoTYPE c; ③ No se puede usar inlineTYPE STANDARD TABLE OF(el parámetro se calcula como 8, reportaDifferent number of parameters) ⇒ primero nombra conTYPES; ④FIELD-SYMBOLdeclarado en un FORM es local, para usarlo en otro FORM debe ser global. FORM ... TABLESdebe escribirse antes deUSING/CHANGING— Si se escribeUSING ... CHANGING ... TABLESreportaField "TABLES" is unknown.(trata la palabra clave como nombre de parámetro). Lo mismo aplica en el lado de la llamadaPERFORM.- El orden de las secciones en
CALL FUNCTIONes obligatorio —EXPORTING → IMPORTING → CHANGING → TABLES → EXCEPTIONS. PonerTABLESantes deIMPORTINGreportaUnable to interpret "EV_OBJNR", sin que parezca un problema de orden. Los parámetrosEXPORTINGdel lado llamado deben ir en la secciónIMPORTINGdel lado llamante; si se invierte, reportaCALL_FUNCTION_PARM_UNKNOWN— muy fácil de malinterpretar como «la interfaz del FM no se generó». - Las selecciones de múltiples campos en SELECT deben separarse con comas, y las variables huésped
@deben escaparse — Reportaelements in the "SELECT LIST" list must be separated using commas. Lo mismo aplica para múltiples asignaciones enUPDATE ... SET; las constantes enWHERE(incluyendoCONSTANTS) también necesitan@. - La posición de
INTOyUP TO n ROWSdepende de lo que reporte el activador — Las sentencias con JOIN/subconsultas/expresiones SQL están en modo estricto, forzandoINTOal final (SELECT 563);UP TO n ROWSdebe ir antes deWHERE. Ambas formas se comportan de manera diferente en diferentes sentencias, copiar de memoria puede fallar. ASSIGN ... CASTING TYPE c LENGTH <variable>no está soportado — La longitud debe ser un literal; si se omiteLENGTH, en caso de índice fuera de rango se produce DUMPInvalid subfield access. La solución correcta es usarcl_abap_container_utilities=>fill_container_c( )(inversa de la función estándarREAD_CONTAINER_C).- El
TYPEdeDESCRIBE FIELDdebe escribirse antes deCOMPONENTS— Incluso si solo quieres el número de componentes, debes escribirlo:DESCRIBE FIELD cs_data TYPE lv_type COMPONENTS lv_count. CONSTANTSdebe llevarVALUE—CONSTANTS: c_x TYPE flag.reporta"VALUE ..." is not specified.MESSAGE TEXT-xxx TYPE 'S' WITH nno es válido — Los símbolos de texto no pueden usarse como texto de mensaje conWITH. Usa un FORM auxiliar:lv_text = pv_text → REPLACE '&' IN lv_text WITH lv_num → MESSAGE lv_text TYPE 'S'.- Los tipos de longitud fija no tienen el nombre
char<n>—TYPE char1000/char55/char220son todos ilegales, reportanType "X" is unknown; usa siempreTYPE c LENGTH n. Adivinar el nombre del tipo = perder una ronda de activación. Si no estás seguro, consultaDD04Loget_source. class=>methodsolo se puede usar para métodos estáticos — Para métodos de instancia debes usarCREATE OBJECT lo. lo->method( ), de lo contrario reportacan only be specified with static methods.- Las plantillas de cadenas no pueden ser argumentos de
PERFORM ... USING— ReportaField "|" is unknown; primero asigna a una variable y luego pasa. CALL TRANSACTION ... WITH f1 v1no es una sintaxis válida —WITHsolo se usa conAUTHORITY-CHECK. Para pasar parámetros solo se puede usarUSING bdcdata; la primera pantalla de SM30 solo tiene un campo con el nombre de la tabla (VIMDYNFLDSno tiene campo de modo de actualización), para entrar en modo de modificación usaVIEW_MAINTENANCE_CALL+action = 'U'.REPLACE ALL OCCURRENCES OF ... WITH ''en este sistema de tipos puede causar un bucle infinito — Para eliminar espacios/caracteres, cambia a filtrado carácter por carácter conDO strlen TIMES+lv_txt+lv_i(1).CONCATENATEno acepta tipos numéricos —lv_cnt TYPE ireporta must be character-like; las funciones integradas (strlen/lines) no pueden usarse directamente en&&(reportaThe syntax for a method specification is objref->method, un error engañoso sobre sintaxis de flecha). Los valores numéricos siempre deben usar plantillas de cadenas|...{ lv_cnt }...|.- Dentro de una sonda
$TMPno se pueden definir FORM; los comentarios fuera del cuerpo de un método no se guardan — La activación de la sonda reportaStatement is not accessible(CheckProgramno reporta esto); los comentarios entreENDMETHODy el siguienteMETHODen una clase reportanunknown comments which can't be stored. - Los nombres de
SELECT-OPTIONSdeben tener ≤ 8 caracteres — 9 caracteres son rechazados en la primera activación. Al acortar el nombre, debes cambiar sincrónicamente el nombre de la entradaselectionsen los elementos de texto (las variantes antiguas almacenan el nombre antiguo y se ignoran silenciosamente, solo se muestra un aviso no un error).
3. Lista de fallos silenciosos (la sección más cara)
Si el resultado es «retorno S / activación 200 / compilación exitosa» pero el resultado es incorrecto, primero ven aquí a comparar.
3.1 Desplazamiento y pérdida de datos
SELECTcon lista explícita de columnasINTO TABLE <tabla completa predeclarada>asigna por posición — Si los tipos son compatibles, no reporta error ni advertencia:DOMVALdeDD07Tcae enDOMNAME,WKGBTRdeCOEPcae enMEGBTR(importe siempre vacío),ZTEPIC3Areportacomponent "ZJK" not compatible with "ZJYRQ"(se obtuvo la coordenada con_act_dbg). Un mismo error recurrió en 5 informes. Tres soluciones:SELECT */INTO TABLE @DATA(...)inline (la estructura se define por la lista) / crear una estructura estrecha con el mismo orden de columnas; oINTO CORRESPONDING FIELDS OF TABLE.- Usar campos de la tabla derecha de un
LEFT JOINenWHERE⇒ semánticamente se degrada aINNER JOIN— Cuando la tabla derecha no tiene coincidencias, los campos son NULL y la condición no se cumple. Mueve los filtros aON. DELETE ADJACENT DUPLICATES ... COMPARING <campo no clave>puede eliminar datos de más — La tabla condicional A017 tiene clave principal que incluyeDATBI,KNUMHno es clave; al eliminar duplicadosCOMPARING knumh, se eliminan todos los períodos de validez posteriores con el mismoKNUMH. Primero hazSORT BYcon la clave completa y luegoCOMPARINGcon la clave completa.AT NEW/AT END OFagrupa por adyacencia física; después de la modificación ALV, falla —REUSE_ALV_*reordenat_outtaben el lugar (si el usuario ordena por una columna de mensaje, se dispara), los mismos grupos ya no son adyacentes yAT END OFse dispara varias veces. El agrupamiento debe cambiarse a «bucle externo sobre claves de agrupación × bucle interno recolectando por clave», los límites del grupo solo reconocen la clave de negocio.APPENDmodificasy-tabix— Después deAPPEND INITIAL LINE ... ASSIGNING <fs>, obtenersy-tabixda el número de segmento, no el número de línea original ⇒ el rango se amplía, 128 líneas se convierten en 558 (el mismo registro se repite 43 veces). Al inicio del bucle, primero guardalv_tabix = sy-tabix..READ TABLE ... WITH KEYque no encuentra clave ponesy-tabixa 0 — LuegoAPPEND sy-tabix TO lt_dello agrega a la lista de eliminación, yDELETE ... INDEX 0produce DUMPTABLE_INVALID_INDEX. Guarda el número de línea al inicio del bucle y antes de eliminar, compruebaIF idx >= 1 AND idx <= lines( ).- Una declaración inline
DATA(...)dentro de una ramaIFque no se ejecuta significa que la tabla interna nunca se crea ⇒ elLOOPposterior produce un volcado corto. Mueve la declaración a la cabecera del método, o antes de la obtención de datos, haz una comprobación de tabla vacía.
3.2 BAPI / funciones estándar «retornan S o no reportan error pero no escriben»
- La estructura
xde un BAPI debe enviarse emparejada con la estructura original — Si solo envíasdepreciationareassindepreciationareasx, al rellenar AFASL reportaNo change parameter given for depreciation area 01. La falta de la estructuraxno produce error de sintaxis, solo se dispara cuando se cumplen las condiciones de datos. - El elemento de datos de la estructura de identificación de campos de mejora SD debe ser
CHAR1—SD_SALESDOCUMENT_CHANGEusaGET_CHANGEABLE_FIELDS, solo examina los campos conWHERE rollname = 'CHAR1' OR 'CHAR01'; los campos conBAPIUPDATEson todos excluidos ⇒ retorna S, el valor no cambia, cero errores, pero la ruta de creación no usa esta lista ⇒ típico «crear puede escribir, modificar no puede». No se puede cambiar de nuevo aBAPIUPDATE. BAPIPAREX-VALUEPART1tiene solo 240 caracteres — Las funciones estándar leenVALUEPART1~4secuencialmente por desplazamiento (total 960), si solo se llena el primer segmento, el final se trunca silenciosamente (BAPE_VBAPprobado con 271 caracteres, los últimos 3 campos quedan vacíos, sin error). Divide en cuatro segmentos de 240 caracteres.- Tipos de pedido de abono/cargo sin planificación: la cantidad solo reconoce
TARGET_QTY— EnBAPISDITM, ningún campo tiene como referenciaKWMENG; esta categoría de posición no tiene configuración enTVEPZ⇒ no se generan planificaciones, elBAPISCHDLenviado se descarta silenciosamente, retorna S peroKWMENG=0,NETWR=0,VBEP0 líneas. El criterio de diagnóstico es siTVEPZtiene entrada, no se puede usarTVAP-LFREL(tantoZ001comoZ014tienenLFRELvacío), niVBTYP. Misma trampa en lectura:VBAP-KWMENGsiempre es 0, la cantidad está enVBAP-ZMENG⇒ criterio de relleno «priorizarKWMENGno vacío, si está vacío usarZMENG». El primer campo deBAPE_*es la clave; si se omite en modo modificación, se borra el número de documento — La función estándar hacemove-correspondingdel valorEXTENSIONINaE_VBAKKOM, siVBELNse deja vacío, sobrescribe el número de documento a vacío ⇒ V1 045 seguido de V4 219 (la causa raíz está en el mensaje E anterior al V4 219). - Los documentos de referencia SD son un trío —
REF_DOC+REF_DOC_IT+REF_DOC_CA(categoría de documento de referencia); si solo envías los dos primeros, reportaV2 068 sin categoría de documento pero contiene C(&2vacío). Si el número de línea es 0 o el documento no existe, es el mismo mensaje pero&3está vacío. La categoría se toma del propio documento de referenciaVBAK-VBTYP, no del encabezadoVGTYP. SD_SALESDOCUMENT_CHANGEordena las dos tablas de entrada por separado — Las líneas que se eliminan deben tener tantoITEM_INcomoITEM_INXconITM_NUMBER; si solo se llenaITEM_INX, se desalinea y reporta un error completamente no relacionadoT184 no hay categoría de posición disponible.BAPI_USER_EXISTENCE_CHECKen este sistema devuelve 0 para cualquier cuenta (inclusoZZZZZZ999pasa) ⇒ la comprobación es simbólica, los documentos transferidos a cuentas inválidas no son visibles en ninguna de las dos interfaces. Consulta directamenteUSR02+ protección de longitud (0 < len <= 12).DELETE_TEXTsobre un texto inexistenteRAISING NOT_FOUND— La función estándar agrega este mensaje E aBAPIRET2, yfrm_check_returnlo trata como fallo ⇒ «vaciar» se convierte en error E. Antes de vaciar, consultaSTXH(TDOBJECT+TDNAME+TDID+TDSPRAS), si no existe, salta (esto también hace que la operación sea idempotente). Además: enSD_SALES_DOCU_MAINTAIN, el código de función para vaciar es003(DELETE_TEXT); pasar&&/005solo reescribe el texto como una línea en blanco.- Al añadir una nueva condición de pricing, no se debe especificar el número de paso — Si se codifica
COND_ST_NO='001'peroZP01está enSTUNR=010, reporta «tipo de pricing no está en el número de paso 001». Déjalo vacío, que la secuencia de pricing lo localice automáticamente; usa «si la condición ya existe» para controlarUPDATEFLAGI/U. - Al redividir una interfaz,
OBJ_KEYsolo devuelve un número de documento ⇒ el número de documentos se subestima, y las líneas escritas señalan al documento incorrecto. El diseño deOBJ_KEYesnúmero de documento(10)+sociedad(4)+año fiscal(4), el año está en+14(4)(a menudo mal escrito como+10(4)tomando la sociedad). BSEG-BUZEIesNUMC(3), límite físico de 999 líneas por documento, el BAPI solo admite 990 líneas — El límite de fragmentación es989 detalles + 1 compensación; el límite debe cubrir 999/1000/1997 (1997 juzgado como>999se dividiría en 1000 líneas, causando desbordamiento).BAPIACHE09no tiene el campoEXCH_RATE— En documentos bimoneda (líneas 00+10), si solo la línea 00 lleva tipo de cambio, el sistema recalcula el tipo de cambio del encabezado según OB08 (poner 7 da 8.28). La línea conCURR_TYPE='10'también debe llevarexch_rate, tanto en la línea de detalle como en la línea de compensación.- En un documento bimoneda, cada
ITEMNO_ACCdebe tener dos entradasCURRENCYAMOUNT, y el subtotal de cada moneda debe ser 0 — Si falta la línea10, reportaInconsistent currency information; la línea de compensación generada en la división también debe completarse. UMSKZno tiene campo en ninguna de las tres estructuras BAPI — Las clases de efecto (W/S) que pasan por el canal BAPI estándar se descartan silenciosamente y la cuenta se registra incorrectamente. Solo se puede grabar a través de la mejoraEXTENSION2, o usar todo el procesoPOSTING_INTERFACE_*(entrada por lote FB01).- El parámetro
NAMEdeREAD_TEXTesTDOBNAME(70), el argumento realTDNAME(16) provoca un conflicto de tipos en tiempo de ejecución — Compila exitosamente, funciona con parámetros vacíos, pero al encontrar datos produce 500Type conflict during a function module call. Declara el parámetro comoTYPE tdobname, o lee directamente la tabla clusterSTXL.
3.3 Texto / descripción / título de columna vacío
- Un campo con tipo anónimo incorporado ⇒
DD03Ttiene texto vacío ⇒ los títulos de columna en ALV están en blanco — El campo debe tener un elemento de datos (al asignarlo, queDD03Ttenga 0 líneas es correcto, el texto se mueve hacia arriba). El texto de los campos de una estructura solo se puede definir mediante DDL, campo por campo, con@EndUserText.label; los campos de tipoSTRU/TTYPanidados nunca tienen textoDD03T(los estándares de SAP también son así). Elfields[].descriptiondeCreateStructureno se guarda enDD03T. - Elementos de texto inactivos / solo con versión inactiva ⇒ en tiempo de ejecución,
TEXT-xxxsiempre está vacío — Se manifiesta como títulos de columna ALV que retroceden al texto corto DDIC, y columnas de estado vacías. - Dominio que define tipo pero no mantiene valores ⇒ la pantalla de selección no puede desplegar F4 — Al consultar
DD07T, debe incluirAS4LOCAL='A'(de lo contrario se obtienen líneas inactivas),ORDER BY VALPOS(de lo contrario se obtiene orden lexicográfico1,10,11,2,3…), y el idioma debe retroceder al idioma maestro del dominioDD01L-DOMMASTER(el nombre del campo no esMASTERLANG). MESSAGE ... WITHtiene un límite de aproximadamente 70 caracteres para el texto completo ⇒ Los errores largos de BAPI se truncan silenciosamente aField Reas..., desviando la investigación. Usa un prefijo corto + plantilla de cadena con el texto original (≤220 caracteres, alineado conBAPI_MSG).BAPIACGL09-ITEM_TEXT(50) /BAPIACHE09-HEADER_TXT(25) se truncan a la derecha silenciosamente — Al concatenar «prefijo+sufijo», primero trunca el prefijo alongitud - longitud del sufijo.- Los nombres de componentes DDIC se truncan silenciosamente a 30 caracteres — Al mapear nombres de clave JSON, algunas claves pueden desaparecer inexplicablemente.
3.4 Autorizaciones y filtros
AUTHORITY-CHECKcon valor vacío generalmente resulta en «sin autorización» ⇒ los registros relacionados son permanentemente invisibles — El rol no suele concederEKGRP = space. Cuando el campo de nivel organizativo está vacío, no se realiza la comprobación, se permite el paso directamente:IF lv_ekgrp IS NOT INITIAL. AUTHORITY-CHECK ... ENDIF.. Usa filtros a nivel de línea en lugar de rechazar toda la pantalla; de lo contrario, los usuarios sin autorización ni siquiera pueden entrar al código de transacción.MODIF IDsolo controla visibilidad/ocultación; los valores ya rellenados en campos ocultos siguen participando en el filtro — En escenarios de ocultación, usa RANGE para bloquear explícitamente en el lado de obtención de datos.
4. Transacciones y LUW
EXPORT ... TO DATABASE(log cluster) está controlado por la transacción — «Tabla subyacente → crear documento → fallo → rollback» hace que el log y la tabla subyacente estén en el mismo LUW;ROLLBACKtambién se lleva la instantánea de parámetros de entradaindx=01(indx=02permanece). Además, el FM de contabilización subyacente internamente haceBAPI_TRANSACTION_ROLLBACK, por lo que «eliminar tu propio ROLLBACK» o «cambiar el orden» no funciona. ⇒ Solución: Antes de la única acción de escritura en base de datos y antes de llamar al BAPI, inserta una sentencia independienteCOMMIT WORK, asíROLLBACKconserva el log. Beneficio adicional: Hacer COMMIT del propio LUW antes de llamar al BAPI es una práctica estándar.- Si necesitas atomicidad, no hagas COMMIT por separado; si necesitas COMMIT independiente, mantén la relectura —
SD_SALESDOCUMENT_CHANGEconCALL_FROM_BAPI='X'encola la actualización en la cola de actualización; sin COMMIT no se puede consultar ⇒ eliminarCOMMIT WORK AND WAITy mantener la verificación de relectura SELECT necesariamente juzgará erróneamente que «la bandera no se actualizó». En la rama de fallo de contabilización, debes añadirROLLBACK WORK, de lo contrario la actualización SD pendiente quedará en el aire y se grabará incorrectamente en el próximo COMMIT. - La instantánea de la plataforma solo captura los parámetros del FM; las variables locales dentro de la función no se capturan — Los mensajes en
lv_req/lv_respno se ven en la plataforma; elserializedel monitor no incluyename_mappings, por lo que genera los nombres DDIC en mayúsculas en lugar de los nombres del contrato XML. El lado del negocio debe crear su propia tabla de log de mensajes y una línea desave( )escrita en el único lugar donde se genera el mensaje (al final defrm_send), y un COMMIT independiente precisamente compensa la debilidad de la plataforma de que el log se pierde con el rollback.
5. Cuadros de diálogo / F4 / ALV / Pantalla de selección
- La semántica de
RETURNCODEenPOPUP_GET_VALUESes contraintuitiva —'A'= Cancelar; espacio = Confirmar yFIELDSse ha rellenado (LSPO4U01:IF ok_code_save = 'CANC'. returncode = 'A'. ELSE. fill_values. returncode = space.). Si escribesIF lv_rc <> 'A', tratarás «Confirmar» como «Cancelar» ⇒ el síntoma es hacer clic en Confirmar en la ventana emergente y no ocurre nada (sin mensaje, sin acción, sin volcado). POPUP_GET_VALUESsolo muestra F4 cuando el campo tiene una ayuda de búsqueda DDIC vinculada — El criterio esF4IF_DETERMINE_SEARCHHELP(0=tiene / 2=el campo existe pero no tiene ayuda / 1=el campo no existe). No intentes tomar prestada la ayuda cambiando aSKA1-SAKNR— introducirá el complemento ALPHA, convirtiendo'8000'en'000008000', la validación reportará errores y el formato de almacenamiento cambiará. ⇒ En su lugar, usaPOPUP_GET_VALUES_USER_HELP+F4_FORMNAME+F4_PROGRAMNAME = sy-repid, sin cambiar los atributos del campo. El FORM de salida es llamado por el FM con 5 parámetrosUSING(tabname/fieldname/display_flag/returncode/value), debes escribir el valor seleccionado en el quinto parámetro y establecerreturncodeen'F'para que la ventana emergente se actualice (no puedes obtenersy-stepl, no uses dynprofield para devolver).POPUP_GET_VALUESno soporta ocultar campos por condición —SVALsolo puede controlarFIELD_ATTR(obligatorio/solo lectura). Carga solo el conjunto de campos correspondiente al tipo; si el usuario cambia de tipo, vuelve a mostrar la ventana según el nuevo tipo (si el tipo no cambia, sale, no hay bucle).- Al pasar
FIELD_TABaF4IF_INT_TABLE_VALUE_REQUEST, cada línea debe poder deducirINTTYPE— La cadena de deducción esTABNAME → INTTYPE → DATATYPE → DOMNAME → ROLLNAME. Si la deducción falla, el F4 no muestra datos. La solución es pre-llenarINTTYPEmanualmente enFIELD_TAB. - Los eventos ALV (
data_changed/user_command/hotspot_click) no se ejecutan si el layout de ALV no se ha creado correctamente — En particular, si faltaSET PF-STATUSoSET TITLEBAR, los eventos de interacción del usuario no se reciben. Asegúrate de que el estado de GUI esté configurado y que el layout esté correctamente inicializado. REUSE_ALV_GRID_DISPLAYmodificat_outtaben el lugar — Cualquier filtro, ordenación o modificación del usuario reordena la tabla de salida. Si necesitas la tabla original después de mostrar ALV, haz una copia de seguridad antes de la llamada.
6. Interfaces JSON y Gateway
- La validación de JSON en ABAP no es robusta por defecto —
cl_tcode_json=>deserializeno lanza errores para JSON mal formado; devuelve datos iniciales o vacíos. Siempre debes validar el JSON primero concl_tcode_json=>validateo una biblioteca externa. - El orden de los campos en JSON no está garantizado — Al serializar/deserializar, el orden de los campos en la estructura ABAP puede no coincidir con el orden en JSON. Usa
NAME_MAPPINGSpara controlar la correspondencia. - Los nombres de campo ABAP (mayúsculas) no coinciden con los nombres de campo JSON (minúsculas/camelCase) — Sap debe usar
NAME_MAPPINGSpara mapear nombres. Sin mapeo, la serialización produce nombres DDIC en mayúsculas. - Las cadenas vacías en JSON se convierten en espacios en ABAP — Un campo JSON vacío (
"") se convierte en un espacio en ABAP (' '). Si el negocio espera una cadena vacía (''), debes convertir manualmente los espacios a vacío después de la deserialización. - Las fechas en JSON (formato ISO
YYYY-MM-DD) no se convierten automáticamente a formato SAP (YYYYMMDD) — Debes hacer la conversión manualmente. - Los campos numéricos en JSON con decimales pueden redondearse incorrectamente al convertirlos a tipos
PoIde ABAP — La precisión y el escalado deben coincidir; de lo contrario, puede haber pérdida de datos. /IWBEP/IF_MGW_*interfaces de Gateway requieren que los parámetros de entrada se pasen en estructuras específicas — No todas las llamadas de función aceptan parámetros de tabla; algunas esperan estructuras anidadas.
7. Creación de objetos DDIC
CreateStructureno estableceDD03T— Los textos de campo de la estructura no se escriben. Después de crear la estructura, debes leerDD03Ty escribir los textos manualmente a través deDDUT_DD03T_MAINTAINo similar.CreateDataElement/CreateDomainrequieren que el dominio y el elemento de datos se activen por separado — Los objetos DDIC no se activan implícitamente al crearlos. Debes llamar explícitamente a la activación.- Los nombres de dominio y elemento de datos deben ser únicos globalmente — Si el nombre ya existe (aunque esté inactivo), la operación falla. Consulta primero
DD01L/DD04LconAS4LOCAL='A'. - Al modificar un dominio existente, debes considerar todas las referencias — Cambiar el tipo de datos o la longitud de un dominio puede afectar a todos los elementos de datos y campos de tabla que lo usan. El sistema puede no permitir la modificación si hay dependencias activas.
- Los catálogos de búsqueda (search helps) se crean y activan por separado — No están vinculados a la estructura o tabla, se asignan mediante
DD35Vo en el mantenimiento de la tabla.
8. ST22 y técnicas de diagnóstico con sondas
- ST22 es el primer paso ante cualquier volcado corto — El
Error analysiscontiene la ubicación exacta del error (Program,Include,Line) y el tipo de excepción (CX_SY_*), mucho más rápido que revisar FORM por FORM. - Usa sondas de solo lectura para aislar el problema — Crea un programa
$TMPo Z que llame a las funciones sospechosas con parámetros fijos y verifique los resultados. No modifiques el programa original hasta que la causa raíz esté clara. - El log de la aplicación (SLG0/SLG1) puede estar desactivado — Si tu función usa
BAL_LOG_CREATEpero no hay logs, verifica la activación del objeto de log. - Los puntos de interrupción (breakpoints) en programas de producción pueden ser eliminados por el sistema — En sistemas productivos, los puntos de interrupción externos pueden ser rechazados. Usa
BREAK-POINTsolo en sistemas de desarrollo/calidad. - La transacción SAT (Análisis de tiempo de ejecución) es mejor que SE30 — SAT proporciona más detalles sobre el rendimiento, incluyendo el tiempo de base de datos y el tiempo de aplicación.
9. Trampas en scripts de parches
- Los scripts que modifican código fuente en producción deben ejecutarse en modo de prueba primero — Usa la funcionalidad de «simulación» o «test run» si está disponible; de lo contrario, ejecuta el script en un sistema de calidad primero.
- Los scripts que eliminan objetos (
DELETE TRANSPORT) deben verificar primero que el objeto no esté en uso — Eliminar un objeto que todavía es referenciado por otro puede causar errores en tiempo de ejecución. - Los scripts que mueven objetos entre paquetes deben verificar los permisos — Mover un objeto a un paquete diferente puede requerir autorizaciones especiales.
- Los scripts de parche que modifican elementos de texto deben primero leer la versión activa — Como se mencionó en la regla de oro #2,
get_sourceno incluye TEXTPOOL; debes leer los elementos de texto activos antes de sobrescribir. - Los scripts que ejecutan
RS_CUA_INTERNAL_FETCH/_WRITEdeben manejar correctamente las excepciones — El estado'A'(activo) es crítico; si el objeto GUI no está activo, la escritura falla silenciosamente.
10. Búsqueda rápida por síntoma
| Síntoma | Sección a consultar |
|---|---|
| «Compila pero la activación falla» | §1 (ADT/MCP) y §2 (Sintaxis) |
| «Activación exitosa pero el programa se comporta incorrectamente» | §1 (Falsa activación) y §3 (Fallo silencioso) |
| «Retorna S pero los datos no cambian» | §3.2 (BAPI) y §3.4 (Autorizaciones) |
| «El texto del error no coincide con la causa real» | §0 (Meta-regla 1) y §2 (Sintaxis) |
| «Botón no responde / F4 no se abre» | §5 (Diálogo/F4) |
| «Datos desplazados / perdidos» | §3.1 (Desplazamiento) |
| «ST22: Excepción CX_SY_*» | §8 (ST22) |
| «El script de parche no funciona» | §9 (Scripts) |
| «Problemas con BAPI SD» | §3.2 (BAPI SD) y §4 (Transacciones) |
| «Problemas de interfaz JSON» | §6 (JSON/Gateway) |
11. Checklist de inicio de proyecto
Fase 1: Evaluación (Antes de empezar)
- Leer la sección 0 (Diez reglas de oro) de este skill.
- Revisar el resumen general del proyecto (
sap-abap-project-ops) para entornos, solicitudes y objetos. - Configurar el acceso a los sistemas (desarrollo, calidad, producción).
- Verificar que las autorizaciones necesarias están concedidas.
Fase 2: Pre-implementación (Antes de escribir código)
- Identificar los objetos existentes que se modificarán.
get_sourcede cada uno y guardar copia enbackup/. - Verificar bloqueos en los objetos que se van a modificar (SM12).
- Leer los elementos de texto activos (
read_textelements) y guardar copia. - Confirmar los números de solicitud con el usuario.
Fase 3: Autocomprobación de codificación
- Seguir las reglas de sintaxis de §2 para evitar errores de compilación.
- Para BAPI/funciones estándar, revisar §3.2 para evitar fallos silenciosos.
- Después de modificar un objeto, activar y verificar con
?version=activee inactive. - Realizar una conciliación con la base de datos después de operaciones de escritura (SELECT de verificación).
Fase 4: Entrega
- Generar el objeto.
- Subir código fuente.
- Subir y activar elementos de texto (
PROG/PXpor separado). - Generar estado GUI (CUA) si es necesario.
- Verificar que el objeto funciona en el sistema de calidad.
Fase 5: Cierre
- Eliminar objetos de prueba/sonda (después de
get_sourcey respaldo). - Verificar que
TADIRestá limpio. - Actualizar la documentación del proyecto.
- Comunicar al usuario que la entrega está completa.
Este skill se actualiza continuamente a partir de la experiencia en proyectos reales. Las nuevas entradas se añaden a references/casebook.md y se resumen aquí.