Pipeline controller: plan 2 (hard +, error +50 %), backlog throttle, 2 trajectory workers, stop rules, summaries every 50; budget reserve

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Kral
2026-10-05 13:35:54 +02:00
parent 1c4667a0fc
commit 13360292dd
781 changed files with 50211 additions and 22 deletions

View File

@@ -0,0 +1,73 @@
CLASS {{p}}oil_settlement DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES ty_restaurant TYPE c LENGTH 10.
TYPES:
BEGIN OF ty_collection,
restaurant TYPE ty_restaurant,
collected_on TYPE d,
litres TYPE i,
END OF ty_collection.
TYPES tt_collections TYPE STANDARD TABLE OF ty_collection WITH EMPTY KEY.
TYPES:
BEGIN OF ty_settlement,
restaurant TYPE ty_restaurant,
total_litres TYPE i,
round_count TYPE i,
last_collection TYPE d,
END OF ty_settlement.
TYPES tt_settlements TYPE SORTED TABLE OF ty_settlement WITH UNIQUE KEY restaurant.
METHODS settle
IMPORTING it_collections TYPE tt_collections
RETURNING VALUE(rt_settlements) TYPE tt_settlements.
PRIVATE SECTION.
TYPES:
BEGIN OF ty_buffer,
restaurant TYPE ty_restaurant,
total_litres TYPE i,
round_count TYPE i,
last_collection TYPE d,
END OF ty_buffer.
TYPES tt_buffer TYPE HASHED TABLE OF ty_buffer WITH UNIQUE KEY restaurant.
METHODS normalize
IMPORTING iv_restaurant TYPE ty_restaurant
RETURNING VALUE(rv_restaurant) TYPE ty_restaurant.
ENDCLASS.
CLASS {{p}}oil_settlement IMPLEMENTATION.
METHOD settle.
DATA lt_buffer TYPE tt_buffer.
LOOP AT it_collections INTO DATA(ls_collection).
DATA(lv_restaurant) = normalize( ls_collection-restaurant ).
IF lv_restaurant IS INITIAL AND ls_collection-litres <= 0.
CONTINUE.
ENDIF.
ASSIGN lt_buffer[ restaurant = lv_restaurant ] TO FIELD-SYMBOL(<ls_buffer>).
IF sy-subrc <> 0.
INSERT VALUE #( restaurant = lv_restaurant ) INTO TABLE lt_buffer ASSIGNING <ls_buffer>.
ENDIF.
<ls_buffer>-total_litres = <ls_buffer>-total_litres + ls_collection-litres.
<ls_buffer>-round_count = <ls_buffer>-round_count + 1.
IF ls_collection-collected_on > <ls_buffer>-last_collection.
<ls_buffer>-last_collection = ls_collection-collected_on.
ENDIF.
ENDLOOP.
rt_settlements = CORRESPONDING #( lt_buffer ).
ENDMETHOD.
METHOD normalize.
rv_restaurant = to_upper( replace( val = iv_restaurant sub = ` ` with = `` occ = 0 ) ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,73 @@
CLASS {{p}}oil_settlement DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES ty_restaurant TYPE c LENGTH 10.
TYPES:
BEGIN OF ty_collection,
restaurant TYPE ty_restaurant,
collected_on TYPE d,
litres TYPE i,
END OF ty_collection.
TYPES tt_collections TYPE STANDARD TABLE OF ty_collection WITH EMPTY KEY.
TYPES:
BEGIN OF ty_settlement,
restaurant TYPE ty_restaurant,
total_litres TYPE i,
round_count TYPE i,
last_collection TYPE d,
END OF ty_settlement.
TYPES tt_settlements TYPE SORTED TABLE OF ty_settlement WITH UNIQUE KEY restaurant.
METHODS settle
IMPORTING it_collections TYPE tt_collections
RETURNING VALUE(rt_settlements) TYPE tt_settlements.
PRIVATE SECTION.
TYPES:
BEGIN OF ty_buffer,
restaurant TYPE ty_restaurant,
total_litres TYPE i,
round_count TYPE i,
last_collection TYPE d,
END OF ty_buffer.
TYPES tt_buffer TYPE HASHED TABLE OF ty_buffer WITH UNIQUE KEY restaurant.
METHODS normalize
IMPORTING iv_restaurant TYPE ty_restaurant
RETURNING VALUE(rv_restaurant) TYPE ty_restaurant.
ENDCLASS.
CLASS {{p}}oil_settlement IMPLEMENTATION.
METHOD settle.
DATA lt_buffer TYPE tt_buffer.
LOOP AT it_collections INTO DATA(ls_collection).
DATA(lv_restaurant) = normalize( ls_collection-restaurant ).
IF lv_restaurant IS INITIAL OR ls_collection-litres > 0.
CONTINUE.
ENDIF.
ASSIGN lt_buffer[ restaurant = lv_restaurant ] TO FIELD-SYMBOL(<ls_buffer>).
IF sy-subrc <> 0.
INSERT VALUE #( restaurant = lv_restaurant ) INTO TABLE lt_buffer ASSIGNING <ls_buffer>.
ENDIF.
<ls_buffer>-total_litres = <ls_buffer>-total_litres + ls_collection-litres.
<ls_buffer>-round_count = <ls_buffer>-round_count + 1.
IF ls_collection-collected_on > <ls_buffer>-last_collection.
<ls_buffer>-last_collection = ls_collection-collected_on.
ENDIF.
ENDLOOP.
rt_settlements = CORRESPONDING #( lt_buffer ).
ENDMETHOD.
METHOD normalize.
rv_restaurant = to_upper( replace( val = iv_restaurant sub = ` ` with = `` occ = 0 ) ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,73 @@
CLASS {{p}}oil_settlement DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES ty_restaurant TYPE c LENGTH 10.
TYPES:
BEGIN OF ty_collection,
restaurant TYPE ty_restaurant,
collected_on TYPE d,
litres TYPE i,
END OF ty_collection.
TYPES tt_collections TYPE STANDARD TABLE OF ty_collection WITH EMPTY KEY.
TYPES:
BEGIN OF ty_settlement,
restaurant TYPE ty_restaurant,
total_litres TYPE i,
round_count TYPE i,
last_collection TYPE d,
END OF ty_settlement.
TYPES tt_settlements TYPE SORTED TABLE OF ty_settlement WITH UNIQUE KEY restaurant.
METHODS settle
IMPORTING it_collections TYPE tt_collections
RETURNING VALUE(rt_settlements) TYPE tt_settlements.
PRIVATE SECTION.
TYPES:
BEGIN OF ty_buffer,
restaurant TYPE ty_restaurant,
total_litres TYPE i,
round_count TYPE i,
last_collection TYPE d,
END OF ty_buffer.
TYPES tt_buffer TYPE HASHED TABLE OF ty_buffer WITH UNIQUE KEY restaurant.
METHODS normalize
IMPORTING iv_restaurant TYPE ty_restaurant
RETURNING VALUE(rv_restaurant) TYPE ty_restaurant.
ENDCLASS.
CLASS {{p}}oil_settlement IMPLEMENTATION.
METHOD settle.
DATA lt_buffer TYPE tt_buffer.
LOOP AT it_collections INTO DATA(ls_collection).
DATA(lv_restaurant) = normalize( ls_collection-restaurant ).
IF lv_restaurant IS INITIAL OR ls_collection-litres <= 0.
CONTINUE.
ENDIF.
ASSIGN lt_buffer[ restaurant = lv_restaurant ] TO FIELD-SYMBOL(<ls_buffer>).
IF sy-subrc <> 0.
INSERT VALUE #( restaurant = lv_restaurant ) INTO TABLE lt_buffer ASSIGNING <ls_buffer>.
ENDIF.
<ls_buffer>-total_litres = <ls_buffer>-total_litres - ls_collection-litres.
<ls_buffer>-round_count = <ls_buffer>-round_count + 1.
IF ls_collection-collected_on > <ls_buffer>-last_collection.
<ls_buffer>-last_collection = ls_collection-collected_on.
ENDIF.
ENDLOOP.
rt_settlements = CORRESPONDING #( lt_buffer ).
ENDMETHOD.
METHOD normalize.
rv_restaurant = to_upper( replace( val = iv_restaurant sub = ` ` with = `` occ = 0 ) ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,73 @@
CLASS {{p}}oil_settlement DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES ty_restaurant TYPE c LENGTH 10.
TYPES:
BEGIN OF ty_collection,
restaurant TYPE ty_restaurant,
collected_on TYPE d,
litres TYPE i,
END OF ty_collection.
TYPES tt_collections TYPE STANDARD TABLE OF ty_collection WITH EMPTY KEY.
TYPES:
BEGIN OF ty_settlement,
restaurant TYPE ty_restaurant,
total_litres TYPE i,
round_count TYPE i,
last_collection TYPE d,
END OF ty_settlement.
TYPES tt_settlements TYPE SORTED TABLE OF ty_settlement WITH UNIQUE KEY restaurant.
METHODS settle
IMPORTING it_collections TYPE tt_collections
RETURNING VALUE(rt_settlements) TYPE tt_settlements.
PRIVATE SECTION.
TYPES:
BEGIN OF ty_buffer,
restaurant TYPE ty_restaurant,
total_litres TYPE i,
round_count TYPE i,
last_collection TYPE d,
END OF ty_buffer.
TYPES tt_buffer TYPE HASHED TABLE OF ty_buffer WITH UNIQUE KEY restaurant.
METHODS normalize
IMPORTING iv_restaurant TYPE ty_restaurant
RETURNING VALUE(rv_restaurant) TYPE ty_restaurant.
ENDCLASS.
CLASS {{p}}oil_settlement IMPLEMENTATION.
METHOD settle.
DATA lt_buffer TYPE tt_buffer.
LOOP AT it_collections INTO DATA(ls_collection).
DATA(lv_restaurant) = normalize( ls_collection-restaurant ).
IF lv_restaurant IS INITIAL OR ls_collection-litres <= 0.
CONTINUE.
ENDIF.
ASSIGN lt_buffer[ restaurant = lv_restaurant ] TO FIELD-SYMBOL(<ls_buffer>).
IF sy-subrc <> 0.
INSERT VALUE #( restaurant = lv_restaurant ) INTO TABLE lt_buffer ASSIGNING <ls_buffer>.
ENDIF.
<ls_buffer>-total_litres = <ls_buffer>-total_litres + ls_collection-litres.
<ls_buffer>-round_count = <ls_buffer>-round_count - 1.
IF ls_collection-collected_on > <ls_buffer>-last_collection.
<ls_buffer>-last_collection = ls_collection-collected_on.
ENDIF.
ENDLOOP.
rt_settlements = CORRESPONDING #( lt_buffer ).
ENDMETHOD.
METHOD normalize.
rv_restaurant = to_upper( replace( val = iv_restaurant sub = ` ` with = `` occ = 0 ) ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,73 @@
CLASS {{p}}oil_settlement DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES ty_restaurant TYPE c LENGTH 10.
TYPES:
BEGIN OF ty_collection,
restaurant TYPE ty_restaurant,
collected_on TYPE d,
litres TYPE i,
END OF ty_collection.
TYPES tt_collections TYPE STANDARD TABLE OF ty_collection WITH EMPTY KEY.
TYPES:
BEGIN OF ty_settlement,
restaurant TYPE ty_restaurant,
total_litres TYPE i,
round_count TYPE i,
last_collection TYPE d,
END OF ty_settlement.
TYPES tt_settlements TYPE SORTED TABLE OF ty_settlement WITH UNIQUE KEY restaurant.
METHODS settle
IMPORTING it_collections TYPE tt_collections
RETURNING VALUE(rt_settlements) TYPE tt_settlements.
PRIVATE SECTION.
TYPES:
BEGIN OF ty_buffer,
restaurant TYPE ty_restaurant,
total_litres TYPE i,
round_count TYPE i,
last_collection TYPE d,
END OF ty_buffer.
TYPES tt_buffer TYPE HASHED TABLE OF ty_buffer WITH UNIQUE KEY restaurant.
METHODS normalize
IMPORTING iv_restaurant TYPE ty_restaurant
RETURNING VALUE(rv_restaurant) TYPE ty_restaurant.
ENDCLASS.
CLASS {{p}}oil_settlement IMPLEMENTATION.
METHOD settle.
DATA lt_buffer TYPE tt_buffer.
LOOP AT it_collections INTO DATA(ls_collection).
DATA(lv_restaurant) = normalize( ls_collection-restaurant ).
IF lv_restaurant IS INITIAL OR ls_collection-litres <= 0.
CONTINUE.
ENDIF.
ASSIGN lt_buffer[ restaurant = lv_restaurant ] TO FIELD-SYMBOL(<ls_buffer>).
IF sy-subrc <> 0.
INSERT VALUE #( restaurant = lv_restaurant ) INTO TABLE lt_buffer ASSIGNING <ls_buffer>.
ENDIF.
<ls_buffer>-total_litres = <ls_buffer>-total_litres + ls_collection-litres.
<ls_buffer>-round_count = <ls_buffer>-round_count + 1.
IF ls_collection-collected_on <= <ls_buffer>-last_collection.
<ls_buffer>-last_collection = ls_collection-collected_on.
ENDIF.
ENDLOOP.
rt_settlements = CORRESPONDING #( lt_buffer ).
ENDMETHOD.
METHOD normalize.
rv_restaurant = to_upper( replace( val = iv_restaurant sub = ` ` with = `` occ = 0 ) ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,19 @@
{
"id": "G1033",
"pool": "train",
"object_type": "CLAS",
"category": "C",
"attempts": [
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 5,
"killed": 5,
"ok": true
}
}
],
"accepted": true
}

View File

@@ -0,0 +1,134 @@
CLASS {{p}}t_oil_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA mo_cut TYPE REF TO {{p}}oil_settlement.
METHODS setup.
METHODS sums_same_restaurant FOR TESTING.
METHODS separates_restaurants FOR TESTING.
METHODS ignores_zero_litres FOR TESTING.
METHODS ignores_negative_litres FOR TESTING.
METHODS ignores_empty_name FOR TESTING.
METHODS normalises_name FOR TESTING.
METHODS keeps_latest_date FOR TESTING.
METHODS sorts_by_restaurant FOR TESTING.
METHODS empty_input FOR TESTING.
METHODS all_invalid FOR TESTING.
METHODS find_row
IMPORTING it_result TYPE {{p}}oil_settlement=>tt_settlements
iv_restaurant TYPE {{p}}oil_settlement=>ty_restaurant
RETURNING VALUE(rs_row) TYPE {{p}}oil_settlement=>ty_settlement.
ENDCLASS.
CLASS {{p}}t_oil_hidden IMPLEMENTATION.
METHOD setup.
mo_cut = NEW {{p}}oil_settlement( ).
ENDMETHOD.
METHOD sums_same_restaurant.
DATA(lt_result) = mo_cut->settle( VALUE #(
( restaurant = 'R001' collected_on = '20240110' litres = 100 )
( restaurant = 'R001' collected_on = '20240112' litres = 50 ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
DATA(ls_row) = find_row( it_result = lt_result iv_restaurant = 'R001' ).
cl_abap_unit_assert=>assert_equals( exp = 150 act = ls_row-total_litres ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-round_count ).
ENDMETHOD.
METHOD separates_restaurants.
DATA(lt_result) = mo_cut->settle( VALUE #(
( restaurant = 'R001' collected_on = '20240110' litres = 100 )
( restaurant = 'R002' collected_on = '20240110' litres = 200 ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ).
DATA(ls_r001) = find_row( it_result = lt_result iv_restaurant = 'R001' ).
cl_abap_unit_assert=>assert_equals( exp = 100 act = ls_r001-total_litres ).
DATA(ls_r002) = find_row( it_result = lt_result iv_restaurant = 'R002' ).
cl_abap_unit_assert=>assert_equals( exp = 200 act = ls_r002-total_litres ).
ENDMETHOD.
METHOD ignores_zero_litres.
DATA(lt_result) = mo_cut->settle( VALUE #(
( restaurant = 'R001' collected_on = '20240110' litres = 0 )
( restaurant = 'R002' collected_on = '20240110' litres = 30 ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
DATA(ls_row) = find_row( it_result = lt_result iv_restaurant = 'R002' ).
cl_abap_unit_assert=>assert_equals( exp = 30 act = ls_row-total_litres ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_row-round_count ).
ENDMETHOD.
METHOD ignores_negative_litres.
DATA(lt_result) = mo_cut->settle( VALUE #(
( restaurant = 'R001' collected_on = '20240110' litres = -5 )
( restaurant = 'R001' collected_on = '20240111' litres = 40 ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
DATA(ls_row) = find_row( it_result = lt_result iv_restaurant = 'R001' ).
cl_abap_unit_assert=>assert_equals( exp = 40 act = ls_row-total_litres ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_row-round_count ).
ENDMETHOD.
METHOD ignores_empty_name.
DATA(lt_result) = mo_cut->settle( VALUE #(
( restaurant = space collected_on = '20240110' litres = 100 )
( restaurant = ' ' collected_on = '20240110' litres = 100 ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ).
ENDMETHOD.
METHOD normalises_name.
DATA(lt_result) = mo_cut->settle( VALUE #(
( restaurant = 'r001' collected_on = '20240110' litres = 100 )
( restaurant = ' R001 ' collected_on = '20240111' litres = 50 ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
DATA(ls_row) = find_row( it_result = lt_result iv_restaurant = 'R001' ).
cl_abap_unit_assert=>assert_equals( exp = 150 act = ls_row-total_litres ).
cl_abap_unit_assert=>assert_equals( exp = 'R001' act = ls_row-restaurant ).
ENDMETHOD.
METHOD keeps_latest_date.
DATA(lt_result) = mo_cut->settle( VALUE #(
( restaurant = 'R001' collected_on = '20240110' litres = 10 )
( restaurant = 'R001' collected_on = '20240305' litres = 10 )
( restaurant = 'R001' collected_on = '20240201' litres = 10 ) ) ).
DATA lv_expected TYPE d.
lv_expected = '20240305'.
DATA(ls_row) = find_row( it_result = lt_result iv_restaurant = 'R001' ).
cl_abap_unit_assert=>assert_equals( exp = lv_expected act = ls_row-last_collection ).
ENDMETHOD.
METHOD sorts_by_restaurant.
DATA(lt_result) = mo_cut->settle( VALUE #(
( restaurant = 'R003' collected_on = '20240110' litres = 10 )
( restaurant = 'R001' collected_on = '20240110' litres = 10 )
( restaurant = 'R002' collected_on = '20240110' litres = 10 ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_result ) ).
DATA lv_order TYPE string.
LOOP AT lt_result INTO DATA(ls_row).
lv_order = lv_order && ls_row-restaurant+0(4).
ENDLOOP.
cl_abap_unit_assert=>assert_equals( exp = 'R001R002R003' act = lv_order ).
ENDMETHOD.
METHOD empty_input.
DATA lt_empty TYPE {{p}}oil_settlement=>tt_collections.
DATA(lt_result) = mo_cut->settle( lt_empty ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ).
ENDMETHOD.
METHOD all_invalid.
DATA(lt_result) = mo_cut->settle( VALUE #(
( restaurant = 'R001' collected_on = '20240110' litres = 0 )
( restaurant = space collected_on = '20240110' litres = 50 ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ).
ENDMETHOD.
METHOD find_row.
DATA ls_row TYPE {{p}}oil_settlement=>ty_settlement.
DATA lv_found TYPE abap_bool.
LOOP AT it_result INTO ls_row WHERE restaurant = iv_restaurant.
rs_row = ls_row.
lv_found = abap_true.
EXIT.
ENDLOOP.
cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_found ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,70 @@
{
"task": "G1033",
"mutants": [
{
"object": "{{P}}OIL_SETTLEMENT",
"mutant": "line 51: OR -> AND (logic)",
"status": "killed",
"hidden": "6/10",
"failed_tests": [
"ALL_INVALID",
"IGNORES_EMPTY_NAME",
"IGNORES_NEGATIVE_LITRES",
"IGNORES_ZERO_LITRES"
]
},
{
"object": "{{P}}OIL_SETTLEMENT",
"mutant": "line 51: <= -> > (rel)",
"status": "killed",
"hidden": "2/10",
"failed_tests": [
"ALL_INVALID",
"IGNORES_NEGATIVE_LITRES",
"IGNORES_ZERO_LITRES",
"KEEPS_LATEST_DATE",
"NORMALISES_NAME",
"SEPARATES_RESTAURANTS",
"SORTS_BY_RESTAURANT",
"SUMS_SAME_RESTAURANT"
]
},
{
"object": "{{P}}OIL_SETTLEMENT",
"mutant": "line 60: + -> - (arith)",
"status": "killed",
"hidden": "5/10",
"failed_tests": [
"IGNORES_NEGATIVE_LITRES",
"IGNORES_ZERO_LITRES",
"NORMALISES_NAME",
"SEPARATES_RESTAURANTS",
"SUMS_SAME_RESTAURANT"
]
},
{
"object": "{{P}}OIL_SETTLEMENT",
"mutant": "line 61: + -> - (arith)",
"status": "killed",
"hidden": "7/10",
"failed_tests": [
"IGNORES_NEGATIVE_LITRES",
"IGNORES_ZERO_LITRES",
"SUMS_SAME_RESTAURANT"
]
},
{
"object": "{{P}}OIL_SETTLEMENT",
"mutant": "line 62: > -> <= (rel)",
"status": "killed",
"hidden": "9/10",
"failed_tests": [
"KEEPS_LATEST_DATE"
]
}
],
"valid": 5,
"killed": 5,
"kill_rate": 1.0,
"ok": true
}

View File

@@ -0,0 +1,73 @@
CLASS {{p}}oil_settlement DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES ty_restaurant TYPE c LENGTH 10.
TYPES:
BEGIN OF ty_collection,
restaurant TYPE ty_restaurant,
collected_on TYPE d,
litres TYPE i,
END OF ty_collection.
TYPES tt_collections TYPE STANDARD TABLE OF ty_collection WITH EMPTY KEY.
TYPES:
BEGIN OF ty_settlement,
restaurant TYPE ty_restaurant,
total_litres TYPE i,
round_count TYPE i,
last_collection TYPE d,
END OF ty_settlement.
TYPES tt_settlements TYPE SORTED TABLE OF ty_settlement WITH UNIQUE KEY restaurant.
METHODS settle
IMPORTING it_collections TYPE tt_collections
RETURNING VALUE(rt_settlements) TYPE tt_settlements.
PRIVATE SECTION.
TYPES:
BEGIN OF ty_buffer,
restaurant TYPE ty_restaurant,
total_litres TYPE i,
round_count TYPE i,
last_collection TYPE d,
END OF ty_buffer.
TYPES tt_buffer TYPE HASHED TABLE OF ty_buffer WITH UNIQUE KEY restaurant.
METHODS normalize
IMPORTING iv_restaurant TYPE ty_restaurant
RETURNING VALUE(rv_restaurant) TYPE ty_restaurant.
ENDCLASS.
CLASS {{p}}oil_settlement IMPLEMENTATION.
METHOD settle.
DATA lt_buffer TYPE tt_buffer.
LOOP AT it_collections INTO DATA(ls_collection).
DATA(lv_restaurant) = normalize( ls_collection-restaurant ).
IF lv_restaurant IS INITIAL OR ls_collection-litres <= 0.
CONTINUE.
ENDIF.
ASSIGN lt_buffer[ restaurant = lv_restaurant ] TO FIELD-SYMBOL(<ls_buffer>).
IF sy-subrc <> 0.
INSERT VALUE #( restaurant = lv_restaurant ) INTO TABLE lt_buffer ASSIGNING <ls_buffer>.
ENDIF.
<ls_buffer>-total_litres = <ls_buffer>-total_litres + ls_collection-litres.
<ls_buffer>-round_count = <ls_buffer>-round_count + 1.
IF ls_collection-collected_on > <ls_buffer>-last_collection.
<ls_buffer>-last_collection = ls_collection-collected_on.
ENDIF.
ENDLOOP.
rt_settlements = CORRESPONDING #( lt_buffer ).
ENDMETHOD.
METHOD normalize.
rv_restaurant = to_upper( replace( val = iv_restaurant sub = ` ` with = `` occ = 0 ) ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,86 @@
CLASS ltc_oil_settlement DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA mo_cut TYPE REF TO {{p}}oil_settlement.
METHODS setup.
METHODS sums_same_restaurant FOR TESTING.
METHODS ignores_invalid_records FOR TESTING.
METHODS normalises_restaurant FOR TESTING.
METHODS keeps_latest_date FOR TESTING.
METHODS sorts_result FOR TESTING.
METHODS find_row
IMPORTING it_result TYPE {{p}}oil_settlement=>tt_settlements
iv_restaurant TYPE {{p}}oil_settlement=>ty_restaurant
RETURNING VALUE(rs_row) TYPE {{p}}oil_settlement=>ty_settlement.
ENDCLASS.
CLASS ltc_oil_settlement IMPLEMENTATION.
METHOD setup.
mo_cut = NEW {{p}}oil_settlement( ).
ENDMETHOD.
METHOD sums_same_restaurant.
DATA(lt_result) = mo_cut->settle( VALUE #(
( restaurant = 'R001' collected_on = '20240110' litres = 100 )
( restaurant = 'R001' collected_on = '20240115' litres = 50 ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
DATA(ls_row) = find_row( it_result = lt_result iv_restaurant = 'R001' ).
cl_abap_unit_assert=>assert_equals( exp = 150 act = ls_row-total_litres ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-round_count ).
ENDMETHOD.
METHOD ignores_invalid_records.
DATA(lt_result) = mo_cut->settle( VALUE #(
( restaurant = 'R001' collected_on = '20240110' litres = 0 )
( restaurant = space collected_on = '20240110' litres = 20 )
( restaurant = 'R002' collected_on = '20240110' litres = 30 ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
DATA(ls_row) = find_row( it_result = lt_result iv_restaurant = 'R002' ).
cl_abap_unit_assert=>assert_equals( exp = 30 act = ls_row-total_litres ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_row-round_count ).
ENDMETHOD.
METHOD normalises_restaurant.
DATA(lt_result) = mo_cut->settle( VALUE #(
( restaurant = 'r001' collected_on = '20240110' litres = 10 )
( restaurant = ' R001 ' collected_on = '20240111' litres = 20 ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
DATA(ls_row) = find_row( it_result = lt_result iv_restaurant = 'R001' ).
cl_abap_unit_assert=>assert_equals( exp = 30 act = ls_row-total_litres ).
cl_abap_unit_assert=>assert_equals( exp = 'R001' act = ls_row-restaurant ).
ENDMETHOD.
METHOD keeps_latest_date.
DATA(lt_result) = mo_cut->settle( VALUE #(
( restaurant = 'R001' collected_on = '20240110' litres = 10 )
( restaurant = 'R001' collected_on = '20240305' litres = 10 ) ) ).
DATA lv_expected TYPE d.
lv_expected = '20240305'.
DATA(ls_row) = find_row( it_result = lt_result iv_restaurant = 'R001' ).
cl_abap_unit_assert=>assert_equals( exp = lv_expected act = ls_row-last_collection ).
ENDMETHOD.
METHOD sorts_result.
DATA(lt_result) = mo_cut->settle( VALUE #(
( restaurant = 'R003' collected_on = '20240110' litres = 10 )
( restaurant = 'R001' collected_on = '20240110' litres = 10 )
( restaurant = 'R002' collected_on = '20240110' litres = 10 ) ) ).
DATA lv_order TYPE string.
LOOP AT lt_result INTO DATA(ls_row).
lv_order = lv_order && ls_row-restaurant+0(4).
ENDLOOP.
cl_abap_unit_assert=>assert_equals( exp = 'R001R002R003' act = lv_order ).
ENDMETHOD.
METHOD find_row.
DATA ls_row TYPE {{p}}oil_settlement=>ty_settlement.
DATA lv_found TYPE abap_bool.
LOOP AT it_result INTO ls_row WHERE restaurant = iv_restaurant.
rs_row = ls_row.
lv_found = abap_true.
EXIT.
ENDLOOP.
cl_abap_unit_assert=>assert_equals( exp = abap_true act = lv_found ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,68 @@
# 1. Goal
A company collects used cooking oil from restaurants. The company pays the
restaurants for the oil. Each collection is recorded with the restaurant, the
collection date and the volume in litres. A settlement program calls the class
once for each settlement run. The class returns one row for each restaurant.
# 2. Open questions
None.
# 3. Context
- The class {{P}}OIL_SETTLEMENT does not exist. Create it in package $TMP.
- The caller sends a list of collection records. One restaurant can appear in
more than one record.
- The class does not read the database. All data comes from the caller.
# 4. Contract
- Create the class {{P}}OIL_SETTLEMENT in package $TMP.
- The class is public and final. It has a public constructor without parameters.
- The class has these public types:
- ty_restaurant: character, length 10.
- ty_collection: structure with the components restaurant (ty_restaurant),
collected_on (date) and litres (integer).
- tt_collections: table of ty_collection. The table can hold more than one row
for the same restaurant.
- ty_settlement: structure with the components restaurant (ty_restaurant),
total_litres (integer), round_count (integer) and last_collection (date).
- tt_settlements: table of ty_settlement.
- The class has this public method:
settle
IMPORTING it_collections TYPE tt_collections
RETURNING VALUE(rt_settlements) TYPE tt_settlements.
- Do not add other public methods.
# 5. Business rules
1. Two restaurant names are the same when they are equal after you remove all
spaces and convert the letters to upper case. A restaurant is empty when it
contains only spaces.
2. A collection record is valid when its restaurant is not empty and its volume
is greater than zero.
3. Ignore every record that is not valid. An ignored record does not change a
total and it does not increase a counter.
4. The result has exactly one row for each restaurant that has at least one
valid record.
5. A result row has these values:
- restaurant: the name of the valid records, without spaces and in upper case.
- total_litres: the sum of the volumes of the valid records.
- round_count: the number of the valid records.
- last_collection: the latest collection date of the valid records.
6. The result is sorted in ascending order of restaurant.
7. If there is no valid record, the result is empty.
# 6. Constraints
- Release target: SAP_BASIS 816 (ABAP Platform 2025).
- Coding standards: Clean ABAP. Method length below 40 statements. No global
variables. No comment that restates the code.
- Out of scope: no database access, no ALV, no dynpro, no RAP.
# 7. Acceptance
- The class is active and has no syntax error.
- The hidden tests pass.
- Write your own ABAP Unit tests for the class.

View File

@@ -0,0 +1,42 @@
{
"id": "G1033",
"category": "C",
"object_type": "CLAS",
"difficulty": 2,
"release_target": "v816",
"expected_outcome": "implement",
"budget": {
"max_tool_calls": 70,
"max_activations": 20
},
"seed": [],
"contract": [
{
"type": "CLAS",
"name": "{{P}}OIL_SETTLEMENT"
}
],
"out_of_scope": [],
"hidden_tests": [
{
"type": "CLAS",
"name": "{{P}}T_OIL_HIDDEN",
"file": "hidden/t_oil_hidden.clas.abap",
"description": "Hidden ABAP Unit tests for the used cooking oil settlement class"
}
],
"reference": [
{
"type": "CLAS",
"name": "{{P}}OIL_SETTLEMENT",
"file": "reference/oil_settlement.clas.abap",
"description": "Settlement class that aggregates oil collections per restaurant",
"testclasses_file": "reference/oil_settlement.testclasses.abap"
}
],
"craft_checks": [
"method_length",
"table_kind",
"access_path"
]
}