Generator: pilot analysis and fixes; G0002-G0021 tasks; handover notes merged

- Static bundle checks (name length, seed type, reserved words, contract test classes,
  testclasses_file), local abaplint parser check before SAP, max_tokens, robust JSON parse
- Runner: G2 for CDS views with parameters, contract_check detail in report
- G0020 and G0013 fixed by review and revalidated (oracle 100, null 0)
- Pilot results in docs/faz1-tasarim.md 11e; handover notes merged into CLAUDE.md and docs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kral
2026-10-03 05:04:23 +02:00
parent 15eb9f1bb2
commit 5de5e2851c
162 changed files with 9943 additions and 23 deletions

View File

@@ -0,0 +1,14 @@
{
"id": "G0004",
"pool": "eval",
"object_type": "CLAS",
"category": "C",
"attempts": [
{
"stage": "validate",
"oracle": 100.0,
"null": 0
}
],
"accepted": true
}

View File

@@ -0,0 +1,207 @@
CLASS {{p}}stock_age_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
CONSTANTS c_key_date TYPE dats VALUE '20240401'.
DATA mo_cut TYPE REF TO {{p}}if_stock_age.
METHODS setup.
METHODS gr_date_for_age
IMPORTING iv_age TYPE i
RETURNING VALUE(rv_date) TYPE dats.
METHODS empty_input FOR TESTING.
METHODS single_batch_bucket_01 FOR TESTING.
METHODS bucket_boundaries FOR TESTING.
METHODS groups_sum_quantity_and_count FOR TESTING.
METHODS result_is_sorted FOR TESTING.
METHODS invalid_rows_are_ignored FOR TESTING.
METHODS negative_and_zero_quantity FOR TESTING.
METHODS total_groups_by_material FOR TESTING.
METHODS total_is_sorted FOR TESTING.
METHODS total_ignores_invalid_rows FOR TESTING.
ENDCLASS.
CLASS {{p}}stock_age_hidden IMPLEMENTATION.
METHOD setup.
mo_cut = NEW {{p}}stock_age( ).
ENDMETHOD.
METHOD gr_date_for_age.
rv_date = c_key_date - iv_age.
ENDMETHOD.
METHOD empty_input.
DATA(lt_result) = mo_cut->analyze(
it_batch = VALUE {{p}}if_stock_age=>tt_batch( )
iv_key_date = c_key_date ).
cl_abap_unit_assert=>assert_initial( act = lt_result ).
ENDMETHOD.
METHOD single_batch_bucket_01.
DATA(lt_result) = mo_cut->analyze(
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
( material = 'MAT-1' batch = 'B1' quantity = 10
gr_date = gr_date_for_age( 0 ) ) )
iv_key_date = c_key_date ).
cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 1 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-material
exp = 'MAT-1' ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-bucket
exp = {{p}}if_stock_age=>c_bucket-d0_30 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-quantity exp = 10 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-batch_count exp = 1 ).
ENDMETHOD.
METHOD bucket_boundaries.
DATA(lt_result) = mo_cut->analyze(
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
( material = 'MAT-B' batch = 'B1' quantity = 1 gr_date = gr_date_for_age( 30 ) )
( material = 'MAT-B' batch = 'B2' quantity = 1 gr_date = gr_date_for_age( 31 ) )
( material = 'MAT-B' batch = 'B3' quantity = 1 gr_date = gr_date_for_age( 60 ) )
( material = 'MAT-B' batch = 'B4' quantity = 1 gr_date = gr_date_for_age( 61 ) )
( material = 'MAT-B' batch = 'B5' quantity = 1 gr_date = gr_date_for_age( 90 ) )
( material = 'MAT-B' batch = 'B6' quantity = 1 gr_date = gr_date_for_age( 91 ) ) )
iv_key_date = c_key_date ).
cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 4 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-bucket
exp = {{p}}if_stock_age=>c_bucket-d0_30 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-batch_count exp = 1 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-bucket
exp = {{p}}if_stock_age=>c_bucket-d31_60 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-batch_count exp = 2 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 3 ]-bucket
exp = {{p}}if_stock_age=>c_bucket-d61_90 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 3 ]-batch_count exp = 2 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 4 ]-bucket
exp = {{p}}if_stock_age=>c_bucket-d91_plus ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 4 ]-batch_count exp = 1 ).
ENDMETHOD.
METHOD groups_sum_quantity_and_count.
DATA(lt_result) = mo_cut->analyze(
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
( material = 'MAT-A' batch = 'B1' quantity = 10 gr_date = gr_date_for_age( 1 ) )
( material = 'MAT-A' batch = 'B2' quantity = 5 gr_date = gr_date_for_age( 2 ) )
( material = 'MAT-A' batch = 'B3' quantity = 2 gr_date = gr_date_for_age( 200 ) ) )
iv_key_date = c_key_date ).
cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 2 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-bucket
exp = {{p}}if_stock_age=>c_bucket-d0_30 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-quantity exp = 15 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-batch_count exp = 2 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-bucket
exp = {{p}}if_stock_age=>c_bucket-d91_plus ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-quantity exp = 2 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-batch_count exp = 1 ).
ENDMETHOD.
METHOD result_is_sorted.
DATA(lt_result) = mo_cut->analyze(
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
( material = 'MAT-C' batch = 'B1' quantity = 1 gr_date = gr_date_for_age( 200 ) )
( material = 'MAT-A' batch = 'B2' quantity = 1 gr_date = gr_date_for_age( 40 ) )
( material = 'MAT-B' batch = 'B3' quantity = 1 gr_date = gr_date_for_age( 1 ) )
( material = 'MAT-A' batch = 'B4' quantity = 1 gr_date = gr_date_for_age( 1 ) )
( material = 'MAT-C' batch = 'B5' quantity = 1 gr_date = gr_date_for_age( 1 ) )
( material = 'MAT-B' batch = 'B6' quantity = 1 gr_date = gr_date_for_age( 70 ) ) )
iv_key_date = c_key_date ).
DATA(lt_expected) = VALUE {{p}}if_stock_age=>tt_age_row(
( material = 'MAT-A' bucket = {{p}}if_stock_age=>c_bucket-d0_30 )
( material = 'MAT-A' bucket = {{p}}if_stock_age=>c_bucket-d31_60 )
( material = 'MAT-B' bucket = {{p}}if_stock_age=>c_bucket-d0_30 )
( material = 'MAT-B' bucket = {{p}}if_stock_age=>c_bucket-d61_90 )
( material = 'MAT-C' bucket = {{p}}if_stock_age=>c_bucket-d0_30 )
( material = 'MAT-C' bucket = {{p}}if_stock_age=>c_bucket-d91_plus ) ).
cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 6 ).
DATA lv_index TYPE i.
LOOP AT lt_expected INTO DATA(ls_expected).
lv_index = lv_index + 1.
cl_abap_unit_assert=>assert_equals( act = lt_result[ lv_index ]-material
exp = ls_expected-material ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ lv_index ]-bucket
exp = ls_expected-bucket ).
ENDLOOP.
ENDMETHOD.
METHOD invalid_rows_are_ignored.
DATA(lt_result) = mo_cut->analyze(
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
( material = 'MAT-A' batch = 'B1' quantity = 10 gr_date = gr_date_for_age( 5 ) )
( material = 'MAT-A' batch = 'B2' quantity = 99 gr_date = '00000000' )
( material = 'MAT-A' batch = 'B3' quantity = 99 gr_date = gr_date_for_age( -5 ) ) )
iv_key_date = c_key_date ).
cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 1 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-quantity exp = 10 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-batch_count exp = 1 ).
ENDMETHOD.
METHOD negative_and_zero_quantity.
DATA(lt_result) = mo_cut->analyze(
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
( material = 'MAT-A' batch = 'B1' quantity = 10 gr_date = gr_date_for_age( 5 ) )
( material = 'MAT-A' batch = 'B2' quantity = -4 gr_date = gr_date_for_age( 6 ) )
( material = 'MAT-B' batch = 'B3' quantity = 5 gr_date = gr_date_for_age( 5 ) )
( material = 'MAT-B' batch = 'B4' quantity = -5 gr_date = gr_date_for_age( 6 ) ) )
iv_key_date = c_key_date ).
cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 2 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-material exp = 'MAT-A' ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-quantity exp = 6 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-batch_count exp = 2 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-material exp = 'MAT-B' ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-quantity exp = 0 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-batch_count exp = 2 ).
ENDMETHOD.
METHOD total_groups_by_material.
DATA(lt_result) = mo_cut->total_by_material(
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
( material = 'MAT-A' batch = 'B1' quantity = 10 gr_date = gr_date_for_age( 5 ) )
( material = 'MAT-A' batch = 'B2' quantity = 5 gr_date = gr_date_for_age( 100 ) )
( material = 'MAT-B' batch = 'B3' quantity = 7 gr_date = gr_date_for_age( 5 ) ) )
iv_key_date = c_key_date ).
cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 2 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-material exp = 'MAT-A' ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-quantity exp = 15 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-batch_count exp = 2 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-material exp = 'MAT-B' ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-quantity exp = 7 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-batch_count exp = 1 ).
ENDMETHOD.
METHOD total_is_sorted.
DATA(lt_result) = mo_cut->total_by_material(
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
( material = 'MAT-Z' batch = 'B1' quantity = 1 gr_date = gr_date_for_age( 5 ) )
( material = 'MAT-A' batch = 'B2' quantity = 1 gr_date = gr_date_for_age( 5 ) )
( material = 'MAT-M' batch = 'B3' quantity = 1 gr_date = gr_date_for_age( 5 ) ) )
iv_key_date = c_key_date ).
cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 3 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-material exp = 'MAT-A' ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-material exp = 'MAT-M' ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 3 ]-material exp = 'MAT-Z' ).
ENDMETHOD.
METHOD total_ignores_invalid_rows.
DATA(lt_result) = mo_cut->total_by_material(
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
( material = 'MAT-A' batch = 'B1' quantity = 99 gr_date = '00000000' )
( material = 'MAT-A' batch = 'B2' quantity = 99 gr_date = gr_date_for_age( -1 ) ) )
iv_key_date = c_key_date ).
cl_abap_unit_assert=>assert_initial( act = lt_result ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,92 @@
CLASS {{p}}stock_age DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES {{p}}if_stock_age.
PRIVATE SECTION.
TYPES tt_age_group TYPE HASHED TABLE OF {{p}}if_stock_age=>ty_age_row
WITH UNIQUE KEY material bucket.
TYPES tt_total_group TYPE HASHED TABLE OF {{p}}if_stock_age=>ty_total
WITH UNIQUE KEY material.
METHODS age_in_days
IMPORTING is_batch TYPE {{p}}if_stock_age=>ty_batch
iv_key_date TYPE dats
RETURNING VALUE(rv_age) TYPE i.
METHODS bucket_of
IMPORTING iv_age TYPE i
RETURNING VALUE(rv_bucket) TYPE {{p}}if_stock_age=>ty_bucket.
ENDCLASS.
CLASS {{p}}stock_age IMPLEMENTATION.
METHOD {{p}}if_stock_age~analyze.
DATA lt_group TYPE tt_age_group.
LOOP AT it_batch INTO DATA(ls_batch).
DATA(lv_age) = age_in_days( is_batch = ls_batch iv_key_date = iv_key_date ).
IF lv_age < 0.
CONTINUE.
ENDIF.
DATA(lv_bucket) = bucket_of( lv_age ).
ASSIGN lt_group[ material = ls_batch-material
bucket = lv_bucket ] TO FIELD-SYMBOL(<ls_group>).
IF sy-subrc <> 0.
INSERT VALUE #( material = ls_batch-material bucket = lv_bucket )
INTO TABLE lt_group.
ASSIGN lt_group[ material = ls_batch-material
bucket = lv_bucket ] TO <ls_group>.
ENDIF.
<ls_group>-quantity = <ls_group>-quantity + ls_batch-quantity.
<ls_group>-batch_count = <ls_group>-batch_count + 1.
ENDLOOP.
rt_age = lt_group.
SORT rt_age BY material bucket.
ENDMETHOD.
METHOD {{p}}if_stock_age~total_by_material.
DATA lt_group TYPE tt_total_group.
LOOP AT it_batch INTO DATA(ls_batch).
IF age_in_days( is_batch = ls_batch iv_key_date = iv_key_date ) < 0.
CONTINUE.
ENDIF.
ASSIGN lt_group[ material = ls_batch-material ] TO FIELD-SYMBOL(<ls_group>).
IF sy-subrc <> 0.
INSERT VALUE #( material = ls_batch-material ) INTO TABLE lt_group.
ASSIGN lt_group[ material = ls_batch-material ] TO <ls_group>.
ENDIF.
<ls_group>-quantity = <ls_group>-quantity + ls_batch-quantity.
<ls_group>-batch_count = <ls_group>-batch_count + 1.
ENDLOOP.
rt_total = lt_group.
SORT rt_total BY material.
ENDMETHOD.
METHOD age_in_days.
IF is_batch-gr_date IS INITIAL OR is_batch-gr_date > iv_key_date.
rv_age = -1.
ELSE.
rv_age = iv_key_date - is_batch-gr_date.
ENDIF.
ENDMETHOD.
METHOD bucket_of.
IF iv_age <= 30.
rv_bucket = {{p}}if_stock_age=>c_bucket-d0_30.
ELSEIF iv_age <= 60.
rv_bucket = {{p}}if_stock_age=>c_bucket-d31_60.
ELSEIF iv_age <= 90.
rv_bucket = {{p}}if_stock_age=>c_bucket-d61_90.
ELSE.
rv_bucket = {{p}}if_stock_age=>c_bucket-d91_plus.
ENDIF.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,80 @@
CLASS ltc_stock_age DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
CONSTANTS c_key_date TYPE dats VALUE '20240401'.
DATA mo_cut TYPE REF TO {{p}}if_stock_age.
METHODS setup.
METHODS gr_date_for_age
IMPORTING iv_age TYPE i
RETURNING VALUE(rv_date) TYPE dats.
METHODS empty_input FOR TESTING.
METHODS bucket_01 FOR TESTING.
METHODS bucket_04 FOR TESTING.
METHODS invalid_rows FOR TESTING.
METHODS total_by_material FOR TESTING.
ENDCLASS.
CLASS ltc_stock_age IMPLEMENTATION.
METHOD setup.
mo_cut = NEW {{p}}stock_age( ).
ENDMETHOD.
METHOD gr_date_for_age.
rv_date = c_key_date - iv_age.
ENDMETHOD.
METHOD empty_input.
cl_abap_unit_assert=>assert_initial(
act = mo_cut->analyze( it_batch = VALUE {{p}}if_stock_age=>tt_batch( )
iv_key_date = c_key_date ) ).
ENDMETHOD.
METHOD bucket_01.
DATA(lt_result) = mo_cut->analyze(
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
( material = 'MAT-1' batch = 'B1' quantity = 10
gr_date = gr_date_for_age( 30 ) ) )
iv_key_date = c_key_date ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-bucket
exp = {{p}}if_stock_age=>c_bucket-d0_30 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-quantity exp = 10 ).
ENDMETHOD.
METHOD bucket_04.
DATA(lt_result) = mo_cut->analyze(
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
( material = 'MAT-1' batch = 'B1' quantity = 10
gr_date = gr_date_for_age( 91 ) ) )
iv_key_date = c_key_date ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-bucket
exp = {{p}}if_stock_age=>c_bucket-d91_plus ).
ENDMETHOD.
METHOD invalid_rows.
DATA(lt_result) = mo_cut->analyze(
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
( material = 'MAT-1' batch = 'B1' quantity = 10 gr_date = '00000000' ) )
iv_key_date = c_key_date ).
cl_abap_unit_assert=>assert_initial( act = lt_result ).
ENDMETHOD.
METHOD total_by_material.
DATA(lt_result) = mo_cut->total_by_material(
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
( material = 'MAT-2' batch = 'B1' quantity = 10 gr_date = gr_date_for_age( 1 ) )
( material = 'MAT-1' batch = 'B2' quantity = 5 gr_date = gr_date_for_age( 100 ) ) )
iv_key_date = c_key_date ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-material exp = 'MAT-1' ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-quantity exp = 5 ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-material exp = 'MAT-2' ).
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-quantity exp = 10 ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,45 @@
INTERFACE {{p}}if_stock_age PUBLIC.
TYPES:
ty_material TYPE c LENGTH 10,
ty_batch_id TYPE c LENGTH 10,
ty_bucket TYPE c LENGTH 2,
ty_quantity TYPE p LENGTH 13 DECIMALS 3,
BEGIN OF ty_batch,
material TYPE ty_material,
batch TYPE ty_batch_id,
quantity TYPE ty_quantity,
gr_date TYPE dats,
END OF ty_batch,
tt_batch TYPE STANDARD TABLE OF ty_batch WITH EMPTY KEY,
BEGIN OF ty_age_row,
material TYPE ty_material,
bucket TYPE ty_bucket,
quantity TYPE ty_quantity,
batch_count TYPE i,
END OF ty_age_row,
tt_age_row TYPE STANDARD TABLE OF ty_age_row WITH EMPTY KEY,
BEGIN OF ty_total,
material TYPE ty_material,
quantity TYPE ty_quantity,
batch_count TYPE i,
END OF ty_total,
tt_total TYPE STANDARD TABLE OF ty_total WITH EMPTY KEY.
CONSTANTS:
BEGIN OF c_bucket,
d0_30 TYPE ty_bucket VALUE '01',
d31_60 TYPE ty_bucket VALUE '02',
d61_90 TYPE ty_bucket VALUE '03',
d91_plus TYPE ty_bucket VALUE '04',
END OF c_bucket.
METHODS analyze
IMPORTING it_batch TYPE tt_batch
iv_key_date TYPE dats
RETURNING VALUE(rt_age) TYPE tt_age_row.
METHODS total_by_material
IMPORTING it_batch TYPE tt_batch
iv_key_date TYPE dats
RETURNING VALUE(rt_total) TYPE tt_total.
ENDINTERFACE.

View File

@@ -0,0 +1,80 @@
# 1. Goal
Analyse the age of stock batches. A warehouse report calls the analysis before it
prints the stock aging report. The report needs the grouped quantity and the number
of batches for each material and age bucket.
# 2. Open questions
None.
# 3. Context
- The interface {{P}}IF_STOCK_AGE exists in package $TMP. It is active.
- The interface defines these types:
- ty_material: character, length 10.
- ty_batch_id: character, length 10.
- ty_bucket: character, length 2.
- ty_quantity: packed number, length 13, 3 decimals.
- ty_batch: structure with the components material, batch, quantity, gr_date.
- tt_batch: table of ty_batch.
- ty_age_row: structure with the components material, bucket, quantity, batch_count.
- tt_age_row: table of ty_age_row.
- ty_total: structure with the components material, quantity, batch_count.
- tt_total: table of ty_total.
- The interface defines the constant structure C_BUCKET with the components
D0_30 = '01', D31_60 = '02', D61_90 = '03', D91_PLUS = '04'.
- The interface defines two methods:
- analyze: importing it_batch type tt_batch and iv_key_date type dats,
returning rt_age type tt_age_row.
- total_by_material: importing it_batch type tt_batch and iv_key_date type dats,
returning rt_total type tt_total.
# 4. Contract
- Create the class {{P}}STOCK_AGE in package $TMP.
- The class is public, final, and has a public constructor without parameters.
- The class implements {{P}}IF_STOCK_AGE.
- Do not add other public methods.
# 5. Business rules
Both methods use the same validity rules and the same age buckets.
Validity
1. A row of IT_BATCH is valid when its goods receipt date is not initial and is not
after IV_KEY_DATE.
2. A row that is not valid is ignored. It must not change any result.
Age and bucket
3. The age of a batch is the number of days between its goods receipt date and
IV_KEY_DATE.
4. The bucket of a batch is:
- age 0 to 30 days: bucket 01 (C_BUCKET-D0_30)
- age 31 to 60 days: bucket 02 (C_BUCKET-D31_60)
- age 61 to 90 days: bucket 03 (C_BUCKET-D61_90)
- age more than 90 days: bucket 04 (C_BUCKET-D91_PLUS)
Method ANALYZE
5. Group the valid rows by material and bucket.
6. For each group, sum the quantity and count the valid rows.
7. Return one row per group. Sort the result by material ascending and then by
bucket ascending.
8. A group is in the result as soon as one valid row belongs to it, also when the
summed quantity is zero.
9. If no valid row exists, return an empty table.
Method TOTAL_BY_MATERIAL
10. Group the valid rows by material only.
11. For each group, sum the quantity and count the valid rows.
12. Return one row per material. Sort the result by material ascending.
13. If no valid row exists, return an empty table.
Quantity
14. The quantity of a batch can be negative. Add the quantities as they are.
# 6. Constraints
- Release target: SAP_BASIS 8.16 (ABAP Platform 2025).
- Coding standards: Clean ABAP. Method length below 40 statements. No global
variables. No comment that restates the code. Pass large parameters by reference.
- Out of scope: do not change {{P}}IF_STOCK_AGE.
# 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,53 @@
{
"id": "G0004",
"category": "C",
"object_type": "CLAS",
"difficulty": 2,
"release_target": "v816",
"expected_outcome": "implement",
"budget": {
"max_tool_calls": 60,
"max_activations": 12
},
"seed": [
{
"type": "INTF",
"name": "{{P}}IF_STOCK_AGE",
"file": "seed/if_stock_age.intf.abap",
"description": "Stock age contract: row types, bucket constants and the two analysis methods"
}
],
"contract": [
{
"type": "CLAS",
"name": "{{P}}STOCK_AGE",
"implements": "{{P}}IF_STOCK_AGE"
}
],
"out_of_scope": [
"{{P}}IF_STOCK_AGE"
],
"hidden_tests": [
{
"type": "CLAS",
"name": "{{P}}STOCK_AGE_HIDDEN",
"file": "hidden/stock_age_hidden.clas.abap",
"description": "Hidden tests for the stock age analysis"
}
],
"reference": [
{
"type": "CLAS",
"name": "{{P}}STOCK_AGE",
"file": "reference/stock_age.clas.abap",
"description": "Stock age analysis",
"testclasses_file": "reference/stock_age.testclasses.abap"
}
],
"craft_checks": [
"table_kind",
"access_path",
"method_length",
"no_nested_loops"
]
}