Consulta masiva de características de materiales en SAP y generación dinámica de reportes ABAP

En entornos productivos donde se gestionan volúmenes considerables de materiales, los departamentos de calidad o logística suelen solicitar reportes que combinan datos maestros con atributos clasificatorios específicos: por ejemplo, el lote de proveedor, la certificación ambiental o la vida útil de un conjunto de productos. La consulta individual a través de transacciones estándar como CL20N o MM03 resulta inviable cuando se trata de cientos o miles de materiales, además de que los nombres de las características pueden variar entre clases de clasificación.

El sistema de clasificación de SAP almacena estos atributos como pares clave-valor en la tabla AUSP, vinculados a los objetos mediante INOB, y con sus metadatos en CABN y CABNT. Aunque potente, esta arquitectura no está pensada para consultas transversales masivas. Por ello, desarrollar un programa ABAP personalizado que extraiga, transforme y presente estos datos de forma dinámica se convierte en una solución de gran valor.

Arquitectura de la solución: del modelo EAV a una estructura tabular

Los datos de características en SAP siguen un patrón similar a Entity-Attribute-Value (EAV): cada registro en AUSP representa una combinación objeto-característica-valor. El desafío consiste en pivotar esa estructura vertical a una representación horizontal donde cada característica se convierta en una columna.

El procesamiento se divide en tres fases:

  1. Extracción masiva: recuperar todos los pares característica-valor asociados a los materiales seleccionados, consultando INOB, AUSP y CABN.
  2. Enriquecimiento: vincular con datos maestros de MARA y MAKT para incorporar descripciones, unidades y dimensiones.
  3. Construcción dinámica del ALV: determinar en tiempo de ejecución qué características están presentes en el resultado y generar una tabla interna con estructura variable, utilizando cl_alv_table_create=>create_dynamic_table o la API de RTTS.

La técnica de tabla interna dinámica es el núcleo de la solución, ya que permite que el reporte se adapte automáticamente a las características encontradas en cada ejecución.

Definición de tipos y estructuras de datos

Comenzamos declarando los tipos que utilizaremos como contenedores intermedios durante el procesamiento. Se han renombrado y reorganizado respecto a un enfoque convencional para mejorar la legibilidad:

*&---------------------------------------------------------------------*
*& Declaración de TYPES
*&---------------------------------------------------------------------*
TYPES: BEGIN OF t_matkey,
         matnr TYPE mara-matnr,
       END OF t_matkey.

TYPES: BEGIN OF t_chardef,
         internal_id TYPE cabn-atinn,
         tech_name   TYPE cabn-atnam,
         label_text  TYPE cabnt-atbez,
       END OF t_chardef.

TYPES: BEGIN OF t_rawval,
         obj_key    TYPE ausp-objek,
         char_id    TYPE cabn-atinn,
         char_name  TYPE cabn-atnam,
         char_text  TYPE ausp-atwrt,
         char_num   TYPE ausp-atflv,
       END OF t_rawval.

TYPES: BEGIN OF t_enriched_val,
         obj_key    TYPE ausp-objek,
         char_name  TYPE cabn-atnam,
         char_text  TYPE ausp-atwrt,
         size_text  TYPE mara-groes,
         norm_code  TYPE mara-normt,
         mat_desc   TYPE makt-maktx,
       END OF t_enriched_val.

TYPES: BEGIN OF t_codedesc_ref,
         char_id    TYPE cawnt-atinn,
         code_grp   TYPE cawnt-atwtb,
       END OF t_codedesc_ref.

Captura de datos: selección desde tablas de clasificación

La primera etapa del bloque de procesamiento consiste en leer los materiales dentro del rango ingresado y luego recuperar todos los valores de características asociados. Se utiliza INOB como puente entre el número de material y el identificador interno de objeto que utiliza AUSP.

*&---------------------------------------------------------------------*
*& Recuperación de datos de clasificación
*&---------------------------------------------------------------------*
DATA: r_matnr   TYPE RANGE OF mara-matnr,
      lt_rawval TYPE TABLE OF t_rawval,
      ls_rawval TYPE t_rawval.

SELECT objek, atinn, atwrt, atflv
  FROM ausp
  INTO TABLE @DATA(lt_ausp_raw)
  FOR ALL ENTRIES IN @lt_materials
  WHERE objek = @lt_materials-objek
    AND klart = '001'.

IF sy-subrc = 0.
  SELECT atinn, atnam
    FROM cabn
    INTO TABLE @DATA(lt_cabn_map)
    FOR ALL ENTRIES IN @lt_ausp_raw
    WHERE atinn = @lt_ausp_raw-atinn.

  LOOP AT lt_ausp_raw INTO DATA(ls_ausp).
    ls_rawval-obj_key   = ls_ausp-objek.
    ls_rawval-char_id   = ls_ausp-atinn.
    ls_rawval-char_text = ls_ausp-atwrt.
    ls_rawval-char_num  = ls_ausp-atflv.

    READ TABLE lt_cabn_map INTO DATA(ls_cabn)
      WITH KEY atinn = ls_ausp-atinn.
    IF sy-subrc = 0.
      ls_rawval-char_name = ls_cabn-atnam.
    ENDIF.

    APPEND ls_rawval TO lt_rawval.
  ENDLOOP.
ENDIF.

Enriquecimiento con datos maestros

Una vez disponibles los valores crudos, se incorporan los textos descriptivos del material y atributos relevantes de MARA. Esto permite que el reporte final incluya no solo las características clasificatorias sino también información de contexto:

*&---------------------------------------------------------------------*
*& Unión con datos maestros de material
*&---------------------------------------------------------------------*
DATA: lt_enriched TYPE TABLE OF t_enriched_val,
      ls_enriched TYPE t_enriched_val.

SELECT m~matnr, m~groes, m~normt, t~maktx
  FROM mara AS m
  INNER JOIN makt AS t
    ON m~matnr = t~matnr
  FOR ALL ENTRIES IN @lt_rawval
  WHERE m~matnr = @lt_rawval-obj_key
    AND t~spras = @sy-langu
  INTO TABLE @DATA(lt_mara_join).

LOOP AT lt_rawval INTO ls_rawval.
  ls_enriched-obj_key   = ls_rawval-obj_key.
  ls_enriched-char_name = ls_rawval-char_name.
  ls_enriched-char_text = ls_rawval-char_text.

  READ TABLE lt_mara_join INTO DATA(ls_mara)
    WITH KEY matnr = ls_rawval-obj_key.
  IF sy-subrc = 0.
    ls_enriched-size_text = ls_mara-groes.
    ls_enriched-norm_code = ls_mara-normt.
    ls_enriched-mat_desc  = ls_mara-maktx.
  ENDIF.

  APPEND ls_enriched TO lt_enriched.
ENDLOOP.

Construcción de la tabla dinámica con RTTS

Para generar una tabla interna cuya estructura depende de las características encontradas en teimpo de ejecución, empleamos el servicio de tipos de ejecución de ABAP (Run Time Type Services). El proceso consiste en:

  1. Extraer la lista única de características presentes en lt_enriched.
  2. Construir un descriptor de tabla (cl_abap_tabledescr) a partir de componentes de tipo cl_abap_elemdescr.
  3. Crear la referencia de datos y asignarla a un field-symbol.
  4. Popular cada fila recorriendo los materiales y buscando sus valores por característica.
*&---------------------------------------------------------------------*
*& Generación dinámica de estructura de tabla
*&---------------------------------------------------------------------*
DATA: lt_components TYPE cl_abap_structdescr=>component_table,
      ls_component  LIKE LINE OF lt_components.

* Componente fijo: número de material
ls_component-name = 'MATNR'.
ls_component-type = cl_abap_elemdescr=>get_c( 18 ).
APPEND ls_component TO lt_components.

* Componente fijo: descripción
ls_component-name = 'MAKTX'.
ls_component-type = cl_abap_elemdescr=>get_c( 40 ).
APPEND ls_component TO lt_components.

* Componentes dinámicos: una columna por característica encontrada
DATA(lt_distinct_chars) = lt_enriched.
SORT lt_distinct_chars BY char_name.
DELETE ADJACENT DUPLICATES FROM lt_distinct_chars COMPARING char_name.

LOOP AT lt_distinct_chars INTO DATA(ls_distinct).
  ls_component-name = ls_distinct-char_name.
  ls_component-type = cl_abap_elemdescr=>get_c( 50 ).
  APPEND ls_component TO lt_components.
ENDLOOP.

* Crear descriptor y tabla dinámica
DATA(lo_structdescr) = cl_abap_structdescr=>create( lt_components ).
DATA(lo_tabledescr)  = cl_abap_tabledescr=>create( lo_structdescr ).

DATA: dref_table TYPE REF TO data,
      dref_row   TYPE REF TO data.

CREATE DATA dref_table TYPE HANDLE lo_tabledescr.
CREATE DATA dref_row   TYPE HANDLE lo_structdescr.

FIELD-SYMBOLS: <ft_dyntab> TYPE STANDARD TABLE,
               <fs_dynrow> TYPE any.

ASSIGN dref_table->* TO <ft_dyntab>.
ASSIGN dref_row->*   TO <fs_dynrow>.

Poblado de la tabla dinámica

Con la tabla dinámica disponible, iteramos sobre los materiales únicos y completamos cada columna buscando el valor correspondiente en lt_enriched:

*&---------------------------------------------------------------------*
*& Llenado de tabla dinámica
*&---------------------------------------------------------------------*
DATA(lt_unique_mats) = lt_enriched.
SORT lt_unique_mats BY obj_key.
DELETE ADJACENT DUPLICATES FROM lt_unique_mats COMPARING obj_key.

LOOP AT lt_unique_mats INTO DATA(ls_mat).
  CLEAR <fs_dynrow>.

  ASSIGN COMPONENT 'MATNR' OF STRUCTURE <fs_dynrow> TO FIELD-SYMBOL(<fv_matnr>).
  <fv_matnr> = ls_mat-obj_key.

  ASSIGN COMPONENT 'MAKTX' OF STRUCTURE <fs_dynrow> TO FIELD-SYMBOL(<fv_maktx>).
  <fv_maktx> = ls_mat-mat_desc.

  LOOP AT lt_enriched INTO DATA(ls_val)
    WHERE obj_key = ls_mat-obj_key.
    ASSIGN COMPONENT ls_val-char_name OF STRUCTURE <fs_dynrow>
      TO FIELD-SYMBOL(<fv_char>).
    IF sy-subrc = 0.
      <fv_char> = ls_val-char_text.
    ENDIF.
  ENDLOOP.

  APPEND <fs_dynrow> TO <ft_dyntab>.
ENDLOOP.

Visualización mediante ALV dinámico

Finalmente, se presenta el resultado utilizando cl_salv_table, que maneja de forma transparente estructuras dinámicas:

*&---------------------------------------------------------------------*
*& Visualización ALV
*&---------------------------------------------------------------------*
TRY.
    cl_salv_table=>factory(
      IMPORTING r_salv_table = DATA(lo_alv)
      CHANGING  t_table      = <ft_dyntab> ).

    lo_alv->display( ).
  CATCH cx_salv_msg INTO DATA(lx_msg).
    MESSAGE lx_msg TYPE 'E'.
ENDTRY.

Etiquetas: SAP ABAP AUSP CLASIFICACIÓN DE MATERIALES RTTS ALV DINÁMICO

Publicado el 7-4 22:37