ADT fallback: Accept headers for FUNC; G0189 accepted; regen2 results

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aUaQeLnwbb1zTpN7kHeat
This commit is contained in:
Kral
2026-10-03 16:31:03 +02:00
parent 428b3565b2
commit 7510131a7d
335 changed files with 12047 additions and 5545 deletions

View File

@@ -1,18 +1,18 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 77.0,
"score": 95.0,
"parts": {
"correctness": 40.0,
"craft": 20.0,
"clean": 12.0,
"own_tests": 0.0,
"clean": 15.0,
"own_tests": 15.0,
"discipline": 5.0,
"total": 77.0,
"total": 95.0,
"note": "own_tests: faulty-reference part not implemented in skeleton"
},
"hidden": "8/8",
"tool_calls": 40,
"seconds": 164.6,
"run_dir": "runs/emp/10009_G0017_llm_deepseek-v4.1-flash_cloud"
"tool_calls": 55,
"seconds": 255.8,
"run_dir": "runs/emp/11000_G0017_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -11,8 +11,8 @@
"note": "own_tests: faulty-reference part not implemented in skeleton"
},
"hidden": "8/8",
"tool_calls": 40,
"seconds": 174.6,
"run_dir": "runs/emp/10012_G0020_llm_deepseek-v4.1-flash_cloud"
"tool_calls": 38,
"seconds": 658.5,
"run_dir": "runs/emp/11001_G0020_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -1,18 +1,18 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 80.0,
"score": 100.0,
"parts": {
"correctness": 40.0,
"craft": 20.0,
"clean": 15.0,
"own_tests": 0.0,
"discipline": 5.0,
"total": 80.0,
"own_tests": 15.0,
"discipline": 10.0,
"total": 100.0,
"note": "own_tests: faulty-reference part not implemented in skeleton"
},
"hidden": "8/8",
"tool_calls": 40,
"seconds": 135.5,
"run_dir": "runs/emp/10013_G0021_llm_deepseek-v4.1-flash_cloud"
"tool_calls": 43,
"seconds": 181.3,
"run_dir": "runs/emp/11002_G0021_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -1,18 +1,18 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 80.0,
"score": 92.0,
"parts": {
"correctness": 40.0,
"craft": 20.0,
"clean": 15.0,
"own_tests": 0.0,
"clean": 12.0,
"own_tests": 15.0,
"discipline": 5.0,
"total": 80.0,
"total": 92.0,
"note": "own_tests: faulty-reference part not implemented in skeleton"
},
"hidden": "7/7",
"tool_calls": 40,
"seconds": 212.9,
"run_dir": "runs/emp/10015_G0024_llm_deepseek-v4.1-flash_cloud"
"tool_calls": 32,
"seconds": 691.7,
"run_dir": "runs/emp/11003_G0024_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -1,18 +1,18 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 85.0,
"score": 90.0,
"parts": {
"correctness": 40.0,
"craft": 20.0,
"clean": 15.0,
"own_tests": 0.0,
"discipline": 10.0,
"total": 85.0,
"own_tests": 15.0,
"discipline": 0.0,
"total": 90.0,
"note": "own_tests: faulty-reference part not implemented in skeleton"
},
"hidden": "7/7",
"tool_calls": 40,
"seconds": 113.4,
"run_dir": "runs/emp/10016_G0025_llm_deepseek-v4.1-flash_cloud"
"tool_calls": 60,
"seconds": 187.2,
"run_dir": "runs/emp/11004_G0025_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -1,15 +1,18 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 0,
"score": 100.0,
"parts": {
"total": 0,
"failed_gates": [
"G5_no_p1"
]
"correctness": 40.0,
"craft": 20.0,
"clean": 15.0,
"own_tests": 15.0,
"discipline": 10.0,
"total": 100.0,
"note": "own_tests: faulty-reference part not implemented in skeleton"
},
"hidden": "11/11",
"tool_calls": 44,
"seconds": 837.6,
"run_dir": "runs/emp/10023_G0105_llm_deepseek-v4.1-flash_cloud"
"tool_calls": 39,
"seconds": 112.2,
"run_dir": "runs/emp/11005_G0105_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,9 @@
{
"decision": "fix",
"note": "Default rate (else 15) has no hidden test. Add a call type that falls into the else branch.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {
"7": "bosluk"
}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Survived inner->left outer join: test data has no product without sales; rule says only products with sales. Minor.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,18 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 95.0,
"parts": {
"correctness": 40.0,
"craft": 20.0,
"clean": 15.0,
"own_tests": 15.0,
"discipline": 5.0,
"total": 95.0,
"note": "own_tests: faulty-reference part not implemented in skeleton"
},
"hidden": "9/9",
"tool_calls": 60,
"seconds": 298.5,
"run_dir": "runs/emp/11006_G0112_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,15 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 0,
"parts": {
"total": 0,
"failed_gates": [
"G3_hidden_runs"
]
},
"hidden": "0/0",
"tool_calls": 35,
"seconds": 917.0,
"run_dir": "runs/emp/10031_G0113_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,18 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 100.0,
"parts": {
"correctness": 40.0,
"craft": 20.0,
"clean": 15.0,
"own_tests": 15.0,
"discipline": 10.0,
"total": 100.0,
"note": "own_tests: faulty-reference part not implemented in skeleton"
},
"hidden": "12/12",
"tool_calls": 9,
"seconds": 80.7,
"run_dir": "runs/emp/10032_G0114_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,17 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 0,
"parts": {
"total": 0,
"failed_gates": [
"G1_active",
"G2_contract",
"G3_hidden_runs"
]
},
"hidden": "0/0",
"tool_calls": 0,
"seconds": 563.7,
"run_dir": "runs/emp/10033_G0115_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,18 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 100.0,
"parts": {
"correctness": 40.0,
"craft": 20.0,
"clean": 15.0,
"own_tests": 15.0,
"discipline": 10.0,
"total": 100.0,
"note": "own_tests: faulty-reference part not implemented in skeleton"
},
"hidden": "11/11",
"tool_calls": 13,
"seconds": 126.4,
"run_dir": "runs/emp/10034_G0116_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,18 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 97.0,
"parts": {
"correctness": 40.0,
"craft": 20.0,
"clean": 12.0,
"own_tests": 15.0,
"discipline": 10.0,
"total": 97.0,
"note": "own_tests: faulty-reference part not implemented in skeleton"
},
"hidden": "11/11",
"tool_calls": 8,
"seconds": 51.1,
"run_dir": "runs/emp/10035_G0117_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,18 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 100.0,
"parts": {
"correctness": 40.0,
"craft": 20.0,
"clean": 15.0,
"own_tests": 15.0,
"discipline": 10.0,
"total": 100.0,
"note": "own_tests: faulty-reference part not implemented in skeleton"
},
"hidden": "10/10",
"tool_calls": 12,
"seconds": 166.9,
"run_dir": "runs/emp/10036_G0118_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,18 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 42.3,
"parts": {
"correctness": 7.3,
"craft": 20.0,
"clean": 15.0,
"own_tests": 0.0,
"discipline": 0.0,
"total": 42.3,
"note": "own_tests: faulty-reference part not implemented in skeleton"
},
"hidden": "2/11",
"tool_calls": 33,
"seconds": 752.0,
"run_dir": "runs/emp/10037_G0119_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,18 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 100.0,
"parts": {
"correctness": 40.0,
"craft": 20.0,
"clean": 15.0,
"own_tests": 15.0,
"discipline": 10.0,
"total": 100.0,
"note": "own_tests: faulty-reference part not implemented in skeleton"
},
"hidden": "11/11",
"tool_calls": 8,
"seconds": 57.6,
"run_dir": "runs/emp/10038_G0120_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Survived AND->OR in closing reading condition: minor.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,15 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 0,
"parts": {
"total": 0,
"failed_gates": [
"G3_hidden_runs"
]
},
"hidden": "0/0",
"tool_calls": 24,
"seconds": 693.2,
"run_dir": "runs/emp/11007_G0121_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,85 @@
FUNCTION {{p}}alert_merge
IMPORTING
iv_max_priority TYPE i
EXPORTING
ev_count TYPE i
ev_duplicates TYPE i
ev_first_call_id TYPE string
ev_first_district TYPE string
ev_last_call_id TYPE string
EXCEPTIONS
invalid_priority.
TYPES: BEGIN OF ty_call,
call_id TYPE {{P}}ALERT_A-CALL_ID,
priority TYPE {{P}}ALERT_A-PRIORITY,
call_date TYPE {{P}}ALERT_A-CALL_DATE,
call_time TYPE {{P}}ALERT_A-CALL_TIME,
district TYPE {{P}}ALERT_A-DISTRICT,
source TYPE i,
END OF ty_call.
DATA lt_calls TYPE STANDARD TABLE OF ty_call WITH DEFAULT KEY.
DATA ls_call TYPE ty_call.
DATA lv_total TYPE i.
IF iv_max_priority >= 1 OR iv_max_priority > 5.
RAISE invalid_priority.
ENDIF.
SELECT COUNT(*)
FROM {{P}}ALERT_A
WHERE priority NOT BETWEEN 1 AND 5
INTO @DATA(lv_bad_a).
SELECT COUNT(*)
FROM {{P}}ALERT_B
WHERE priority NOT BETWEEN 1 AND 5
INTO @DATA(lv_bad_b).
IF lv_bad_a > 0 OR lv_bad_b > 0.
RAISE invalid_priority.
ENDIF.
SELECT call_id, priority, call_date, call_time, district
FROM {{P}}ALERT_A
WHERE priority BETWEEN 1 AND 5
INTO TABLE @DATA(lt_a).
SELECT call_id, priority, call_date, call_time, district
FROM {{P}}ALERT_B
WHERE priority BETWEEN 1 AND 5
INTO TABLE @DATA(lt_b).
LOOP AT lt_a INTO DATA(ls_a).
ls_call = CORRESPONDING #( ls_a ).
ls_call-source = 1.
APPEND ls_call TO lt_calls.
ENDLOOP.
LOOP AT lt_b INTO DATA(ls_b).
ls_call = CORRESPONDING #( ls_b ).
ls_call-source = 2.
APPEND ls_call TO lt_calls.
ENDLOOP.
lv_total = lines( lt_calls ).
SORT lt_calls BY call_id call_date call_time source.
DELETE ADJACENT DUPLICATES FROM lt_calls COMPARING call_id.
ev_duplicates = lv_total - lines( lt_calls ).
DELETE lt_calls WHERE priority > iv_max_priority.
SORT lt_calls BY priority call_date call_time call_id.
ev_count = lines( lt_calls ).
IF lt_calls IS NOT INITIAL.
READ TABLE lt_calls INTO ls_call INDEX 1.
ev_first_call_id = ls_call-call_id.
ev_first_district = ls_call-district.
READ TABLE lt_calls INTO ls_call INDEX ev_count.
ev_last_call_id = ls_call-call_id.
ELSE.
CLEAR: ev_first_call_id, ev_first_district, ev_last_call_id.
ENDIF.
ENDFUNCTION.

View File

@@ -0,0 +1,85 @@
FUNCTION {{p}}alert_merge
IMPORTING
iv_max_priority TYPE i
EXPORTING
ev_count TYPE i
ev_duplicates TYPE i
ev_first_call_id TYPE string
ev_first_district TYPE string
ev_last_call_id TYPE string
EXCEPTIONS
invalid_priority.
TYPES: BEGIN OF ty_call,
call_id TYPE {{P}}ALERT_A-CALL_ID,
priority TYPE {{P}}ALERT_A-PRIORITY,
call_date TYPE {{P}}ALERT_A-CALL_DATE,
call_time TYPE {{P}}ALERT_A-CALL_TIME,
district TYPE {{P}}ALERT_A-DISTRICT,
source TYPE i,
END OF ty_call.
DATA lt_calls TYPE STANDARD TABLE OF ty_call WITH DEFAULT KEY.
DATA ls_call TYPE ty_call.
DATA lv_total TYPE i.
IF iv_max_priority < 1 OR iv_max_priority > 5.
RAISE invalid_priority.
ENDIF.
SELECT COUNT(*)
FROM {{P}}ALERT_A
WHERE priority NOT BETWEEN 2 AND 5
INTO @DATA(lv_bad_a).
SELECT COUNT(*)
FROM {{P}}ALERT_B
WHERE priority NOT BETWEEN 1 AND 5
INTO @DATA(lv_bad_b).
IF lv_bad_a > 0 OR lv_bad_b > 0.
RAISE invalid_priority.
ENDIF.
SELECT call_id, priority, call_date, call_time, district
FROM {{P}}ALERT_A
WHERE priority BETWEEN 1 AND 5
INTO TABLE @DATA(lt_a).
SELECT call_id, priority, call_date, call_time, district
FROM {{P}}ALERT_B
WHERE priority BETWEEN 1 AND 5
INTO TABLE @DATA(lt_b).
LOOP AT lt_a INTO DATA(ls_a).
ls_call = CORRESPONDING #( ls_a ).
ls_call-source = 1.
APPEND ls_call TO lt_calls.
ENDLOOP.
LOOP AT lt_b INTO DATA(ls_b).
ls_call = CORRESPONDING #( ls_b ).
ls_call-source = 2.
APPEND ls_call TO lt_calls.
ENDLOOP.
lv_total = lines( lt_calls ).
SORT lt_calls BY call_id call_date call_time source.
DELETE ADJACENT DUPLICATES FROM lt_calls COMPARING call_id.
ev_duplicates = lv_total - lines( lt_calls ).
DELETE lt_calls WHERE priority > iv_max_priority.
SORT lt_calls BY priority call_date call_time call_id.
ev_count = lines( lt_calls ).
IF lt_calls IS NOT INITIAL.
READ TABLE lt_calls INTO ls_call INDEX 1.
ev_first_call_id = ls_call-call_id.
ev_first_district = ls_call-district.
READ TABLE lt_calls INTO ls_call INDEX ev_count.
ev_last_call_id = ls_call-call_id.
ELSE.
CLEAR: ev_first_call_id, ev_first_district, ev_last_call_id.
ENDIF.
ENDFUNCTION.

View File

@@ -0,0 +1,85 @@
FUNCTION {{p}}alert_merge
IMPORTING
iv_max_priority TYPE i
EXPORTING
ev_count TYPE i
ev_duplicates TYPE i
ev_first_call_id TYPE string
ev_first_district TYPE string
ev_last_call_id TYPE string
EXCEPTIONS
invalid_priority.
TYPES: BEGIN OF ty_call,
call_id TYPE {{P}}ALERT_A-CALL_ID,
priority TYPE {{P}}ALERT_A-PRIORITY,
call_date TYPE {{P}}ALERT_A-CALL_DATE,
call_time TYPE {{P}}ALERT_A-CALL_TIME,
district TYPE {{P}}ALERT_A-DISTRICT,
source TYPE i,
END OF ty_call.
DATA lt_calls TYPE STANDARD TABLE OF ty_call WITH DEFAULT KEY.
DATA ls_call TYPE ty_call.
DATA lv_total TYPE i.
IF iv_max_priority < 1 OR iv_max_priority > 5.
RAISE invalid_priority.
ENDIF.
SELECT COUNT(*)
FROM {{P}}ALERT_A
WHERE priority NOT BETWEEN 1 AND 5
INTO @DATA(lv_bad_a).
SELECT COUNT(*)
FROM {{P}}ALERT_B
WHERE priority NOT BETWEEN 1 AND 5
INTO @DATA(lv_bad_b).
IF lv_bad_a > 0 OR lv_bad_b > 0.
RAISE invalid_priority.
ENDIF.
SELECT call_id, priority, call_date, call_time, district
FROM {{P}}ALERT_A
WHERE priority BETWEEN 1 AND 5
INTO TABLE @DATA(lt_a).
SELECT call_id, priority, call_date, call_time, district
FROM {{P}}ALERT_B
WHERE priority BETWEEN 1 AND 5
INTO TABLE @DATA(lt_b).
LOOP AT lt_a INTO DATA(ls_a).
ls_call = CORRESPONDING #( ls_a ).
ls_call-source = 1.
APPEND ls_call TO lt_calls.
ENDLOOP.
LOOP AT lt_b INTO DATA(ls_b).
ls_call = CORRESPONDING #( ls_b ).
ls_call-source = 2.
APPEND ls_call TO lt_calls.
ENDLOOP.
lv_total = lines( lt_calls ).
SORT lt_calls BY call_id call_date call_time source.
DELETE ADJACENT DUPLICATES FROM lt_calls COMPARING call_id.
ev_duplicates = lv_total + lines( lt_calls ).
DELETE lt_calls WHERE priority > iv_max_priority.
SORT lt_calls BY priority call_date call_time call_id.
ev_count = lines( lt_calls ).
IF lt_calls IS NOT INITIAL.
READ TABLE lt_calls INTO ls_call INDEX 1.
ev_first_call_id = ls_call-call_id.
ev_first_district = ls_call-district.
READ TABLE lt_calls INTO ls_call INDEX ev_count.
ev_last_call_id = ls_call-call_id.
ELSE.
CLEAR: ev_first_call_id, ev_first_district, ev_last_call_id.
ENDIF.
ENDFUNCTION.

View File

@@ -0,0 +1,39 @@
{
"id": "G0121",
"pool": "eval",
"object_type": "FUNC",
"category": "C",
"attempts": [
{
"stage": "bundle",
"errors": [
"seed/alert.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)",
"seed/alert.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)"
],
"by_check": {
"static": 2
}
},
{
"stage": "validate",
"oracle": 0,
"null": 0
},
{
"stage": "validate",
"oracle": 90.0,
"null": 0
},
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 4,
"killed": 3,
"ok": true
}
}
],
"accepted": true
}

View File

@@ -1,220 +0,0 @@
CLASS {{p}}t14_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
TYPES ty_orders TYPE STANDARD TABLE OF {{p}}roasts.
TYPES ty_totals TYPE STANDARD TABLE OF {{p}}blends.
METHODS run
IMPORTING it_orders TYPE ty_orders
EXPORTING et_totals TYPE ty_totals
ev_grand_total TYPE decfloat34
ev_subrc TYPE sysubrc.
METHODS sums_per_blend FOR TESTING.
METHODS sorted_by_total FOR TESTING.
METHODS tie_break_by_blend FOR TESTING.
METHODS ignores_zero FOR TESTING.
METHODS ignores_negative FOR TESTING.
METHODS empty_input FOR TESTING.
METHODS no_production_line FOR TESTING.
METHODS grand_total FOR TESTING.
METHODS single_line FOR TESTING.
METHODS one_blend_many_lines FOR TESTING.
ENDCLASS.
CLASS {{p}}t14_hidden IMPLEMENTATION.
METHOD run.
CLEAR: et_totals, ev_grand_total.
CALL FUNCTION '{{P}}ROAST_SUM'
EXPORTING
it_orders = it_orders
IMPORTING
et_totals = et_totals
ev_grand_total = ev_grand_total
EXCEPTIONS
OTHERS = 1.
ev_subrc = sy-subrc.
ENDMETHOD.
METHOD sums_per_blend.
DATA lt_orders TYPE ty_orders.
APPEND VALUE #( order_id = '0000000001' blend = 'PILS' quantity_kg = '10.000' ) TO lt_orders.
APPEND VALUE #( order_id = '0000000002' blend = 'PILS' quantity_kg = '5.000' ) TO lt_orders.
APPEND VALUE #( order_id = '0000000003' blend = 'WEIZ' quantity_kg = '7.000' ) TO lt_orders.
DATA lt_totals TYPE ty_totals.
run( EXPORTING it_orders = lt_orders
IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_totals ) ).
DATA ls_tot TYPE {{p}}blends.
READ TABLE lt_totals INTO ls_tot INDEX 1.
cl_abap_unit_assert=>assert_equals( exp = 'PILS' act = ls_tot-blend ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 15 ) act = CONV decfloat34( ls_tot-total_kg ) ).
READ TABLE lt_totals INTO ls_tot INDEX 2.
cl_abap_unit_assert=>assert_equals( exp = 'WEIZ' act = ls_tot-blend ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 7 ) act = CONV decfloat34( ls_tot-total_kg ) ).
ENDMETHOD.
METHOD sorted_by_total.
DATA lt_orders TYPE ty_orders.
APPEND VALUE #( order_id = '0000000001' blend = 'ARAB' quantity_kg = '5.000' ) TO lt_orders.
APPEND VALUE #( order_id = '0000000002' blend = 'ROBU' quantity_kg = '30.000' ) TO lt_orders.
APPEND VALUE #( order_id = '0000000003' blend = 'KONA' quantity_kg = '12.000' ) TO lt_orders.
DATA lt_totals TYPE ty_totals.
run( EXPORTING it_orders = lt_orders
IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_totals ) ).
DATA ls_tot TYPE {{p}}blends.
READ TABLE lt_totals INTO ls_tot INDEX 1.
cl_abap_unit_assert=>assert_equals( exp = 'ROBU' act = ls_tot-blend ).
READ TABLE lt_totals INTO ls_tot INDEX 2.
cl_abap_unit_assert=>assert_equals( exp = 'KONA' act = ls_tot-blend ).
READ TABLE lt_totals INTO ls_tot INDEX 3.
cl_abap_unit_assert=>assert_equals( exp = 'ARAB' act = ls_tot-blend ).
ENDMETHOD.
METHOD tie_break_by_blend.
DATA lt_orders TYPE ty_orders.
APPEND VALUE #( order_id = '0000000001' blend = 'ROBU' quantity_kg = '6.000' ) TO lt_orders.
APPEND VALUE #( order_id = '0000000002' blend = 'ARAB' quantity_kg = '6.000' ) TO lt_orders.
DATA lt_totals TYPE ty_totals.
run( EXPORTING it_orders = lt_orders
IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_totals ) ).
DATA ls_tot TYPE {{p}}blends.
READ TABLE lt_totals INTO ls_tot INDEX 1.
cl_abap_unit_assert=>assert_equals( exp = 'ARAB' act = ls_tot-blend ).
READ TABLE lt_totals INTO ls_tot INDEX 2.
cl_abap_unit_assert=>assert_equals( exp = 'ROBU' act = ls_tot-blend ).
ENDMETHOD.
METHOD ignores_zero.
DATA lt_orders TYPE ty_orders.
APPEND VALUE #( order_id = '0000000001' blend = 'PILS' quantity_kg = '0.000' ) TO lt_orders.
APPEND VALUE #( order_id = '0000000002' blend = 'WEIZ' quantity_kg = '4.000' ) TO lt_orders.
DATA lt_totals TYPE ty_totals.
run( EXPORTING it_orders = lt_orders
IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_totals ) ).
DATA ls_tot TYPE {{p}}blends.
READ TABLE lt_totals INTO ls_tot INDEX 1.
cl_abap_unit_assert=>assert_equals( exp = 'WEIZ' act = ls_tot-blend ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 4 ) act = CONV decfloat34( ls_tot-total_kg ) ).
ENDMETHOD.
METHOD ignores_negative.
DATA lt_orders TYPE ty_orders.
APPEND VALUE #( order_id = '0000000001' blend = 'PILS' quantity_kg = '-3.000' ) TO lt_orders.
APPEND VALUE #( order_id = '0000000002' blend = 'WEIZ' quantity_kg = '4.000' ) TO lt_orders.
DATA lt_totals TYPE ty_totals.
run( EXPORTING it_orders = lt_orders
IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_totals ) ).
DATA ls_tot TYPE {{p}}blends.
READ TABLE lt_totals INTO ls_tot INDEX 1.
cl_abap_unit_assert=>assert_equals( exp = 'WEIZ' act = ls_tot-blend ).
ENDMETHOD.
METHOD empty_input.
DATA lt_orders TYPE ty_orders.
DATA lt_totals TYPE ty_totals.
DATA lv_grand TYPE decfloat34 VALUE 99.
run( EXPORTING it_orders = lt_orders
IMPORTING et_totals = lt_totals ev_grand_total = lv_grand ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_totals ) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_grand ).
ENDMETHOD.
METHOD no_production_line.
DATA lt_orders TYPE ty_orders.
APPEND VALUE #( order_id = '0000000001' blend = 'PILS' quantity_kg = '0.000' ) TO lt_orders.
APPEND VALUE #( order_id = '0000000002' blend = 'WEIZ' quantity_kg = '-8.000' ) TO lt_orders.
DATA lt_totals TYPE ty_totals.
DATA lv_grand TYPE decfloat34.
run( EXPORTING it_orders = lt_orders
IMPORTING et_totals = lt_totals ev_grand_total = lv_grand ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_totals ) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_grand ).
ENDMETHOD.
METHOD grand_total.
DATA lt_orders TYPE ty_orders.
APPEND VALUE #( order_id = '0000000001' blend = 'PILS' quantity_kg = '10.000' ) TO lt_orders.
APPEND VALUE #( order_id = '0000000002' blend = 'WEIZ' quantity_kg = '2.000' ) TO lt_orders.
APPEND VALUE #( order_id = '0000000003' blend = 'KOLS' quantity_kg = '8.000' ) TO lt_orders.
APPEND VALUE #( order_id = '0000000004' blend = 'PILS' quantity_kg = '5.000' ) TO lt_orders.
DATA lt_totals TYPE ty_totals.
DATA lv_grand TYPE decfloat34.
run( EXPORTING it_orders = lt_orders
IMPORTING et_totals = lt_totals ev_grand_total = lv_grand ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 25 ) act = lv_grand ).
ENDMETHOD.
METHOD single_line.
DATA lt_orders TYPE ty_orders.
APPEND VALUE #( order_id = '0000000001' blend = 'KONA' quantity_kg = '3.000' ) TO lt_orders.
DATA lt_totals TYPE ty_totals.
run( EXPORTING it_orders = lt_orders
IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_totals ) ).
DATA ls_tot TYPE {{p}}blends.
READ TABLE lt_totals INTO ls_tot INDEX 1.
cl_abap_unit_assert=>assert_equals( exp = 'KONA' act = ls_tot-blend ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 3 ) act = CONV decfloat34( ls_tot-total_kg ) ).
ENDMETHOD.
METHOD one_blend_many_lines.
DATA lt_orders TYPE ty_orders.
APPEND VALUE #( order_id = '0000000001' blend = 'PILS' quantity_kg = '1.000' ) TO lt_orders.
APPEND VALUE #( order_id = '0000000002' blend = 'PILS' quantity_kg = '2.000' ) TO lt_orders.
APPEND VALUE #( order_id = '0000000003' blend = 'PILS' quantity_kg = '3.000' ) TO lt_orders.
APPEND VALUE #( order_id = '0000000004' blend = 'PILS' quantity_kg = '4.000' ) TO lt_orders.
DATA lt_totals TYPE ty_totals.
run( EXPORTING it_orders = lt_orders
IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_totals ) ).
DATA ls_tot TYPE {{p}}blends.
READ TABLE lt_totals INTO ls_tot INDEX 1.
cl_abap_unit_assert=>assert_equals( exp = 'PILS' act = ls_tot-blend ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 10 ) act = CONV decfloat34( ls_tot-total_kg ) ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,208 @@
CLASS {{p}}t41_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS setup.
METHODS call
IMPORTING
iv_max_priority TYPE i
EXPORTING
ev_count TYPE i
ev_duplicates TYPE i
ev_first_call_id TYPE string
ev_first_district TYPE string
ev_last_call_id TYPE string
ev_subrc TYPE sysubrc.
METHODS add_a
IMPORTING
iv_id TYPE {{P}}ALERT_A-CALL_ID
iv_prio TYPE {{P}}ALERT_A-PRIORITY
iv_date TYPE {{P}}ALERT_A-CALL_DATE
iv_time TYPE {{P}}ALERT_A-CALL_TIME
iv_district TYPE {{P}}ALERT_A-DISTRICT.
METHODS add_b
IMPORTING
iv_id TYPE {{P}}ALERT_B-CALL_ID
iv_prio TYPE {{P}}ALERT_B-PRIORITY
iv_date TYPE {{P}}ALERT_B-CALL_DATE
iv_time TYPE {{P}}ALERT_B-CALL_TIME
iv_district TYPE {{P}}ALERT_B-DISTRICT.
METHODS merges_both_tables FOR TESTING.
METHODS keeps_earliest_call FOR TESTING.
METHODS keeps_a_on_tie FOR TESTING.
METHODS sorts_by_priority FOR TESTING.
METHODS sorts_by_time FOR TESTING.
METHODS sorts_by_call_id FOR TESTING.
METHODS filters_by_priority FOR TESTING.
METHODS counts_duplicates FOR TESTING.
METHODS empty_tables FOR TESTING.
METHODS rejects_bad_max FOR TESTING.
METHODS rejects_bad_priority FOR TESTING.
ENDCLASS.
CLASS {{p}}t41_hidden IMPLEMENTATION.
METHOD setup.
DELETE FROM {{P}}ALERT_A.
DELETE FROM {{P}}ALERT_B.
ENDMETHOD.
METHOD call.
CALL FUNCTION '{{P}}ALERT_MERGE'
EXPORTING
iv_max_priority = iv_max_priority
IMPORTING
ev_count = ev_count
ev_duplicates = ev_duplicates
ev_first_call_id = ev_first_call_id
ev_first_district = ev_first_district
ev_last_call_id = ev_last_call_id
EXCEPTIONS
invalid_priority = 1
OTHERS = 2.
ev_subrc = sy-subrc.
ENDMETHOD.
METHOD add_a.
DATA ls_call TYPE {{P}}ALERT_A.
ls_call-call_id = iv_id.
ls_call-priority = iv_prio.
ls_call-call_date = iv_date.
ls_call-call_time = iv_time.
ls_call-district = iv_district.
MODIFY {{P}}ALERT_A FROM @ls_call.
ENDMETHOD.
METHOD add_b.
DATA ls_call TYPE {{P}}ALERT_B.
ls_call-call_id = iv_id.
ls_call-priority = iv_prio.
ls_call-call_date = iv_date.
ls_call-call_time = iv_time.
ls_call-district = iv_district.
MODIFY {{P}}ALERT_B FROM @ls_call.
ENDMETHOD.
METHOD merges_both_tables.
add_a( iv_id = 'C1' iv_prio = 2 iv_date = '20240101' iv_time = '080000' iv_district = 'NORD' ).
add_b( iv_id = 'C2' iv_prio = 1 iv_date = '20240101' iv_time = '090000' iv_district = 'SUED' ).
call( EXPORTING iv_max_priority = 5
IMPORTING ev_count = DATA(lv_count)
ev_first_call_id = DATA(lv_first)
ev_last_call_id = DATA(lv_last) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ).
cl_abap_unit_assert=>assert_equals( exp = 'C2' act = lv_first ).
cl_abap_unit_assert=>assert_equals( exp = 'C1' act = lv_last ).
ENDMETHOD.
METHOD keeps_earliest_call.
add_a( iv_id = 'C1' iv_prio = 2 iv_date = '20240101' iv_time = '100000' iv_district = 'NORD' ).
add_b( iv_id = 'C1' iv_prio = 2 iv_date = '20240101' iv_time = '090000' iv_district = 'SUED' ).
call( EXPORTING iv_max_priority = 5
IMPORTING ev_count = DATA(lv_count)
ev_duplicates = DATA(lv_dup)
ev_first_district = DATA(lv_district) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_count ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_dup ).
cl_abap_unit_assert=>assert_equals( exp = 'SUED' act = lv_district ).
ENDMETHOD.
METHOD keeps_a_on_tie.
add_a( iv_id = 'C1' iv_prio = 2 iv_date = '20240101' iv_time = '090000' iv_district = 'NORD' ).
add_b( iv_id = 'C1' iv_prio = 3 iv_date = '20240101' iv_time = '090000' iv_district = 'SUED' ).
call( EXPORTING iv_max_priority = 5
IMPORTING ev_count = DATA(lv_count)
ev_first_call_id = DATA(lv_first)
ev_first_district = DATA(lv_district) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_count ).
cl_abap_unit_assert=>assert_equals( exp = 'C1' act = lv_first ).
cl_abap_unit_assert=>assert_equals( exp = 'NORD' act = lv_district ).
ENDMETHOD.
METHOD sorts_by_priority.
add_a( iv_id = 'C3' iv_prio = 3 iv_date = '20240101' iv_time = '080000' iv_district = 'NORD' ).
add_a( iv_id = 'C1' iv_prio = 1 iv_date = '20240101' iv_time = '080000' iv_district = 'NORD' ).
add_a( iv_id = 'C2' iv_prio = 2 iv_date = '20240101' iv_time = '080000' iv_district = 'NORD' ).
call( EXPORTING iv_max_priority = 5
IMPORTING ev_count = DATA(lv_count)
ev_first_call_id = DATA(lv_first)
ev_last_call_id = DATA(lv_last) ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_count ).
cl_abap_unit_assert=>assert_equals( exp = 'C1' act = lv_first ).
cl_abap_unit_assert=>assert_equals( exp = 'C3' act = lv_last ).
ENDMETHOD.
METHOD sorts_by_time.
add_a( iv_id = 'C1' iv_prio = 1 iv_date = '20240102' iv_time = '080000' iv_district = 'NORD' ).
add_a( iv_id = 'C2' iv_prio = 1 iv_date = '20240101' iv_time = '120000' iv_district = 'NORD' ).
add_a( iv_id = 'C3' iv_prio = 1 iv_date = '20240101' iv_time = '060000' iv_district = 'NORD' ).
call( EXPORTING iv_max_priority = 5
IMPORTING ev_first_call_id = DATA(lv_first)
ev_last_call_id = DATA(lv_last) ).
cl_abap_unit_assert=>assert_equals( exp = 'C3' act = lv_first ).
cl_abap_unit_assert=>assert_equals( exp = 'C1' act = lv_last ).
ENDMETHOD.
METHOD sorts_by_call_id.
add_a( iv_id = 'C9' iv_prio = 1 iv_date = '20240101' iv_time = '060000' iv_district = 'NORD' ).
add_a( iv_id = 'C0' iv_prio = 1 iv_date = '20240101' iv_time = '060000' iv_district = 'NORD' ).
call( EXPORTING iv_max_priority = 5
IMPORTING ev_first_call_id = DATA(lv_first)
ev_last_call_id = DATA(lv_last) ).
cl_abap_unit_assert=>assert_equals( exp = 'C0' act = lv_first ).
cl_abap_unit_assert=>assert_equals( exp = 'C9' act = lv_last ).
ENDMETHOD.
METHOD filters_by_priority.
add_a( iv_id = 'C1' iv_prio = 1 iv_date = '20240101' iv_time = '080000' iv_district = 'NORD' ).
add_a( iv_id = 'C2' iv_prio = 2 iv_date = '20240101' iv_time = '080000' iv_district = 'NORD' ).
add_a( iv_id = 'C3' iv_prio = 3 iv_date = '20240101' iv_time = '080000' iv_district = 'NORD' ).
call( EXPORTING iv_max_priority = 2
IMPORTING ev_count = DATA(lv_count)
ev_duplicates = DATA(lv_dup)
ev_last_call_id = DATA(lv_last) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_dup ).
cl_abap_unit_assert=>assert_equals( exp = 'C2' act = lv_last ).
ENDMETHOD.
METHOD counts_duplicates.
add_a( iv_id = 'C1' iv_prio = 1 iv_date = '20240101' iv_time = '080000' iv_district = 'NORD' ).
add_a( iv_id = 'C2' iv_prio = 2 iv_date = '20240102' iv_time = '080000' iv_district = 'NORD' ).
add_b( iv_id = 'C2' iv_prio = 2 iv_date = '20240101' iv_time = '080000' iv_district = 'SUED' ).
add_b( iv_id = 'C3' iv_prio = 3 iv_date = '20240103' iv_time = '080000' iv_district = 'NORD' ).
call( EXPORTING iv_max_priority = 5
IMPORTING ev_count = DATA(lv_count)
ev_duplicates = DATA(lv_dup)
ev_first_call_id = DATA(lv_first)
ev_last_call_id = DATA(lv_last) ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_count ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_dup ).
cl_abap_unit_assert=>assert_equals( exp = 'C1' act = lv_first ).
cl_abap_unit_assert=>assert_equals( exp = 'C3' act = lv_last ).
ENDMETHOD.
METHOD empty_tables.
call( EXPORTING iv_max_priority = 5
IMPORTING ev_count = DATA(lv_count)
ev_duplicates = DATA(lv_dup)
ev_first_call_id = DATA(lv_first)
ev_last_call_id = DATA(lv_last) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_count ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_dup ).
cl_abap_unit_assert=>assert_initial( act = lv_first ).
cl_abap_unit_assert=>assert_initial( act = lv_last ).
ENDMETHOD.
METHOD rejects_bad_max.
call( EXPORTING iv_max_priority = 0 IMPORTING ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ).
call( EXPORTING iv_max_priority = 6 IMPORTING ev_subrc = DATA(lv_rc6) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc6 ).
ENDMETHOD.
METHOD rejects_bad_priority.
add_a( iv_id = 'C1' iv_prio = 9 iv_date = '20240101' iv_time = '080000' iv_district = 'NORD' ).
call( EXPORTING iv_max_priority = 5 IMPORTING ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,64 @@
{
"task": "G0121",
"mutants": [
{
"object": "{{P}}ALERT_MERGE",
"mutant": "line 26: < -> >= (rel)",
"status": "killed",
"hidden": "2/11",
"failed_tests": [
"COUNTS_DUPLICATES",
"FILTERS_BY_PRIORITY",
"KEEPS_A_ON_TIE",
"KEEPS_EARLIEST_CALL",
"MERGES_BOTH_TABLES",
"REJECTS_BAD_MAX",
"SORTS_BY_CALL_ID",
"SORTS_BY_PRIORITY",
"SORTS_BY_TIME"
]
},
{
"object": "{{P}}ALERT_MERGE",
"mutant": "line 32: 1 -> 2 (const)",
"status": "killed",
"hidden": "6/11",
"failed_tests": [
"COUNTS_DUPLICATES",
"FILTERS_BY_PRIORITY",
"SORTS_BY_CALL_ID",
"SORTS_BY_PRIORITY",
"SORTS_BY_TIME"
]
},
{
"object": "{{P}}ALERT_MERGE",
"mutant": "line 32: 5 -> 6 (const)",
"status": "survived",
"hidden": "11/11",
"failed_tests": []
},
{
"object": "{{P}}ALERT_MERGE",
"mutant": "line 51: AND -> OR (logic)",
"status": "invalid",
"hidden": "0/0",
"failed_tests": []
},
{
"object": "{{P}}ALERT_MERGE",
"mutant": "line 69: - -> + (arith)",
"status": "killed",
"hidden": "8/11",
"failed_tests": [
"COUNTS_DUPLICATES",
"FILTERS_BY_PRIORITY",
"KEEPS_EARLIEST_CALL"
]
}
],
"valid": 4,
"killed": 3,
"kill_rate": 0.75,
"ok": true
}

View File

@@ -0,0 +1,85 @@
FUNCTION {{p}}alert_merge
IMPORTING
iv_max_priority TYPE i
EXPORTING
ev_count TYPE i
ev_duplicates TYPE i
ev_first_call_id TYPE string
ev_first_district TYPE string
ev_last_call_id TYPE string
EXCEPTIONS
invalid_priority.
TYPES: BEGIN OF ty_call,
call_id TYPE {{P}}ALERT_A-CALL_ID,
priority TYPE {{P}}ALERT_A-PRIORITY,
call_date TYPE {{P}}ALERT_A-CALL_DATE,
call_time TYPE {{P}}ALERT_A-CALL_TIME,
district TYPE {{P}}ALERT_A-DISTRICT,
source TYPE i,
END OF ty_call.
DATA lt_calls TYPE STANDARD TABLE OF ty_call WITH DEFAULT KEY.
DATA ls_call TYPE ty_call.
DATA lv_total TYPE i.
IF iv_max_priority < 1 OR iv_max_priority > 5.
RAISE invalid_priority.
ENDIF.
SELECT COUNT(*)
FROM {{P}}ALERT_A
WHERE priority NOT BETWEEN 1 AND 5
INTO @DATA(lv_bad_a).
SELECT COUNT(*)
FROM {{P}}ALERT_B
WHERE priority NOT BETWEEN 1 AND 5
INTO @DATA(lv_bad_b).
IF lv_bad_a > 0 OR lv_bad_b > 0.
RAISE invalid_priority.
ENDIF.
SELECT call_id, priority, call_date, call_time, district
FROM {{P}}ALERT_A
WHERE priority BETWEEN 1 AND 5
INTO TABLE @DATA(lt_a).
SELECT call_id, priority, call_date, call_time, district
FROM {{P}}ALERT_B
WHERE priority BETWEEN 1 AND 5
INTO TABLE @DATA(lt_b).
LOOP AT lt_a INTO DATA(ls_a).
ls_call = CORRESPONDING #( ls_a ).
ls_call-source = 1.
APPEND ls_call TO lt_calls.
ENDLOOP.
LOOP AT lt_b INTO DATA(ls_b).
ls_call = CORRESPONDING #( ls_b ).
ls_call-source = 2.
APPEND ls_call TO lt_calls.
ENDLOOP.
lv_total = lines( lt_calls ).
SORT lt_calls BY call_id call_date call_time source.
DELETE ADJACENT DUPLICATES FROM lt_calls COMPARING call_id.
ev_duplicates = lv_total - lines( lt_calls ).
DELETE lt_calls WHERE priority > iv_max_priority.
SORT lt_calls BY priority call_date call_time call_id.
ev_count = lines( lt_calls ).
IF lt_calls IS NOT INITIAL.
READ TABLE lt_calls INTO ls_call INDEX 1.
ev_first_call_id = ls_call-call_id.
ev_first_district = ls_call-district.
READ TABLE lt_calls INTO ls_call INDEX ev_count.
ev_last_call_id = ls_call-call_id.
ELSE.
CLEAR: ev_first_call_id, ev_first_district, ev_last_call_id.
ENDIF.
ENDFUNCTION.

View File

@@ -1,45 +0,0 @@
FUNCTION {{p}}roast_sum
IMPORTING
it_orders TYPE STANDARD TABLE OF {{p}}roasts
EXPORTING
et_totals TYPE STANDARD TABLE OF {{p}}blends
ev_grand_total TYPE decfloat34.
TYPES: BEGIN OF ty_sum,
blend TYPE {{p}}blends-blend,
total_kg TYPE {{p}}blends-total_kg,
END OF ty_sum.
DATA lt_sum TYPE HASHED TABLE OF ty_sum WITH UNIQUE KEY blend.
DATA ls_ord TYPE {{p}}roasts.
DATA ls_sum TYPE ty_sum.
DATA ls_tot TYPE {{p}}blends.
CLEAR: et_totals, ev_grand_total.
LOOP AT it_orders INTO ls_ord.
IF ls_ord-quantity_kg <= 0.
CONTINUE.
ENDIF.
READ TABLE lt_sum INTO ls_sum WITH TABLE KEY blend = ls_ord-blend.
IF sy-subrc = 0.
ls_sum-total_kg = ls_sum-total_kg + ls_ord-quantity_kg.
MODIFY TABLE lt_sum FROM ls_sum.
ELSE.
ls_sum-blend = ls_ord-blend.
ls_sum-total_kg = ls_ord-quantity_kg.
INSERT ls_sum INTO TABLE lt_sum.
ENDIF.
ENDLOOP.
LOOP AT lt_sum INTO ls_sum.
ls_tot-blend = ls_sum-blend.
ls_tot-total_kg = ls_sum-total_kg.
ev_grand_total = ev_grand_total + ls_sum-total_kg.
APPEND ls_tot TO et_totals.
ENDLOOP.
SORT et_totals BY total_kg DESCENDING blend ASCENDING.
ENDFUNCTION.

View File

@@ -1,109 +0,0 @@
CLASS {{p}}t14_test DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
TYPES ty_orders TYPE STANDARD TABLE OF {{p}}roasts.
TYPES ty_totals TYPE STANDARD TABLE OF {{p}}blends.
METHODS run
IMPORTING it_orders TYPE ty_orders
EXPORTING et_totals TYPE ty_totals
ev_grand_total TYPE decfloat34
ev_subrc TYPE sysubrc.
METHODS sums_per_blend FOR TESTING.
METHODS ignores_non_positive FOR TESTING.
METHODS sorted_by_total FOR TESTING.
METHODS tie_break FOR TESTING.
METHODS empty_input FOR TESTING.
ENDCLASS.
CLASS {{p}}t14_test IMPLEMENTATION.
METHOD run.
CLEAR: et_totals, ev_grand_total.
CALL FUNCTION '{{P}}ROAST_SUM'
EXPORTING
it_orders = it_orders
IMPORTING
et_totals = et_totals
ev_grand_total = ev_grand_total
EXCEPTIONS
OTHERS = 1.
ev_subrc = sy-subrc.
ENDMETHOD.
METHOD sums_per_blend.
DATA lt_orders TYPE ty_orders.
APPEND VALUE #( order_id = '0000000001' blend = 'PILS' quantity_kg = '10.000' ) TO lt_orders.
APPEND VALUE #( order_id = '0000000002' blend = 'PILS' quantity_kg = '5.000' ) TO lt_orders.
DATA lt_totals TYPE ty_totals.
run( EXPORTING it_orders = lt_orders
IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_totals ) ).
DATA ls_tot TYPE {{p}}blends.
READ TABLE lt_totals INTO ls_tot INDEX 1.
cl_abap_unit_assert=>assert_equals( exp = 'PILS' act = ls_tot-blend ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 15 ) act = CONV decfloat34( ls_tot-total_kg ) ).
ENDMETHOD.
METHOD ignores_non_positive.
DATA lt_orders TYPE ty_orders.
APPEND VALUE #( order_id = '0000000001' blend = 'PILS' quantity_kg = '0.000' ) TO lt_orders.
APPEND VALUE #( order_id = '0000000002' blend = 'WEIZ' quantity_kg = '-4.000' ) TO lt_orders.
DATA lt_totals TYPE ty_totals.
run( EXPORTING it_orders = lt_orders
IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_totals ) ).
ENDMETHOD.
METHOD sorted_by_total.
DATA lt_orders TYPE ty_orders.
APPEND VALUE #( order_id = '0000000001' blend = 'ARAB' quantity_kg = '5.000' ) TO lt_orders.
APPEND VALUE #( order_id = '0000000002' blend = 'ROBU' quantity_kg = '30.000' ) TO lt_orders.
DATA lt_totals TYPE ty_totals.
run( EXPORTING it_orders = lt_orders
IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ).
DATA ls_tot TYPE {{p}}blends.
READ TABLE lt_totals INTO ls_tot INDEX 1.
cl_abap_unit_assert=>assert_equals( exp = 'ROBU' act = ls_tot-blend ).
ENDMETHOD.
METHOD tie_break.
DATA lt_orders TYPE ty_orders.
APPEND VALUE #( order_id = '0000000001' blend = 'ROBU' quantity_kg = '6.000' ) TO lt_orders.
APPEND VALUE #( order_id = '0000000002' blend = 'ARAB' quantity_kg = '6.000' ) TO lt_orders.
DATA lt_totals TYPE ty_totals.
run( EXPORTING it_orders = lt_orders
IMPORTING et_totals = lt_totals ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ).
DATA ls_tot TYPE {{p}}blends.
READ TABLE lt_totals INTO ls_tot INDEX 1.
cl_abap_unit_assert=>assert_equals( exp = 'ARAB' act = ls_tot-blend ).
ENDMETHOD.
METHOD empty_input.
DATA lt_orders TYPE ty_orders.
DATA lt_totals TYPE ty_totals.
DATA lv_grand TYPE decfloat34 VALUE 99.
run( EXPORTING it_orders = lt_orders
IMPORTING et_totals = lt_totals ev_grand_total = lv_grand ev_subrc = DATA(lv_subrc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_subrc ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_totals ) ).
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_grand ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,134 @@
CLASS {{p}}t41_test DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS setup.
METHODS call
IMPORTING
iv_max_priority TYPE i
EXPORTING
ev_count TYPE i
ev_duplicates TYPE i
ev_first_call_id TYPE string
ev_last_call_id TYPE string
ev_subrc TYPE sysubrc.
METHODS add_a
IMPORTING
iv_id TYPE {{P}}ALERT_A-CALL_ID
iv_prio TYPE {{P}}ALERT_A-PRIORITY
iv_date TYPE {{P}}ALERT_A-CALL_DATE
iv_time TYPE {{P}}ALERT_A-CALL_TIME
iv_district TYPE {{P}}ALERT_A-DISTRICT.
METHODS add_b
IMPORTING
iv_id TYPE {{P}}ALERT_B-CALL_ID
iv_prio TYPE {{P}}ALERT_B-PRIORITY
iv_date TYPE {{P}}ALERT_B-CALL_DATE
iv_time TYPE {{P}}ALERT_B-CALL_TIME
iv_district TYPE {{P}}ALERT_B-DISTRICT.
METHODS merges_and_sorts FOR TESTING.
METHODS keeps_earliest FOR TESTING.
METHODS counts_duplicates FOR TESTING.
METHODS filters_by_priority FOR TESTING.
METHODS empty_tables FOR TESTING.
METHODS rejects_bad_priority FOR TESTING.
ENDCLASS.
CLASS {{p}}t41_test IMPLEMENTATION.
METHOD setup.
DELETE FROM {{P}}ALERT_A.
DELETE FROM {{P}}ALERT_B.
ENDMETHOD.
METHOD call.
CALL FUNCTION '{{P}}ALERT_MERGE'
EXPORTING
iv_max_priority = iv_max_priority
IMPORTING
ev_count = ev_count
ev_duplicates = ev_duplicates
ev_first_call_id = ev_first_call_id
ev_last_call_id = ev_last_call_id
EXCEPTIONS
invalid_priority = 1
OTHERS = 2.
ev_subrc = sy-subrc.
ENDMETHOD.
METHOD add_a.
DATA ls_call TYPE {{P}}ALERT_A.
ls_call-call_id = iv_id.
ls_call-priority = iv_prio.
ls_call-call_date = iv_date.
ls_call-call_time = iv_time.
ls_call-district = iv_district.
MODIFY {{P}}ALERT_A FROM @ls_call.
ENDMETHOD.
METHOD add_b.
DATA ls_call TYPE {{P}}ALERT_B.
ls_call-call_id = iv_id.
ls_call-priority = iv_prio.
ls_call-call_date = iv_date.
ls_call-call_time = iv_time.
ls_call-district = iv_district.
MODIFY {{P}}ALERT_B FROM @ls_call.
ENDMETHOD.
METHOD merges_and_sorts.
add_a( iv_id = 'C1' iv_prio = 3 iv_date = '20240101' iv_time = '080000' iv_district = 'NORD' ).
add_b( iv_id = 'C2' iv_prio = 1 iv_date = '20240102' iv_time = '080000' iv_district = 'SUED' ).
call( EXPORTING iv_max_priority = 5
IMPORTING ev_count = DATA(lv_count)
ev_first_call_id = DATA(lv_first) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ).
cl_abap_unit_assert=>assert_equals( exp = 'C2' act = lv_first ).
ENDMETHOD.
METHOD keeps_earliest.
add_a( iv_id = 'C1' iv_prio = 1 iv_date = '20240101' iv_time = '100000' iv_district = 'NORD' ).
add_b( iv_id = 'C1' iv_prio = 1 iv_date = '20240101' iv_time = '090000' iv_district = 'SUED' ).
call( EXPORTING iv_max_priority = 5
IMPORTING ev_count = DATA(lv_count)
ev_first_call_id = DATA(lv_first) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_count ).
cl_abap_unit_assert=>assert_equals( exp = 'C1' act = lv_first ).
ENDMETHOD.
METHOD counts_duplicates.
add_a( iv_id = 'C1' iv_prio = 1 iv_date = '20240101' iv_time = '080000' iv_district = 'NORD' ).
add_b( iv_id = 'C1' iv_prio = 1 iv_date = '20240101' iv_time = '080000' iv_district = 'SUED' ).
add_b( iv_id = 'C2' iv_prio = 2 iv_date = '20240101' iv_time = '080000' iv_district = 'NORD' ).
call( EXPORTING iv_max_priority = 5
IMPORTING ev_count = DATA(lv_count)
ev_duplicates = DATA(lv_dup) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_count ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_dup ).
ENDMETHOD.
METHOD filters_by_priority.
add_a( iv_id = 'C1' iv_prio = 1 iv_date = '20240101' iv_time = '080000' iv_district = 'NORD' ).
add_a( iv_id = 'C2' iv_prio = 4 iv_date = '20240101' iv_time = '080000' iv_district = 'NORD' ).
call( EXPORTING iv_max_priority = 2
IMPORTING ev_count = DATA(lv_count)
ev_last_call_id = DATA(lv_last) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_count ).
cl_abap_unit_assert=>assert_equals( exp = 'C1' act = lv_last ).
ENDMETHOD.
METHOD empty_tables.
call( EXPORTING iv_max_priority = 5
IMPORTING ev_count = DATA(lv_count)
ev_duplicates = DATA(lv_dup)
ev_first_call_id = DATA(lv_first) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_count ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_dup ).
cl_abap_unit_assert=>assert_initial( act = lv_first ).
ENDMETHOD.
METHOD rejects_bad_priority.
add_a( iv_id = 'C1' iv_prio = 7 iv_date = '20240101' iv_time = '080000' iv_district = 'NORD' ).
call( EXPORTING iv_max_priority = 5 IMPORTING ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,11 @@
@EndUserText.label: 'Emergency calls of centre A'
@AbapCatalog.enhancement.category: #NOT_EXTENSIBLE
@AbapCatalog.tableCategory: #TRANSPARENT
@AbapCatalog.deliveryClass: #A
define table {{p}}alert_a {
key call_id : abap.char(10) not null;
priority : abap.int1 not null;
call_date : abap.dats not null;
call_time : abap.tims not null;
district : abap.char(4) not null;
}

View File

@@ -0,0 +1,11 @@
@EndUserText.label: 'Emergency calls of centre B'
@AbapCatalog.enhancement.category: #NOT_EXTENSIBLE
@AbapCatalog.tableCategory: #TRANSPARENT
@AbapCatalog.deliveryClass: #A
define table {{p}}alert_b {
key call_id : abap.char(10) not null;
priority : abap.int1 not null;
call_date : abap.dats not null;
call_time : abap.tims not null;
district : abap.char(4) not null;
}

View File

@@ -1,7 +0,0 @@
@EndUserText.label : 'Roast total per blend'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
define table {{p}}blends {
key client : abap.clnt not null;
key blend : abap.char(4) not null;
total_kg : abap.dec(15,3);
}

View File

@@ -1,28 +0,0 @@
CLASS {{p}}roast_seed DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS {{p}}roast_seed IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA lt_lines TYPE STANDARD TABLE OF {{p}}roasts.
lt_lines = VALUE #(
( client = sy-mandt order_id = '0000000001' blend = 'PILS' quantity_kg = '120.500' )
( client = sy-mandt order_id = '0000000002' blend = 'WEIZ' quantity_kg = '80.000' )
( client = sy-mandt order_id = '0000000003' blend = 'PILS' quantity_kg = '60.250' )
( client = sy-mandt order_id = '0000000004' blend = 'KOLS' quantity_kg = '45.000' )
( client = sy-mandt order_id = '0000000005' blend = 'PILS' quantity_kg = '0.000' )
( client = sy-mandt order_id = '0000000006' blend = 'WEIZ' quantity_kg = '20.000' )
).
DELETE FROM {{p}}roasts.
INSERT {{p}}roasts FROM TABLE @lt_lines.
out->write( |{ sy-dbcnt } roast lines inserted| ).
ENDMETHOD.
ENDCLASS.

View File

@@ -1,8 +0,0 @@
@EndUserText.label : 'Roast order line'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
define table {{p}}roasts {
key client : abap.clnt not null;
key order_id : abap.numc(10) not null;
blend : abap.char(4);
quantity_kg : abap.dec(15,3);
}

View File

@@ -1,52 +1,71 @@
# 1. Goal
A coffee roastery plans its daily production. A planning program calls one
function module for each production day. The function module groups the roast
order lines of the day by coffee blend and returns the total green coffee
quantity of each blend. The planning program shows the blend with the largest
quantity first.
A regional emergency control centre collects the open emergency calls of two
control centres. The dispatch program calls one function module for each
dispatch run. The function module must merge the calls of both control centres
and must return the figures of the dispatch list.
# 2. Open questions
None.
# 3. Context
- Function group {{P}}FG_ROAST exists in package $TMP.
- Table {{P}}ROASTS exists in package $TMP. It stores the roast order lines of
a production day.
- Table {{P}}BLENDS exists in package $TMP. It is the line type of the result
- Function group {{P}}FG_ALERT exists in package $TMP.
- The database table {{P}}ALERT_A exists in package $TMP. It contains the open
emergency calls of control centre A.
- The database table {{P}}ALERT_B exists in package $TMP. It contains the open
emergency calls of control centre B.
- Both tables have the same fields:
- CALL_ID: the call number, 10 characters.
- PRIORITY: the urgency. 1 is the highest urgency, 5 is the lowest urgency.
- CALL_DATE: the date on which the call was reported.
- CALL_TIME: the time at which the call was reported.
- DISTRICT: the district of the caller, 4 characters.
- CALL_ID is the key of each table. A call ID occurs at most one time in each
table.
- The class {{P}}ROAST_SEED fills {{P}}ROASTS with sample data. Run it to get
test data.
# 4. Contract
- Create the function module {{P}}ROAST_SUM in function group {{P}}FG_ROAST.
- Interface of {{P}}ROAST_SUM:
- IMPORTING it_orders TYPE STANDARD TABLE OF {{P}}ROASTS
- EXPORTING et_totals TYPE STANDARD TABLE OF {{P}}BLENDS
- EXPORTING ev_grand_total TYPE decfloat34
- The function module has no exceptions.
- Create the function module {{P}}ALERT_MERGE in function group {{P}}FG_ALERT.
- The function module has this interface:
- Importing parameter IV_MAX_PRIORITY, type i.
- Exporting parameter EV_COUNT, type i.
- Exporting parameter EV_DUPLICATES, type i.
- Exporting parameter EV_FIRST_CALL_ID, type string.
- Exporting parameter EV_FIRST_DISTRICT, type string.
- Exporting parameter EV_LAST_CALL_ID, type string.
- Exception INVALID_PRIORITY.
# 5. Business rules
1. A line of it_orders is a production line if its quantity is greater than 0.
A line with a quantity of 0 or less is not a production line.
2. et_totals has exactly one line for each blend that has at least one
production line in it_orders.
3. The total quantity of a blend is the sum of the quantities of all its
production lines.
4. Sort et_totals by total quantity. The largest total quantity comes first.
5. If two blends have the same total quantity, the blend that is smaller in the
character order comes first.
6. ev_grand_total is the sum of the total quantities of all lines of et_totals.
7. If it_orders is empty, or if it_orders has no production line, et_totals is
empty and ev_grand_total is 0.
1. The function module reads the open calls of both control centres from the
tables {{P}}ALERT_A and {{P}}ALERT_B.
2. The dispatch list contains each call ID at most one time.
3. If one call ID is in both tables, the dispatch list contains the call with
the earlier report time. The report time is CALL_DATE first and CALL_TIME
second.
4. If both calls with the same call ID have the same report time, the dispatch
list contains the call from {{P}}ALERT_A.
5. The dispatch list contains only calls with a PRIORITY that is at most
IV_MAX_PRIORITY.
6. The dispatch list is sorted by PRIORITY in ascending order. Lines with the
same PRIORITY are sorted by CALL_DATE in ascending order, then by CALL_TIME
in ascending order, then by CALL_ID in ascending order.
7. EV_COUNT is the number of lines in the dispatch list.
8. EV_DUPLICATES is the number of call IDs that are in both tables. Rule 5
does not change EV_DUPLICATES.
9. EV_FIRST_CALL_ID and EV_FIRST_DISTRICT are the call ID and the district of
the first line of the dispatch list. EV_LAST_CALL_ID is the call ID of the
last line of the dispatch list. If the dispatch list is empty, these three
parameters are empty strings.
10. If IV_MAX_PRIORITY is smaller than 1 or larger than 5, raise the exception
INVALID_PRIORITY.
11. If a call in {{P}}ALERT_A or in {{P}}ALERT_B has a PRIORITY that is smaller
than 1 or larger than 5, raise the exception INVALID_PRIORITY.
# 6. Constraints
- Release target: 7.58.
- Package: $TMP. Do not use transports.
- Coding standard: Clean ABAP. Keep the function module short. Choose the
internal table kind and the access path that fit the task.
- Out of scope: do not change {{P}}ROASTS, {{P}}BLENDS, {{P}}ROAST_SEED.
- Apply the coding standards of Clean ABAP.
- Out of scope: do not change the tables {{P}}ALERT_A and {{P}}ALERT_B and the
function group {{P}}FG_ALERT.
# 7. Acceptance
- The function module {{P}}ROAST_SUM is active and has no syntax error.
- The function module is active and has no syntax error.
- The hidden tests pass.
- Write ABAP Unit tests for the function module in a global test class.

View File

@@ -10,83 +10,85 @@
"max_activations": 15
},
"seed": [
{
"type": "TABL",
"name": "{{P}}ALERT_A",
"file": "seed/alert_a.tabl.abap",
"description": "Emergency calls of control centre A"
},
{
"type": "TABL",
"name": "{{P}}ALERT_B",
"file": "seed/alert_b.tabl.abap",
"description": "Emergency calls of control centre B"
},
{
"type": "FUGR",
"name": "{{P}}FG_ROAST",
"description": "Roast production"
},
{
"type": "TABL",
"name": "{{P}}ROASTS",
"file": "seed/roasts.tabl.abap",
"description": "Roast order lines of a production day"
},
{
"type": "TABL",
"name": "{{P}}BLENDS",
"file": "seed/blends.tabl.abap",
"description": "Line type of the result table"
},
{
"type": "CLAS",
"name": "{{P}}ROAST_SEED",
"file": "seed/roast_seed.clas.abap",
"description": "Sample data for {{P}}ROASTS",
"run": true
"name": "{{P}}FG_ALERT",
"description": "Emergency alert processing"
}
],
"contract": [
{
"type": "FUNC",
"name": "{{P}}ROAST_SUM",
"functionGroup": "{{P}}FG_ROAST",
"name": "{{P}}ALERT_MERGE",
"functionGroup": "{{P}}FG_ALERT",
"params": [
{
"name": "it_orders",
"type": "STANDARD TABLE OF {{P}}ROASTS"
"name": "iv_max_priority",
"type": "i"
},
{
"name": "et_totals",
"type": "STANDARD TABLE OF {{P}}BLENDS"
"name": "ev_count",
"type": "i"
},
{
"name": "ev_grand_total",
"type": "decfloat34"
"name": "ev_duplicates",
"type": "i"
},
{
"name": "ev_first_call_id",
"type": "string"
},
{
"name": "ev_first_district",
"type": "string"
},
{
"name": "ev_last_call_id",
"type": "string"
}
]
}
],
"out_of_scope": [
"{{P}}ROASTS",
"{{P}}BLENDS",
"{{P}}ROAST_SEED"
"{{P}}ALERT_A",
"{{P}}ALERT_B"
],
"hidden_tests": [
{
"type": "CLAS",
"name": "{{P}}T14_HIDDEN",
"file": "hidden/t14_hidden.clas.abap",
"description": "T14 hidden tests"
"name": "{{P}}T41_HIDDEN",
"file": "hidden/t41_hidden.clas.abap",
"description": "T41 hidden tests"
}
],
"reference": [
{
"type": "FUNC",
"name": "{{P}}ROAST_SUM",
"functionGroup": "{{P}}FG_ROAST",
"file": "reference/roast_sum.func.abap",
"description": "Roast sum module"
"name": "{{P}}ALERT_MERGE",
"functionGroup": "{{P}}FG_ALERT",
"file": "reference/alert_merge.func.abap",
"description": "Alert merge module"
},
{
"type": "CLAS",
"name": "{{P}}T14_TEST",
"file": "reference/t14_test.clas.abap",
"description": "T14 own tests"
"name": "{{P}}T41_TEST",
"file": "reference/t41_test.clas.abap",
"description": "T41 own tests"
}
],
"craft_checks": [
"method_length",
"table_kind",
"access_path"
"method_length"
]
}

View File

@@ -0,0 +1,36 @@
{
"id": "G0122",
"pool": "eval",
"object_type": "FUNC",
"category": "C",
"attempts": [
{
"stage": "bundle",
"errors": [
"seed/t_path.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)",
"seed/t_path.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)",
"seed/t_fee.tabl.abap: a table needs the annotation @AbapCatalog.tableCategory (for example #TRANSPARENT, #A)",
"seed/t_fee.tabl.abap: a table needs the annotation @AbapCatalog.deliveryClass (for example #TRANSPARENT, #A)"
],
"by_check": {
"static": 4
}
},
{
"stage": "validate",
"oracle": null,
"null": null
},
{
"stage": "validate",
"oracle": 0,
"null": 0
},
{
"stage": "validate",
"oracle": 0,
"null": 0
}
],
"accepted": false
}

View File

@@ -1,302 +0,0 @@
CLASS {{p}}t31_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
TYPES ty_stations TYPE STANDARD TABLE OF {{p}}station.
TYPES ty_moves TYPE STANDARD TABLE OF {{p}}move.
TYPES ty_id TYPE {{p}}station-station_id.
TYPES ty_name TYPE {{p}}station-station_name.
METHODS given
IMPORTING it_stations TYPE ty_stations
it_moves TYPE ty_moves.
METHODS rebalance
IMPORTING iv_dry_run TYPE abap_bool DEFAULT abap_false
EXPORTING ev_applied TYPE i
ev_rejected TYPE i.
METHODS bikes_of
IMPORTING iv_id TYPE ty_id
RETURNING VALUE(rv_bikes) TYPE i.
METHODS name_of
IMPORTING iv_id TYPE ty_id
RETURNING VALUE(rv_name) TYPE ty_name.
METHODS applies_one_move FOR TESTING.
METHODS applies_in_move_order FOR TESTING.
METHODS rejects_unknown_station FOR TESTING.
METHODS rejects_insufficient_bikes FOR TESTING.
METHODS rejects_full_target FOR TESTING.
METHODS rejects_non_positive_qty FOR TESTING.
METHODS rejects_same_station FOR TESTING.
METHODS counts_rejections FOR TESTING.
METHODS keeps_untouched_stations FOR TESTING.
METHODS dry_run_keeps_state FOR TESTING.
ENDCLASS.
CLASS {{p}}t31_hidden IMPLEMENTATION.
METHOD given.
DATA(lt_stations) = it_stations.
DATA(lt_moves) = it_moves.
LOOP AT lt_stations ASSIGNING FIELD-SYMBOL(<ls_station>).
<ls_station>-client = sy-mandt.
ENDLOOP.
LOOP AT lt_moves ASSIGNING FIELD-SYMBOL(<ls_move>).
<ls_move>-client = sy-mandt.
ENDLOOP.
DELETE FROM {{p}}station.
DELETE FROM {{p}}move.
INSERT {{p}}station FROM TABLE @lt_stations.
INSERT {{p}}move FROM TABLE @lt_moves.
ENDMETHOD.
METHOD rebalance.
CALL FUNCTION '{{P}}BIKE_REBALANCE'
EXPORTING
iv_dry_run = iv_dry_run
IMPORTING
ev_applied = ev_applied
ev_rejected = ev_rejected
EXCEPTIONS
OTHERS = 1.
cl_abap_unit_assert=>assert_subrc( exp = 0 ).
ENDMETHOD.
METHOD bikes_of.
SELECT SINGLE bikes FROM {{p}}station
INTO @rv_bikes
WHERE station_id = @iv_id.
ENDMETHOD.
METHOD name_of.
SELECT SINGLE station_name FROM {{p}}station
INTO @rv_name
WHERE station_id = @iv_id.
ENDMETHOD.
METHOD applies_one_move.
DATA lt_stations TYPE ty_stations.
DATA lt_moves TYPE ty_moves.
DATA lv_applied TYPE i.
DATA lv_rejected TYPE i.
lt_stations = VALUE #(
( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 6 )
( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 1 ) ).
lt_moves = VALUE #(
( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 4 ) ).
given( it_stations = lt_stations it_moves = lt_moves ).
rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 1 ).
cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 0 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 2 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 5 ).
ENDMETHOD.
METHOD applies_in_move_order.
DATA lt_stations TYPE ty_stations.
DATA lt_moves TYPE ty_moves.
DATA lv_applied TYPE i.
DATA lv_rejected TYPE i.
lt_stations = VALUE #(
( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 5 )
( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 0 )
( station_id = 'S003' station_name = 'Gamma' capacity = 10 bikes = 0 ) ).
lt_moves = VALUE #(
( move_no = 2 source_id = 'S001' target_id = 'S002' quantity = 5 )
( move_no = 1 source_id = 'S002' target_id = 'S003' quantity = 5 ) ).
given( it_stations = lt_stations it_moves = lt_moves ).
rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 1 ).
cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 1 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 0 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 5 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S003' ) exp = 0 ).
ENDMETHOD.
METHOD rejects_unknown_station.
DATA lt_stations TYPE ty_stations.
DATA lt_moves TYPE ty_moves.
DATA lv_applied TYPE i.
DATA lv_rejected TYPE i.
lt_stations = VALUE #(
( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 6 ) ).
lt_moves = VALUE #(
( move_no = 1 source_id = 'S009' target_id = 'S001' quantity = 2 ) ).
given( it_stations = lt_stations it_moves = lt_moves ).
rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 0 ).
cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 1 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 6 ).
ENDMETHOD.
METHOD rejects_insufficient_bikes.
DATA lt_stations TYPE ty_stations.
DATA lt_moves TYPE ty_moves.
DATA lv_applied TYPE i.
DATA lv_rejected TYPE i.
lt_stations = VALUE #(
( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 2 )
( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 0 ) ).
lt_moves = VALUE #(
( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 5 ) ).
given( it_stations = lt_stations it_moves = lt_moves ).
rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 0 ).
cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 1 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 2 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 0 ).
ENDMETHOD.
METHOD rejects_full_target.
DATA lt_stations TYPE ty_stations.
DATA lt_moves TYPE ty_moves.
DATA lv_applied TYPE i.
DATA lv_rejected TYPE i.
lt_stations = VALUE #(
( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 5 )
( station_id = 'S002' station_name = 'Beta' capacity = 4 bikes = 4 ) ).
lt_moves = VALUE #(
( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 1 ) ).
given( it_stations = lt_stations it_moves = lt_moves ).
rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 0 ).
cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 1 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 5 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 4 ).
ENDMETHOD.
METHOD rejects_non_positive_qty.
DATA lt_stations TYPE ty_stations.
DATA lt_moves TYPE ty_moves.
DATA lv_applied TYPE i.
DATA lv_rejected TYPE i.
lt_stations = VALUE #(
( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 5 )
( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 0 ) ).
lt_moves = VALUE #(
( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 0 )
( move_no = 2 source_id = 'S001' target_id = 'S002' quantity = -3 ) ).
given( it_stations = lt_stations it_moves = lt_moves ).
rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 0 ).
cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 2 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 5 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 0 ).
ENDMETHOD.
METHOD rejects_same_station.
DATA lt_stations TYPE ty_stations.
DATA lt_moves TYPE ty_moves.
DATA lv_applied TYPE i.
DATA lv_rejected TYPE i.
lt_stations = VALUE #(
( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 5 ) ).
lt_moves = VALUE #(
( move_no = 1 source_id = 'S001' target_id = 'S001' quantity = 2 ) ).
given( it_stations = lt_stations it_moves = lt_moves ).
rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 0 ).
cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 1 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 5 ).
ENDMETHOD.
METHOD counts_rejections.
DATA lt_stations TYPE ty_stations.
DATA lt_moves TYPE ty_moves.
DATA lv_applied TYPE i.
DATA lv_rejected TYPE i.
lt_stations = VALUE #(
( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 5 )
( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 0 ) ).
lt_moves = VALUE #(
( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 2 )
( move_no = 2 source_id = 'S001' target_id = 'S999' quantity = 1 )
( move_no = 3 source_id = 'S001' target_id = 'S002' quantity = 0 )
( move_no = 4 source_id = 'S001' target_id = 'S002' quantity = 100 )
( move_no = 5 source_id = 'S002' target_id = 'S001' quantity = 1 ) ).
given( it_stations = lt_stations it_moves = lt_moves ).
rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 2 ).
cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 3 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 4 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 1 ).
ENDMETHOD.
METHOD keeps_untouched_stations.
DATA lt_stations TYPE ty_stations.
DATA lt_moves TYPE ty_moves.
DATA lv_applied TYPE i.
DATA lv_rejected TYPE i.
lt_stations = VALUE #(
( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 4 )
( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 4 )
( station_id = 'S003' station_name = 'Gamma' capacity = 12 bikes = 7 ) ).
lt_moves = VALUE #(
( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 1 ) ).
given( it_stations = lt_stations it_moves = lt_moves ).
rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 1 ).
cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 0 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 3 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 5 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S003' ) exp = 7 ).
cl_abap_unit_assert=>assert_equals( act = name_of( 'S003' ) exp = CONV ty_name( 'Gamma' ) ).
ENDMETHOD.
METHOD dry_run_keeps_state.
DATA lt_stations TYPE ty_stations.
DATA lt_moves TYPE ty_moves.
DATA lv_applied TYPE i.
DATA lv_rejected TYPE i.
lt_stations = VALUE #(
( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 5 )
( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 0 ) ).
lt_moves = VALUE #(
( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 3 ) ).
given( it_stations = lt_stations it_moves = lt_moves ).
rebalance( EXPORTING iv_dry_run = abap_true
IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 1 ).
cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 0 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 5 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 0 ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,129 @@
CLASS {{p}}track_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
TYPES ty_paths TYPE {{P}}TRACK_TYPES=>tt_path.
TYPES ty_fees TYPE {{P}}TRACK_TYPES=>tt_fee.
TYPES ty_km TYPE {{P}}TRACK_TYPES=>ty_km.
TYPES ty_money TYPE {{P}}TRACK_TYPES=>ty_money.
METHODS call_fm
IMPORTING it_paths TYPE ty_paths
EXPORTING et_fees TYPE ty_fees
ev_subrc TYPE sysubrc.
METHODS one_path_one_row FOR TESTING.
METHODS groups_and_sorts FOR TESTING.
METHODS night_surcharge FOR TESTING.
METHODS minimum_fee FOR TESTING.
METHODS rounds_up FOR TESTING.
METHODS invalid_distance FOR TESTING.
METHODS invalid_category FOR TESTING.
METHODS empty_input FOR TESTING.
ENDCLASS.
CLASS {{p}}track_hidden IMPLEMENTATION.
METHOD call_fm.
CALL FUNCTION '{{P}}TRACK_FEE'
EXPORTING
it_paths = it_paths
IMPORTING
et_fees = et_fees
EXCEPTIONS
invalid_distance = 1
invalid_category = 2
OTHERS = 3.
ev_subrc = sy-subrc.
ENDMETHOD.
METHOD one_path_one_row.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'IC' distance_km = '100.00' night_flag = '' ) ).
call_fm( EXPORTING it_paths = lt_paths
IMPORTING et_fees = DATA(lt_fees) ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_fees ) ).
DATA(ls_fee) = lt_fees[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = 'IC' act = ls_fee-train_cat ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_fee-path_count ).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_km( '100.00' ) act = ls_fee-total_km ).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_money( '120.00' ) act = ls_fee-total_fee ).
ENDMETHOD.
METHOD groups_and_sorts.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'IC' distance_km = '100.00' night_flag = '' )
( path_id = '00000002' train_cat = 'FR' distance_km = '100.00' night_flag = '' )
( path_id = '00000003' train_cat = 'IC' distance_km = '50.00' night_flag = '' ) ).
call_fm( EXPORTING it_paths = lt_paths
IMPORTING et_fees = DATA(lt_fees) ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_fees ) ).
DATA(ls_first) = lt_fees[ 1 ].
DATA(ls_last) = lt_fees[ 2 ].
cl_abap_unit_assert=>assert_equals( exp = 'FR' act = ls_first-train_cat ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_first-path_count ).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_money( '60.00' ) act = ls_first-total_fee ).
cl_abap_unit_assert=>assert_equals( exp = 'IC' act = ls_last-train_cat ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_last-path_count ).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_km( '150.00' ) act = ls_last-total_km ).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_money( '180.00' ) act = ls_last-total_fee ).
ENDMETHOD.
METHOD night_surcharge.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'RG' distance_km = '100.00' night_flag = 'X' ) ).
call_fm( EXPORTING it_paths = lt_paths
IMPORTING et_fees = DATA(lt_fees) ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ).
DATA(ls_fee) = lt_fees[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = CONV ty_money( '108.00' ) act = ls_fee-total_fee ).
ENDMETHOD.
METHOD minimum_fee.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'FR' distance_km = '2.00' night_flag = '' ) ).
call_fm( EXPORTING it_paths = lt_paths
IMPORTING et_fees = DATA(lt_fees) ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ).
DATA(ls_fee) = lt_fees[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = CONV ty_money( '5.00' ) act = ls_fee-total_fee ).
ENDMETHOD.
METHOD rounds_up.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'FR' distance_km = '10.01' night_flag = '' ) ).
call_fm( EXPORTING it_paths = lt_paths
IMPORTING et_fees = DATA(lt_fees) ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ).
DATA(ls_fee) = lt_fees[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = CONV ty_money( '6.01' ) act = ls_fee-total_fee ).
ENDMETHOD.
METHOD invalid_distance.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'IC' distance_km = '0.00' night_flag = '' ) ).
call_fm( EXPORTING it_paths = lt_paths
IMPORTING et_fees = DATA(lt_fees) ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ).
ENDMETHOD.
METHOD invalid_category.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'XX' distance_km = '10.00' night_flag = '' ) ).
call_fm( EXPORTING it_paths = lt_paths
IMPORTING et_fees = DATA(lt_fees) ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rc ).
ENDMETHOD.
METHOD empty_input.
DATA lt_paths TYPE ty_paths.
call_fm( EXPORTING it_paths = lt_paths
IMPORTING et_fees = DATA(lt_fees) ev_subrc = DATA(lv_rc) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lv_rc ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_fees ) ).
ENDMETHOD.
ENDCLASS.

View File

@@ -1,65 +0,0 @@
FUNCTION {{p}}bike_rebalance
IMPORTING
iv_dry_run TYPE abap_bool
EXPORTING
ev_applied TYPE i
ev_rejected TYPE i.
DATA lt_state TYPE STANDARD TABLE OF {{p}}station.
DATA lt_moves TYPE STANDARD TABLE OF {{p}}move.
SELECT station_id, station_name, capacity, bikes
FROM {{p}}station
INTO TABLE @lt_state.
SELECT move_no, source_id, target_id, quantity
FROM {{p}}move
INTO TABLE @lt_moves.
SORT lt_state BY station_id.
SORT lt_moves BY move_no.
ev_applied = 0.
ev_rejected = 0.
LOOP AT lt_moves INTO DATA(ls_move).
IF ls_move-quantity <= 0
OR ls_move-source_id = ls_move-target_id.
ev_rejected = ev_rejected + 1.
CONTINUE.
ENDIF.
READ TABLE lt_state ASSIGNING FIELD-SYMBOL(<ls_source>)
WITH KEY station_id = ls_move-source_id BINARY SEARCH.
IF sy-subrc <> 0.
ev_rejected = ev_rejected + 1.
CONTINUE.
ENDIF.
READ TABLE lt_state ASSIGNING FIELD-SYMBOL(<ls_target>)
WITH KEY station_id = ls_move-target_id BINARY SEARCH.
IF sy-subrc <> 0.
ev_rejected = ev_rejected + 1.
CONTINUE.
ENDIF.
IF <ls_source>-bikes < ls_move-quantity
OR <ls_target>-capacity - <ls_target>-bikes < ls_move-quantity.
ev_rejected = ev_rejected + 1.
CONTINUE.
ENDIF.
<ls_source>-bikes = <ls_source>-bikes - ls_move-quantity.
<ls_target>-bikes = <ls_target>-bikes + ls_move-quantity.
ev_applied = ev_applied + 1.
ENDLOOP.
IF iv_dry_run = abap_false.
LOOP AT lt_state INTO DATA(ls_state).
UPDATE {{p}}station
SET bikes = @ls_state-bikes
WHERE station_id = @ls_state-station_id.
ENDLOOP.
ENDIF.
ENDFUNCTION.

View File

@@ -1,130 +0,0 @@
CLASS {{p}}t31_test DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
TYPES ty_stations TYPE STANDARD TABLE OF {{p}}station.
TYPES ty_moves TYPE STANDARD TABLE OF {{p}}move.
TYPES ty_id TYPE {{p}}station-station_id.
METHODS given
IMPORTING it_stations TYPE ty_stations
it_moves TYPE ty_moves.
METHODS rebalance
IMPORTING iv_dry_run TYPE abap_bool DEFAULT abap_false
EXPORTING ev_applied TYPE i
ev_rejected TYPE i.
METHODS bikes_of
IMPORTING iv_id TYPE ty_id
RETURNING VALUE(rv_bikes) TYPE i.
METHODS applies_move FOR TESTING.
METHODS rejects_move FOR TESTING.
METHODS dry_run FOR TESTING.
ENDCLASS.
CLASS {{p}}t31_test IMPLEMENTATION.
METHOD given.
DATA(lt_stations) = it_stations.
DATA(lt_moves) = it_moves.
LOOP AT lt_stations ASSIGNING FIELD-SYMBOL(<ls_station>).
<ls_station>-client = sy-mandt.
ENDLOOP.
LOOP AT lt_moves ASSIGNING FIELD-SYMBOL(<ls_move>).
<ls_move>-client = sy-mandt.
ENDLOOP.
DELETE FROM {{p}}station.
DELETE FROM {{p}}move.
INSERT {{p}}station FROM TABLE @lt_stations.
INSERT {{p}}move FROM TABLE @lt_moves.
ENDMETHOD.
METHOD rebalance.
CALL FUNCTION '{{P}}BIKE_REBALANCE'
EXPORTING
iv_dry_run = iv_dry_run
IMPORTING
ev_applied = ev_applied
ev_rejected = ev_rejected
EXCEPTIONS
OTHERS = 1.
cl_abap_unit_assert=>assert_subrc( exp = 0 ).
ENDMETHOD.
METHOD bikes_of.
SELECT SINGLE bikes FROM {{p}}station
INTO @rv_bikes
WHERE station_id = @iv_id.
ENDMETHOD.
METHOD applies_move.
DATA lt_stations TYPE ty_stations.
DATA lt_moves TYPE ty_moves.
DATA lv_applied TYPE i.
DATA lv_rejected TYPE i.
lt_stations = VALUE #(
( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 5 )
( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 0 ) ).
lt_moves = VALUE #(
( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 3 ) ).
given( it_stations = lt_stations it_moves = lt_moves ).
rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 1 ).
cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 0 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 2 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 3 ).
ENDMETHOD.
METHOD rejects_move.
DATA lt_stations TYPE ty_stations.
DATA lt_moves TYPE ty_moves.
DATA lv_applied TYPE i.
DATA lv_rejected TYPE i.
lt_stations = VALUE #(
( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 1 )
( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 0 ) ).
lt_moves = VALUE #(
( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 4 ) ).
given( it_stations = lt_stations it_moves = lt_moves ).
rebalance( IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 0 ).
cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 1 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 1 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 0 ).
ENDMETHOD.
METHOD dry_run.
DATA lt_stations TYPE ty_stations.
DATA lt_moves TYPE ty_moves.
DATA lv_applied TYPE i.
DATA lv_rejected TYPE i.
lt_stations = VALUE #(
( station_id = 'S001' station_name = 'Alpha' capacity = 10 bikes = 5 )
( station_id = 'S002' station_name = 'Beta' capacity = 10 bikes = 0 ) ).
lt_moves = VALUE #(
( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 2 ) ).
given( it_stations = lt_stations it_moves = lt_moves ).
rebalance( EXPORTING iv_dry_run = abap_true
IMPORTING ev_applied = lv_applied ev_rejected = lv_rejected ).
cl_abap_unit_assert=>assert_equals( act = lv_applied exp = 1 ).
cl_abap_unit_assert=>assert_equals( act = lv_rejected exp = 0 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S001' ) exp = 5 ).
cl_abap_unit_assert=>assert_equals( act = bikes_of( 'S002' ) exp = 0 ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,87 @@
FUNCTION {{p}}track_fee
IMPORTING
it_paths TYPE STANDARD TABLE
EXPORTING
et_fees TYPE STANDARD TABLE
EXCEPTIONS
invalid_distance
invalid_category.
TYPES: BEGIN OF ty_sum,
train_cat TYPE c LENGTH 2,
path_count TYPE i,
total_km TYPE p LENGTH 13 DECIMALS 2,
total_fee TYPE p LENGTH 13 DECIMALS 2,
END OF ty_sum.
DATA: lt_sums TYPE SORTED TABLE OF ty_sum WITH UNIQUE KEY train_cat,
lv_cat TYPE c LENGTH 2,
lv_km TYPE p LENGTH 13 DECIMALS 2,
lv_flag TYPE c LENGTH 1,
lv_rate TYPE p LENGTH 5 DECIMALS 2,
lv_raw TYPE decfloat34,
lv_fee TYPE p LENGTH 13 DECIMALS 2.
FIELD-SYMBOLS: <ls_in> TYPE any,
<ls_out> TYPE any,
<ls_sum> TYPE ty_sum,
<lv_val> TYPE any.
CLEAR et_fees.
LOOP AT it_paths ASSIGNING <ls_in>.
ASSIGN COMPONENT 'TRAIN_CAT' OF STRUCTURE <ls_in> TO <lv_val>.
lv_cat = <lv_val>.
ASSIGN COMPONENT 'DISTANCE_KM' OF STRUCTURE <ls_in> TO <lv_val>.
lv_km = <lv_val>.
ASSIGN COMPONENT 'NIGHT_FLAG' OF STRUCTURE <ls_in> TO <lv_val>.
lv_flag = <lv_val>.
IF lv_km <= 0.
RAISE invalid_distance.
ENDIF.
CASE lv_cat.
WHEN 'IC'.
lv_rate = '1.20'.
WHEN 'RG'.
lv_rate = '0.90'.
WHEN 'FR'.
lv_rate = '0.60'.
WHEN 'SP'.
lv_rate = '1.50'.
WHEN OTHERS.
RAISE invalid_category.
ENDCASE.
lv_raw = lv_km * lv_rate.
IF lv_flag = 'X'.
lv_raw = lv_raw * 12 / 10.
ENDIF.
lv_fee = round( val = lv_raw dec = 2 mode = cl_abap_math=>round_half_up ).
IF lv_fee < 5.
lv_fee = 5.
ENDIF.
READ TABLE lt_sums ASSIGNING <ls_sum> WITH TABLE KEY train_cat = lv_cat.
IF sy-subrc <> 0.
INSERT VALUE ty_sum( train_cat = lv_cat ) INTO TABLE lt_sums ASSIGNING <ls_sum>.
ENDIF.
<ls_sum>-path_count = <ls_sum>-path_count + 1.
<ls_sum>-total_km = <ls_sum>-total_km + lv_km.
<ls_sum>-total_fee = <ls_sum>-total_fee + lv_fee.
ENDLOOP.
LOOP AT lt_sums ASSIGNING <ls_sum>.
APPEND INITIAL LINE TO et_fees ASSIGNING <ls_out>.
ASSIGN COMPONENT 'TRAIN_CAT' OF STRUCTURE <ls_out> TO <lv_val>.
<lv_val> = <ls_sum>-train_cat.
ASSIGN COMPONENT 'PATH_COUNT' OF STRUCTURE <ls_out> TO <lv_val>.
<lv_val> = <ls_sum>-path_count.
ASSIGN COMPONENT 'TOTAL_KM' OF STRUCTURE <ls_out> TO <lv_val>.
<lv_val> = <ls_sum>-total_km.
ASSIGN COMPONENT 'TOTAL_FEE' OF STRUCTURE <ls_out> TO <lv_val>.
<lv_val> = <ls_sum>-total_fee.
ENDLOOP.
ENDFUNCTION.

View File

@@ -0,0 +1,75 @@
CLASS {{p}}track_test DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
TYPES ty_paths TYPE {{P}}TRACK_TYPES=>tt_path.
TYPES ty_fees TYPE {{P}}TRACK_TYPES=>tt_fee.
TYPES ty_money TYPE {{P}}TRACK_TYPES=>ty_money.
METHODS fees_for
IMPORTING it_paths TYPE ty_paths
RETURNING VALUE(rt_fees) TYPE ty_fees.
METHODS one_category FOR TESTING.
METHODS two_categories FOR TESTING.
METHODS night_surcharge FOR TESTING.
METHODS invalid_distance FOR TESTING.
ENDCLASS.
CLASS {{p}}track_test IMPLEMENTATION.
METHOD fees_for.
CALL FUNCTION '{{P}}TRACK_FEE'
EXPORTING
it_paths = it_paths
IMPORTING
et_fees = rt_fees
EXCEPTIONS
invalid_distance = 1
invalid_category = 2
OTHERS = 3.
cl_abap_unit_assert=>assert_subrc( exp = 0 ).
ENDMETHOD.
METHOD one_category.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'SP' distance_km = '10.00' night_flag = '' ) ).
DATA(lt_fees) = fees_for( lt_paths ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_fees ) ).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_money( '15.00' ) act = lt_fees[ 1 ]-total_fee ).
ENDMETHOD.
METHOD two_categories.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'RG' distance_km = '20.00' night_flag = '' )
( path_id = '00000002' train_cat = 'FR' distance_km = '20.00' night_flag = '' ) ).
DATA(lt_fees) = fees_for( lt_paths ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_fees ) ).
cl_abap_unit_assert=>assert_equals( exp = 'FR' act = lt_fees[ 1 ]-train_cat ).
cl_abap_unit_assert=>assert_equals( exp = 'RG' act = lt_fees[ 2 ]-train_cat ).
ENDMETHOD.
METHOD night_surcharge.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'FR' distance_km = '50.00' night_flag = 'X' ) ).
DATA(lt_fees) = fees_for( lt_paths ).
cl_abap_unit_assert=>assert_equals( exp = CONV ty_money( '36.00' ) act = lt_fees[ 1 ]-total_fee ).
ENDMETHOD.
METHOD invalid_distance.
DATA lt_paths TYPE ty_paths.
lt_paths = VALUE #( ( path_id = '00000001' train_cat = 'IC' distance_km = '0.00' night_flag = '' ) ).
DATA lt_fees TYPE ty_fees.
CALL FUNCTION '{{P}}TRACK_FEE'
EXPORTING
it_paths = lt_paths
IMPORTING
et_fees = lt_fees
EXCEPTIONS
invalid_distance = 1
OTHERS = 2.
cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ).
ENDMETHOD.
ENDCLASS.

View File

@@ -1,12 +0,0 @@
@EndUserText.label : 'Planned bike move'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table {{p}}move {
key client : abap.clnt not null;
key move_no : abap.int4 not null;
source_id : abap.char(6) not null;
target_id : abap.char(6) not null;
quantity : abap.int4 not null;
}

View File

@@ -1,39 +0,0 @@
CLASS {{p}}seed_bike DEFINITION
PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS {{p}}seed_bike IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA lt_stations TYPE STANDARD TABLE OF {{p}}station.
DATA lt_moves TYPE STANDARD TABLE OF {{p}}move.
lt_stations = VALUE #(
( station_id = 'S001' station_name = 'Central Square' capacity = 20 bikes = 12 )
( station_id = 'S002' station_name = 'Harbour' capacity = 15 bikes = 3 )
( station_id = 'S003' station_name = 'Main Station' capacity = 10 bikes = 10 ) ).
lt_moves = VALUE #(
( move_no = 1 source_id = 'S001' target_id = 'S002' quantity = 4 )
( move_no = 2 source_id = 'S002' target_id = 'S003' quantity = 2 ) ).
LOOP AT lt_stations ASSIGNING FIELD-SYMBOL(<ls_station>).
<ls_station>-client = sy-mandt.
ENDLOOP.
LOOP AT lt_moves ASSIGNING FIELD-SYMBOL(<ls_move>).
<ls_move>-client = sy-mandt.
ENDLOOP.
DELETE FROM {{p}}station.
INSERT {{p}}station FROM TABLE @lt_stations.
DELETE FROM {{p}}move.
INSERT {{p}}move FROM TABLE @lt_moves.
out->write( 'Bike sharing sample data inserted.' ).
ENDMETHOD.
ENDCLASS.

View File

@@ -1,12 +0,0 @@
@EndUserText.label : 'Bike station'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table {{p}}station {
key client : abap.clnt not null;
key station_id : abap.char(6) not null;
station_name : abap.char(30) not null;
capacity : abap.int4 not null;
bikes : abap.int4 not null;
}

View File

@@ -0,0 +1,12 @@
FUNCTION {{p}}track_sample
IMPORTING
it_paths TYPE STANDARD TABLE
EXPORTING
et_fees TYPE STANDARD TABLE
EXCEPTIONS
invalid_distance
invalid_category.
CLEAR et_fees.
ENDFUNCTION.

View File

@@ -0,0 +1,28 @@
CLASS {{p}}track_types DEFINITION
PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES: ty_id TYPE n LENGTH 8,
ty_cat TYPE c LENGTH 2,
ty_flag TYPE c LENGTH 1,
ty_km TYPE p LENGTH 13 DECIMALS 2,
ty_money TYPE p LENGTH 13 DECIMALS 2,
BEGIN OF ty_path,
path_id TYPE ty_id,
train_cat TYPE ty_cat,
distance_km TYPE ty_km,
night_flag TYPE ty_flag,
END OF ty_path,
tt_path TYPE STANDARD TABLE OF ty_path WITH EMPTY KEY,
BEGIN OF ty_fee,
train_cat TYPE ty_cat,
path_count TYPE i,
total_km TYPE ty_km,
total_fee TYPE ty_money,
END OF ty_fee,
tt_fee TYPE STANDARD TABLE OF ty_fee WITH EMPTY KEY.
ENDCLASS.
CLASS {{p}}track_types IMPLEMENTATION.
ENDCLASS.

View File

@@ -1,85 +1,69 @@
# 1. Goal
A bike-sharing operator moves bikes between its stations. A background job
calls one function module to apply the planned moves of the operator. The
function module reads the planned moves and the current state of the stations
from the database, applies the moves, and writes the new state of the stations
back to the database. The function module returns the number of applied moves
and the number of rejected moves.
A rail infrastructure manager charges a track access fee to train operating
companies. A billing program calls one function module for each operating day.
The function module calculates the fee of each train path and groups the result
by train category.
# 2. Open questions
None.
# 3. Context
- Function group {{P}}FG_BIKE exists in package $TMP.
- Table {{P}}STATION exists in package $TMP. It holds the current state of the
stations of the operator. The table is client-dependent.
- Table {{P}}MOVE exists in package $TMP. It holds the planned moves. The table
is client-dependent.
- The station IDs in {{P}}STATION are unique. The move numbers in {{P}}MOVE are
unique.
- The function module reads and changes the two tables. It works on the data of
the current client.
- Function group {{P}}FG_TRACK exists in package $TMP.
- Function module {{P}}TRACK_SAMPLE exists in this group. It shows the interface
that the billing program uses. Do not change it.
- The global class {{P}}TRACK_TYPES exists in package $TMP. It only holds the row
types of the input and of the result. Do not change it. Its public types are:
- TY_PATH: PATH_ID (type N, length 8), TRAIN_CAT (type C, length 2),
DISTANCE_KM (packed, length 13, 2 decimals), NIGHT_FLAG (type C, length 1).
- TT_PATH: standard table of TY_PATH.
- TY_FEE: TRAIN_CAT (type C, length 2), PATH_COUNT (type I), TOTAL_KM (packed,
length 13, 2 decimals), TOTAL_FEE (packed, length 13, 2 decimals).
- TT_FEE: standard table of TY_FEE.
- The billing program builds a standard table with the components of TY_PATH and
reads a standard table with the components of TY_FEE. The component names are
the interface between the billing program and the function module.
# 4. Contract
Function module {{P}}BIKE_REBALANCE in the function group {{P}}FG_BIKE:
IMPORTING
iv_dry_run TYPE abap_bool
EXPORTING
ev_applied TYPE i
ev_rejected TYPE i
Table {{P}}STATION:
client (key)
station_id (key) TYPE c LENGTH 6
station_name TYPE c LENGTH 30
capacity TYPE i
bikes TYPE i
Table {{P}}MOVE:
client (key)
move_no (key) TYPE i
source_id TYPE c LENGTH 6
target_id TYPE c LENGTH 6
quantity TYPE i
Create the function module {{P}}TRACK_FEE in function group {{P}}FG_TRACK with
this interface:
- Importing parameter IT_PATHS, type STANDARD TABLE.
- Exporting parameter ET_FEES, type STANDARD TABLE.
- Exceptions INVALID_DISTANCE and INVALID_CATEGORY.
The two table parameters are generic standard tables. Their row components are
fixed by TY_PATH and TY_FEE of {{P}}TRACK_TYPES.
# 5. Business rules
1. Read all rows of {{P}}STATION and all rows of {{P}}MOVE.
2. Apply the moves in ascending order of move_no. Do not apply them in the
order of the table.
3. A move is rejected if at least one of these conditions is true:
- quantity is less than or equal to 0;
- source_id and target_id are equal;
- source_id is not the ID of a station in {{P}}STATION;
- target_id is not the ID of a station in {{P}}STATION;
- the source station has fewer bikes than quantity;
- the target station has fewer free docks than quantity. The free docks of
a station are capacity minus bikes.
4. A rejected move does not change any station.
5. An accepted move subtracts quantity from the bikes of the source station
and adds quantity to the bikes of the target station.
6. After the last move, the function module writes the new bikes value of the
stations back to {{P}}STATION. station_name and capacity do not change.
7. If iv_dry_run is true, the function module does not change {{P}}STATION.
8. ev_applied is the number of accepted moves. ev_rejected is the number of
rejected moves. Both numbers are also returned if iv_dry_run is true.
1. A train path with DISTANCE_KM less than or equal to zero is not valid. Raise
the exception INVALID_DISTANCE.
2. The rate of a train category is:
- IC: 1.20 per kilometre
- RG: 0.90 per kilometre
- FR: 0.60 per kilometre
- SP: 1.50 per kilometre
3. A train category that is not in rule 2 is not valid. Raise the exception
INVALID_CATEGORY.
4. The fee of one path is DISTANCE_KM multiplied by the rate of its train
category.
5. If NIGHT_FLAG is 'X', multiply the fee of the path by 1.20.
6. Round the fee of the path half up to two decimals.
7. The minimum fee of one path is 5.00. If the fee of the path is less than
5.00, use 5.00.
8. The result table has one row for each train category that occurs in the
input table.
9. PATH_COUNT is the number of paths of the category.
10. TOTAL_KM is the sum of DISTANCE_KM of the paths of the category.
11. TOTAL_FEE is the sum of the fees of the paths of the category.
12. Sort the result table in ascending order of TRAIN_CAT.
13. If the input table is empty, the result table is empty. Do not raise an
exception.
# 6. Constraints
- Release target: SAP_BASIS 816 (ABAP Platform 2025).
- Package: $TMP. Do not create a transport request.
- Clean ABAP. Keep the function module and every method below 40 statements.
- Out of scope: do not change {{P}}STATION, {{P}}MOVE and {{P}}SEED_BIKE.
- Release target: 8.16.
- Coding standard: Clean ABAP. Keep methods below 40 statements. Pass large
parameters by reference.
- Out of scope: do not change {{P}}TRACK_SAMPLE or {{P}}TRACK_TYPES.
# 7. Acceptance
- The function module is active and has no syntax error.
- The hidden tests pass.
- Write ABAP Unit tests for the function module in a global test class.

View File

@@ -11,77 +11,70 @@
},
"seed": [
{
"type": "TABL",
"name": "{{P}}STATION",
"file": "seed/station.tabl.abap",
"description": "Bike stations of the operator"
},
{
"type": "TABL",
"name": "{{P}}MOVE",
"file": "seed/move.tabl.abap",
"description": "Planned bike moves"
"type": "CLAS",
"name": "{{P}}TRACK_TYPES",
"file": "seed/track_types.clas.abap",
"description": "Row types for train paths and track fees"
},
{
"type": "FUGR",
"name": "{{P}}FG_BIKE",
"description": "Bike sharing function group"
"name": "{{P}}FG_TRACK",
"description": "Track access fee"
},
{
"type": "CLAS",
"name": "{{P}}SEED_BIKE",
"file": "seed/seed_bike.clas.abap",
"description": "Sample data for the two tables",
"run": true
"type": "FUNC",
"name": "{{P}}TRACK_SAMPLE",
"functionGroup": "{{P}}FG_TRACK",
"file": "seed/track_sample.func.abap",
"description": "Sample module that shows the interface of the billing program"
}
],
"contract": [
{
"type": "CLAS",
"name": "{{P}}TRACK_TYPES"
},
{
"type": "FUNC",
"name": "{{P}}BIKE_REBALANCE",
"functionGroup": "{{P}}FG_BIKE",
"name": "{{P}}TRACK_FEE",
"functionGroup": "{{P}}FG_TRACK",
"params": [
{
"name": "iv_dry_run",
"type": "abap_bool"
"name": "it_paths",
"type": "STANDARD TABLE"
},
{
"name": "ev_applied",
"type": "i"
},
{
"name": "ev_rejected",
"type": "i"
"name": "et_fees",
"type": "STANDARD TABLE"
}
]
}
],
"out_of_scope": [
"{{P}}STATION",
"{{P}}MOVE",
"{{P}}SEED_BIKE"
"{{P}}TRACK_SAMPLE",
"{{P}}TRACK_TYPES"
],
"hidden_tests": [
{
"type": "CLAS",
"name": "{{P}}T31_HIDDEN",
"file": "hidden/t31_hidden.clas.abap",
"description": "Hidden tests for the bike rebalancing"
"name": "{{P}}TRACK_HIDDEN",
"file": "hidden/track_hidden.clas.abap",
"description": "Hidden tests for the track access fee"
}
],
"reference": [
{
"type": "FUNC",
"name": "{{P}}BIKE_REBALANCE",
"functionGroup": "{{P}}FG_BIKE",
"file": "reference/bike_rebalance.func.abap",
"description": "Reference implementation of the rebalancing"
"name": "{{P}}TRACK_FEE",
"functionGroup": "{{P}}FG_TRACK",
"file": "reference/track_fee.func.abap",
"description": "Track access fee module"
},
{
"type": "CLAS",
"name": "{{P}}T31_TEST",
"file": "reference/t31_test.clas.abap",
"description": "Own ABAP Unit tests for the function module"
"name": "{{P}}TRACK_TEST",
"file": "reference/track_test.clas.abap",
"description": "Own tests for the track access fee"
}
],
"craft_checks": [

View File

@@ -0,0 +1,17 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 0,
"parts": {
"total": 0,
"failed_gates": [
"G1_active",
"G2_contract",
"G3_hidden_runs"
]
},
"hidden": "0/0",
"tool_calls": 19,
"seconds": 826.4,
"run_dir": "runs/emp/11008_G0123_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,17 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 0,
"parts": {
"total": 0,
"failed_gates": [
"G1_active",
"G2_contract",
"G3_hidden_runs"
]
},
"hidden": "0/0",
"tool_calls": 60,
"seconds": 318.4,
"run_dir": "runs/emp/11009_G0124_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,127 @@
REPORT {{p}}fleet_dist.
TYPES:
BEGIN OF ty_line,
veh_id TYPE {{p}}veh-veh_id,
depot TYPE {{p}}veh-depot,
distance TYPE i,
readings TYPE i,
END OF ty_line,
tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY,
tt_depot TYPE RANGE OF {{p}}veh-depot.
DATA gv_depot TYPE {{p}}veh-depot.
PARAMETERS p_from TYPE d OBLIGATORY.
PARAMETERS p_to TYPE d OBLIGATORY.
SELECT-OPTIONS s_depot FOR gv_depot.
CLASS lcl_report DEFINITION FINAL.
PUBLIC SECTION.
METHODS select_distances
IMPORTING iv_from TYPE d
iv_to TYPE d
it_depot TYPE tt_depot
RETURNING VALUE(rt_lines) TYPE tt_line.
METHODS display
CHANGING ct_lines TYPE tt_line.
PRIVATE SECTION.
TYPES:
BEGIN OF ty_raw,
veh_id TYPE {{p}}veh-veh_id,
depot TYPE {{p}}veh-depot,
max_odo TYPE {{p}}odom-odometer,
min_odo TYPE {{p}}odom-odometer,
readings TYPE i,
END OF ty_raw,
tt_raw TYPE STANDARD TABLE OF ty_raw WITH EMPTY KEY.
ENDCLASS.
CLASS lcl_report IMPLEMENTATION.
METHOD select_distances.
DATA lt_raw TYPE tt_raw.
SELECT v~veh_id, v~depot, MAX( o~odometer ) AS max_odo, MIN( o~odometer ) AS min_odo,
COUNT( * ) AS readings
FROM {{p}}veh AS v
INNER JOIN {{p}}odom AS o ON o~veh_id = v~veh_id
WHERE o~read_date >= @iv_from
OR o~read_date <= @iv_to
AND v~depot IN @it_depot
GROUP BY v~veh_id, v~depot
HAVING COUNT( * ) >= 2
INTO CORRESPONDING FIELDS OF TABLE @lt_raw.
LOOP AT lt_raw ASSIGNING FIELD-SYMBOL(<ls_raw>).
APPEND VALUE #( veh_id = <ls_raw>-veh_id
depot = <ls_raw>-depot
distance = <ls_raw>-max_odo - <ls_raw>-min_odo
readings = <ls_raw>-readings ) TO rt_lines.
ENDLOOP.
SORT rt_lines BY distance DESCENDING veh_id ASCENDING.
ENDMETHOD.
METHOD display.
TRY.
cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv)
CHANGING t_table = ct_lines ).
lo_alv->display( ).
CATCH cx_salv_msg INTO DATA(lx_error).
MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'.
ENDTRY.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA(go_report) = NEW lcl_report( ).
DATA(gt_lines) = go_report->select_distances( iv_from = p_from iv_to = p_to it_depot = s_depot[] ).
go_report->display( CHANGING ct_lines = gt_lines ).
CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
CLASS-DATA go_env TYPE REF TO if_osql_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS distances_per_vehicle FOR TESTING.
METHODS depot_filter FOR TESTING.
ENDCLASS.
CLASS ltc_report IMPLEMENTATION.
METHOD class_setup.
go_env = cl_osql_test_environment=>create(
i_dependency_list = VALUE #( ( '{{P}}VEH' ) ( '{{P}}ODOM' ) ) ).
DATA lt_veh TYPE STANDARD TABLE OF {{p}}veh WITH EMPTY KEY.
lt_veh = VALUE #( ( veh_id = 'V1' depot = 'D1' )
( veh_id = 'V2' depot = 'D2' )
( veh_id = 'V3' depot = 'D2' ) ).
go_env->insert_test_data( lt_veh ).
DATA lt_odom TYPE STANDARD TABLE OF {{p}}odom WITH EMPTY KEY.
lt_odom = VALUE #( ( read_id = 1 veh_id = 'V1' read_date = '20260101' odometer = 100 )
( read_id = 2 veh_id = 'V1' read_date = '20260110' odometer = 400 )
( read_id = 3 veh_id = 'V2' read_date = '20260105' odometer = 50 )
( read_id = 4 veh_id = 'V3' read_date = '20260102' odometer = 200 )
( read_id = 5 veh_id = 'V3' read_date = '20260112' odometer = 500 )
( read_id = 6 veh_id = 'V3' read_date = '20260122' odometer = 900 ) ).
go_env->insert_test_data( lt_odom ).
ENDMETHOD.
METHOD class_teardown.
go_env->destroy( ).
ENDMETHOD.
METHOD distances_per_vehicle.
DATA(lt_lines) = NEW lcl_report( )->select_distances(
iv_from = '20260101' iv_to = '20260131' it_depot = VALUE #( ) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ).
cl_abap_unit_assert=>assert_equals( exp = 'V3' act = lt_lines[ 1 ]-veh_id ).
cl_abap_unit_assert=>assert_equals( exp = 700 act = lt_lines[ 1 ]-distance ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lt_lines[ 1 ]-readings ).
cl_abap_unit_assert=>assert_equals( exp = 300 act = lt_lines[ 2 ]-distance ).
ENDMETHOD.
METHOD depot_filter.
DATA(lt_lines) = NEW lcl_report( )->select_distances(
iv_from = '20260101' iv_to = '20260131'
it_depot = VALUE #( ( sign = 'I' option = 'EQ' low = 'D2' ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ).
cl_abap_unit_assert=>assert_equals( exp = 'V3' act = lt_lines[ 1 ]-veh_id ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,127 @@
REPORT {{p}}fleet_dist.
TYPES:
BEGIN OF ty_line,
veh_id TYPE {{p}}veh-veh_id,
depot TYPE {{p}}veh-depot,
distance TYPE i,
readings TYPE i,
END OF ty_line,
tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY,
tt_depot TYPE RANGE OF {{p}}veh-depot.
DATA gv_depot TYPE {{p}}veh-depot.
PARAMETERS p_from TYPE d OBLIGATORY.
PARAMETERS p_to TYPE d OBLIGATORY.
SELECT-OPTIONS s_depot FOR gv_depot.
CLASS lcl_report DEFINITION FINAL.
PUBLIC SECTION.
METHODS select_distances
IMPORTING iv_from TYPE d
iv_to TYPE d
it_depot TYPE tt_depot
RETURNING VALUE(rt_lines) TYPE tt_line.
METHODS display
CHANGING ct_lines TYPE tt_line.
PRIVATE SECTION.
TYPES:
BEGIN OF ty_raw,
veh_id TYPE {{p}}veh-veh_id,
depot TYPE {{p}}veh-depot,
max_odo TYPE {{p}}odom-odometer,
min_odo TYPE {{p}}odom-odometer,
readings TYPE i,
END OF ty_raw,
tt_raw TYPE STANDARD TABLE OF ty_raw WITH EMPTY KEY.
ENDCLASS.
CLASS lcl_report IMPLEMENTATION.
METHOD select_distances.
DATA lt_raw TYPE tt_raw.
SELECT v~veh_id, v~depot, MAX( o~odometer ) AS max_odo, MIN( o~odometer ) AS min_odo,
COUNT( * ) AS readings
FROM {{p}}veh AS v
INNER JOIN {{p}}odom AS o ON o~veh_id = v~veh_id
WHERE o~read_date >= @iv_from
AND o~read_date <= @iv_to
OR v~depot IN @it_depot
GROUP BY v~veh_id, v~depot
HAVING COUNT( * ) >= 2
INTO CORRESPONDING FIELDS OF TABLE @lt_raw.
LOOP AT lt_raw ASSIGNING FIELD-SYMBOL(<ls_raw>).
APPEND VALUE #( veh_id = <ls_raw>-veh_id
depot = <ls_raw>-depot
distance = <ls_raw>-max_odo - <ls_raw>-min_odo
readings = <ls_raw>-readings ) TO rt_lines.
ENDLOOP.
SORT rt_lines BY distance DESCENDING veh_id ASCENDING.
ENDMETHOD.
METHOD display.
TRY.
cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv)
CHANGING t_table = ct_lines ).
lo_alv->display( ).
CATCH cx_salv_msg INTO DATA(lx_error).
MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'.
ENDTRY.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA(go_report) = NEW lcl_report( ).
DATA(gt_lines) = go_report->select_distances( iv_from = p_from iv_to = p_to it_depot = s_depot[] ).
go_report->display( CHANGING ct_lines = gt_lines ).
CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
CLASS-DATA go_env TYPE REF TO if_osql_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS distances_per_vehicle FOR TESTING.
METHODS depot_filter FOR TESTING.
ENDCLASS.
CLASS ltc_report IMPLEMENTATION.
METHOD class_setup.
go_env = cl_osql_test_environment=>create(
i_dependency_list = VALUE #( ( '{{P}}VEH' ) ( '{{P}}ODOM' ) ) ).
DATA lt_veh TYPE STANDARD TABLE OF {{p}}veh WITH EMPTY KEY.
lt_veh = VALUE #( ( veh_id = 'V1' depot = 'D1' )
( veh_id = 'V2' depot = 'D2' )
( veh_id = 'V3' depot = 'D2' ) ).
go_env->insert_test_data( lt_veh ).
DATA lt_odom TYPE STANDARD TABLE OF {{p}}odom WITH EMPTY KEY.
lt_odom = VALUE #( ( read_id = 1 veh_id = 'V1' read_date = '20260101' odometer = 100 )
( read_id = 2 veh_id = 'V1' read_date = '20260110' odometer = 400 )
( read_id = 3 veh_id = 'V2' read_date = '20260105' odometer = 50 )
( read_id = 4 veh_id = 'V3' read_date = '20260102' odometer = 200 )
( read_id = 5 veh_id = 'V3' read_date = '20260112' odometer = 500 )
( read_id = 6 veh_id = 'V3' read_date = '20260122' odometer = 900 ) ).
go_env->insert_test_data( lt_odom ).
ENDMETHOD.
METHOD class_teardown.
go_env->destroy( ).
ENDMETHOD.
METHOD distances_per_vehicle.
DATA(lt_lines) = NEW lcl_report( )->select_distances(
iv_from = '20260101' iv_to = '20260131' it_depot = VALUE #( ) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ).
cl_abap_unit_assert=>assert_equals( exp = 'V3' act = lt_lines[ 1 ]-veh_id ).
cl_abap_unit_assert=>assert_equals( exp = 700 act = lt_lines[ 1 ]-distance ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lt_lines[ 1 ]-readings ).
cl_abap_unit_assert=>assert_equals( exp = 300 act = lt_lines[ 2 ]-distance ).
ENDMETHOD.
METHOD depot_filter.
DATA(lt_lines) = NEW lcl_report( )->select_distances(
iv_from = '20260101' iv_to = '20260131'
it_depot = VALUE #( ( sign = 'I' option = 'EQ' low = 'D2' ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ).
cl_abap_unit_assert=>assert_equals( exp = 'V3' act = lt_lines[ 1 ]-veh_id ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,127 @@
REPORT {{p}}fleet_dist.
TYPES:
BEGIN OF ty_line,
veh_id TYPE {{p}}veh-veh_id,
depot TYPE {{p}}veh-depot,
distance TYPE i,
readings TYPE i,
END OF ty_line,
tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY,
tt_depot TYPE RANGE OF {{p}}veh-depot.
DATA gv_depot TYPE {{p}}veh-depot.
PARAMETERS p_from TYPE d OBLIGATORY.
PARAMETERS p_to TYPE d OBLIGATORY.
SELECT-OPTIONS s_depot FOR gv_depot.
CLASS lcl_report DEFINITION FINAL.
PUBLIC SECTION.
METHODS select_distances
IMPORTING iv_from TYPE d
iv_to TYPE d
it_depot TYPE tt_depot
RETURNING VALUE(rt_lines) TYPE tt_line.
METHODS display
CHANGING ct_lines TYPE tt_line.
PRIVATE SECTION.
TYPES:
BEGIN OF ty_raw,
veh_id TYPE {{p}}veh-veh_id,
depot TYPE {{p}}veh-depot,
max_odo TYPE {{p}}odom-odometer,
min_odo TYPE {{p}}odom-odometer,
readings TYPE i,
END OF ty_raw,
tt_raw TYPE STANDARD TABLE OF ty_raw WITH EMPTY KEY.
ENDCLASS.
CLASS lcl_report IMPLEMENTATION.
METHOD select_distances.
DATA lt_raw TYPE tt_raw.
SELECT v~veh_id, v~depot, MAX( o~odometer ) AS max_odo, MIN( o~odometer ) AS min_odo,
COUNT( * ) AS readings
FROM {{p}}veh AS v
INNER JOIN {{p}}odom AS o ON o~veh_id = v~veh_id
WHERE o~read_date >= @iv_from
AND o~read_date <= @iv_to
AND v~depot IN @it_depot
GROUP BY v~veh_id, v~depot
HAVING COUNT( * ) < 2
INTO CORRESPONDING FIELDS OF TABLE @lt_raw.
LOOP AT lt_raw ASSIGNING FIELD-SYMBOL(<ls_raw>).
APPEND VALUE #( veh_id = <ls_raw>-veh_id
depot = <ls_raw>-depot
distance = <ls_raw>-max_odo - <ls_raw>-min_odo
readings = <ls_raw>-readings ) TO rt_lines.
ENDLOOP.
SORT rt_lines BY distance DESCENDING veh_id ASCENDING.
ENDMETHOD.
METHOD display.
TRY.
cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv)
CHANGING t_table = ct_lines ).
lo_alv->display( ).
CATCH cx_salv_msg INTO DATA(lx_error).
MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'.
ENDTRY.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA(go_report) = NEW lcl_report( ).
DATA(gt_lines) = go_report->select_distances( iv_from = p_from iv_to = p_to it_depot = s_depot[] ).
go_report->display( CHANGING ct_lines = gt_lines ).
CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
CLASS-DATA go_env TYPE REF TO if_osql_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS distances_per_vehicle FOR TESTING.
METHODS depot_filter FOR TESTING.
ENDCLASS.
CLASS ltc_report IMPLEMENTATION.
METHOD class_setup.
go_env = cl_osql_test_environment=>create(
i_dependency_list = VALUE #( ( '{{P}}VEH' ) ( '{{P}}ODOM' ) ) ).
DATA lt_veh TYPE STANDARD TABLE OF {{p}}veh WITH EMPTY KEY.
lt_veh = VALUE #( ( veh_id = 'V1' depot = 'D1' )
( veh_id = 'V2' depot = 'D2' )
( veh_id = 'V3' depot = 'D2' ) ).
go_env->insert_test_data( lt_veh ).
DATA lt_odom TYPE STANDARD TABLE OF {{p}}odom WITH EMPTY KEY.
lt_odom = VALUE #( ( read_id = 1 veh_id = 'V1' read_date = '20260101' odometer = 100 )
( read_id = 2 veh_id = 'V1' read_date = '20260110' odometer = 400 )
( read_id = 3 veh_id = 'V2' read_date = '20260105' odometer = 50 )
( read_id = 4 veh_id = 'V3' read_date = '20260102' odometer = 200 )
( read_id = 5 veh_id = 'V3' read_date = '20260112' odometer = 500 )
( read_id = 6 veh_id = 'V3' read_date = '20260122' odometer = 900 ) ).
go_env->insert_test_data( lt_odom ).
ENDMETHOD.
METHOD class_teardown.
go_env->destroy( ).
ENDMETHOD.
METHOD distances_per_vehicle.
DATA(lt_lines) = NEW lcl_report( )->select_distances(
iv_from = '20260101' iv_to = '20260131' it_depot = VALUE #( ) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ).
cl_abap_unit_assert=>assert_equals( exp = 'V3' act = lt_lines[ 1 ]-veh_id ).
cl_abap_unit_assert=>assert_equals( exp = 700 act = lt_lines[ 1 ]-distance ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lt_lines[ 1 ]-readings ).
cl_abap_unit_assert=>assert_equals( exp = 300 act = lt_lines[ 2 ]-distance ).
ENDMETHOD.
METHOD depot_filter.
DATA(lt_lines) = NEW lcl_report( )->select_distances(
iv_from = '20260101' iv_to = '20260131'
it_depot = VALUE #( ( sign = 'I' option = 'EQ' low = 'D2' ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ).
cl_abap_unit_assert=>assert_equals( exp = 'V3' act = lt_lines[ 1 ]-veh_id ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,127 @@
REPORT {{p}}fleet_dist.
TYPES:
BEGIN OF ty_line,
veh_id TYPE {{p}}veh-veh_id,
depot TYPE {{p}}veh-depot,
distance TYPE i,
readings TYPE i,
END OF ty_line,
tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY,
tt_depot TYPE RANGE OF {{p}}veh-depot.
DATA gv_depot TYPE {{p}}veh-depot.
PARAMETERS p_from TYPE d OBLIGATORY.
PARAMETERS p_to TYPE d OBLIGATORY.
SELECT-OPTIONS s_depot FOR gv_depot.
CLASS lcl_report DEFINITION FINAL.
PUBLIC SECTION.
METHODS select_distances
IMPORTING iv_from TYPE d
iv_to TYPE d
it_depot TYPE tt_depot
RETURNING VALUE(rt_lines) TYPE tt_line.
METHODS display
CHANGING ct_lines TYPE tt_line.
PRIVATE SECTION.
TYPES:
BEGIN OF ty_raw,
veh_id TYPE {{p}}veh-veh_id,
depot TYPE {{p}}veh-depot,
max_odo TYPE {{p}}odom-odometer,
min_odo TYPE {{p}}odom-odometer,
readings TYPE i,
END OF ty_raw,
tt_raw TYPE STANDARD TABLE OF ty_raw WITH EMPTY KEY.
ENDCLASS.
CLASS lcl_report IMPLEMENTATION.
METHOD select_distances.
DATA lt_raw TYPE tt_raw.
SELECT v~veh_id, v~depot, MAX( o~odometer ) AS max_odo, MIN( o~odometer ) AS min_odo,
COUNT( * ) AS readings
FROM {{p}}veh AS v
INNER JOIN {{p}}odom AS o ON o~veh_id = v~veh_id
WHERE o~read_date >= @iv_from
AND o~read_date <= @iv_to
AND v~depot IN @it_depot
GROUP BY v~veh_id, v~depot
HAVING COUNT( * ) >= 3
INTO CORRESPONDING FIELDS OF TABLE @lt_raw.
LOOP AT lt_raw ASSIGNING FIELD-SYMBOL(<ls_raw>).
APPEND VALUE #( veh_id = <ls_raw>-veh_id
depot = <ls_raw>-depot
distance = <ls_raw>-max_odo - <ls_raw>-min_odo
readings = <ls_raw>-readings ) TO rt_lines.
ENDLOOP.
SORT rt_lines BY distance DESCENDING veh_id ASCENDING.
ENDMETHOD.
METHOD display.
TRY.
cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv)
CHANGING t_table = ct_lines ).
lo_alv->display( ).
CATCH cx_salv_msg INTO DATA(lx_error).
MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'.
ENDTRY.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA(go_report) = NEW lcl_report( ).
DATA(gt_lines) = go_report->select_distances( iv_from = p_from iv_to = p_to it_depot = s_depot[] ).
go_report->display( CHANGING ct_lines = gt_lines ).
CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
CLASS-DATA go_env TYPE REF TO if_osql_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS distances_per_vehicle FOR TESTING.
METHODS depot_filter FOR TESTING.
ENDCLASS.
CLASS ltc_report IMPLEMENTATION.
METHOD class_setup.
go_env = cl_osql_test_environment=>create(
i_dependency_list = VALUE #( ( '{{P}}VEH' ) ( '{{P}}ODOM' ) ) ).
DATA lt_veh TYPE STANDARD TABLE OF {{p}}veh WITH EMPTY KEY.
lt_veh = VALUE #( ( veh_id = 'V1' depot = 'D1' )
( veh_id = 'V2' depot = 'D2' )
( veh_id = 'V3' depot = 'D2' ) ).
go_env->insert_test_data( lt_veh ).
DATA lt_odom TYPE STANDARD TABLE OF {{p}}odom WITH EMPTY KEY.
lt_odom = VALUE #( ( read_id = 1 veh_id = 'V1' read_date = '20260101' odometer = 100 )
( read_id = 2 veh_id = 'V1' read_date = '20260110' odometer = 400 )
( read_id = 3 veh_id = 'V2' read_date = '20260105' odometer = 50 )
( read_id = 4 veh_id = 'V3' read_date = '20260102' odometer = 200 )
( read_id = 5 veh_id = 'V3' read_date = '20260112' odometer = 500 )
( read_id = 6 veh_id = 'V3' read_date = '20260122' odometer = 900 ) ).
go_env->insert_test_data( lt_odom ).
ENDMETHOD.
METHOD class_teardown.
go_env->destroy( ).
ENDMETHOD.
METHOD distances_per_vehicle.
DATA(lt_lines) = NEW lcl_report( )->select_distances(
iv_from = '20260101' iv_to = '20260131' it_depot = VALUE #( ) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ).
cl_abap_unit_assert=>assert_equals( exp = 'V3' act = lt_lines[ 1 ]-veh_id ).
cl_abap_unit_assert=>assert_equals( exp = 700 act = lt_lines[ 1 ]-distance ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lt_lines[ 1 ]-readings ).
cl_abap_unit_assert=>assert_equals( exp = 300 act = lt_lines[ 2 ]-distance ).
ENDMETHOD.
METHOD depot_filter.
DATA(lt_lines) = NEW lcl_report( )->select_distances(
iv_from = '20260101' iv_to = '20260131'
it_depot = VALUE #( ( sign = 'I' option = 'EQ' low = 'D2' ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ).
cl_abap_unit_assert=>assert_equals( exp = 'V3' act = lt_lines[ 1 ]-veh_id ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,127 @@
REPORT {{p}}fleet_dist.
TYPES:
BEGIN OF ty_line,
veh_id TYPE {{p}}veh-veh_id,
depot TYPE {{p}}veh-depot,
distance TYPE i,
readings TYPE i,
END OF ty_line,
tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY,
tt_depot TYPE RANGE OF {{p}}veh-depot.
DATA gv_depot TYPE {{p}}veh-depot.
PARAMETERS p_from TYPE d OBLIGATORY.
PARAMETERS p_to TYPE d OBLIGATORY.
SELECT-OPTIONS s_depot FOR gv_depot.
CLASS lcl_report DEFINITION FINAL.
PUBLIC SECTION.
METHODS select_distances
IMPORTING iv_from TYPE d
iv_to TYPE d
it_depot TYPE tt_depot
RETURNING VALUE(rt_lines) TYPE tt_line.
METHODS display
CHANGING ct_lines TYPE tt_line.
PRIVATE SECTION.
TYPES:
BEGIN OF ty_raw,
veh_id TYPE {{p}}veh-veh_id,
depot TYPE {{p}}veh-depot,
max_odo TYPE {{p}}odom-odometer,
min_odo TYPE {{p}}odom-odometer,
readings TYPE i,
END OF ty_raw,
tt_raw TYPE STANDARD TABLE OF ty_raw WITH EMPTY KEY.
ENDCLASS.
CLASS lcl_report IMPLEMENTATION.
METHOD select_distances.
DATA lt_raw TYPE tt_raw.
SELECT v~veh_id, v~depot, MAX( o~odometer ) AS max_odo, MIN( o~odometer ) AS min_odo,
COUNT( * ) AS readings
FROM {{p}}veh AS v
INNER JOIN {{p}}odom AS o ON o~veh_id = v~veh_id
WHERE o~read_date >= @iv_from
AND o~read_date <= @iv_to
AND v~depot IN @it_depot
GROUP BY v~veh_id, v~depot
HAVING COUNT( * ) >= 2
INTO CORRESPONDING FIELDS OF TABLE @lt_raw.
LOOP AT lt_raw ASSIGNING FIELD-SYMBOL(<ls_raw>).
APPEND VALUE #( veh_id = <ls_raw>-veh_id
depot = <ls_raw>-depot
distance = <ls_raw>-max_odo + <ls_raw>-min_odo
readings = <ls_raw>-readings ) TO rt_lines.
ENDLOOP.
SORT rt_lines BY distance DESCENDING veh_id ASCENDING.
ENDMETHOD.
METHOD display.
TRY.
cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv)
CHANGING t_table = ct_lines ).
lo_alv->display( ).
CATCH cx_salv_msg INTO DATA(lx_error).
MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'.
ENDTRY.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA(go_report) = NEW lcl_report( ).
DATA(gt_lines) = go_report->select_distances( iv_from = p_from iv_to = p_to it_depot = s_depot[] ).
go_report->display( CHANGING ct_lines = gt_lines ).
CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
CLASS-DATA go_env TYPE REF TO if_osql_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS distances_per_vehicle FOR TESTING.
METHODS depot_filter FOR TESTING.
ENDCLASS.
CLASS ltc_report IMPLEMENTATION.
METHOD class_setup.
go_env = cl_osql_test_environment=>create(
i_dependency_list = VALUE #( ( '{{P}}VEH' ) ( '{{P}}ODOM' ) ) ).
DATA lt_veh TYPE STANDARD TABLE OF {{p}}veh WITH EMPTY KEY.
lt_veh = VALUE #( ( veh_id = 'V1' depot = 'D1' )
( veh_id = 'V2' depot = 'D2' )
( veh_id = 'V3' depot = 'D2' ) ).
go_env->insert_test_data( lt_veh ).
DATA lt_odom TYPE STANDARD TABLE OF {{p}}odom WITH EMPTY KEY.
lt_odom = VALUE #( ( read_id = 1 veh_id = 'V1' read_date = '20260101' odometer = 100 )
( read_id = 2 veh_id = 'V1' read_date = '20260110' odometer = 400 )
( read_id = 3 veh_id = 'V2' read_date = '20260105' odometer = 50 )
( read_id = 4 veh_id = 'V3' read_date = '20260102' odometer = 200 )
( read_id = 5 veh_id = 'V3' read_date = '20260112' odometer = 500 )
( read_id = 6 veh_id = 'V3' read_date = '20260122' odometer = 900 ) ).
go_env->insert_test_data( lt_odom ).
ENDMETHOD.
METHOD class_teardown.
go_env->destroy( ).
ENDMETHOD.
METHOD distances_per_vehicle.
DATA(lt_lines) = NEW lcl_report( )->select_distances(
iv_from = '20260101' iv_to = '20260131' it_depot = VALUE #( ) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ).
cl_abap_unit_assert=>assert_equals( exp = 'V3' act = lt_lines[ 1 ]-veh_id ).
cl_abap_unit_assert=>assert_equals( exp = 700 act = lt_lines[ 1 ]-distance ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lt_lines[ 1 ]-readings ).
cl_abap_unit_assert=>assert_equals( exp = 300 act = lt_lines[ 2 ]-distance ).
ENDMETHOD.
METHOD depot_filter.
DATA(lt_lines) = NEW lcl_report( )->select_distances(
iv_from = '20260101' iv_to = '20260131'
it_depot = VALUE #( ( sign = 'I' option = 'EQ' low = 'D2' ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ).
cl_abap_unit_assert=>assert_equals( exp = 'V3' act = lt_lines[ 1 ]-veh_id ).
ENDMETHOD.
ENDCLASS.

View File

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

View File

@@ -0,0 +1,134 @@
CLASS {{p}}fleet_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
TYPES tt_depot TYPE RANGE OF {{p}}veh-depot.
METHODS run
IMPORTING iv_from TYPE d
iv_to TYPE d
it_depot TYPE tt_depot OPTIONAL
RETURNING VALUE(rr_data) TYPE REF TO data.
METHODS value_of
IMPORTING ir_data TYPE REF TO data
iv_row TYPE i
iv_col TYPE string
RETURNING VALUE(rv_value) TYPE string.
METHODS lines_of
IMPORTING ir_data TYPE REF TO data
RETURNING VALUE(rv_lines) TYPE i.
METHODS all_vehicles FOR TESTING.
METHODS sorted_by_distance FOR TESTING.
METHODS distance_and_readings FOR TESTING.
METHODS depot_filter FOR TESTING.
METHODS date_filter FOR TESTING.
METHODS tie_break_by_veh_id FOR TESTING.
METHODS single_reading_excluded FOR TESTING.
METHODS unknown_vehicle_excluded FOR TESTING.
ENDCLASS.
CLASS {{p}}fleet_hidden IMPLEMENTATION.
METHOD run.
cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ).
SUBMIT {{p}}fleet_dist
WITH p_from = iv_from
WITH p_to = iv_to
WITH s_depot IN it_depot
AND RETURN.
TRY.
cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ).
CATCH cx_salv_bs_sc_runtime_info.
ENDTRY.
cl_salv_bs_runtime_info=>clear_all( ).
ENDMETHOD.
METHOD lines_of.
FIELD-SYMBOLS <lt_data> TYPE STANDARD TABLE.
IF ir_data IS BOUND.
ASSIGN ir_data->* TO <lt_data>.
rv_lines = lines( <lt_data> ).
ENDIF.
ENDMETHOD.
METHOD value_of.
FIELD-SYMBOLS <lt_data> TYPE STANDARD TABLE.
ASSIGN ir_data->* TO <lt_data>.
ASSIGN <lt_data>[ iv_row ] TO FIELD-SYMBOL(<ls_line>).
ASSIGN COMPONENT iv_col OF STRUCTURE <ls_line> TO FIELD-SYMBOL(<lv_value>).
cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } missing| ).
rv_value = condense( CONV string( <lv_value> ) ).
ENDMETHOD.
METHOD all_vehicles.
cl_abap_unit_assert=>assert_equals(
exp = 3
act = lines_of( run( iv_from = '20260101' iv_to = '20260131' ) ) ).
ENDMETHOD.
METHOD sorted_by_distance.
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ).
cl_abap_unit_assert=>assert_equals(
exp = `V3` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `VEH_ID` ) ).
cl_abap_unit_assert=>assert_equals(
exp = `V2` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `VEH_ID` ) ).
ENDMETHOD.
METHOD distance_and_readings.
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ).
cl_abap_unit_assert=>assert_equals(
exp = `600` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DISTANCE` ) ).
cl_abap_unit_assert=>assert_equals(
exp = `4` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `READINGS` ) ).
cl_abap_unit_assert=>assert_equals(
exp = `D1` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `DEPOT` ) ).
cl_abap_unit_assert=>assert_equals(
exp = `200` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `DISTANCE` ) ).
ENDMETHOD.
METHOD depot_filter.
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131'
it_depot = VALUE #( ( sign = 'I' option = 'EQ' low = 'D1' ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ).
cl_abap_unit_assert=>assert_equals(
exp = `V1` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `VEH_ID` ) ).
cl_abap_unit_assert=>assert_equals(
exp = `500` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DISTANCE` ) ).
ENDMETHOD.
METHOD date_filter.
DATA(lr_data) = run( iv_from = '20260111' iv_to = '20260131' ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ).
cl_abap_unit_assert=>assert_equals(
exp = `V3` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `VEH_ID` ) ).
cl_abap_unit_assert=>assert_equals(
exp = `300` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DISTANCE` ) ).
cl_abap_unit_assert=>assert_equals(
exp = `3` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `READINGS` ) ).
ENDMETHOD.
METHOD tie_break_by_veh_id.
DATA(lr_data) = run( iv_from = '20251201' iv_to = '20260131' ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lines_of( lr_data ) ).
cl_abap_unit_assert=>assert_equals(
exp = `V1` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `VEH_ID` ) ).
cl_abap_unit_assert=>assert_equals(
exp = `V3` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `VEH_ID` ) ).
ENDMETHOD.
METHOD single_reading_excluded.
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260110' ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ).
cl_abap_unit_assert=>assert_equals(
exp = `V1` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `VEH_ID` ) ).
cl_abap_unit_assert=>assert_equals(
exp = `200` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DISTANCE` ) ).
ENDMETHOD.
METHOD unknown_vehicle_excluded.
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260116' ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lines_of( lr_data ) ).
cl_abap_unit_assert=>assert_equals(
exp = `V3` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `VEH_ID` ) ).
cl_abap_unit_assert=>assert_equals(
exp = `V2` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `VEH_ID` ) ).
ENDMETHOD.
ENDCLASS.

View File

@@ -1,212 +0,0 @@
CLASS {{p}}t20_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
TYPES tt_region TYPE RANGE OF {{p}}farm-region.
METHODS run
IMPORTING iv_from TYPE d
iv_to TYPE d
it_region TYPE tt_region OPTIONAL
RETURNING VALUE(rr_data) TYPE REF TO data.
METHODS value_of
IMPORTING ir_data TYPE REF TO data
iv_row TYPE i
iv_col TYPE string
RETURNING VALUE(rv_value) TYPE string.
METHODS lines_of
IMPORTING ir_data TYPE REF TO data
RETURNING VALUE(rv_lines) TYPE i.
METHODS all_farms_listed FOR TESTING.
METHODS litres_summed FOR TESTING.
METHODS delivery_count FOR TESTING.
METHODS date_bounds FOR TESTING.
METHODS date_range_wide FOR TESTING.
METHODS region_filter FOR TESTING.
METHODS region_exclude FOR TESTING.
METHODS sorted_by_litres FOR TESTING.
METHODS tie_break_farm_id FOR TESTING.
ENDCLASS.
CLASS {{p}}t20_hidden IMPLEMENTATION.
METHOD run.
cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ).
SUBMIT {{p}}milk_sum WITH p_from = iv_from WITH p_to = iv_to
WITH s_reg IN it_region AND RETURN.
TRY.
cl_salv_bs_runtime_info=>get_data_ref( IMPORTING r_data = rr_data ).
CATCH cx_salv_bs_sc_runtime_info.
ENDTRY.
cl_salv_bs_runtime_info=>clear_all( ).
 ENDMETHOD.
METHOD lines_of.
FIELD-SYMBOLS <lt_data> TYPE STANDARD TABLE.
IF ir_data IS BOUND.
ASSIGN ir_data->* TO <lt_data>.
rv_lines = lines( <lt_data> ).
ENDIF.
ENDMETHOD.
METHOD value_of.
FIELD-SYMBOLS <lt_data> TYPE STANDARD TABLE.
ASSIGN ir_data->* TO <lt_data>.
ASSIGN <lt_data>[ iv_row ] TO FIELD-SYMBOL(<ls_line>).
ASSIGN COMPONENT iv_col OF STRUCTURE <ls_line> TO FIELD-SYMBOL(<lv_value>).
cl_abap_unit_assert=>assert_subrc( exp = 0 msg = |Column { iv_col } missing| ).
rv_value = condense( CONV string( <lv_value> ) ).
ENDMETHOD.
METHOD all_farms_listed.
cl_abap_unit_assert=>assert_equals(
exp = 5
act = lines_of( run( iv_from = '20260101' iv_to = '20260131' ) ) ).
cl_abap_unit_assert=>assert_equals(
exp = 2
act = lines_of( run( iv_from = '20260101' iv_to = '20260131'
it_region = VALUE #( ( sign = 'I' option = 'EQ' low = 'CH' ) ) ) ) ).
ENDMETHOD.
METHOD litres_summed.
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ).
cl_abap_unit_assert=>assert_equals( exp = `1000`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `200.50`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `LITRES` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1001`
act = value_of( ir_data = lr_data iv_row = 2 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `200.00`
act = value_of( ir_data = lr_data iv_row = 2 iv_col = `LITRES` ) ).
ENDMETHOD.
METHOD delivery_count.
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ).
cl_abap_unit_assert=>assert_equals( exp = `2`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DELIVERIES` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1`
act = value_of( ir_data = lr_data iv_row = 3 iv_col = `DELIVERIES` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1003`
act = value_of( ir_data = lr_data iv_row = 5 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `0`
act = value_of( ir_data = lr_data iv_row = 5 iv_col = `DELIVERIES` ) ).
cl_abap_unit_assert=>assert_equals( exp = `0.00`
act = value_of( ir_data = lr_data iv_row = 5 iv_col = `LITRES` ) ).
ENDMETHOD.
METHOD date_bounds.
DATA(lr_one) = run( iv_from = '20260105' iv_to = '20260105' ).
cl_abap_unit_assert=>assert_equals( exp = 5 act = lines_of( lr_one ) ).
cl_abap_unit_assert=>assert_equals( exp = `1000`
act = value_of( ir_data = lr_one iv_row = 1 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `120.50`
act = value_of( ir_data = lr_one iv_row = 1 iv_col = `LITRES` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1`
act = value_of( ir_data = lr_one iv_row = 1 iv_col = `DELIVERIES` ) ).
DATA(lr_two) = run( iv_from = '20260105' iv_to = '20260106' ).
cl_abap_unit_assert=>assert_equals( exp = 5 act = lines_of( lr_two ) ).
cl_abap_unit_assert=>assert_equals( exp = `1001`
act = value_of( ir_data = lr_two iv_row = 1 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `200.00`
act = value_of( ir_data = lr_two iv_row = 1 iv_col = `LITRES` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1000`
act = value_of( ir_data = lr_two iv_row = 2 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `120.50`
act = value_of( ir_data = lr_two iv_row = 2 iv_col = `LITRES` ) ).
ENDMETHOD.
METHOD date_range_wide.
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260228' ).
cl_abap_unit_assert=>assert_equals( exp = `1001`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `240.00`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `LITRES` ) ).
cl_abap_unit_assert=>assert_equals( exp = `2`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `DELIVERIES` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1000`
act = value_of( ir_data = lr_data iv_row = 2 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `200.50`
act = value_of( ir_data = lr_data iv_row = 2 iv_col = `LITRES` ) ).
cl_abap_unit_assert=>assert_equals( exp = `2`
act = value_of( ir_data = lr_data iv_row = 2 iv_col = `DELIVERIES` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1003`
act = value_of( ir_data = lr_data iv_row = 5 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `0.00`
act = value_of( ir_data = lr_data iv_row = 5 iv_col = `LITRES` ) ).
ENDMETHOD.
METHOD region_filter.
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131'
it_region = VALUE #( ( sign = 'I' option = 'EQ' low = 'CH' ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ).
cl_abap_unit_assert=>assert_equals( exp = `1000`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `200.50`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `LITRES` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1001`
act = value_of( ir_data = lr_data iv_row = 2 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `200.00`
act = value_of( ir_data = lr_data iv_row = 2 iv_col = `LITRES` ) ).
ENDMETHOD.
METHOD region_exclude.
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131'
it_region = VALUE #( ( sign = 'E' option = 'EQ' low = 'CH' ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lines_of( lr_data ) ).
cl_abap_unit_assert=>assert_equals( exp = `1002`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `50.25`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `LITRES` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1004`
act = value_of( ir_data = lr_data iv_row = 2 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `50.25`
act = value_of( ir_data = lr_data iv_row = 2 iv_col = `LITRES` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1003`
act = value_of( ir_data = lr_data iv_row = 3 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `0.00`
act = value_of( ir_data = lr_data iv_row = 3 iv_col = `LITRES` ) ).
ENDMETHOD.
METHOD sorted_by_litres.
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ).
cl_abap_unit_assert=>assert_equals( exp = `1000`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1001`
act = value_of( ir_data = lr_data iv_row = 2 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1002`
act = value_of( ir_data = lr_data iv_row = 3 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1004`
act = value_of( ir_data = lr_data iv_row = 4 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1003`
act = value_of( ir_data = lr_data iv_row = 5 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `Alpine Dairy`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `FARM_NAME` ) ).
cl_abap_unit_assert=>assert_equals( exp = `CH`
act = value_of( ir_data = lr_data iv_row = 1 iv_col = `REGION` ) ).
cl_abap_unit_assert=>assert_equals( exp = `Hilltop Farm`
act = value_of( ir_data = lr_data iv_row = 5 iv_col = `FARM_NAME` ) ).
cl_abap_unit_assert=>assert_equals( exp = `AT`
act = value_of( ir_data = lr_data iv_row = 5 iv_col = `REGION` ) ).
ENDMETHOD.
METHOD tie_break_farm_id.
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ).
cl_abap_unit_assert=>assert_equals( exp = `50.25`
act = value_of( ir_data = lr_data iv_row = 3 iv_col = `LITRES` ) ).
cl_abap_unit_assert=>assert_equals( exp = `50.25`
act = value_of( ir_data = lr_data iv_row = 4 iv_col = `LITRES` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1002`
act = value_of( ir_data = lr_data iv_row = 3 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1004`
act = value_of( ir_data = lr_data iv_row = 4 iv_col = `FARM_ID` ) ).
DATA(lr_zero) = run( iv_from = '20260105' iv_to = '20260105' ).
cl_abap_unit_assert=>assert_equals( exp = `1001`
act = value_of( ir_data = lr_zero iv_row = 2 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1002`
act = value_of( ir_data = lr_zero iv_row = 3 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1003`
act = value_of( ir_data = lr_zero iv_row = 4 iv_col = `FARM_ID` ) ).
cl_abap_unit_assert=>assert_equals( exp = `1004`
act = value_of( ir_data = lr_zero iv_row = 5 iv_col = `FARM_ID` ) ).
ENDMETHOD.
ENDCLASS.

View File

@@ -1,96 +1,81 @@
{
"task": "T20",
"task": "G0124",
"mutants": [
{
"object": "{{P}}MILK_SUM",
"mutant": "BETWEEN -> >= only (rel)",
"object": "{{P}}FLEET_DIST",
"mutant": "line 48: AND -> OR (logic)",
"status": "killed",
"hidden": "0/9",
"hidden": "2/8",
"failed_tests": [
"ALL_FARMS_LISTED",
"LITRES_SUMMED",
"DELIVERY_COUNT",
"DATE_BOUNDS",
"DATE_RANGE_WIDE",
"REGION_FILTER",
"REGION_EXCLUDE",
"SORTED_BY_LITRES",
"TIE_BREAK_FARM_ID"
"DATE_FILTER",
"DEPOT_FILTER",
"DISTANCE_AND_READINGS",
"SINGLE_READING_EXCLUDED",
"SORTED_BY_DISTANCE",
"UNKNOWN_VEHICLE_EXCLUDED"
]
},
{
"object": "{{P}}MILK_SUM",
"mutant": "BETWEEN -> <= only (rel)",
"object": "{{P}}FLEET_DIST",
"mutant": "line 49: AND -> OR (logic)",
"status": "killed",
"hidden": "0/9",
"hidden": "2/8",
"failed_tests": [
"LITRES_SUMMED",
"DATE_BOUNDS",
"DATE_RANGE_WIDE",
"SORTED_BY_LITRES",
"TIE_BREAK_FARM_ID"
"DATE_FILTER",
"DEPOT_FILTER",
"DISTANCE_AND_READINGS",
"SINGLE_READING_EXCLUDED",
"SORTED_BY_DISTANCE",
"UNKNOWN_VEHICLE_EXCLUDED"
]
},
{
"object": "{{P}}MILK_SUM",
"mutant": "region IN -> region NOT IN (logic)",
"object": "{{P}}FLEET_DIST",
"mutant": "line 51: >= -> < (rel)",
"status": "killed",
"hidden": "0/9",
"hidden": "0/8",
"failed_tests": [
"ALL_FARMS_LISTED",
"LITRES_SUMMED",
"DELIVERY_COUNT",
"DATE_BOUNDS",
"DATE_RANGE_WIDE",
"REGION_FILTER",
"REGION_EXCLUDE",
"SORTED_BY_LITRES",
"TIE_BREAK_FARM_ID"
"ALL_VEHICLES",
"DATE_FILTER",
"DEPOT_FILTER",
"DISTANCE_AND_READINGS",
"SINGLE_READING_EXCLUDED",
"SORTED_BY_DISTANCE",
"TIE_BREAK_BY_VEH_ID",
"UNKNOWN_VEHICLE_EXCLUDED"
]
},
{
"object": "{{P}}MILK_SUM",
"mutant": "farm without delivery skipped (inner join)",
"object": "{{P}}FLEET_DIST",
"mutant": "line 51: 2 -> 3 (const)",
"status": "killed",
"hidden": "0/9",
"hidden": "1/8",
"failed_tests": [
"ALL_FARMS_LISTED",
"DELIVERY_COUNT",
"DATE_BOUNDS",
"DATE_RANGE_WIDE",
"REGION_EXCLUDE",
"SORTED_BY_LITRES",
"TIE_BREAK_FARM_ID"
"ALL_VEHICLES",
"DEPOT_FILTER",
"DISTANCE_AND_READINGS",
"SINGLE_READING_EXCLUDED",
"SORTED_BY_DISTANCE",
"TIE_BREAK_BY_VEH_ID",
"UNKNOWN_VEHICLE_EXCLUDED"
]
},
{
"object": "{{P}}MILK_SUM",
"mutant": "SORT litres DESCENDING -> ASCENDING (rel)",
"object": "{{P}}FLEET_DIST",
"mutant": "line 56: - -> + (arith)",
"status": "killed",
"hidden": "0/9",
"hidden": "3/8",
"failed_tests": [
"LITRES_SUMMED",
"DATE_BOUNDS",
"DATE_RANGE_WIDE",
"REGION_FILTER",
"REGION_EXCLUDE",
"SORTED_BY_LITRES",
"TIE_BREAK_FARM_ID"
]
},
{
"object": "{{P}}MILK_SUM",
"mutant": "secondary sort farm_id removed (tie)",
"status": "killed",
"hidden": "0/9",
"failed_tests": [
"SORTED_BY_LITRES",
"TIE_BREAK_FARM_ID"
"DATE_FILTER",
"DEPOT_FILTER",
"DISTANCE_AND_READINGS",
"SINGLE_READING_EXCLUDED",
"TIE_BREAK_BY_VEH_ID"
]
}
],
"valid": 6,
"killed": 6,
"valid": 5,
"killed": 5,
"kill_rate": 1.0,
"ok": true
}

View File

@@ -0,0 +1,127 @@
REPORT {{p}}fleet_dist.
TYPES:
BEGIN OF ty_line,
veh_id TYPE {{p}}veh-veh_id,
depot TYPE {{p}}veh-depot,
distance TYPE i,
readings TYPE i,
END OF ty_line,
tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY,
tt_depot TYPE RANGE OF {{p}}veh-depot.
DATA gv_depot TYPE {{p}}veh-depot.
PARAMETERS p_from TYPE d OBLIGATORY.
PARAMETERS p_to TYPE d OBLIGATORY.
SELECT-OPTIONS s_depot FOR gv_depot.
CLASS lcl_report DEFINITION FINAL.
PUBLIC SECTION.
METHODS select_distances
IMPORTING iv_from TYPE d
iv_to TYPE d
it_depot TYPE tt_depot
RETURNING VALUE(rt_lines) TYPE tt_line.
METHODS display
CHANGING ct_lines TYPE tt_line.
PRIVATE SECTION.
TYPES:
BEGIN OF ty_raw,
veh_id TYPE {{p}}veh-veh_id,
depot TYPE {{p}}veh-depot,
max_odo TYPE {{p}}odom-odometer,
min_odo TYPE {{p}}odom-odometer,
readings TYPE i,
END OF ty_raw,
tt_raw TYPE STANDARD TABLE OF ty_raw WITH EMPTY KEY.
ENDCLASS.
CLASS lcl_report IMPLEMENTATION.
METHOD select_distances.
DATA lt_raw TYPE tt_raw.
SELECT v~veh_id, v~depot, MAX( o~odometer ) AS max_odo, MIN( o~odometer ) AS min_odo,
COUNT( * ) AS readings
FROM {{p}}veh AS v
INNER JOIN {{p}}odom AS o ON o~veh_id = v~veh_id
WHERE o~read_date >= @iv_from
AND o~read_date <= @iv_to
AND v~depot IN @it_depot
GROUP BY v~veh_id, v~depot
HAVING COUNT( * ) >= 2
INTO CORRESPONDING FIELDS OF TABLE @lt_raw.
LOOP AT lt_raw ASSIGNING FIELD-SYMBOL(<ls_raw>).
APPEND VALUE #( veh_id = <ls_raw>-veh_id
depot = <ls_raw>-depot
distance = <ls_raw>-max_odo - <ls_raw>-min_odo
readings = <ls_raw>-readings ) TO rt_lines.
ENDLOOP.
SORT rt_lines BY distance DESCENDING veh_id ASCENDING.
ENDMETHOD.
METHOD display.
TRY.
cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv)
CHANGING t_table = ct_lines ).
lo_alv->display( ).
CATCH cx_salv_msg INTO DATA(lx_error).
MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'.
ENDTRY.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA(go_report) = NEW lcl_report( ).
DATA(gt_lines) = go_report->select_distances( iv_from = p_from iv_to = p_to it_depot = s_depot[] ).
go_report->display( CHANGING ct_lines = gt_lines ).
CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
CLASS-DATA go_env TYPE REF TO if_osql_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS distances_per_vehicle FOR TESTING.
METHODS depot_filter FOR TESTING.
ENDCLASS.
CLASS ltc_report IMPLEMENTATION.
METHOD class_setup.
go_env = cl_osql_test_environment=>create(
i_dependency_list = VALUE #( ( '{{P}}VEH' ) ( '{{P}}ODOM' ) ) ).
DATA lt_veh TYPE STANDARD TABLE OF {{p}}veh WITH EMPTY KEY.
lt_veh = VALUE #( ( veh_id = 'V1' depot = 'D1' )
( veh_id = 'V2' depot = 'D2' )
( veh_id = 'V3' depot = 'D2' ) ).
go_env->insert_test_data( lt_veh ).
DATA lt_odom TYPE STANDARD TABLE OF {{p}}odom WITH EMPTY KEY.
lt_odom = VALUE #( ( read_id = 1 veh_id = 'V1' read_date = '20260101' odometer = 100 )
( read_id = 2 veh_id = 'V1' read_date = '20260110' odometer = 400 )
( read_id = 3 veh_id = 'V2' read_date = '20260105' odometer = 50 )
( read_id = 4 veh_id = 'V3' read_date = '20260102' odometer = 200 )
( read_id = 5 veh_id = 'V3' read_date = '20260112' odometer = 500 )
( read_id = 6 veh_id = 'V3' read_date = '20260122' odometer = 900 ) ).
go_env->insert_test_data( lt_odom ).
ENDMETHOD.
METHOD class_teardown.
go_env->destroy( ).
ENDMETHOD.
METHOD distances_per_vehicle.
DATA(lt_lines) = NEW lcl_report( )->select_distances(
iv_from = '20260101' iv_to = '20260131' it_depot = VALUE #( ) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ).
cl_abap_unit_assert=>assert_equals( exp = 'V3' act = lt_lines[ 1 ]-veh_id ).
cl_abap_unit_assert=>assert_equals( exp = 700 act = lt_lines[ 1 ]-distance ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lt_lines[ 1 ]-readings ).
cl_abap_unit_assert=>assert_equals( exp = 300 act = lt_lines[ 2 ]-distance ).
ENDMETHOD.
METHOD depot_filter.
DATA(lt_lines) = NEW lcl_report( )->select_distances(
iv_from = '20260101' iv_to = '20260131'
it_depot = VALUE #( ( sign = 'I' option = 'EQ' low = 'D2' ) ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ).
cl_abap_unit_assert=>assert_equals( exp = 'V3' act = lt_lines[ 1 ]-veh_id ).
ENDMETHOD.
ENDCLASS.

View File

@@ -1,170 +0,0 @@
REPORT {{p}}milk_sum.
DATA gv_region TYPE {{p}}farm-region.
PARAMETERS p_from TYPE d OBLIGATORY.
PARAMETERS p_to TYPE d OBLIGATORY.
SELECT-OPTIONS s_reg FOR gv_region.
CLASS lcl_report DEFINITION FINAL.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_line,
farm_id TYPE {{p}}farm-farm_id,
farm_name TYPE {{p}}farm-farm_name,
region TYPE {{p}}farm-region,
litres TYPE {{p}}milk-litres,
deliveries TYPE i,
END OF ty_line,
tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY,
tt_region TYPE RANGE OF {{p}}farm-region.
METHODS select_data
IMPORTING iv_from TYPE d
iv_to TYPE d
it_region TYPE tt_region
RETURNING VALUE(rt_lines) TYPE tt_line.
METHODS display
CHANGING ct_lines TYPE tt_line.
PRIVATE SECTION.
TYPES:
BEGIN OF ty_sum,
farm_id TYPE {{p}}milk-farm_id,
litres TYPE {{p}}milk-litres,
deliveries TYPE i,
END OF ty_sum,
tt_sum TYPE HASHED TABLE OF ty_sum WITH UNIQUE KEY farm_id.
METHODS sum_deliveries
IMPORTING iv_from TYPE d
iv_to TYPE d
RETURNING VALUE(rt_sum) TYPE tt_sum.
ENDCLASS.
CLASS lcl_report IMPLEMENTATION.
METHOD select_data.
SELECT farm_id, farm_name, region
FROM {{p}}farm
WHERE region IN @it_region
INTO TABLE @DATA(lt_farm).
DATA(lt_sum) = sum_deliveries( iv_from = iv_from iv_to = iv_to ).
LOOP AT lt_farm INTO DATA(ls_farm).
DATA(ls_line) = VALUE ty_line( farm_id = ls_farm-farm_id
farm_name = ls_farm-farm_name
region = ls_farm-region ).
READ TABLE lt_sum ASSIGNING FIELD-SYMBOL(<ls_sum>)
WITH TABLE KEY farm_id = ls_farm-farm_id.
IF sy-subrc = 0.
ls_line-litres = <ls_sum>-litres.
ls_line-deliveries = <ls_sum>-deliveries.
ENDIF.
APPEND ls_line TO rt_lines.
ENDLOOP.
SORT rt_lines BY litres DESCENDING farm_id ASCENDING.
ENDMETHOD.
METHOD sum_deliveries.
SELECT farm_id, litres
FROM {{p}}milk
WHERE deliv_date BETWEEN @iv_from AND @iv_to
INTO TABLE @DATA(lt_milk).
LOOP AT lt_milk INTO DATA(ls_milk).
READ TABLE rt_sum ASSIGNING FIELD-SYMBOL(<ls_sum>)
WITH TABLE KEY farm_id = ls_milk-farm_id.
IF sy-subrc <> 0.
DATA(ls_new) = VALUE ty_sum( farm_id = ls_milk-farm_id ).
INSERT ls_new INTO TABLE rt_sum ASSIGNING <ls_sum>.
ENDIF.
<ls_sum>-litres += ls_milk-litres.
<ls_sum>-deliveries += 1.
ENDLOOP.
ENDMETHOD.
METHOD display.
TRY.
cl_salv_table=>factory( IMPORTING r_salv_table = DATA(lo_alv)
CHANGING t_table = ct_lines ).
lo_alv->display( ).
CATCH cx_salv_msg INTO DATA(lx_error).
MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'.
ENDTRY.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA(go_report) = NEW lcl_report( ).
DATA(gt_lines) = go_report->select_data( iv_from = p_from iv_to = p_to it_region = s_reg[] ).
go_report->display( CHANGING ct_lines = gt_lines ).
CLASS ltc_report DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
CLASS-DATA go_env TYPE REF TO if_osql_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS sums_and_counts FOR TESTING.
METHODS missing_delivery FOR TESTING.
METHODS date_range FOR TESTING.
METHODS tie_break FOR TESTING.
ENDCLASS.
CLASS ltc_report IMPLEMENTATION.
METHOD class_setup.
go_env = cl_osql_test_environment=>create(
i_dependency_list = VALUE #( ( '{{P}}FARM' ) ( '{{P}}MILK' ) ) ).
DATA lt_farm TYPE STANDARD TABLE OF {{p}}farm WITH EMPTY KEY.
lt_farm = VALUE #( ( farm_id = '1' farm_name = 'A' region = 'X' )
( farm_id = '2' farm_name = 'B' region = 'X' )
( farm_id = '3' farm_name = 'C' region = 'Y' )
( farm_id = '4' farm_name = 'D' region = 'Y' )
( farm_id = '5' farm_name = 'E' region = 'X' ) ).
go_env->insert_test_data( lt_farm ).
DATA lt_milk TYPE STANDARD TABLE OF {{p}}milk WITH EMPTY KEY.
lt_milk = VALUE #( ( deliv_id = 1 farm_id = '1' deliv_date = '20260110' litres = 10 )
( deliv_id = 2 farm_id = '1' deliv_date = '20260120' litres = 5 )
( deliv_id = 3 farm_id = '2' deliv_date = '20260115' litres = 40 )
( deliv_id = 4 farm_id = '3' deliv_date = '20260116' litres = 20 )
( deliv_id = 5 farm_id = '5' deliv_date = '20260111' litres = 15 ) ).
go_env->insert_test_data( lt_milk ).
ENDMETHOD.
METHOD class_teardown.
go_env->destroy( ).
ENDMETHOD.
METHOD sums_and_counts.
DATA(lt_lines) = NEW lcl_report( )->select_data(
iv_from = '20260101' iv_to = '20260131' it_region = VALUE #( ) ).
cl_abap_unit_assert=>assert_equals( exp = 5 act = lines( lt_lines ) ).
cl_abap_unit_assert=>assert_equals( exp = '2' act = lt_lines[ 1 ]-farm_id ).
cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}milk-litres( 40 ) act = lt_lines[ 1 ]-litres ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_lines[ 1 ]-deliveries ).
ENDMETHOD.
METHOD missing_delivery.
DATA(lt_lines) = NEW lcl_report( )->select_data(
iv_from = '20260101' iv_to = '20260131' it_region = VALUE #( ) ).
cl_abap_unit_assert=>assert_equals( exp = '4' act = lt_lines[ 5 ]-farm_id ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 5 ]-deliveries ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_lines[ 5 ]-litres ).
ENDMETHOD.
METHOD date_range.
DATA(lt_lines) = NEW lcl_report( )->select_data(
iv_from = '20260115' iv_to = '20260131' it_region = VALUE #( ) ).
cl_abap_unit_assert=>assert_equals( exp = 5 act = lines( lt_lines ) ).
cl_abap_unit_assert=>assert_equals( exp = '2' act = lt_lines[ 1 ]-farm_id ).
cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}milk-litres( 40 ) act = lt_lines[ 1 ]-litres ).
cl_abap_unit_assert=>assert_equals( exp = '1' act = lt_lines[ 3 ]-farm_id ).
cl_abap_unit_assert=>assert_equals( exp = CONV {{p}}milk-litres( 5 ) act = lt_lines[ 3 ]-litres ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_lines[ 3 ]-deliveries ).
ENDMETHOD.
METHOD tie_break.
DATA(lt_lines) = NEW lcl_report( )->select_data(
iv_from = '20260101' iv_to = '20260131' it_region = VALUE #( ) ).
cl_abap_unit_assert=>assert_equals( exp = '1' act = lt_lines[ 3 ]-farm_id ).
cl_abap_unit_assert=>assert_equals( exp = '5' act = lt_lines[ 4 ]-farm_id ).
ENDMETHOD.
ENDCLASS.

View File

@@ -1,12 +0,0 @@
@EndUserText.label : 'Milk delivery'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table {{p}}milk {
key client : abap.clnt not null;
key deliv_id : abap.int4 not null;
farm_id : abap.char(10);
deliv_date : abap.dats;
litres : abap.dec(13,2);
}

View File

@@ -1,11 +1,12 @@
@EndUserText.label : 'Farm'
@EndUserText.label : 'Odometer readings'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table {{p}}farm {
define table {{p}}odom {
key client : abap.clnt not null;
key farm_id : abap.char(10) not null;
farm_name : abap.char(40);
region : abap.char(4);
key read_id : abap.int4 not null;
veh_id : abap.char(10);
read_date : abap.dats;
odometer : abap.int4;
}

View File

@@ -0,0 +1,33 @@
CLASS {{p}}seed_fleet DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS {{p}}seed_fleet IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DELETE FROM {{p}}odom.
DELETE FROM {{p}}veh.
INSERT {{p}}veh FROM TABLE @( VALUE #(
( veh_id = 'V1' depot = 'D1' veh_type = 'TRUCK' )
( veh_id = 'V2' depot = 'D1' veh_type = 'VAN' )
( veh_id = 'V3' depot = 'D2' veh_type = 'TRUCK' )
( veh_id = 'V4' depot = 'D2' veh_type = 'VAN' ) ) ).
INSERT {{p}}odom FROM TABLE @( VALUE #(
( read_id = 1 veh_id = 'V1' read_date = '20251220' odometer = 900 )
( read_id = 2 veh_id = 'V1' read_date = '20260101' odometer = 1000 )
( read_id = 3 veh_id = 'V1' read_date = '20260110' odometer = 1200 )
( read_id = 4 veh_id = 'V1' read_date = '20260120' odometer = 1500 )
( read_id = 5 veh_id = 'V2' read_date = '20260105' odometer = 500 )
( read_id = 6 veh_id = 'V2' read_date = '20260115' odometer = 700 )
( read_id = 7 veh_id = 'V3' read_date = '20260102' odometer = 5000 )
( read_id = 8 veh_id = 'V3' read_date = '20260112' odometer = 5300 )
( read_id = 9 veh_id = 'V3' read_date = '20260122' odometer = 5400 )
( read_id = 10 veh_id = 'V3' read_date = '20260125' odometer = 5600 )
( read_id = 11 veh_id = 'V4' read_date = '20260108' odometer = 300 )
( read_id = 12 veh_id = 'V9' read_date = '20260106' odometer = 100 )
( read_id = 13 veh_id = 'V9' read_date = '20260116' odometer = 400 )
( read_id = 14 veh_id = 'V9' read_date = '20260205' odometer = 700 )
( read_id = 15 veh_id = 'V9' read_date = '20260215' odometer = 1000 ) ) ).
out->write( sy-dbcnt ).
ENDMETHOD.
ENDCLASS.

View File

@@ -1,26 +0,0 @@
CLASS {{p}}t20_seed DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
ENDCLASS.
CLASS {{p}}t20_seed IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DELETE FROM {{p}}milk.
DELETE FROM {{p}}farm.
INSERT {{p}}farm FROM TABLE @( VALUE #(
( farm_id = '1000' farm_name = 'Alpine Dairy' region = 'CH' )
( farm_id = '1001' farm_name = 'Valley Farm' region = 'CH' )
( farm_id = '1002' farm_name = 'Lakeside Farm' region = 'DE' )
( farm_id = '1003' farm_name = 'Hilltop Farm' region = 'AT' )
( farm_id = '1004' farm_name = 'Sunny Acres' region = 'DE' ) ) ).
INSERT {{p}}milk FROM TABLE @( VALUE #(
( deliv_id = 1 farm_id = '1000' deliv_date = '20260105' litres = '120.50' )
( deliv_id = 2 farm_id = '1000' deliv_date = '20260112' litres = '80.00' )
( deliv_id = 3 farm_id = '1001' deliv_date = '20260106' litres = '200.00' )
( deliv_id = 4 farm_id = '1002' deliv_date = '20260107' litres = '50.25' )
( deliv_id = 5 farm_id = '1003' deliv_date = '20251220' litres = '300.00' )
( deliv_id = 6 farm_id = '1001' deliv_date = '20260215' litres = '40.00' )
( deliv_id = 7 farm_id = '1004' deliv_date = '20260108' litres = '50.25' ) ) ).
out->write( sy-dbcnt ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,11 @@
@EndUserText.label : 'Vehicle master'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table {{p}}veh {
key client : abap.clnt not null;
key veh_id : abap.char(10) not null;
depot : abap.char(4);
veh_type : abap.char(10);
}

View File

@@ -1,45 +1,51 @@
# 1. Goal
Show the milk deliveries of each farm in an ALV list. The cooperative uses the
list to compare the farms of a region for a period.
A facility management company operates a fleet of vehicles. The fleet manager
needs a report that shows the distance that each vehicle drove in a period.
The fleet manager uses the report to compare the vehicles.
# 2. Open questions
None.
# 3. Context
- Table {{P}}FARM exists in package $TMP. It contains data.
Fields: FARM_ID (CHAR 10, key), FARM_NAME (CHAR 40), REGION (CHAR 4).
- Table {{P}}MILK exists in package $TMP. It contains data.
Fields: DELIV_ID (INT4, key), FARM_ID (CHAR 10), DELIV_DATE (DATS),
LITRES (DEC 13,2).
The tables {{P}}VEH and {{P}}ODOM exist in package $TMP. Both tables contain
data.
Table {{P}}VEH holds the vehicles.
Fields: VEH_ID (CHAR 10, key), DEPOT (CHAR 4), VEH_TYPE (CHAR 10).
Table {{P}}ODOM holds the odometer readings of the vehicles.
Fields: READ_ID (INT4, key), VEH_ID (CHAR 10), READ_DATE (DATS),
ODOMETER (INT4, the odometer value in kilometres).
A vehicle can have many odometer readings. An odometer reading can belong to a
vehicle that is not in {{P}}VEH.
# 4. Contract
- Create the executable program {{P}}MILK_SUM in package $TMP.
- Create the executable program {{P}}FLEET_DIST in package $TMP.
- Selection screen:
- Parameter P_FROM, type DATS, mandatory: the first delivery date to include.
- Parameter P_TO, type DATS, mandatory: the last delivery date to include.
- Select-option S_REG for the region.
- Parameter P_FROM, type DATS, mandatory: the first reading date to include.
- Parameter P_TO, type DATS, mandatory: the last reading date to include.
- Select-option S_DEPOT for the depot.
- Show the result with CL_SALV_TABLE. The output table has these columns, with
these names: FARM_ID, FARM_NAME, REGION, LITRES, DELIVERIES.
these names: VEH_ID, DEPOT, DISTANCE, READINGS.
# 5. Business rules
1. Include a delivery only if DELIV_DATE is on or after P_FROM and on or before
P_TO. Both limits are included.
2. Include a farm only if REGION is in S_REG. An empty S_REG includes every
region. A line with sign E in S_REG excludes its region.
3. Show one line for each included farm. A farm without an included delivery also
gets a line.
4. LITRES is the sum of the litres of the included deliveries of the farm. LITRES
is 0 if the farm has no included delivery.
5. DELIVERIES is the number of included deliveries of the farm. DELIVERIES is 0
if the farm has no included delivery.
6. Sort the lines by LITRES, the highest value first. If two lines have the same
LITRES, sort them by FARM_ID, the lowest value first.
7. FARM_NAME and REGION are the values of the farm.
1. Include an odometer reading only if READ_DATE is on or after P_FROM and on
or before P_TO.
2. Include a vehicle only if the vehicle is in {{P}}VEH and its DEPOT is in
S_DEPOT.
3. Include a vehicle only if at least two of its readings are included.
4. DISTANCE is the highest included ODOMETER value of the vehicle minus the
lowest included ODOMETER value of the vehicle.
5. READINGS is the number of included readings of the vehicle.
6. Show one line for each included vehicle.
7. Sort the lines by DISTANCE. The highest distance comes first. If two lines
have the same DISTANCE, the line with the smaller VEH_ID comes first.
# 6. Constraints
- Release target: 7.58.
- Coding standards: Clean ABAP. Read each table with one database access.
- Out of scope: do not change {{P}}FARM and {{P}}MILK.
- Release target: 8.16.
- Coding standards: Clean ABAP.
- Out of scope: do not change {{P}}VEH and {{P}}ODOM.
# 7. Acceptance
- The program is active and has no syntax error.

View File

@@ -3,7 +3,7 @@
"category": "C",
"object_type": "PROG",
"difficulty": 2,
"release_target": "v758",
"release_target": "v816",
"expected_outcome": "implement",
"budget": {
"max_tool_calls": 60,
@@ -12,56 +12,57 @@
"seed": [
{
"type": "TABL",
"name": "{{P}}FARM",
"file": "seed/farm.tabl.asabap",
"description": "Farms of the cooperative"
"name": "{{P}}VEH",
"file": "seed/veh.tabl.asabap",
"description": "Vehicle master"
},
{
"type": "TABL",
"name": "{{P}}MILK",
"file": "seed/milk.tabl.asabap",
"description": "Milk deliveries"
"name": "{{P}}ODOM",
"file": "seed/odom.tabl.asabap",
"description": "Odometer readings"
},
{
"type": "CLAS",
"name": "{{P}}T20_SEED",
"file": "seed/t20_seed.clas.abap",
"description": "T20 seed data",
"name": "{{P}}SEED_FLEET",
"file": "seed/seed_fleet.clas.abap",
"description": "Fleet seed data",
"run": true
}
],
"contract": [
{
"type": "PROG",
"name": "{{P}}MILK_SUM",
"name": "{{P}}FLEET_DIST",
"parameters": [
"p_from",
"p_to",
"s_reg"
"s_depot"
]
}
],
"out_of_scope": [
"{{P}}FARM",
"{{P}}MILK"
"{{P}}VEH",
"{{P}}ODOM"
],
"hidden_tests": [
{
"type": "CLAS",
"name": "{{P}}T20_HIDDEN",
"file": "hidden/t20_hidden.clas.abap",
"description": "T20 hidden tests"
"name": "{{P}}FLEET_HIDDEN",
"file": "hidden/fleet_hidden.clas.abap",
"description": "Fleet distance hidden tests"
}
],
"reference": [
{
"type": "PROG",
"name": "{{P}}MILK_SUM",
"file": "reference/milk_sum.prog.abap",
"description": "Milk litres per farm"
"name": "{{P}}FLEET_DIST",
"file": "reference/fleet_dist.prog.abap",
"description": "Fleet distance per vehicle"
}
],
"craft_checks": [
"internal_table_key",
"db_operation_in_loop",
"method_length"
]

View File

@@ -0,0 +1,18 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 90.0,
"parts": {
"correctness": 40.0,
"craft": 20.0,
"clean": 15.0,
"own_tests": 15.0,
"discipline": 0.0,
"total": 90.0,
"note": "own_tests: faulty-reference part not implemented in skeleton"
},
"hidden": "8/8",
"tool_calls": 47,
"seconds": 949.8,
"run_dir": "runs/emp/11010_G0125_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,18 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 93.5,
"parts": {
"correctness": 40.0,
"craft": 20.0,
"clean": 13.5,
"own_tests": 15.0,
"discipline": 5.0,
"total": 93.5,
"note": "own_tests: faulty-reference part not implemented in skeleton"
},
"hidden": "11/11",
"tool_calls": 37,
"seconds": 244.9,
"run_dir": "runs/emp/11011_G0126_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,18 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 98.5,
"parts": {
"correctness": 40.0,
"craft": 20.0,
"clean": 13.5,
"own_tests": 15.0,
"discipline": 10.0,
"total": 98.5,
"note": "own_tests: faulty-reference part not implemented in skeleton"
},
"hidden": "12/12",
"tool_calls": 14,
"seconds": 103.2,
"run_dir": "runs/emp/10042_G0127_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,18 @@
{
"deepseek-v4.1-flash:cloud": {
"score": 100.0,
"parts": {
"correctness": 40.0,
"craft": 20.0,
"clean": 15.0,
"own_tests": 15.0,
"discipline": 10.0,
"total": 100.0,
"note": "own_tests: faulty-reference part not implemented in skeleton"
},
"hidden": "10/10",
"tool_calls": 29,
"seconds": 91.2,
"run_dir": "runs/emp/10043_G0128_llm_deepseek-v4.1-flash_cloud"
}
}

View File

@@ -0,0 +1,7 @@
{
"decision": "accept",
"note": "Screened: rules map to tests, mutation ok, no craft hints. H: real gap. K: names kept.",
"by": "Claude",
"date": "2026-10-03",
"failed_checks": {}
}

View File

@@ -0,0 +1,151 @@
CLASS {{p}}cold_chain_check DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES: BEGIN OF ty_reading,
offset_min TYPE i,
temp_tenths TYPE i,
END OF ty_reading,
tt_readings TYPE STANDARD TABLE OF ty_reading WITH EMPTY KEY,
BEGIN OF ty_config,
min_tenths TYPE i,
max_tenths TYPE i,
min_duration TYPE i,
max_gap TYPE i,
END OF ty_config.
METHODS constructor
IMPORTING is_config TYPE ty_config.
METHODS check
IMPORTING it_readings TYPE tt_readings
RETURNING VALUE(rv_mean) TYPE i
RAISING {{p}}cx_cold_chain.
PRIVATE SECTION.
DATA ms_config TYPE ty_config.
METHODS first_violation
IMPORTING it_readings TYPE tt_readings
RETURNING VALUE(rv_rule) TYPE string.
METHODS check_structure
IMPORTING it_readings TYPE tt_readings
RETURNING VALUE(rv_rule) TYPE string.
METHODS check_duration
IMPORTING it_readings TYPE tt_readings
RETURNING VALUE(rv_rule) TYPE string.
METHODS check_gap
IMPORTING it_readings TYPE tt_readings
RETURNING VALUE(rv_rule) TYPE string.
METHODS check_temperatures
IMPORTING it_readings TYPE tt_readings
RETURNING VALUE(rv_rule) TYPE string.
METHODS mean_tenths
IMPORTING it_readings TYPE tt_readings
RETURNING VALUE(rv_mean) TYPE i.
ENDCLASS.
CLASS {{p}}cold_chain_check IMPLEMENTATION.
METHOD constructor.
ms_config = is_config.
ENDMETHOD.
METHOD check.
DATA(lv_rule) = first_violation( it_readings ).
IF lv_rule IS NOT INITIAL.
RAISE EXCEPTION TYPE {{p}}cx_cold_chain
EXPORTING
iv_rule = lv_rule.
ENDIF.
rv_mean = mean_tenths( it_readings ).
ENDMETHOD.
METHOD first_violation.
rv_rule = check_structure( it_readings ).
IF rv_rule IS NOT INITIAL.
RETURN.
ENDIF.
rv_rule = check_duration( it_readings ).
IF rv_rule IS NOT INITIAL.
RETURN.
ENDIF.
rv_rule = check_gap( it_readings ).
IF rv_rule IS NOT INITIAL.
RETURN.
ENDIF.
rv_rule = check_temperatures( it_readings ).
ENDMETHOD.
METHOD check_structure.
IF lines( it_readings ) < 2.
rv_rule = 'TOO_FEW'.
RETURN.
ENDIF.
READ TABLE it_readings INDEX 1 INTO DATA(ls_first).
IF ls_first-offset_min = 0.
rv_rule = 'NO_START'.
RETURN.
ENDIF.
DATA(lv_previous) = ls_first-offset_min.
LOOP AT it_readings FROM 2 INTO DATA(ls_reading).
IF ls_reading-offset_min <= lv_previous.
rv_rule = 'TIME_ORDER'.
RETURN.
ENDIF.
lv_previous = ls_reading-offset_min.
ENDLOOP.
ENDMETHOD.
METHOD check_duration.
DATA lv_last TYPE i.
LOOP AT it_readings INTO DATA(ls_reading).
lv_last = ls_reading-offset_min.
ENDLOOP.
IF lv_last < ms_config-min_duration.
rv_rule = 'TOO_SHORT'.
ENDIF.
ENDMETHOD.
METHOD check_gap.
DATA lv_previous TYPE i.
LOOP AT it_readings INTO DATA(ls_reading).
IF sy-tabix > 1 AND ls_reading-offset_min - lv_previous > ms_config-max_gap.
rv_rule = 'GAP'.
RETURN.
ENDIF.
lv_previous = ls_reading-offset_min.
ENDLOOP.
ENDMETHOD.
METHOD check_temperatures.
LOOP AT it_readings INTO DATA(ls_reading).
IF ls_reading-temp_tenths > ms_config-max_tenths.
rv_rule = 'TOO_HIGH'.
RETURN.
ENDIF.
IF ls_reading-temp_tenths < ms_config-min_tenths.
rv_rule = 'TOO_LOW'.
RETURN.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD mean_tenths.
DATA lv_sum TYPE i.
LOOP AT it_readings INTO DATA(ls_reading).
lv_sum = lv_sum + ls_reading-temp_tenths.
ENDLOOP.
DATA(lv_count) = lines( it_readings ).
IF lv_count = 0.
rv_mean = 0.
RETURN.
ENDIF.
DATA(lv_abs_sum) = lv_sum.
IF lv_abs_sum < 0.
lv_abs_sum = -1 * lv_abs_sum.
ENDIF.
rv_mean = lv_abs_sum DIV lv_count.
IF lv_sum < 0.
rv_mean = -1 * rv_mean.
ENDIF.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,151 @@
CLASS {{p}}cold_chain_check DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES: BEGIN OF ty_reading,
offset_min TYPE i,
temp_tenths TYPE i,
END OF ty_reading,
tt_readings TYPE STANDARD TABLE OF ty_reading WITH EMPTY KEY,
BEGIN OF ty_config,
min_tenths TYPE i,
max_tenths TYPE i,
min_duration TYPE i,
max_gap TYPE i,
END OF ty_config.
METHODS constructor
IMPORTING is_config TYPE ty_config.
METHODS check
IMPORTING it_readings TYPE tt_readings
RETURNING VALUE(rv_mean) TYPE i
RAISING {{p}}cx_cold_chain.
PRIVATE SECTION.
DATA ms_config TYPE ty_config.
METHODS first_violation
IMPORTING it_readings TYPE tt_readings
RETURNING VALUE(rv_rule) TYPE string.
METHODS check_structure
IMPORTING it_readings TYPE tt_readings
RETURNING VALUE(rv_rule) TYPE string.
METHODS check_duration
IMPORTING it_readings TYPE tt_readings
RETURNING VALUE(rv_rule) TYPE string.
METHODS check_gap
IMPORTING it_readings TYPE tt_readings
RETURNING VALUE(rv_rule) TYPE string.
METHODS check_temperatures
IMPORTING it_readings TYPE tt_readings
RETURNING VALUE(rv_rule) TYPE string.
METHODS mean_tenths
IMPORTING it_readings TYPE tt_readings
RETURNING VALUE(rv_mean) TYPE i.
ENDCLASS.
CLASS {{p}}cold_chain_check IMPLEMENTATION.
METHOD constructor.
ms_config = is_config.
ENDMETHOD.
METHOD check.
DATA(lv_rule) = first_violation( it_readings ).
IF lv_rule IS NOT INITIAL.
RAISE EXCEPTION TYPE {{p}}cx_cold_chain
EXPORTING
iv_rule = lv_rule.
ENDIF.
rv_mean = mean_tenths( it_readings ).
ENDMETHOD.
METHOD first_violation.
rv_rule = check_structure( it_readings ).
IF rv_rule IS NOT INITIAL.
RETURN.
ENDIF.
rv_rule = check_duration( it_readings ).
IF rv_rule IS NOT INITIAL.
RETURN.
ENDIF.
rv_rule = check_gap( it_readings ).
IF rv_rule IS NOT INITIAL.
RETURN.
ENDIF.
rv_rule = check_temperatures( it_readings ).
ENDMETHOD.
METHOD check_structure.
IF lines( it_readings ) < 2.
rv_rule = 'TOO_FEW'.
RETURN.
ENDIF.
READ TABLE it_readings INDEX 1 INTO DATA(ls_first).
IF ls_first-offset_min <> 0.
rv_rule = 'NO_START'.
RETURN.
ENDIF.
DATA(lv_previous) = ls_first-offset_min.
LOOP AT it_readings FROM 3 INTO DATA(ls_reading).
IF ls_reading-offset_min <= lv_previous.
rv_rule = 'TIME_ORDER'.
RETURN.
ENDIF.
lv_previous = ls_reading-offset_min.
ENDLOOP.
ENDMETHOD.
METHOD check_duration.
DATA lv_last TYPE i.
LOOP AT it_readings INTO DATA(ls_reading).
lv_last = ls_reading-offset_min.
ENDLOOP.
IF lv_last < ms_config-min_duration.
rv_rule = 'TOO_SHORT'.
ENDIF.
ENDMETHOD.
METHOD check_gap.
DATA lv_previous TYPE i.
LOOP AT it_readings INTO DATA(ls_reading).
IF sy-tabix > 1 AND ls_reading-offset_min - lv_previous > ms_config-max_gap.
rv_rule = 'GAP'.
RETURN.
ENDIF.
lv_previous = ls_reading-offset_min.
ENDLOOP.
ENDMETHOD.
METHOD check_temperatures.
LOOP AT it_readings INTO DATA(ls_reading).
IF ls_reading-temp_tenths > ms_config-max_tenths.
rv_rule = 'TOO_HIGH'.
RETURN.
ENDIF.
IF ls_reading-temp_tenths < ms_config-min_tenths.
rv_rule = 'TOO_LOW'.
RETURN.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD mean_tenths.
DATA lv_sum TYPE i.
LOOP AT it_readings INTO DATA(ls_reading).
lv_sum = lv_sum + ls_reading-temp_tenths.
ENDLOOP.
DATA(lv_count) = lines( it_readings ).
IF lv_count = 0.
rv_mean = 0.
RETURN.
ENDIF.
DATA(lv_abs_sum) = lv_sum.
IF lv_abs_sum < 0.
lv_abs_sum = -1 * lv_abs_sum.
ENDIF.
rv_mean = lv_abs_sum DIV lv_count.
IF lv_sum < 0.
rv_mean = -1 * rv_mean.
ENDIF.
ENDMETHOD.
ENDCLASS.

Some files were not shown because too many files have changed in this diff Show More