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 :
| Option | Description | Cas d’utilisation |
|---|---|---|
| API XCO Spreadsheet | API native Cloud SAP | Export Excel standard |
| Bibliothèque XLSX | Bibliothèque Open-Source | Formatage avancé |
| Services externes | APIs basées sur HTTP | Conversions spéciales |
| CSV comme alternative | Format texte simple | Compatibilité 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 XCOCLASS 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 quelconquesCLASS 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 ExcelCLASS 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 interneCLASS 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 formatageCLASS 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-feuillesCLASS 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 SalesOrderpersistent table zsalesorderlock masterauthorization 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 ExcelCLASS 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 à ExcelCLASS 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
| Aspect | Recommandation |
|---|---|
| Petits volumes de données | API XCO Spreadsheet directement |
| Grands volumes de données | Streaming/Chunking ou CSV |
| Formatage complexe | Bibliothèque XLSX externe |
| Compatibilité maximale | CSV avec BOM UTF-8 |
| Intégration Fiori | Utiliser l’export OData |
Conseils de performance
- Traitement par lots : Traiter les gros fichiers par chunks
- Streaming : Traitement basé sur le flux pour les très gros fichiers
- Caching : Mettre en cache les exports répétitifs
- Asynchrone : Gros exports en arrière-plan (bgPF)
Conseils de sécurité
- Valider le type de fichier : Vérifier le type MIME avant traitement
- Limite de taille : Définir une taille de fichier maximale
- Valider le contenu : Valider les données avant import
- Autorisations : Vérifier les autorisations d’export/import
Résumé
Le traitement Excel dans ABAP Cloud utilise des APIs modernes :
- API XCO Spreadsheet pour export/import standard
- Exporteurs génériques pour types de tables quelconques
- Formatage via styles de cellules et propriétés
- Feuilles multiples pour rapports complexes
- RAP Actions pour intégration Fiori transparente
- 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
- RAP Actions et Functions - Actions pour Download/Upload
- Opérations sur fichiers - Traitement de fichiers en ABAP
- Gestion des pièces jointes - Pièces jointes de documents dans RAP