Fix coverage with several contract classes; table annotation and file path checks; clean task dir on write

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aUaQeLnwbb1zTpN7kHeat
This commit is contained in:
Kral
2026-10-03 08:44:35 +02:00
parent fb576b0954
commit 03ab36149e
388 changed files with 28620 additions and 339 deletions

View File

@@ -0,0 +1,136 @@
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
OR 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.

View File

@@ -0,0 +1,136 @@
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 / 61
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.

View File

@@ -0,0 +1,19 @@
{
"id": "G0111",
"pool": "eval",
"object_type": "PROG",
"category": "B",
"attempts": [
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 2,
"killed": 2,
"ok": true
}
}
],
"accepted": true
}

View File

@@ -0,0 +1,118 @@
CLASS {{p}}court_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
TYPES tt_sport TYPE RANGE OF {{p}}court-sport.
METHODS run
IMPORTING iv_from TYPE d
iv_to TYPE d
it_sport TYPE tt_sport OPTIONAL
RETURNING VALUE(rr_data) TYPE REF TO data.
METHODS value_of
IMPORTING ir_data TYPE REF TO data
iv_row TYPE i
iv_col TYPE string
RETURNING VALUE(rv_value) TYPE string.
METHODS lines_of
IMPORTING ir_data TYPE REF TO data
RETURNING VALUE(rv_lines) TYPE i.
METHODS all_courts FOR TESTING.
METHODS totals_and_counts FOR TESTING.
METHODS duration_calc FOR TESTING.
METHODS sport_filter FOR TESTING.
METHODS date_filter FOR TESTING.
METHODS december_only FOR TESTING.
ENDCLASS.
CLASS {{p}}court_hidden IMPLEMENTATION.
METHOD run.
cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ).
SUBMIT {{p}}court_use WITH p_from = iv_from WITH p_to = iv_to WITH s_sport IN it_sport AND RETURN.
TRY.
cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ).
CATCH cx_salv_bs_sc_runtime_info.
ENDTRY.
cl_salv_bs_runtime_info=>clear_all( ).
ENDMETHOD.
METHOD lines_of.
FIELD-SYMBOLS <lt_data> TYPE STANDARD TABLE.
IF ir_data IS BOUND.
ASSIGN ir_data->* TO <lt_data>.
rv_lines = lines( <lt_data> ).
ENDIF.
ENDMETHOD.
METHOD value_of.
FIELD-SYMBOLS <lt_data> TYPE STANDARD TABLE.
ASSIGN ir_data->* TO <lt_data>.
ASSIGN <lt_data>[ iv_row ] TO FIELD-SYMBOL(<ls_line>).
ASSIGN COMPONENT iv_col OF STRUCTURE <ls_line> TO FIELD-SYMBOL(<lv_value>).
cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } missing| ).
rv_value = condense( CONV string( <lv_value> ) ).
ENDMETHOD.
METHOD all_courts.
cl_abap_unit_assert=>assert_equals(
exp = 3
act = lines_of( run( iv_from = '20260101' iv_to = '20260131' ) ) ).
ENDMETHOD.
METHOD totals_and_counts.
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ).
cl_abap_unit_assert=>assert_equals( exp = `Center Court`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `COURT_NAME` ) ).
cl_abap_unit_assert=>assert_equals( exp = `75.00`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TOTAL` ) ).
cl_abap_unit_assert=>assert_equals( exp = `2`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `BOOKINGS` ) ).
cl_abap_unit_assert=>assert_equals( exp = `Hall A`
act = value_of( ir_data = lr_data iv_row = 2 iv_col = `COURT_NAME` ) ).
cl_abap_unit_assert=>assert_equals( exp = `35.00`
act = value_of( ir_data = lr_data iv_row = 2 iv_col = `TOTAL` ) ).
cl_abap_unit_assert=>assert_equals( exp = `North Court`
act = value_of( ir_data = lr_data iv_row = 3 iv_col = `COURT_NAME` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1`
act = value_of( ir_data = lr_data iv_row = 3 iv_col = `BOOKINGS` ) ).
ENDMETHOD.
METHOD duration_calc.
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ).
cl_abap_unit_assert=>assert_equals( exp = `2.50`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DURATION` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1.25`
act = value_of( ir_data = lr_data iv_row = 2 iv_col = `DURATION` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1.00`
act = value_of( ir_data = lr_data iv_row = 3 iv_col = `DURATION` ) ).
ENDMETHOD.
METHOD sport_filter.
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131'
it_sport = VALUE #( ( sign = 'I' option = 'EQ' low = 'TENNIS' ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ).
cl_abap_unit_assert=>assert_equals( exp = `Center Court`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `COURT_NAME` ) ).
cl_abap_unit_assert=>assert_equals( exp = `North Court`
act = value_of( ir_data = lr_data iv_row = 2 iv_col = `COURT_NAME` ) ).
ENDMETHOD.
METHOD date_filter.
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260228' ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lines_of( lr_data ) ).
cl_abap_unit_assert=>assert_equals( exp = `North Court`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `COURT_NAME` ) ).
cl_abap_unit_assert=>assert_equals( exp = `90.00`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TOTAL` ) ).
cl_abap_unit_assert=>assert_equals( exp = `2`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `BOOKINGS` ) ).
ENDMETHOD.
METHOD december_only.
DATA(lr_data) = run( iv_from = '20251201' iv_to = '20251231' ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ).
cl_abap_unit_assert=>assert_equals( exp = `Hall B`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `COURT_NAME` ) ).
cl_abap_unit_assert=>assert_equals( exp = `25.00`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `TOTAL` ) ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,39 @@
{
"task": "G0111",
"mutants": [
{
"object": "{{P}}COURT_USE",
"mutant": "line 50: AND -> OR (logic)",
"status": "invalid",
"hidden": "0/0",
"failed_tests": []
},
{
"object": "{{P}}COURT_USE",
"mutant": "line 51: AND -> OR (logic)",
"status": "killed",
"hidden": "0/6",
"failed_tests": [
"ALL_COURTS",
"DATE_FILTER",
"DECEMBER_ONLY",
"DURATION_CALC",
"SPORT_FILTER",
"TOTALS_AND_COUNTS"
]
},
{
"object": "{{P}}COURT_USE",
"mutant": "line 60: 60 -> 61 (const)",
"status": "killed",
"hidden": "5/6",
"failed_tests": [
"DURATION_CALC"
]
}
],
"valid": 2,
"killed": 2,
"kill_rate": 1.0,
"ok": true
}

View File

@@ -0,0 +1,136 @@
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.

View File

@@ -0,0 +1,13 @@
@EndUserText.label : 'Booking'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table {{p}}booking {
key client : abap.clnt not null;
key booking_id : abap.int4 not null;
court_id : abap.int4;
booking_date : abap.dats;
duration_min : abap.int4;
fee : abap.dec(15,2);
}

View File

@@ -0,0 +1,11 @@
@EndUserText.label : 'Court'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table {{p}}court {
key client : abap.clnt not null;
key court_id : abap.int4 not null;
court_name : abap.char(40);
sport : abap.char(20);
}

View File

@@ -0,0 +1,25 @@
CLASS {{p}}court_seed DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS {{p}}court_seed IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DELETE FROM {{p}}booking.
DELETE FROM {{p}}court.
INSERT {{p}}court FROM TABLE @( VALUE #(
( court_id = 1 court_name = 'Center Court' sport = 'TENNIS' )
( court_id = 2 court_name = 'North Court' sport = 'TENNIS' )
( court_id = 3 court_name = 'Hall A' sport = 'BADMINTON' )
( court_id = 4 court_name = 'Hall B' sport = 'SQUASH' ) ) ).
INSERT {{p}}booking FROM TABLE @( VALUE #(
( booking_id = 1 court_id = 1 booking_date = '20260110' duration_min = 60 fee = '30.00' )
( booking_id = 2 court_id = 1 booking_date = '20260115' duration_min = 90 fee = '45.00' )
( booking_id = 3 court_id = 2 booking_date = '20260112' duration_min = 60 fee = '30.00' )
( booking_id = 4 court_id = 3 booking_date = '20260111' duration_min = 45 fee = '20.00' )
( booking_id = 5 court_id = 3 booking_date = '20260120' duration_min = 30 fee = '15.00' )
( booking_id = 6 court_id = 4 booking_date = '20251220' duration_min = 60 fee = '25.00' )
( booking_id = 7 court_id = 2 booking_date = '20260205' duration_min = 120 fee = '60.00' ) ) ).
out->write( sy-dbcnt ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,46 @@
# 1. Goal
A sports centre rents its courts to clubs. The manager needs a report about the
use of the courts. The report shows, for each court, the number of bookings,
the booked time in hours and the total fee for a period.
# 2. Open questions
None.
# 3. Context
- Table {{P}}COURT exists in package $TMP. It contains data.
Fields: COURT_ID (INT4, key), COURT_NAME (CHAR 40), SPORT (CHAR 20).
- Table {{P}}BOOKING exists in package $TMP. It contains data.
Fields: BOOKING_ID (INT4, key), COURT_ID (INT4), BOOKING_DATE (DATS),
DURATION_MIN (INT4, the booked time in minutes), FEE (DEC 15,2).
- A booking belongs to exactly one court. The field BOOKING.COURT_ID refers to
the field COURT.COURT_ID.
# 4. Contract
- Create the executable program {{P}}COURT_USE in package $TMP.
- Selection screen:
- Parameter P_FROM, type DATS, mandatory: the first booking date to include.
- Parameter P_TO, type DATS, mandatory: the last booking date to include.
- Select-option S_SPORT for the sport of the court.
- Show the result with CL_SALV_TABLE. The output table has these columns, with
these names: COURT_NAME, SPORT, BOOKINGS, DURATION, TOTAL.
# 5. Business rules
1. Include a booking only if BOOKING_DATE is on or after P_FROM and on or
before P_TO.
2. Include a booking only if the SPORT of its court is in S_SPORT.
3. Show one line for each court that has at least one included booking.
4. BOOKINGS is the number of included bookings of the court.
5. DURATION is the sum of DURATION_MIN of the included bookings, divided by 60.
DURATION has two decimal places.
6. TOTAL is the sum of FEE of the included bookings.
7. Sort the lines by TOTAL, the highest total first.
# 6. Constraints
- Release target: 7.58.
- Coding standards: Clean ABAP. Read the data with one database access.
- Out of scope: do not change {{P}}COURT and {{P}}BOOKING.
# 7. Acceptance
- The program is active and has no syntax error.
- The hidden tests pass.
- Write ABAP Unit tests as local test classes in the program.

View File

@@ -0,0 +1,68 @@
{
"id": "G0111",
"category": "B",
"object_type": "PROG",
"difficulty": 2,
"release_target": "v758",
"expected_outcome": "implement",
"budget": {
"max_tool_calls": 40,
"max_activations": 10
},
"seed": [
{
"type": "TABL",
"name": "{{P}}COURT",
"file": "seed/court.tabl.asabap",
"description": "Courts of the sports centre"
},
{
"type": "TABL",
"name": "{{P}}BOOKING",
"file": "seed/booking.tabl.asabap",
"description": "Court bookings"
},
{
"type": "CLAS",
"name": "{{P}}COURT_SEED",
"file": "seed/court_seed.clas.abap",
"description": "Court and booking seed data",
"run": true
}
],
"contract": [
{
"type": "PROG",
"name": "{{P}}COURT_USE",
"parameters": [
"p_from",
"p_to",
"s_sport"
]
}
],
"out_of_scope": [
"{{P}}COURT",
"{{P}}BOOKING"
],
"hidden_tests": [
{
"type": "CLAS",
"name": "{{P}}COURT_HIDDEN",
"file": "hidden/court_hidden.clas.abap",
"description": "Hidden tests for the court usage report"
}
],
"reference": [
{
"type": "PROG",
"name": "{{P}}COURT_USE",
"file": "reference/court_use.prog.abap",
"description": "Court usage report"
}
],
"craft_checks": [
"db_operation_in_loop",
"method_length"
]
}