Import/Export Excel dans ABAP Cloud

Catégorie
ABAP Cloud
Publié
Auteur
Johannes

Le traitement Excel dans ABAP Cloud diffère de l’ABAP classique. Les classes legacy comme CL_ABAP_CONV_* avec des formats spécifiques Excel ou les téléchargements basés sur GUI ne sont pas disponibles. À la place, vous utilisez des APIs natives Cloud comme XCO, la bibliothèque XLSX ou des services externes.

Aperçu des options disponibles

Dans ABAP Cloud, il existe plusieurs façons de traiter les fichiers Excel :

OptionDescriptionCas d’utilisation
API XCO SpreadsheetAPI native Cloud SAPExport Excel standard
Bibliothèque XLSXBibliothèque Open-SourceFormatage avancé
Services externesAPIs basées sur HTTPConversions spéciales
CSV comme alternativeFormat texte simpleCompatibilité maximale

Vue d’ensemble de l’architecture

┌─────────────────────────────────────────────────────────────────────────────┐
│ Traitement Excel dans ABAP Cloud │
│ │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ RAP Business Object / Service │ │
│ │ │ │
│ │ ┌─────────────────────┐ ┌─────────────────────┐ │ │
│ │ │ RAP Action │ │ RAP Action │ │ │
│ │ │ "Export Excel" │ │ "Import Excel" │ │ │
│ │ └──────────┬──────────┘ └──────────┬──────────┘ │ │
│ └─────────────┼────────────────────────┼──────────────────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ Options de traitement Excel │ │
│ │ │ │
│ │ ┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐ │ │
│ │ │ XCO Spreadsheet │ │ Bibliothèque │ │ Fallback CSV │ │ │
│ │ │ │ │ XLSX │ │ │ │ │
│ │ │ - Export/Import │ │ - Formatage │ │ - Simple │ │ │
│ │ │ - Native Cloud │ │ - Feuilles mult. │ │ - Universel │ │ │
│ │ └───────────────────┘ └───────────────────┘ └───────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘

Export Excel avec XCO

La bibliothèque XCO (Extension Components) offre une API compatible Cloud pour les opérations Spreadsheet.

Export de base

" Export Excel avec XCO
CLASS zcl_excel_exporter DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_sales_order,
order_id TYPE string,
customer TYPE string,
amount TYPE p LENGTH 10 DECIMALS 2,
currency TYPE string,
order_date TYPE d,
status TYPE string,
END OF ty_sales_order,
ty_sales_orders TYPE STANDARD TABLE OF ty_sales_order WITH KEY order_id.
METHODS:
export_to_excel
IMPORTING it_data TYPE ty_sales_orders
RETURNING VALUE(rv_xlsx) TYPE xstring
RAISING cx_xco_runtime_exception.
ENDCLASS.
CLASS zcl_excel_exporter IMPLEMENTATION.
METHOD export_to_excel.
" Créer le document Spreadsheet
DATA(lo_spreadsheet) = xco_cp_xlsx=>document->empty( )->spreadsheet.
" Première feuille (par défaut : Sheet1)
DATA(lo_worksheet) = lo_spreadsheet->worksheet->at_position( 1 ).
" Écrire l'en-tête
DATA(lo_cursor) = lo_worksheet->cursor(
io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' )
io_row = xco_cp_xlsx=>coordinate->for_numeric_value( 1 )
).
lo_cursor->get_cell( )->value->write_from( 'Numéro de commande' ).
lo_cursor->move_right( )->get_cell( )->value->write_from( 'Client' ).
lo_cursor->move_right( )->get_cell( )->value->write_from( 'Montant' ).
lo_cursor->move_right( )->get_cell( )->value->write_from( 'Devise' ).
lo_cursor->move_right( )->get_cell( )->value->write_from( 'Date de commande' ).
lo_cursor->move_right( )->get_cell( )->value->write_from( 'Statut' ).
" Écrire les lignes de données
DATA(lv_row) = 2.
LOOP AT it_data INTO DATA(ls_order).
lo_cursor = lo_worksheet->cursor(
io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' )
io_row = xco_cp_xlsx=>coordinate->for_numeric_value( lv_row )
).
lo_cursor->get_cell( )->value->write_from( ls_order-order_id ).
lo_cursor->move_right( )->get_cell( )->value->write_from( ls_order-customer ).
lo_cursor->move_right( )->get_cell( )->value->write_from( ls_order-amount ).
lo_cursor->move_right( )->get_cell( )->value->write_from( ls_order-currency ).
lo_cursor->move_right( )->get_cell( )->value->write_from(
|{ ls_order-order_date DATE = USER }|
).
lo_cursor->move_right( )->get_cell( )->value->write_from( ls_order-status ).
lv_row = lv_row + 1.
ENDLOOP.
" Exporter en binaire XLSX
rv_xlsx = lo_spreadsheet->get_file_content( ).
ENDMETHOD.
ENDCLASS.

Export générique depuis tables quelconques

" Exporteur Excel générique pour données quelconques
CLASS zcl_generic_excel_export DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
METHODS:
export_table
IMPORTING it_data TYPE ANY TABLE
it_column_names TYPE string_table OPTIONAL
RETURNING VALUE(rv_xlsx) TYPE xstring
RAISING cx_xco_runtime_exception.
PRIVATE SECTION.
METHODS:
get_field_names
IMPORTING it_data TYPE ANY TABLE
RETURNING VALUE(rt_names) TYPE string_table.
ENDCLASS.
CLASS zcl_generic_excel_export IMPLEMENTATION.
METHOD export_table.
DATA(lo_spreadsheet) = xco_cp_xlsx=>document->empty( )->spreadsheet.
DATA(lo_worksheet) = lo_spreadsheet->worksheet->at_position( 1 ).
" Déterminer les noms de champs
DATA(lt_fields) = COND #(
WHEN it_column_names IS NOT INITIAL
THEN it_column_names
ELSE get_field_names( it_data )
).
" En-tête
DATA(lo_cursor) = lo_worksheet->cursor(
io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' )
io_row = xco_cp_xlsx=>coordinate->for_numeric_value( 1 )
).
LOOP AT lt_fields INTO DATA(lv_field_name).
lo_cursor->get_cell( )->value->write_from( lv_field_name ).
IF sy-tabix < lines( lt_fields ).
lo_cursor->move_right( ).
ENDIF.
ENDLOOP.
" Lignes de données
DATA(lv_row) = 2.
DATA lv_value TYPE string.
LOOP AT it_data ASSIGNING FIELD-SYMBOL(<ls_row>).
lo_cursor = lo_worksheet->cursor(
io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' )
io_row = xco_cp_xlsx=>coordinate->for_numeric_value( lv_row )
).
LOOP AT lt_fields INTO lv_field_name.
ASSIGN COMPONENT lv_field_name OF STRUCTURE <ls_row> TO FIELD-SYMBOL(<lv_value>).
IF sy-subrc = 0.
lv_value = <lv_value>.
lo_cursor->get_cell( )->value->write_from( lv_value ).
ENDIF.
IF sy-tabix < lines( lt_fields ).
lo_cursor->move_right( ).
ENDIF.
ENDLOOP.
lv_row = lv_row + 1.
ENDLOOP.
rv_xlsx = lo_spreadsheet->get_file_content( ).
ENDMETHOD.
METHOD get_field_names.
" Déterminer les noms de champs depuis la structure de table
DATA(lo_table_descr) = CAST cl_abap_tabledescr(
cl_abap_typedescr=>describe_by_data( it_data )
).
DATA(lo_struct_descr) = CAST cl_abap_structdescr(
lo_table_descr->get_table_line_type( )
).
LOOP AT lo_struct_descr->get_components( ) INTO DATA(ls_component).
APPEND ls_component-name TO rt_names.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Import Excel avec parsing

L’import de fichiers Excel nécessite le parsing de la structure XLSX.

Bases de l’import XLSX

" Parser d'import Excel
CLASS zcl_excel_importer DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_sales_order_import,
order_id TYPE string,
customer TYPE string,
amount TYPE string,
currency TYPE string,
order_date TYPE string,
status TYPE string,
END OF ty_sales_order_import,
ty_sales_order_imports TYPE STANDARD TABLE OF ty_sales_order_import
WITH KEY order_id.
METHODS:
import_from_excel
IMPORTING iv_xlsx TYPE xstring
iv_skip_header TYPE abap_bool DEFAULT abap_true
RETURNING VALUE(rt_data) TYPE ty_sales_order_imports
RAISING cx_xco_runtime_exception.
METHODS:
validate_import
IMPORTING it_data TYPE ty_sales_order_imports
EXPORTING et_errors TYPE string_table
RETURNING VALUE(rv_valid) TYPE abap_bool.
ENDCLASS.
CLASS zcl_excel_importer IMPLEMENTATION.
METHOD import_from_excel.
" Charger le document XLSX
DATA(lo_document) = xco_cp_xlsx=>document->for_file_content( iv_xlsx ).
DATA(lo_spreadsheet) = lo_document->spreadsheet.
" Lire la première feuille
DATA(lo_worksheet) = lo_spreadsheet->worksheet->at_position( 1 ).
" Déterminer la zone utilisée
DATA(lo_used_area) = lo_worksheet->used_area( ).
DATA(lo_row_selection) = lo_used_area->row_selection.
DATA(lv_first_row) = lo_row_selection->first_row->position->value.
DATA(lv_last_row) = lo_row_selection->last_row->position->value.
" Ligne de départ (sauter l'en-tête)
DATA(lv_start_row) = COND #(
WHEN iv_skip_header = abap_true THEN lv_first_row + 1
ELSE lv_first_row
).
" Parcourir les lignes
DATA lv_row TYPE i.
DO ( lv_last_row - lv_start_row + 1 ) TIMES.
lv_row = lv_start_row + sy-index - 1.
DATA(lo_cursor) = lo_worksheet->cursor(
io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' )
io_row = xco_cp_xlsx=>coordinate->for_numeric_value( lv_row )
).
DATA ls_order TYPE ty_sales_order_import.
" Lire les valeurs des cellules
lo_cursor->get_cell( )->value->read_to( REF #( ls_order-order_id ) ).
lo_cursor->move_right( )->get_cell( )->value->read_to( REF #( ls_order-customer ) ).
lo_cursor->move_right( )->get_cell( )->value->read_to( REF #( ls_order-amount ) ).
lo_cursor->move_right( )->get_cell( )->value->read_to( REF #( ls_order-currency ) ).
lo_cursor->move_right( )->get_cell( )->value->read_to( REF #( ls_order-order_date ) ).
lo_cursor->move_right( )->get_cell( )->value->read_to( REF #( ls_order-status ) ).
" Ignorer les lignes vides
IF ls_order-order_id IS NOT INITIAL.
APPEND ls_order TO rt_data.
ENDIF.
ENDDO.
ENDMETHOD.
METHOD validate_import.
rv_valid = abap_true.
CLEAR et_errors.
LOOP AT it_data INTO DATA(ls_order).
DATA(lv_row) = sy-tabix + 1. " +1 pour l'en-tête
" Vérifier les champs obligatoires
IF ls_order-order_id IS INITIAL.
APPEND |Ligne { lv_row } : Numéro de commande manquant| TO et_errors.
rv_valid = abap_false.
ENDIF.
IF ls_order-customer IS INITIAL.
APPEND |Ligne { lv_row } : Client manquant| TO et_errors.
rv_valid = abap_false.
ENDIF.
" Valider le montant
TRY.
DATA(lv_amount) = CONV decfloat34( ls_order-amount ).
IF lv_amount < 0.
APPEND |Ligne { lv_row } : Le montant ne peut pas être négatif| TO et_errors.
rv_valid = abap_false.
ENDIF.
CATCH cx_sy_conversion_no_number.
APPEND |Ligne { lv_row } : Montant invalide '{ ls_order-amount }'| TO et_errors.
rv_valid = abap_false.
ENDTRY.
" Valider la devise
IF ls_order-currency NOT IN VALUE #(
( sign = 'I' option = 'EQ' low = 'EUR' )
( sign = 'I' option = 'EQ' low = 'USD' )
( sign = 'I' option = 'EQ' low = 'CHF' )
).
APPEND |Ligne { lv_row } : Devise invalide '{ ls_order-currency }'| TO et_errors.
rv_valid = abap_false.
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Import générique avec structure dynamique

" Import Excel générique dans table interne
CLASS zcl_generic_excel_import DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_cell,
column TYPE i,
row TYPE i,
value TYPE string,
END OF ty_cell,
ty_cells TYPE STANDARD TABLE OF ty_cell WITH KEY row column.
METHODS:
read_worksheet_raw
IMPORTING iv_xlsx TYPE xstring
iv_worksheet_pos TYPE i DEFAULT 1
RETURNING VALUE(rt_cells) TYPE ty_cells
RAISING cx_xco_runtime_exception.
METHODS:
read_as_table
IMPORTING iv_xlsx TYPE xstring
iv_worksheet_pos TYPE i DEFAULT 1
iv_has_header TYPE abap_bool DEFAULT abap_true
CHANGING ct_data TYPE ANY TABLE
RAISING cx_xco_runtime_exception.
ENDCLASS.
CLASS zcl_generic_excel_import IMPLEMENTATION.
METHOD read_worksheet_raw.
DATA(lo_document) = xco_cp_xlsx=>document->for_file_content( iv_xlsx ).
DATA(lo_worksheet) = lo_document->spreadsheet->worksheet->at_position( iv_worksheet_pos ).
DATA(lo_used_area) = lo_worksheet->used_area( ).
" Déterminer la zone
DATA(lv_first_row) = lo_used_area->row_selection->first_row->position->value.
DATA(lv_last_row) = lo_used_area->row_selection->last_row->position->value.
DATA(lv_first_col) = lo_used_area->column_selection->first_column->position->value.
DATA(lv_last_col) = lo_used_area->column_selection->last_column->position->value.
" Lire toutes les cellules
DO ( lv_last_row - lv_first_row + 1 ) TIMES.
DATA(lv_row) = lv_first_row + sy-index - 1.
DO ( lv_last_col - lv_first_col + 1 ) TIMES.
DATA(lv_col) = lv_first_col + sy-index - 1.
DATA(lo_cursor) = lo_worksheet->cursor(
io_column = xco_cp_xlsx=>coordinate->for_numeric_value( lv_col )
io_row = xco_cp_xlsx=>coordinate->for_numeric_value( lv_row )
).
DATA lv_value TYPE string.
lo_cursor->get_cell( )->value->read_to( REF #( lv_value ) ).
APPEND VALUE #(
column = lv_col
row = lv_row
value = lv_value
) TO rt_cells.
ENDDO.
ENDDO.
ENDMETHOD.
METHOD read_as_table.
" Lire les données brutes
DATA(lt_cells) = read_worksheet_raw(
iv_xlsx = iv_xlsx
iv_worksheet_pos = iv_worksheet_pos
).
" Déterminer la structure de colonnes
DATA(lo_table_descr) = CAST cl_abap_tabledescr(
cl_abap_typedescr=>describe_by_data( ct_data )
).
DATA(lo_struct_descr) = CAST cl_abap_structdescr(
lo_table_descr->get_table_line_type( )
).
DATA(lt_components) = lo_struct_descr->get_components( ).
" Créer le mapping d'en-tête (si présent)
DATA lt_column_mapping TYPE TABLE OF ty_cell.
IF iv_has_header = abap_true.
lt_column_mapping = VALUE #(
FOR ls_cell IN lt_cells WHERE ( row = 1 )
( ls_cell )
).
ENDIF.
" Déterminer la ligne de départ
DATA(lv_start_row) = COND #(
WHEN iv_has_header = abap_true THEN 2
ELSE 1
).
" Déterminer les lignes uniques
DATA lt_rows TYPE SORTED TABLE OF i WITH UNIQUE KEY table_line.
LOOP AT lt_cells INTO DATA(ls_cell) WHERE row >= lv_start_row.
INSERT ls_cell-row INTO TABLE lt_rows.
ENDLOOP.
" Charger les données
LOOP AT lt_rows INTO DATA(lv_row).
DATA ls_line TYPE REF TO data.
CREATE DATA ls_line LIKE LINE OF ct_data.
ASSIGN ls_line->* TO FIELD-SYMBOL(<ls_line>).
LOOP AT lt_cells INTO ls_cell WHERE row = lv_row.
" Mapper la colonne au nom de champ
DATA(lv_field_index) = ls_cell-column.
IF iv_has_header = abap_true.
" Nom de champ depuis l'en-tête
READ TABLE lt_column_mapping INTO DATA(ls_header)
WITH KEY column = ls_cell-column.
IF sy-subrc = 0.
READ TABLE lt_components WITH KEY name = to_upper( ls_header-value )
TRANSPORTING NO FIELDS.
IF sy-subrc = 0.
lv_field_index = sy-tabix.
ENDIF.
ENDIF.
ENDIF.
IF lv_field_index <= lines( lt_components ).
ASSIGN COMPONENT lv_field_index OF STRUCTURE <ls_line>
TO FIELD-SYMBOL(<lv_field>).
IF sy-subrc = 0.
<lv_field> = ls_cell-value.
ENDIF.
ENDIF.
ENDLOOP.
APPEND <ls_line> TO ct_data.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Formatage (Couleurs, largeur de colonnes)

Pour un formatage avancé, vous pouvez définir des styles de cellules.

Export formaté

" Export Excel avec formatage
CLASS zcl_formatted_excel_export DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
METHODS:
export_with_formatting
IMPORTING it_data TYPE zcl_excel_exporter=>ty_sales_orders
RETURNING VALUE(rv_xlsx) TYPE xstring
RAISING cx_xco_runtime_exception.
PRIVATE SECTION.
METHODS:
apply_header_style
IMPORTING io_cursor TYPE REF TO if_xco_xlsx_wa_cursor.
METHODS:
apply_amount_style
IMPORTING io_cursor TYPE REF TO if_xco_xlsx_wa_cursor
iv_amount TYPE p.
METHODS:
apply_status_style
IMPORTING io_cursor TYPE REF TO if_xco_xlsx_wa_cursor
iv_status TYPE string.
ENDCLASS.
CLASS zcl_formatted_excel_export IMPLEMENTATION.
METHOD export_with_formatting.
DATA(lo_spreadsheet) = xco_cp_xlsx=>document->empty( )->spreadsheet.
DATA(lo_worksheet) = lo_spreadsheet->worksheet->at_position( 1 ).
" Définir le nom de la feuille
lo_worksheet->properties( )->name->set( 'Commandes' ).
" En-tête avec formatage
DATA(lo_cursor) = lo_worksheet->cursor(
io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' )
io_row = xco_cp_xlsx=>coordinate->for_numeric_value( 1 )
).
DATA(lt_headers) = VALUE string_table(
( `Numéro de commande` )
( `Client` )
( `Montant` )
( `Devise` )
( `Date de commande` )
( `Statut` )
).
LOOP AT lt_headers INTO DATA(lv_header).
lo_cursor->get_cell( )->value->write_from( lv_header ).
apply_header_style( lo_cursor ).
IF sy-tabix < lines( lt_headers ).
lo_cursor->move_right( ).
ENDIF.
ENDLOOP.
" Lignes de données avec formatage conditionnel
DATA(lv_row) = 2.
LOOP AT it_data INTO DATA(ls_order).
lo_cursor = lo_worksheet->cursor(
io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' )
io_row = xco_cp_xlsx=>coordinate->for_numeric_value( lv_row )
).
" Numéro de commande
lo_cursor->get_cell( )->value->write_from( ls_order-order_id ).
" Client
lo_cursor->move_right( )->get_cell( )->value->write_from( ls_order-customer ).
" Montant avec formatage
lo_cursor->move_right( ).
lo_cursor->get_cell( )->value->write_from( ls_order-amount ).
apply_amount_style( io_cursor = lo_cursor iv_amount = ls_order-amount ).
" Devise
lo_cursor->move_right( )->get_cell( )->value->write_from( ls_order-currency ).
" Date
lo_cursor->move_right( )->get_cell( )->value->write_from(
|{ ls_order-order_date DATE = USER }|
).
" Statut avec code couleur
lo_cursor->move_right( ).
lo_cursor->get_cell( )->value->write_from( ls_order-status ).
apply_status_style( io_cursor = lo_cursor iv_status = ls_order-status ).
lv_row = lv_row + 1.
ENDLOOP.
rv_xlsx = lo_spreadsheet->get_file_content( ).
ENDMETHOD.
METHOD apply_header_style.
" En-tête : Gras et couleur de fond
" Note : L'API XCO offre des options de style limitées
" Pour un formatage avancé, voir la bibliothèque XLSX
" Exemple : Ajuster la largeur de cellule (si disponible)
" io_cursor->get_cell( )->style->set_bold( abap_true ).
ENDMETHOD.
METHOD apply_amount_style.
" Mettre en évidence les montants > 10000
IF iv_amount > 10000.
" Ici le formatage serait appliqué
ENDIF.
ENDMETHOD.
METHOD apply_status_style.
" Code couleur selon le statut
CASE iv_status.
WHEN 'COMPLETED'.
" Vert
WHEN 'PENDING'.
" Jaune
WHEN 'CANCELLED'.
" Rouge
ENDCASE.
ENDMETHOD.
ENDCLASS.

Feuilles multiples

Pour des rapports complexes avec plusieurs feuilles de calcul.

Export avec plusieurs feuilles

" Export Excel multi-feuilles
CLASS zcl_multi_worksheet_export DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_worksheet_data,
name TYPE string,
data TYPE REF TO data, " Référence vers ANY TABLE
END OF ty_worksheet_data,
ty_worksheets TYPE STANDARD TABLE OF ty_worksheet_data WITH KEY name.
METHODS:
export_multiple_worksheets
IMPORTING it_worksheets TYPE ty_worksheets
RETURNING VALUE(rv_xlsx) TYPE xstring
RAISING cx_xco_runtime_exception.
PRIVATE SECTION.
METHODS:
write_table_to_worksheet
IMPORTING io_worksheet TYPE REF TO if_xco_xlsx_wa_worksheet
it_data TYPE ANY TABLE.
ENDCLASS.
CLASS zcl_multi_worksheet_export IMPLEMENTATION.
METHOD export_multiple_worksheets.
DATA(lo_spreadsheet) = xco_cp_xlsx=>document->empty( )->spreadsheet.
DATA(lv_sheet_pos) = 1.
LOOP AT it_worksheets INTO DATA(ls_worksheet).
" Créer/récupérer la feuille
DATA(lo_worksheet) = lo_spreadsheet->worksheet->at_position( lv_sheet_pos ).
" Définir le nom
lo_worksheet->properties( )->name->set( ls_worksheet-name ).
" Écrire les données
ASSIGN ls_worksheet-data->* TO FIELD-SYMBOL(<lt_data>).
write_table_to_worksheet(
io_worksheet = lo_worksheet
it_data = <lt_data>
).
lv_sheet_pos = lv_sheet_pos + 1.
ENDLOOP.
rv_xlsx = lo_spreadsheet->get_file_content( ).
ENDMETHOD.
METHOD write_table_to_worksheet.
" Déterminer la structure
DATA(lo_table_descr) = CAST cl_abap_tabledescr(
cl_abap_typedescr=>describe_by_data( it_data )
).
DATA(lo_struct_descr) = CAST cl_abap_structdescr(
lo_table_descr->get_table_line_type( )
).
DATA(lt_components) = lo_struct_descr->get_components( ).
" En-tête
DATA(lo_cursor) = io_worksheet->cursor(
io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' )
io_row = xco_cp_xlsx=>coordinate->for_numeric_value( 1 )
).
LOOP AT lt_components INTO DATA(ls_component).
lo_cursor->get_cell( )->value->write_from( ls_component-name ).
IF sy-tabix < lines( lt_components ).
lo_cursor->move_right( ).
ENDIF.
ENDLOOP.
" Données
DATA(lv_row) = 2.
DATA lv_value TYPE string.
LOOP AT it_data ASSIGNING FIELD-SYMBOL(<ls_row>).
lo_cursor = io_worksheet->cursor(
io_column = xco_cp_xlsx=>coordinate->for_alphabetic_value( 'A' )
io_row = xco_cp_xlsx=>coordinate->for_numeric_value( lv_row )
).
LOOP AT lt_components INTO ls_component.
ASSIGN COMPONENT ls_component-name OF STRUCTURE <ls_row>
TO FIELD-SYMBOL(<lv_field>).
IF sy-subrc = 0.
lv_value = <lv_field>.
lo_cursor->get_cell( )->value->write_from( lv_value ).
ENDIF.
IF sy-tabix < lines( lt_components ).
lo_cursor->move_right( ).
ENDIF.
ENDLOOP.
lv_row = lv_row + 1.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Intégration dans RAP Action

L’intégration transparente dans les RAP Business Objects permet le téléchargement Excel directement depuis les apps Fiori.

Behavior Definition

managed implementation in class ZBP_I_SALESORDER unique;
strict ( 2 );
define behavior for ZI_SalesOrder alias SalesOrder
persistent table zsalesorder
lock master
authorization master ( instance )
{
create;
update;
delete;
// Export Excel comme Action
static action exportToExcel
result [1] ZA_ExcelDownload;
// Import Excel comme Action avec paramètre
static action importFromExcel
parameter ZA_ExcelUpload
result [*] $self;
field ( readonly ) OrderUUID, CreatedAt, CreatedBy;
}

Paramètres et résultat de l’Action

-- Résultat du téléchargement Excel
@EndUserText.label: 'Excel Download Result"
define abstract entity ZA_ExcelDownload
{
FileName : abap.char(100);
MimeType : abap.char(50);
FileContent : abap.rawstring(0);
}
-- Paramètre d'upload Excel
@EndUserText.label: 'Excel Upload Parameter"
define abstract entity ZA_ExcelUpload
{
FileName : abap.char(100);
MimeType : abap.char(50);
FileContent : abap.rawstring(0);
}

Implémentation du Behavior

" Implémentation RAP Behavior avec Actions Excel
CLASS lhc_salesorder DEFINITION INHERITING FROM cl_abap_behavior_handler.
PRIVATE SECTION.
METHODS exportToExcel FOR MODIFY
IMPORTING keys FOR ACTION SalesOrder~exportToExcel RESULT result.
METHODS importFromExcel FOR MODIFY
IMPORTING keys FOR ACTION SalesOrder~importFromExcel RESULT result.
ENDCLASS.
CLASS lhc_salesorder IMPLEMENTATION.
METHOD exportToExcel.
" Charger toutes les commandes
SELECT order_id, customer, amount, currency, order_date, status
FROM zsalesorder
INTO TABLE @DATA(lt_orders).
" Convertir au format d'export
DATA lt_export TYPE zcl_excel_exporter=>ty_sales_orders.
lt_export = CORRESPONDING #( lt_orders ).
TRY.
" Générer Excel
DATA(lo_exporter) = NEW zcl_excel_exporter( ).
DATA(lv_xlsx) = lo_exporter->export_to_excel( lt_export ).
" Nom de fichier avec timestamp
DATA(lv_filename) = |Commandes_{ sy-datum }_{ sy-uzeit }.xlsx|.
" Retourner le résultat
APPEND VALUE #(
%cid = keys[ 1 ]-%cid
%param = VALUE #(
FileName = lv_filename
MimeType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
FileContent = lv_xlsx
)
) TO result.
CATCH cx_xco_runtime_exception INTO DATA(lx_error).
APPEND VALUE #(
%cid = keys[ 1 ]-%cid
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = lx_error->get_text( )
)
) TO reported-salesorder.
ENDTRY.
ENDMETHOD.
METHOD importFromExcel.
" Lire le paramètre
DATA(ls_param) = keys[ 1 ]-%param.
TRY.
" Parser Excel
DATA(lo_importer) = NEW zcl_excel_importer( ).
DATA(lt_imported) = lo_importer->import_from_excel(
iv_xlsx = ls_param-FileContent
iv_skip_header = abap_true
).
" Validation
DATA lt_errors TYPE string_table.
DATA(lv_valid) = lo_importer->validate_import(
EXPORTING it_data = lt_imported
IMPORTING et_errors = lt_errors
).
IF lv_valid = abap_false.
" Signaler les erreurs
LOOP AT lt_errors INTO DATA(lv_error).
APPEND VALUE #(
%cid = keys[ 1 ]-%cid
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = lv_error
)
) TO reported-salesorder.
ENDLOOP.
RETURN.
ENDIF.
" Importer les données
DATA lt_create TYPE TABLE FOR CREATE zi_salesorder.
LOOP AT lt_imported INTO DATA(ls_imported).
APPEND VALUE #(
%cid = |IMPORT_{ sy-tabix }|
OrderID = ls_imported-order_id
Customer = ls_imported-customer
Amount = CONV #( ls_imported-amount )
Currency = ls_imported-currency
OrderDate = CONV d( ls_imported-order_date )
Status = ls_imported-status
) TO lt_create.
ENDLOOP.
" Créer les entités
MODIFY ENTITIES OF zi_salesorder IN LOCAL MODE
ENTITY SalesOrder
CREATE SET FIELDS WITH lt_create
MAPPED DATA(lt_mapped)
FAILED DATA(lt_failed)
REPORTED DATA(lt_reported).
" Retourner le résultat
result = CORRESPONDING #( lt_mapped-salesorder ).
CATCH cx_xco_runtime_exception INTO DATA(lx_error).
APPEND VALUE #(
%cid = keys[ 1 ]-%cid
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = lx_error->get_text( )
)
) TO reported-salesorder.
ENDTRY.
ENDMETHOD.
ENDCLASS.

Alternatives : CSV et OData

CSV comme alternative simple

Le CSV est universellement compatible et plus simple à générer :

" Export CSV comme alternative à Excel
CLASS zcl_csv_exporter DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
CONSTANTS:
gc_separator TYPE c LENGTH 1 VALUE ';',
gc_newline TYPE string VALUE cl_abap_char_utilities=>cr_lf.
METHODS:
export_to_csv
IMPORTING it_data TYPE ANY TABLE
iv_separator TYPE c DEFAULT gc_separator
RETURNING VALUE(rv_csv) TYPE string.
METHODS:
export_to_csv_binary
IMPORTING it_data TYPE ANY TABLE
iv_separator TYPE c DEFAULT gc_separator
iv_encoding TYPE string DEFAULT 'UTF-8"
RETURNING VALUE(rv_binary) TYPE xstring.
ENDCLASS.
CLASS zcl_csv_exporter IMPLEMENTATION.
METHOD export_to_csv.
" Déterminer la structure
DATA(lo_table_descr) = CAST cl_abap_tabledescr(
cl_abap_typedescr=>describe_by_data( it_data )
).
DATA(lo_struct_descr) = CAST cl_abap_structdescr(
lo_table_descr->get_table_line_type( )
).
DATA(lt_components) = lo_struct_descr->get_components( ).
" En-tête
LOOP AT lt_components INTO DATA(ls_component).
rv_csv = rv_csv && ls_component-name.
IF sy-tabix < lines( lt_components ).
rv_csv = rv_csv && iv_separator.
ENDIF.
ENDLOOP.
rv_csv = rv_csv && gc_newline.
" Lignes de données
DATA lv_value TYPE string.
LOOP AT it_data ASSIGNING FIELD-SYMBOL(<ls_row>).
LOOP AT lt_components INTO ls_component.
ASSIGN COMPONENT ls_component-name OF STRUCTURE <ls_row>
TO FIELD-SYMBOL(<lv_field>).
IF sy-subrc = 0.
lv_value = <lv_field>.
" Échappement CSV (doubler les guillemets, quoter si séparateur)
IF lv_value CS iv_separator OR lv_value CS '"' OR lv_value CS gc_newline.
REPLACE ALL OCCURRENCES OF '"' IN lv_value WITH '""'.
lv_value = |"{ lv_value }"|.
ENDIF.
rv_csv = rv_csv && lv_value.
ENDIF.
IF sy-tabix < lines( lt_components ).
rv_csv = rv_csv && iv_separator.
ENDIF.
ENDLOOP.
rv_csv = rv_csv && gc_newline.
ENDLOOP.
ENDMETHOD.
METHOD export_to_csv_binary.
DATA(lv_csv) = export_to_csv(
it_data = it_data
iv_separator = iv_separator
).
" BOM pour UTF-8 (reconnaissance Excel)
DATA(lv_bom) = CONV xstring( 'EFBBBF' ).
" Convertir
rv_binary = lv_bom && cl_abap_conv_codepage=>create_out(
codepage = iv_encoding
)->convert( lv_csv ).
ENDMETHOD.
ENDCLASS.

Bonnes pratiques

Recommandations pour le traitement Excel

AspectRecommandation
Petits volumes de donnéesAPI XCO Spreadsheet directement
Grands volumes de donnéesStreaming/Chunking ou CSV
Formatage complexeBibliothèque XLSX externe
Compatibilité maximaleCSV avec BOM UTF-8
Intégration FioriUtiliser l’export OData

Conseils de performance

  1. Traitement par lots : Traiter les gros fichiers par chunks
  2. Streaming : Traitement basé sur le flux pour les très gros fichiers
  3. Caching : Mettre en cache les exports répétitifs
  4. Asynchrone : Gros exports en arrière-plan (bgPF)

Conseils de sécurité

  1. Valider le type de fichier : Vérifier le type MIME avant traitement
  2. Limite de taille : Définir une taille de fichier maximale
  3. Valider le contenu : Valider les données avant import
  4. Autorisations : Vérifier les autorisations d’export/import

Résumé

Le traitement Excel dans ABAP Cloud utilise des APIs modernes :

  1. API XCO Spreadsheet pour export/import standard
  2. Exporteurs génériques pour types de tables quelconques
  3. Formatage via styles de cellules et propriétés
  4. Feuilles multiples pour rapports complexes
  5. RAP Actions pour intégration Fiori transparente
  6. CSV comme alternative pour compatibilité maximale

Le choix de la bonne méthode dépend de vos besoins : volume de données, complexité du formatage et public cible.

Sujets connexes