Stage 2 summary at 50 accepted trajectories

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Kral
2026-10-05 18:02:52 +02:00
parent 332fb4601d
commit 5bcd1bf9cd
1230 changed files with 77563 additions and 33 deletions

View File

@@ -0,0 +1,29 @@
{
"id": "G1136",
"pool": "train",
"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
}
],
"accepted": false
}

View File

@@ -0,0 +1,202 @@
CLASS {{p}}t_d_job_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA mo_cut TYPE REF TO {{p}}job_planner.
METHODS setup.
METHODS valid_job RETURNING VALUE(rs_job) TYPE {{p}}job_planner=>ty_job.
METHODS standard_cost FOR TESTING.
METHODS express_cost FOR TESTING.
METHODS priority_cost FOR TESTING.
METHODS wood_cost FOR TESTING.
METHODS missing_id_raises FOR TESTING.
METHODS bad_mode_raises FOR TESTING.
METHODS bad_hours_raises FOR TESTING.
METHODS bad_count_raises FOR TESTING.
METHODS bad_group_raises FOR TESTING.
METHODS bad_date_raises FOR TESTING.
METHODS bad_value_raises FOR TESTING.
ENDCLASS.
CLASS {{p}}t_d_job_hidden IMPLEMENTATION.
METHOD setup.
mo_cut = NEW {{p}}job_planner( ).
ENDMETHOD.
METHOD valid_job.
rs_job = VALUE #( job_id = 'JOB-1000'
calc_mode = 'S'
work_hours = '10.00'
part_count = 4
mat_group = 'ST'
due_date = '20250601'
net_value = '100.00' ).
ENDMETHOD.
METHOD standard_cost.
DATA lv_exp TYPE {{p}}job_planner=>ty_cost.
lv_exp = '750.00'.
TRY.
cl_abap_unit_assert=>assert_equals( exp = lv_exp
act = mo_cut->calculate_cost( valid_job( ) ) ).
CATCH {{p}}cx_job_error.
cl_abap_unit_assert=>fail( `unexpected exception` ).
ENDTRY.
ENDMETHOD.
METHOD express_cost.
DATA ls_job TYPE {{p}}job_planner=>ty_job.
DATA lv_exp TYPE {{p}}job_planner=>ty_cost.
ls_job = valid_job( ).
ls_job-calc_mode = 'E'.
ls_job-work_hours = '2.50'.
ls_job-part_count = 2.
ls_job-mat_group = 'AL'.
ls_job-net_value = 0.
lv_exp = '241.00'.
TRY.
cl_abap_unit_assert=>assert_equals( exp = lv_exp
act = mo_cut->calculate_cost( ls_job ) ).
CATCH {{p}}cx_job_error.
cl_abap_unit_assert=>fail( `unexpected exception` ).
ENDTRY.
ENDMETHOD.
METHOD priority_cost.
DATA ls_job TYPE {{p}}job_planner=>ty_job.
DATA lv_exp TYPE {{p}}job_planner=>ty_cost.
ls_job = valid_job( ).
ls_job-calc_mode = 'P'.
ls_job-work_hours = '1.00'.
ls_job-part_count = 1.
ls_job-mat_group = 'PL'.
ls_job-net_value = '10.00'.
lv_exp = '88.50'.
TRY.
cl_abap_unit_assert=>assert_equals( exp = lv_exp
act = mo_cut->calculate_cost( ls_job ) ).
CATCH {{p}}cx_job_error.
cl_abap_unit_assert=>fail( `unexpected exception` ).
ENDTRY.
ENDMETHOD.
METHOD wood_cost.
DATA ls_job TYPE {{p}}job_planner=>ty_job.
DATA lv_exp TYPE {{p}}job_planner=>ty_cost.
ls_job = valid_job( ).
ls_job-work_hours = '0.50'.
ls_job-part_count = 1.
ls_job-mat_group = 'WD'.
ls_job-net_value = 0.
lv_exp = '35.00'.
TRY.
cl_abap_unit_assert=>assert_equals( exp = lv_exp
act = mo_cut->calculate_cost( ls_job ) ).
CATCH {{p}}cx_job_error.
cl_abap_unit_assert=>fail( `unexpected exception` ).
ENDTRY.
ENDMETHOD.
METHOD missing_id_raises.
DATA(ls_job) = valid_job( ).
ls_job-job_id = ''.
ls_job-calc_mode = 'Q'.
ls_job-work_hours = 0.
TRY.
mo_cut->validate( ls_job ).
cl_abap_unit_assert=>fail( `NO_JOB_ID expected` ).
CATCH {{p}}cx_job_error INTO DATA(lx_error).
cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-no_job_id
act = lx_error->get_reason( ) ).
ENDTRY.
ENDMETHOD.
METHOD bad_mode_raises.
DATA(ls_job) = valid_job( ).
ls_job-calc_mode = 'Q'.
ls_job-work_hours = 0.
TRY.
mo_cut->validate( ls_job ).
cl_abap_unit_assert=>fail( `BAD_MODE expected` ).
CATCH {{p}}cx_job_error INTO DATA(lx_error).
cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-bad_mode
act = lx_error->get_reason( ) ).
ENDTRY.
ENDMETHOD.
METHOD bad_hours_raises.
DATA(ls_job) = valid_job( ).
ls_job-work_hours = 0.
TRY.
mo_cut->validate( ls_job ).
cl_abap_unit_assert=>fail( `BAD_HOURS expected` ).
CATCH {{p}}cx_job_error INTO DATA(lx_error).
cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-bad_hours
act = lx_error->get_reason( ) ).
ENDTRY.
ls_job-work_hours = '100.01'.
TRY.
mo_cut->calculate_cost( ls_job ).
cl_abap_unit_assert=>fail( `BAD_HOURS expected` ).
CATCH {{p}}cx_job_error INTO DATA(lx_cost_error).
cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-bad_hours
act = lx_cost_error->get_reason( ) ).
ENDTRY.
ENDMETHOD.
METHOD bad_count_raises.
DATA(ls_job) = valid_job( ).
ls_job-part_count = 0.
TRY.
mo_cut->validate( ls_job ).
cl_abap_unit_assert=>fail( `BAD_COUNT expected` ).
CATCH {{p}}cx_job_error INTO DATA(lx_error).
cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-bad_count
act = lx_error->get_reason( ) ).
ENDTRY.
ENDMETHOD.
METHOD bad_group_raises.
DATA(ls_job) = valid_job( ).
DATA lv_msg TYPE string.
ls_job-mat_group = 'XY'.
lv_msg = 'The material group is not valid.'.
TRY.
mo_cut->validate( ls_job ).
cl_abap_unit_assert=>fail( `BAD_GROUP expected` ).
CATCH {{p}}cx_job_error INTO DATA(lx_error).
cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-bad_group
act = lx_error->get_reason( ) ).
cl_abap_unit_assert=>assert_equals( exp = lv_msg
act = lx_error->get_message( ) ).
ENDTRY.
ENDMETHOD.
METHOD bad_date_raises.
DATA(ls_job) = valid_job( ).
ls_job-due_date = '00000000'.
TRY.
mo_cut->validate( ls_job ).
cl_abap_unit_assert=>fail( `BAD_DATE expected` ).
CATCH {{p}}cx_job_error INTO DATA(lx_error).
cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-bad_date
act = lx_error->get_reason( ) ).
ENDTRY.
ENDMETHOD.
METHOD bad_value_raises.
DATA(ls_job) = valid_job( ).
ls_job-net_value = -1.
TRY.
mo_cut->validate( ls_job ).
cl_abap_unit_assert=>fail( `BAD_VALUE expected` ).
CATCH {{p}}cx_job_error INTO DATA(lx_error).
cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-bad_value
act = lx_error->get_reason( ) ).
ENDTRY.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,60 @@
CLASS {{p}}cx_job_error DEFINITION PUBLIC FINAL CREATE PUBLIC INHERITING FROM cx_static_check.
PUBLIC SECTION.
TYPES ty_reason TYPE c LENGTH 12.
CONSTANTS:
BEGIN OF c_reason,
no_job_id TYPE ty_reason VALUE 'NO_JOB_ID',
bad_mode TYPE ty_reason VALUE 'BAD_MODE',
bad_hours TYPE ty_reason VALUE 'BAD_HOURS',
bad_count TYPE ty_reason VALUE 'BAD_COUNT',
bad_group TYPE ty_reason VALUE 'BAD_GROUP',
bad_date TYPE ty_reason VALUE 'BAD_DATE',
bad_value TYPE ty_reason VALUE 'BAD_VALUE',
END OF c_reason.
METHODS constructor
IMPORTING iv_reason TYPE ty_reason.
METHODS get_reason
RETURNING VALUE(rv_reason) TYPE ty_reason.
METHODS get_message
RETURNING VALUE(rv_message) TYPE string.
PRIVATE SECTION.
DATA mv_reason TYPE ty_reason.
ENDCLASS.
CLASS {{p}}cx_job_error IMPLEMENTATION.
METHOD constructor.
super->constructor( ).
mv_reason = iv_reason.
ENDMETHOD.
METHOD get_reason.
rv_reason = mv_reason.
ENDMETHOD.
METHOD get_message.
CASE mv_reason.
WHEN c_reason-no_job_id.
rv_message = 'The job ID is missing.'.
WHEN c_reason-bad_mode.
rv_message = 'The calculation mode is not valid.'.
WHEN c_reason-bad_hours.
rv_message = 'The work hours are not valid.'.
WHEN c_reason-bad_count.
rv_message = 'The part count is not valid.'.
WHEN c_reason-bad_group.
rv_message = 'The material group is not valid.'.
WHEN c_reason-bad_date.
rv_message = 'The due date is not valid.'.
WHEN c_reason-bad_value.
rv_message = 'The material value is not valid.'.
WHEN OTHERS.
rv_message = 'The job is not valid.'.
ENDCASE.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,132 @@
CLASS {{p}}job_planner DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_job,
job_id TYPE c LENGTH 10,
calc_mode TYPE c LENGTH 1,
work_hours TYPE p LENGTH 5 DECIMALS 2,
part_count TYPE i,
mat_group TYPE c LENGTH 4,
due_date TYPE d,
net_value TYPE p LENGTH 11 DECIMALS 2,
END OF ty_job.
TYPES ty_cost TYPE p LENGTH 11 DECIMALS 2.
METHODS validate
IMPORTING is_job TYPE ty_job
RAISING {{p}}cx_job_error.
METHODS calculate_cost
IMPORTING is_job TYPE ty_job
RETURNING VALUE(rv_cost) TYPE ty_cost
RAISING {{p}}cx_job_error.
PRIVATE SECTION.
TYPES ty_mode TYPE c LENGTH 1.
TYPES ty_group TYPE c LENGTH 4.
METHODS fail_with
IMPORTING iv_reason TYPE {{p}}cx_job_error=>ty_reason
RAISING {{p}}cx_job_error.
METHODS is_mode_valid
IMPORTING iv_mode TYPE ty_mode
RETURNING VALUE(rv_valid) TYPE abap_bool.
METHODS is_hours_valid
IMPORTING iv_hours TYPE ty_job-work_hours
RETURNING VALUE(rv_valid) TYPE abap_bool.
METHODS is_group_valid
IMPORTING iv_group TYPE ty_group
RETURNING VALUE(rv_valid) TYPE abap_bool.
METHODS hour_rate
IMPORTING iv_mode TYPE ty_mode
RETURNING VALUE(rv_rate) TYPE ty_cost.
METHODS part_rate
IMPORTING iv_group TYPE ty_group
RETURNING VALUE(rv_rate) TYPE ty_cost.
ENDCLASS.
CLASS {{p}}job_planner IMPLEMENTATION.
METHOD validate.
IF is_job-job_id IS INITIAL.
fail_with( {{p}}cx_job_error=>c_reason-no_job_id ).
ENDIF.
IF is_mode_valid( is_job-calc_mode ) = abap_false.
fail_with( {{p}}cx_job_error=>c_reason-bad_mode ).
ENDIF.
IF is_hours_valid( is_job-work_hours ) = abap_false.
fail_with( {{p}}cx_job_error=>c_reason-bad_hours ).
ENDIF.
IF is_job-part_count <= 0.
fail_with( {{p}}cx_job_error=>c_reason-bad_count ).
ENDIF.
IF is_group_valid( is_job-mat_group ) = abap_false.
fail_with( {{p}}cx_job_error=>c_reason-bad_group ).
ENDIF.
IF is_job-due_date IS INITIAL.
fail_with( {{p}}cx_job_error=>c_reason-bad_date ).
ENDIF.
IF is_job-net_value < 0.
fail_with( {{p}}cx_job_error=>c_reason-bad_value ).
ENDIF.
ENDMETHOD.
METHOD calculate_cost.
validate( is_job ).
rv_cost = is_job-work_hours * hour_rate( is_job-calc_mode )
+ is_job-part_count * part_rate( is_job-mat_group )
+ is_job-net_value.
ENDMETHOD.
METHOD fail_with.
RAISE EXCEPTION TYPE {{p}}cx_job_error
EXPORTING
iv_reason = iv_reason.
ENDMETHOD.
METHOD is_mode_valid.
rv_valid = xsdbool( iv_mode = 'S' OR iv_mode = 'E' OR iv_mode = 'P' ).
ENDMETHOD.
METHOD is_hours_valid.
rv_valid = xsdbool( iv_hours > 0 AND iv_hours <= 100 ).
ENDMETHOD.
METHOD is_group_valid.
rv_valid = xsdbool( iv_group = 'ST' OR iv_group = 'AL'
OR iv_group = 'PL' OR iv_group = 'WD' ).
ENDMETHOD.
METHOD hour_rate.
CASE iv_mode.
WHEN 'S'.
rv_rate = '60.00'.
WHEN 'E'.
rv_rate = '90.00'.
WHEN 'P'.
rv_rate = '75.00'.
WHEN OTHERS.
rv_rate = 0.
ENDCASE.
ENDMETHOD.
METHOD part_rate.
CASE iv_group.
WHEN 'ST'.
rv_rate = '12.50'.
WHEN 'AL'.
rv_rate = '8.00'.
WHEN 'PL'.
rv_rate = '3.50'.
WHEN 'WD'.
rv_rate = '5.00'.
WHEN OTHERS.
rv_rate = 0.
ENDCASE.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,95 @@
CLASS ltc_job_planner DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA mo_cut TYPE REF TO {{p}}job_planner.
METHODS setup.
METHODS valid_job RETURNING VALUE(rs_job) TYPE {{p}}job_planner=>ty_job.
METHODS standard_cost FOR TESTING.
METHODS express_cost FOR TESTING.
METHODS missing_id FOR TESTING.
METHODS bad_hours FOR TESTING.
METHODS bad_group FOR TESTING.
ENDCLASS.
CLASS ltc_job_planner IMPLEMENTATION.
METHOD setup.
mo_cut = NEW {{p}}job_planner( ).
ENDMETHOD.
METHOD valid_job.
rs_job = VALUE #( job_id = 'JOB-1000'
calc_mode = 'S'
work_hours = '10.00'
part_count = 4
mat_group = 'ST'
due_date = '20250601'
net_value = '100.00' ).
ENDMETHOD.
METHOD standard_cost.
DATA lv_exp TYPE {{p}}job_planner=>ty_cost.
lv_exp = '750.00'.
TRY.
cl_abap_unit_assert=>assert_equals( exp = lv_exp
act = mo_cut->calculate_cost( valid_job( ) ) ).
CATCH {{p}}cx_job_error.
cl_abap_unit_assert=>fail( `unexpected exception` ).
ENDTRY.
ENDMETHOD.
METHOD express_cost.
DATA ls_job TYPE {{p}}job_planner=>ty_job.
DATA lv_exp TYPE {{p}}job_planner=>ty_cost.
ls_job = valid_job( ).
ls_job-calc_mode = 'E'.
ls_job-work_hours = '2.50'.
ls_job-part_count = 2.
ls_job-mat_group = 'AL'.
ls_job-net_value = 0.
lv_exp = '241.00'.
TRY.
cl_abap_unit_assert=>assert_equals( exp = lv_exp
act = mo_cut->calculate_cost( ls_job ) ).
CATCH {{p}}cx_job_error.
cl_abap_unit_assert=>fail( `unexpected exception` ).
ENDTRY.
ENDMETHOD.
METHOD missing_id.
DATA(ls_job) = valid_job( ).
ls_job-job_id = ''.
TRY.
mo_cut->validate( ls_job ).
cl_abap_unit_assert=>fail( `NO_JOB_ID expected` ).
CATCH {{p}}cx_job_error INTO DATA(lx_error).
cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-no_job_id
act = lx_error->get_reason( ) ).
ENDTRY.
ENDMETHOD.
METHOD bad_hours.
DATA(ls_job) = valid_job( ).
ls_job-work_hours = '101.00'.
TRY.
mo_cut->validate( ls_job ).
cl_abap_unit_assert=>fail( `BAD_HOURS expected` ).
CATCH {{p}}cx_job_error INTO DATA(lx_error).
cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-bad_hours
act = lx_error->get_reason( ) ).
ENDTRY.
ENDMETHOD.
METHOD bad_group.
DATA(ls_job) = valid_job( ).
ls_job-mat_group = 'XY'.
TRY.
mo_cut->calculate_cost( ls_job ).
cl_abap_unit_assert=>fail( `BAD_GROUP expected` ).
CATCH {{p}}cx_job_error INTO DATA(lx_error).
cl_abap_unit_assert=>assert_equals( exp = {{p}}cx_job_error=>c_reason-bad_group
act = lx_error->get_reason( ) ).
ENDTRY.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,87 @@
# 1. Goal
Plan the cost of a production job in a metal workshop. The workshop office
checks a job before it accepts the job and calculates the planned cost of the
job. The office needs one clear reason for each job that it rejects.
# 2. Open questions
None.
# 3. Context
- A job order has a job ID, a calculation mode, work hours, a part count, a
material group, a due date and a material value.
- The words HOURS, MODE, ORDER, DATE, COUNT, GROUP and VALUE are reserved words
in ABAP SQL. Do not use them as names of fields, types, variables or
constants. Use the names of the contract.
- The office passes the job data to the class. No database table is necessary.
# 4. Contract
Both classes are in package $TMP.
## 4.1 Class {{P}}CX_JOB_ERROR
- The class is public, final, and inherits from CX_STATIC_CHECK.
- Public type TY_REASON, character 12.
- Public constant structure C_REASON with these components and values:
NO_JOB_ID, BAD_MODE, BAD_HOURS, BAD_COUNT, BAD_GROUP, BAD_DATE, BAD_VALUE.
- Public method CONSTRUCTOR, IMPORTING iv_reason TYPE ty_reason.
- Public method GET_REASON, RETURNING VALUE(rv_reason) TYPE ty_reason.
- Public method GET_MESSAGE, RETURNING VALUE(rv_message) TYPE string.
- Do not add other public components.
## 4.2 Class {{P}}JOB_PLANNER
- The class is public, final, and has a public constructor without parameters.
- Public type TY_JOB with these components:
- job_id TYPE c LENGTH 10
- calc_mode TYPE c LENGTH 1
- work_hours TYPE p LENGTH 5 DECIMALS 2
- part_count TYPE i
- mat_group TYPE c LENGTH 4
- due_date TYPE d
- net_value TYPE p LENGTH 11 DECIMALS 2
- Public type TY_COST, packed number, length 11, 2 decimals.
- Public method VALIDATE, IMPORTING is_job TYPE ty_job,
RAISING {{P}}CX_JOB_ERROR.
- Public method CALCULATE_COST, IMPORTING is_job TYPE ty_job,
RETURNING VALUE(rv_cost) TYPE ty_cost, RAISING {{P}}CX_JOB_ERROR.
- Do not add other public methods.
# 5. Business rules
## 5.1 Validation
VALIDATE checks the job in this sequence. It raises {{P}}CX_JOB_ERROR with the
reason of the first rule that fails. A rule after the first failing rule is not
checked.
1. job_id must not be empty. Reason NO_JOB_ID.
2. calc_mode must be S, E or P. Reason BAD_MODE.
3. work_hours must be greater than 0 and not greater than 100. Reason BAD_HOURS.
4. part_count must be greater than 0. Reason BAD_COUNT.
5. mat_group must be ST, AL, PL or WD. Reason BAD_GROUP.
6. due_date must not be empty. Reason BAD_DATE.
7. net_value must not be negative. Reason BAD_VALUE.
## 5.2 Cost
CALCULATE_COST applies all rules of 5.1 first and raises the same exception.
Then it calculates the cost:
cost = work_hours * hour rate + part_count * part rate + net_value
Hour rate: S 60.00, E 90.00, P 75.00.
Part rate: ST 12.50, AL 8.00, PL 3.50, WD 5.00.
## 5.3 Messages
GET_MESSAGE returns the message text of the reason:
- NO_JOB_ID: The job ID is missing.
- BAD_MODE: The calculation mode is not valid.
- BAD_HOURS: The work hours are not valid.
- BAD_COUNT: The part count is not valid.
- BAD_GROUP: The material group is not valid.
- BAD_DATE: The due date is not valid.
- BAD_VALUE: The material value is not valid.
# 6. Constraints
- Release target: 7.58.
- Coding standards: Clean ABAP. Method length below 40 statements. No global
variables. Pass structures by reference. No comment that restates the code.
- Out of scope: no database table, no report, no ALV list, no CDS view.
# 7. Acceptance
- Both classes are active and have no syntax error.
- The hidden tests pass.
- Write your own ABAP Unit tests for {{P}}JOB_PLANNER.

View File

@@ -0,0 +1,57 @@
{
"id": "G1136",
"category": "D",
"object_type": "CLAS",
"difficulty": 2,
"release_target": "v758",
"expected_outcome": "implement",
"budget": {
"max_tool_calls": 70,
"max_activations": 18
},
"seed": [],
"contract": [
{
"type": "CLAS",
"name": "{{P}}CX_JOB_ERROR"
},
{
"type": "CLAS",
"name": "{{P}}JOB_PLANNER"
}
],
"out_of_scope": [
"database tables",
"CDS views",
"reports",
"ALV lists",
"other exception classes"
],
"hidden_tests": [
{
"type": "CLAS",
"name": "{{P}}T_D_JOB_HIDDEN",
"file": "hidden/t_d_job_hidden.clas.abap",
"description": "Hidden tests for the job validation and the job cost"
}
],
"reference": [
{
"type": "CLAS",
"name": "{{P}}CX_JOB_ERROR",
"file": "reference/cx_job_error.clas.abap",
"description": "Class-based exception with a reason and a message text"
},
{
"type": "CLAS",
"name": "{{P}}JOB_PLANNER",
"file": "reference/job_planner.clas.abap",
"description": "Job validation and job cost calculation",
"testclasses_file": "reference/job_planner.testclasses.abap"
}
],
"craft_checks": [
"method_length",
"no_reserved_word_names"
]
}