REPORT {{p}}bakery_bill_old. DATA: gv_shop_id TYPE {{p}}shop-shop_id. PARAMETERS p_from TYPE d OBLIGATORY. PARAMETERS p_to TYPE d OBLIGATORY. SELECT-OPTIONS s_shop FOR gv_shop_id. CLASS lcl_billing DEFINITION FINAL. PUBLIC SECTION. TYPES: BEGIN OF ty_line, shop_id TYPE {{p}}shop-shop_id, shop_name TYPE {{p}}shop-shop_name, loaves TYPE i, rolls TYPE i, total TYPE {{p}}shop-loaf_price, END OF ty_line, tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY, BEGIN OF ty_shop, shop_id TYPE {{p}}shop-shop_id, shop_name TYPE {{p}}shop-shop_name, loaf_price TYPE {{p}}shop-loaf_price, roll_price TYPE {{p}}shop-roll_price, deliv_fee TYPE {{p}}shop-deliv_fee, END OF ty_shop, tt_shop TYPE SORTED TABLE OF ty_shop WITH UNIQUE KEY shop_id, BEGIN OF ty_deliv, shop_id TYPE {{p}}deliv-shop_id, loaves TYPE {{p}}deliv-loaves, rolls TYPE {{p}}deliv-rolls, END OF ty_deliv, tt_deliv TYPE STANDARD TABLE OF ty_deliv WITH EMPTY KEY, tt_range TYPE RANGE OF {{p}}shop-shop_id. METHODS read_shops IMPORTING it_range TYPE tt_range RETURNING VALUE(rt_shop) TYPE tt_shop. METHODS read_deliveries IMPORTING iv_from TYPE d iv_to TYPE d RETURNING VALUE(rt_deliv) TYPE tt_deliv. METHODS calculate IMPORTING it_shop TYPE tt_shop it_deliv TYPE tt_deliv RETURNING VALUE(rt_line) TYPE tt_line. METHODS display CHANGING ct_line TYPE tt_line. ENDCLASS. CLASS lcl_billing IMPLEMENTATION. METHOD read_shops. SELECT shop_id, shop_name, loaf_price, roll_price, deliv_fee FROM {{p}}shop WHERE shop_id IN @it_range AND is_active = 'X' INTO TABLE @rt_shop. ENDMETHOD. METHOD read_deliveries. SELECT shop_id, loaves, rolls FROM {{p}}deliv WHERE deliv_date >= @iv_from AND deliv_date <= @iv_to INTO TABLE @rt_deliv. ENDMETHOD. METHOD calculate. DATA lt_sum TYPE SORTED TABLE OF ty_line WITH UNIQUE KEY shop_id. LOOP AT it_deliv INTO DATA(ls_deliv). IF ls_deliv-loaves + ls_deliv-rolls <= 0. CONTINUE. ENDIF. READ TABLE it_shop INTO DATA(ls_shop) WITH KEY shop_id = ls_deliv-shop_id. IF sy-subrc <> 0. CONTINUE. ENDIF. READ TABLE lt_sum ASSIGNING FIELD-SYMBOL() WITH KEY shop_id = ls_deliv-shop_id. IF sy-subrc <> 0. INSERT VALUE #( shop_id = ls_shop-shop_id shop_name = ls_shop-shop_name ) INTO TABLE lt_sum ASSIGNING . ENDIF. -loaves = -loaves + ls_deliv-loaves. -rolls = -rolls + ls_deliv-rolls. -total = -total + ls_deliv-loaves * ls_shop-loaf_price + ls_deliv-rolls * ls_shop-roll_price + ls_shop-deliv_fee. ENDLOOP. rt_line = CORRESPONDING #( lt_sum ). SORT rt_line BY total DESCENDING shop_id ASCENDING. ENDMETHOD. METHOD display. TRY. cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv) CHANGING t_table = ct_line ). 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(lo_billing) = NEW lcl_billing( ). DATA(lt_shop) = lo_billing->read_shops( s_shop[] ). DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = p_from iv_to = p_to ). DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). lo_billing->display( CHANGING ct_line = lt_line ). CLASS ltc_billing 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_shop FOR TESTING. METHODS skips_empty_delivery FOR TESTING. METHODS skips_inactive_shop FOR TESTING. ENDCLASS. CLASS ltc_billing IMPLEMENTATION. METHOD class_setup. go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}SHOP' ) ( '{{P}}DELIV' ) ) ). DATA lt_shop TYPE STANDARD TABLE OF {{p}}shop WITH EMPTY KEY. lt_shop = VALUE #( ( shop_id = 'A1' shop_name = 'Alpha' loaf_price = 10 roll_price = 2 deliv_fee = 1 is_active = 'X' ) ( shop_id = 'B1' shop_name = 'Beta' loaf_price = 20 roll_price = 0 deliv_fee = 0 is_active = 'X' ) ( shop_id = 'C1' shop_name = 'Gamma' loaf_price = 30 roll_price = 0 deliv_fee = 0 is_active = ' ' ) ). go_env->insert_test_data( lt_shop ). DATA lt_deliv TYPE STANDARD TABLE OF {{p}}deliv WITH EMPTY KEY. lt_deliv = VALUE #( ( deliv_id = 1 shop_id = 'A1' deliv_date = '20260105' loaves = 2 rolls = 1 ) ( deliv_id = 2 shop_id = 'A1' deliv_date = '20260106' loaves = 0 rolls = 0 ) ( deliv_id = 3 shop_id = 'B1' deliv_date = '20260107' loaves = 3 rolls = 0 ) ( deliv_id = 4 shop_id = 'C1' deliv_date = '20260108' loaves = 4 rolls = 0 ) ). go_env->insert_test_data( lt_deliv ). ENDMETHOD. METHOD class_teardown. go_env->destroy( ). ENDMETHOD. METHOD totals_per_shop. DATA(lo_billing) = NEW lcl_billing( ). DATA(lt_shop) = lo_billing->read_shops( VALUE #( ) ). DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260101' iv_to = '20260131' ). DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_line ) ). cl_abap_unit_assert=>assert_equals( exp = 'B1' act = lt_line[ 1 ]-shop_id ). cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}shop-loaf_price( 60 ) act = lt_line[ 1 ]-total ). cl_abap_unit_assert=>assert_equals( exp = 'A1' act = lt_line[ 2 ]-shop_id ). cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_line[ 2 ]-loaves ). cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_line[ 2 ]-rolls ). cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}shop-loaf_price( 23 ) act = lt_line[ 2 ]-total ). ENDMETHOD. METHOD skips_empty_delivery. DATA(lo_billing) = NEW lcl_billing( ). DATA(lt_shop) = lo_billing->read_shops( VALUE #( ) ). DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260106' iv_to = '20260106' ). DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_line ) ). ENDMETHOD. METHOD skips_inactive_shop. DATA(lo_billing) = NEW lcl_billing( ). DATA(lt_shop) = lo_billing->read_shops( VALUE #( ( sign = 'I' option = 'EQ' low = 'C1' ) ) ). DATA(lt_deliv) = lo_billing->read_deliveries( iv_from = '20260101' iv_to = '20260131' ). DATA(lt_line) = lo_billing->calculate( it_shop = lt_shop it_deliv = lt_deliv ). cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_line ) ). ENDMETHOD. ENDCLASS.