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,92 @@
CLASS {{p}}quote_merger DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES ty_price TYPE p LENGTH 11 DECIMALS 2.
TYPES:
BEGIN OF ty_quote,
material TYPE c LENGTH 18,
supplier TYPE c LENGTH 10,
price TYPE ty_price,
END OF ty_quote.
TYPES tt_quote TYPE STANDARD TABLE OF ty_quote WITH DEFAULT KEY.
TYPES:
BEGIN OF ty_result,
material TYPE c LENGTH 18,
supplier TYPE c LENGTH 10,
price TYPE ty_price,
quote_count TYPE i,
END OF ty_result.
TYPES tt_result TYPE STANDARD TABLE OF ty_result WITH DEFAULT KEY.
METHODS merge
IMPORTING it_quotes_a TYPE tt_quote
it_quotes_b TYPE tt_quote
EXPORTING et_result TYPE tt_result
ev_rejected TYPE i.
PRIVATE SECTION.
TYPES tt_aggregate TYPE HASHED TABLE OF ty_result WITH UNIQUE KEY material.
METHODS collect
IMPORTING it_quotes TYPE tt_quote
CHANGING ct_aggregate TYPE tt_aggregate
cv_rejected TYPE i.
METHODS is_valid
IMPORTING is_quote TYPE ty_quote
RETURNING VALUE(rv_valid) TYPE abap_bool.
METHODS update_aggregate
IMPORTING is_quote TYPE ty_quote
CHANGING ct_aggregate TYPE tt_aggregate.
ENDCLASS.
CLASS {{p}}quote_merger IMPLEMENTATION.
METHOD merge.
DATA lt_aggregate TYPE tt_aggregate.
DATA lv_rejected TYPE i.
collect( EXPORTING it_quotes = it_quotes_a
CHANGING ct_aggregate = lt_aggregate
cv_rejected = lv_rejected ).
collect( EXPORTING it_quotes = it_quotes_b
CHANGING ct_aggregate = lt_aggregate
cv_rejected = lv_rejected ).
et_result = lt_aggregate.
SORT et_result BY material.
ev_rejected = lv_rejected.
ENDMETHOD.
METHOD collect.
LOOP AT it_quotes INTO DATA(ls_quote).
IF is_valid( ls_quote ) <> abap_false.
cv_rejected = cv_rejected + 1.
CONTINUE.
ENDIF.
update_aggregate( EXPORTING is_quote = ls_quote
CHANGING ct_aggregate = ct_aggregate ).
ENDLOOP.
ENDMETHOD.
METHOD is_valid.
rv_valid = xsdbool( is_quote-material IS NOT INITIAL AND is_quote-price > 0 ).
ENDMETHOD.
METHOD update_aggregate.
READ TABLE ct_aggregate ASSIGNING FIELD-SYMBOL(<ls_aggregate>)
WITH TABLE KEY material = is_quote-material.
IF sy-subrc <> 0.
INSERT VALUE #( material = is_quote-material
supplier = is_quote-supplier
price = is_quote-price
quote_count = 1 ) INTO TABLE ct_aggregate.
RETURN.
ENDIF.
<ls_aggregate>-quote_count = <ls_aggregate>-quote_count + 1.
IF is_quote-price < <ls_aggregate>-price
OR ( is_quote-price = <ls_aggregate>-price
AND is_quote-supplier < <ls_aggregate>-supplier ).
<ls_aggregate>-price = is_quote-price.
<ls_aggregate>-supplier = is_quote-supplier.
ENDIF.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,92 @@
CLASS {{p}}quote_merger DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES ty_price TYPE p LENGTH 11 DECIMALS 2.
TYPES:
BEGIN OF ty_quote,
material TYPE c LENGTH 18,
supplier TYPE c LENGTH 10,
price TYPE ty_price,
END OF ty_quote.
TYPES tt_quote TYPE STANDARD TABLE OF ty_quote WITH DEFAULT KEY.
TYPES:
BEGIN OF ty_result,
material TYPE c LENGTH 18,
supplier TYPE c LENGTH 10,
price TYPE ty_price,
quote_count TYPE i,
END OF ty_result.
TYPES tt_result TYPE STANDARD TABLE OF ty_result WITH DEFAULT KEY.
METHODS merge
IMPORTING it_quotes_a TYPE tt_quote
it_quotes_b TYPE tt_quote
EXPORTING et_result TYPE tt_result
ev_rejected TYPE i.
PRIVATE SECTION.
TYPES tt_aggregate TYPE HASHED TABLE OF ty_result WITH UNIQUE KEY material.
METHODS collect
IMPORTING it_quotes TYPE tt_quote
CHANGING ct_aggregate TYPE tt_aggregate
cv_rejected TYPE i.
METHODS is_valid
IMPORTING is_quote TYPE ty_quote
RETURNING VALUE(rv_valid) TYPE abap_bool.
METHODS update_aggregate
IMPORTING is_quote TYPE ty_quote
CHANGING ct_aggregate TYPE tt_aggregate.
ENDCLASS.
CLASS {{p}}quote_merger IMPLEMENTATION.
METHOD merge.
DATA lt_aggregate TYPE tt_aggregate.
DATA lv_rejected TYPE i.
collect( EXPORTING it_quotes = it_quotes_a
CHANGING ct_aggregate = lt_aggregate
cv_rejected = lv_rejected ).
collect( EXPORTING it_quotes = it_quotes_b
CHANGING ct_aggregate = lt_aggregate
cv_rejected = lv_rejected ).
et_result = lt_aggregate.
SORT et_result BY material.
ev_rejected = lv_rejected.
ENDMETHOD.
METHOD collect.
LOOP AT it_quotes INTO DATA(ls_quote).
IF is_valid( ls_quote ) = abap_false.
cv_rejected = cv_rejected - 1.
CONTINUE.
ENDIF.
update_aggregate( EXPORTING is_quote = ls_quote
CHANGING ct_aggregate = ct_aggregate ).
ENDLOOP.
ENDMETHOD.
METHOD is_valid.
rv_valid = xsdbool( is_quote-material IS NOT INITIAL AND is_quote-price > 0 ).
ENDMETHOD.
METHOD update_aggregate.
READ TABLE ct_aggregate ASSIGNING FIELD-SYMBOL(<ls_aggregate>)
WITH TABLE KEY material = is_quote-material.
IF sy-subrc <> 0.
INSERT VALUE #( material = is_quote-material
supplier = is_quote-supplier
price = is_quote-price
quote_count = 1 ) INTO TABLE ct_aggregate.
RETURN.
ENDIF.
<ls_aggregate>-quote_count = <ls_aggregate>-quote_count + 1.
IF is_quote-price < <ls_aggregate>-price
OR ( is_quote-price = <ls_aggregate>-price
AND is_quote-supplier < <ls_aggregate>-supplier ).
<ls_aggregate>-price = is_quote-price.
<ls_aggregate>-supplier = is_quote-supplier.
ENDIF.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,92 @@
CLASS {{p}}quote_merger DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES ty_price TYPE p LENGTH 11 DECIMALS 2.
TYPES:
BEGIN OF ty_quote,
material TYPE c LENGTH 18,
supplier TYPE c LENGTH 10,
price TYPE ty_price,
END OF ty_quote.
TYPES tt_quote TYPE STANDARD TABLE OF ty_quote WITH DEFAULT KEY.
TYPES:
BEGIN OF ty_result,
material TYPE c LENGTH 18,
supplier TYPE c LENGTH 10,
price TYPE ty_price,
quote_count TYPE i,
END OF ty_result.
TYPES tt_result TYPE STANDARD TABLE OF ty_result WITH DEFAULT KEY.
METHODS merge
IMPORTING it_quotes_a TYPE tt_quote
it_quotes_b TYPE tt_quote
EXPORTING et_result TYPE tt_result
ev_rejected TYPE i.
PRIVATE SECTION.
TYPES tt_aggregate TYPE HASHED TABLE OF ty_result WITH UNIQUE KEY material.
METHODS collect
IMPORTING it_quotes TYPE tt_quote
CHANGING ct_aggregate TYPE tt_aggregate
cv_rejected TYPE i.
METHODS is_valid
IMPORTING is_quote TYPE ty_quote
RETURNING VALUE(rv_valid) TYPE abap_bool.
METHODS update_aggregate
IMPORTING is_quote TYPE ty_quote
CHANGING ct_aggregate TYPE tt_aggregate.
ENDCLASS.
CLASS {{p}}quote_merger IMPLEMENTATION.
METHOD merge.
DATA lt_aggregate TYPE tt_aggregate.
DATA lv_rejected TYPE i.
collect( EXPORTING it_quotes = it_quotes_a
CHANGING ct_aggregate = lt_aggregate
cv_rejected = lv_rejected ).
collect( EXPORTING it_quotes = it_quotes_b
CHANGING ct_aggregate = lt_aggregate
cv_rejected = lv_rejected ).
et_result = lt_aggregate.
SORT et_result BY material.
ev_rejected = lv_rejected.
ENDMETHOD.
METHOD collect.
LOOP AT it_quotes INTO DATA(ls_quote).
IF is_valid( ls_quote ) = abap_false.
cv_rejected = cv_rejected + 1.
CONTINUE.
ENDIF.
update_aggregate( EXPORTING is_quote = ls_quote
CHANGING ct_aggregate = ct_aggregate ).
ENDLOOP.
ENDMETHOD.
METHOD is_valid.
rv_valid = xsdbool( is_quote-material IS NOT INITIAL OR is_quote-price > 0 ).
ENDMETHOD.
METHOD update_aggregate.
READ TABLE ct_aggregate ASSIGNING FIELD-SYMBOL(<ls_aggregate>)
WITH TABLE KEY material = is_quote-material.
IF sy-subrc <> 0.
INSERT VALUE #( material = is_quote-material
supplier = is_quote-supplier
price = is_quote-price
quote_count = 1 ) INTO TABLE ct_aggregate.
RETURN.
ENDIF.
<ls_aggregate>-quote_count = <ls_aggregate>-quote_count + 1.
IF is_quote-price < <ls_aggregate>-price
OR ( is_quote-price = <ls_aggregate>-price
AND is_quote-supplier < <ls_aggregate>-supplier ).
<ls_aggregate>-price = is_quote-price.
<ls_aggregate>-supplier = is_quote-supplier.
ENDIF.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,92 @@
CLASS {{p}}quote_merger DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES ty_price TYPE p LENGTH 11 DECIMALS 2.
TYPES:
BEGIN OF ty_quote,
material TYPE c LENGTH 18,
supplier TYPE c LENGTH 10,
price TYPE ty_price,
END OF ty_quote.
TYPES tt_quote TYPE STANDARD TABLE OF ty_quote WITH DEFAULT KEY.
TYPES:
BEGIN OF ty_result,
material TYPE c LENGTH 18,
supplier TYPE c LENGTH 10,
price TYPE ty_price,
quote_count TYPE i,
END OF ty_result.
TYPES tt_result TYPE STANDARD TABLE OF ty_result WITH DEFAULT KEY.
METHODS merge
IMPORTING it_quotes_a TYPE tt_quote
it_quotes_b TYPE tt_quote
EXPORTING et_result TYPE tt_result
ev_rejected TYPE i.
PRIVATE SECTION.
TYPES tt_aggregate TYPE HASHED TABLE OF ty_result WITH UNIQUE KEY material.
METHODS collect
IMPORTING it_quotes TYPE tt_quote
CHANGING ct_aggregate TYPE tt_aggregate
cv_rejected TYPE i.
METHODS is_valid
IMPORTING is_quote TYPE ty_quote
RETURNING VALUE(rv_valid) TYPE abap_bool.
METHODS update_aggregate
IMPORTING is_quote TYPE ty_quote
CHANGING ct_aggregate TYPE tt_aggregate.
ENDCLASS.
CLASS {{p}}quote_merger IMPLEMENTATION.
METHOD merge.
DATA lt_aggregate TYPE tt_aggregate.
DATA lv_rejected TYPE i.
collect( EXPORTING it_quotes = it_quotes_a
CHANGING ct_aggregate = lt_aggregate
cv_rejected = lv_rejected ).
collect( EXPORTING it_quotes = it_quotes_b
CHANGING ct_aggregate = lt_aggregate
cv_rejected = lv_rejected ).
et_result = lt_aggregate.
SORT et_result BY material.
ev_rejected = lv_rejected.
ENDMETHOD.
METHOD collect.
LOOP AT it_quotes INTO DATA(ls_quote).
IF is_valid( ls_quote ) = abap_false.
cv_rejected = cv_rejected + 1.
CONTINUE.
ENDIF.
update_aggregate( EXPORTING is_quote = ls_quote
CHANGING ct_aggregate = ct_aggregate ).
ENDLOOP.
ENDMETHOD.
METHOD is_valid.
rv_valid = xsdbool( is_quote-material IS NOT INITIAL AND is_quote-price > 0 ).
ENDMETHOD.
METHOD update_aggregate.
READ TABLE ct_aggregate ASSIGNING FIELD-SYMBOL(<ls_aggregate>)
WITH TABLE KEY material = is_quote-material.
IF sy-subrc <> 0.
INSERT VALUE #( material = is_quote-material
supplier = is_quote-supplier
price = is_quote-price
quote_count = 2 ) INTO TABLE ct_aggregate.
RETURN.
ENDIF.
<ls_aggregate>-quote_count = <ls_aggregate>-quote_count + 1.
IF is_quote-price < <ls_aggregate>-price
OR ( is_quote-price = <ls_aggregate>-price
AND is_quote-supplier < <ls_aggregate>-supplier ).
<ls_aggregate>-price = is_quote-price.
<ls_aggregate>-supplier = is_quote-supplier.
ENDIF.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,92 @@
CLASS {{p}}quote_merger DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES ty_price TYPE p LENGTH 11 DECIMALS 2.
TYPES:
BEGIN OF ty_quote,
material TYPE c LENGTH 18,
supplier TYPE c LENGTH 10,
price TYPE ty_price,
END OF ty_quote.
TYPES tt_quote TYPE STANDARD TABLE OF ty_quote WITH DEFAULT KEY.
TYPES:
BEGIN OF ty_result,
material TYPE c LENGTH 18,
supplier TYPE c LENGTH 10,
price TYPE ty_price,
quote_count TYPE i,
END OF ty_result.
TYPES tt_result TYPE STANDARD TABLE OF ty_result WITH DEFAULT KEY.
METHODS merge
IMPORTING it_quotes_a TYPE tt_quote
it_quotes_b TYPE tt_quote
EXPORTING et_result TYPE tt_result
ev_rejected TYPE i.
PRIVATE SECTION.
TYPES tt_aggregate TYPE HASHED TABLE OF ty_result WITH UNIQUE KEY material.
METHODS collect
IMPORTING it_quotes TYPE tt_quote
CHANGING ct_aggregate TYPE tt_aggregate
cv_rejected TYPE i.
METHODS is_valid
IMPORTING is_quote TYPE ty_quote
RETURNING VALUE(rv_valid) TYPE abap_bool.
METHODS update_aggregate
IMPORTING is_quote TYPE ty_quote
CHANGING ct_aggregate TYPE tt_aggregate.
ENDCLASS.
CLASS {{p}}quote_merger IMPLEMENTATION.
METHOD merge.
DATA lt_aggregate TYPE tt_aggregate.
DATA lv_rejected TYPE i.
collect( EXPORTING it_quotes = it_quotes_a
CHANGING ct_aggregate = lt_aggregate
cv_rejected = lv_rejected ).
collect( EXPORTING it_quotes = it_quotes_b
CHANGING ct_aggregate = lt_aggregate
cv_rejected = lv_rejected ).
et_result = lt_aggregate.
SORT et_result BY material.
ev_rejected = lv_rejected.
ENDMETHOD.
METHOD collect.
LOOP AT it_quotes INTO DATA(ls_quote).
IF is_valid( ls_quote ) = abap_false.
cv_rejected = cv_rejected + 1.
CONTINUE.
ENDIF.
update_aggregate( EXPORTING is_quote = ls_quote
CHANGING ct_aggregate = ct_aggregate ).
ENDLOOP.
ENDMETHOD.
METHOD is_valid.
rv_valid = xsdbool( is_quote-material IS NOT INITIAL AND is_quote-price > 0 ).
ENDMETHOD.
METHOD update_aggregate.
READ TABLE ct_aggregate ASSIGNING FIELD-SYMBOL(<ls_aggregate>)
WITH TABLE KEY material = is_quote-material.
IF sy-subrc <> 0.
INSERT VALUE #( material = is_quote-material
supplier = is_quote-supplier
price = is_quote-price
quote_count = 1 ) INTO TABLE ct_aggregate.
RETURN.
ENDIF.
<ls_aggregate>-quote_count = <ls_aggregate>-quote_count + 1.
IF is_quote-price < <ls_aggregate>-price
OR ( is_quote-price = <ls_aggregate>-price
AND is_quote-supplier >= <ls_aggregate>-supplier ).
<ls_aggregate>-price = is_quote-price.
<ls_aggregate>-supplier = is_quote-supplier.
ENDIF.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,28 @@
{
"id": "G1049",
"pool": "train",
"object_type": "CLAS",
"category": "C",
"attempts": [
{
"stage": "bundle",
"errors": [
"reference/quote_merger.testclasses.abap line 96: syntax error for release v758 (abaplint): Statement does not exist in the configured ABAP version(or a parser error), \"lt_quotes_a\""
],
"by_check": {
"abaplint": 1
}
},
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 5,
"killed": 5,
"ok": true
}
}
],
"accepted": true
}

View File

@@ -0,0 +1,249 @@
CLASS {{p}}quote_merger_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA mo_cut TYPE REF TO {{p}}quote_merger.
METHODS setup.
METHODS merges_two_lists FOR TESTING.
METHODS keeps_lowest_price FOR TESTING.
METHODS breaks_tie_by_supplier FOR TESTING.
METHODS counts_all_quotes FOR TESTING.
METHODS rejects_zero_price FOR TESTING.
METHODS rejects_negative_price FOR TESTING.
METHODS rejects_blank_material FOR TESTING.
METHODS sorts_by_material FOR TESTING.
METHODS handles_empty_input FOR TESTING.
METHODS lowest_in_one_table FOR TESTING.
METHODS mixed_valid_invalid FOR TESTING.
ENDCLASS.
CLASS {{p}}quote_merger_hidden IMPLEMENTATION.
METHOD setup.
mo_cut = NEW {{p}}quote_merger( ).
ENDMETHOD.
METHOD merges_two_lists.
DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote.
DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote.
DATA lt_result TYPE {{p}}quote_merger=>tt_result.
DATA lv_rejected TYPE i.
DATA lv_price TYPE {{p}}quote_merger=>ty_price.
lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) ).
lt_quotes_b = VALUE #( ( material = 'MAT-2' supplier = 'BETA' price = '20.00' ) ).
mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a
it_quotes_b = lt_quotes_b
IMPORTING et_result = lt_result
ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rejected ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_quotes_a ) ).
cl_abap_unit_assert=>assert_equals( exp = 'MAT-1' act = lt_result[ 1 ]-material ).
cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lt_result[ 1 ]-supplier ).
lv_price = '10.00'.
cl_abap_unit_assert=>assert_equals( exp = lv_price act = lt_result[ 1 ]-price ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_result[ 1 ]-quote_count ).
cl_abap_unit_assert=>assert_equals( exp = 'MAT-2' act = lt_result[ 2 ]-material ).
cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = lt_result[ 2 ]-supplier ).
ENDMETHOD.
METHOD keeps_lowest_price.
DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote.
DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote.
DATA lt_result TYPE {{p}}quote_merger=>tt_result.
DATA lv_rejected TYPE i.
DATA lv_price TYPE {{p}}quote_merger=>ty_price.
lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) ).
lt_quotes_b = VALUE #( ( material = 'MAT-1' supplier = 'BETA' price = '8.00' ) ).
mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a
it_quotes_b = lt_quotes_b
IMPORTING et_result = lt_result
ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = lt_result[ 1 ]-supplier ).
lv_price = '8.00'.
cl_abap_unit_assert=>assert_equals( exp = lv_price act = lt_result[ 1 ]-price ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-quote_count ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rejected ).
ENDMETHOD.
METHOD breaks_tie_by_supplier.
DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote.
DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote.
DATA lt_result TYPE {{p}}quote_merger=>tt_result.
DATA lv_rejected TYPE i.
lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'BETA' price = '10.00' ) ).
lt_quotes_b = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) ).
mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a
it_quotes_b = lt_quotes_b
IMPORTING et_result = lt_result
ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lt_result[ 1 ]-supplier ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-quote_count ).
ENDMETHOD.
METHOD counts_all_quotes.
DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote.
DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote.
DATA lt_result TYPE {{p}}quote_merger=>tt_result.
DATA lv_rejected TYPE i.
lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' )
( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) ).
lt_quotes_b = VALUE #( ( material = 'MAT-1' supplier = 'BETA' price = '12.00' ) ).
mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a
it_quotes_b = lt_quotes_b
IMPORTING et_result = lt_result
ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lt_result[ 1 ]-quote_count ).
cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lt_result[ 1 ]-supplier ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rejected ).
ENDMETHOD.
METHOD rejects_zero_price.
DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote.
DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote.
DATA lt_result TYPE {{p}}quote_merger=>tt_result.
DATA lv_rejected TYPE i.
lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '0.00' ) ).
mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a
it_quotes_b = lt_quotes_b
IMPORTING et_result = lt_result
ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rejected ).
ENDMETHOD.
METHOD rejects_negative_price.
DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote.
DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote.
DATA lt_result TYPE {{p}}quote_merger=>tt_result.
DATA lv_rejected TYPE i.
lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '-5.00' ) ).
mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a
it_quotes_b = lt_quotes_b
IMPORTING et_result = lt_result
ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rejected ).
ENDMETHOD.
METHOD rejects_blank_material.
DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote.
DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote.
DATA lt_result TYPE {{p}}quote_merger=>tt_result.
DATA lv_rejected TYPE i.
lt_quotes_a = VALUE #( ( material = '' supplier = 'ALPHA' price = '10.00' ) ).
mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a
it_quotes_b = lt_quotes_b
IMPORTING et_result = lt_result
ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rejected ).
ENDMETHOD.
METHOD sorts_by_material.
DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote.
DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote.
DATA lt_result TYPE {{p}}quote_merger=>tt_result.
DATA lv_rejected TYPE i.
lt_quotes_a = VALUE #( ( material = 'MAT-3' supplier = 'ALPHA' price = '10.00' )
( material = 'MAT-1' supplier = 'BETA' price = '11.00' ) ).
lt_quotes_b = VALUE #( ( material = 'MAT-2' supplier = 'GAMMA' price = '12.00' ) ).
mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a
it_quotes_b = lt_quotes_b
IMPORTING et_result = lt_result
ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 'MAT-1' act = lt_result[ 1 ]-material ).
cl_abap_unit_assert=>assert_equals( exp = 'MAT-2' act = lt_result[ 2 ]-material ).
cl_abap_unit_assert=>assert_equals( exp = 'MAT-3' act = lt_result[ 3 ]-material ).
ENDMETHOD.
METHOD handles_empty_input.
DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote.
DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote.
DATA lt_result TYPE {{p}}quote_merger=>tt_result.
DATA lv_rejected TYPE i.
mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a
it_quotes_b = lt_quotes_b
IMPORTING et_result = lt_result
ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rejected ).
ENDMETHOD.
METHOD lowest_in_one_table.
DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote.
DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote.
DATA lt_result TYPE {{p}}quote_merger=>tt_result.
DATA lv_rejected TYPE i.
DATA lv_price TYPE {{p}}quote_merger=>ty_price.
lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' )
( material = 'MAT-1' supplier = 'BETA' price = '7.50' ) ).
mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a
it_quotes_b = lt_quotes_b
IMPORTING et_result = lt_result
ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = lt_result[ 1 ]-supplier ).
lv_price = '7.50'.
cl_abap_unit_assert=>assert_equals( exp = lv_price act = lt_result[ 1 ]-price ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-quote_count ).
ENDMETHOD.
METHOD mixed_valid_invalid.
DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote.
DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote.
DATA lt_result TYPE {{p}}quote_merger=>tt_result.
DATA lv_rejected TYPE i.
DATA lv_price TYPE {{p}}quote_merger=>ty_price.
lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' )
( material = '' supplier = 'BETA' price = '5.00' ) ).
lt_quotes_b = VALUE #( ( material = 'MAT-2' supplier = 'GAMMA' price = '0.00' )
( material = 'MAT-1' supplier = 'DELTA' price = '9.00' ) ).
mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a
it_quotes_b = lt_quotes_b
IMPORTING et_result = lt_result
ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rejected ).
cl_abap_unit_assert=>assert_equals( exp = 'MAT-1' act = lt_result[ 1 ]-material ).
cl_abap_unit_assert=>assert_equals( exp = 'DELTA' act = lt_result[ 1 ]-supplier ).
lv_price = '9.00'.
cl_abap_unit_assert=>assert_equals( exp = lv_price act = lt_result[ 1 ]-price ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-quote_count ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,74 @@
{
"task": "G1049",
"mutants": [
{
"object": "{{P}}QUOTE_MERGER",
"mutant": "line 61: = -> <> (eq)",
"status": "killed",
"hidden": "1/11",
"failed_tests": [
"BREAKS_TIE_BY_SUPPLIER",
"COUNTS_ALL_QUOTES",
"KEEPS_LOWEST_PRICE",
"LOWEST_IN_ONE_TABLE",
"MERGES_TWO_LISTS",
"MIXED_VALID_INVALID",
"REJECTS_BLANK_MATERIAL",
"REJECTS_NEGATIVE_PRICE",
"REJECTS_ZERO_PRICE",
"SORTS_BY_MATERIAL"
]
},
{
"object": "{{P}}QUOTE_MERGER",
"mutant": "line 62: + -> - (arith)",
"status": "killed",
"hidden": "7/11",
"failed_tests": [
"MIXED_VALID_INVALID",
"REJECTS_BLANK_MATERIAL",
"REJECTS_NEGATIVE_PRICE",
"REJECTS_ZERO_PRICE"
]
},
{
"object": "{{P}}QUOTE_MERGER",
"mutant": "line 71: AND -> OR (logic)",
"status": "killed",
"hidden": "7/11",
"failed_tests": [
"MIXED_VALID_INVALID",
"REJECTS_BLANK_MATERIAL",
"REJECTS_NEGATIVE_PRICE",
"REJECTS_ZERO_PRICE"
]
},
{
"object": "{{P}}QUOTE_MERGER",
"mutant": "line 81: 1 -> 2 (const)",
"status": "killed",
"hidden": "5/11",
"failed_tests": [
"BREAKS_TIE_BY_SUPPLIER",
"COUNTS_ALL_QUOTES",
"KEEPS_LOWEST_PRICE",
"LOWEST_IN_ONE_TABLE",
"MERGES_TWO_LISTS",
"MIXED_VALID_INVALID"
]
},
{
"object": "{{P}}QUOTE_MERGER",
"mutant": "line 87: < -> >= (rel)",
"status": "killed",
"hidden": "10/11",
"failed_tests": [
"BREAKS_TIE_BY_SUPPLIER"
]
}
],
"valid": 5,
"killed": 5,
"kill_rate": 1.0,
"ok": true
}

View File

@@ -0,0 +1,92 @@
CLASS {{p}}quote_merger DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES ty_price TYPE p LENGTH 11 DECIMALS 2.
TYPES:
BEGIN OF ty_quote,
material TYPE c LENGTH 18,
supplier TYPE c LENGTH 10,
price TYPE ty_price,
END OF ty_quote.
TYPES tt_quote TYPE STANDARD TABLE OF ty_quote WITH DEFAULT KEY.
TYPES:
BEGIN OF ty_result,
material TYPE c LENGTH 18,
supplier TYPE c LENGTH 10,
price TYPE ty_price,
quote_count TYPE i,
END OF ty_result.
TYPES tt_result TYPE STANDARD TABLE OF ty_result WITH DEFAULT KEY.
METHODS merge
IMPORTING it_quotes_a TYPE tt_quote
it_quotes_b TYPE tt_quote
EXPORTING et_result TYPE tt_result
ev_rejected TYPE i.
PRIVATE SECTION.
TYPES tt_aggregate TYPE HASHED TABLE OF ty_result WITH UNIQUE KEY material.
METHODS collect
IMPORTING it_quotes TYPE tt_quote
CHANGING ct_aggregate TYPE tt_aggregate
cv_rejected TYPE i.
METHODS is_valid
IMPORTING is_quote TYPE ty_quote
RETURNING VALUE(rv_valid) TYPE abap_bool.
METHODS update_aggregate
IMPORTING is_quote TYPE ty_quote
CHANGING ct_aggregate TYPE tt_aggregate.
ENDCLASS.
CLASS {{p}}quote_merger IMPLEMENTATION.
METHOD merge.
DATA lt_aggregate TYPE tt_aggregate.
DATA lv_rejected TYPE i.
collect( EXPORTING it_quotes = it_quotes_a
CHANGING ct_aggregate = lt_aggregate
cv_rejected = lv_rejected ).
collect( EXPORTING it_quotes = it_quotes_b
CHANGING ct_aggregate = lt_aggregate
cv_rejected = lv_rejected ).
et_result = lt_aggregate.
SORT et_result BY material.
ev_rejected = lv_rejected.
ENDMETHOD.
METHOD collect.
LOOP AT it_quotes INTO DATA(ls_quote).
IF is_valid( ls_quote ) = abap_false.
cv_rejected = cv_rejected + 1.
CONTINUE.
ENDIF.
update_aggregate( EXPORTING is_quote = ls_quote
CHANGING ct_aggregate = ct_aggregate ).
ENDLOOP.
ENDMETHOD.
METHOD is_valid.
rv_valid = xsdbool( is_quote-material IS NOT INITIAL AND is_quote-price > 0 ).
ENDMETHOD.
METHOD update_aggregate.
READ TABLE ct_aggregate ASSIGNING FIELD-SYMBOL(<ls_aggregate>)
WITH TABLE KEY material = is_quote-material.
IF sy-subrc <> 0.
INSERT VALUE #( material = is_quote-material
supplier = is_quote-supplier
price = is_quote-price
quote_count = 1 ) INTO TABLE ct_aggregate.
RETURN.
ENDIF.
<ls_aggregate>-quote_count = <ls_aggregate>-quote_count + 1.
IF is_quote-price < <ls_aggregate>-price
OR ( is_quote-price = <ls_aggregate>-price
AND is_quote-supplier < <ls_aggregate>-supplier ).
<ls_aggregate>-price = is_quote-price.
<ls_aggregate>-supplier = is_quote-supplier.
ENDIF.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,126 @@
CLASS ltc_quote_merger DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA mo_cut TYPE REF TO {{p}}quote_merger.
METHODS setup.
METHODS merges_two_lists FOR TESTING.
METHODS keeps_lowest_price FOR TESTING.
METHODS breaks_tie_by_supplier FOR TESTING.
METHODS counts_all_quotes FOR TESTING.
METHODS rejects_invalid_rows FOR TESTING.
METHODS sorts_by_material FOR TESTING.
ENDCLASS.
CLASS ltc_quote_merger IMPLEMENTATION.
METHOD setup.
mo_cut = NEW {{p}}quote_merger( ).
ENDMETHOD.
METHOD merges_two_lists.
DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote.
DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote.
DATA lt_result TYPE {{p}}quote_merger=>tt_result.
DATA lv_rejected TYPE i.
lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) ).
lt_quotes_b = VALUE #( ( material = 'MAT-2' supplier = 'BETA' price = '20.00' ) ).
mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a
it_quotes_b = lt_quotes_b
IMPORTING et_result = lt_result
ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rejected ).
ENDMETHOD.
METHOD keeps_lowest_price.
DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote.
DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote.
DATA lt_result TYPE {{p}}quote_merger=>tt_result.
DATA lv_rejected TYPE i.
lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) ).
lt_quotes_b = VALUE #( ( material = 'MAT-1' supplier = 'BETA' price = '8.00' ) ).
mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a
it_quotes_b = lt_quotes_b
IMPORTING et_result = lt_result
ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = lt_result[ 1 ]-supplier ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-quote_count ).
ENDMETHOD.
METHOD breaks_tie_by_supplier.
DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote.
DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote.
DATA lt_result TYPE {{p}}quote_merger=>tt_result.
DATA lv_rejected TYPE i.
lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'BETA' price = '10.00' ) ).
lt_quotes_b = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) ).
mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a
it_quotes_b = lt_quotes_b
IMPORTING et_result = lt_result
ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = lt_result[ 1 ]-supplier ).
ENDMETHOD.
METHOD counts_all_quotes.
DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote.
DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote.
DATA lt_result TYPE {{p}}quote_merger=>tt_result.
DATA lv_rejected TYPE i.
lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' )
( material = 'MAT-1' supplier = 'ALPHA' price = '10.00' ) ).
lt_quotes_b = VALUE #( ( material = 'MAT-1' supplier = 'BETA' price = '12.00' ) ).
mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a
it_quotes_b = lt_quotes_b
IMPORTING et_result = lt_result
ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lt_result[ 1 ]-quote_count ).
ENDMETHOD.
METHOD rejects_invalid_rows.
DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote.
DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote.
DATA lt_result TYPE {{p}}quote_merger=>tt_result.
DATA lv_rejected TYPE i.
lt_quotes_a = VALUE #( ( material = 'MAT-1' supplier = 'ALPHA' price = '0.00' ) ).
lt_quotes_b = VALUE #( ( material = '' supplier = 'BETA' price = '5.00' ) ).
mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a
it_quotes_b = lt_quotes_b
IMPORTING et_result = lt_result
ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rejected ).
ENDMETHOD.
METHOD sorts_by_material.
DATA lt_quotes_a TYPE {{p}}quote_merger=>tt_quote.
DATA lt_quotes_b TYPE {{p}}quote_merger=>tt_quote.
DATA lt_result TYPE {{p}}quote_merger=>tt_result.
DATA lv_rejected TYPE i.
lt_quotes_a = VALUE #( ( material = 'MAT-3' supplier = 'ALPHA' price = '10.00' )
( material = 'MAT-1' supplier = 'BETA' price = '11.00' ) ).
lt_quotes_b = VALUE #( ( material = 'MAT-2' supplier = 'GAMMA' price = '12.00' ) ).
mo_cut->merge( EXPORTING it_quotes_a = lt_quotes_a
it_quotes_b = lt_quotes_b
IMPORTING et_result = lt_result
ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( exp = 'MAT-1' act = lt_result[ 1 ]-material ).
cl_abap_unit_assert=>assert_equals( exp = 'MAT-3' act = lt_result[ 3 ]-material ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,59 @@
# 1. Goal
A wholesaler buys materials from two suppliers. Each supplier sends a price list.
The purchasing team needs one merged list. The list shows, for each material, the
lowest price and the supplier that offers this price.
# 2. Open questions
None.
# 3. Context
- The class is new. It is in package $TMP.
- The class does not read a database table. The caller passes the two price lists.
# 4. Contract
- Create the class {{P}}QUOTE_MERGER in package $TMP.
- The class is public, final, and has a public constructor without parameters.
- Public types:
* TY_PRICE: packed number, length 11, 2 decimal places.
* TY_QUOTE: structure with the components MATERIAL (CHAR 18), SUPPLIER (CHAR 10)
and PRICE (TY_PRICE).
* TT_QUOTE: standard table of TY_QUOTE with default key.
* TY_RESULT: structure with the components MATERIAL (CHAR 18), SUPPLIER (CHAR 10),
PRICE (TY_PRICE) and QUOTE_COUNT (integer).
* TT_RESULT: standard table of TY_RESULT with default key.
- Public method MERGE:
IMPORTING it_quotes_a TYPE tt_quote
it_quotes_b TYPE tt_quote
EXPORTING et_result TYPE tt_result
ev_rejected TYPE i.
- Do not add other public methods.
# 5. Business rules
1. The method reads all rows of it_quotes_a and all rows of it_quotes_b. The order
of the two tables is not important.
2. A row is invalid if its material contains only blanks, or if its price is zero
or negative. Every invalid row increases ev_rejected by one. Invalid rows do not
appear in et_result.
3. All valid rows with the same material belong to one group.
4. et_result has exactly one row for each group. The PRICE of this row is the
lowest price of all valid rows in the group.
5. The SUPPLIER of this row is the supplier of a valid row that has this lowest
price. If more than one valid row has this lowest price, use the supplier that
is first in ascending order of the supplier name.
6. QUOTE_COUNT is the number of valid rows in the group. A row that appears two
times counts two times.
7. Sort et_result in ascending order of material.
8. If no valid row exists, et_result is empty and ev_rejected is the number of
invalid rows.
9. The method does not change it_quotes_a or it_quotes_b.
# 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 table, no CDS view, no report, no function module.
# 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,48 @@
{
"id": "G1049",
"category": "C",
"object_type": "CLAS",
"difficulty": 2,
"release_target": "v816",
"expected_outcome": "implement",
"budget": {
"max_tool_calls": 70,
"max_activations": 18
},
"seed": [],
"contract": [
{
"type": "CLAS",
"name": "{{P}}QUOTE_MERGER",
"description": "Merges two supplier price lists into one list per material with the lowest price and the cheapest supplier."
}
],
"out_of_scope": [
"Database tables",
"CDS view entities",
"Reports",
"Function modules"
],
"hidden_tests": [
{
"type": "CLAS",
"name": "{{P}}QUOTE_MERGER_HIDDEN",
"file": "hidden/quote_merger_hidden.clas.abap",
"description": "Hidden ABAP Unit tests for {{P}}QUOTE_MERGER"
}
],
"reference": [
{
"type": "CLAS",
"name": "{{P}}QUOTE_MERGER",
"file": "reference/quote_merger.clas.abap",
"description": "Reference implementation of the price list merger",
"testclasses_file": "reference/quote_merger.testclasses.abap"
}
],
"craft_checks": [
"table_kind",
"access_path",
"method_length"
]
}