REPORT {{p}}vet_sum. TYPES: BEGIN OF ty_treat, treat_date TYPE {{p}}treat-treat_date, animal TYPE {{p}}treat-animal, fee TYPE {{p}}treat-fee, END OF ty_treat. TYPES tt_treat TYPE STANDARD TABLE OF ty_treat. TYPES: BEGIN OF ty_line, animal TYPE {{p}}treat-animal, total TYPE {{p}}treat-fee, cnt TYPE i, frequent TYPE c LENGTH 1, END OF ty_line. TYPES tt_line TYPE STANDARD TABLE OF ty_line. TYPES tt_anim TYPE RANGE OF {{p}}treat-animal. DATA gv_animal TYPE {{p}}treat-animal. PARAMETERS p_from TYPE d OBLIGATORY. SELECT-OPTIONS s_anim FOR gv_animal. CLASS lcl_report DEFINITION FINAL. PUBLIC SECTION. METHODS read_treatments IMPORTING iv_from TYPE d it_anim TYPE tt_anim EXPORTING et_treat TYPE tt_treat. METHODS build_lines IMPORTING it_treat TYPE tt_treat EXPORTING et_lines TYPE tt_line. METHODS display CHANGING ct_lines TYPE tt_line. ENDCLASS. CLASS lcl_report IMPLEMENTATION. METHOD read_treatments. SELECT treat_date animal fee FROM {{p}}treat INTO TABLE et_treat WHERE treat_date >= iv_from AND animal IN it_anim. ENDMETHOD. METHOD build_lines. DATA ls_treat TYPE ty_treat. DATA ls_line TYPE ty_line. FIELD-SYMBOLS TYPE ty_line. CLEAR et_lines. LOOP AT it_treat INTO ls_treat. READ TABLE et_lines ASSIGNING WITH KEY animal = ls_treat-animal. IF sy-subrc = 0. -total = -total + ls_treat-fee. -cnt = -cnt + 1. ELSE. CLEAR ls_line. ls_line-animal = ls_treat-animal. ls_line-total = ls_treat-fee. ls_line-cnt = 1. INSERT ls_line INTO TABLE et_lines. ENDIF. ENDLOOP. LOOP AT et_lines ASSIGNING . IF -cnt >= 3. -frequent = 'X'. ENDIF. ENDLOOP. SORT et_lines BY total DESCENDING. ENDMETHOD. METHOD display. DATA lo_alv TYPE REF TO cl_salv_table. DATA lx_msg TYPE REF TO cx_salv_msg. TRY. cl_salv_table=>factory( IMPORTING r_salv_table = lo_alv CHANGING t_table = ct_lines ). lo_alv->display( ). CATCH cx_salv_msg INTO lx_msg. MESSAGE lx_msg TYPE 'I' DISPLAY LIKE 'E'. ENDTRY. ENDMETHOD. ENDCLASS. DATA: go_report TYPE REF TO lcl_report, gt_treat TYPE tt_treat, gt_lines TYPE tt_line. START-OF-SELECTION. CREATE OBJECT go_report. go_report->read_treatments( EXPORTING iv_from = p_from it_anim = s_anim[] IMPORTING et_treat = gt_treat ). go_report->build_lines( EXPORTING it_treat = gt_treat IMPORTING et_lines = gt_lines ). go_report->display( CHANGING ct_lines = gt_lines ). CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. PRIVATE SECTION. DATA mt_treat TYPE tt_treat. METHODS add_treatment IMPORTING iv_date TYPE d iv_animal TYPE {{p}}treat-animal iv_fee TYPE {{p}}treat-fee. METHODS aggregate EXPORTING et_lines TYPE tt_line. METHODS totals_per_animal FOR TESTING. METHODS frequent_flag FOR TESTING. METHODS sorted_by_total FOR TESTING. ENDCLASS. CLASS ltc_report IMPLEMENTATION. METHOD add_treatment. DATA ls_treat TYPE ty_treat. ls_treat-treat_date = iv_date. ls_treat-animal = iv_animal. ls_treat-fee = iv_fee. APPEND ls_treat TO mt_treat. ENDMETHOD. METHOD aggregate. DATA lo_report TYPE REF TO lcl_report. CREATE OBJECT lo_report. lo_report->build_lines( EXPORTING it_treat = mt_treat IMPORTING et_lines = et_lines ). ENDMETHOD. METHOD totals_per_animal. DATA lt_lines TYPE tt_line. DATA ls_line TYPE ty_line. DATA lv_expected TYPE {{p}}treat-fee. DATA lv_animal TYPE {{p}}treat-animal. add_treatment( iv_date = '20260101' iv_animal = 'DOG' iv_fee = '10.00' ). add_treatment( iv_date = '20260102' iv_animal = 'DOG' iv_fee = '5.00' ). add_treatment( iv_date = '20260101' iv_animal = 'CAT' iv_fee = '40.00' ). aggregate( IMPORTING et_lines = lt_lines ). cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ). READ TABLE lt_lines INTO ls_line INDEX 1. cl_abap_unit_assert=>assert_subrc( exp = 0 ). lv_animal = 'CAT'. cl_abap_unit_assert=>assert_equals( exp = lv_animal act = ls_line-animal ). cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_line-cnt ). lv_expected = '40.00'. cl_abap_unit_assert=>assert_equals( exp = lv_expected act = ls_line-total ). READ TABLE lt_lines INTO ls_line INDEX 2. cl_abap_unit_assert=>assert_subrc( exp = 0 ). lv_animal = 'DOG'. cl_abap_unit_assert=>assert_equals( exp = lv_animal act = ls_line-animal ). cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_line-cnt ). lv_expected = '15.00'. cl_abap_unit_assert=>assert_equals( exp = lv_expected act = ls_line-total ). ENDMETHOD. METHOD frequent_flag. DATA lt_lines TYPE tt_line. DATA ls_line TYPE ty_line. DATA lv_animal TYPE {{p}}treat-animal. add_treatment( iv_date = '20260101' iv_animal = 'DOG' iv_fee = '10.00' ). add_treatment( iv_date = '20260102' iv_animal = 'DOG' iv_fee = '10.00' ). add_treatment( iv_date = '20260103' iv_animal = 'DOG' iv_fee = '10.00' ). add_treatment( iv_date = '20260101' iv_animal = 'CAT' iv_fee = '10.00' ). aggregate( IMPORTING et_lines = lt_lines ). lv_animal = 'DOG'. READ TABLE lt_lines INTO ls_line WITH KEY animal = lv_animal. cl_abap_unit_assert=>assert_subrc( exp = 0 ). cl_abap_unit_assert=>assert_equals( exp = 'X' act = ls_line-frequent ). lv_animal = 'CAT'. READ TABLE lt_lines INTO ls_line WITH KEY animal = lv_animal. cl_abap_unit_assert=>assert_subrc( exp = 0 ). cl_abap_unit_assert=>assert_initial( act = ls_line-frequent ). ENDMETHOD. METHOD sorted_by_total. DATA lt_lines TYPE tt_line. DATA ls_line TYPE ty_line. DATA lv_animal TYPE {{p}}treat-animal. add_treatment( iv_date = '20260101' iv_animal = 'DOG' iv_fee = '10.00' ). add_treatment( iv_date = '20260101' iv_animal = 'CAT' iv_fee = '40.00' ). add_treatment( iv_date = '20260101' iv_animal = 'BIRD' iv_fee = '25.00' ). aggregate( IMPORTING et_lines = lt_lines ). READ TABLE lt_lines INTO ls_line INDEX 1. cl_abap_unit_assert=>assert_subrc( exp = 0 ). lv_animal = 'CAT'. cl_abap_unit_assert=>assert_equals( exp = lv_animal act = ls_line-animal ). READ TABLE lt_lines INTO ls_line INDEX 3. cl_abap_unit_assert=>assert_subrc( exp = 0 ). lv_animal = 'BIRD'. cl_abap_unit_assert=>assert_equals( exp = lv_animal act = ls_line-animal ). ENDMETHOD. ENDCLASS.