REPORT {{p}}court_use. DATA gv_sport TYPE {{p}}court-sport. PARAMETERS p_from TYPE d OBLIGATORY. PARAMETERS p_to TYPE d OBLIGATORY. SELECT-OPTIONS s_sport FOR gv_sport. CLASS lcl_report DEFINITION FINAL. PUBLIC SECTION. TYPES: BEGIN OF ty_line, court_name TYPE {{p}}court-court_name, sport TYPE {{p}}court-sport, bookings TYPE i, duration TYPE p LENGTH 8 DECIMALS 2, total TYPE p LENGTH 8 DECIMALS 2, END OF ty_line, tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, BEGIN OF ty_db, court_name TYPE {{p}}court-court_name, sport TYPE {{p}}court-sport, bookings TYPE i, dur_min TYPE p LENGTH 8 DECIMALS 2, total TYPE p LENGTH 8 DECIMALS 2, END OF ty_db, tt_db TYPE STANDARD TABLE OF ty_db WITH EMPTY KEY. TYPES tt_sport TYPE RANGE OF {{p}}court-sport. METHODS select_usage IMPORTING iv_from TYPE d iv_to TYPE d it_sport TYPE tt_sport RETURNING VALUE(rt_lines) TYPE tt_line. METHODS display CHANGING ct_lines TYPE tt_line. ENDCLASS. CLASS lcl_report IMPLEMENTATION. METHOD select_usage. DATA lt_db TYPE tt_db. SELECT c~court_name, c~sport, COUNT( * ) AS bookings, SUM( b~duration_min ) AS dur_min, SUM( b~fee ) AS total FROM {{p}}booking AS b INNER JOIN {{p}}court AS c ON c~court_id = b~court_id WHERE b~booking_date BETWEEN @iv_from AND @iv_to AND c~sport IN @it_sport GROUP BY c~court_name, c~sport ORDER BY total DESCENDING INTO CORRESPONDING FIELDS OF TABLE @lt_db. rt_lines = VALUE #( FOR ls_db IN lt_db ( court_name = ls_db-court_name sport = ls_db-sport bookings = ls_db-bookings duration = ls_db-dur_min / 60 total = ls_db-total ) ). ENDMETHOD. METHOD display. TRY. cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) CHANGING t_table = ct_lines ). lo_alv->display( ). CATCH cx_salv_msg INTO DATA(lx_error). MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. ENDTRY. ENDMETHOD. ENDCLASS. START-OF-SELECTION. DATA(go_report) = NEW lcl_report( ). DATA(gt_lines) = go_report->select_usage( iv_from = p_from iv_to = p_to it_sport = s_sport[] ). go_report->display( CHANGING ct_lines = gt_lines ). CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. PRIVATE SECTION. CLASS-DATA go_env TYPE REF TO if_osql_test_environment. CLASS-METHODS class_setup. CLASS-METHODS class_teardown. METHODS totals_per_court FOR TESTING. METHODS duration_calc FOR TESTING. METHODS sport_filter FOR TESTING. ENDCLASS. CLASS ltc_report IMPLEMENTATION. METHOD class_setup. go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}COURT' ) ( '{{P}}BOOKING' ) ) ). DATA lt_court TYPE STANDARD TABLE OF {{p}}court WITH EMPTY KEY. lt_court = VALUE #( ( court_id = 1 court_name = 'A' sport = 'TENNIS' ) ( court_id = 2 court_name = 'B' sport = 'SQUASH' ) ). go_env->insert_test_data( lt_court ). DATA lt_booking TYPE STANDARD TABLE OF {{p}}booking WITH EMPTY KEY. lt_booking = VALUE #( ( booking_id = 1 court_id = 1 booking_date = '20260110' duration_min = 60 fee = '30.00' ) ( booking_id = 2 court_id = 1 booking_date = '20260120' duration_min = 30 fee = '15.00' ) ( booking_id = 3 court_id = 2 booking_date = '20260115' duration_min = 90 fee = '50.00' ) ). go_env->insert_test_data( lt_booking ). ENDMETHOD. METHOD class_teardown. go_env->destroy( ). ENDMETHOD. METHOD totals_per_court. DATA(lt_lines) = NEW lcl_report( )->select_usage( iv_from = '20260101' iv_to = '20260131' it_sport = VALUE #( ) ). cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). cl_abap_unit_assert=>assert_equals( exp = 'B' act = lt_lines[ 1 ]-court_name ). cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}booking-fee( 50 ) act = lt_lines[ 1 ]-total ). cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_lines[ 2 ]-bookings ). ENDMETHOD. METHOD duration_calc. DATA(lt_lines) = NEW lcl_report( )->select_usage( iv_from = '20260101' iv_to = '20260131' it_sport = VALUE #( ) ). cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}booking-fee( '1.50' ) act = lt_lines[ 1 ]-duration ). cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}booking-fee( '1.50' ) act = lt_lines[ 2 ]-duration ). ENDMETHOD. METHOD sport_filter. DATA(lt_lines) = NEW lcl_report( )->select_usage( iv_from = '20260101' iv_to = '20260131' it_sport = VALUE #( ( sign = 'I' option = 'EQ' low = 'SQUASH' ) ) ). cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ). cl_abap_unit_assert=>assert_equals( exp = 'B' act = lt_lines[ 1 ]-court_name ). ENDMETHOD. ENDCLASS.