Hard batch G0022-G0026: 5/5 accepted; budget floor, CDS checks, mutation check

- generator: budget floor (2x oracle activations, 3x calls), static checks for CDS
  $parameters and UNION annotation
- harness/mutation.py: deterministic mutants of the reference; hidden tests must fail
- G0022 revalidated with harness fixes: oracle 100, null 0
- results in docs/faz1-tasarim.md 11f

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kral
2026-10-03 05:16:30 +02:00
parent 3570d7ef9b
commit 32f03fcad4
53 changed files with 2931 additions and 14 deletions

View File

@@ -0,0 +1,43 @@
{
"id": "G0022",
"pool": "eval",
"object_type": "CLAS",
"category": "D",
"attempts": [
{
"stage": "validate",
"oracle": 0,
"null": 0
},
{
"stage": "validate",
"oracle": 0,
"null": 0
},
{
"stage": "validate",
"oracle": 0,
"null": 0
},
{
"stage": "validate",
"oracle": 0,
"null": 0
},
{
"stage": "revalidate",
"by": "harness fix 2026-10-03",
"change": "G6 ignores unknown standard superclass (abaplint); reference in dependency order. Bundle not changed.",
"oracle": 95.0,
"null": 0
},
{
"stage": "revalidate",
"by": "harness fix 2026-10-03",
"change": "budget floor: max_activations 12 -> 14 (reference alone needs 13).",
"oracle": 100.0,
"null": 0
}
],
"accepted": true
}

View File

@@ -0,0 +1,223 @@
CLASS {{p}}d01_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
TYPES ty_amount TYPE {{p}}if_expense_claim=>ty_amount.
DATA mo_cut TYPE REF TO {{p}}expense_validator.
METHODS setup.
METHODS valid_claim_passes FOR TESTING.
METHODS zero_amount_rejected FOR TESTING.
METHODS negative_amount_rejected FOR TESTING.
METHODS unknown_currency_rejected FOR TESTING.
METHODS future_date_rejected FOR TESTING.
METHODS today_is_accepted FOR TESTING.
METHODS receipt_required FOR TESTING.
METHODS receipt_not_required_below FOR TESTING.
METHODS cost_center_required FOR TESTING.
METHODS cost_center_not_required FOR TESTING.
METHODS amount_rule_has_priority FOR TESTING.
METHODS receipt_rule_has_priority FOR TESTING.
METHODS build_claim
IMPORTING iv_amount TYPE ty_amount
iv_currency TYPE {{p}}if_expense_claim=>ty_currency
iv_date TYPE d
iv_cc TYPE {{p}}if_expense_claim=>ty_cost_center
iv_receipt TYPE abap_bool
RETURNING VALUE(rs_claim) TYPE {{p}}if_expense_claim=>ty_claim.
ENDCLASS.
CLASS {{p}}d01_hidden IMPLEMENTATION.
METHOD setup.
mo_cut = NEW {{p}}expense_validator( ).
ENDMETHOD.
METHOD build_claim.
rs_claim = VALUE #( amount = iv_amount
currency = iv_currency
claim_date = iv_date
cost_center = iv_cc
has_receipt = iv_receipt ).
ENDMETHOD.
METHOD valid_claim_passes.
DATA(ls_claim) = build_claim( iv_amount = '250.00' iv_currency = 'EUR'
iv_date = sy-datum iv_cc = 'CC1000'
iv_receipt = abap_true ).
TRY.
mo_cut->validate( ls_claim ).
CATCH {{p}}cx_claim_error INTO DATA(lx).
cl_abap_unit_assert=>fail( msg = lx->get_message( ) ).
ENDTRY.
ENDMETHOD.
METHOD zero_amount_rejected.
DATA(ls_claim) = build_claim( iv_amount = 0 iv_currency = 'EUR'
iv_date = sy-datum iv_cc = 'CC1000'
iv_receipt = abap_true ).
TRY.
mo_cut->validate( ls_claim ).
cl_abap_unit_assert=>fail( msg = 'Exception expected' ).
CATCH {{p}}cx_claim_amount INTO DATA(lx).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_amount( '0.00' )
act = lx->mv_amount ).
cl_abap_unit_assert=>assert_equals( exp = 'Amount must be positive'
act = lx->get_message( ) ).
CATCH {{p}}cx_claim_error INTO DATA(lx_other).
cl_abap_unit_assert=>fail( msg = lx_other->get_message( ) ).
ENDTRY.
ENDMETHOD.
METHOD negative_amount_rejected.
DATA(ls_claim) = build_claim( iv_amount = '-50.00' iv_currency = 'EUR'
iv_date = sy-datum iv_cc = 'CC1000'
iv_receipt = abap_true ).
TRY.
mo_cut->validate( ls_claim ).
cl_abap_unit_assert=>fail( msg = 'Exception expected' ).
CATCH {{p}}cx_claim_amount INTO DATA(lx).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_amount( '-50.00' )
act = lx->mv_amount ).
CATCH {{p}}cx_claim_error INTO DATA(lx_other).
cl_abap_unit_assert=>fail( msg = lx_other->get_message( ) ).
ENDTRY.
ENDMETHOD.
METHOD unknown_currency_rejected.
DATA(ls_claim) = build_claim( iv_amount = '250.00' iv_currency = 'JPY'
iv_date = sy-datum iv_cc = 'CC1000'
iv_receipt = abap_true ).
TRY.
mo_cut->validate( ls_claim ).
cl_abap_unit_assert=>fail( msg = 'Exception expected' ).
CATCH {{p}}cx_claim_currency INTO DATA(lx).
cl_abap_unit_assert=>assert_equals( exp = 'JPY'
act = lx->mv_currency ).
cl_abap_unit_assert=>assert_equals( exp = 'Currency is not supported'
act = lx->get_message( ) ).
CATCH {{p}}cx_claim_error INTO DATA(lx_other).
cl_abap_unit_assert=>fail( msg = lx_other->get_message( ) ).
ENDTRY.
ENDMETHOD.
METHOD future_date_rejected.
DATA lv_tomorrow TYPE d.
lv_tomorrow = sy-datum + 1.
DATA(ls_claim) = build_claim( iv_amount = '250.00' iv_currency = 'EUR'
iv_date = lv_tomorrow iv_cc = 'CC1000'
iv_receipt = abap_true ).
TRY.
mo_cut->validate( ls_claim ).
cl_abap_unit_assert=>fail( msg = 'Exception expected' ).
CATCH {{p}}cx_claim_date INTO DATA(lx).
cl_abap_unit_assert=>assert_equals( exp = lv_tomorrow
act = lx->mv_claim_date ).
cl_abap_unit_assert=>assert_equals( exp = 'Claim date is in the future'
act = lx->get_message( ) ).
CATCH {{p}}cx_claim_error INTO DATA(lx_other).
cl_abap_unit_assert=>fail( msg = lx_other->get_message( ) ).
ENDTRY.
ENDMETHOD.
METHOD today_is_accepted.
DATA(ls_claim) = build_claim( iv_amount = '250.00' iv_currency = 'EUR'
iv_date = sy-datum iv_cc = 'CC1000'
iv_receipt = abap_true ).
TRY.
mo_cut->validate( ls_claim ).
CATCH {{p}}cx_claim_error INTO DATA(lx).
cl_abap_unit_assert=>fail( msg = lx->get_message( ) ).
ENDTRY.
ENDMETHOD.
METHOD receipt_required.
DATA(ls_claim) = build_claim( iv_amount = '250.00' iv_currency = 'EUR'
iv_date = sy-datum iv_cc = 'CC1000'
iv_receipt = abap_false ).
TRY.
mo_cut->validate( ls_claim ).
cl_abap_unit_assert=>fail( msg = 'Exception expected' ).
CATCH {{p}}cx_claim_receipt INTO DATA(lx).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_amount( '250.00' )
act = lx->mv_amount ).
cl_abap_unit_assert=>assert_equals( exp = 'Receipt is required'
act = lx->get_message( ) ).
CATCH {{p}}cx_claim_error INTO DATA(lx_other).
cl_abap_unit_assert=>fail( msg = lx_other->get_message( ) ).
ENDTRY.
ENDMETHOD.
METHOD receipt_not_required_below.
DATA(ls_claim) = build_claim( iv_amount = '99.99' iv_currency = 'EUR'
iv_date = sy-datum iv_cc = 'CC1000'
iv_receipt = abap_false ).
TRY.
mo_cut->validate( ls_claim ).
CATCH {{p}}cx_claim_error INTO DATA(lx).
cl_abap_unit_assert=>fail( msg = lx->get_message( ) ).
ENDTRY.
ENDMETHOD.
METHOD cost_center_required.
DATA(ls_claim) = build_claim( iv_amount = '1500.00' iv_currency = 'EUR'
iv_date = sy-datum iv_cc = ''
iv_receipt = abap_true ).
TRY.
mo_cut->validate( ls_claim ).
cl_abap_unit_assert=>fail( msg = 'Exception expected' ).
CATCH {{p}}cx_claim_costcenter INTO DATA(lx).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_amount( '1500.00' )
act = lx->mv_amount ).
cl_abap_unit_assert=>assert_equals( exp = 'Cost center is required'
act = lx->get_message( ) ).
CATCH {{p}}cx_claim_error INTO DATA(lx_other).
cl_abap_unit_assert=>fail( msg = lx_other->get_message( ) ).
ENDTRY.
ENDMETHOD.
METHOD cost_center_not_required.
DATA(ls_claim) = build_claim( iv_amount = '999.99' iv_currency = 'EUR'
iv_date = sy-datum iv_cc = ''
iv_receipt = abap_true ).
TRY.
mo_cut->validate( ls_claim ).
CATCH {{p}}cx_claim_error INTO DATA(lx).
cl_abap_unit_assert=>fail( msg = lx->get_message( ) ).
ENDTRY.
ENDMETHOD.
METHOD amount_rule_has_priority.
DATA lv_future TYPE d.
lv_future = sy-datum + 1.
DATA(ls_claim) = build_claim( iv_amount = 0 iv_currency = 'JPY'
iv_date = lv_future iv_cc = ''
iv_receipt = abap_false ).
TRY.
mo_cut->validate( ls_claim ).
cl_abap_unit_assert=>fail( msg = 'Exception expected' ).
CATCH {{p}}cx_claim_amount INTO DATA(lx).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_amount( '0.00' )
act = lx->mv_amount ).
CATCH {{p}}cx_claim_error INTO DATA(lx_other).
cl_abap_unit_assert=>fail( msg = lx_other->get_message( ) ).
ENDTRY.
ENDMETHOD.
METHOD receipt_rule_has_priority.
DATA(ls_claim) = build_claim( iv_amount = '1500.00' iv_currency = 'EUR'
iv_date = sy-datum iv_cc = ''
iv_receipt = abap_false ).
TRY.
mo_cut->validate( ls_claim ).
cl_abap_unit_assert=>fail( msg = 'Exception expected' ).
CATCH {{p}}cx_claim_receipt INTO DATA(lx).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_amount( '1500.00' )
act = lx->mv_amount ).
CATCH {{p}}cx_claim_error INTO DATA(lx_other).
cl_abap_unit_assert=>fail( msg = lx_other->get_message( ) ).
ENDTRY.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,20 @@
CLASS {{p}}cx_claim_amount DEFINITION
PUBLIC
INHERITING FROM {{p}}cx_claim_error
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
DATA mv_amount TYPE {{p}}if_expense_claim=>ty_amount READ-ONLY.
METHODS constructor
IMPORTING iv_amount TYPE {{p}}if_expense_claim=>ty_amount.
ENDCLASS.
CLASS {{p}}cx_claim_amount IMPLEMENTATION.
METHOD constructor.
super->constructor( iv_message = 'Amount must be positive' ).
mv_amount = iv_amount.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,20 @@
CLASS {{p}}cx_claim_costcenter DEFINITION
PUBLIC
INHERITING FROM {{p}}cx_claim_error
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
DATA mv_amount TYPE {{p}}if_expense_claim=>ty_amount READ-ONLY.
METHODS constructor
IMPORTING iv_amount TYPE {{p}}if_expense_claim=>ty_amount.
ENDCLASS.
CLASS {{p}}cx_claim_costcenter IMPLEMENTATION.
METHOD constructor.
super->constructor( iv_message = 'Cost center is required' ).
mv_amount = iv_amount.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,20 @@
CLASS {{p}}cx_claim_currency DEFINITION
PUBLIC
INHERITING FROM {{p}}cx_claim_error
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
DATA mv_currency TYPE {{p}}if_expense_claim=>ty_currency READ-ONLY.
METHODS constructor
IMPORTING iv_currency TYPE {{p}}if_expense_claim=>ty_currency.
ENDCLASS.
CLASS {{p}}cx_claim_currency IMPLEMENTATION.
METHOD constructor.
super->constructor( iv_message = 'Currency is not supported' ).
mv_currency = iv_currency.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,20 @@
CLASS {{p}}cx_claim_date DEFINITION
PUBLIC
INHERITING FROM {{p}}cx_claim_error
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
DATA mv_claim_date TYPE d READ-ONLY.
METHODS constructor
IMPORTING iv_claim_date TYPE d.
ENDCLASS.
CLASS {{p}}cx_claim_date IMPLEMENTATION.
METHOD constructor.
super->constructor( iv_message = 'Claim date is in the future' ).
mv_claim_date = iv_claim_date.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,20 @@
CLASS {{p}}cx_claim_receipt DEFINITION
PUBLIC
INHERITING FROM {{p}}cx_claim_error
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
DATA mv_amount TYPE {{p}}if_expense_claim=>ty_amount READ-ONLY.
METHODS constructor
IMPORTING iv_amount TYPE {{p}}if_expense_claim=>ty_amount.
ENDCLASS.
CLASS {{p}}cx_claim_receipt IMPLEMENTATION.
METHOD constructor.
super->constructor( iv_message = 'Receipt is required' ).
mv_amount = iv_amount.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,85 @@
CLASS {{p}}expense_validator DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
METHODS validate
IMPORTING is_claim TYPE {{p}}if_expense_claim=>ty_claim
RAISING {{p}}cx_claim_error.
PRIVATE SECTION.
CONSTANTS:
c_receipt_limit TYPE {{p}}if_expense_claim=>ty_amount VALUE '100.00',
c_cc_limit TYPE {{p}}if_expense_claim=>ty_amount VALUE '1000.00'.
METHODS check_amount
IMPORTING is_claim TYPE {{p}}if_expense_claim=>ty_claim
RAISING {{p}}cx_claim_error.
METHODS check_currency
IMPORTING is_claim TYPE {{p}}if_expense_claim=>ty_claim
RAISING {{p}}cx_claim_error.
METHODS check_date
IMPORTING is_claim TYPE {{p}}if_expense_claim=>ty_claim
RAISING {{p}}cx_claim_error.
METHODS check_receipt
IMPORTING is_claim TYPE {{p}}if_expense_claim=>ty_claim
RAISING {{p}}cx_claim_error.
METHODS check_cost_center
IMPORTING is_claim TYPE {{p}}if_expense_claim=>ty_claim
RAISING {{p}}cx_claim_error.
METHODS is_supported_currency
IMPORTING iv_currency TYPE {{p}}if_expense_claim=>ty_currency
RETURNING VALUE(rv_supported) TYPE abap_bool.
ENDCLASS.
CLASS {{p}}expense_validator IMPLEMENTATION.
METHOD validate.
check_amount( is_claim ).
check_currency( is_claim ).
check_date( is_claim ).
check_receipt( is_claim ).
check_cost_center( is_claim ).
ENDMETHOD.
METHOD check_amount.
IF is_claim-amount <= 0.
RAISE EXCEPTION TYPE {{p}}cx_claim_amount
EXPORTING iv_amount = is_claim-amount.
ENDIF.
ENDMETHOD.
METHOD check_currency.
IF is_supported_currency( is_claim-currency ) = abap_false.
RAISE EXCEPTION TYPE {{p}}cx_claim_currency
EXPORTING iv_currency = is_claim-currency.
ENDIF.
ENDMETHOD.
METHOD check_date.
IF is_claim-claim_date > sy-datum.
RAISE EXCEPTION TYPE {{p}}cx_claim_date
EXPORTING iv_claim_date = is_claim-claim_date.
ENDIF.
ENDMETHOD.
METHOD check_receipt.
IF is_claim-amount >= c_receipt_limit AND is_claim-has_receipt = abap_false.
RAISE EXCEPTION TYPE {{p}}cx_claim_receipt
EXPORTING iv_amount = is_claim-amount.
ENDIF.
ENDMETHOD.
METHOD check_cost_center.
IF is_claim-amount >= c_cc_limit AND is_claim-cost_center IS INITIAL.
RAISE EXCEPTION TYPE {{p}}cx_claim_costcenter
EXPORTING iv_amount = is_claim-amount.
ENDIF.
ENDMETHOD.
METHOD is_supported_currency.
rv_supported = xsdbool( iv_currency = 'EUR' OR iv_currency = 'USD'
OR iv_currency = 'GBP' OR iv_currency = 'CHF' ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,127 @@
CLASS ltc_expense_validator DEFINITION FINAL FOR TESTING
DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
TYPES ty_amount TYPE {{p}}if_expense_claim=>ty_amount.
DATA mo_cut TYPE REF TO {{p}}expense_validator.
METHODS setup.
METHODS valid_claim FOR TESTING.
METHODS zero_amount FOR TESTING.
METHODS unknown_currency FOR TESTING.
METHODS future_date FOR TESTING.
METHODS receipt_missing FOR TESTING.
METHODS cost_center_missing FOR TESTING.
METHODS build_claim
IMPORTING iv_amount TYPE ty_amount
iv_currency TYPE {{p}}if_expense_claim=>ty_currency
iv_date TYPE d
iv_cc TYPE {{p}}if_expense_claim=>ty_cost_center
iv_receipt TYPE abap_bool
RETURNING VALUE(rs_claim) TYPE {{p}}if_expense_claim=>ty_claim.
ENDCLASS.
CLASS ltc_expense_validator IMPLEMENTATION.
METHOD setup.
mo_cut = NEW {{p}}expense_validator( ).
ENDMETHOD.
METHOD build_claim.
rs_claim = VALUE #( amount = iv_amount
currency = iv_currency
claim_date = iv_date
cost_center = iv_cc
has_receipt = iv_receipt ).
ENDMETHOD.
METHOD valid_claim.
DATA(ls_claim) = build_claim( iv_amount = '250.00' iv_currency = 'EUR'
iv_date = sy-datum iv_cc = 'CC1000'
iv_receipt = abap_true ).
TRY.
mo_cut->validate( ls_claim ).
CATCH {{p}}cx_claim_error INTO DATA(lx).
cl_abap_unit_assert=>fail( msg = lx->get_message( ) ).
ENDTRY.
ENDMETHOD.
METHOD zero_amount.
DATA(ls_claim) = build_claim( iv_amount = 0 iv_currency = 'EUR'
iv_date = sy-datum iv_cc = 'CC1000'
iv_receipt = abap_true ).
TRY.
mo_cut->validate( ls_claim ).
cl_abap_unit_assert=>fail( msg = 'Exception expected' ).
CATCH {{p}}cx_claim_amount INTO DATA(lx).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_amount( '0.00' )
act = lx->mv_amount ).
CATCH {{p}}cx_claim_error INTO DATA(lx_other).
cl_abap_unit_assert=>fail( msg = lx_other->get_message( ) ).
ENDTRY.
ENDMETHOD.
METHOD unknown_currency.
DATA(ls_claim) = build_claim( iv_amount = '250.00' iv_currency = 'JPY'
iv_date = sy-datum iv_cc = 'CC1000'
iv_receipt = abap_true ).
TRY.
mo_cut->validate( ls_claim ).
cl_abap_unit_assert=>fail( msg = 'Exception expected' ).
CATCH {{p}}cx_claim_currency INTO DATA(lx).
cl_abap_unit_assert=>assert_equals( exp = 'JPY'
act = lx->mv_currency ).
CATCH {{p}}cx_claim_error INTO DATA(lx_other).
cl_abap_unit_assert=>fail( msg = lx_other->get_message( ) ).
ENDTRY.
ENDMETHOD.
METHOD future_date.
DATA lv_future TYPE d.
lv_future = sy-datum + 1.
DATA(ls_claim) = build_claim( iv_amount = '250.00' iv_currency = 'EUR'
iv_date = lv_future iv_cc = 'CC1000'
iv_receipt = abap_true ).
TRY.
mo_cut->validate( ls_claim ).
cl_abap_unit_assert=>fail( msg = 'Exception expected' ).
CATCH {{p}}cx_claim_date INTO DATA(lx).
cl_abap_unit_assert=>assert_equals( exp = lv_future
act = lx->mv_claim_date ).
CATCH {{p}}cx_claim_error INTO DATA(lx_other).
cl_abap_unit_assert=>fail( msg = lx_other->get_message( ) ).
ENDTRY.
ENDMETHOD.
METHOD receipt_missing.
DATA(ls_claim) = build_claim( iv_amount = '250.00' iv_currency = 'EUR'
iv_date = sy-datum iv_cc = 'CC1000'
iv_receipt = abap_false ).
TRY.
mo_cut->validate( ls_claim ).
cl_abap_unit_assert=>fail( msg = 'Exception expected' ).
CATCH {{p}}cx_claim_receipt INTO DATA(lx).
cl_abap_unit_assert=>assert_equals( exp = 'Receipt is required'
act = lx->get_message( ) ).
CATCH {{p}}cx_claim_error INTO DATA(lx_other).
cl_abap_unit_assert=>fail( msg = lx_other->get_message( ) ).
ENDTRY.
ENDMETHOD.
METHOD cost_center_missing.
DATA(ls_claim) = build_claim( iv_amount = '1500.00' iv_currency = 'EUR'
iv_date = sy-datum iv_cc = ''
iv_receipt = abap_true ).
TRY.
mo_cut->validate( ls_claim ).
cl_abap_unit_assert=>fail( msg = 'Exception expected' ).
CATCH {{p}}cx_claim_costcenter INTO DATA(lx).
cl_abap_unit_assert=>assert_equals( exp = 'Cost center is required'
act = lx->get_message( ) ).
CATCH {{p}}cx_claim_error INTO DATA(lx_other).
cl_abap_unit_assert=>fail( msg = lx_other->get_message( ) ).
ENDTRY.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,27 @@
CLASS {{p}}cx_claim_error DEFINITION
PUBLIC
INHERITING FROM cx_static_check
CREATE PUBLIC.
PUBLIC SECTION.
METHODS constructor
IMPORTING iv_message TYPE string.
METHODS get_message
RETURNING VALUE(rv_message) TYPE string.
PRIVATE SECTION.
DATA mv_message TYPE string.
ENDCLASS.
CLASS {{p}}cx_claim_error IMPLEMENTATION.
METHOD constructor.
super->constructor( ).
mv_message = iv_message.
ENDMETHOD.
METHOD get_message.
rv_message = mv_message.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,14 @@
INTERFACE {{p}}if_expense_claim PUBLIC.
TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2.
TYPES ty_currency TYPE c LENGTH 3.
TYPES ty_cost_center TYPE c LENGTH 10.
TYPES:
BEGIN OF ty_claim,
amount TYPE ty_amount,
currency TYPE ty_currency,
claim_date TYPE d,
cost_center TYPE ty_cost_center,
has_receipt TYPE abap_bool,
END OF ty_claim.
ENDINTERFACE.

View File

@@ -0,0 +1,72 @@
# 1. Goal
Validate an employee expense claim before it is posted to accounting. The check
reports the first rule that fails as a class-based exception. Each exception
carries the value that caused the failure and a short message.
# 2. Open questions
None.
# 3. Context
- The interface {{P}}IF_EXPENSE_CLAIM exists in package $TMP and is active. It
contains types only and has no methods:
- TY_AMOUNT, a packed number with length 8 and 2 decimals.
- TY_CURRENCY, a character field with length 3.
- TY_COST_CENTER, a character field with length 10.
- TY_CLAIM, a structure with the components AMOUNT (TY_AMOUNT), CURRENCY
(TY_CURRENCY), CLAIM_DATE (type D), COST_CENTER (TY_COST_CENTER) and
HAS_RECEIPT (ABAP_BOOL).
- The class {{P}}CX_CLAIM_ERROR exists in package $TMP and is active. It is a
direct subclass of CX_STATIC_CHECK. Its constructor has the importing
parameter IV_MESSAGE (TYPE string). The method GET_MESSAGE returns this text
(TYPE string).
# 4. Contract
- Create the class {{P}}EXPENSE_VALIDATOR in package $TMP.
- The class is public and final and has a public constructor without parameters.
- The class has exactly one public method:
VALIDATE IMPORTING is_claim TYPE {{p}}if_expense_claim=>ty_claim
RAISING {{p}}cx_claim_error.
- Create these exception classes in package $TMP. Each one inherits from
{{P}}CX_CLAIM_ERROR, is final and has a public constructor.
- {{P}}CX_CLAIM_AMOUNT: constructor parameter IV_AMOUNT of type TY_AMOUNT of
{{P}}IF_EXPENSE_CLAIM. Public read-only attribute MV_AMOUNT of the same
type. Message: Amount must be positive
- {{P}}CX_CLAIM_CURRENCY: constructor parameter IV_CURRENCY of type
TY_CURRENCY. Public read-only attribute MV_CURRENCY of the same type.
Message: Currency is not supported
- {{P}}CX_CLAIM_DATE: constructor parameter IV_CLAIM_DATE of type D. Public
read-only attribute MV_CLAIM_DATE of type D. Message: Claim date is in the
future
- {{P}}CX_CLAIM_RECEIPT: constructor parameter IV_AMOUNT of type TY_AMOUNT.
Public read-only attribute MV_AMOUNT of the same type. Message: Receipt is
required
- {{P}}CX_CLAIM_COSTCENTER: constructor parameter IV_AMOUNT of type TY_AMOUNT.
Public read-only attribute MV_AMOUNT of the same type. Message: Cost center
is required
- GET_MESSAGE returns the message of the exception.
- Do not add other public methods or attributes.
# 5. Business rules
Check the claim in this sequence. Raise the exception of the first rule that
fails. If no rule fails, the method ends without an exception.
1. AMOUNT must be greater than zero. Else raise {{P}}CX_CLAIM_AMOUNT with
IV_AMOUNT = AMOUNT.
2. CURRENCY must be one of EUR, USD, GBP, CHF. Else raise
{{P}}CX_CLAIM_CURRENCY with IV_CURRENCY = CURRENCY.
3. CLAIM_DATE must not be later than the current date. Else raise
{{P}}CX_CLAIM_DATE with IV_CLAIM_DATE = CLAIM_DATE.
4. If AMOUNT is 100.00 or more, HAS_RECEIPT must be true. Else raise
{{P}}CX_CLAIM_RECEIPT with IV_AMOUNT = AMOUNT.
5. If AMOUNT is 1000.00 or more, COST_CENTER must not be initial. Else raise
{{P}}CX_CLAIM_COSTCENTER with IV_AMOUNT = AMOUNT.
# 6. Constraints
- Release target: 8.16.
- Coding standards: Clean ABAP. Method length below 40 statements. No global
variables. No comment that restates the code.
- Out of scope: do not change {{P}}IF_EXPENSE_CLAIM and {{P}}CX_CLAIM_ERROR.
# 7. Acceptance
- All objects are active and have no syntax error.
- The hidden tests pass.
- Write your own ABAP Unit tests for {{P}}EXPENSE_VALIDATOR.

View File

@@ -0,0 +1,107 @@
{
"id": "G0022",
"category": "D",
"object_type": "CLAS",
"difficulty": 2,
"release_target": "v816",
"expected_outcome": "implement",
"budget": {
"max_tool_calls": 45,
"max_activations": 14
},
"seed": [
{
"type": "INTF",
"name": "{{P}}IF_EXPENSE_CLAIM",
"file": "seed/if_expense_claim.intf.abap",
"description": "Types for an expense claim"
},
{
"type": "CLAS",
"name": "{{P}}CX_CLAIM_ERROR",
"file": "seed/cx_claim_error.clas.abap",
"description": "Base exception that carries a message text"
}
],
"contract": [
{
"type": "CLAS",
"name": "{{P}}CX_CLAIM_AMOUNT"
},
{
"type": "CLAS",
"name": "{{P}}CX_CLAIM_CURRENCY"
},
{
"type": "CLAS",
"name": "{{P}}CX_CLAIM_DATE"
},
{
"type": "CLAS",
"name": "{{P}}CX_CLAIM_RECEIPT"
},
{
"type": "CLAS",
"name": "{{P}}CX_CLAIM_COSTCENTER"
},
{
"type": "CLAS",
"name": "{{P}}EXPENSE_VALIDATOR"
}
],
"out_of_scope": [
"{{P}}IF_EXPENSE_CLAIM",
"{{P}}CX_CLAIM_ERROR"
],
"hidden_tests": [
{
"type": "CLAS",
"name": "{{P}}D01_HIDDEN",
"file": "hidden/d01_hidden.clas.abap",
"description": "Hidden tests for the expense claim validation"
}
],
"reference": [
{
"type": "CLAS",
"name": "{{P}}CX_CLAIM_AMOUNT",
"file": "reference/cx_claim_amount.clas.abap",
"description": "Exception for a non positive amount"
},
{
"type": "CLAS",
"name": "{{P}}CX_CLAIM_CURRENCY",
"file": "reference/cx_claim_currency.clas.abap",
"description": "Exception for an unsupported currency"
},
{
"type": "CLAS",
"name": "{{P}}CX_CLAIM_DATE",
"file": "reference/cx_claim_date.clas.abap",
"description": "Exception for a claim date in the future"
},
{
"type": "CLAS",
"name": "{{P}}CX_CLAIM_RECEIPT",
"file": "reference/cx_claim_receipt.clas.abap",
"description": "Exception for a missing receipt"
},
{
"type": "CLAS",
"name": "{{P}}CX_CLAIM_COSTCENTER",
"file": "reference/cx_claim_costcenter.clas.abap",
"description": "Exception for a missing cost center"
},
{
"type": "CLAS",
"name": "{{P}}EXPENSE_VALIDATOR",
"file": "reference/expense_validator.clas.abap",
"description": "Validates an expense claim and raises class-based exceptions",
"testclasses_file": "reference/expense_validator.testclasses.abap"
}
],
"craft_checks": [
"method_length",
"exception_design"
]
}