Review fixes G0019, G0108: added hidden tests; revalidated

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 16:35:20 +02:00
parent 7510131a7d
commit 0e96c6708e
31 changed files with 632 additions and 608 deletions

View File

@@ -7,10 +7,10 @@
{
"stage": "bundle",
"errors": [
"seed/t_path.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)",
"seed/t_path.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)",
"seed/t_fee.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)",
"seed/t_fee.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)"
"seed/botord.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)",
"seed/botord.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)",
"seed/botpln.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)",
"seed/botpln.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)"
],
"by_check": {
"static": 4

View File

@@ -0,0 +1,222 @@
CLASS {{p}}t14_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
TYPES ty_plan TYPE STANDARD TABLE OF {{p}}botpln WITH EMPTY KEY.
CLASS-DATA mo_env TYPE REF TO if_osql_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS setup.
METHODS add_order
IMPORTING iv_id TYPE {{p}}botord-order_id
iv_type TYPE {{p}}botord-beer_type
iv_volume TYPE {{p}}botord-volume_hl.
METHODS add_old_plan_row
IMPORTING iv_type TYPE {{p}}botpln-beer_type.
METHODS run_plan
IMPORTING iv_min_hl TYPE decfloat34
EXPORTING ev_total TYPE decfloat34
ev_count TYPE int4.
METHODS read_plan RETURNING VALUE(rt_plan) TYPE ty_plan.
METHODS empty_input FOR TESTING.
METHODS single_order FOR TESTING.
METHODS sums_same_type FOR TESTING.
METHODS ignores_bad_volume FOR TESTING.
METHODS sorts_by_total FOR TESTING.
METHODS tie_break_by_type FOR TESTING.
METHODS total_over_groups FOR TESTING.
METHODS min_volume_filters FOR TESTING.
METHODS min_volume_boundary FOR TESTING.
METHODS counts_orders FOR TESTING.
METHODS replaces_old_plan FOR TESTING.
ENDCLASS.
CLASS {{p}}t14_hidden IMPLEMENTATION.
METHOD class_setup.
mo_env = cl_osql_test_environment=>create(
i_dependency_list = VALUE #( ( '{{P}}BOTORD' ) ( '{{P}}BOTPLN' ) ) ).
ENDMETHOD.
METHOD class_teardown.
IF mo_env IS BOUND.
mo_env->destroy( ).
ENDIF.
ENDMETHOD.
METHOD setup.
DELETE FROM {{p}}botord.
DELETE FROM {{p}}botpln.
ENDMETHOD.
METHOD add_order.
DATA ls_order TYPE {{p}}botord.
ls_order-client = sy-mandt.
ls_order-order_id = iv_id.
ls_order-beer_type = iv_type.
ls_order-volume_hl = iv_volume.
INSERT {{p}}botord FROM @ls_order.
ENDMETHOD.
METHOD add_old_plan_row.
DATA ls_row TYPE {{p}}botpln.
ls_row-client = sy-mandt.
ls_row-beer_type = iv_type.
ls_row-plan_pos = 99.
ls_row-total_hl = 999.
ls_row-order_count = 99.
INSERT {{p}}botpln FROM @ls_row.
ENDMETHOD.
METHOD run_plan.
CALL FUNCTION '{{P}}BOT_PLAN'
EXPORTING
iv_min_hl = iv_min_hl
IMPORTING
ev_total_hl = ev_total
ev_plan_count = ev_count.
ENDMETHOD.
METHOD read_plan.
SELECT beer_type, plan_pos, total_hl, order_count
FROM {{p}}botpln
INTO TABLE @rt_plan.
SORT rt_plan BY plan_pos.
ENDMETHOD.
METHOD empty_input.
run_plan( EXPORTING iv_min_hl = 0
IMPORTING ev_total = DATA(lv_total) ev_count = DATA(lv_count) ).
DATA(lt_plan) = read_plan( ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_count ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_total ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_plan ) ).
ENDMETHOD.
METHOD single_order.
add_order( iv_id = '1' iv_type = 'PILS' iv_volume = 100 ).
run_plan( EXPORTING iv_min_hl = 0
IMPORTING ev_total = DATA(lv_total) ev_count = DATA(lv_count) ).
DATA(lt_plan) = read_plan( ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_plan ) ).
cl_abap_unit_assert=>assert_equals( exp = 'PILS' act = lt_plan[ 1 ]-beer_type ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_plan[ 1 ]-plan_pos ).
cl_abap_unit_assert=>assert_equals( exp = 100 act = lt_plan[ 1 ]-total_hl ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_plan[ 1 ]-order_count ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 100 ) act = lv_total ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_count ).
ENDMETHOD.
METHOD sums_same_type.
add_order( iv_id = '1' iv_type = 'PILS' iv_volume = 100 ).
add_order( iv_id = '2' iv_type = 'PILS' iv_volume = 50 ).
run_plan( EXPORTING iv_min_hl = 0
IMPORTING ev_total = DATA(lv_total) ev_count = DATA(lv_count) ).
DATA(lt_plan) = read_plan( ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_plan ) ).
cl_abap_unit_assert=>assert_equals( exp = 150 act = lt_plan[ 1 ]-total_hl ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_plan[ 1 ]-order_count ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 150 ) act = lv_total ).
ENDMETHOD.
METHOD ignores_bad_volume.
add_order( iv_id = '1' iv_type = 'PILS' iv_volume = 100 ).
add_order( iv_id = '2' iv_type = 'PILS' iv_volume = 0 ).
add_order( iv_id = '3' iv_type = 'ZERO' iv_volume = 0 ).
add_order( iv_id = '4' iv_type = 'ZERO' iv_volume = -5 ).
run_plan( EXPORTING iv_min_hl = 0
IMPORTING ev_total = DATA(lv_total) ev_count = DATA(lv_count) ).
DATA(lt_plan) = read_plan( ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_plan ) ).
cl_abap_unit_assert=>assert_equals( exp = 'PILS' act = lt_plan[ 1 ]-beer_type ).
cl_abap_unit_assert=>assert_equals( exp = 100 act = lt_plan[ 1 ]-total_hl ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_plan[ 1 ]-order_count ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_count ).
ENDMETHOD.
METHOD sorts_by_total.
add_order( iv_id = '1' iv_type = 'PILS' iv_volume = 100 ).
add_order( iv_id = '2' iv_type = 'ALE' iv_volume = 300 ).
add_order( iv_id = '3' iv_type = 'STOU' iv_volume = 200 ).
run_plan( EXPORTING iv_min_hl = 0
IMPORTING ev_total = DATA(lv_total) ev_count = DATA(lv_count) ).
DATA(lt_plan) = read_plan( ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_plan ) ).
cl_abap_unit_assert=>assert_equals( exp = 'ALE' act = lt_plan[ 1 ]-beer_type ).
cl_abap_unit_assert=>assert_equals( exp = 'STOU' act = lt_plan[ 2 ]-beer_type ).
cl_abap_unit_assert=>assert_equals( exp = 'PILS' act = lt_plan[ 3 ]-beer_type ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_plan[ 1 ]-plan_pos ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_plan[ 2 ]-plan_pos ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lt_plan[ 3 ]-plan_pos ).
ENDMETHOD.
METHOD tie_break_by_type.
add_order( iv_id = '1' iv_type = 'PILS' iv_volume = 100 ).
add_order( iv_id = '2' iv_type = 'ALE' iv_volume = 100 ).
run_plan( EXPORTING iv_min_hl = 0
IMPORTING ev_total = DATA(lv_total) ev_count = DATA(lv_count) ).
DATA(lt_plan) = read_plan( ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_plan ) ).
cl_abap_unit_assert=>assert_equals( exp = 'ALE' act = lt_plan[ 1 ]-beer_type ).
cl_abap_unit_assert=>assert_equals( exp = 'PILS' act = lt_plan[ 2 ]-beer_type ).
ENDMETHOD.
METHOD total_over_groups.
add_order( iv_id = '1' iv_type = 'PILS' iv_volume = 100 ).
add_order( iv_id = '2' iv_type = 'ALE' iv_volume = 300 ).
add_order( iv_id = '3' iv_type = 'STOU' iv_volume = 200 ).
run_plan( EXPORTING iv_min_hl = 0
IMPORTING ev_total = DATA(lv_total) ev_count = DATA(lv_count) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 600 ) act = lv_total ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_count ).
ENDMETHOD.
METHOD min_volume_filters.
add_order( iv_id = '1' iv_type = 'PILS' iv_volume = 100 ).
add_order( iv_id = '2' iv_type = 'ALE' iv_volume = 50 ).
run_plan( EXPORTING iv_min_hl = 75
IMPORTING ev_total = DATA(lv_total) ev_count = DATA(lv_count) ).
DATA(lt_plan) = read_plan( ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_plan ) ).
cl_abap_unit_assert=>assert_equals( exp = 'PILS' act = lt_plan[ 1 ]-beer_type ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 100 ) act = lv_total ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_count ).
ENDMETHOD.
METHOD min_volume_boundary.
add_order( iv_id = '1' iv_type = 'PILS' iv_volume = 100 ).
run_plan( EXPORTING iv_min_hl = 100
IMPORTING ev_total = DATA(lv_total) ev_count = DATA(lv_count) ).
DATA(lt_plan) = read_plan( ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_plan ) ).
cl_abap_unit_assert=>assert_equals( exp = 'PILS' act = lt_plan[ 1 ]-beer_type ).
ENDMETHOD.
METHOD counts_orders.
add_order( iv_id = '1' iv_type = 'PILS' iv_volume = 10 ).
add_order( iv_id = '2' iv_type = 'PILS' iv_volume = 20 ).
add_order( iv_id = '3' iv_type = 'PILS' iv_volume = 30 ).
add_order( iv_id = '4' iv_type = 'ALE' iv_volume = 40 ).
run_plan( EXPORTING iv_min_hl = 0
IMPORTING ev_total = DATA(lv_total) ev_count = DATA(lv_count) ).
DATA(lt_plan) = read_plan( ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_plan ) ).
cl_abap_unit_assert=>assert_equals( exp = 'PILS' act = lt_plan[ 1 ]-beer_type ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lt_plan[ 1 ]-order_count ).
cl_abap_unit_assert=>assert_equals( exp = 'ALE' act = lt_plan[ 2 ]-beer_type ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_plan[ 2 ]-order_count ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ).
ENDMETHOD.
METHOD replaces_old_plan.
add_old_plan_row( iv_type = 'OLD' ).
add_order( iv_id = '1' iv_type = 'PILS' iv_volume = 100 ).
run_plan( EXPORTING iv_min_hl = 0
IMPORTING ev_total = DATA(lv_total) ev_count = DATA(lv_count) ).
DATA(lt_plan) = read_plan( ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_plan ) ).
cl_abap_unit_assert=>assert_equals( exp = 'PILS' act = lt_plan[ 1 ]-beer_type ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_count ).
ENDMETHOD.
ENDCLASS.

View File

@@ -1,129 +0,0 @@
CLASS {{p}}track_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
TYPES ty_paths TYPE {{P}}TRACK_TYPES=>tt_path.
TYPES ty_fees TYPE {{P}}TRACK_TYPES=>tt_fee.
TYPES ty_km TYPE {{P}}TRACK_TYPES=>ty_km.
TYPES ty_money TYPE {{P}}TRACK_TYPES=>ty_money.
METHODS call_fm
IMPORTING it_paths TYPE ty_paths
EXPORTING et_fees TYPE ty_fees
ev_subrc TYPE sysubrc.
METHODS one_path_one_row FOR TESTING.
METHODS groups_and_sorts FOR TESTING.
METHODS night_surcharge FOR TESTING.
METHODS minimum_fee FOR TESTING.
METHODS rounds_up FOR TESTING.
METHODS invalid_distance FOR TESTING.
METHODS invalid_category FOR TESTING.
METHODS empty_input FOR TESTING.
ENDCLASS.
CLASS {{p}}track_hidden IMPLEMENTATION.
METHOD call_fm.
CALL FUNCTION '{{P}}TRACK_FEE'
EXPORTING
it_paths = it_paths
IMPORTING
et_fees = et_fees
EXCEPTIONS
invalid_distance = 1
invalid_category = 2
OTHERS = 3.
ev_subrc = sy-subrc.
ENDMETHOD.
METHOD one_path_one_row.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'IC' distance_km = '100.00' night_flag = '' ) ).
call_fm( EXPORTING it_paths = lt_paths
IMPORTING et_fees = DATA(lt_fees) ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_fees ) ).
DATA(ls_fee) = lt_fees[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = 'IC' act = ls_fee-train_cat ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_fee-path_count ).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_km( '100.00' ) act = ls_fee-total_km ).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_money( '120.00' ) act = ls_fee-total_fee ).
ENDMETHOD.
METHOD groups_and_sorts.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'IC' distance_km = '100.00' night_flag = '' )
( path_id = '00000002' train_cat = 'FR' distance_km = '100.00' night_flag = '' )
( path_id = '00000003' train_cat = 'IC' distance_km = '50.00' night_flag = '' ) ).
call_fm( EXPORTING it_paths = lt_paths
IMPORTING et_fees = DATA(lt_fees) ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_fees ) ).
DATA(ls_first) = lt_fees[ 1 ].
DATA(ls_last) = lt_fees[ 2 ].
cl_abap_unit_assert=>assert_equals( exp = 'FR' act = ls_first-train_cat ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_first-path_count ).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_money( '60.00' ) act = ls_first-total_fee ).
cl_abap_unit_assert=>assert_equals( exp = 'IC' act = ls_last-train_cat ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_last-path_count ).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_km( '150.00' ) act = ls_last-total_km ).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_money( '180.00' ) act = ls_last-total_fee ).
ENDMETHOD.
METHOD night_surcharge.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'RG' distance_km = '100.00' night_flag = 'X' ) ).
call_fm( EXPORTING it_paths = lt_paths
IMPORTING et_fees = DATA(lt_fees) ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ).
DATA(ls_fee) = lt_fees[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = CONV ty_money( '108.00' ) act = ls_fee-total_fee ).
ENDMETHOD.
METHOD minimum_fee.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'FR' distance_km = '2.00' night_flag = '' ) ).
call_fm( EXPORTING it_paths = lt_paths
IMPORTING et_fees = DATA(lt_fees) ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ).
DATA(ls_fee) = lt_fees[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = CONV ty_money( '5.00' ) act = ls_fee-total_fee ).
ENDMETHOD.
METHOD rounds_up.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'FR' distance_km = '10.01' night_flag = '' ) ).
call_fm( EXPORTING it_paths = lt_paths
IMPORTING et_fees = DATA(lt_fees) ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ).
DATA(ls_fee) = lt_fees[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = CONV ty_money( '6.01' ) act = ls_fee-total_fee ).
ENDMETHOD.
METHOD invalid_distance.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'IC' distance_km = '0.00' night_flag = '' ) ).
call_fm( EXPORTING it_paths = lt_paths
IMPORTING et_fees = DATA(lt_fees) ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ).
ENDMETHOD.
METHOD invalid_category.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'XX' distance_km = '10.00' night_flag = '' ) ).
call_fm( EXPORTING it_paths = lt_paths
IMPORTING et_fees = DATA(lt_fees) ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rc ).
ENDMETHOD.
METHOD empty_input.
DATA lt_paths TYPE ty_paths.
call_fm( EXPORTING it_paths = lt_paths
IMPORTING et_fees = DATA(lt_fees) ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_fees ) ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,66 @@
FUNCTION {{p}}bot_plan
IMPORTING
iv_min_hl TYPE decfloat34
EXPORTING
ev_total_hl TYPE decfloat34
ev_plan_count TYPE int4.
TYPES: BEGIN OF ty_group,
beer_type TYPE {{p}}botord-beer_type,
total_hl TYPE {{p}}botpln-total_hl,
order_count TYPE {{p}}botpln-order_count,
END OF ty_group.
DATA lt_groups TYPE HASHED TABLE OF ty_group WITH UNIQUE KEY beer_type.
DATA ls_group TYPE ty_group.
DATA ls_row TYPE {{p}}botpln.
DATA lt_plan TYPE STANDARD TABLE OF {{p}}botpln WITH EMPTY KEY.
CLEAR: ev_total_hl, ev_plan_count.
SELECT beer_type, volume_hl
FROM {{p}}botord
INTO TABLE @DATA(lt_orders).
LOOP AT lt_orders INTO DATA(ls_order).
IF ls_order-volume_hl <= 0.
CONTINUE.
ENDIF.
READ TABLE lt_groups ASSIGNING FIELD-SYMBOL(<ls_group>)
WITH TABLE KEY beer_type = ls_order-beer_type.
IF sy-subrc = 0.
<ls_group>-total_hl = <ls_group>-total_hl + ls_order-volume_hl.
<ls_group>-order_count = <ls_group>-order_count + 1.
ELSE.
ls_group = VALUE #( beer_type = ls_order-beer_type
total_hl = ls_order-volume_hl
order_count = 1 ).
INSERT ls_group INTO TABLE lt_groups.
ENDIF.
ENDLOOP.
LOOP AT lt_groups ASSIGNING FIELD-SYMBOL(<ls_grp>).
IF <ls_grp>-total_hl < iv_min_hl.
CONTINUE.
ENDIF.
ls_row = VALUE #( client = sy-mandt
beer_type = <ls_grp>-beer_type
plan_pos = 0
total_hl = <ls_grp>-total_hl
order_count = <ls_grp>-order_count ).
APPEND ls_row TO lt_plan.
ev_total_hl = ev_total_hl + <ls_grp>-total_hl.
ENDLOOP.
SORT lt_plan BY total_hl DESCENDING beer_type ASCENDING.
LOOP AT lt_plan ASSIGNING FIELD-SYMBOL(<ls_row>).
<ls_row>-plan_pos = sy-tabix.
ENDLOOP.
ev_plan_count = lines( lt_plan ).
DELETE FROM {{p}}botpln.
IF lt_plan IS NOT INITIAL.
INSERT {{p}}botpln FROM TABLE @lt_plan.
ENDIF.
ENDFUNCTION.

View File

@@ -0,0 +1,115 @@
CLASS {{p}}t14_test DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
TYPES ty_plan TYPE STANDARD TABLE OF {{p}}botpln WITH EMPTY KEY.
CLASS-DATA mo_env TYPE REF TO if_osql_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS setup.
METHODS add_order
IMPORTING iv_id TYPE {{p}}botord-order_id
iv_type TYPE {{p}}botord-beer_type
iv_volume TYPE {{p}}botord-volume_hl.
METHODS run_plan
IMPORTING iv_min_hl TYPE decfloat34
EXPORTING ev_total TYPE decfloat34
ev_count TYPE int4.
METHODS read_plan RETURNING VALUE(rt_plan) TYPE ty_plan.
METHODS groups_orders FOR TESTING.
METHODS filters_by_min_volume FOR TESTING.
METHODS sorts_plan FOR TESTING.
METHODS replaces_plan FOR TESTING.
ENDCLASS.
CLASS {{p}}t14_test IMPLEMENTATION.
METHOD class_setup.
mo_env = cl_osql_test_environment=>create(
i_dependency_list = VALUE #( ( '{{P}}BOTORD' ) ( '{{P}}BOTPLN' ) ) ).
ENDMETHOD.
METHOD class_teardown.
IF mo_env IS BOUND.
mo_env->destroy( ).
ENDIF.
ENDMETHOD.
METHOD setup.
DELETE FROM {{p}}botord.
DELETE FROM {{p}}botpln.
ENDMETHOD.
METHOD add_order.
DATA ls_order TYPE {{p}}botord.
ls_order-client = sy-mandt.
ls_order-order_id = iv_id.
ls_order-beer_type = iv_type.
ls_order-volume_hl = iv_volume.
INSERT {{p}}botord FROM @ls_order.
ENDMETHOD.
METHOD run_plan.
CALL FUNCTION '{{P}}BOT_PLAN'
EXPORTING
iv_min_hl = iv_min_hl
IMPORTING
ev_total_hl = ev_total
ev_plan_count = ev_count.
ENDMETHOD.
METHOD read_plan.
SELECT beer_type, plan_pos, total_hl, order_count
FROM {{p}}botpln
INTO TABLE @rt_plan.
SORT rt_plan BY plan_pos.
ENDMETHOD.
METHOD groups_orders.
add_order( iv_id = '1' iv_type = 'PILS' iv_volume = 100 ).
add_order( iv_id = '2' iv_type = 'PILS' iv_volume = 50 ).
run_plan( EXPORTING iv_min_hl = 0
IMPORTING ev_total = DATA(lv_total) ev_count = DATA(lv_count) ).
DATA(lt_plan) = read_plan( ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_plan ) ).
cl_abap_unit_assert=>assert_equals( exp = 150 act = lt_plan[ 1 ]-total_hl ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_plan[ 1 ]-order_count ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 150 ) act = lv_total ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_count ).
ENDMETHOD.
METHOD filters_by_min_volume.
add_order( iv_id = '1' iv_type = 'PILS' iv_volume = 100 ).
add_order( iv_id = '2' iv_type = 'ALE' iv_volume = 50 ).
run_plan( EXPORTING iv_min_hl = 75
IMPORTING ev_total = DATA(lv_total) ev_count = DATA(lv_count) ).
DATA(lt_plan) = read_plan( ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_plan ) ).
cl_abap_unit_assert=>assert_equals( exp = 'PILS' act = lt_plan[ 1 ]-beer_type ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 100 ) act = lv_total ).
ENDMETHOD.
METHOD sorts_plan.
add_order( iv_id = '1' iv_type = 'PILS' iv_volume = 100 ).
add_order( iv_id = '2' iv_type = 'ALE' iv_volume = 300 ).
run_plan( EXPORTING iv_min_hl = 0
IMPORTING ev_total = DATA(lv_total) ev_count = DATA(lv_count) ).
DATA(lt_plan) = read_plan( ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_plan ) ).
cl_abap_unit_assert=>assert_equals( exp = 'ALE' act = lt_plan[ 1 ]-beer_type ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_plan[ 1 ]-plan_pos ).
cl_abap_unit_assert=>assert_equals( exp = 'PILS' act = lt_plan[ 2 ]-beer_type ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_plan[ 2 ]-plan_pos ).
ENDMETHOD.
METHOD replaces_plan.
add_order( iv_id = '1' iv_type = 'PILS' iv_volume = 0 ).
run_plan( EXPORTING iv_min_hl = 0
IMPORTING ev_total = DATA(lv_total) ev_count = DATA(lv_count) ).
DATA(lt_plan) = read_plan( ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_plan ) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_total ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_count ).
ENDMETHOD.
ENDCLASS.

View File

@@ -1,87 +0,0 @@
FUNCTION {{p}}track_fee
IMPORTING
it_paths TYPE STANDARD TABLE
EXPORTING
et_fees TYPE STANDARD TABLE
EXCEPTIONS
invalid_distance
invalid_category.
TYPES: BEGIN OF ty_sum,
train_cat TYPE c LENGTH 2,
path_count TYPE i,
total_km TYPE p LENGTH 13 DECIMALS 2,
total_fee TYPE p LENGTH 13 DECIMALS 2,
END OF ty_sum.
DATA: lt_sums TYPE SORTED TABLE OF ty_sum WITH UNIQUE KEY train_cat,
lv_cat TYPE c LENGTH 2,
lv_km TYPE p LENGTH 13 DECIMALS 2,
lv_flag TYPE c LENGTH 1,
lv_rate TYPE p LENGTH 5 DECIMALS 2,
lv_raw TYPE decfloat34,
lv_fee TYPE p LENGTH 13 DECIMALS 2.
FIELD-SYMBOLS: <ls_in> TYPE any,
<ls_out> TYPE any,
<ls_sum> TYPE ty_sum,
<lv_val> TYPE any.
CLEAR et_fees.
LOOP AT it_paths ASSIGNING <ls_in>.
ASSIGN COMPONENT 'TRAIN_CAT' OF STRUCTURE <ls_in> TO <lv_val>.
lv_cat = <lv_val>.
ASSIGN COMPONENT 'DISTANCE_KM' OF STRUCTURE <ls_in> TO <lv_val>.
lv_km = <lv_val>.
ASSIGN COMPONENT 'NIGHT_FLAG' OF STRUCTURE <ls_in> TO <lv_val>.
lv_flag = <lv_val>.
IF lv_km <= 0.
RAISE invalid_distance.
ENDIF.
CASE lv_cat.
WHEN 'IC'.
lv_rate = '1.20'.
WHEN 'RG'.
lv_rate = '0.90'.
WHEN 'FR'.
lv_rate = '0.60'.
WHEN 'SP'.
lv_rate = '1.50'.
WHEN OTHERS.
RAISE invalid_category.
ENDCASE.
lv_raw = lv_km * lv_rate.
IF lv_flag = 'X'.
lv_raw = lv_raw * 12 / 10.
ENDIF.
lv_fee = round( val = lv_raw dec = 2 mode = cl_abap_math=>round_half_up ).
IF lv_fee < 5.
lv_fee = 5.
ENDIF.
READ TABLE lt_sums ASSIGNING <ls_sum> WITH TABLE KEY train_cat = lv_cat.
IF sy-subrc <> 0.
INSERT VALUE ty_sum( train_cat = lv_cat ) INTO TABLE lt_sums ASSIGNING <ls_sum>.
ENDIF.
<ls_sum>-path_count = <ls_sum>-path_count + 1.
<ls_sum>-total_km = <ls_sum>-total_km + lv_km.
<ls_sum>-total_fee = <ls_sum>-total_fee + lv_fee.
ENDLOOP.
LOOP AT lt_sums ASSIGNING <ls_sum>.
APPEND INITIAL LINE TO et_fees ASSIGNING <ls_out>.
ASSIGN COMPONENT 'TRAIN_CAT' OF STRUCTURE <ls_out> TO <lv_val>.
<lv_val> = <ls_sum>-train_cat.
ASSIGN COMPONENT 'PATH_COUNT' OF STRUCTURE <ls_out> TO <lv_val>.
<lv_val> = <ls_sum>-path_count.
ASSIGN COMPONENT 'TOTAL_KM' OF STRUCTURE <ls_out> TO <lv_val>.
<lv_val> = <ls_sum>-total_km.
ASSIGN COMPONENT 'TOTAL_FEE' OF STRUCTURE <ls_out> TO <lv_val>.
<lv_val> = <ls_sum>-total_fee.
ENDLOOP.
ENDFUNCTION.

View File

@@ -1,75 +0,0 @@
CLASS {{p}}track_test DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
TYPES ty_paths TYPE {{P}}TRACK_TYPES=>tt_path.
TYPES ty_fees TYPE {{P}}TRACK_TYPES=>tt_fee.
TYPES ty_money TYPE {{P}}TRACK_TYPES=>ty_money.
METHODS fees_for
IMPORTING it_paths TYPE ty_paths
RETURNING VALUE(rt_fees) TYPE ty_fees.
METHODS one_category FOR TESTING.
METHODS two_categories FOR TESTING.
METHODS night_surcharge FOR TESTING.
METHODS invalid_distance FOR TESTING.
ENDCLASS.
CLASS {{p}}track_test IMPLEMENTATION.
METHOD fees_for.
CALL FUNCTION '{{P}}TRACK_FEE'
EXPORTING
it_paths = it_paths
IMPORTING
et_fees = rt_fees
EXCEPTIONS
invalid_distance = 1
invalid_category = 2
OTHERS = 3.
cl_abap_unit_assert=>assert_subrc( exp = 0 ).
ENDMETHOD.
METHOD one_category.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'SP' distance_km = '10.00' night_flag = '' ) ).
DATA(lt_fees) = fees_for( lt_paths ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_fees ) ).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_money( '15.00' ) act = lt_fees[ 1 ]-total_fee ).
ENDMETHOD.
METHOD two_categories.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'RG' distance_km = '20.00' night_flag = '' )
( path_id = '00000002' train_cat = 'FR' distance_km = '20.00' night_flag = '' ) ).
DATA(lt_fees) = fees_for( lt_paths ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_fees ) ).
cl_abap_unit_assert=>assert_equals( exp = 'FR' act = lt_fees[ 1 ]-train_cat ).
cl_abap_unit_assert=>assert_equals( exp = 'RG' act = lt_fees[ 2 ]-train_cat ).
ENDMETHOD.
METHOD night_surcharge.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'FR' distance_km = '50.00' night_flag = 'X' ) ).
DATA(lt_fees) = fees_for( lt_paths ).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_money( '36.00' ) act = lt_fees[ 1 ]-total_fee ).
ENDMETHOD.
METHOD invalid_distance.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'IC' distance_km = '0.00' night_flag = '' ) ).
DATA lt_fees TYPE ty_fees.
CALL FUNCTION '{{P}}TRACK_FEE'
EXPORTING
it_paths = lt_paths
IMPORTING
et_fees = lt_fees
EXCEPTIONS
invalid_distance = 1
OTHERS = 2.
cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,11 @@
@EndUserText.label: 'Bottling orders'
@AbapCatalog.enhancement.category: #NOT_EXTENSIBLE
@AbapCatalog.tableCategory: #TRANSPARENT
@AbapCatalog.deliveryClass: #A
@AbapCatalog.dataMaintenance: #RESTRICTED
define table {{p}}botord {
key client : abap.clnt not null;
key order_id : abap.numc(10) not null;
beer_type : abap.char(4);
volume_hl : abap.dec(13,3);
}

View File

@@ -0,0 +1,12 @@
@EndUserText.label: 'Bottling plan rows'
@AbapCatalog.enhancement.category: #NOT_EXTENSIBLE
@AbapCatalog.tableCategory: #TRANSPARENT
@AbapCatalog.deliveryClass: #A
@AbapCatalog.dataMaintenance: #RESTRICTED
define table {{p}}botpln {
key client : abap.clnt not null;
key beer_type : abap.char(4) not null;
plan_pos : abap.int4;
total_hl : abap.dec(13,3);
order_count : abap.int4;
}

View File

@@ -1,12 +0,0 @@
FUNCTION {{p}}track_sample
IMPORTING
it_paths TYPE STANDARD TABLE
EXPORTING
et_fees TYPE STANDARD TABLE
EXCEPTIONS
invalid_distance
invalid_category.
CLEAR et_fees.
ENDFUNCTION.

View File

@@ -1,28 +0,0 @@
CLASS {{p}}track_types DEFINITION
PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES: ty_id TYPE n LENGTH 8,
ty_cat TYPE c LENGTH 2,
ty_flag TYPE c LENGTH 1,
ty_km TYPE p LENGTH 13 DECIMALS 2,
ty_money TYPE p LENGTH 13 DECIMALS 2,
BEGIN OF ty_path,
path_id TYPE ty_id,
train_cat TYPE ty_cat,
distance_km TYPE ty_km,
night_flag TYPE ty_flag,
END OF ty_path,
tt_path TYPE STANDARD TABLE OF ty_path WITH EMPTY KEY,
BEGIN OF ty_fee,
train_cat TYPE ty_cat,
path_count TYPE i,
total_km TYPE ty_km,
total_fee TYPE ty_money,
END OF ty_fee,
tt_fee TYPE STANDARD TABLE OF ty_fee WITH EMPTY KEY.
ENDCLASS.
CLASS {{p}}track_types IMPLEMENTATION.
ENDCLASS.

View File

@@ -1,69 +1,49 @@
# 1. Goal
A rail infrastructure manager charges a track access fee to train operating
companies. A billing program calls one function module for each operating day.
The function module calculates the fee of each train path and groups the result
by train category.
A brewery plans the bottling runs of its beer types. The planning program calls
one function module. The function module reads the open bottling orders, groups
them by beer type and stores the bottling plan.
# 2. Open questions
None.
# 3. Context
- Function group {{P}}FG_TRACK exists in package $TMP.
- Function module {{P}}TRACK_SAMPLE exists in this group. It shows the interface
that the billing program uses. Do not change it.
- The global class {{P}}TRACK_TYPES exists in package $TMP. It only holds the row
types of the input and of the result. Do not change it. Its public types are:
- TY_PATH: PATH_ID (type N, length 8), TRAIN_CAT (type C, length 2),
DISTANCE_KM (packed, length 13, 2 decimals), NIGHT_FLAG (type C, length 1).
- TT_PATH: standard table of TY_PATH.
- TY_FEE: TRAIN_CAT (type C, length 2), PATH_COUNT (type I), TOTAL_KM (packed,
length 13, 2 decimals), TOTAL_FEE (packed, length 13, 2 decimals).
- TT_FEE: standard table of TY_FEE.
- The billing program builds a standard table with the components of TY_PATH and
reads a standard table with the components of TY_FEE. The component names are
the interface between the billing program and the function module.
- Function group {{P}}FG_BOT exists in package $TMP.
- The database table {{P}}BOTORD exists in package $TMP. Its fields are
client (key), order_id (key, NUMC 10), beer_type (CHAR 4) and
volume_hl (DEC 13,3).
- The database table {{P}}BOTPLN exists in package $TMP. Its fields are
client (key), beer_type (key, CHAR 4), plan_pos (INT4), total_hl (DEC 13,3)
and order_count (INT4).
# 4. Contract
Create the function module {{P}}TRACK_FEE in function group {{P}}FG_TRACK with
this interface:
- Importing parameter IT_PATHS, type STANDARD TABLE.
- Exporting parameter ET_FEES, type STANDARD TABLE.
- Exceptions INVALID_DISTANCE and INVALID_CATEGORY.
The two table parameters are generic standard tables. Their row components are
fixed by TY_PATH and TY_FEE of {{P}}TRACK_TYPES.
Create the function module {{P}}BOT_PLAN in function group {{P}}FG_BOT.
The interface of the function module is:
- Importing parameter iv_min_hl TYPE decfloat34.
- Exporting parameter ev_total_hl TYPE decfloat34.
- Exporting parameter ev_plan_count TYPE int4.
# 5. Business rules
1. A train path with DISTANCE_KM less than or equal to zero is not valid. Raise
the exception INVALID_DISTANCE.
2. The rate of a train category is:
- IC: 1.20 per kilometre
- RG: 0.90 per kilometre
- FR: 0.60 per kilometre
- SP: 1.50 per kilometre
3. A train category that is not in rule 2 is not valid. Raise the exception
INVALID_CATEGORY.
4. The fee of one path is DISTANCE_KM multiplied by the rate of its train
category.
5. If NIGHT_FLAG is 'X', multiply the fee of the path by 1.20.
6. Round the fee of the path half up to two decimals.
7. The minimum fee of one path is 5.00. If the fee of the path is less than
5.00, use 5.00.
8. The result table has one row for each train category that occurs in the
input table.
9. PATH_COUNT is the number of paths of the category.
10. TOTAL_KM is the sum of DISTANCE_KM of the paths of the category.
11. TOTAL_FEE is the sum of the fees of the paths of the category.
12. Sort the result table in ascending order of TRAIN_CAT.
13. If the input table is empty, the result table is empty. Do not raise an
exception.
1. Read all rows of the table {{P}}BOTORD.
2. Ignore every order whose volume_hl is less than or equal to 0.
3. Group the other orders by beer_type. Compare beer types exactly.
4. For each group, total_hl is the sum of the volume_hl values of the orders
of the group, and order_count is the number of orders of the group.
5. Keep only the groups whose total_hl is greater than or equal to iv_min_hl.
6. Delete all rows of the table {{P}}BOTPLN and write one row per kept group.
7. The rows of {{P}}BOTPLN are numbered with plan_pos, starting at 1. The row
with the largest total_hl gets plan_pos 1. If two rows have the same
total_hl, the row with the smaller beer_type gets the smaller plan_pos.
8. ev_total_hl is the sum of the total_hl values of the kept groups. If no
group is kept, ev_total_hl is 0.
9. ev_plan_count is the number of kept groups.
10. {{P}}BOTPLN contains no rows other than the rows that rules 2 to 7 define.
# 6. Constraints
- Release target: 8.16.
- Coding standard: Clean ABAP. Keep methods below 40 statements. Pass large
parameters by reference.
- Out of scope: do not change {{P}}TRACK_SAMPLE or {{P}}TRACK_TYPES.
- Release target: SAP_BASIS 8.16.
- Use Clean ABAP. Keep every procedure below 40 statements.
- Out of scope: do not change the tables {{P}}BOTORD and {{P}}BOTPLN.
# 7. Acceptance
- The function module is active and has no syntax error.
- The function module {{P}}BOT_PLAN is active and has no syntax error.
- The hidden tests pass.
- Write ABAP Unit tests for the function module in a global test class.

View File

@@ -11,70 +11,69 @@
},
"seed": [
{
"type": "CLAS",
"name": "{{P}}TRACK_TYPES",
"file": "seed/track_types.clas.abap",
"description": "Row types for train paths and track fees"
"type": "TABL",
"name": "{{P}}BOTORD",
"file": "seed/botord.tabl.abap",
"description": "Bottling orders"
},
{
"type": "TABL",
"name": "{{P}}BOTPLN",
"file": "seed/botpln.tabl.abap",
"description": "Bottling plan rows"
},
{
"type": "FUGR",
"name": "{{P}}FG_TRACK",
"description": "Track access fee"
},
{
"type": "FUNC",
"name": "{{P}}TRACK_SAMPLE",
"functionGroup": "{{P}}FG_TRACK",
"file": "seed/track_sample.func.abap",
"description": "Sample module that shows the interface of the billing program"
"name": "{{P}}FG_BOT",
"description": "Bottling function group"
}
],
"contract": [
{
"type": "CLAS",
"name": "{{P}}TRACK_TYPES"
},
{
"type": "FUNC",
"name": "{{P}}TRACK_FEE",
"functionGroup": "{{P}}FG_TRACK",
"name": "{{P}}BOT_PLAN",
"functionGroup": "{{P}}FG_BOT",
"params": [
{
"name": "it_paths",
"type": "STANDARD TABLE"
"name": "iv_min_hl",
"type": "decfloat34"
},
{
"name": "et_fees",
"type": "STANDARD TABLE"
"name": "ev_total_hl",
"type": "decfloat34"
},
{
"name": "ev_plan_count",
"type": "int4"
}
]
}
],
"out_of_scope": [
"{{P}}TRACK_SAMPLE",
"{{P}}TRACK_TYPES"
"{{P}}BOTORD",
"{{P}}BOTPLN"
],
"hidden_tests": [
{
"type": "CLAS",
"name": "{{P}}TRACK_HIDDEN",
"file": "hidden/track_hidden.clas.abap",
"description": "Hidden tests for the track access fee"
"name": "{{P}}T14_HIDDEN",
"file": "hidden/t14_hidden.clas.abap",
"description": "T14 hidden tests"
}
],
"reference": [
{
"type": "FUNC",
"name": "{{P}}TRACK_FEE",
"functionGroup": "{{P}}FG_TRACK",
"file": "reference/track_fee.func.abap",
"description": "Track access fee module"
"name": "{{P}}BOT_PLAN",
"functionGroup": "{{P}}FG_BOT",
"file": "reference/bot_plan.func.abap",
"description": "Bottling plan module"
},
{
"type": "CLAS",
"name": "{{P}}TRACK_TEST",
"file": "reference/track_test.clas.abap",
"description": "Own tests for the track access fee"
"name": "{{P}}T14_TEST",
"file": "reference/t14_test.clas.abap",
"description": "T14 own tests"
}
],
"craft_checks": [