Generator: pilot analysis and fixes; G0002-G0021 tasks; handover notes merged
- Static bundle checks (name length, seed type, reserved words, contract test classes, testclasses_file), local abaplint parser check before SAP, max_tokens, robust JSON parse - Runner: G2 for CDS views with parameters, contract_check detail in report - G0020 and G0013 fixed by review and revalidated (oracle 100, null 0) - Pilot results in docs/faz1-tasarim.md 11e; handover notes merged into CLAUDE.md and docs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
37
CLAUDE.md
37
CLAUDE.md
@@ -1,8 +1,15 @@
|
||||
# ABAP LLM harness — instructions for Claude Code
|
||||
|
||||
Talk to the user (Kral) in Turkish. Keep answers short; remove words that add no value.
|
||||
When you write English text (specs, prompts, docs for the model), use ASD-STE100 Simplified Technical English.
|
||||
Ask before an action that uses much cloud budget.
|
||||
## 0. Working with Kral
|
||||
|
||||
- Talk in Turkish. Short answers. Remove words and sentences that add no value.
|
||||
- English text (specs, prompts, docs for the model): ASD-STE100 Simplified Technical English.
|
||||
- Ask before an action that costs much (cloud budget) or that changes the A4H system outside harness runs.
|
||||
- For a series of updates to one document, deliver the result directly as a Markdown file; do not ask each time.
|
||||
- Record each decision with its date in `docs/yol-haritasi.md` or `docs/faz1-tasarim.md`.
|
||||
- Commit the current changes before a new generation or test batch.
|
||||
- Kral has no time for a full review. Eval review = automatic gates + empirical filter (2–3 models) +
|
||||
Claude review with a checklist; Kral reviews only flagged tasks + one per category (~10 tasks).
|
||||
|
||||
## 1. Project
|
||||
|
||||
@@ -12,6 +19,12 @@ Ask before an action that uses much cloud budget.
|
||||
- Contract of the model: "ABAP task as text + generic ABAP MCP interface".
|
||||
The plan format (plan-agent.md) gives the best result, but it is not mandatory.
|
||||
- Training data: never Claude output. Eval tasks never go into training data.
|
||||
- Input: the plan format gives the best result but is not mandatory. The old playbook format is not used.
|
||||
- Generic ABAP MCP interface (core: search_object, read_source, object_structure, where_used, create_object,
|
||||
write_source, activate, syntax_check, run_unit_tests, run_atc). Small tool name/schema variation in training.
|
||||
- Category K: incomplete or free-text input + other tool schema.
|
||||
- Object type mix: CLAS/INTF 35 %, FUNC 15 %, PROG 10 %, DDIC 10 %, CDS 25 %, MSAG + exception 5 %.
|
||||
- Teacher and task author: DeepSeek V4.1 Flash (Ollama cloud). Budget calendar in `docs/yol-haritasi.md`.
|
||||
- Read first: `docs/yol-haritasi.md` (roadmap, current position) and `docs/faz1-tasarim.md` (design).
|
||||
When a decision or a result changes, update these files.
|
||||
|
||||
@@ -76,21 +89,27 @@ python3 -c "from harness.ledger import spent; print(spent())"
|
||||
- Done: harness on Mac mini; test include creation in the server; teardown; FUNC/PROG/DDLS support;
|
||||
task generator; ledger and budget guard.
|
||||
- Results: T01 oracle 100, Qwen 27B 41.7 (old run 103), DeepSeek 98.5. T13 DeepSeek 85, T14 DeepSeek 100.
|
||||
- Running: pilot (`harness.pilot`, 20 tasks) → `runs/gen/pilot.json`, log `runs/gen/pilot.log`.
|
||||
A macOS notification shows when it ends.
|
||||
- First generated task G0001: not accepted after 3 attempts (activation error, seed save error,
|
||||
reference failed 1 hidden test). Feedback now includes the reference write/activation errors.
|
||||
- Pilot (G0002–G0021) done: 16/20 accepted, 0.16 USD per accepted task (`runs/gen/pilot.json`,
|
||||
analysis in `docs/faz1-tasarim.md` 11e). Generator improved after the pilot: static checks
|
||||
(name length, seed type, reserved words, contract/test classes), local abaplint parser check before SAP,
|
||||
max_tokens 100k, G2 detail in the repair feedback. Runner: G2 works for CDS views with parameters.
|
||||
The improved generator is not yet tested with new tasks.
|
||||
- G0020 (rejected) passes with oracle 100 after one fix (remove parameter `default`). G0013 (accepted) has a test
|
||||
class in its contract: fix in review.
|
||||
|
||||
## 7. Next steps
|
||||
|
||||
1. Analyze the pilot: acceptance rate, failure causes, cost per task. Improve the generator prompt.
|
||||
1. Test the improved generator with a small batch (~5 tasks, ~0.7 USD); compare repairs per task with the pilot (2.75 calls).
|
||||
2. Step D: mutation check (hidden tests must fail on a broken reference).
|
||||
3. Step F: eval set of 110 tasks: ~150 candidates, empirical filter (2–3 models), Claude review with a
|
||||
checklist, Kral spot-checks ~10 flagged tasks.
|
||||
4. Scoring for stop tasks (category H) and category K (free-text input, other tool schema).
|
||||
5. Step 1.3b: generic ABAP MCP interface (spec `abap-mcp-arayuz.md`, later). The proxy is the first adapter.
|
||||
|
||||
## 8. Open items for Kral (server)
|
||||
## 8. Open items for Kral (EPOD server)
|
||||
|
||||
- When a write fails, run a syntax check and return its messages. Now the server says only "save failed"
|
||||
(for example for `TYPE c LENGTH n` in a method signature). The model cannot see the cause and cannot repair;
|
||||
this hurts eval and training. Workaround in the generator: local abaplint parser check.
|
||||
- Concurrency: queue calls per RFC connection, or use a connection pool.
|
||||
- BDEF creation (needed for RAP tasks).
|
||||
|
||||
603
docs/faz1-tasarim.md
Normal file
603
docs/faz1-tasarim.md
Normal file
@@ -0,0 +1,603 @@
|
||||
# Faz 1 Tasarım — ABAP Danışman Modeli: Eval Seti, Görev Formatı, Harness
|
||||
|
||||
Durum: taslak v23 · 2026-10-02 · v23: Adım B — FM, rapor, CDS desteği; T13–T15; eşzamanlılık bulgusu.
|
||||
|
||||
## 1. Amaç
|
||||
|
||||
Faz 1 iki şey üretir:
|
||||
|
||||
1. **Eval seti:** A4H'da otomatik puanlanan ABAP görevleri. Hedef 100 görev; bu dokümanda 10 örnek.
|
||||
2. **Harness:** Bir modeli EPOD ADT MCP ile A4H'ya bağlar, görevi çalıştırır, puanlar, yörüngeyi kaydeder.
|
||||
|
||||
Aynı harness sonraki fazlarda eğitim verisi üretir: başarılı yörüngeler SFT verisi, puanlar RL ödülü olur.
|
||||
|
||||
## 2. Rol sınırı
|
||||
|
||||
**Modelin sözleşmesi (karar, 2026-10-02):** *Metin olarak ABAP görevi + genel ABAP MCP arayüzü.* Model belirli bir plan formatına veya belirli bir server'ın tool isimlerine bağlı değildir.
|
||||
|
||||
|
||||
`plan-agent.md` bugün implementer'ı "ABAP idiomlarını güvenilir hatırlamaz" diye tanımlıyor. Bu yüzden zanaat kararlarını planner veriyor. Yeni düzende bu kararlar modele geçer.
|
||||
|
||||
| Konu | Spec (fonksiyonel taraf / planner) | Model (ABAP danışmanı) |
|
||||
|---|---|---|
|
||||
| Goal, iş kuralları, alan eşlemesi | ✓ | |
|
||||
| Open questions | ✓ | Eksik görürse durur, sorar |
|
||||
| Context: obje adı, imza, DDIC | ✓ (kısmi olabilir) | Eksikse ADT ile okur |
|
||||
| Public kontrat (sınıf adı, public metot imzası, hata davranışı) | ✓ | Değiştirmez |
|
||||
| Zanaat kararları: tablo tipi, key, erişim yolu, SELECT stratejisi, exception tasarımı, release'e uygun syntax | | ✓ |
|
||||
| Private tasarım, yardımcı metotlar, testler | | ✓ |
|
||||
| Kod, aktivasyon, doğrulama döngüsü | | ✓ |
|
||||
|
||||
**Kritik kural:** Public kontrat spec'te sabittir. Gizli testler bu kontrata bağlanır. İç tasarım tamamen modelindir.
|
||||
|
||||
## 3. Spec formatı (modelin girdisi)
|
||||
|
||||
`plan-agent.md` yapısı temel alınır, dil STE100 kalır. Değişiklikler:
|
||||
|
||||
- **Decisions** bölümü kaldırılır. Sadece iş kararları kalır ("Business rules").
|
||||
- **Steps** iş seviyesine çekilir. Obje sırası ve zanaat detayı modele bırakılır.
|
||||
- **Contract** ayrı bölüm olur. Gizli testlerin bağlandığı yer burası.
|
||||
- **Constraints** eklenir. İçeriği:
|
||||
- Hedef release, paket, isim öneki.
|
||||
- Coding standards: projeye özgü, denetlenebilir kurallar.
|
||||
- Out of scope: dokunulmayacak objeler ve eklenmeyecek davranışlar.
|
||||
|
||||
Bölümler, bu sırayla:
|
||||
|
||||
1. Goal
|
||||
2. Open questions (pozitif görevlerde boş)
|
||||
3. Context
|
||||
4. Contract
|
||||
5. Business rules
|
||||
6. Constraints
|
||||
7. Acceptance
|
||||
|
||||
**Plan formatının rolü (karar, 2026-10-02):** `plan-agent.md` formatı zorunlu girdi değil, **en iyi sonuç veren** girdidir. Model serbest metin ve eksik görev tanımıyla da çalışabilmelidir. Görevlerin çoğu bu formatta yazılır; kategori K (bölüm 8) serbest ve eksik girdiyi ölçer. Kral'ın kendi akışında (`/sapplan` → build) plan kullanımı değişmez.
|
||||
|
||||
**Coding standards örneği** (her görevde değişebilir):
|
||||
|
||||
- No comment restates what the code does.
|
||||
- No Given/When/Then comment header in a test.
|
||||
- Method length stays below 40 statements.
|
||||
- No new global variable.
|
||||
- No method chaining (release kısıtı olan görevlerde).
|
||||
- Use text elements for user-facing text.
|
||||
|
||||
Bu kuralların çoğu abaplint ile denetlenir (`method_length`, `global_class`, syntax version). Kalanlar özel AST kontrolüdür.
|
||||
|
||||
## 4. Model çıktısı
|
||||
|
||||
Model şunları üretir:
|
||||
|
||||
1. **Decisions bloğu** (STE100): `plan-agent.md` Decisions listesindeki maddeler. Release, tablo tipi ve key, erişim yolu, loop dışı SELECT, exception sınıfı, yeni objelerin adı ve paketi. Her biri tek satır gerekçeyle.
|
||||
2. **Objeler:** A4H'da oluşturulmuş ve aktif.
|
||||
3. **Kendi testleri:** ABAP Unit, modelin kendi yazdığı.
|
||||
4. **ATC kararları:** Her P1/P2 bulgusu ya düzeltilir ya da tek satır gerekçeyle bırakılır. False positive belgelenir.
|
||||
5. **Rapor** (STE100): `build-agent.md` Reporting kuralı. Her adım, doğrulama sonucu, sapmalar.
|
||||
6. **Change summary tablosu:** Object · Type · Action · Reason. Throwaway objeler (`$TMP` smoke sınıfı vb.) ayrı işaretlenir.
|
||||
|
||||
Davranış kuralları `build-agent.md`'den uyarlanır:
|
||||
|
||||
- Public kontratı değiştirme. Değiştirmen gerekiyorsa dur ve raporla.
|
||||
- İş kuralı eksik veya çelişkiliyse dur ve sor. Tahmin etme.
|
||||
- Zanaat kararı spec'te yoksa **kendin ver**. (Eski kural: "dur ve sor". Bu kural tersine döner.)
|
||||
- Spec'te istenmeyen davranış ekleme.
|
||||
- Constraints altındaki out of scope listesine dokunma.
|
||||
- Aynı doğrulama iki kez başarısız olursa dur ve raporla. Üçüncü deneme yok.
|
||||
- ABAP Unit'in kapsayamadığı bir davranışı (ör. factory'nin runtime çözümü) `$TMP` smoke objesiyle doğrula. Smoke objesi teslimata girmez.
|
||||
- Testler gerçek veritabanını okumaz. DB erişimi `CL_OSQL_TEST_ENVIRONMENT` veya `CL_CDS_TEST_ENVIRONMENT` ile izole edilir.
|
||||
|
||||
## 5. Ortam
|
||||
|
||||
**Sistem:** A4H, Mac mini üzerinde Docker.
|
||||
|
||||
**Sistem kimliği:** MCP server'da tek sistem bağlı: `epodabap`, mod `write`. SAP_BASIS 816 SP01. Modern syntax'ın tamamı mevcut; eski release hedefleri sadece abaplint ile denetlenir.
|
||||
|
||||
**İzolasyon:** MCP server paket (DEVC) oluşturamaz. Bu yüzden her koşu `$TMP` içinde çalışır, objeler koşuya özel önekle adlanır. Önek şeması: `Z<run 3 hane><task 3 hane>_` (ör. `Z007003_DISC_CALC`). Önek her koşuda yenidir; eski koşunun objeleriyle çakışma olmaz. Spec ve gizli testler isimleri yer tutucuyla taşır (`{{P}}DISCOUNT_CALC`); harness koşu başında doldurur. 30 karakter sınırı görev yazımında kontrol edilir.
|
||||
|
||||
**Seed verisi:** MCP server'da tablo yazma tool'u yok (`sap_sql_query` sadece okur). Harness, seed verisini `IF_OO_ADT_CLASSRUN` uygulayan bir seed sınıfı ile yükler ve `sap_run_class` ile çalıştırır.
|
||||
|
||||
**Teardown:** Harness, koşu sonunda objeleri ADT'nin kendi silme API'si ile siler. MCP server değişmez, model bu yolu görmez.
|
||||
|
||||
```
|
||||
POST /sap/bc/adt/deletion/delete
|
||||
Accept: application/vnd.sap.adt.deletion.response.v1+xml
|
||||
Content-Type: application/vnd.sap.adt.deletion.request.v1+xml
|
||||
|
||||
<del:deletionRequest xmlns:del="http://www.sap.com/adt/deletion"
|
||||
xmlns:adtcore="http://www.sap.com/adt/core">
|
||||
<del:object adtcore:uri="/sap/bc/adt/ddic/ddl/sources/zz_sample_client">
|
||||
<del:transportNumber/>
|
||||
</del:object>
|
||||
</del:deletionRequest>
|
||||
```
|
||||
|
||||
- Yanıt her obje için `del:isDeleted="true|false"` ve mesaj döner. DDIC objelerinde silme log linki gelir.
|
||||
- Obje URI'leri `sap_search_object` sonucundaki `uri` alanından alınır (önekle arama).
|
||||
- `$TMP` objelerinde `transportNumber` boş kalır.
|
||||
- Harness, CSRF token'ı (`x-csrf-token: fetch`) ve oturum cookie'sini yönetir.
|
||||
|
||||
**Doğrulandı (2026-10-02, Mac mini):** 3 bağımlı obje (interface, onu implemente eden sınıf, test sınıfı) tek istekte silindi. Toplam 20 objeden 19'u silindi. Kalan 1 obje (`Z002001_IBAN_VALIDATOR`) "You are already editing" ile reddedildi: başka bir oturumda (muhtemelen MacBook'taki ADT) açık kilit. Harness bunu `deleted: false` olarak raporluyor; kilit kalkınca `teardown` tekrar çalıştırılır.
|
||||
|
||||
Önceki doğrulama listesi (tarihsel):
|
||||
- Bir istekte birden çok `del:object` kabul ediliyor mu?
|
||||
- Bağımlı objeler (ör. sınıf + kullandığı tablo tipi) aynı istekte doğru sırayla siliniyor mu? Değilse harness sırayı verir: test sınıfı → sınıf → interface → CDS → tablo tipi → yapı/tablo → data element → domain → mesaj sınıfı.
|
||||
- `isDeleted="false"` dönen obje: harness loglar, koşuyu "kirli" işaretler, sonraki koşu yine benzersiz önekle başlar.
|
||||
|
||||
**Sızıntı koruması** (silme başarısız olursa diye, ek güvenlik): Her koşunun öneki benzersizdir. Harness, modelin `sap_search_object` ve `sap_usage_references` sonuçlarından başka koşuların öneklerini filtreler.
|
||||
|
||||
**Paralellik (test edildi, 2026-10-01):** MCP server en fazla **8 eşzamanlı oturum** tutuyor. Bu sınır bilinçli: A4H ve ADT'nin taşıyabileceği yük kadar. 9. oturum `initialize`'da ID alıyor ama ilk çağrıda HTTP 404 dönüyor. 8 paralel syntax check 1–2 saniyede bitti. Toplam kapasite 8 oturumla sınırlı; Adım 3 ve 5 (binlerce koşu) için süre hesabı buna göre yapılır. (Karşılaştırma: normal geliştirme sistemlerinde 12 agent aynı anda çalışabiliyor; kısıt A4H'nın kaynakları.)
|
||||
|
||||
**Makine dağılımı (v13: her şey Mac mini'de):**
|
||||
|
||||
Yük testi, 2026-10-02, Mac mini M4 Pro 64 GB, A4H çalışırken Qwen 3.8 27B (MLX, nvfp4), `num_ctx` 32k:
|
||||
|
||||
| Ölçüm | Değer |
|
||||
|---|---|
|
||||
| A4H bellek (üretim sırasında) | 28,9 GB (Docker limiti 62,7 GB) |
|
||||
| Sistem boş bellek / swap | %49 / 0,5 GB — baskı yok |
|
||||
| A4H tepkisi | Etkilenmedi |
|
||||
| Prompt eval | 119 token/s |
|
||||
| Üretim (eval) | 31 token/s |
|
||||
| Test prompt'u (T01 benzeri, tek sınıf + testler) | 11 678 token, 6 dk 18 sn |
|
||||
|
||||
Sonuç: A4H, MCP server, harness ve model Mac mini'de birlikte çalışır. MacBook serbest kalır.
|
||||
|
||||
- 11 678 token'ın çoğu muhtemelen düşünme (thinking) çıktısı. Ölçümde iki mod da denenir: thinking açık ve kapalı. Süre ve puan farkı baz model seçimine girer.
|
||||
- Kaba süre tahmini: görev başına 15 tur × ~2 000 token ≈ 16 dk üretim + tool süresi → 20–30 dk/görev. 100 görev ≈ 35–50 saat/model. Mac mini 7/24 koşarsa model başına ~2 gün.
|
||||
- Opsiyonel koruma: Docker Desktop bellek limitini ~34 GB'a indirmek; HANA büyüyüp modelin alanına girmesin.
|
||||
- Adım 3'te kapasite yetmezse: ikinci A4H (kiralık VM) veya modeli kiralık GPU'da koşturmak.
|
||||
|
||||
Harness kuralları:
|
||||
- Yerel model (MacBook): **tek worker**, paralel istek yok. Paralel worker (en fazla 6, 2 oturum senin kullanımın için) sadece model uzakta çalışıyorsa (kiralık GPU).
|
||||
- Her worker koşu boyunca tek oturum kullanır, koşu sonunda oturumu `DELETE` ile kapatır.
|
||||
- 404 alan çağrı: oturumu yeniden aç, çağrıyı bir kez tekrarla, koşuyu "kirli" işaretle.
|
||||
|
||||
Opsiyonel server iyileştirmesi: sınır aşıldığında `initialize` aşamasında açık hata (429/503) dönmek. Böylece harness oturumu almadan bekler. Paralel **yazma ve aktivasyon** henüz test edilmedi; Adım 1.3'te ölçülecek.
|
||||
|
||||
**Genel ABAP MCP arayüzü (karar, 2026-10-02).** Model EPOD tool isimlerini değil, genel bir "ABAP MCP" arayüzünü görür. Ayrıntılı spec: `abap-mcp-arayuz.md` (sonra yazılacak).
|
||||
|
||||
| Genel tool | EPOD karşılığı (ilk adapter) |
|
||||
|---|---|
|
||||
| `search_object` | `sap_search_object` |
|
||||
| `read_source` | `sap_pull_source` |
|
||||
| `object_structure` | `sap_object_structure`, `sap_object_members` |
|
||||
| `where_used` | `sap_usage_references` |
|
||||
| `create_object` | `sap_create_object` |
|
||||
| `write_source` | `sap_push_source`, `sap_push_element` |
|
||||
| `activate` | `sap_activate` |
|
||||
| `syntax_check` | `sap_syntax_check` |
|
||||
| `run_unit_tests` | `sap_run_unit_test`, `sap_check_object` (coverage) |
|
||||
| `run_atc` | `sap_atc_run` |
|
||||
|
||||
- EPOD bu arayüzü native destekleyecek. O zamana kadar harness proxy'si genel arayüzü EPOD tool'larına çeviren **ilk adapter**dir.
|
||||
- **Delete/teardown arayüzde yok.** Harness'e aittir (ADT deletion API).
|
||||
- Çekirdek dışındaki EPOD tool'ları (`sap_sql_query`, `sap_run_class`, `sap_short_dumps`, `sap_element_info`, `sap_inactive_objects`, `sap_pretty_print`, `sap_push_message`) için genel karşılık spec'te kararlaştırılacak.
|
||||
- **Eğitimde az miktarda tool isim ve şema varyasyonu** kullanılır (ör. `read_source` / `get_source`, parametre adlarında farklılık). Amaç: model belirli bir isim setine ezber yapmasın.
|
||||
|
||||
**Tool seti (server v2.3.0, 33 tool).** Model sadece aşağıdaki beyaz listeyi görür. Server tool'larında `readOnlyHint` anotasyonu yok; bu yüzden ayrım harness'te beyaz listeyle yapılır.
|
||||
|
||||
| Grup | Model görür | Model görmez |
|
||||
|---|---|---|
|
||||
| Okuma | `sap_search_object`, `sap_pull_source`, `sap_object_structure`, `sap_object_members`, `sap_usage_references`, `sap_element_info`, `sap_inactive_objects`, `sap_short_dumps`, `sap_sql_query` | `sap_adt_raw_get`, `sap_list_systems`, `sap_compare_systems`, `sap_object_versions`, `sap_object_diff`, `sap_pull_source_to_file` |
|
||||
| Yazma | `sap_create_object`, `sap_push_source`, `sap_push_element`, `sap_push_message`, `sap_activate` | `sap_push_source_from_file`, `sap_lock`, `sap_unlock` |
|
||||
| Kalite | `sap_syntax_check`, `sap_check_object` (`runUnitTest`, `coverage`), `sap_run_unit_test`, `sap_atc_run`, `sap_pretty_print` | |
|
||||
| Çalıştırma | `sap_run_class` (`$TMP` smoke için) | |
|
||||
| Transport | | `sap_transport_list`, `sap_transport_of_object`, `sap_transport_create`, `sap_transport_release` |
|
||||
|
||||
Notlar:
|
||||
- `sap_create_object` 16 tip oluşturur: PROG, CLAS, INTF, FUGR, FUNC, DTEL, DOMA, TABL, TTYP, STRU, DDLS, DDLX, SRVD, DCLS, DDLA, MSAG. BDEF oluşturamaz → kategori J (RAP) için server'a BDEF create eklenmeli.
|
||||
- `sap_check_object` P1/P2 ATC bulgusunda başarısız döner; G5 bununla ölçülür. `coverage=true` sadece global sınıfı ölçer.
|
||||
- `sap_push_source` varsayılan olarak aktive eder. Birden çok objeli değişiklikte `activate=false` + `sap_activate` sırası modelin öğrenmesi gereken bir beceri.
|
||||
|
||||
**Aktivasyon kontrolü:** Planlarda geçen "MCP activation wedge" nedeniyle harness, her push sonrası aktif versiyonu ayrıca okur. Modelin raporu değil, aktif kaynak puanlanır.
|
||||
|
||||
## 6. Puanlama
|
||||
|
||||
### 6.1 Kapılar (0/1)
|
||||
|
||||
Bir kapı geçilmezse görev puanı 0'dır.
|
||||
|
||||
- G1: Kontrattaki tüm objeler var ve aktif.
|
||||
- G2: Kontrattaki public imzalar spec ile birebir aynı.
|
||||
- G3: Gizli testlerin derlenmesi ve en az bir testin geçmesi.
|
||||
- G4: Out of scope listesindeki hiçbir obje değişmemiş. Harness, seed objelerinin aktif kaynağını koşu öncesi ve sonrası karşılaştırır.
|
||||
- G5: Açık P1 ATC bulgusu yok.
|
||||
|
||||
### 6.2 Bileşenler (kapılar geçildiyse)
|
||||
|
||||
| Bileşen | Ağırlık | Ölçüm |
|
||||
|---|---|---|
|
||||
| Doğruluk | 40 | Gizli ABAP Unit testlerinin geçme oranı |
|
||||
| Zanaat | 20 | Görevin `craft_checks` listesi (abaplint kuralları + özel AST kontrolleri) |
|
||||
| Clean ABAP + Coding standards | 15 | abaplint Clean ABAP kuralları + görevin coding standards kontrolleri |
|
||||
| Modelin testleri | 15 | (a) Hatalı referans implementasyonu yakalama, (b) statement/branch coverage (hedef görevde verilir, ör. %70), (c) gerçek DB erişimi yok |
|
||||
| Disiplin | 10 | Gerekçesiz P2 bulgusu yok; iki başarısızlık sonrası durma kuralına uyma; change summary eksiksiz ve doğru; tool çağrısı bütçesi |
|
||||
|
||||
Ağırlıklar başlangıç değeri. İlk baz model ölçümünden sonra ayarlanır.
|
||||
|
||||
### 6.3 Negatif görevler
|
||||
|
||||
Beklenen sonuç `stop` olan görevlerde:
|
||||
|
||||
- Model obje oluşturmadan durur ve eksik noktayı adlandırır: 100.
|
||||
- Model durur ama yanlış noktayı adlandırır: 30.
|
||||
- Model uygular: 0.
|
||||
|
||||
"Doğru noktayı adlandırdı mı" kontrolü: görevde `expected_gap` anahtar kelimeleri + hakem model. Hakem modeli sadece bu kontrol için kullanılır.
|
||||
|
||||
### 6.4 Release hedefi
|
||||
|
||||
A4H en yeni release'te. Ama gerçek sistemler eski olabilir: senin planlarında "this system does not support method chaining" kısıtı var. Bu yüzden her görev bir `release_target` taşır. abaplint `syntax.version` ayarı (ör. `v702`, `v740sp05`, `v750`) bu hedefi A4H'dan bağımsız denetler. Hedef release'i aşan syntax G1'i düşürür.
|
||||
|
||||
### 6.5 abaplint ve standart tipler (test edildi, 2026-10-01)
|
||||
|
||||
Test: abaplint 2.120.63, abapGit formatında seed objeleri (yapı, tablo tipi, şeffaf tablo) + bunları kullanan bir sınıf.
|
||||
|
||||
| Kontrol | Sonuç |
|
||||
|---|---|
|
||||
| Seed tiplerini çözme (alan seviyesine kadar) | ✓ `ls_order-amountx` → "Component not found" |
|
||||
| Eksik Z tipi | ✓ `ZEVAL_TT_MISSING` → `unknown_types` |
|
||||
| Standart tipler (`TIMESTAMPL`, `SYUNAME`, `WAERS`, `CL_ABAP_TSTMP`) | Yanlış alarm yok (`errorNamespace: ^(Z|Y|LCL_|TY_|LIF_)`) |
|
||||
| Standart sınıfta yanlış parametre adı | ✗ Yakalanmıyor (beklenen) |
|
||||
| `db_operation_in_loop` | ✓ |
|
||||
| Release hedefi v702: inline `DATA(...)` | ✓ Hata |
|
||||
|
||||
Sonuç ve iş bölümü:
|
||||
- **A4H syntax check / aktivasyon = doğruluk.** Standart API kullanımı burada denetlenir.
|
||||
- **abaplint = release hedefi, zanaat, stil.** Standart tipleri bilmesi gerekmez.
|
||||
- Seed objeleri abapGit formatında tutulur; abaplint onları doğrudan okur. Aynı dosyalar A4H'ya MCP ile kurulur.
|
||||
- Not: v702 hatalarında zincirleme (cascade) hatalar oluşuyor. Release kapısı için hata sayısı değil, "en az bir `check_syntax` hatası var mı" ölçülür.
|
||||
|
||||
## 7. Görev şeması
|
||||
|
||||
```yaml
|
||||
id: T03
|
||||
version: 1
|
||||
category: C # bkz. bölüm 8
|
||||
difficulty: 2 # 1-3
|
||||
release_target: v750
|
||||
expected_outcome: implement # implement | stop
|
||||
budget:
|
||||
max_tool_calls: 60
|
||||
max_activations: 15
|
||||
spec: spec.md # bölüm 3 formatında, STE100
|
||||
seed: # görev başında A4H'ya kurulur
|
||||
objects: seed/ # abapGit formatında DDIC, sınıflar
|
||||
data: seed/data.json # tablo içerikleri
|
||||
faulty_reference: seed/ref_faulty/ # modelin testlerini ölçmek için
|
||||
hidden_tests: hidden/ # global test sınıfı, kontrata bağlı
|
||||
craft_checks:
|
||||
- abaplint: db_operation_in_loop
|
||||
- custom: lookup_table_is_sorted_or_hashed
|
||||
expected_gap: [] # sadece stop görevlerinde
|
||||
```
|
||||
|
||||
Gizli testler global test sınıfıdır (`FOR TESTING`). Modelin sınıfına sadece public kontrat üzerinden erişir. Bu yüzden G2 (imza eşleşmesi) kapıdır.
|
||||
|
||||
## 8. Kategoriler
|
||||
|
||||
| Kod | Kategori | Ölçülen beceri |
|
||||
|---|---|---|
|
||||
| A | Saf mantık, yeni sınıf | Dil, OO, Clean ABAP |
|
||||
| B | Veritabanı erişimi + test double | Open SQL, CDS, `CL_OSQL_TEST_ENVIRONMENT` |
|
||||
| C | Internal table zanaatı | Tablo tipi, key, erişim yolu |
|
||||
| D | Exception tasarımı | Class-based exception, T100 mesajları |
|
||||
| E | Refactoring | Legacy kodu davranışı koruyarak temizleme |
|
||||
| F | Bilinmeyen obje | Spec'te imzası olmayan seed objesini ADT ile okuyup kullanma |
|
||||
| G | Release kısıtı | Eski release'e uygun syntax |
|
||||
| H | Negatif | Eksik veya çelişkili spec'te durma |
|
||||
| I | Hata düzeltme | Kırmızı test veya short dump'tan kök nedene |
|
||||
| J | CDS / RAP | View entity, behavior (sonraki dalga) |
|
||||
| K | Eksik / serbest girdi + farklı tool şeması | Plan formatı olmadan, serbest metin veya eksik görev tanımıyla çalışma; farklı tool isim ve şemalarına uyum. Eksik bilgi kritikse durup sorma (H ile ilişkili) |
|
||||
|
||||
Hedef dağılım (beceri kategorisine göre): A 10, B 15, C 15, D 10, E 15, F 10, G 10, H 10, I 5, **K 10** (toplam 110). J ikinci dalgada. K görevleri diğer kategorilerin görevlerinin serbest metin veya farklı şemalı varyantları olarak üretilir.
|
||||
|
||||
### 8.1 İkinci boyut: obje tipi
|
||||
|
||||
Beceri kategorisi tek başına yetmez. T01–T12'nin hepsi sınıf; gerçek işte function module, rapor ve DDIC de var. Her görev bir beceri kategorisi **ve** bir ana obje tipi taşır.
|
||||
|
||||
| Obje tipi | Hedef pay | MCP ile mümkün mü | Gizli test yöntemi |
|
||||
|---|---|---|---|
|
||||
| CLAS / INTF | ~35 % | ✓ | Global test sınıfı, public kontrat üzerinden |
|
||||
| FUGR / FUNC | ~15 % | ✓ (`sap_create_object` FUGR + FUNC) | Global test sınıfı `CALL FUNCTION` ile çağırır; exception'lar `EXCEPTIONS` ile kontrol edilir |
|
||||
| PROG (rapor, selection screen, ALV) | ~10 % | ✓ | `SUBMIT ... WITH ... AND RETURN` + `cl_salv_bs_runtime_info=>set( display = abap_false data = abap_true )` ile ALV verisi yakalanır; WRITE listeleri `EXPORTING LIST TO MEMORY` + `LIST_FROM_MEMORY` ile |
|
||||
| DDIC (TABL, STRU, DTEL, DOMA, TTYP) | ~10 % | ✓ | Yapı kontrolü: `sap_object_structure` / `DD03L`, `DD04L`, `DD01L` sorguları; kullanan kodun derlenmesi |
|
||||
| CDS (DDLS, DCLS, DDLX, SRVD) | ~25 % | ✓ (BDEF hariç) | `CL_CDS_TEST_ENVIRONMENT` ile test double; sonuç kümesi karşılaştırması |
|
||||
| MSAG + exception | ~5 % | ✓ (`sap_push_message`) | T100 mesaj metni ve parametre kontrolü |
|
||||
|
||||
**CDS alt konuları** (25 görev civarı): view entity ve join'ler, association ve path expression, aggregation ve `GROUP BY`, `CASE` / cast / built-in fonksiyonlar, parametreli view, access control (DCLS), annotation ve metadata extension (DDLX), table function + AMDP sınıfı, service definition (SRVD), ABAP'tan CDS tüketimi (Open SQL ile association kullanımı). RAP (BDEF) server'a BDEF oluşturma eklenince J kategorisinde.
|
||||
|
||||
Kapsam dışı (ADT/MCP ile yapılamıyor): dynpro (`CALL SCREEN`), SmartForms, BAdI/enhancement implementasyonu, IDoc tanımı. Gerçek işte var ama A4H + MCP ile doğrulanamıyor.
|
||||
|
||||
**Gerçek işten uyarlanabilir desen:** FI-CA event modülleri gibi "sabit arayüzlü FM" işleri. Seed'de örnek bir FM (sabit imza) verilir; görev aynı imzayla yeni bir FM yazmaktır. Bu, F kategorisini (bilinmeyen objeyi okuyup taklit etme) gerçek bir desenle ölçer.
|
||||
|
||||
**Release ile ilişki:** Eski release hedefli görevler (G kategorisi) doğal olarak FM ve rapor ağırlıklı olur: 7.02'de sınıf yerine FORM rutinleri ve FM'ler yaygın.
|
||||
|
||||
**Harness'te gereken değişiklikler:**
|
||||
- G2 (kontrat): FUNC için imza kontrolü (`FUNCTION ... IMPORTING/EXPORTING/TABLES/EXCEPTIONS` bloğu), PROG için selection screen parametreleri.
|
||||
- abaplint dışa aktarımı: şu an sadece CLAS/INTF. PROG ve FUGR abapGit formatı eklenecek.
|
||||
- Teardown sırası: FUNC → FUGR, PROG.
|
||||
- Seed kurulumu: FUGR + FUNC (FM önce grup ister).
|
||||
|
||||
**Sıradaki görevler:** T13 (FUNC, sabit arayüzlü FM, kategori F) ve T14 (PROG, selection screen + ALV, kategori B).
|
||||
|
||||
## 9. Örnek görevler
|
||||
|
||||
### 9.1 Tam örnek: T03 (kategori C)
|
||||
|
||||
**Seed:** `ZEVAL_DISC_RULE` tablosu (`CUST_GROUP` CHAR4 key, `MIN_AMOUNT` CURR, `DISCOUNT_PCT` DEC 5,2), 200 satır. `ZEVAL_S_ORDER` yapısı ve `ZEVAL_TT_ORDER` tablo tipi.
|
||||
|
||||
**spec.md:**
|
||||
|
||||
```markdown
|
||||
# 1. Goal
|
||||
Calculate the discount for each order line. The sales team uses the result in a
|
||||
nightly batch run with up to 500 000 order lines.
|
||||
|
||||
# 2. Open questions
|
||||
None.
|
||||
|
||||
# 3. Context
|
||||
- Table ZEVAL_DISC_RULE, package ZEVAL_T03. Key: CUST_GROUP.
|
||||
Fields: CUST_GROUP (CHAR4), MIN_AMOUNT (CURR 15,2), DISCOUNT_PCT (DEC 5,2).
|
||||
One customer group has one or more rules.
|
||||
- Table type ZEVAL_TT_ORDER, line type ZEVAL_S_ORDER.
|
||||
Fields: ORDER_ID (CHAR10), CUST_GROUP (CHAR4), AMOUNT (CURR 15,2),
|
||||
CURRENCY (CUKY).
|
||||
|
||||
# 4. Contract
|
||||
- Create the class ZCL_EVAL_DISCOUNT_CALC in the package ZEVAL_T03.
|
||||
- Public method CALCULATE:
|
||||
IMPORTING it_orders TYPE zeval_tt_order
|
||||
RETURNING VALUE(rt_result) TYPE zeval_tt_disc_result
|
||||
- Create the table type ZEVAL_TT_DISC_RESULT. Line fields: ORDER_ID,
|
||||
DISCOUNT_PCT, DISCOUNT_AMOUNT (CURR 15,2), CURRENCY.
|
||||
- The method returns one result line for each order line, in the input order.
|
||||
|
||||
# 5. Business rules
|
||||
- For an order line, use the rule of the same customer group with the highest
|
||||
MIN_AMOUNT that is less than or equal to AMOUNT.
|
||||
- If no rule applies, the discount is 0.
|
||||
- DISCOUNT_AMOUNT is AMOUNT multiplied by DISCOUNT_PCT divided by 100. Round
|
||||
half up to 2 decimals.
|
||||
|
||||
# 6. Constraints
|
||||
- Release target: 7.50.
|
||||
- Do not change ZEVAL_DISC_RULE.
|
||||
|
||||
# 7. Acceptance
|
||||
- The hidden tests pass.
|
||||
- The class runs 500 000 order lines without a database access in a loop.
|
||||
```
|
||||
|
||||
**Gizli testler:** kural yok, tek kural, sınır değer (`AMOUNT = MIN_AMOUNT`), çoklu kural, yuvarlama, boş girdi, sıra korunumu.
|
||||
|
||||
**craft_checks:** `db_operation_in_loop` (abaplint); özel: kural tablosu `SORTED` veya `HASHED`, ya da tek `SELECT` + sıralı okuma; loop içinde `LOOP AT ... WHERE` üzerinde standart tablo yok.
|
||||
|
||||
**faulty_reference:** Sınır değerde `<` yerine `<=` hatası olan implementasyon. Modelin testleri bunu yakalamalı.
|
||||
|
||||
### 9.2 Kısa örnekler
|
||||
|
||||
| ID | Kat. | Release | Görev özeti | Beklenen zanaat / davranış |
|
||||
|---|---|---|---|---|
|
||||
| T01 | A | 7.58 | IBAN biçim doğrulayıcı sınıf; mod-97 kontrolü, hata nedenleri enum olarak | Saf fonksiyonlar, `ENUM` veya sabitler, küçük metotlar |
|
||||
| T02 | B | 7.58 | Seed tablosundan açık kalemleri yaşlandırma bantlarına göre toplayan sınıf | Tek aggregate `SELECT`, `CL_OSQL_TEST_ENVIRONMENT` ile test |
|
||||
| T03 | C | 7.50 | Yukarıdaki tam örnek | Loop dışı SELECT, sorted/hashed lookup |
|
||||
| T04 | D | 7.58 | Dosya satırı parser'ı; satır ve alan bilgisiyle anlamlı hata | Class-based exception, T100 mesaj sınıfı, `IF_T100_DYN_MSG` |
|
||||
| T05 | E | 7.58 | 300 satırlık seed rapor (FORM'lar, global data) → sınıfa refactor; mevcut çıktı testle sabit | Davranış korunur, global state yok, testlenebilir yapı |
|
||||
| T06 | F | 7.58 | Seed'deki `ZCL_EVAL_FX_PROVIDER` ile para birimi çevirimi. Spec sadece sınıf adını verir, imzayı vermez | Model önce ADT ile okur, sonra kullanır |
|
||||
| T07 | G | 7.02 | T03 benzeri lookup, ama 7.02 hedefi | Inline declaration yok, `NEW`/`VALUE`/`COND` yok, method chaining yok |
|
||||
| T08 | H | 7.58 | Spec iki çelişkili yuvarlama kuralı içeriyor | `stop`; `expected_gap`: rounding |
|
||||
| T09 | H | 7.58 | Spec bir alan eşlemesini vermiyor (kaynak alan belirsiz) | `stop`; `expected_gap`: field mapping |
|
||||
| T10 | I | 7.58 | Seed sınıf büyük girdide `TSV_TNEW_PAGE_ALLOC_FAILED` dump'ı veriyor; dump bilgisi spec'te | Kök neden: gereksiz tablo kopyası; düzeltme + regresyon testi |
|
||||
| T11 | B+F | 7.50 | Nötr alan: seed'de log tablosu + referans tablosu + factory sınıfı. Görev: iki yönlü reader interface ve sınıfı (anahtardan kayda, kayıttan anahtara), factory'ye `GET_READER` ekle. Belge anahtarı `REF TO data` olarak gelir; iki eşleme dalı | Join + en yeni satır (`ORDER BY ... DESCENDING`, `UP TO 1 ROWS`), cast hatası → anlamlı exception, factory'nin mevcut desenini okuyup taklit etme (F), method chaining yok, OSQL test double, `$TMP` smoke ile factory doğrulaması |
|
||||
| T12 | H | 7.58 | T11'in varyantı: istenen exception text id'si için gereken attribute mevcut exception sınıfında yok | `stop` veya açık sapma raporu; `expected_gap`: exception attribute |
|
||||
|
||||
## 10. Harness akışı
|
||||
|
||||
```
|
||||
1. setup paket oluştur, seed objelerini kur, seed verisini yükle
|
||||
2. run modeli başlat: system prompt + spec + MCP tool'ları
|
||||
bütçe: max_tool_calls, max_activations, zaman aşımı
|
||||
3. collect paketteki tüm objelerin aktif kaynağını çek
|
||||
4. gate G1 (aktif mi), G2 (imza eşleşmesi)
|
||||
5. test gizli test sınıfını kur, ABAP Unit koştur
|
||||
6. check ATC + abaplint + özel craft kontrolleri
|
||||
7. own-tests modelin testlerini faulty_reference'a karşı koştur
|
||||
8. score puanı hesapla, JSON olarak yaz
|
||||
9. log tam yörüngeyi JSONL olarak sakla (SFT adayı)
|
||||
10. teardown paketi sil
|
||||
```
|
||||
|
||||
Harness dili: Python (eğitim ekosistemi aynı dilde). MCP istemcisi olarak EPOD ADT MCP server'ına bağlanır.
|
||||
|
||||
Model arayüzü: OpenAI-uyumlu chat API. Böylece Ollama/MLX ile yerel modeller ve uzak API'ler aynı harness'te koşar.
|
||||
|
||||
## 11. Baz model ölçümü
|
||||
|
||||
Faz 1 sonunda aynı 100 görev şu gruplarla koşar:
|
||||
|
||||
- Aday baz modeller (Apache 2.0, orta boy, agentic). Liste ölçüm öncesi güncel durumla seçilir.
|
||||
- Referans üst sınır: Claude. Sadece ölçüm için; çıktıları eğitim verisine girmez.
|
||||
|
||||
Sonuç tablosu: kategori bazında puan. Baz model seçimi ve eğitim önceliği bu tablodan çıkar.
|
||||
|
||||
**Hedef çizgi:** Claude referansına tüm kategorilerde en fazla 10 puan geride kalmak. T11 ve T12 bu karşılaştırmanın ana görevleri.
|
||||
|
||||
## 11a. Adım 1.3 sonuçları (2026-10-01)
|
||||
|
||||
**Konum:** `~/projects/abap-llm/harness` (MacBook)
|
||||
|
||||
| Modül | Görev |
|
||||
|---|---|
|
||||
| `harness/mcp_client.py` | MCP streamable-http istemcisi; 404'te oturumu yeniden açar |
|
||||
| `harness/adt_client.py` | ADT deletion API (sadece teardown; kimlik bilgisi `.env`'den) |
|
||||
| `harness/proxy.py` | Beyaz liste, bütçe, başka koşu öneklerini filtreleme, yazma hata serisi sayacı, yörünge logu |
|
||||
| `harness/agents.py` | `oracle` (referans çözüm), `null`, `llm` (OpenAI-uyumlu, zaman bütçesi 30 dk) |
|
||||
| `harness/runner.py` | setup → agent → collect → G1–G6 → gizli testler → kendi testleri → ATC → abaplint → puan → teardown |
|
||||
| `harness/cli.py` | `run`, `teardown`, `teardown-all`, `cleanup-list` |
|
||||
| `tasks/T01/` | IBAN görevi: spec, seed interface, 12 gizli test, referans |
|
||||
|
||||
**Koşular (T01):**
|
||||
|
||||
| Koşu | Agent | Puan | Not |
|
||||
|---|---|---|---|
|
||||
| 002 | oracle | 85 | 12/12 gizli test, abaplint ve ATC temiz. Kendi testleri 0 (referansta test yok). |
|
||||
| 003 | null | 0 | G1, G2, G3 düştü. |
|
||||
| 004 | qwen3.8:27b-mlx | — (32 dk sonra elle durduruldu; erken) | 15 tool çağrısı, 11 yazmadan 10'u aktivasyon hatası. Interface metodunu niteleme, offset syntax'ı (`lv_pos+1`), `find( ... ignore_case )` parametreleri. İki başarısızlıktan sonra durmadı. |
|
||||
|
||||
Oracle ve null, puan aralığının iki ucunu doğruluyor. Koşu 004, eval'in ölçmesi gereken zayıflığı tam gösteriyor: ABAP syntax bilgisi ve durma disiplini.
|
||||
|
||||
**Eklenen kurallar:**
|
||||
- Yerel model kuralları: zaman limiti yok (sınırı tool çağrısı bütçesi koyar; `--max-minutes` opsiyonel). İstekler tek tek gider: tek worker, `parallel_tool_calls: false`, tool çağrıları sırayla çalışır.
|
||||
- Disiplin: art arda 3 veya daha fazla başarısız yazma/aktivasyon → disiplin puanından −5.
|
||||
|
||||
**Ölçüm süresi riski:** Yerel 27B model, Ollama'da 262k bağlamla tur başına 2–3 dakika harcadı. 100 görev × 30 dakika ≈ 50 saat/model. Adım 1.6 öncesi: bağlam penceresini sınırlamak (ör. 32k), MLX server'ı denemek, ölçümü gece ve hafta sonu koşularına bölmek. Koşu 005 (sınırsız süre) ilk gerçek süre ölçümünü verecek.
|
||||
|
||||
**MCP server eksiği (test include):** Yeni sınıfta test include yok; ADT artık onu otomatik oluşturmuyor (butona basmak gerekiyor). MCP oluşturamıyor. Karar: server'a eklenecek. Geçiş döneminde model testleri global test sınıfına yazabilir; puanlama ikisini de sayıyor. Eklendikten sonra yeni bir probe sınıfıyla test edilir.
|
||||
|
||||
**Teardown:** Mac mini'de `.env` (A4H kimlik bilgisi) dolduruldu; teardown her koşu sonunda otomatik çalışır. Kilitli 1 obje ve MacBook'taki koşu 005'in objeleri (`Z005001_`) bekliyor; koşu 005 bitmeden silinmez.
|
||||
|
||||
## 11b. Mac mini kurulumu (2026-10-02)
|
||||
|
||||
| Konu | Durum |
|
||||
|---|---|
|
||||
| Konum | `~/projects/abap-llm/harness` (kullanıcı `erhankeseli`) |
|
||||
| Erişim | Bu Project'in sohbetleri Mac mini'deki Claude Desktop'tan; Desktop Commander Mac mini'de |
|
||||
| MCP server | ADT (Eclipse) içinde, `127.0.0.1:3000`. Sistem adı `A4H`, mod `write` |
|
||||
| MCP token | ADT plugin ayarından (`com.epod.adt.mcp.plugin.prefs`) `.env`'e yazıldı; `.env` izni 600 |
|
||||
| Python / Node | Python 3.9.6 (sistem), Node 26.8.2; abaplint 2.120.63 |
|
||||
| Model | `qwen3.8-27b-32k` = `qwen3.8:27b-mlx` + `num_ctx 32768` (Ollama Modelfile) |
|
||||
| Bağlam takibi | Her model yanıtının `usage` değeri yörüngeye yazılıyor; 32k sınırına yaklaşma buradan görülür (Ollama sınırı aşınca sessizce keser) |
|
||||
| Koşu numaraları | MacBook: 001–099, Mac mini: 101+. Önekler çakışmaz. |
|
||||
|
||||
Doğrulama: oracle (koşu 101) **85**, null (koşu 102) **0**. MacBook sonuçlarıyla aynı.
|
||||
|
||||
### Koşu 103: qwen3.8-27b-32k, T01 — **41,7 / 100**
|
||||
|
||||
| Bileşen | Puan | Neden |
|
||||
|---|---|---|
|
||||
| Doğruluk | 16,7 / 40 | 12 gizli testten 5'i geçti. Geçerli IBAN'lar OK dönmüyor (checksum hesabı yanlış); COUNTRY ve LENGTH kontrolleri yanlış sırada veya yanlış. |
|
||||
| Zanaat | 10 / 20 | `method_length`: ana metot 45, yardımcı runner metodu 84 statement. |
|
||||
| Clean ABAP | 15 / 15 | Bulgu yok. |
|
||||
| Kendi testleri | 0 / 15 | İki "test" sınıfı yazdı ama `FOR TESTING` yok; biri interface metodunu niteleme hatasıyla derlenmiyor. Gerçek ABAP Unit testi yok. |
|
||||
| Disiplin | 0 / 10 | Tool bütçesi (40) bitti, 22 aktivasyon (bütçe 10), art arda 5 başarısız yazma. Rapor yazamadan durdu. |
|
||||
|
||||
Süre: 38 dakika (2 269 sn). Yardımcı objeler: bir `IF_OO_ADT_CLASSRUN` runner (smoke için; kurala uygun) ve iki test sınıfı denemesi.
|
||||
|
||||
**Bu koşudan çıkan harness düzeltmeleri:**
|
||||
- abaplint JSON çıktısında `file` alanı string geliyor; parse düzeltildi (koşu 103 bu yüzden puanlama adımında çökmüştü).
|
||||
- `rescore` komutu: agent'ı tekrar çalıştırmadan, A4H'daki objelerden ve yörüngeden yeniden puanlar. Puanlayıcı değiştiğinde eski koşular tekrar değerlendirilebilir.
|
||||
- G6 (release kapısı) sadece kontrat objelerine uygulanır. Yardımcı objeler abaplint'in bilmediği standart API'leri (`IF_OO_ADT_CLASSRUN`) kullanabilir; bunlarda yanlış alarm oluşuyordu. Doğruluk için A4H syntax check yeterli.
|
||||
- G4 karşılaştırması normalize edildi (boşluk ve büyük/küçük harf).
|
||||
- Kendi testleri: modelin yazdığı **global test sınıfları** da sayılır (MCP local test include oluşturamadığı için modelin tek yolu bu).
|
||||
|
||||
**Açık puanlama soruları:**
|
||||
- Yardımcı objelerdeki (runner, smoke) bulgular zanaat puanından düşsün mü? Şu an düşüyor.
|
||||
- Bütçe bitince rapor yok. Rapor eksikliği ayrıca cezalandırılsın mı, yoksa bütçe aşımı cezası yeterli mi?
|
||||
|
||||
## 11c. Baştan başlangıç (2026-10-02, koşu 201+)
|
||||
|
||||
**Test include çözüldü (server):** `sap_create_object` CLAS ile test include'u da oluşturuyor; `sap_push_source` `includeType: "testclasses"` ile local test yazıyor (include yoksa oluşturuyor). Probe `Z191001_TINC`: 1 test, coverage %50 (beklenen). Global test sınıfı geçici çözümüne artık gerek yok; puanlama ikisini de saymaya devam ediyor.
|
||||
|
||||
**Uygulanan puanlama kararları:**
|
||||
- Zanaat bulguları sadece kontrat objelerinden sayılır (yardımcı runner/smoke objeleri hariç).
|
||||
- Model rapor yazmadan biterse (boş veya "Stopped:") disiplinden −5.
|
||||
|
||||
**Referans çözüme local testler eklendi** (T01: 6 test). Oracle artık "kendi testleri" bileşenini de doğruluyor.
|
||||
|
||||
| Koşu | Agent | Puan | Not |
|
||||
|---|---|---|---|
|
||||
| 201 | oracle | **100** | 12/12 gizli test; kendi testleri 6/6, coverage %100; teardown 3/3 otomatik |
|
||||
| 202 | null | **0** | |
|
||||
| 203 | qwen3.8-27b-32k | (çalışıyor) | |
|
||||
| 204 | deepseek-v4.1-flash:cloud (Ollama cloud) | **98,5** | 12/12 gizli test; 16 local test, coverage %98; 8 tool çağrısı, 5 aktivasyon; 2,5 dk. Tek bulgu `prefer_xsdbool`. Rapor plan formatında, kararlar gerekçeli. İlk aktivasyon hatasını (string offset) bir denemede düzeltti. |
|
||||
|
||||
Koşu 204 token kullanımı: 8 tur, 104 093 girdi (kümülatif, çoğu cache), 51 830 çıktı (düşünme dahil), en büyük bağlam 16 496. DeepSeek liste fiyatıyla ≈ 0,09 $ / koşu.
|
||||
|
||||
Eski koşular `runs/_archive_v1` altında.
|
||||
|
||||
## 11d. Adım B: FM, rapor ve CDS desteği (2026-10-02)
|
||||
|
||||
**Doğrulanan mekanizmalar (A4H + MCP):** TABL (DDL kaynağı), DDLS, FUGR, FUNC (kaynakta imza), PROG oluşturma ve yazma; seed verisi `IF_OO_ADT_CLASSRUN` sınıfı + `sap_run_class` (`className`); gizli testlerde `CALL FUNCTION` + `EXCEPTIONS`, `SUBMIT ... AND RETURN` + `cl_salv_bs_runtime_info` ile ALV verisi, `CL_CDS_TEST_ENVIRONMENT` ile CDS test double'ları.
|
||||
|
||||
**Harness değişiklikleri:**
|
||||
- Kurulum: FUGR (kaynaksız), FUNC (`functionGroup`), seed sınıfını çalıştırma (`run: true`), test include.
|
||||
- G2 kontrat: FUNC parametre adı + tipi (FUNCTION başlığında), PROG selection screen parametreleri, DDLS alan adları (`sap_sql_query` kolonları).
|
||||
- Kendi testleri: PROG içi local test sınıfları; FUNC/DDLS için global test sınıfı; coverage yoksa testler tam puan.
|
||||
- abaplint: PROG dışa aktarımı; abaplint'in okuyamadığı koşu objeleri (TABL, DDLS, FUNC) hata sayılmaz, doğruluk A4H syntax check'te.
|
||||
- Silme sırası: CLAS → INTF → PROG → FUNC → FUGR → SRVD/DDLX/DCLS → DDLS → TTYP → TABL → … ; CDS'in STOB kayıtları atlanır.
|
||||
- Kurulum başarısızsa koşu puanlanmaz (`setup_failed`), objeler silinir.
|
||||
|
||||
**Eşzamanlılık bulgusu (önemli):** Server oturumlar arasında tek RFC bağlantısı paylaşıyor. Paralel bir çağrı beklemek yerine `[LOCK] Concurrent call detected` hatası alıyor. Koşu 214'te T14'ün seed tablosu bu yüzden aktive olmadı. Harness artık bu hatada bekleyip tekrar deniyor (en fazla 8 kez); model bu hatayı görmez. Kalıcı çözüm server'da: çağrıları bağlantı başına sıraya almak veya bağlantı havuzu.
|
||||
|
||||
**Koşular:**
|
||||
|
||||
| Görev | Tip | Oracle | Null | DeepSeek V4.1 Flash |
|
||||
|---|---|---|---|---|
|
||||
| T13 | FUNC (sabit arayüz, F) | 100 | 0 | 85: 8/8 gizli test; kendi test sınıfı yazmadı; 47 sn |
|
||||
| T14 | PROG + ALV (B) | 100 | 0 | 214: seed kurulumu eşzamanlılık hatası → model tabloyu inaktif görüp **doğru şekilde durdu**. 216 (düzeltmeden sonra): **100**, 4 dk |
|
||||
| T15 | DDLS (B) | 100 | 0 | 215: çalışıyor |
|
||||
|
||||
T13'te ATC "slow parameter passing" bulgusu seed arayüzündeki `VALUE()` yüzündendi; seed referans geçişe çevrildi.
|
||||
|
||||
## 11e. Pilot sonuçları (2026-10-02, G0002–G0021)
|
||||
|
||||
DeepSeek V4.1 Flash, 20 görev, her görev en fazla 3 onarım turu. Sonuçlar: `runs/gen/pilot.json`.
|
||||
|
||||
| | Sonuç |
|
||||
|---|---|
|
||||
| Kabul (oracle 100, null 0) | **16 / 20 (%80)**. CLAS 7/9, FUNC 2/3, PROG 3/3, DDLS 4/5 |
|
||||
| İlk denemede kabul | 3 / 20 (G0004, G0007, G0012) |
|
||||
| LLM çağrısı | 55 (görev başına 2,75) |
|
||||
| Maliyet (liste fiyatı, üst sınır) | 2,52 $ → **0,126 $ / görev**, **0,16 $ / kabul edilen görev** (tahmin 0,05 $ idi) |
|
||||
| Çıktı token | ortalama 35k / çağrı (çoğu reasoning). Bir çağrıda 393k token, içerik boş (G0006, ~0,47 $) |
|
||||
| Süre | ~2 saat (görev başına ~6 dk) |
|
||||
|
||||
Başarısız denemelerin nedenleri (oracle koşusu < 100 olan 37 deneme):
|
||||
|
||||
| Neden | Sayı | Görevler | Önlem |
|
||||
|---|---|---|---|
|
||||
| İsim > 30 karakter (çoğu test metodu) | 6 | G0002, G0003, G0005, G0009, G0010, G0017 | Statik kontrol + prompt kuralı |
|
||||
| Metot imzasında `TYPE c LENGTH n` / `TYPE p LENGTH n`; SAP sadece "save operation failed" der | 7 | G0005 (3×), G0006 (3×), G0008 | Yerel abaplint `parser_error` ön kontrolü (satır numarasıyla) + prompt kuralı |
|
||||
| CDS sözdizimi: parametre `default`, UNION anahtar uyumu, eksik anotasyon | 4 | G0020, G0021 | Prompt kuralı |
|
||||
| Ayrılmış kelime alan adı (HOURS, MODE) | 2 | G0017, G0021 | Statik kontrol + prompt kuralı |
|
||||
| Parametreli CDS'te G2 her zaman düşüyor (**harness hatası**: `SELECT *` parametresiz çalışmaz) | 3 | G0020 | Runner: SELECT sütun döndürmezse eleman adları kaynaktan okunur. G0020 tek satır düzeltmeyle oracle 100 |
|
||||
| Tablo seed'i DDLS tipiyle | 2 | G0003 | Statik kontrol + prompt kuralı |
|
||||
| Geçersiz / boş JSON | 3 | G0002, G0006, G0010 | `raw_decode`, `max_tokens` 100k, boş içerikte yeniden deneme |
|
||||
| Eski seed kodu 816'da aktive olmuyor (yeni/eski SQL karışık) | 1 | G0016 | Prompt kuralı |
|
||||
| Referans mantık hatası (gizli test düşüyor), tip uyumsuzluğu, CX_CDS_FAILURE | 6 | G0013, G0014, G0018, G0019, G0015 | Onarım turu çözdü |
|
||||
| Sözleşmede sınıf var, yerel testi yok → own_tests 7,5 | 2 | G0011 | Statik kontrol: her referans sınıfının `testclasses_file`'ı olmalı (istisna sınıfları hariç) |
|
||||
|
||||
Diğer bulgular:
|
||||
- Onarım geri bildiriminde G2 nedeni yoktu; model sözleşme hatasını göremiyordu. Runner artık `contract_check` yazar, generator bunu geri bildirime ekler.
|
||||
- Kabul edilen G0013'ün sözleşmesinde test sınıfı var (`REPL_TEST`). Statik kontrol artık bunu reddeder. G0013 incelemede düzeltilmeli.
|
||||
- Üretilen görevler örnekteki isimleri kopyalıyor (`T02_HIDDEN`, `T14_TEST`). Kozmetik.
|
||||
- Statik kontrol ve abaplint ön kontrolü SAP'ye gitmez. Pilottaki 37 başarısız denemenin ~18'ini doğrulama koşusundan önce yakalar (tahmin).
|
||||
|
||||
## 12. Açık sorular
|
||||
|
||||
Cevaplananlar (Adım 1.2):
|
||||
- ~~MCP yazma tool'ları~~ → bölüm 5.
|
||||
- ~~A4H release ve abapGit~~ → SAP_BASIS 816 SP01. abapGit standalone kurulu (`ZABAPGIT_STANDALONE`). Standalone sürüm script'le kullanılamaz; seed kurulumu MCP üzerinden yapılır. code pal for ABAP kurulumu için kullanılabilir.
|
||||
|
||||
- ~~Eşzamanlı oturum sınırı~~ → 8 (MCP server), bölüm 5.
|
||||
- ~~abaplint ve seed tipleri~~ → çalışıyor, bölüm 6.5.
|
||||
|
||||
Açık olanlar:
|
||||
- EPOD server istekleri (Kral): (1) yazma başarısız olursa syntax check çalıştırıp mesajlarını döndürsün; şu an sadece "save failed" diyor, model nedeni göremez (2026-10-02). (2) RFC bağlantısı başına kuyruk veya bağlantı havuzu ("Concurrent call detected"). (3) BDEF oluşturma (RAP).
|
||||
- Genel ABAP MCP arayüzünün spec'i (`abap-mcp-arayuz.md`): tool isimleri, parametre şemaları, çekirdek dışı tool'lar, hata formatı. Sonra yazılacak.
|
||||
1. ~~Görevleri kim yazar?~~ → Öneri (2026-10-02): DeepSeek V4.1 Flash yazar. İnceleme üç katmanlı, Kral'ın tam incelemesi yok:
|
||||
- **Otomatik kapılar:** oracle = 100, null = 0, gizli testler bozuk referansta (mutasyon) düşer, her iş kuralı en az bir gizli testle örtülü.
|
||||
- **Ampirik süzgeç:** Görev 2–3 modelle koşulur. Güçlü model düşük, referans yüksek puan alıyorsa spec belirsiz olabilir → işaretlenir. Herkesin kolayca geçtiği görevler eval'den çıkarılır.
|
||||
- **Claude incelemesi** (bu Project'te, kontrol listesiyle): spec netliği, kontrat–test uyumu, gerçekçilik. Sadece eval görevleri; eval görevleri eğitim verisine girmez.
|
||||
- **Kral:** sadece işaretlenen görevler + kategori başına 1 örnek (~10 görev, ~30 dk).
|
||||
2. Hakem modeli yerel mi, uzak mı?
|
||||
|
||||
## 13. Faz 1 bitti sayılır, eğer
|
||||
|
||||
- ~~abaplint, A4H seed tiplerini tanıyarak çalışıyor.~~ ✓
|
||||
- Harness T01–T10'u uçtan uca koşturuyor ve puanlıyor.
|
||||
- 100 görevlik set yazılmış ve senin incelemenden geçmiş.
|
||||
- En az iki baz model ve Claude referansı ölçülmüş.
|
||||
161
docs/yol-haritasi.md
Normal file
161
docs/yol-haritasi.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# ABAP Danışman Modeli — Yol Haritası
|
||||
|
||||
Durum: v19 · 2026-10-02
|
||||
Şu anki konum: **Adım 1.3**
|
||||
|
||||
## Adım 0 — Çerçeve ve kararlar
|
||||
|
||||
| Karar | Durum |
|
||||
|---|---|
|
||||
| Modelin rolü: teknik ABAP danışmanı, modül bilgisi yok | ✓ |
|
||||
| Sözleşme: metin olarak ABAP görevi + genel ABAP MCP arayüzü | ✓ (2026-10-02) |
|
||||
| Girdi: `plan-agent.md` formatı en iyi sonuç veren girdi, zorunlu değil; Kral'ın akışında plan kullanımı değişmez | ✓ (2026-10-02) |
|
||||
| Tool arayüzü: EPOD isimleri yerine genel ABAP MCP; EPOD native destekleyecek, harness proxy ilk adapter; delete/teardown harness'te | ✓ (2026-10-02) |
|
||||
| Eğitimde az miktarda tool isim/şema varyasyonu | ✓ (2026-10-02) |
|
||||
| Ortam: A4H (yazma + doğrulama) | ✓ |
|
||||
| Mevcut planlar: sadece şablon, eğitim verisi değil | ✓ |
|
||||
| Eski playbook formatı kullanılmaz | ✓ (2026-10-02) |
|
||||
| Eğitim verisinde Claude çıktısı yok; eval görevleri eğitim verisine girmez | ✓ (2026-10-02) |
|
||||
| Lisans: Apache 2.0 | ✓ |
|
||||
| Baz model: sadece Apache 2.0 veya MIT lisanslı adaylar | ✓ (seçim Adım 2'de) |
|
||||
|
||||
Bitti. ✓
|
||||
|
||||
## Adım 1 — Eval seti ve harness
|
||||
|
||||
Amaç: modeli ölçebilmek. Ölçüm olmadan eğitim kör olur.
|
||||
|
||||
| Alt adım | İş | Durum |
|
||||
|---|---|---|
|
||||
| 1.1 | Tasarım dokümanı (`faz1-tasarim.md`) | ✓ v3 |
|
||||
| 1.2 | Teknik doğrulamalar: MCP tool'ları, A4H release, teardown (ADT deletion API), abaplint + seed tipleri, paralellik (8 oturum) | ✓ |
|
||||
| 1.3 | Harness iskeleti: setup → run → puan → teardown, T01 ile uçtan uca | **✓** — Mac mini'de; test include ve teardown çözüldü. Baştan koşular: oracle 100, null 0, Qwen (koşu 203) çalışıyor |
|
||||
| 1.3b | Genel ABAP MCP arayüz spec'i (`abap-mcp-arayuz.md`) ve harness proxy'sinin bu arayüze çevrilmesi | Sonra |
|
||||
| 1.4 | T01–T15: seed, gizli testler, hatalı referans | |
|
||||
| 1.5 | 110 görevlik set (kategori K dahil: eksik/serbest girdi + farklı tool şeması): üretim + senin incelemen | |
|
||||
| 1.6 | Ölçüm: aday baz modeller + Claude referansı | |
|
||||
|
||||
Bitti sayılır: 110 görev, kategori bazında sonuç tablosu.
|
||||
|
||||
## Hacim tahmini (kaba, 2026-10-02)
|
||||
|
||||
İlk ölçümlerle kalibre edilecek (koşu süresi, kabul oranı).
|
||||
|
||||
| Aşama | Görev | Koşu | Obje (oluşturulup silinen) | Süre |
|
||||
|---|---|---|---|---|
|
||||
| Eval seti (Adım 1) | 110 | 110 / model / mod | ~5–8 / koşu → ~700 / ölçüm | Yerel 27B: 35–50 saat / ölçüm |
|
||||
| Ölçüm turu (Adım 1.6) | 110 | ~3–4 model × 2 mod + Claude ≈ 900 | ~6 000 | Yerel modeller birkaç hafta; uzak modeller birkaç gün |
|
||||
| Eğitim verisi (Adım 3) | 1 500–2 000 üretilmiş görev | 4 deneme / görev ≈ 6 000–8 000 | ~40 000–50 000 | Yerel 27B ile ~5 000 saat → **yerelde mümkün değil** |
|
||||
| Hedef çıktı (Adım 3) | | %40 kabul → **~3 000 yörünge** | | |
|
||||
| RL (Adım 5, opsiyonel) | aynı havuz | on binlerce | yüz binler | Ayrı planlama |
|
||||
|
||||
**Teacher maliyeti (DeepSeek V4.1 Flash, Ollama cloud):** T01'de ölçülen ≈ 0,09 $ / koşu (liste fiyatı). T01 kolay bir görev; ortalama görev için 0,15–0,20 $ varsayımı:
|
||||
|
||||
| İş | Koşu | Maliyet (liste fiyatı) |
|
||||
|---|---|---|
|
||||
| Eval ölçümü, DeepSeek (110 × 2 mod) | 220 | ~35–45 $ |
|
||||
| Adım 3 (2 000 görev × 2 deneme; T01'deki başarıyla kabul oranı yüksek beklenir) | ~4 000 | ~600–800 $ |
|
||||
| **Toplam (RL hariç)** | | **~650–850 $** |
|
||||
|
||||
Ollama paneli, koşu 204'ün 8 isteği tek tek: 6 × <0,01 $, 1 × 0,01 $, 1 × 0,03 $ → **≈ 0,05–0,08 $ / koşu**. Liste fiyatı tahminine (0,09 $) yakın; Ollama kullanımı liste fiyatına yakın sayıyor. (Ayın toplamındaki düşük ortalama, başka küçük isteklerden geliyor.)
|
||||
|
||||
| İş | Koşu / görev | Tahmin |
|
||||
|---|---|---|
|
||||
| Pilot (B–E): ~20 görev üretimi + doğrulama + koşular | ~100 istek grubu | ~5–10 $ |
|
||||
| Eval seti üretimi + ölçüm (110 görev, 2 mod) | ~330 | ~25–40 $ |
|
||||
| Eğitim görevi üretimi (2 000 görev) | 2 000 | ~100 $ |
|
||||
| Yörünge üretimi (2 000 × 2 deneme) | ~4 000 | ~400–600 $ |
|
||||
| **Toplam (RL hariç)** | | **~550–750 $** |
|
||||
|
||||
Bütçe: şu an ~50 $, her ay +60 $. B–F ve eval ölçümü bu bütçeye sığar. Yörünge üretimi aylık 60 $ ile 7–10 ay sürer; o aşamada ~500–700 $ ek kredi önerilir. Kesin rakam pilottan sonra.
|
||||
|
||||
**Karar (2026-10-02): DeepSeek V4.1 Flash (Ollama cloud) teacher ve görev yazarı olarak hemen kullanılıyor.**
|
||||
|
||||
Sonuçlar:
|
||||
- Aynı anda A4H'da en fazla ~50 obje olur (6 worker × ~8). Toplam sayı büyük ama birikmez; teardown her koşu sonunda.
|
||||
- **Adım 3 yerel modelle yapılamaz.** Teacher model hızlı ve uzakta olmalı (kiralık GPU veya açık lisanslı modelin API'si). O zaman darboğaz A4H olur: 6 worker, koşu başına ~5 dk → saatte ~70 koşu → 8 000 koşu ≈ 5 gün.
|
||||
- **Önek şeması yetmez:** `Z<run 3 hane><task 3 hane>_` en fazla 999 koşu. Adım 3 öncesi koşu kimliği base36 4 karaktere çıkarılmalı (~1,7 milyon koşu).
|
||||
- Uzun vadede A4H'da izlenecekler: disk (HANA log, versiyon geçmişi, silme logları).
|
||||
|
||||
## Hızlandırılmış sıra (2026-10-02)
|
||||
|
||||
Adım 3 görev ister; görevler el ile yazılırsa darboğaz olur. Bu yüzden ilk iş **görev üreteci**: hem eval setini (1.5) hem eğitim görevlerini (3.1) aynı hat üretir.
|
||||
|
||||
| Sıra | İş | Durum |
|
||||
|---|---|---|
|
||||
| A | Önek şeması: koşu 4 karakter base36, görev 3 karakter base36 (`Z005P001_`) | ✓ |
|
||||
| B | Harness: FUGR/FUNC, PROG, DDLS desteği (G2 kontrat kontrolü, abaplint dışa aktarımı, silme sırası) | ✓ T13–T15 oracle 100 / null 0. Server eşzamanlılık sorunu bulundu (harness'te retry) |
|
||||
| C | Görev üreteci: DeepSeek görev yazar (spec, seed, gizli testler, referans + local testler); kategori ve obje tipi dağılımına göre | ✓ `harness/generator.py`: üretim → yapı + statik kontrol + yerel abaplint → oracle/null doğrulama → en fazla 3 onarım turu. Pilot sonrası iyileştirildi (faz1-tasarim 11e) |
|
||||
| D | Otomatik görev doğrulama: oracle = 100, null = 0, gizli testler bozuk referansta düşmeli | oracle/null hazır; mutasyon kontrolü sonra |
|
||||
| E | Pilot: ~20 görev, tüm obje tipleri; kabul oranı ve gerçek maliyet ölçümü | ✓ 16/20 kabul, 0,16 $ / kabul edilen görev (faz1-tasarim 11e). İyileştirilmiş generator henüz denenmedi |
|
||||
| F | Eval seti: 110 görev; Kral incelemesi. Eğitim havuzu: otomatik doğrulanmış görevler, eval ile çakışmasız | |
|
||||
| G | Ölçüm (1.6) ve yörünge üretimi (3.2) paralel worker'larla (bulut model: en fazla 6) | |
|
||||
|
||||
## Bütçe takvimi (Ollama cloud, 2026-10-02)
|
||||
|
||||
Kalan: ~50 $ (12 Ekim'e kadar). 12 Ekim'de +60 $, sonra her ay +60 $. Birim maliyet (ölçülen): DeepSeek koşusu ≈ 0,05–0,08 $; görev üretimi ≈ 0,13 $ / görev, 0,16 $ / kabul edilen görev (pilot, liste fiyatı). Yerel modeller (Qwen vb.) ücretsiz; gece koşuları.
|
||||
|
||||
| Dönem | Bütçe | İş | Tahmini harcama |
|
||||
|---|---|---|---|
|
||||
| **2–12 Ekim** | ~50 $ | C: görev üreteci. D: otomatik doğrulama. E: pilot (20 görev, üretim + DeepSeek koşusu) | ~5 $ |
|
||||
| | | F: eval seti — ~150 aday görev üret (~20 $), süz, 110 kalsın; her aday 1–2 DeepSeek koşusu (ampirik süzgeç, ~10–20 $) | ~30–40 $ |
|
||||
| | | Yerel ölçüm (Qwen ve aday baz modeller, ücretsiz, geceleri) | 0 $ |
|
||||
| | | Yedek | ~15 $ |
|
||||
| **12 Ekim – 12 Kasım** | 60 $ | Adım 2: baz model seçimi (yerel ölçüm sonuçlarıyla). Adım 3 pilotu: ~300 eğitim görevi + ~600 yörünge denemesi | ~60 $ |
|
||||
| | | Pilot sonucu: gerçek kabul oranı ve maliyet → Adım 3'ün tamamı için ek kredi kararı (tahmin ~400–600 $) | |
|
||||
| **12 Kasım sonrası** | 60 $/ay + ek kredi | Adım 3'ün tamamı, ardından Adım 4 (SFT) | |
|
||||
|
||||
Kurallar:
|
||||
- Harness her bulut isteğinin token kullanımını `runs/ledger.jsonl`'e yazar (liste fiyatı, cache indirimi olmadan = üst sınır). Dönem harcaması `BUDGET_LIMIT_USD`'ye (şu an 45 $) ulaşınca yeni üretim veya bulut koşusu başlamaz. ✓
|
||||
- Eval görevleri ve eğitim görevleri ayrı havuzlar; eval görevleri eğitim verisine girmez.
|
||||
- Bulut ve yerel model aynı Ollama sunucusunda kuyrukta bekleyebilir; ölçümler sırasında ikisi aynı anda koşturulmaz.
|
||||
|
||||
## Adım 2 — Baz model seçimi
|
||||
|
||||
Adım 1.6 tablosundan seçilir. Aday havuzu: sadece Apache 2.0 veya MIT lisanslı modeller. Kriterler: puan, boyut, Mac mini'de çalışabilirlik.
|
||||
|
||||
Bitti sayılır: tek baz model seçildi, gerekçesi yazıldı.
|
||||
|
||||
## Adım 3 — Eğitim verisi üretimi
|
||||
|
||||
| Alt adım | İş |
|
||||
|---|---|
|
||||
| 3.1 | Görev üreteci: eval görevleriyle aynı şemada binlerce sentetik görev. Eval görevleriyle çakışma yok. Girdilerin bir kısmı serbest metin; yörüngelerin az bir kısmı farklı tool isim/şemalarıyla. |
|
||||
| 3.2 | Yörünge üretimi: açık lisanslı teacher model görevleri A4H'da çözer. |
|
||||
| 3.3 | Filtre: sadece puan eşiğini geçen yörüngeler kalır. Deduplikasyon. |
|
||||
| 3.4 | Opsiyonel: izinli lisanslı açık ABAP kodu (continued pretraining için). |
|
||||
|
||||
Eğitim verisinde mutlaka olmalı (pilot bulguları, 2026-10-02): modellerin sık yaptığı hatalar için görev ve onarım yörüngeleri.
|
||||
- Metot parametresinde `TYPE c LENGTH n` geçersiz; adlandırılmış tip (`TYPES`) gerekir.
|
||||
- Ayrılmış kelime alan adı (HOURS, MODE).
|
||||
- 30 karakterden uzun isimler (özellikle test metotları).
|
||||
|
||||
Bitti sayılır: filtrelenmiş yörünge seti hazır, kategori dağılımı dengeli.
|
||||
|
||||
## Adım 4 — SFT (LoRA)
|
||||
|
||||
| Alt adım | İş |
|
||||
|---|---|
|
||||
| 4.1 | Küçük deneme: Mac mini, MLX |
|
||||
| 4.2 | Gerçek koşu: kiralık GPU |
|
||||
| 4.3 | Eval: Adım 1 setiyle, baz modelle karşılaştırma |
|
||||
|
||||
Bitti sayılır: eğitilmiş model baz modeli anlamlı farkla geçiyor.
|
||||
|
||||
## Adım 5 — RL (opsiyonel)
|
||||
|
||||
SFT hedefe ulaşmazsa. Ödül: harness puanı (syntax, aktivasyon, testler, abaplint, ATC).
|
||||
|
||||
Bitti sayılır: hedef çizgi (Claude referansına ≤ 10 puan fark).
|
||||
|
||||
## Adım 6 — Gerçek iş akışında deneme
|
||||
|
||||
`/sapplan` planı → model build-agent rolünde → gerçek bir görev. Eval'in yakalamadığı sorunlar burada çıkar.
|
||||
|
||||
Bitti sayılır: senin seçtiğin gerçek görevlerde kabul edilebilir sonuç.
|
||||
|
||||
## Adım 7 — Yayın
|
||||
|
||||
Ağırlıklar, model kartı, lisans, eval sonuçları, genel ABAP MCP arayüz spec'i. Opsiyonel: harness ve eval seti açık kaynak.
|
||||
|
||||
Bitti sayılır: yayınlandı.
|
||||
@@ -6,6 +6,9 @@ import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
import urllib.request
|
||||
|
||||
@@ -15,6 +18,9 @@ from .ledger import add_usage, check_budget
|
||||
from .runner import Runner
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
ABAPLINT = os.path.join(ROOT, "node_modules", ".bin", "abaplint")
|
||||
MAX_OUT_TOKENS = 100000
|
||||
ABAPLINT_VERSIONS = {"v702", "v740sp05", "v750", "v758"}
|
||||
EXAMPLE_FOR = {"CLAS": "T01", "INTF": "T01", "FUNC": "T13", "PROG": "T14", "DDLS": "T15", "TABL": "T15"}
|
||||
CATEGORIES = {
|
||||
"A": "pure logic in a new class (language, OO design, Clean ABAP)",
|
||||
@@ -37,6 +43,18 @@ Rules for the bundle:
|
||||
The harness replaces it with a run prefix of 9 characters (for example Z005P001_).
|
||||
Name length after replacement: classes, programs, function modules, CDS entities max 30;
|
||||
database tables max 16; function groups max 26.
|
||||
- Every other ABAP name (methods, test methods, local classes, data, types, constants) has max 30
|
||||
characters. Keep test method names short, for example "rejects_zero_qty".
|
||||
- Method parameters need a complete type: "TYPE c LENGTH 4" is not allowed in a signature.
|
||||
Declare a type first (TYPES ty_zone TYPE c LENGTH 4) and use it.
|
||||
- Field names in tables and CDS views are not SQL reserved words (no HOURS, MODE, ORDER, DATE, COUNT ...).
|
||||
- CDS: use "define view entity". Parameters have no default value. In a UNION, all branches have the same
|
||||
key elements and element names. Hidden tests for a view with parameters pass all parameters.
|
||||
- Legacy seed code (categories E, I) must activate on SAP_BASIS 816: do not mix old and new syntax in one
|
||||
SQL statement (with new syntax, every host variable needs "@").
|
||||
- The contract lists only the objects that the hidden tests call. Test classes are never in the contract.
|
||||
Every reference class (except exception classes) has a "testclasses_file".
|
||||
- A database table seed has type "TABL" and DDL source "define table ...". Type "DDLS" is only for CDS views.
|
||||
- All objects are in package $TMP. Do not use transports.
|
||||
- Do not use SAP application module data (no FI, SD, MM tables). Use generic business domains and
|
||||
only objects that exist in every ABAP Platform system (language, ABAP SQL, CDS, CL_ABAP_*, CL_SALV_TABLE,
|
||||
@@ -84,7 +102,8 @@ def bundle_of(task_dir):
|
||||
|
||||
|
||||
def chat(model, messages, base_url):
|
||||
body = {"model": model, "messages": messages, "temperature": 0.7}
|
||||
# max_tokens: one pilot call ran to 393k output tokens (reasoning) and returned no content
|
||||
body = {"model": model, "messages": messages, "temperature": 0.7, "max_tokens": MAX_OUT_TOKENS}
|
||||
req = urllib.request.Request(f"{base_url}/chat/completions", json.dumps(body).encode(),
|
||||
{"Content-Type": "application/json", "Authorization": "Bearer none"})
|
||||
for attempt in range(4):
|
||||
@@ -92,7 +111,13 @@ def chat(model, messages, base_url):
|
||||
with urllib.request.urlopen(req, timeout=1800) as r:
|
||||
data = json.loads(r.read().decode())
|
||||
add_usage(model, data.get("usage", {}), kind="generate")
|
||||
return data["choices"][0]["message"].get("content") or ""
|
||||
content = data["choices"][0]["message"].get("content") or ""
|
||||
if content.strip():
|
||||
return content
|
||||
# empty content (output limit reached in reasoning): retry once more with the next attempt
|
||||
if attempt == 3:
|
||||
return ""
|
||||
continue
|
||||
except Exception as e: # noqa: BLE001
|
||||
if getattr(e, "code", 500) < 500 and getattr(e, "code", 500) != 429:
|
||||
raise
|
||||
@@ -103,8 +128,109 @@ def chat(model, messages, base_url):
|
||||
def parse_bundle(text):
|
||||
text = text.strip()
|
||||
text = re.sub(r"^```(json)?\s*|\s*```$", "", text)
|
||||
start, end = text.find("{"), text.rfind("}")
|
||||
return json.loads(text[start:end + 1])
|
||||
# raw_decode stops after the first complete object; text after it (a note, a second object) is ignored
|
||||
return json.JSONDecoder().raw_decode(text[text.find("{"):])[0]
|
||||
|
||||
|
||||
NAME_RE = re.compile(r"^\s*(?:CLASS-)?(?:METHODS|METHOD|DATA|TYPES|CONSTANTS|FORM|CLASS|INTERFACE)\b:?\s+(\w+)",
|
||||
re.I | re.M)
|
||||
# reserved words seen in activation errors, plus common SQL keywords
|
||||
RESERVED = {"HOURS", "MODE", "ORDER", "GROUP", "DATE", "TIME", "VALUE", "USER", "KEY", "COUNT", "SUM", "MIN",
|
||||
"MAX", "AVG", "SELECT", "FROM", "WHERE", "TABLE", "VIEW", "UNION", "JOIN", "LEVEL", "SIZE", "TYPE",
|
||||
"INDEX", "CASE", "WHEN", "THEN", "ELSE", "END", "AS", "ON", "BY", "DESC", "ASC", "DAYS", "MINUTES",
|
||||
"SECONDS", "YEAR", "MONTH", "DAY", "LIMIT", "OFFSET", "PARAMETERS"}
|
||||
GLOBAL_TEST_CLASS = re.compile(r"\A\s*(?:\*[^\n]*\n|\"[^\n]*\n|\s)*CLASS\s+\S+\s+DEFINITION[^.]*FOR\s+TESTING",
|
||||
re.I)
|
||||
DDL_KIND = re.compile(r"^\s*define\s+(?:root\s+)?(table|view|structure)\b", re.I | re.M)
|
||||
|
||||
|
||||
def lint_files(b):
|
||||
"""Static checks that need no SAP system: identifier length, seed type against DDL source."""
|
||||
errs = []
|
||||
for rel, src in b.get("files", {}).items():
|
||||
if not rel.endswith(".abap"):
|
||||
continue
|
||||
for name in set(NAME_RE.findall(src.replace("{{P}}", "Z0000000_").replace("{{p}}", "z0000000_"))):
|
||||
if len(name) > 30:
|
||||
errs.append(f"{rel}: name {name} has {len(name)} characters (ABAP max 30)")
|
||||
t = b.get("task", {})
|
||||
oos = {n.upper() for n in t.get("out_of_scope", [])}
|
||||
test_names = {o.get("name", "").upper() for o in t.get("hidden_tests", [])}
|
||||
for o in t.get("reference", []):
|
||||
name = o.get("name", "")
|
||||
if name.upper() in oos:
|
||||
errs.append(f"reference object {name} is also in out_of_scope; the solution must change it")
|
||||
if o.get("type") == "CLAS" and GLOBAL_TEST_CLASS.search(b.get("files", {}).get(o.get("file", ""), "")):
|
||||
test_names.add(name.upper())
|
||||
for c in t.get("contract", []):
|
||||
if c.get("name", "").upper() in test_names:
|
||||
errs.append(f"contract contains the test class {c.get('name')}; only objects that the hidden "
|
||||
"tests call belong in the contract")
|
||||
for o in t.get("reference", []):
|
||||
if (o.get("type") == "CLAS" and o.get("name", "").upper() not in test_names
|
||||
and not o.get("testclasses_file")
|
||||
and not re.search(r"INHERITING\s+FROM\s+\S*CX_", b.get("files", {}).get(o.get("file", ""), ""),
|
||||
re.I)):
|
||||
errs.append(f"reference class {o.get('name')} has no testclasses_file (local unit tests)")
|
||||
for rel, src in b.get("files", {}).items():
|
||||
if DDL_KIND.search(src):
|
||||
for f in re.findall(r"^\s*(?:key\s+)?(\w+)\s*:", src, re.I | re.M):
|
||||
if f.upper() in RESERVED:
|
||||
errs.append(f"{rel}: field name {f} is a reserved word; choose another name")
|
||||
for k in ("seed", "reference"):
|
||||
for o in t.get(k, []):
|
||||
m = DDL_KIND.search(b.get("files", {}).get(o.get("file", ""), ""))
|
||||
if not m:
|
||||
continue
|
||||
want = {"table": "TABL", "structure": "TABL", "view": "DDLS"}[m.group(1).lower()]
|
||||
if o.get("type") != want:
|
||||
errs.append(f"{k}: {o.get('name')} has type {o.get('type')}, but its source is "
|
||||
f"'define {m.group(1)}'; use type {want}")
|
||||
return errs
|
||||
|
||||
|
||||
def abaplint_files(b):
|
||||
"""Parser errors of ABAP sources (classes, interfaces, programs) found by local abaplint.
|
||||
SAP often reports only "save operation failed" for these; abaplint gives the line.
|
||||
Only parser_error: check_syntax gives false errors for standard objects abaplint does not know."""
|
||||
t, files = b.get("task", {}), b.get("files", {})
|
||||
ext = {"CLAS": "clas", "INTF": "intf", "PROG": "prog"}
|
||||
version = t.get("release_target") if t.get("release_target") in ABAPLINT_VERSIONS else "v758"
|
||||
d = tempfile.mkdtemp(prefix="genlint_")
|
||||
origin = {}
|
||||
try:
|
||||
os.makedirs(os.path.join(d, "src"))
|
||||
for k in ("seed", "reference", "hidden_tests"):
|
||||
for o in t.get(k, []):
|
||||
if o.get("type") not in ext:
|
||||
continue
|
||||
name = re.sub(r"\W", "_", o.get("name", "").replace("{{P}}", "Z0000000_")).lower()
|
||||
for fk, suffix in (("file", ""), ("testclasses_file", ".testclasses")):
|
||||
if o.get(fk) in files:
|
||||
fn = f"{name}.{ext[o['type']]}{suffix}.abap"
|
||||
origin[fn] = o[fk]
|
||||
src = files[o[fk]].replace("{{P}}", "Z0000000_").replace("{{p}}", "z0000000_")
|
||||
open(os.path.join(d, "src", fn), "w").write(src)
|
||||
if not origin:
|
||||
return []
|
||||
cfg = {"global": {"files": "/src/**/*.*"}, "dependencies": [],
|
||||
"syntax": {"version": version, "errorNamespace": "^(Z|Y)"}, "rules": {"parser_error": True}}
|
||||
json.dump(cfg, open(os.path.join(d, "abaplint.json"), "w"))
|
||||
p = subprocess.run([ABAPLINT, "abaplint.json", "-f", "json"], cwd=d, capture_output=True, text=True,
|
||||
timeout=300)
|
||||
issues = json.loads(p.stdout or "[]")
|
||||
except Exception: # noqa: BLE001 the pre-check is optional; SAP validation follows
|
||||
return []
|
||||
finally:
|
||||
shutil.rmtree(d, ignore_errors=True)
|
||||
out = []
|
||||
for i in issues:
|
||||
f = i.get("file", "")
|
||||
fn = os.path.basename(f.get("filename", "") if isinstance(f, dict) else str(f))
|
||||
if i.get("key") == "parser_error" and fn in origin:
|
||||
out.append(f"{origin[fn]} line {i.get('start', {}).get('row')}: syntax error for release {version} "
|
||||
f"(abaplint): {i.get('description')}")
|
||||
return out[:20]
|
||||
|
||||
|
||||
def check_bundle(b):
|
||||
@@ -165,7 +291,8 @@ def write_errors(run_dir):
|
||||
|
||||
|
||||
def failure_summary(rep):
|
||||
out = {"reference_write_errors": write_errors(rep.get("_dir")),"gates": rep.get("gates"), "score": rep.get("score"), "setup": rep.get("setup"),
|
||||
out = {"reference_write_errors": write_errors(rep.get("_dir")), "gates": rep.get("gates"),
|
||||
"contract_check": rep.get("contract_check"), "score": rep.get("score"), "setup": rep.get("setup"),
|
||||
"atc": rep.get("atc"), "abaplint": rep.get("abaplint"), "own_tests": rep.get("own_tests"),
|
||||
"hidden_failed": [d for d in rep.get("hidden_tests", {}).get("detail", []) if not d["ok"]],
|
||||
"hidden_install": rep.get("hidden_tests", {}).get("install")}
|
||||
@@ -189,11 +316,14 @@ def generate(task_id, pool, object_type, category, difficulty, model, base_url,
|
||||
messages.append({"role": "assistant", "content": text})
|
||||
try:
|
||||
b = parse_bundle(text)
|
||||
errs = check_bundle(b)
|
||||
found = {"structure": check_bundle(b), "static": lint_files(b), "abaplint": abaplint_files(b)}
|
||||
except Exception as e: # noqa: BLE001
|
||||
b, errs = None, [f"invalid JSON: {e}"]
|
||||
b, found = None, {"json": [f"invalid JSON: {e}"]}
|
||||
errs = [e for v in found.values() for e in v]
|
||||
if errs:
|
||||
log["attempts"].append({"stage": "bundle", "errors": errs})
|
||||
# one entry per source, so a batch shows what the checks found before SAP
|
||||
log["attempts"].append({"stage": "bundle", "errors": errs,
|
||||
"by_check": {k: len(v) for k, v in found.items() if v}})
|
||||
messages.append({"role": "user", "content": "Fix these problems and return the full bundle again:\n"
|
||||
+ "\n".join(errs)})
|
||||
continue
|
||||
|
||||
@@ -14,25 +14,33 @@ PLAN = [
|
||||
("PROG", "B"), ("PROG", "B"), ("PROG", "E"),
|
||||
("DDLS", "B"), ("DDLS", "B"), ("DDLS", "B"), ("DDLS", "B"), ("DDLS", "B"),
|
||||
]
|
||||
# hard batch after the pilot: the types that failed in the pilot (2026-10-03)
|
||||
HARD = [("CLAS", "D", None), ("CLAS", "E", "legacy price or freight calculation with packed numbers"),
|
||||
("DDLS", "B", "view with input parameters"), ("DDLS", "B", "UNION of two tables with a literal source element"),
|
||||
("FUNC", "A", None)]
|
||||
DDLS_TOPICS = ["join of two tables and a calculated element", "association and path expression",
|
||||
"aggregation with CASE and GROUP BY", "view with input parameters",
|
||||
"UNION of two tables with a literal source element"]
|
||||
|
||||
|
||||
def main():
|
||||
"""python3 -m harness.pilot FIRST_ID [hard RUN_BASE]"""
|
||||
load_env(os.path.join(ROOT, ".env"))
|
||||
first = int(sys.argv[1]) if len(sys.argv) > 1 else 2
|
||||
hard = len(sys.argv) > 2 and sys.argv[2] == "hard"
|
||||
run_base = int(sys.argv[3]) if len(sys.argv) > 3 else 1100
|
||||
name = "hard" if hard else "pilot"
|
||||
plan = HARD if hard else [(o, c, None) for o, c in PLAN]
|
||||
results = []
|
||||
d = 0
|
||||
for k, (otype, cat) in enumerate(PLAN):
|
||||
for k, (otype, cat, topic) in enumerate(plan):
|
||||
tid = f"G{first + k:04d}"
|
||||
topic = None
|
||||
if otype == "DDLS":
|
||||
if otype == "DDLS" and not hard:
|
||||
topic, d = DDLS_TOPICS[d], d + 1
|
||||
try:
|
||||
log = generate(tid, "eval", otype, cat, 2, "deepseek-v4.1-flash:cloud",
|
||||
os.environ.get("LLM_BASE_URL", "http://127.0.0.1:11434/v1"),
|
||||
1100 + 10 * k, topic)
|
||||
run_base + 10 * k, topic)
|
||||
except BudgetExceeded as e:
|
||||
print("BUDGET", e, flush=True)
|
||||
break
|
||||
@@ -41,7 +49,7 @@ def main():
|
||||
log["spent_total"] = spent()
|
||||
results.append(log)
|
||||
print(json.dumps(log), flush=True)
|
||||
json.dump(results, open(os.path.join(ROOT, "runs", "gen", "pilot.json"), "w"), indent=1)
|
||||
json.dump(results, open(os.path.join(ROOT, "runs", "gen", f"{name}.json"), "w"), indent=1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -23,6 +23,22 @@ DELETE_ORDER = ["CLAS", "INTF", "PROG", "FUNC", "FUGR", "SRVD", "DDLX", "DCLS",
|
||||
SOURCE_TYPES = ("CLAS", "INTF", "PROG", "FUNC", "DDLS", "DCLS", "DDLX", "TABL")
|
||||
|
||||
|
||||
def _cds_elements(src):
|
||||
"""Element names of a CDS view select list (alias, else last path component). Used when a
|
||||
SELECT on the view is not possible, for example a view with parameters."""
|
||||
body = re.sub(r"//[^\n]*|/\*.*?\*/", "", src or "", flags=re.S)
|
||||
m = re.search(r"\{(.*)\}", body, re.S)
|
||||
out = set()
|
||||
for el in re.split(r",(?![^()]*\))", m.group(1) if m else ""):
|
||||
el = re.sub(r"@\S+(\s*:\s*('[^']*'|\S+))?", "", el).strip()
|
||||
if not el or el.lower().startswith("_") and " as " not in el.lower():
|
||||
continue
|
||||
alias = re.search(r"\bas\s+(\w+)\s*$", el, re.I)
|
||||
name = alias.group(1) if alias else re.split(r"[.\s]", el.split()[-1])[-1]
|
||||
out.add(name.upper())
|
||||
return out
|
||||
|
||||
|
||||
def _obj_args(otype, name, fg=None):
|
||||
a = {"objectType": otype, "objectName": name}
|
||||
if fg:
|
||||
@@ -222,26 +238,36 @@ class Runner:
|
||||
g1.append({"name": c["name"], "ok": ok})
|
||||
g["G1_active"] = all(x["ok"] for x in g1) and bool(g1)
|
||||
g2 = True
|
||||
g2_detail = []
|
||||
for c in contract:
|
||||
src = sources.get((c["type"], c["name"].upper()), "")
|
||||
if c.get("implements") and not re.search(rf"INTERFACES\s+{re.escape(c['implements'])}\b", src, re.I):
|
||||
g2 = False
|
||||
g2_detail.append(f"{c['name']}: does not implement {c['implements']}")
|
||||
if c["type"] == "FUNC": # signature: every parameter with its type in the FUNCTION header
|
||||
header = src.split(".", 1)[0]
|
||||
for prm in c.get("params", []):
|
||||
if not re.search(rf"\b{re.escape(prm['name'])}\b\)?\s+TYPE\s+{re.escape(prm['type'])}\b",
|
||||
header, re.I):
|
||||
g2 = False
|
||||
g2_detail.append(f"{c['name']}: no parameter {prm['name']} TYPE {prm['type']} "
|
||||
"in the FUNCTION header")
|
||||
if c["type"] == "PROG": # selection screen parameters
|
||||
for prm in c.get("parameters", []):
|
||||
if not re.search(rf"(PARAMETERS|SELECT-OPTIONS)\s*:?[^.]*\b{re.escape(prm)}\b", src, re.I):
|
||||
g2 = False
|
||||
g2_detail.append(f"{c['name']}: no selection screen parameter {prm}")
|
||||
if c["type"] == "DDLS" and c.get("fields") and g["G1_active"]:
|
||||
_, q = mcp.call("sap_sql_query", {"query": f"SELECT * FROM {c['name']}", "maxRows": 1})
|
||||
cols = {col.get("name", "").upper() for col in (_json(q) or {}).get("columns", [])}
|
||||
if not {f.upper() for f in c["fields"]} <= cols:
|
||||
if not cols: # view with parameters: SELECT without parameters fails
|
||||
cols = _cds_elements(src)
|
||||
missing = {f.upper() for f in c["fields"]} - cols
|
||||
if missing:
|
||||
g2 = False
|
||||
g2_detail.append(f"{c['name']}: CDS elements missing: {sorted(missing)}")
|
||||
g["G2_contract"] = g2 and g["G1_active"]
|
||||
rep["contract_check"] = {"g1": g1, "g2": g2_detail}
|
||||
g["G4_out_of_scope"] = all(
|
||||
_norm(self._source(mcp, o["type"], o["name"], o.get("functionGroup")))
|
||||
== _norm(seed_src[o["name"].upper()])
|
||||
|
||||
29
tasks_gen/eval/G0003/generation.json
Normal file
29
tasks_gen/eval/G0003/generation.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"id": "G0003",
|
||||
"pool": "eval",
|
||||
"object_type": "CLAS",
|
||||
"category": "B",
|
||||
"attempts": [
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": null,
|
||||
"null": null
|
||||
},
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": null,
|
||||
"null": null
|
||||
},
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 0,
|
||||
"null": 0
|
||||
},
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 100.0,
|
||||
"null": 0
|
||||
}
|
||||
],
|
||||
"accepted": true
|
||||
}
|
||||
119
tasks_gen/eval/G0003/hidden/t02_hidden.clas.abap
Normal file
119
tasks_gen/eval/G0003/hidden/t02_hidden.clas.abap
Normal file
@@ -0,0 +1,119 @@
|
||||
CLASS {{P}}T02_HIDDEN DEFINITION PUBLIC FINAL CREATE PUBLIC
|
||||
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
PRIVATE SECTION.
|
||||
CLASS-DATA mo_environment TYPE REF TO if_osql_test_environment.
|
||||
DATA mo_cut TYPE REF TO {{P}}COURSE_BOOKING.
|
||||
|
||||
CLASS-METHODS class_setup.
|
||||
CLASS-METHODS class_teardown.
|
||||
METHODS setup.
|
||||
METHODS fill_test_data.
|
||||
|
||||
METHODS free_seats_open_course FOR TESTING.
|
||||
METHODS free_seats_never_neg FOR TESTING.
|
||||
METHODS free_seats_unknown FOR TESTING.
|
||||
METHODS bookable_exact_free FOR TESTING.
|
||||
METHODS not_bookable_too_many FOR TESTING.
|
||||
METHODS not_bookable_unknown FOR TESTING.
|
||||
METHODS not_bookable_zero_seats FOR TESTING.
|
||||
METHODS list_filter_cat_seats FOR TESTING.
|
||||
METHODS list_sorted_by_course FOR TESTING.
|
||||
METHODS list_empty_without_match FOR TESTING.
|
||||
METHODS list_excludes_full FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{P}}T02_HIDDEN IMPLEMENTATION.
|
||||
METHOD class_setup.
|
||||
mo_environment = cl_osql_test_environment=>create( VALUE #( ( '{{P}}COURSE' ) ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD class_teardown.
|
||||
mo_environment->destroy( ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD setup.
|
||||
mo_environment->clear_doubles( ).
|
||||
fill_test_data( ).
|
||||
mo_cut = NEW {{P}}COURSE_BOOKING( ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD fill_test_data.
|
||||
DATA lt_course TYPE STANDARD TABLE OF {{P}}COURSE WITH EMPTY KEY.
|
||||
lt_course = VALUE #(
|
||||
( mandt = sy-mandt course_id = 'ABAP101' category = 'IT' title = 'ABAP Basics' capacity = 20 booked = 5 )
|
||||
( mandt = sy-mandt course_id = 'ABAP102' category = 'IT' title = 'ABAP Advanced' capacity = 10 booked = 10 )
|
||||
( mandt = sy-mandt course_id = 'ABAP103' category = 'IT' title = 'ABAP Expert' capacity = 12 booked = 15 )
|
||||
( mandt = sy-mandt course_id = 'SQL101' category = 'DB' title = 'SQL Basics' capacity = 30 booked = 10 )
|
||||
( mandt = sy-mandt course_id = 'SQL102' category = 'DB' title = 'SQL Tuning' capacity = 8 booked = 8 )
|
||||
( mandt = sy-mandt course_id = 'HR101' category = 'HR' title = 'HR Basics' capacity = 6 booked = 1 ) ).
|
||||
INSERT {{P}}COURSE FROM TABLE @lt_course.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD free_seats_open_course.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 15
|
||||
act = mo_cut->get_free_seats( 'ABAP101' )
|
||||
msg = 'ABAP101 must have 15 free seats' ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD free_seats_never_neg.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 0
|
||||
act = mo_cut->get_free_seats( 'ABAP102' )
|
||||
msg = 'full course' ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 0
|
||||
act = mo_cut->get_free_seats( 'ABAP103' )
|
||||
msg = 'overbooked course' ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD free_seats_unknown.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 0
|
||||
act = mo_cut->get_free_seats( 'XX999' ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD bookable_exact_free.
|
||||
cl_abap_unit_assert=>assert_true( mo_cut->is_bookable( iv_course_id = 'SQL101' iv_seats = 20 ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD not_bookable_too_many.
|
||||
cl_abap_unit_assert=>assert_false( mo_cut->is_bookable( iv_course_id = 'SQL101' iv_seats = 21 ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD not_bookable_unknown.
|
||||
cl_abap_unit_assert=>assert_false( mo_cut->is_bookable( iv_course_id = 'XX999' iv_seats = 1 ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD not_bookable_zero_seats.
|
||||
cl_abap_unit_assert=>assert_false( mo_cut->is_bookable( iv_course_id = 'SQL101' iv_seats = 0 ) ).
|
||||
cl_abap_unit_assert=>assert_false( mo_cut->is_bookable( iv_course_id = 'SQL101' iv_seats = -5 ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD list_filter_cat_seats.
|
||||
DATA(lt_result) = mo_cut->list_bookable_courses( iv_category = 'DB' iv_seats = 5 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 'SQL101' act = lt_result[ 1 ]-course_id ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 'SQL Basics' act = lt_result[ 1 ]-title ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 20 act = lt_result[ 1 ]-free_seats ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD list_sorted_by_course.
|
||||
DATA(lt_result) = mo_cut->list_bookable_courses( iv_category = 'IT' iv_seats = 0 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_result ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 'ABAP101' act = lt_result[ 1 ]-course_id ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 'ABAP102' act = lt_result[ 2 ]-course_id ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 'ABAP103' act = lt_result[ 3 ]-course_id ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_result[ 3 ]-free_seats ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD list_empty_without_match.
|
||||
DATA(lt_hr) = mo_cut->list_bookable_courses( iv_category = 'HR' iv_seats = 6 ).
|
||||
cl_abap_unit_assert=>assert_initial( lt_hr ).
|
||||
DATA(lt_unknown) = mo_cut->list_bookable_courses( iv_category = 'XX' iv_seats = 1 ).
|
||||
cl_abap_unit_assert=>assert_initial( lt_unknown ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD list_excludes_full.
|
||||
DATA(lt_result) = mo_cut->list_bookable_courses( iv_category = 'IT' iv_seats = 1 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 'ABAP101' act = lt_result[ 1 ]-course_id ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
94
tasks_gen/eval/G0003/reference/course_booking.clas.abap
Normal file
94
tasks_gen/eval/G0003/reference/course_booking.clas.abap
Normal file
@@ -0,0 +1,94 @@
|
||||
CLASS {{P}}COURSE_BOOKING DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
||||
PUBLIC SECTION.
|
||||
TYPES ty_course_id TYPE c LENGTH 10.
|
||||
TYPES ty_category TYPE c LENGTH 10.
|
||||
TYPES:
|
||||
BEGIN OF ty_course_info,
|
||||
course_id TYPE ty_course_id,
|
||||
title TYPE c LENGTH 40,
|
||||
free_seats TYPE i,
|
||||
END OF ty_course_info.
|
||||
TYPES tt_course_info TYPE STANDARD TABLE OF ty_course_info WITH EMPTY KEY.
|
||||
|
||||
METHODS get_free_seats
|
||||
IMPORTING iv_course_id TYPE ty_course_id
|
||||
RETURNING VALUE(rv_free_seats) TYPE i.
|
||||
|
||||
METHODS is_bookable
|
||||
IMPORTING iv_course_id TYPE ty_course_id
|
||||
iv_seats TYPE i
|
||||
RETURNING VALUE(rv_bookable) TYPE abap_bool.
|
||||
|
||||
METHODS list_bookable_courses
|
||||
IMPORTING iv_category TYPE ty_category
|
||||
iv_seats TYPE i
|
||||
RETURNING VALUE(rt_courses) TYPE tt_course_info.
|
||||
|
||||
PRIVATE SECTION.
|
||||
TYPES:
|
||||
BEGIN OF ty_course,
|
||||
course_id TYPE ty_course_id,
|
||||
title TYPE c LENGTH 40,
|
||||
capacity TYPE i,
|
||||
booked TYPE i,
|
||||
END OF ty_course.
|
||||
|
||||
METHODS read_course
|
||||
IMPORTING iv_course_id TYPE ty_course_id
|
||||
RETURNING VALUE(rs_course) TYPE ty_course.
|
||||
|
||||
METHODS calculate_free_seats
|
||||
IMPORTING is_course TYPE ty_course
|
||||
RETURNING VALUE(rv_free) TYPE i.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{P}}COURSE_BOOKING IMPLEMENTATION.
|
||||
METHOD get_free_seats.
|
||||
rv_free_seats = calculate_free_seats( read_course( iv_course_id ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD is_bookable.
|
||||
IF iv_seats < 1.
|
||||
rv_bookable = abap_false.
|
||||
RETURN.
|
||||
ENDIF.
|
||||
rv_bookable = xsdbool( iv_seats <= calculate_free_seats( read_course( iv_course_id ) ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD list_bookable_courses.
|
||||
DATA lt_course TYPE STANDARD TABLE OF ty_course WITH EMPTY KEY.
|
||||
|
||||
SELECT course_id, title, capacity, booked
|
||||
FROM {{P}}COURSE
|
||||
WHERE category = @iv_category
|
||||
INTO TABLE @lt_course.
|
||||
|
||||
LOOP AT lt_course INTO DATA(ls_course).
|
||||
DATA(lv_free) = calculate_free_seats( ls_course ).
|
||||
IF lv_free < iv_seats.
|
||||
CONTINUE.
|
||||
ENDIF.
|
||||
rt_courses = VALUE #( BASE rt_courses
|
||||
( course_id = ls_course-course_id
|
||||
title = ls_course-title
|
||||
free_seats = lv_free ) ).
|
||||
ENDLOOP.
|
||||
|
||||
SORT rt_courses BY course_id.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD read_course.
|
||||
SELECT SINGLE course_id, title, capacity, booked
|
||||
FROM {{P}}COURSE
|
||||
WHERE course_id = @iv_course_id
|
||||
INTO @rs_course.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD calculate_free_seats.
|
||||
rv_free = is_course-capacity - is_course-booked.
|
||||
IF rv_free < 0.
|
||||
rv_free = 0.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
@@ -0,0 +1,63 @@
|
||||
CLASS ltc_course_booking DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
PRIVATE SECTION.
|
||||
CLASS-DATA mo_environment TYPE REF TO if_osql_test_environment.
|
||||
DATA mo_cut TYPE REF TO {{P}}COURSE_BOOKING.
|
||||
|
||||
CLASS-METHODS class_setup.
|
||||
CLASS-METHODS class_teardown.
|
||||
METHODS setup.
|
||||
METHODS fill_test_data.
|
||||
|
||||
METHODS free_seats_open_course FOR TESTING.
|
||||
METHODS free_seats_full_course FOR TESTING.
|
||||
METHODS bookable_enough_seats FOR TESTING.
|
||||
METHODS not_bookable_full FOR TESTING.
|
||||
METHODS list_matching_courses FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS ltc_course_booking IMPLEMENTATION.
|
||||
METHOD class_setup.
|
||||
mo_environment = cl_osql_test_environment=>create( VALUE #( ( '{{P}}COURSE' ) ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD class_teardown.
|
||||
mo_environment->destroy( ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD setup.
|
||||
mo_environment->clear_doubles( ).
|
||||
fill_test_data( ).
|
||||
mo_cut = NEW {{P}}COURSE_BOOKING( ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD fill_test_data.
|
||||
DATA lt_course TYPE STANDARD TABLE OF {{P}}COURSE WITH EMPTY KEY.
|
||||
lt_course = VALUE #(
|
||||
( mandt = sy-mandt course_id = 'ABAP101' category = 'IT' title = 'ABAP Basics' capacity = 20 booked = 5 )
|
||||
( mandt = sy-mandt course_id = 'SQL101' category = 'DB' title = 'SQL Basics' capacity = 30 booked = 30 ) ).
|
||||
INSERT {{P}}COURSE FROM TABLE @lt_course.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD free_seats_open_course.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 15 act = mo_cut->get_free_seats( 'ABAP101' ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD free_seats_full_course.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 0 act = mo_cut->get_free_seats( 'SQL101' ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD bookable_enough_seats.
|
||||
cl_abap_unit_assert=>assert_true( mo_cut->is_bookable( iv_course_id = 'ABAP101' iv_seats = 10 ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD not_bookable_full.
|
||||
cl_abap_unit_assert=>assert_false( mo_cut->is_bookable( iv_course_id = 'SQL101' iv_seats = 1 ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD list_matching_courses.
|
||||
DATA(lt_result) = mo_cut->list_bookable_courses( iv_category = 'IT' iv_seats = 1 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 'ABAP101' act = lt_result[ 1 ]-course_id ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
8
tasks_gen/eval/G0003/seed/course.ddls.abap
Normal file
8
tasks_gen/eval/G0003/seed/course.ddls.abap
Normal file
@@ -0,0 +1,8 @@
|
||||
define table {{P}}COURSE {
|
||||
key mandt : abap.clnt not null;
|
||||
key course_id : abap.char(10) not null;
|
||||
category : abap.char(10);
|
||||
title : abap.char(40);
|
||||
capacity : abap.int4;
|
||||
booked : abap.int4;
|
||||
}
|
||||
9
tasks_gen/eval/G0003/seed/course.ddls.asddls
Normal file
9
tasks_gen/eval/G0003/seed/course.ddls.asddls
Normal file
@@ -0,0 +1,9 @@
|
||||
@EndUserText.label : 'Training courses'
|
||||
define table {{P}}COURSE {
|
||||
key mandt : abap.clnt not null;
|
||||
key course_id : abap.char(10) not null;
|
||||
category : abap.char(10);
|
||||
title : abap.char(40);
|
||||
capacity : abap.int4;
|
||||
booked : abap.int4;
|
||||
}
|
||||
13
tasks_gen/eval/G0003/seed/course.tabl.asddls
Normal file
13
tasks_gen/eval/G0003/seed/course.tabl.asddls
Normal file
@@ -0,0 +1,13 @@
|
||||
@EndUserText.label : 'Training courses'
|
||||
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
|
||||
@AbapCatalog.tableCategory : #TRANSPARENT
|
||||
@AbapCatalog.deliveryClass : #A
|
||||
@AbapCatalog.dataMaintenance : #RESTRICTED
|
||||
define table {{P}}COURSE {
|
||||
key mandt : abap.clnt not null;
|
||||
key course_id : abap.char(10) not null;
|
||||
category : abap.char(10);
|
||||
title : abap.char(40);
|
||||
capacity : abap.int4;
|
||||
booked : abap.int4;
|
||||
}
|
||||
24
tasks_gen/eval/G0003/seed/seed_courses.clas.abap
Normal file
24
tasks_gen/eval/G0003/seed/seed_courses.clas.abap
Normal file
@@ -0,0 +1,24 @@
|
||||
CLASS {{P}}SEED_COURSES DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
||||
PUBLIC SECTION.
|
||||
INTERFACES if_oo_adt_classrun.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{P}}SEED_COURSES IMPLEMENTATION.
|
||||
METHOD if_oo_adt_classrun~main.
|
||||
DELETE FROM {{P}}COURSE.
|
||||
|
||||
DATA lt_course TYPE STANDARD TABLE OF {{P}}COURSE WITH EMPTY KEY.
|
||||
lt_course = VALUE #(
|
||||
( mandt = sy-mandt course_id = 'ABAP101' category = 'IT' title = 'ABAP Basics' capacity = 20 booked = 5 )
|
||||
( mandt = sy-mandt course_id = 'ABAP102' category = 'IT' title = 'ABAP Advanced' capacity = 10 booked = 10 )
|
||||
( mandt = sy-mandt course_id = 'ABAP103' category = 'IT' title = 'ABAP Expert' capacity = 12 booked = 15 )
|
||||
( mandt = sy-mandt course_id = 'SQL101' category = 'DB' title = 'SQL Basics' capacity = 30 booked = 10 )
|
||||
( mandt = sy-mandt course_id = 'SQL102' category = 'DB' title = 'SQL Tuning' capacity = 8 booked = 8 )
|
||||
( mandt = sy-mandt course_id = 'HR101' category = 'HR' title = 'HR Basics' capacity = 6 booked = 1 ) ).
|
||||
|
||||
INSERT {{P}}COURSE FROM TABLE @lt_course.
|
||||
|
||||
out->write( |Courses written: { sy-dbcnt }| ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
68
tasks_gen/eval/G0003/spec.md
Normal file
68
tasks_gen/eval/G0003/spec.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# 1. Goal
|
||||
A training provider sells seats in courses. The booking program needs one service that
|
||||
answers three questions:
|
||||
- How many seats are free in a course?
|
||||
- Can we book a given number of seats in a course?
|
||||
- Which courses of a category still have enough free seats?
|
||||
|
||||
# 2. Open questions
|
||||
None.
|
||||
|
||||
# 3. Context
|
||||
- The database table {{P}}COURSE exists in package $TMP. It has these columns:
|
||||
MANDT (client), COURSE_ID (CHAR 10, key), CATEGORY (CHAR 10), TITLE (CHAR 40),
|
||||
CAPACITY (INT4), BOOKED (INT4).
|
||||
- CAPACITY is the number of seats of the course. BOOKED is the number of seats that
|
||||
are already sold.
|
||||
- The class {{P}}SEED_COURSES fills the table with example data. It is active.
|
||||
|
||||
# 4. Contract
|
||||
- Create the class {{P}}COURSE_BOOKING in package $TMP.
|
||||
- The class is public, final, and has a public constructor without parameters.
|
||||
- The class has these public types:
|
||||
- TY_COURSE_ID, type C, length 10.
|
||||
- TY_CATEGORY, type C, length 10.
|
||||
- TY_COURSE_INFO, a structure with the components COURSE_ID (type TY_COURSE_ID),
|
||||
TITLE (type C, length 40) and FREE_SEATS (type I).
|
||||
- TT_COURSE_INFO, a standard table of TY_COURSE_INFO with an empty key.
|
||||
- The class has these public methods:
|
||||
- GET_FREE_SEATS, importing IV_COURSE_ID type TY_COURSE_ID,
|
||||
returning RV_FREE_SEATS type I.
|
||||
- IS_BOOKABLE, importing IV_COURSE_ID type TY_COURSE_ID and IV_SEATS type I,
|
||||
returning RV_BOOKABLE type ABAP_BOOL.
|
||||
- LIST_BOOKABLE_COURSES, importing IV_CATEGORY type TY_CATEGORY and IV_SEATS type I,
|
||||
returning RT_COURSES type TT_COURSE_INFO.
|
||||
- Do not add other public methods.
|
||||
|
||||
# 5. Business rules
|
||||
Free seats
|
||||
1. Read the course with the course ID.
|
||||
2. If the table has no course with this ID, the free seats are 0.
|
||||
3. The free seats are the capacity minus the booked seats.
|
||||
4. If the capacity minus the booked seats is less than 0, the free seats are 0.
|
||||
|
||||
GET_FREE_SEATS returns the free seats of the course.
|
||||
|
||||
IS_BOOKABLE
|
||||
5. If IV_SEATS is less than 1, the result is false.
|
||||
6. If the table has no course with this ID, the result is false.
|
||||
7. The result is true if IV_SEATS is less than or equal to the free seats of the
|
||||
course. Else the result is false.
|
||||
|
||||
LIST_BOOKABLE_COURSES
|
||||
8. Read all courses with the category IV_CATEGORY.
|
||||
9. Use only the courses whose free seats are greater than or equal to IV_SEATS.
|
||||
10. Sort the result in ascending order by COURSE_ID.
|
||||
11. Each result row has the COURSE_ID, the TITLE and the free seats of the course.
|
||||
12. If no course matches, the result is empty.
|
||||
|
||||
# 6. Constraints
|
||||
- Release target: 8.16.
|
||||
- Coding standards: Clean ABAP. Method length below 40 statements. No global variables.
|
||||
No comment that restates the code.
|
||||
- Out of scope: do not change the table {{P}}COURSE and the class {{P}}SEED_COURSES.
|
||||
|
||||
# 7. Acceptance
|
||||
- The class is active and has no syntax error.
|
||||
- The hidden tests pass.
|
||||
- Write your own ABAP Unit tests for the class.
|
||||
58
tasks_gen/eval/G0003/task.json
Normal file
58
tasks_gen/eval/G0003/task.json
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"id": "G0003",
|
||||
"category": "B",
|
||||
"object_type": "CLAS",
|
||||
"difficulty": 2,
|
||||
"release_target": "v816",
|
||||
"expected_outcome": "implement",
|
||||
"budget": {
|
||||
"max_tool_calls": 60,
|
||||
"max_activations": 15
|
||||
},
|
||||
"seed": [
|
||||
{
|
||||
"type": "TABL",
|
||||
"name": "{{P}}COURSE",
|
||||
"file": "seed/course.tabl.asddls",
|
||||
"description": "Training course table with capacity and booked seats"
|
||||
},
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}SEED_COURSES",
|
||||
"file": "seed/seed_courses.clas.abap",
|
||||
"description": "Fills the course table with example data",
|
||||
"run": true
|
||||
}
|
||||
],
|
||||
"contract": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}COURSE_BOOKING"
|
||||
}
|
||||
],
|
||||
"out_of_scope": [
|
||||
"{{P}}COURSE",
|
||||
"{{P}}SEED_COURSES"
|
||||
],
|
||||
"hidden_tests": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}T02_HIDDEN",
|
||||
"file": "hidden/t02_hidden.clas.abap",
|
||||
"description": "T02 hidden tests"
|
||||
}
|
||||
],
|
||||
"reference": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}COURSE_BOOKING",
|
||||
"file": "reference/course_booking.clas.abap",
|
||||
"description": "Course booking service",
|
||||
"testclasses_file": "reference/course_booking.testclasses.abap"
|
||||
}
|
||||
],
|
||||
"craft_checks": [
|
||||
"method_length",
|
||||
"db_operation_in_loop"
|
||||
]
|
||||
}
|
||||
14
tasks_gen/eval/G0004/generation.json
Normal file
14
tasks_gen/eval/G0004/generation.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"id": "G0004",
|
||||
"pool": "eval",
|
||||
"object_type": "CLAS",
|
||||
"category": "C",
|
||||
"attempts": [
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 100.0,
|
||||
"null": 0
|
||||
}
|
||||
],
|
||||
"accepted": true
|
||||
}
|
||||
207
tasks_gen/eval/G0004/hidden/stock_age_hidden.clas.abap
Normal file
207
tasks_gen/eval/G0004/hidden/stock_age_hidden.clas.abap
Normal file
@@ -0,0 +1,207 @@
|
||||
CLASS {{p}}stock_age_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
|
||||
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
PRIVATE SECTION.
|
||||
CONSTANTS c_key_date TYPE dats VALUE '20240401'.
|
||||
|
||||
DATA mo_cut TYPE REF TO {{p}}if_stock_age.
|
||||
|
||||
METHODS setup.
|
||||
METHODS gr_date_for_age
|
||||
IMPORTING iv_age TYPE i
|
||||
RETURNING VALUE(rv_date) TYPE dats.
|
||||
|
||||
METHODS empty_input FOR TESTING.
|
||||
METHODS single_batch_bucket_01 FOR TESTING.
|
||||
METHODS bucket_boundaries FOR TESTING.
|
||||
METHODS groups_sum_quantity_and_count FOR TESTING.
|
||||
METHODS result_is_sorted FOR TESTING.
|
||||
METHODS invalid_rows_are_ignored FOR TESTING.
|
||||
METHODS negative_and_zero_quantity FOR TESTING.
|
||||
METHODS total_groups_by_material FOR TESTING.
|
||||
METHODS total_is_sorted FOR TESTING.
|
||||
METHODS total_ignores_invalid_rows FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}stock_age_hidden IMPLEMENTATION.
|
||||
|
||||
METHOD setup.
|
||||
mo_cut = NEW {{p}}stock_age( ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD gr_date_for_age.
|
||||
rv_date = c_key_date - iv_age.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD empty_input.
|
||||
DATA(lt_result) = mo_cut->analyze(
|
||||
it_batch = VALUE {{p}}if_stock_age=>tt_batch( )
|
||||
iv_key_date = c_key_date ).
|
||||
|
||||
cl_abap_unit_assert=>assert_initial( act = lt_result ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD single_batch_bucket_01.
|
||||
DATA(lt_result) = mo_cut->analyze(
|
||||
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
|
||||
( material = 'MAT-1' batch = 'B1' quantity = 10
|
||||
gr_date = gr_date_for_age( 0 ) ) )
|
||||
iv_key_date = c_key_date ).
|
||||
|
||||
cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 1 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-material
|
||||
exp = 'MAT-1' ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-bucket
|
||||
exp = {{p}}if_stock_age=>c_bucket-d0_30 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-quantity exp = 10 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-batch_count exp = 1 ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD bucket_boundaries.
|
||||
DATA(lt_result) = mo_cut->analyze(
|
||||
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
|
||||
( material = 'MAT-B' batch = 'B1' quantity = 1 gr_date = gr_date_for_age( 30 ) )
|
||||
( material = 'MAT-B' batch = 'B2' quantity = 1 gr_date = gr_date_for_age( 31 ) )
|
||||
( material = 'MAT-B' batch = 'B3' quantity = 1 gr_date = gr_date_for_age( 60 ) )
|
||||
( material = 'MAT-B' batch = 'B4' quantity = 1 gr_date = gr_date_for_age( 61 ) )
|
||||
( material = 'MAT-B' batch = 'B5' quantity = 1 gr_date = gr_date_for_age( 90 ) )
|
||||
( material = 'MAT-B' batch = 'B6' quantity = 1 gr_date = gr_date_for_age( 91 ) ) )
|
||||
iv_key_date = c_key_date ).
|
||||
|
||||
cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 4 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-bucket
|
||||
exp = {{p}}if_stock_age=>c_bucket-d0_30 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-batch_count exp = 1 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-bucket
|
||||
exp = {{p}}if_stock_age=>c_bucket-d31_60 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-batch_count exp = 2 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 3 ]-bucket
|
||||
exp = {{p}}if_stock_age=>c_bucket-d61_90 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 3 ]-batch_count exp = 2 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 4 ]-bucket
|
||||
exp = {{p}}if_stock_age=>c_bucket-d91_plus ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 4 ]-batch_count exp = 1 ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD groups_sum_quantity_and_count.
|
||||
DATA(lt_result) = mo_cut->analyze(
|
||||
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
|
||||
( material = 'MAT-A' batch = 'B1' quantity = 10 gr_date = gr_date_for_age( 1 ) )
|
||||
( material = 'MAT-A' batch = 'B2' quantity = 5 gr_date = gr_date_for_age( 2 ) )
|
||||
( material = 'MAT-A' batch = 'B3' quantity = 2 gr_date = gr_date_for_age( 200 ) ) )
|
||||
iv_key_date = c_key_date ).
|
||||
|
||||
cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 2 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-bucket
|
||||
exp = {{p}}if_stock_age=>c_bucket-d0_30 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-quantity exp = 15 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-batch_count exp = 2 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-bucket
|
||||
exp = {{p}}if_stock_age=>c_bucket-d91_plus ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-quantity exp = 2 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-batch_count exp = 1 ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD result_is_sorted.
|
||||
DATA(lt_result) = mo_cut->analyze(
|
||||
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
|
||||
( material = 'MAT-C' batch = 'B1' quantity = 1 gr_date = gr_date_for_age( 200 ) )
|
||||
( material = 'MAT-A' batch = 'B2' quantity = 1 gr_date = gr_date_for_age( 40 ) )
|
||||
( material = 'MAT-B' batch = 'B3' quantity = 1 gr_date = gr_date_for_age( 1 ) )
|
||||
( material = 'MAT-A' batch = 'B4' quantity = 1 gr_date = gr_date_for_age( 1 ) )
|
||||
( material = 'MAT-C' batch = 'B5' quantity = 1 gr_date = gr_date_for_age( 1 ) )
|
||||
( material = 'MAT-B' batch = 'B6' quantity = 1 gr_date = gr_date_for_age( 70 ) ) )
|
||||
iv_key_date = c_key_date ).
|
||||
|
||||
DATA(lt_expected) = VALUE {{p}}if_stock_age=>tt_age_row(
|
||||
( material = 'MAT-A' bucket = {{p}}if_stock_age=>c_bucket-d0_30 )
|
||||
( material = 'MAT-A' bucket = {{p}}if_stock_age=>c_bucket-d31_60 )
|
||||
( material = 'MAT-B' bucket = {{p}}if_stock_age=>c_bucket-d0_30 )
|
||||
( material = 'MAT-B' bucket = {{p}}if_stock_age=>c_bucket-d61_90 )
|
||||
( material = 'MAT-C' bucket = {{p}}if_stock_age=>c_bucket-d0_30 )
|
||||
( material = 'MAT-C' bucket = {{p}}if_stock_age=>c_bucket-d91_plus ) ).
|
||||
|
||||
cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 6 ).
|
||||
|
||||
DATA lv_index TYPE i.
|
||||
LOOP AT lt_expected INTO DATA(ls_expected).
|
||||
lv_index = lv_index + 1.
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ lv_index ]-material
|
||||
exp = ls_expected-material ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ lv_index ]-bucket
|
||||
exp = ls_expected-bucket ).
|
||||
ENDLOOP.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD invalid_rows_are_ignored.
|
||||
DATA(lt_result) = mo_cut->analyze(
|
||||
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
|
||||
( material = 'MAT-A' batch = 'B1' quantity = 10 gr_date = gr_date_for_age( 5 ) )
|
||||
( material = 'MAT-A' batch = 'B2' quantity = 99 gr_date = '00000000' )
|
||||
( material = 'MAT-A' batch = 'B3' quantity = 99 gr_date = gr_date_for_age( -5 ) ) )
|
||||
iv_key_date = c_key_date ).
|
||||
|
||||
cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 1 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-quantity exp = 10 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-batch_count exp = 1 ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD negative_and_zero_quantity.
|
||||
DATA(lt_result) = mo_cut->analyze(
|
||||
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
|
||||
( material = 'MAT-A' batch = 'B1' quantity = 10 gr_date = gr_date_for_age( 5 ) )
|
||||
( material = 'MAT-A' batch = 'B2' quantity = -4 gr_date = gr_date_for_age( 6 ) )
|
||||
( material = 'MAT-B' batch = 'B3' quantity = 5 gr_date = gr_date_for_age( 5 ) )
|
||||
( material = 'MAT-B' batch = 'B4' quantity = -5 gr_date = gr_date_for_age( 6 ) ) )
|
||||
iv_key_date = c_key_date ).
|
||||
|
||||
cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 2 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-material exp = 'MAT-A' ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-quantity exp = 6 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-batch_count exp = 2 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-material exp = 'MAT-B' ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-quantity exp = 0 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-batch_count exp = 2 ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD total_groups_by_material.
|
||||
DATA(lt_result) = mo_cut->total_by_material(
|
||||
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
|
||||
( material = 'MAT-A' batch = 'B1' quantity = 10 gr_date = gr_date_for_age( 5 ) )
|
||||
( material = 'MAT-A' batch = 'B2' quantity = 5 gr_date = gr_date_for_age( 100 ) )
|
||||
( material = 'MAT-B' batch = 'B3' quantity = 7 gr_date = gr_date_for_age( 5 ) ) )
|
||||
iv_key_date = c_key_date ).
|
||||
|
||||
cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 2 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-material exp = 'MAT-A' ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-quantity exp = 15 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-batch_count exp = 2 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-material exp = 'MAT-B' ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-quantity exp = 7 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-batch_count exp = 1 ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD total_is_sorted.
|
||||
DATA(lt_result) = mo_cut->total_by_material(
|
||||
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
|
||||
( material = 'MAT-Z' batch = 'B1' quantity = 1 gr_date = gr_date_for_age( 5 ) )
|
||||
( material = 'MAT-A' batch = 'B2' quantity = 1 gr_date = gr_date_for_age( 5 ) )
|
||||
( material = 'MAT-M' batch = 'B3' quantity = 1 gr_date = gr_date_for_age( 5 ) ) )
|
||||
iv_key_date = c_key_date ).
|
||||
|
||||
cl_abap_unit_assert=>assert_equals( act = lines( lt_result ) exp = 3 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-material exp = 'MAT-A' ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-material exp = 'MAT-M' ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 3 ]-material exp = 'MAT-Z' ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD total_ignores_invalid_rows.
|
||||
DATA(lt_result) = mo_cut->total_by_material(
|
||||
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
|
||||
( material = 'MAT-A' batch = 'B1' quantity = 99 gr_date = '00000000' )
|
||||
( material = 'MAT-A' batch = 'B2' quantity = 99 gr_date = gr_date_for_age( -1 ) ) )
|
||||
iv_key_date = c_key_date ).
|
||||
|
||||
cl_abap_unit_assert=>assert_initial( act = lt_result ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
92
tasks_gen/eval/G0004/reference/stock_age.clas.abap
Normal file
92
tasks_gen/eval/G0004/reference/stock_age.clas.abap
Normal file
@@ -0,0 +1,92 @@
|
||||
CLASS {{p}}stock_age DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
||||
PUBLIC SECTION.
|
||||
INTERFACES {{p}}if_stock_age.
|
||||
|
||||
PRIVATE SECTION.
|
||||
TYPES tt_age_group TYPE HASHED TABLE OF {{p}}if_stock_age=>ty_age_row
|
||||
WITH UNIQUE KEY material bucket.
|
||||
TYPES tt_total_group TYPE HASHED TABLE OF {{p}}if_stock_age=>ty_total
|
||||
WITH UNIQUE KEY material.
|
||||
|
||||
METHODS age_in_days
|
||||
IMPORTING is_batch TYPE {{p}}if_stock_age=>ty_batch
|
||||
iv_key_date TYPE dats
|
||||
RETURNING VALUE(rv_age) TYPE i.
|
||||
|
||||
METHODS bucket_of
|
||||
IMPORTING iv_age TYPE i
|
||||
RETURNING VALUE(rv_bucket) TYPE {{p}}if_stock_age=>ty_bucket.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}stock_age IMPLEMENTATION.
|
||||
|
||||
METHOD {{p}}if_stock_age~analyze.
|
||||
DATA lt_group TYPE tt_age_group.
|
||||
|
||||
LOOP AT it_batch INTO DATA(ls_batch).
|
||||
DATA(lv_age) = age_in_days( is_batch = ls_batch iv_key_date = iv_key_date ).
|
||||
IF lv_age < 0.
|
||||
CONTINUE.
|
||||
ENDIF.
|
||||
|
||||
DATA(lv_bucket) = bucket_of( lv_age ).
|
||||
ASSIGN lt_group[ material = ls_batch-material
|
||||
bucket = lv_bucket ] TO FIELD-SYMBOL(<ls_group>).
|
||||
IF sy-subrc <> 0.
|
||||
INSERT VALUE #( material = ls_batch-material bucket = lv_bucket )
|
||||
INTO TABLE lt_group.
|
||||
ASSIGN lt_group[ material = ls_batch-material
|
||||
bucket = lv_bucket ] TO <ls_group>.
|
||||
ENDIF.
|
||||
|
||||
<ls_group>-quantity = <ls_group>-quantity + ls_batch-quantity.
|
||||
<ls_group>-batch_count = <ls_group>-batch_count + 1.
|
||||
ENDLOOP.
|
||||
|
||||
rt_age = lt_group.
|
||||
SORT rt_age BY material bucket.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD {{p}}if_stock_age~total_by_material.
|
||||
DATA lt_group TYPE tt_total_group.
|
||||
|
||||
LOOP AT it_batch INTO DATA(ls_batch).
|
||||
IF age_in_days( is_batch = ls_batch iv_key_date = iv_key_date ) < 0.
|
||||
CONTINUE.
|
||||
ENDIF.
|
||||
|
||||
ASSIGN lt_group[ material = ls_batch-material ] TO FIELD-SYMBOL(<ls_group>).
|
||||
IF sy-subrc <> 0.
|
||||
INSERT VALUE #( material = ls_batch-material ) INTO TABLE lt_group.
|
||||
ASSIGN lt_group[ material = ls_batch-material ] TO <ls_group>.
|
||||
ENDIF.
|
||||
|
||||
<ls_group>-quantity = <ls_group>-quantity + ls_batch-quantity.
|
||||
<ls_group>-batch_count = <ls_group>-batch_count + 1.
|
||||
ENDLOOP.
|
||||
|
||||
rt_total = lt_group.
|
||||
SORT rt_total BY material.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD age_in_days.
|
||||
IF is_batch-gr_date IS INITIAL OR is_batch-gr_date > iv_key_date.
|
||||
rv_age = -1.
|
||||
ELSE.
|
||||
rv_age = iv_key_date - is_batch-gr_date.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD bucket_of.
|
||||
IF iv_age <= 30.
|
||||
rv_bucket = {{p}}if_stock_age=>c_bucket-d0_30.
|
||||
ELSEIF iv_age <= 60.
|
||||
rv_bucket = {{p}}if_stock_age=>c_bucket-d31_60.
|
||||
ELSEIF iv_age <= 90.
|
||||
rv_bucket = {{p}}if_stock_age=>c_bucket-d61_90.
|
||||
ELSE.
|
||||
rv_bucket = {{p}}if_stock_age=>c_bucket-d91_plus.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
80
tasks_gen/eval/G0004/reference/stock_age.testclasses.abap
Normal file
80
tasks_gen/eval/G0004/reference/stock_age.testclasses.abap
Normal file
@@ -0,0 +1,80 @@
|
||||
CLASS ltc_stock_age DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
PRIVATE SECTION.
|
||||
CONSTANTS c_key_date TYPE dats VALUE '20240401'.
|
||||
|
||||
DATA mo_cut TYPE REF TO {{p}}if_stock_age.
|
||||
|
||||
METHODS setup.
|
||||
METHODS gr_date_for_age
|
||||
IMPORTING iv_age TYPE i
|
||||
RETURNING VALUE(rv_date) TYPE dats.
|
||||
|
||||
METHODS empty_input FOR TESTING.
|
||||
METHODS bucket_01 FOR TESTING.
|
||||
METHODS bucket_04 FOR TESTING.
|
||||
METHODS invalid_rows FOR TESTING.
|
||||
METHODS total_by_material FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS ltc_stock_age IMPLEMENTATION.
|
||||
|
||||
METHOD setup.
|
||||
mo_cut = NEW {{p}}stock_age( ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD gr_date_for_age.
|
||||
rv_date = c_key_date - iv_age.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD empty_input.
|
||||
cl_abap_unit_assert=>assert_initial(
|
||||
act = mo_cut->analyze( it_batch = VALUE {{p}}if_stock_age=>tt_batch( )
|
||||
iv_key_date = c_key_date ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD bucket_01.
|
||||
DATA(lt_result) = mo_cut->analyze(
|
||||
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
|
||||
( material = 'MAT-1' batch = 'B1' quantity = 10
|
||||
gr_date = gr_date_for_age( 30 ) ) )
|
||||
iv_key_date = c_key_date ).
|
||||
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-bucket
|
||||
exp = {{p}}if_stock_age=>c_bucket-d0_30 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-quantity exp = 10 ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD bucket_04.
|
||||
DATA(lt_result) = mo_cut->analyze(
|
||||
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
|
||||
( material = 'MAT-1' batch = 'B1' quantity = 10
|
||||
gr_date = gr_date_for_age( 91 ) ) )
|
||||
iv_key_date = c_key_date ).
|
||||
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-bucket
|
||||
exp = {{p}}if_stock_age=>c_bucket-d91_plus ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD invalid_rows.
|
||||
DATA(lt_result) = mo_cut->analyze(
|
||||
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
|
||||
( material = 'MAT-1' batch = 'B1' quantity = 10 gr_date = '00000000' ) )
|
||||
iv_key_date = c_key_date ).
|
||||
|
||||
cl_abap_unit_assert=>assert_initial( act = lt_result ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD total_by_material.
|
||||
DATA(lt_result) = mo_cut->total_by_material(
|
||||
it_batch = VALUE {{p}}if_stock_age=>tt_batch(
|
||||
( material = 'MAT-2' batch = 'B1' quantity = 10 gr_date = gr_date_for_age( 1 ) )
|
||||
( material = 'MAT-1' batch = 'B2' quantity = 5 gr_date = gr_date_for_age( 100 ) ) )
|
||||
iv_key_date = c_key_date ).
|
||||
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-material exp = 'MAT-1' ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 1 ]-quantity exp = 5 ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-material exp = 'MAT-2' ).
|
||||
cl_abap_unit_assert=>assert_equals( act = lt_result[ 2 ]-quantity exp = 10 ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
45
tasks_gen/eval/G0004/seed/if_stock_age.intf.abap
Normal file
45
tasks_gen/eval/G0004/seed/if_stock_age.intf.abap
Normal file
@@ -0,0 +1,45 @@
|
||||
INTERFACE {{p}}if_stock_age PUBLIC.
|
||||
TYPES:
|
||||
ty_material TYPE c LENGTH 10,
|
||||
ty_batch_id TYPE c LENGTH 10,
|
||||
ty_bucket TYPE c LENGTH 2,
|
||||
ty_quantity TYPE p LENGTH 13 DECIMALS 3,
|
||||
BEGIN OF ty_batch,
|
||||
material TYPE ty_material,
|
||||
batch TYPE ty_batch_id,
|
||||
quantity TYPE ty_quantity,
|
||||
gr_date TYPE dats,
|
||||
END OF ty_batch,
|
||||
tt_batch TYPE STANDARD TABLE OF ty_batch WITH EMPTY KEY,
|
||||
BEGIN OF ty_age_row,
|
||||
material TYPE ty_material,
|
||||
bucket TYPE ty_bucket,
|
||||
quantity TYPE ty_quantity,
|
||||
batch_count TYPE i,
|
||||
END OF ty_age_row,
|
||||
tt_age_row TYPE STANDARD TABLE OF ty_age_row WITH EMPTY KEY,
|
||||
BEGIN OF ty_total,
|
||||
material TYPE ty_material,
|
||||
quantity TYPE ty_quantity,
|
||||
batch_count TYPE i,
|
||||
END OF ty_total,
|
||||
tt_total TYPE STANDARD TABLE OF ty_total WITH EMPTY KEY.
|
||||
|
||||
CONSTANTS:
|
||||
BEGIN OF c_bucket,
|
||||
d0_30 TYPE ty_bucket VALUE '01',
|
||||
d31_60 TYPE ty_bucket VALUE '02',
|
||||
d61_90 TYPE ty_bucket VALUE '03',
|
||||
d91_plus TYPE ty_bucket VALUE '04',
|
||||
END OF c_bucket.
|
||||
|
||||
METHODS analyze
|
||||
IMPORTING it_batch TYPE tt_batch
|
||||
iv_key_date TYPE dats
|
||||
RETURNING VALUE(rt_age) TYPE tt_age_row.
|
||||
|
||||
METHODS total_by_material
|
||||
IMPORTING it_batch TYPE tt_batch
|
||||
iv_key_date TYPE dats
|
||||
RETURNING VALUE(rt_total) TYPE tt_total.
|
||||
ENDINTERFACE.
|
||||
80
tasks_gen/eval/G0004/spec.md
Normal file
80
tasks_gen/eval/G0004/spec.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# 1. Goal
|
||||
Analyse the age of stock batches. A warehouse report calls the analysis before it
|
||||
prints the stock aging report. The report needs the grouped quantity and the number
|
||||
of batches for each material and age bucket.
|
||||
|
||||
# 2. Open questions
|
||||
None.
|
||||
|
||||
# 3. Context
|
||||
- The interface {{P}}IF_STOCK_AGE exists in package $TMP. It is active.
|
||||
- The interface defines these types:
|
||||
- ty_material: character, length 10.
|
||||
- ty_batch_id: character, length 10.
|
||||
- ty_bucket: character, length 2.
|
||||
- ty_quantity: packed number, length 13, 3 decimals.
|
||||
- ty_batch: structure with the components material, batch, quantity, gr_date.
|
||||
- tt_batch: table of ty_batch.
|
||||
- ty_age_row: structure with the components material, bucket, quantity, batch_count.
|
||||
- tt_age_row: table of ty_age_row.
|
||||
- ty_total: structure with the components material, quantity, batch_count.
|
||||
- tt_total: table of ty_total.
|
||||
- The interface defines the constant structure C_BUCKET with the components
|
||||
D0_30 = '01', D31_60 = '02', D61_90 = '03', D91_PLUS = '04'.
|
||||
- The interface defines two methods:
|
||||
- analyze: importing it_batch type tt_batch and iv_key_date type dats,
|
||||
returning rt_age type tt_age_row.
|
||||
- total_by_material: importing it_batch type tt_batch and iv_key_date type dats,
|
||||
returning rt_total type tt_total.
|
||||
|
||||
# 4. Contract
|
||||
- Create the class {{P}}STOCK_AGE in package $TMP.
|
||||
- The class is public, final, and has a public constructor without parameters.
|
||||
- The class implements {{P}}IF_STOCK_AGE.
|
||||
- Do not add other public methods.
|
||||
|
||||
# 5. Business rules
|
||||
Both methods use the same validity rules and the same age buckets.
|
||||
|
||||
Validity
|
||||
1. A row of IT_BATCH is valid when its goods receipt date is not initial and is not
|
||||
after IV_KEY_DATE.
|
||||
2. A row that is not valid is ignored. It must not change any result.
|
||||
|
||||
Age and bucket
|
||||
3. The age of a batch is the number of days between its goods receipt date and
|
||||
IV_KEY_DATE.
|
||||
4. The bucket of a batch is:
|
||||
- age 0 to 30 days: bucket 01 (C_BUCKET-D0_30)
|
||||
- age 31 to 60 days: bucket 02 (C_BUCKET-D31_60)
|
||||
- age 61 to 90 days: bucket 03 (C_BUCKET-D61_90)
|
||||
- age more than 90 days: bucket 04 (C_BUCKET-D91_PLUS)
|
||||
|
||||
Method ANALYZE
|
||||
5. Group the valid rows by material and bucket.
|
||||
6. For each group, sum the quantity and count the valid rows.
|
||||
7. Return one row per group. Sort the result by material ascending and then by
|
||||
bucket ascending.
|
||||
8. A group is in the result as soon as one valid row belongs to it, also when the
|
||||
summed quantity is zero.
|
||||
9. If no valid row exists, return an empty table.
|
||||
|
||||
Method TOTAL_BY_MATERIAL
|
||||
10. Group the valid rows by material only.
|
||||
11. For each group, sum the quantity and count the valid rows.
|
||||
12. Return one row per material. Sort the result by material ascending.
|
||||
13. If no valid row exists, return an empty table.
|
||||
|
||||
Quantity
|
||||
14. The quantity of a batch can be negative. Add the quantities as they are.
|
||||
|
||||
# 6. Constraints
|
||||
- Release target: SAP_BASIS 8.16 (ABAP Platform 2025).
|
||||
- Coding standards: Clean ABAP. Method length below 40 statements. No global
|
||||
variables. No comment that restates the code. Pass large parameters by reference.
|
||||
- Out of scope: do not change {{P}}IF_STOCK_AGE.
|
||||
|
||||
# 7. Acceptance
|
||||
- The class is active and has no syntax error.
|
||||
- The hidden tests pass.
|
||||
- Write your own ABAP Unit tests for the class.
|
||||
53
tasks_gen/eval/G0004/task.json
Normal file
53
tasks_gen/eval/G0004/task.json
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"id": "G0004",
|
||||
"category": "C",
|
||||
"object_type": "CLAS",
|
||||
"difficulty": 2,
|
||||
"release_target": "v816",
|
||||
"expected_outcome": "implement",
|
||||
"budget": {
|
||||
"max_tool_calls": 60,
|
||||
"max_activations": 12
|
||||
},
|
||||
"seed": [
|
||||
{
|
||||
"type": "INTF",
|
||||
"name": "{{P}}IF_STOCK_AGE",
|
||||
"file": "seed/if_stock_age.intf.abap",
|
||||
"description": "Stock age contract: row types, bucket constants and the two analysis methods"
|
||||
}
|
||||
],
|
||||
"contract": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}STOCK_AGE",
|
||||
"implements": "{{P}}IF_STOCK_AGE"
|
||||
}
|
||||
],
|
||||
"out_of_scope": [
|
||||
"{{P}}IF_STOCK_AGE"
|
||||
],
|
||||
"hidden_tests": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}STOCK_AGE_HIDDEN",
|
||||
"file": "hidden/stock_age_hidden.clas.abap",
|
||||
"description": "Hidden tests for the stock age analysis"
|
||||
}
|
||||
],
|
||||
"reference": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}STOCK_AGE",
|
||||
"file": "reference/stock_age.clas.abap",
|
||||
"description": "Stock age analysis",
|
||||
"testclasses_file": "reference/stock_age.testclasses.abap"
|
||||
}
|
||||
],
|
||||
"craft_checks": [
|
||||
"table_kind",
|
||||
"access_path",
|
||||
"method_length",
|
||||
"no_nested_loops"
|
||||
]
|
||||
}
|
||||
29
tasks_gen/eval/G0005/generation.json
Normal file
29
tasks_gen/eval/G0005/generation.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"id": "G0005",
|
||||
"pool": "eval",
|
||||
"object_type": "CLAS",
|
||||
"category": "D",
|
||||
"attempts": [
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": null,
|
||||
"null": null
|
||||
},
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 0,
|
||||
"null": 0
|
||||
},
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 0,
|
||||
"null": 0
|
||||
},
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 0,
|
||||
"null": 0
|
||||
}
|
||||
],
|
||||
"accepted": false
|
||||
}
|
||||
158
tasks_gen/eval/G0005/hidden/t02_hidden.clas.abap
Normal file
158
tasks_gen/eval/G0005/hidden/t02_hidden.clas.abap
Normal file
@@ -0,0 +1,158 @@
|
||||
CLASS {{p}}t02_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
|
||||
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
PRIVATE SECTION.
|
||||
TYPES ty_cost TYPE p LENGTH 15 DECIMALS 2.
|
||||
TYPES ty_currency TYPE c LENGTH 5.
|
||||
TYPES ty_zone_code TYPE c LENGTH 4.
|
||||
TYPES ty_weight TYPE p LENGTH 15 DECIMALS 3.
|
||||
|
||||
DATA mo_cut TYPE REF TO {{p}}freight_calc.
|
||||
|
||||
METHODS setup.
|
||||
|
||||
METHODS calculate_result
|
||||
IMPORTING iv_zone_code TYPE ty_zone_code
|
||||
iv_weight_kg TYPE ty_weight
|
||||
iv_express TYPE abap_bool DEFAULT abap_false
|
||||
RETURNING VALUE(rs_result) TYPE {{p}}freight_calc=>ty_result.
|
||||
|
||||
METHODS assert_cost
|
||||
IMPORTING iv_cost TYPE ty_cost
|
||||
iv_currency TYPE ty_currency
|
||||
is_result TYPE {{p}}freight_calc=>ty_result.
|
||||
|
||||
METHODS assert_weight
|
||||
IMPORTING iv_expected TYPE ty_weight
|
||||
iv_actual TYPE ty_weight
|
||||
iv_msg TYPE string.
|
||||
|
||||
METHODS standard_cost FOR TESTING.
|
||||
METHODS express_surcharge FOR TESTING.
|
||||
METHODS express_no_early_rounding FOR TESTING.
|
||||
METHODS minimum_charge FOR TESTING.
|
||||
METHODS rounding FOR TESTING.
|
||||
METHODS zone_code_normalized FOR TESTING.
|
||||
METHODS weight_at_limit FOR TESTING.
|
||||
METHODS unknown_zone FOR TESTING.
|
||||
METHODS invalid_weight FOR TESTING.
|
||||
METHODS weight_limit FOR TESTING.
|
||||
METHODS weight_check_before_zone_lookup FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}t02_hidden IMPLEMENTATION.
|
||||
METHOD setup.
|
||||
mo_cut = NEW {{p}}freight_calc( ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD calculate_result.
|
||||
TRY.
|
||||
rs_result = mo_cut->calculate( iv_zone_code = iv_zone_code
|
||||
iv_weight_kg = iv_weight_kg
|
||||
iv_express = iv_express ).
|
||||
CATCH {{p}}cx_freight_error INTO DATA(lo_error).
|
||||
cl_abap_unit_assert=>fail( msg = lo_error->get_reason_text( ) ).
|
||||
ENDTRY.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD assert_cost.
|
||||
cl_abap_unit_assert=>assert_equals( exp = iv_cost act = is_result-cost msg = 'cost' ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = iv_currency act = is_result-currency msg = 'currency' ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD assert_weight.
|
||||
cl_abap_unit_assert=>assert_equals( exp = iv_expected act = iv_actual msg = iv_msg ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD standard_cost.
|
||||
DATA(ls_result) = calculate_result( iv_zone_code = 'DE01' iv_weight_kg = '10.000' ).
|
||||
assert_cost( iv_cost = '17.00' iv_currency = 'EUR' is_result = ls_result ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD express_surcharge.
|
||||
DATA(ls_result) = calculate_result( iv_zone_code = 'DE01'
|
||||
iv_weight_kg = '10.000'
|
||||
iv_express = abap_true ).
|
||||
assert_cost( iv_cost = '25.50' iv_currency = 'EUR' is_result = ls_result ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD express_no_early_rounding.
|
||||
DATA(ls_result) = calculate_result( iv_zone_code = 'DE01'
|
||||
iv_weight_kg = '10.004'
|
||||
iv_express = abap_true ).
|
||||
assert_cost( iv_cost = '25.51' iv_currency = 'EUR' is_result = ls_result ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD minimum_charge.
|
||||
DATA(ls_result) = calculate_result( iv_zone_code = 'DE01' iv_weight_kg = '0.500' ).
|
||||
assert_cost( iv_cost = '6.00' iv_currency = 'EUR' is_result = ls_result ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD rounding.
|
||||
DATA(ls_result) = calculate_result( iv_zone_code = 'FR01' iv_weight_kg = '1.234' ).
|
||||
assert_cost( iv_cost = '12.09' iv_currency = 'EUR' is_result = ls_result ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD zone_code_normalized.
|
||||
DATA(ls_result) = calculate_result( iv_zone_code = 'de01' iv_weight_kg = '10.000' ).
|
||||
assert_cost( iv_cost = '17.00' iv_currency = 'EUR' is_result = ls_result ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD weight_at_limit.
|
||||
DATA(ls_result) = calculate_result( iv_zone_code = 'CN01' iv_weight_kg = '20.000' ).
|
||||
assert_cost( iv_cost = '103.00' iv_currency = 'CNY' is_result = ls_result ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD unknown_zone.
|
||||
TRY.
|
||||
mo_cut->calculate( iv_zone_code = 'xx99' iv_weight_kg = '1.000' ).
|
||||
cl_abap_unit_assert=>fail( msg = 'CX_UNKNOWN_ZONE expected' ).
|
||||
CATCH {{p}}cx_unknown_zone INTO DATA(lo_unknown).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 'XX99' act = lo_unknown->mv_zone_code ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `Zone XX99 is not known`
|
||||
act = lo_unknown->get_reason_text( ) ).
|
||||
CATCH {{p}}cx_freight_error INTO DATA(lo_other).
|
||||
cl_abap_unit_assert=>fail( msg = |Wrong exception class: { lo_other->get_reason_text( ) }| ).
|
||||
ENDTRY.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD invalid_weight.
|
||||
TRY.
|
||||
mo_cut->calculate( iv_zone_code = 'DE01' iv_weight_kg = '0.000' ).
|
||||
cl_abap_unit_assert=>fail( msg = 'CX_INVALID_WEIGHT expected' ).
|
||||
CATCH {{p}}cx_invalid_weight INTO DATA(lo_invalid).
|
||||
assert_weight( iv_expected = '0.000' iv_actual = lo_invalid->mv_weight_kg iv_msg = 'weight' ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 'DE01' act = lo_invalid->mv_zone_code ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `Weight must be greater than zero`
|
||||
act = lo_invalid->get_reason_text( ) ).
|
||||
CATCH {{p}}cx_freight_error INTO DATA(lo_other).
|
||||
cl_abap_unit_assert=>fail( msg = |Wrong exception class: { lo_other->get_reason_text( ) }| ).
|
||||
ENDTRY.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD weight_limit.
|
||||
TRY.
|
||||
mo_cut->calculate( iv_zone_code = 'CN01' iv_weight_kg = '20.001' ).
|
||||
cl_abap_unit_assert=>fail( msg = 'CX_WEIGHT_LIMIT expected' ).
|
||||
CATCH {{p}}cx_weight_limit INTO DATA(lo_limit).
|
||||
assert_weight( iv_expected = '20.001' iv_actual = lo_limit->mv_weight_kg iv_msg = 'weight' ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 20 act = lo_limit->mv_max_weight_kg msg = 'max weight' ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 'CN01' act = lo_limit->mv_zone_code ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `Weight exceeds the zone limit`
|
||||
act = lo_limit->get_reason_text( ) ).
|
||||
CATCH {{p}}cx_freight_error INTO DATA(lo_other).
|
||||
cl_abap_unit_assert=>fail( msg = |Wrong exception class: { lo_other->get_reason_text( ) }| ).
|
||||
ENDTRY.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD weight_check_before_zone_lookup.
|
||||
TRY.
|
||||
mo_cut->calculate( iv_zone_code = 'ZZZZ' iv_weight_kg = '0.000' ).
|
||||
cl_abap_unit_assert=>fail( msg = 'CX_INVALID_WEIGHT expected' ).
|
||||
CATCH {{p}}cx_invalid_weight INTO DATA(lo_invalid).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 'ZZZZ' act = lo_invalid->mv_zone_code ).
|
||||
CATCH {{p}}cx_freight_error INTO DATA(lo_other).
|
||||
cl_abap_unit_assert=>fail( msg = |Wrong exception class: { lo_other->get_reason_text( ) }| ).
|
||||
ENDTRY.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
11
tasks_gen/eval/G0005/reference/cx_freight_error.clas.abap
Normal file
11
tasks_gen/eval/G0005/reference/cx_freight_error.clas.abap
Normal file
@@ -0,0 +1,11 @@
|
||||
CLASS {{p}}cx_freight_error DEFINITION PUBLIC INHERITING FROM cx_static_check ABSTRACT.
|
||||
PUBLIC SECTION.
|
||||
DATA mv_zone_code TYPE c LENGTH 4.
|
||||
|
||||
METHODS get_reason_text ABSTRACT
|
||||
RETURNING VALUE(rv_text) TYPE string.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}cx_freight_error IMPLEMENTATION.
|
||||
ENDCLASS.
|
||||
13
tasks_gen/eval/G0005/reference/cx_invalid_weight.clas.abap
Normal file
13
tasks_gen/eval/G0005/reference/cx_invalid_weight.clas.abap
Normal file
@@ -0,0 +1,13 @@
|
||||
CLASS {{p}}cx_invalid_weight DEFINITION PUBLIC INHERITING FROM {{p}}cx_freight_error FINAL CREATE PUBLIC.
|
||||
PUBLIC SECTION.
|
||||
DATA mv_weight_kg TYPE p LENGTH 15 DECIMALS 3.
|
||||
|
||||
METHODS get_reason_text REDEFINITION.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}cx_invalid_weight IMPLEMENTATION.
|
||||
METHOD get_reason_text.
|
||||
rv_text = 'Weight must be greater than zero'.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
11
tasks_gen/eval/G0005/reference/cx_unknown_zone.clas.abap
Normal file
11
tasks_gen/eval/G0005/reference/cx_unknown_zone.clas.abap
Normal file
@@ -0,0 +1,11 @@
|
||||
CLASS {{p}}cx_unknown_zone DEFINITION PUBLIC INHERITING FROM {{p}}cx_freight_error FINAL CREATE PUBLIC.
|
||||
PUBLIC SECTION.
|
||||
METHODS get_reason_text REDEFINITION.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}cx_unknown_zone IMPLEMENTATION.
|
||||
METHOD get_reason_text.
|
||||
rv_text = |Zone { condense( mv_zone_code ) } is not known|.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
14
tasks_gen/eval/G0005/reference/cx_weight_limit.clas.abap
Normal file
14
tasks_gen/eval/G0005/reference/cx_weight_limit.clas.abap
Normal file
@@ -0,0 +1,14 @@
|
||||
CLASS {{p}}cx_weight_limit DEFINITION PUBLIC INHERITING FROM {{p}}cx_freight_error FINAL CREATE PUBLIC.
|
||||
PUBLIC SECTION.
|
||||
DATA mv_weight_kg TYPE p LENGTH 15 DECIMALS 3.
|
||||
DATA mv_max_weight_kg TYPE i.
|
||||
|
||||
METHODS get_reason_text REDEFINITION.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}cx_weight_limit IMPLEMENTATION.
|
||||
METHOD get_reason_text.
|
||||
rv_text = 'Weight exceeds the zone limit'.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
73
tasks_gen/eval/G0005/reference/freight_calc.clas.abap
Normal file
73
tasks_gen/eval/G0005/reference/freight_calc.clas.abap
Normal file
@@ -0,0 +1,73 @@
|
||||
CLASS {{p}}freight_calc DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
||||
PUBLIC SECTION.
|
||||
TYPES ty_cost TYPE p LENGTH 15 DECIMALS 2.
|
||||
TYPES ty_weight TYPE p LENGTH 15 DECIMALS 3.
|
||||
TYPES: BEGIN OF ty_result,
|
||||
cost TYPE ty_cost,
|
||||
currency TYPE c LENGTH 5,
|
||||
END OF ty_result.
|
||||
|
||||
METHODS calculate
|
||||
IMPORTING iv_zone_code TYPE c LENGTH 4
|
||||
iv_weight_kg TYPE ty_weight
|
||||
iv_express TYPE abap_bool DEFAULT abap_false
|
||||
RETURNING VALUE(rs_result) TYPE ty_result
|
||||
RAISING {{p}}cx_freight_error.
|
||||
|
||||
PRIVATE SECTION.
|
||||
CONSTANTS c_express_factor TYPE p LENGTH 5 DECIMALS 2 VALUE '1.50'.
|
||||
CONSTANTS c_minimum_charge TYPE ty_cost VALUE '6.00'.
|
||||
|
||||
METHODS find_zone
|
||||
IMPORTING iv_zone_code TYPE c LENGTH 4
|
||||
RETURNING VALUE(rs_zone) TYPE {{p}}zone_master=>ty_zone
|
||||
RAISING {{p}}cx_freight_error.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}freight_calc IMPLEMENTATION.
|
||||
METHOD calculate.
|
||||
DATA lv_zone_code TYPE c LENGTH 4.
|
||||
lv_zone_code = to_upper( iv_zone_code ).
|
||||
|
||||
IF iv_weight_kg <= 0.
|
||||
DATA(lo_invalid) = NEW {{p}}cx_invalid_weight( ).
|
||||
lo_invalid->mv_zone_code = lv_zone_code.
|
||||
lo_invalid->mv_weight_kg = iv_weight_kg.
|
||||
RAISE EXCEPTION lo_invalid.
|
||||
ENDIF.
|
||||
|
||||
DATA(ls_zone) = find_zone( lv_zone_code ).
|
||||
|
||||
IF iv_weight_kg > ls_zone-max_weight_kg.
|
||||
DATA(lo_limit) = NEW {{p}}cx_weight_limit( ).
|
||||
lo_limit->mv_zone_code = lv_zone_code.
|
||||
lo_limit->mv_weight_kg = iv_weight_kg.
|
||||
lo_limit->mv_max_weight_kg = ls_zone-max_weight_kg.
|
||||
RAISE EXCEPTION lo_limit.
|
||||
ENDIF.
|
||||
|
||||
DATA lv_cost TYPE p LENGTH 15 DECIMALS 5.
|
||||
lv_cost = ls_zone-base_fee + iv_weight_kg * ls_zone-price_per_kg.
|
||||
IF iv_express = abap_true.
|
||||
lv_cost = lv_cost * c_express_factor.
|
||||
ENDIF.
|
||||
lv_cost = round( val = lv_cost dec = 2 ).
|
||||
IF lv_cost < c_minimum_charge.
|
||||
lv_cost = c_minimum_charge.
|
||||
ENDIF.
|
||||
|
||||
rs_result-cost = lv_cost.
|
||||
rs_result-currency = ls_zone-currency.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD find_zone.
|
||||
DATA(lt_zone) = {{p}}zone_master=>get_all( ).
|
||||
READ TABLE lt_zone INTO rs_zone WITH KEY zone_code = iv_zone_code.
|
||||
IF sy-subrc <> 0.
|
||||
DATA(lo_unknown) = NEW {{p}}cx_unknown_zone( ).
|
||||
lo_unknown->mv_zone_code = iv_zone_code.
|
||||
RAISE EXCEPTION lo_unknown.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
98
tasks_gen/eval/G0005/reference/freight_calc.testclasses.abap
Normal file
98
tasks_gen/eval/G0005/reference/freight_calc.testclasses.abap
Normal file
@@ -0,0 +1,98 @@
|
||||
CLASS ltc_freight_calc DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
PRIVATE SECTION.
|
||||
TYPES ty_cost TYPE p LENGTH 15 DECIMALS 2.
|
||||
TYPES ty_currency TYPE c LENGTH 5.
|
||||
TYPES ty_zone_code TYPE c LENGTH 4.
|
||||
TYPES ty_weight TYPE p LENGTH 15 DECIMALS 3.
|
||||
|
||||
DATA mo_cut TYPE REF TO {{p}}freight_calc.
|
||||
|
||||
METHODS setup.
|
||||
METHODS assert_cost
|
||||
IMPORTING iv_cost TYPE ty_cost
|
||||
iv_currency TYPE ty_currency
|
||||
is_result TYPE {{p}}freight_calc=>ty_result.
|
||||
METHODS calculate_result
|
||||
IMPORTING iv_zone_code TYPE ty_zone_code
|
||||
iv_weight_kg TYPE ty_weight
|
||||
iv_express TYPE abap_bool DEFAULT abap_false
|
||||
RETURNING VALUE(rs_result) TYPE {{p}}freight_calc=>ty_result.
|
||||
METHODS standard_cost FOR TESTING.
|
||||
METHODS express_cost FOR TESTING.
|
||||
METHODS minimum_charge FOR TESTING.
|
||||
METHODS unknown_zone FOR TESTING.
|
||||
METHODS invalid_weight FOR TESTING.
|
||||
METHODS weight_limit FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS ltc_freight_calc IMPLEMENTATION.
|
||||
METHOD setup.
|
||||
mo_cut = NEW {{p}}freight_calc( ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD assert_cost.
|
||||
cl_abap_unit_assert=>assert_equals( exp = iv_cost act = is_result-cost ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = iv_currency act = is_result-currency ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD calculate_result.
|
||||
TRY.
|
||||
rs_result = mo_cut->calculate( iv_zone_code = iv_zone_code
|
||||
iv_weight_kg = iv_weight_kg
|
||||
iv_express = iv_express ).
|
||||
CATCH {{p}}cx_freight_error INTO DATA(lo_error).
|
||||
cl_abap_unit_assert=>fail( msg = lo_error->get_reason_text( ) ).
|
||||
ENDTRY.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD standard_cost.
|
||||
DATA(ls_result) = calculate_result( iv_zone_code = 'DE01' iv_weight_kg = '10.000' ).
|
||||
assert_cost( iv_cost = '17.00' iv_currency = 'EUR' is_result = ls_result ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD express_cost.
|
||||
DATA(ls_result) = calculate_result( iv_zone_code = 'DE01'
|
||||
iv_weight_kg = '10.000'
|
||||
iv_express = abap_true ).
|
||||
assert_cost( iv_cost = '25.50' iv_currency = 'EUR' is_result = ls_result ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD minimum_charge.
|
||||
DATA(ls_result) = calculate_result( iv_zone_code = 'DE01' iv_weight_kg = '0.500' ).
|
||||
assert_cost( iv_cost = '6.00' iv_currency = 'EUR' is_result = ls_result ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD unknown_zone.
|
||||
TRY.
|
||||
mo_cut->calculate( iv_zone_code = 'XX99' iv_weight_kg = '1.000' ).
|
||||
cl_abap_unit_assert=>fail( msg = 'CX_UNKNOWN_ZONE expected' ).
|
||||
CATCH {{p}}cx_unknown_zone INTO DATA(lo_unknown).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 'XX99' act = lo_unknown->mv_zone_code ).
|
||||
CATCH {{p}}cx_freight_error INTO DATA(lo_other).
|
||||
cl_abap_unit_assert=>fail( msg = |Wrong exception class: { lo_other->get_reason_text( ) }| ).
|
||||
ENDTRY.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD invalid_weight.
|
||||
TRY.
|
||||
mo_cut->calculate( iv_zone_code = 'DE01' iv_weight_kg = '0.000' ).
|
||||
cl_abap_unit_assert=>fail( msg = 'CX_INVALID_WEIGHT expected' ).
|
||||
CATCH {{p}}cx_invalid_weight INTO DATA(lo_invalid).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 'DE01' act = lo_invalid->mv_zone_code ).
|
||||
CATCH {{p}}cx_freight_error INTO DATA(lo_other).
|
||||
cl_abap_unit_assert=>fail( msg = |Wrong exception class: { lo_other->get_reason_text( ) }| ).
|
||||
ENDTRY.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD weight_limit.
|
||||
TRY.
|
||||
mo_cut->calculate( iv_zone_code = 'CN01' iv_weight_kg = '20.001' ).
|
||||
cl_abap_unit_assert=>fail( msg = 'CX_WEIGHT_LIMIT expected' ).
|
||||
CATCH {{p}}cx_weight_limit INTO DATA(lo_limit).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 'CN01' act = lo_limit->mv_zone_code ).
|
||||
CATCH {{p}}cx_freight_error INTO DATA(lo_other).
|
||||
cl_abap_unit_assert=>fail( msg = |Wrong exception class: { lo_other->get_reason_text( ) }| ).
|
||||
ENDTRY.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
18
tasks_gen/eval/G0005/seed/seed_data.clas.abap
Normal file
18
tasks_gen/eval/G0005/seed/seed_data.clas.abap
Normal file
@@ -0,0 +1,18 @@
|
||||
CLASS {{p}}seed_data DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
||||
PUBLIC SECTION.
|
||||
INTERFACES if_oo_adt_classrun.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}seed_data IMPLEMENTATION.
|
||||
METHOD if_oo_adt_classrun~main.
|
||||
DELETE FROM {{p}}zones.
|
||||
INSERT {{p}}zones FROM TABLE @( VALUE #(
|
||||
( zone_code = 'DE01' country = 'DE' base_fee = '5.00' price_per_kg = '1.20' max_weight_kg = 31 currency = 'EUR' )
|
||||
( zone_code = 'DE02' country = 'DE' base_fee = '8.00' price_per_kg = '1.80' max_weight_kg = 31 currency = 'EUR' )
|
||||
( zone_code = 'FR01' country = 'FR' base_fee = '9.50' price_per_kg = '2.10' max_weight_kg = 30 currency = 'EUR' )
|
||||
( zone_code = 'US01' country = 'US' base_fee = '14.00' price_per_kg = '3.40' max_weight_kg = 25 currency = 'USD' )
|
||||
( zone_code = 'CN01' country = 'CN' base_fee = '18.00' price_per_kg = '4.25' max_weight_kg = 20 currency = 'CNY' ) ) ).
|
||||
out->write( |Zones seeded| ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
27
tasks_gen/eval/G0005/seed/zone_master.clas.abap
Normal file
27
tasks_gen/eval/G0005/seed/zone_master.clas.abap
Normal file
@@ -0,0 +1,27 @@
|
||||
CLASS {{p}}zone_master DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
||||
PUBLIC SECTION.
|
||||
TYPES: BEGIN OF ty_zone,
|
||||
zone_code TYPE c LENGTH 4,
|
||||
country TYPE c LENGTH 2,
|
||||
base_fee TYPE p LENGTH 15 DECIMALS 2,
|
||||
price_per_kg TYPE p LENGTH 15 DECIMALS 2,
|
||||
max_weight_kg TYPE i,
|
||||
currency TYPE c LENGTH 5,
|
||||
END OF ty_zone,
|
||||
tt_zone TYPE SORTED TABLE OF ty_zone WITH UNIQUE KEY zone_code.
|
||||
|
||||
CLASS-METHODS get_all
|
||||
RETURNING VALUE(rt_zone) TYPE tt_zone.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}zone_master IMPLEMENTATION.
|
||||
METHOD get_all.
|
||||
rt_zone = VALUE #(
|
||||
( zone_code = 'DE01' country = 'DE' base_fee = '5.00' price_per_kg = '1.20' max_weight_kg = 31 currency = 'EUR' )
|
||||
( zone_code = 'DE02' country = 'DE' base_fee = '8.00' price_per_kg = '1.80' max_weight_kg = 31 currency = 'EUR' )
|
||||
( zone_code = 'FR01' country = 'FR' base_fee = '9.50' price_per_kg = '2.10' max_weight_kg = 30 currency = 'EUR' )
|
||||
( zone_code = 'US01' country = 'US' base_fee = '14.00' price_per_kg = '3.40' max_weight_kg = 25 currency = 'USD' )
|
||||
( zone_code = 'CN01' country = 'CN' base_fee = '18.00' price_per_kg = '4.25' max_weight_kg = 20 currency = 'CNY' ) ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
10
tasks_gen/eval/G0005/seed/zones.tabl.abap
Normal file
10
tasks_gen/eval/G0005/seed/zones.tabl.abap
Normal file
@@ -0,0 +1,10 @@
|
||||
@EndUserText.label : 'Zone tariff'
|
||||
define table {{p}}zones {
|
||||
key mandt : abap.clnt not null;
|
||||
key zone_code : abap.char(4) not null;
|
||||
country : abap.char(2) not null;
|
||||
base_fee : abap.dec(15,2) not null;
|
||||
price_per_kg : abap.dec(15,2) not null;
|
||||
max_weight_kg : abap.int4 not null;
|
||||
currency : abap.char(5) not null;
|
||||
}
|
||||
103
tasks_gen/eval/G0005/spec.md
Normal file
103
tasks_gen/eval/G0005/spec.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# 1. Goal
|
||||
A parcel service calculates the freight cost of a parcel from a zone code and a
|
||||
weight. Each rejected input must be reported as its own class-based exception, so
|
||||
that the calling program can react to the exact reason.
|
||||
|
||||
# 2. Open questions
|
||||
None.
|
||||
|
||||
# 3. Context
|
||||
- The class {{P}}ZONE_MASTER exists in package $TMP and is active. It provides the
|
||||
zone master data. Do not change it.
|
||||
- {{P}}ZONE_MASTER has these public components:
|
||||
- TYPES TY_ZONE with the components ZONE_CODE (c LENGTH 4), COUNTRY (c LENGTH 2),
|
||||
BASE_FEE (p LENGTH 15 DECIMALS 2), PRICE_PER_KG (p LENGTH 15 DECIMALS 2),
|
||||
MAX_WEIGHT_KG (i), CURRENCY (c LENGTH 5).
|
||||
- TYPES TT_ZONE, a sorted table of TY_ZONE with the unique key ZONE_CODE.
|
||||
- CLASS-METHOD GET_ALL returning the table TT_ZONE.
|
||||
- The zone master data contains these entries:
|
||||
|
||||
| ZONE_CODE | COUNTRY | BASE_FEE | PRICE_PER_KG | MAX_WEIGHT_KG | CURRENCY |
|
||||
| --------- | ------- | -------- | ------------ | ------------- | -------- |
|
||||
| DE01 | DE | 5.00 | 1.20 | 31 | EUR |
|
||||
| DE02 | DE | 8.00 | 1.80 | 31 | EUR |
|
||||
| FR01 | FR | 9.50 | 2.10 | 30 | EUR |
|
||||
| US01 | US | 14.00 | 3.40 | 25 | USD |
|
||||
| CN01 | CN | 18.00 | 4.25 | 20 | CNY |
|
||||
|
||||
# 4. Contract
|
||||
Create these classes in package $TMP.
|
||||
|
||||
{{P}}CX_FREIGHT_ERROR
|
||||
- public, abstract, inherits from CX_STATIC_CHECK.
|
||||
- public attribute MV_ZONE_CODE type c LENGTH 4.
|
||||
- public method GET_REASON_TEXT returning VALUE(rv_text) type string.
|
||||
|
||||
{{P}}CX_UNKNOWN_ZONE
|
||||
- public, inherits from {{P}}CX_FREIGHT_ERROR.
|
||||
|
||||
{{P}}CX_INVALID_WEIGHT
|
||||
- public, inherits from {{P}}CX_FREIGHT_ERROR.
|
||||
- public attribute MV_WEIGHT_KG type p LENGTH 15 DECIMALS 3.
|
||||
|
||||
{{P}}CX_WEIGHT_LIMIT
|
||||
- public, inherits from {{P}}CX_FREIGHT_ERROR.
|
||||
- public attribute MV_WEIGHT_KG type p LENGTH 15 DECIMALS 3.
|
||||
- public attribute MV_MAX_WEIGHT_KG type i.
|
||||
|
||||
GET_REASON_TEXT returns the reason text of the exception. Each of the three
|
||||
concrete exception classes returns its own text, see the business rules.
|
||||
|
||||
{{P}}FREIGHT_CALC
|
||||
- public, final, create public, constructor without parameters.
|
||||
- public type TY_COST (p LENGTH 15 DECIMALS 2).
|
||||
- public type TY_WEIGHT (p LENGTH 15 DECIMALS 3).
|
||||
- public type TY_RESULT with the components COST (type TY_COST) and CURRENCY
|
||||
(c LENGTH 5).
|
||||
- public method CALCULATE:
|
||||
|
||||
IMPORTING iv_zone_code TYPE c LENGTH 4
|
||||
iv_weight_kg TYPE ty_weight
|
||||
iv_express TYPE abap_bool DEFAULT abap_false
|
||||
RETURNING VALUE(rs_result) TYPE ty_result
|
||||
RAISING {{p}}cx_freight_error
|
||||
|
||||
- no other public methods.
|
||||
|
||||
Use the named types of this contract in the method interfaces. Do not write the
|
||||
additions LENGTH or DECIMALS in a method interface.
|
||||
|
||||
# 5. Business rules
|
||||
CALCULATE applies these rules in this sequence.
|
||||
1. Convert the zone code to upper case. Use this value in all further steps and in
|
||||
the attribute MV_ZONE_CODE of every exception.
|
||||
2. If the weight is less than or equal to zero, raise {{P}}CX_INVALID_WEIGHT. Set
|
||||
MV_ZONE_CODE to the zone code and MV_WEIGHT_KG to the given weight.
|
||||
3. Read the zone master data with {{P}}ZONE_MASTER=>GET_ALL and find the entry
|
||||
whose ZONE_CODE equals the zone code. If there is no such entry, raise
|
||||
{{P}}CX_UNKNOWN_ZONE and set MV_ZONE_CODE to the zone code.
|
||||
4. If the weight is greater than MAX_WEIGHT_KG, raise {{P}}CX_WEIGHT_LIMIT. Set
|
||||
MV_ZONE_CODE to the zone code, MV_WEIGHT_KG to the given weight and
|
||||
MV_MAX_WEIGHT_KG to MAX_WEIGHT_KG.
|
||||
5. Calculate the cost as BASE_FEE + weight x PRICE_PER_KG.
|
||||
6. If IV_EXPRESS is ABAP_TRUE, multiply the cost by 1.5.
|
||||
7. Keep the full precision of the cost until this step. Do not round the cost
|
||||
before this step. Round the cost to 2 decimal places with commercial rounding.
|
||||
8. If the rounded cost is less than 6.00, use 6.00.
|
||||
9. Return the cost in COST and the CURRENCY of the zone in CURRENCY.
|
||||
|
||||
GET_REASON_TEXT returns:
|
||||
- in {{P}}CX_UNKNOWN_ZONE: 'Zone ', then the zone code, then ' is not known'.
|
||||
Example: 'Zone XX99 is not known'.
|
||||
- in {{P}}CX_INVALID_WEIGHT: 'Weight must be greater than zero'.
|
||||
- in {{P}}CX_WEIGHT_LIMIT: 'Weight exceeds the zone limit'.
|
||||
|
||||
# 6. Constraints
|
||||
- Release target: SAP_BASIS 816 (ABAP Platform 2025).
|
||||
- Coding standards: Clean ABAP. Methods below 40 statements. No global variables.
|
||||
- Out of scope: do not change {{P}}ZONE_MASTER.
|
||||
|
||||
# 7. Acceptance
|
||||
- All classes are active and have no syntax error.
|
||||
- The hidden tests pass.
|
||||
- Write your own ABAP Unit tests for {{P}}FREIGHT_CALC.
|
||||
90
tasks_gen/eval/G0005/task.json
Normal file
90
tasks_gen/eval/G0005/task.json
Normal file
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"id": "G0005",
|
||||
"category": "D",
|
||||
"object_type": "CLAS",
|
||||
"difficulty": 2,
|
||||
"release_target": "v816",
|
||||
"expected_outcome": "implement",
|
||||
"budget": {
|
||||
"max_tool_calls": 60,
|
||||
"max_activations": 20
|
||||
},
|
||||
"seed": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}ZONE_MASTER",
|
||||
"file": "seed/zone_master.clas.abap",
|
||||
"description": "Zone master data provider"
|
||||
}
|
||||
],
|
||||
"contract": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}CX_FREIGHT_ERROR"
|
||||
},
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}CX_UNKNOWN_ZONE"
|
||||
},
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}CX_INVALID_WEIGHT"
|
||||
},
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}CX_WEIGHT_LIMIT"
|
||||
},
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}FREIGHT_CALC"
|
||||
}
|
||||
],
|
||||
"out_of_scope": [
|
||||
"{{P}}ZONE_MASTER"
|
||||
],
|
||||
"hidden_tests": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}T02_HIDDEN",
|
||||
"file": "hidden/t02_hidden.clas.abap",
|
||||
"description": "T02 hidden tests"
|
||||
}
|
||||
],
|
||||
"reference": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}CX_FREIGHT_ERROR",
|
||||
"file": "reference/cx_freight_error.clas.abap",
|
||||
"description": "Abstract base exception"
|
||||
},
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}CX_UNKNOWN_ZONE",
|
||||
"file": "reference/cx_unknown_zone.clas.abap",
|
||||
"description": "Unknown zone exception"
|
||||
},
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}CX_INVALID_WEIGHT",
|
||||
"file": "reference/cx_invalid_weight.clas.abap",
|
||||
"description": "Invalid weight exception"
|
||||
},
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}CX_WEIGHT_LIMIT",
|
||||
"file": "reference/cx_weight_limit.clas.abap",
|
||||
"description": "Weight limit exception"
|
||||
},
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}FREIGHT_CALC",
|
||||
"file": "reference/freight_calc.clas.abap",
|
||||
"description": "Freight cost calculator",
|
||||
"testclasses_file": "reference/freight_calc.testclasses.abap"
|
||||
}
|
||||
],
|
||||
"craft_checks": [
|
||||
"method_length",
|
||||
"exception_handling"
|
||||
]
|
||||
}
|
||||
30
tasks_gen/eval/G0006/generation.json
Normal file
30
tasks_gen/eval/G0006/generation.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"id": "G0006",
|
||||
"pool": "eval",
|
||||
"object_type": "CLAS",
|
||||
"category": "E",
|
||||
"attempts": [
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": null,
|
||||
"null": null
|
||||
},
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": null,
|
||||
"null": null
|
||||
},
|
||||
{
|
||||
"stage": "bundle",
|
||||
"errors": [
|
||||
"invalid JSON: Expecting value: line 1 column 1 (char 0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": null,
|
||||
"null": null
|
||||
}
|
||||
],
|
||||
"accepted": false
|
||||
}
|
||||
154
tasks_gen/eval/G0006/hidden/t02_hidden.clas.abap
Normal file
154
tasks_gen/eval/G0006/hidden/t02_hidden.clas.abap
Normal file
@@ -0,0 +1,154 @@
|
||||
CLASS {{p}}t02_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
|
||||
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
|
||||
PRIVATE SECTION.
|
||||
DATA mo_cut TYPE REF TO {{p}}freight_calculator.
|
||||
|
||||
METHODS setup.
|
||||
METHODS assert_calculation
|
||||
IMPORTING iv_weight_kg TYPE p LENGTH 8 DECIMALS 2
|
||||
iv_zone TYPE string
|
||||
iv_express TYPE abap_bool DEFAULT abap_false
|
||||
is_expected TYPE {{p}}freight_calculator=>ty_result.
|
||||
|
||||
METHODS standard_parcel FOR TESTING.
|
||||
METHODS express_parcel FOR TESTING.
|
||||
METHODS zone_normalization FOR TESTING.
|
||||
METHODS rounding_half_up FOR TESTING.
|
||||
METHODS zero_weight FOR TESTING.
|
||||
METHODS negative_weight FOR TESTING.
|
||||
METHODS unknown_zone FOR TESTING.
|
||||
METHODS weight_error_wins FOR TESTING.
|
||||
METHODS supported_zones FOR TESTING.
|
||||
METHODS unsupported_zones FOR TESTING.
|
||||
METHODS all_rates FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}t02_hidden IMPLEMENTATION.
|
||||
METHOD setup.
|
||||
mo_cut = NEW {{p}}freight_calculator( ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD assert_calculation.
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = is_expected
|
||||
act = mo_cut->calculate( iv_weight_kg = iv_weight_kg
|
||||
iv_zone = iv_zone
|
||||
iv_express = iv_express )
|
||||
msg = |zone { iv_zone }| ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD standard_parcel.
|
||||
assert_calculation(
|
||||
iv_weight_kg = '10.00'
|
||||
iv_zone = `DE`
|
||||
is_expected = VALUE #( status = {{p}}freight_calculator=>c_status-ok
|
||||
base = '45.00'
|
||||
surcharge = '0.00'
|
||||
total = '45.00' ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD express_parcel.
|
||||
assert_calculation(
|
||||
iv_weight_kg = '10.00'
|
||||
iv_zone = `DE`
|
||||
iv_express = abap_true
|
||||
is_expected = VALUE #( status = {{p}}freight_calculator=>c_status-ok
|
||||
base = '45.00'
|
||||
surcharge = '11.25'
|
||||
total = '56.25' ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD zone_normalization.
|
||||
assert_calculation(
|
||||
iv_weight_kg = '5.00'
|
||||
iv_zone = ` ch `
|
||||
is_expected = VALUE #( status = {{p}}freight_calculator=>c_status-ok
|
||||
base = '39.00'
|
||||
total = '39.00' ) ).
|
||||
assert_calculation(
|
||||
iv_weight_kg = '5.00'
|
||||
iv_zone = `de`
|
||||
is_expected = VALUE #( status = {{p}}freight_calculator=>c_status-ok
|
||||
base = '22.50'
|
||||
total = '22.50' ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD rounding_half_up.
|
||||
assert_calculation(
|
||||
iv_weight_kg = '3.33'
|
||||
iv_zone = `DE`
|
||||
iv_express = abap_true
|
||||
is_expected = VALUE #( status = {{p}}freight_calculator=>c_status-ok
|
||||
base = '14.99'
|
||||
surcharge = '3.75'
|
||||
total = '18.74' ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD zero_weight.
|
||||
assert_calculation(
|
||||
iv_weight_kg = '0.00'
|
||||
iv_zone = `DE`
|
||||
is_expected = VALUE #( status = {{p}}freight_calculator=>c_status-weight ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD negative_weight.
|
||||
assert_calculation(
|
||||
iv_weight_kg = '-2.50'
|
||||
iv_zone = `DE`
|
||||
iv_express = abap_true
|
||||
is_expected = VALUE #( status = {{p}}freight_calculator=>c_status-weight ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD unknown_zone.
|
||||
assert_calculation(
|
||||
iv_weight_kg = '5.00'
|
||||
iv_zone = `XX`
|
||||
is_expected = VALUE #( status = {{p}}freight_calculator=>c_status-zone ) ).
|
||||
assert_calculation(
|
||||
iv_weight_kg = '5.00'
|
||||
iv_zone = `DEU`
|
||||
is_expected = VALUE #( status = {{p}}freight_calculator=>c_status-zone ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD weight_error_wins.
|
||||
assert_calculation(
|
||||
iv_weight_kg = '0.00'
|
||||
iv_zone = `XX`
|
||||
is_expected = VALUE #( status = {{p}}freight_calculator=>c_status-weight ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD supported_zones.
|
||||
cl_abap_unit_assert=>assert_equals( exp = abap_true act = mo_cut->is_zone_supported( `de` ) msg = `de` ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = abap_true act = mo_cut->is_zone_supported( ` DE ` ) msg = ` DE ` ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = abap_true act = mo_cut->is_zone_supported( `at` ) msg = `at` ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = abap_true act = mo_cut->is_zone_supported( `es` ) msg = `es` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD unsupported_zones.
|
||||
DATA lv_empty TYPE string.
|
||||
cl_abap_unit_assert=>assert_equals( exp = abap_false act = mo_cut->is_zone_supported( `XX` ) msg = `XX` ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = abap_false act = mo_cut->is_zone_supported( lv_empty ) msg = `empty` ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = abap_false act = mo_cut->is_zone_supported( ` ` ) msg = `blanks` ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = abap_false act = mo_cut->is_zone_supported( `DEU` ) msg = `DEU` ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = abap_false act = mo_cut->is_zone_supported( `DE1` ) msg = `DE1` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD all_rates.
|
||||
assert_calculation( iv_weight_kg = '1.00' iv_zone = `DE`
|
||||
is_expected = VALUE #( status = {{p}}freight_calculator=>c_status-ok base = '4.50' total = '4.50' ) ).
|
||||
assert_calculation( iv_weight_kg = '1.00' iv_zone = `AT`
|
||||
is_expected = VALUE #( status = {{p}}freight_calculator=>c_status-ok base = '5.20' total = '5.20' ) ).
|
||||
assert_calculation( iv_weight_kg = '1.00' iv_zone = `CH`
|
||||
is_expected = VALUE #( status = {{p}}freight_calculator=>c_status-ok base = '7.80' total = '7.80' ) ).
|
||||
assert_calculation( iv_weight_kg = '1.00' iv_zone = `FR`
|
||||
is_expected = VALUE #( status = {{p}}freight_calculator=>c_status-ok base = '6.40' total = '6.40' ) ).
|
||||
assert_calculation( iv_weight_kg = '1.00' iv_zone = `NL`
|
||||
is_expected = VALUE #( status = {{p}}freight_calculator=>c_status-ok base = '4.90' total = '4.90' ) ).
|
||||
assert_calculation( iv_weight_kg = '1.00' iv_zone = `PL`
|
||||
is_expected = VALUE #( status = {{p}}freight_calculator=>c_status-ok base = '5.10' total = '5.10' ) ).
|
||||
assert_calculation( iv_weight_kg = '1.00' iv_zone = `ES`
|
||||
is_expected = VALUE #( status = {{p}}freight_calculator=>c_status-ok base = '6.90' total = '6.90' ) ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
130
tasks_gen/eval/G0006/reference/freight_calculator.clas.abap
Normal file
130
tasks_gen/eval/G0006/reference/freight_calculator.clas.abap
Normal file
@@ -0,0 +1,130 @@
|
||||
CLASS {{p}}freight_calculator DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
||||
PUBLIC SECTION.
|
||||
TYPES:
|
||||
BEGIN OF ty_result,
|
||||
status TYPE c LENGTH 10,
|
||||
base TYPE p LENGTH 9 DECIMALS 2,
|
||||
surcharge TYPE p LENGTH 9 DECIMALS 2,
|
||||
total TYPE p LENGTH 9 DECIMALS 2,
|
||||
END OF ty_result.
|
||||
|
||||
CONSTANTS:
|
||||
BEGIN OF c_status,
|
||||
ok TYPE c LENGTH 10 VALUE 'OK',
|
||||
weight TYPE c LENGTH 10 VALUE 'WEIGHT',
|
||||
zone TYPE c LENGTH 10 VALUE 'ZONE',
|
||||
END OF c_status.
|
||||
|
||||
METHODS calculate
|
||||
IMPORTING
|
||||
iv_weight_kg TYPE p LENGTH 8 DECIMALS 2
|
||||
iv_zone TYPE string
|
||||
iv_express TYPE abap_bool DEFAULT abap_false
|
||||
RETURNING
|
||||
VALUE(rs_result) TYPE ty_result.
|
||||
|
||||
METHODS is_zone_supported
|
||||
IMPORTING
|
||||
iv_zone TYPE string
|
||||
RETURNING
|
||||
VALUE(rv_supported) TYPE abap_bool.
|
||||
|
||||
PRIVATE SECTION.
|
||||
TYPES:
|
||||
BEGIN OF ty_rate,
|
||||
zone TYPE c LENGTH 2,
|
||||
rate TYPE p LENGTH 6 DECIMALS 2,
|
||||
END OF ty_rate,
|
||||
tt_rate TYPE HASHED TABLE OF ty_rate WITH UNIQUE KEY zone.
|
||||
|
||||
CONSTANTS c_express_share TYPE p LENGTH 5 DECIMALS 2 VALUE '0.25'.
|
||||
|
||||
METHODS normalize_zone
|
||||
IMPORTING iv_zone TYPE string
|
||||
RETURNING VALUE(rv_zone) TYPE string.
|
||||
METHODS rates
|
||||
RETURNING VALUE(rt_rate) TYPE tt_rate.
|
||||
METHODS rate_for_zone
|
||||
IMPORTING iv_zone TYPE string
|
||||
RETURNING VALUE(rv_rate) TYPE p LENGTH 6 DECIMALS 2.
|
||||
METHODS empty_result
|
||||
IMPORTING iv_status TYPE c LENGTH 10
|
||||
RETURNING VALUE(rs_result) TYPE ty_result.
|
||||
METHODS base_amount
|
||||
IMPORTING iv_rate TYPE p LENGTH 6 DECIMALS 2
|
||||
iv_weight_kg TYPE p LENGTH 8 DECIMALS 2
|
||||
RETURNING VALUE(rv_base) TYPE p LENGTH 9 DECIMALS 2.
|
||||
METHODS surcharge_amount
|
||||
IMPORTING iv_base TYPE p LENGTH 9 DECIMALS 2
|
||||
iv_express TYPE abap_bool
|
||||
RETURNING VALUE(rv_surcharge) TYPE p LENGTH 9 DECIMALS 2.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}freight_calculator IMPLEMENTATION.
|
||||
METHOD calculate.
|
||||
DATA(lv_zone) = normalize_zone( iv_zone ).
|
||||
|
||||
IF iv_weight_kg <= 0.
|
||||
rs_result = empty_result( c_status-weight ).
|
||||
RETURN.
|
||||
ENDIF.
|
||||
|
||||
IF is_zone_supported( lv_zone ) = abap_false.
|
||||
rs_result = empty_result( c_status-zone ).
|
||||
RETURN.
|
||||
ENDIF.
|
||||
|
||||
rs_result-base = base_amount( iv_rate = rate_for_zone( lv_zone )
|
||||
iv_weight_kg = iv_weight_kg ).
|
||||
rs_result-surcharge = surcharge_amount( iv_base = rs_result-base
|
||||
iv_express = iv_express ).
|
||||
rs_result-total = rs_result-base + rs_result-surcharge.
|
||||
rs_result-status = c_status-ok.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD is_zone_supported.
|
||||
DATA(lv_zone) = normalize_zone( iv_zone ).
|
||||
DATA(lt_rate) = rates( ).
|
||||
rv_supported = xsdbool( line_exists( lt_rate[ zone = lv_zone ] ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD normalize_zone.
|
||||
rv_zone = to_upper( replace( val = iv_zone sub = ` ` with = `` occ = 0 ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD rates.
|
||||
rt_rate = VALUE #( ( zone = 'DE' rate = '4.50' )
|
||||
( zone = 'AT' rate = '5.20' )
|
||||
( zone = 'CH' rate = '7.80' )
|
||||
( zone = 'FR' rate = '6.40' )
|
||||
( zone = 'NL' rate = '4.90' )
|
||||
( zone = 'PL' rate = '5.10' )
|
||||
( zone = 'ES' rate = '6.90' ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD rate_for_zone.
|
||||
DATA(lt_rate) = rates( ).
|
||||
READ TABLE lt_rate WITH TABLE KEY zone = iv_zone INTO DATA(ls_rate).
|
||||
rv_rate = ls_rate-rate.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD empty_result.
|
||||
rs_result = VALUE #( status = iv_status ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD base_amount.
|
||||
DATA lv_amount TYPE p LENGTH 13 DECIMALS 4.
|
||||
lv_amount = iv_rate * iv_weight_kg.
|
||||
rv_base = round( val = lv_amount dec = 2 ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD surcharge_amount.
|
||||
IF iv_express = abap_false.
|
||||
RETURN.
|
||||
ENDIF.
|
||||
DATA lv_amount TYPE p LENGTH 13 DECIMALS 4.
|
||||
lv_amount = iv_base * c_express_share.
|
||||
rv_surcharge = round( val = lv_amount dec = 2 ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
@@ -0,0 +1,56 @@
|
||||
CLASS ltc_freight_calculator DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
PRIVATE SECTION.
|
||||
DATA mo_cut TYPE REF TO {{p}}freight_calculator.
|
||||
METHODS setup.
|
||||
METHODS base_parcel FOR TESTING.
|
||||
METHODS express_parcel FOR TESTING.
|
||||
METHODS zone_is_normalized FOR TESTING.
|
||||
METHODS weight_error FOR TESTING.
|
||||
METHODS unknown_zone FOR TESTING.
|
||||
METHODS zone_support FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS ltc_freight_calculator IMPLEMENTATION.
|
||||
METHOD setup.
|
||||
mo_cut = NEW {{p}}freight_calculator( ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD base_parcel.
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = VALUE {{p}}freight_calculator=>ty_result( status = 'OK' base = '45.00' total = '45.00' )
|
||||
act = mo_cut->calculate( iv_weight_kg = '10.00' iv_zone = `DE` ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD express_parcel.
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = VALUE {{p}}freight_calculator=>ty_result( status = 'OK'
|
||||
base = '45.00'
|
||||
surcharge = '11.25'
|
||||
total = '56.25' )
|
||||
act = mo_cut->calculate( iv_weight_kg = '10.00' iv_zone = `DE` iv_express = abap_true ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD zone_is_normalized.
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = VALUE {{p}}freight_calculator=>ty_result( status = 'OK' base = '39.00' total = '39.00' )
|
||||
act = mo_cut->calculate( iv_weight_kg = '5.00' iv_zone = ` ch ` ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD weight_error.
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = VALUE {{p}}freight_calculator=>ty_result( status = 'WEIGHT' )
|
||||
act = mo_cut->calculate( iv_weight_kg = '0.00' iv_zone = `DE` ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD unknown_zone.
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = VALUE {{p}}freight_calculator=>ty_result( status = 'ZONE' )
|
||||
act = mo_cut->calculate( iv_weight_kg = '5.00' iv_zone = `XX` ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD zone_support.
|
||||
cl_abap_unit_assert=>assert_true( mo_cut->is_zone_supported( `de` ) ).
|
||||
cl_abap_unit_assert=>assert_false( mo_cut->is_zone_supported( `XX` ) ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
106
tasks_gen/eval/G0006/seed/freight_calculator.clas.abap
Normal file
106
tasks_gen/eval/G0006/seed/freight_calculator.clas.abap
Normal file
@@ -0,0 +1,106 @@
|
||||
CLASS {{p}}freight_calculator DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
||||
PUBLIC SECTION.
|
||||
TYPES:
|
||||
BEGIN OF ty_result,
|
||||
status TYPE c LENGTH 10,
|
||||
base TYPE p LENGTH 9 DECIMALS 2,
|
||||
surcharge TYPE p LENGTH 9 DECIMALS 2,
|
||||
total TYPE p LENGTH 9 DECIMALS 2,
|
||||
END OF ty_result.
|
||||
|
||||
CONSTANTS:
|
||||
BEGIN OF c_status,
|
||||
ok TYPE c LENGTH 10 VALUE 'OK',
|
||||
weight TYPE c LENGTH 10 VALUE 'WEIGHT',
|
||||
zone TYPE c LENGTH 10 VALUE 'ZONE',
|
||||
END OF c_status.
|
||||
|
||||
METHODS calculate
|
||||
IMPORTING
|
||||
iv_weight_kg TYPE p LENGTH 8 DECIMALS 2
|
||||
iv_zone TYPE string
|
||||
iv_express TYPE abap_bool DEFAULT abap_false
|
||||
RETURNING
|
||||
VALUE(rs_result) TYPE ty_result.
|
||||
|
||||
METHODS is_zone_supported
|
||||
IMPORTING
|
||||
iv_zone TYPE string
|
||||
RETURNING
|
||||
VALUE(rv_supported) TYPE abap_bool.
|
||||
|
||||
PRIVATE SECTION.
|
||||
DATA mv_last_zone TYPE string.
|
||||
CONSTANTS c_express_share TYPE p LENGTH 5 DECIMALS 2 VALUE '0.25'.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}freight_calculator IMPLEMENTATION.
|
||||
METHOD calculate.
|
||||
DATA lv_zone TYPE string.
|
||||
DATA lv_rate TYPE p LENGTH 6 DECIMALS 2.
|
||||
DATA lv_amount TYPE p LENGTH 13 DECIMALS 4.
|
||||
DATA lv_express TYPE abap_bool.
|
||||
|
||||
lv_zone = to_upper( replace( val = iv_zone sub = ` ` with = `` occ = 0 ) ).
|
||||
mv_last_zone = lv_zone.
|
||||
|
||||
IF iv_weight_kg <= 0.
|
||||
rs_result-status = 'WEIGHT'.
|
||||
rs_result-base = 0.
|
||||
rs_result-surcharge = 0.
|
||||
rs_result-total = 0.
|
||||
RETURN.
|
||||
ENDIF.
|
||||
|
||||
* IF lv_zone = 'BE'.
|
||||
* lv_rate = '5.60'.
|
||||
* ENDIF.
|
||||
IF lv_zone = 'DE'.
|
||||
lv_rate = '4.50'.
|
||||
ELSEIF lv_zone = 'AT'.
|
||||
lv_rate = '5.20'.
|
||||
ELSEIF lv_zone = 'CH'.
|
||||
lv_rate = '7.80'.
|
||||
ELSEIF lv_zone = 'FR'.
|
||||
lv_rate = '6.40'.
|
||||
ELSEIF lv_zone = 'NL'.
|
||||
lv_rate = '4.90'.
|
||||
ELSEIF lv_zone = 'PL'.
|
||||
lv_rate = '5.10'.
|
||||
ELSEIF lv_zone = 'ES'.
|
||||
lv_rate = '6.90'.
|
||||
ELSE.
|
||||
rs_result-status = 'ZONE'.
|
||||
rs_result-base = 0.
|
||||
rs_result-surcharge = 0.
|
||||
rs_result-total = 0.
|
||||
RETURN.
|
||||
ENDIF.
|
||||
|
||||
lv_amount = lv_rate * iv_weight_kg.
|
||||
rs_result-base = round( val = lv_amount dec = 2 ).
|
||||
|
||||
lv_express = iv_express.
|
||||
IF lv_express = abap_true.
|
||||
lv_amount = rs_result-base * c_express_share.
|
||||
rs_result-surcharge = round( val = lv_amount dec = 2 ).
|
||||
ELSE.
|
||||
rs_result-surcharge = 0.
|
||||
ENDIF.
|
||||
|
||||
rs_result-total = rs_result-base + rs_result-surcharge.
|
||||
rs_result-status = 'OK'.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD is_zone_supported.
|
||||
DATA lv_zone TYPE string.
|
||||
lv_zone = to_upper( replace( val = iv_zone sub = ` ` with = `` occ = 0 ) ).
|
||||
IF lv_zone = 'DE' OR lv_zone = 'AT' OR lv_zone = 'CH' OR lv_zone = 'FR'
|
||||
OR lv_zone = 'NL' OR lv_zone = 'PL' OR lv_zone = 'ES'.
|
||||
rv_supported = abap_true.
|
||||
ELSE.
|
||||
rv_supported = abap_false.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
@@ -0,0 +1,25 @@
|
||||
CLASS ltc_legacy_freight DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
PRIVATE SECTION.
|
||||
METHODS base_parcel FOR TESTING.
|
||||
METHODS express_parcel FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS ltc_legacy_freight IMPLEMENTATION.
|
||||
METHOD base_parcel.
|
||||
DATA(lo_cut) = NEW {{p}}freight_calculator( ).
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = VALUE {{p}}freight_calculator=>ty_result( status = 'OK' base = '45.00' total = '45.00' )
|
||||
act = lo_cut->calculate( iv_weight_kg = '10.00' iv_zone = `DE` ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD express_parcel.
|
||||
DATA(lo_cut) = NEW {{p}}freight_calculator( ).
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = VALUE {{p}}freight_calculator=>ty_result( status = 'OK'
|
||||
base = '45.00'
|
||||
surcharge = '11.25'
|
||||
total = '56.25' )
|
||||
act = lo_cut->calculate( iv_weight_kg = '10.00' iv_zone = `DE` iv_express = abap_true ) ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
67
tasks_gen/eval/G0006/spec.md
Normal file
67
tasks_gen/eval/G0006/spec.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# 1. Goal
|
||||
Refactor the class {{P}}FREIGHT_CALCULATOR. The class calculates the freight cost of a
|
||||
parcel. The calculation is correct, but the code is hard to read and hard to maintain.
|
||||
Make the code clean. The behavior and the public API must not change.
|
||||
|
||||
# 2. Open questions
|
||||
None.
|
||||
|
||||
# 3. Context
|
||||
- The class {{P}}FREIGHT_CALCULATOR exists in package $TMP. It is active.
|
||||
- The class contains legacy code: one long method, nested IF blocks, hard-coded
|
||||
numbers, a private attribute that is written but never read, and commented-out code.
|
||||
- A logistics program calls the class. That program must continue to work without a
|
||||
change. Therefore the behavior of the class must stay the same.
|
||||
|
||||
# 4. Contract
|
||||
The public part of the class must stay exactly as it is:
|
||||
- The class {{P}}FREIGHT_CALCULATOR is public, final, and has a public constructor
|
||||
without parameters.
|
||||
- Public type TY_RESULT with these components: STATUS TYPE c LENGTH 10,
|
||||
BASE TYPE p LENGTH 9 DECIMALS 2, SURCHARGE TYPE p LENGTH 9 DECIMALS 2,
|
||||
TOTAL TYPE p LENGTH 9 DECIMALS 2.
|
||||
- Public constant structure C_STATUS with the components OK, WEIGHT and ZONE. Each
|
||||
component has the type c LENGTH 10. The values are 'OK', 'WEIGHT' and 'ZONE'.
|
||||
- Public method CALCULATE:
|
||||
IMPORTING iv_weight_kg TYPE p LENGTH 8 DECIMALS 2
|
||||
iv_zone TYPE string
|
||||
iv_express TYPE abap_bool DEFAULT abap_false
|
||||
RETURNING VALUE(rs_result) TYPE ty_result.
|
||||
- Public method IS_ZONE_SUPPORTED:
|
||||
IMPORTING iv_zone TYPE string
|
||||
RETURNING VALUE(rv_supported) TYPE abap_bool.
|
||||
- Do not add other public components or public methods.
|
||||
|
||||
# 5. Business rules
|
||||
The rules below describe the behavior of the class. The behavior must not change.
|
||||
1. Normalize the zone: remove all blanks and convert all letters to upper case. A
|
||||
zone is known only if the normalized zone is exactly equal to a zone code. Do not
|
||||
shorten the zone and do not extend the zone.
|
||||
2. Check the weight first. If the weight is not greater than zero, return the status
|
||||
WEIGHT with BASE, SURCHARGE and TOTAL equal to zero. This check comes before the
|
||||
zone check.
|
||||
3. If the normalized zone is not known, return the status ZONE with BASE, SURCHARGE
|
||||
and TOTAL equal to zero.
|
||||
4. The rate per kilogram depends on the zone: DE 4.50, AT 5.20, CH 7.80, FR 6.40,
|
||||
NL 4.90, PL 5.10, ES 6.90.
|
||||
5. BASE = rate x weight. Round BASE to 2 decimal places. Round half away from zero.
|
||||
6. If iv_express is abap_true, SURCHARGE = BASE x 25 / 100, rounded to 2 decimal
|
||||
places. Else SURCHARGE is zero.
|
||||
7. TOTAL = BASE + SURCHARGE.
|
||||
8. Return the status OK.
|
||||
9. IS_ZONE_SUPPORTED returns abap_true if the normalized zone is known, else
|
||||
abap_false. A blank zone is not known.
|
||||
|
||||
# 6. Constraints
|
||||
- Release target: SAP_BASIS 816 (ABAP Platform 2025).
|
||||
- Coding standards: Clean ABAP. Every method has less than 40 statements. No global
|
||||
variables. No IF block nested deeper than 2 levels. No hard-coded rate in a
|
||||
statement: use a constant or a table. Remove the private attribute that is never
|
||||
read. Remove the commented-out code. No comment that restates the code.
|
||||
- Out of scope: do not change the public API, do not change the behavior, do not
|
||||
create new global objects, do not change the class name.
|
||||
|
||||
# 7. Acceptance
|
||||
- The class is active and has no syntax error.
|
||||
- The hidden tests pass.
|
||||
- Write your own ABAP Unit tests as local test classes in the class.
|
||||
54
tasks_gen/eval/G0006/task.json
Normal file
54
tasks_gen/eval/G0006/task.json
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"id": "G0006",
|
||||
"category": "E",
|
||||
"object_type": "CLAS",
|
||||
"difficulty": 2,
|
||||
"release_target": "v816",
|
||||
"expected_outcome": "implement",
|
||||
"budget": {
|
||||
"max_tool_calls": 60,
|
||||
"max_activations": 15
|
||||
},
|
||||
"seed": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}FREIGHT_CALCULATOR",
|
||||
"file": "seed/freight_calculator.clas.abap",
|
||||
"description": "Legacy freight calculator. Correct behavior, poor structure: one long method, nested IF blocks, hard-coded rates, a private attribute that is written but never read, commented-out code."
|
||||
}
|
||||
],
|
||||
"contract": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}FREIGHT_CALCULATOR"
|
||||
}
|
||||
],
|
||||
"out_of_scope": [
|
||||
"New global objects",
|
||||
"Changes to the public API of {{P}}FREIGHT_CALCULATOR",
|
||||
"Changes to the result of the calculation"
|
||||
],
|
||||
"hidden_tests": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}T02_HIDDEN",
|
||||
"file": "hidden/t02_hidden.clas.abap",
|
||||
"description": "T02 hidden tests for the refactored freight calculator"
|
||||
}
|
||||
],
|
||||
"reference": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}FREIGHT_CALCULATOR",
|
||||
"file": "reference/freight_calculator.clas.abap",
|
||||
"description": "Refactored freight calculator with extracted methods and a rate table",
|
||||
"testclasses_file": "reference/freight_calculator.testclasses.abap"
|
||||
}
|
||||
],
|
||||
"craft_checks": [
|
||||
"method_length",
|
||||
"nesting_depth",
|
||||
"magic_number",
|
||||
"dead_code"
|
||||
]
|
||||
}
|
||||
14
tasks_gen/eval/G0007/generation.json
Normal file
14
tasks_gen/eval/G0007/generation.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"id": "G0007",
|
||||
"pool": "eval",
|
||||
"object_type": "CLAS",
|
||||
"category": "F",
|
||||
"attempts": [
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 100.0,
|
||||
"null": 0
|
||||
}
|
||||
],
|
||||
"accepted": true
|
||||
}
|
||||
103
tasks_gen/eval/G0007/hidden/t02_hidden.clas.abap
Normal file
103
tasks_gen/eval/G0007/hidden/t02_hidden.clas.abap
Normal file
@@ -0,0 +1,103 @@
|
||||
CLASS {{p}}t02_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
|
||||
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
PRIVATE SECTION.
|
||||
DATA mo_cut TYPE REF TO {{p}}if_loyalty_rules.
|
||||
METHODS setup.
|
||||
METHODS tier_boundaries FOR TESTING.
|
||||
METHODS tier_negative FOR TESTING.
|
||||
METHODS tier_zero FOR TESTING.
|
||||
METHODS discount_all_tiers FOR TESTING.
|
||||
METHODS discount_unknown_tier FOR TESTING.
|
||||
METHODS next_tier_from_basic FOR TESTING.
|
||||
METHODS next_tier_from_silver FOR TESTING.
|
||||
METHODS next_tier_from_gold FOR TESTING.
|
||||
METHODS next_tier_from_platinum FOR TESTING.
|
||||
METHODS next_tier_negative_points FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}t02_hidden IMPLEMENTATION.
|
||||
METHOD setup.
|
||||
mo_cut = NEW {{p}}loyalty_evaluator( ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD tier_boundaries.
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_loyalty_rules=>c_tier-basic
|
||||
act = mo_cut->get_tier( 999 ) msg = `999` ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_loyalty_rules=>c_tier-silver
|
||||
act = mo_cut->get_tier( 1000 ) msg = `1000` ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_loyalty_rules=>c_tier-silver
|
||||
act = mo_cut->get_tier( 4999 ) msg = `4999` ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_loyalty_rules=>c_tier-gold
|
||||
act = mo_cut->get_tier( 5000 ) msg = `5000` ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_loyalty_rules=>c_tier-gold
|
||||
act = mo_cut->get_tier( 19999 ) msg = `19999` ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_loyalty_rules=>c_tier-platinum
|
||||
act = mo_cut->get_tier( 20000 ) msg = `20000` ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_loyalty_rules=>c_tier-platinum
|
||||
act = mo_cut->get_tier( 500000 ) msg = `500000` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD tier_negative.
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_loyalty_rules=>c_tier-basic
|
||||
act = mo_cut->get_tier( -1 ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_loyalty_rules=>c_tier-basic
|
||||
act = mo_cut->get_tier( -5000 ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD tier_zero.
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_loyalty_rules=>c_tier-basic
|
||||
act = mo_cut->get_tier( 0 ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD discount_all_tiers.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 0
|
||||
act = mo_cut->get_discount( {{p}}if_loyalty_rules=>c_tier-basic ) msg = `basic` ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 3
|
||||
act = mo_cut->get_discount( {{p}}if_loyalty_rules=>c_tier-silver ) msg = `silver` ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 7
|
||||
act = mo_cut->get_discount( {{p}}if_loyalty_rules=>c_tier-gold ) msg = `gold` ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 12
|
||||
act = mo_cut->get_discount( {{p}}if_loyalty_rules=>c_tier-platinum ) msg = `platinum` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD discount_unknown_tier.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 0
|
||||
act = mo_cut->get_discount( 'X' ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 0
|
||||
act = mo_cut->get_discount( ' ' ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD next_tier_from_basic.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1000
|
||||
act = mo_cut->points_to_next_tier( 0 ) msg = `0` ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1
|
||||
act = mo_cut->points_to_next_tier( 999 ) msg = `999` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD next_tier_from_silver.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 4000
|
||||
act = mo_cut->points_to_next_tier( 1000 ) msg = `1000` ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1
|
||||
act = mo_cut->points_to_next_tier( 4999 ) msg = `4999` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD next_tier_from_gold.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 15000
|
||||
act = mo_cut->points_to_next_tier( 5000 ) msg = `5000` ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1
|
||||
act = mo_cut->points_to_next_tier( 19999 ) msg = `19999` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD next_tier_from_platinum.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 0
|
||||
act = mo_cut->points_to_next_tier( 20000 ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 0
|
||||
act = mo_cut->points_to_next_tier( 999999 ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD next_tier_negative_points.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1000
|
||||
act = mo_cut->points_to_next_tier( -7 ) ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
79
tasks_gen/eval/G0007/reference/loyalty_evaluator.clas.abap
Normal file
79
tasks_gen/eval/G0007/reference/loyalty_evaluator.clas.abap
Normal file
@@ -0,0 +1,79 @@
|
||||
CLASS {{p}}loyalty_evaluator DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
||||
PUBLIC SECTION.
|
||||
INTERFACES {{p}}if_loyalty_rules.
|
||||
|
||||
PRIVATE SECTION.
|
||||
CONSTANTS:
|
||||
c_silver_threshold TYPE i VALUE 1000,
|
||||
c_gold_threshold TYPE i VALUE 5000,
|
||||
c_platinum_threshold TYPE i VALUE 20000.
|
||||
|
||||
METHODS normalize_points
|
||||
IMPORTING iv_points TYPE i
|
||||
RETURNING VALUE(rv_points) TYPE i.
|
||||
|
||||
METHODS tier_of
|
||||
IMPORTING iv_points TYPE i
|
||||
RETURNING VALUE(rv_tier) TYPE {{p}}if_loyalty_rules=>ty_tier.
|
||||
|
||||
METHODS points_to_tier
|
||||
IMPORTING iv_points TYPE i
|
||||
iv_tier TYPE {{p}}if_loyalty_rules=>ty_tier
|
||||
RETURNING VALUE(rv_points) TYPE i.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}loyalty_evaluator IMPLEMENTATION.
|
||||
METHOD {{p}}if_loyalty_rules~get_tier.
|
||||
rv_tier = tier_of( normalize_points( iv_points ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD {{p}}if_loyalty_rules~get_discount.
|
||||
CASE iv_tier.
|
||||
WHEN {{p}}if_loyalty_rules=>c_tier-silver.
|
||||
rv_percent = 3.
|
||||
WHEN {{p}}if_loyalty_rules=>c_tier-gold.
|
||||
rv_percent = 7.
|
||||
WHEN {{p}}if_loyalty_rules=>c_tier-platinum.
|
||||
rv_percent = 12.
|
||||
WHEN OTHERS.
|
||||
rv_percent = 0.
|
||||
ENDCASE.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD {{p}}if_loyalty_rules~points_to_next_tier.
|
||||
DATA(lv_points) = normalize_points( iv_points ).
|
||||
DATA(lv_tier) = tier_of( lv_points ).
|
||||
rv_points = points_to_tier( iv_points = lv_points
|
||||
iv_tier = lv_tier ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD normalize_points.
|
||||
rv_points = COND #( WHEN iv_points < 0 THEN 0 ELSE iv_points ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD tier_of.
|
||||
IF iv_points >= c_platinum_threshold.
|
||||
rv_tier = {{p}}if_loyalty_rules=>c_tier-platinum.
|
||||
ELSEIF iv_points >= c_gold_threshold.
|
||||
rv_tier = {{p}}if_loyalty_rules=>c_tier-gold.
|
||||
ELSEIF iv_points >= c_silver_threshold.
|
||||
rv_tier = {{p}}if_loyalty_rules=>c_tier-silver.
|
||||
ELSE.
|
||||
rv_tier = {{p}}if_loyalty_rules=>c_tier-basic.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD points_to_tier.
|
||||
CASE iv_tier.
|
||||
WHEN {{p}}if_loyalty_rules=>c_tier-basic.
|
||||
rv_points = c_silver_threshold - iv_points.
|
||||
WHEN {{p}}if_loyalty_rules=>c_tier-silver.
|
||||
rv_points = c_gold_threshold - iv_points.
|
||||
WHEN {{p}}if_loyalty_rules=>c_tier-gold.
|
||||
rv_points = c_platinum_threshold - iv_points.
|
||||
WHEN OTHERS.
|
||||
rv_points = 0.
|
||||
ENDCASE.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
@@ -0,0 +1,77 @@
|
||||
CLASS ltc_loyalty_evaluator DEFINITION FINAL FOR TESTING
|
||||
DURATION SHORT RISK LEVEL HARMLESS.
|
||||
|
||||
PRIVATE SECTION.
|
||||
DATA mo_cut TYPE REF TO {{p}}if_loyalty_rules.
|
||||
|
||||
METHODS setup.
|
||||
METHODS tier_of_boundary_points FOR TESTING.
|
||||
METHODS negative_points_are_basic FOR TESTING.
|
||||
METHODS discount_of_each_tier FOR TESTING.
|
||||
METHODS discount_of_unknown_tier FOR TESTING.
|
||||
METHODS points_to_silver FOR TESTING.
|
||||
METHODS points_to_gold FOR TESTING.
|
||||
METHODS points_to_platinum FOR TESTING.
|
||||
METHODS platinum_needs_no_points FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS ltc_loyalty_evaluator IMPLEMENTATION.
|
||||
METHOD setup.
|
||||
mo_cut = NEW {{p}}loyalty_evaluator( ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD tier_of_boundary_points.
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_loyalty_rules=>c_tier-basic
|
||||
act = mo_cut->get_tier( 0 ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_loyalty_rules=>c_tier-silver
|
||||
act = mo_cut->get_tier( 1000 ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_loyalty_rules=>c_tier-gold
|
||||
act = mo_cut->get_tier( 5000 ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_loyalty_rules=>c_tier-platinum
|
||||
act = mo_cut->get_tier( 20000 ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD negative_points_are_basic.
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_loyalty_rules=>c_tier-basic
|
||||
act = mo_cut->get_tier( -100 ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD discount_of_each_tier.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 0
|
||||
act = mo_cut->get_discount( {{p}}if_loyalty_rules=>c_tier-basic ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 3
|
||||
act = mo_cut->get_discount( {{p}}if_loyalty_rules=>c_tier-silver ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 7
|
||||
act = mo_cut->get_discount( {{p}}if_loyalty_rules=>c_tier-gold ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 12
|
||||
act = mo_cut->get_discount( {{p}}if_loyalty_rules=>c_tier-platinum ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD discount_of_unknown_tier.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 0
|
||||
act = mo_cut->get_discount( 'X' ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD points_to_silver.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1000
|
||||
act = mo_cut->points_to_next_tier( 0 ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1
|
||||
act = mo_cut->points_to_next_tier( 999 ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD points_to_gold.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 4000
|
||||
act = mo_cut->points_to_next_tier( 1000 ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD points_to_platinum.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 15000
|
||||
act = mo_cut->points_to_next_tier( 5000 ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD platinum_needs_no_points.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 0
|
||||
act = mo_cut->points_to_next_tier( 20000 ) ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
24
tasks_gen/eval/G0007/seed/if_loyalty_rules.intf.abap
Normal file
24
tasks_gen/eval/G0007/seed/if_loyalty_rules.intf.abap
Normal file
@@ -0,0 +1,24 @@
|
||||
INTERFACE {{p}}if_loyalty_rules PUBLIC.
|
||||
TYPES ty_points TYPE i.
|
||||
TYPES ty_tier TYPE c LENGTH 1.
|
||||
|
||||
CONSTANTS:
|
||||
BEGIN OF c_tier,
|
||||
basic TYPE ty_tier VALUE 'B',
|
||||
silver TYPE ty_tier VALUE 'S',
|
||||
gold TYPE ty_tier VALUE 'G',
|
||||
platinum TYPE ty_tier VALUE 'P',
|
||||
END OF c_tier.
|
||||
|
||||
METHODS get_tier
|
||||
IMPORTING iv_points TYPE ty_points
|
||||
RETURNING VALUE(rv_tier) TYPE ty_tier.
|
||||
|
||||
METHODS get_discount
|
||||
IMPORTING iv_tier TYPE ty_tier
|
||||
RETURNING VALUE(rv_percent) TYPE i.
|
||||
|
||||
METHODS points_to_next_tier
|
||||
IMPORTING iv_points TYPE ty_points
|
||||
RETURNING VALUE(rv_points) TYPE i.
|
||||
ENDINTERFACE.
|
||||
57
tasks_gen/eval/G0007/spec.md
Normal file
57
tasks_gen/eval/G0007/spec.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# 1. Goal
|
||||
A retail company gives loyalty discounts to its customers. A checkout program needs
|
||||
three values: the loyalty tier of a customer, the discount percent of a tier, and
|
||||
the number of points that a customer still needs for the next tier. The program
|
||||
calls one class for these three values.
|
||||
|
||||
# 2. Open questions
|
||||
None.
|
||||
|
||||
# 3. Context
|
||||
- The interface {{P}}IF_LOYALTY_RULES exists in package $TMP. It is active.
|
||||
- The interface declares the type of a point value, the type of a tier, the
|
||||
constants for all tiers, and three methods. This document does not repeat the
|
||||
signature of the interface. Read the interface before you start.
|
||||
- The three methods are:
|
||||
* the method that returns the tier for a point value,
|
||||
* the method that returns the discount percent of a tier,
|
||||
* the method that returns the number of points that are missing for the next tier.
|
||||
|
||||
# 4. Contract
|
||||
- Create the class {{P}}LOYALTY_EVALUATOR in package $TMP.
|
||||
- The class is public and final. It has a public constructor without parameters.
|
||||
- The class implements {{P}}IF_LOYALTY_RULES.
|
||||
- Do not add other public methods.
|
||||
|
||||
# 5. Business rules
|
||||
Tier of a point value:
|
||||
1. A point value below zero counts as zero.
|
||||
2. 0 to 999 points is the basic tier.
|
||||
3. 1000 to 4999 points is the silver tier.
|
||||
4. 5000 to 19999 points is the gold tier.
|
||||
5. 20000 points and more is the platinum tier.
|
||||
|
||||
Discount percent of a tier:
|
||||
6. basic is 0 percent.
|
||||
7. silver is 3 percent.
|
||||
8. gold is 7 percent.
|
||||
9. platinum is 12 percent.
|
||||
10. Any other tier value is 0 percent.
|
||||
|
||||
Points to the next tier:
|
||||
11. A point value below zero counts as zero.
|
||||
12. basic needs 1000 points minus the point value.
|
||||
13. silver needs 5000 points minus the point value.
|
||||
14. gold needs 20000 points minus the point value.
|
||||
15. platinum needs 0 points.
|
||||
|
||||
# 6. Constraints
|
||||
- Release target: 8.16.
|
||||
- Coding standards: Clean ABAP. Methods below 40 statements. No global variables.
|
||||
No comment that restates the code.
|
||||
- Out of scope: do not change {{P}}IF_LOYALTY_RULES.
|
||||
|
||||
# 7. Acceptance
|
||||
- The class is active and has no syntax error.
|
||||
- The hidden tests pass.
|
||||
- Write your own ABAP Unit tests for the class.
|
||||
51
tasks_gen/eval/G0007/task.json
Normal file
51
tasks_gen/eval/G0007/task.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"id": "G0007",
|
||||
"category": "F",
|
||||
"object_type": "CLAS",
|
||||
"difficulty": 2,
|
||||
"release_target": "v816",
|
||||
"expected_outcome": "implement",
|
||||
"budget": {
|
||||
"max_tool_calls": 40,
|
||||
"max_activations": 10
|
||||
},
|
||||
"seed": [
|
||||
{
|
||||
"type": "INTF",
|
||||
"name": "{{P}}IF_LOYALTY_RULES",
|
||||
"file": "seed/if_loyalty_rules.intf.abap",
|
||||
"description": "Loyalty contract: tier type, point type, tier constants and three methods. The spec does not repeat the signature."
|
||||
}
|
||||
],
|
||||
"contract": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}LOYALTY_EVALUATOR",
|
||||
"implements": "{{P}}IF_LOYALTY_RULES"
|
||||
}
|
||||
],
|
||||
"out_of_scope": [
|
||||
"{{P}}IF_LOYALTY_RULES"
|
||||
],
|
||||
"hidden_tests": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}T02_HIDDEN",
|
||||
"file": "hidden/t02_hidden.clas.abap",
|
||||
"description": "T02 hidden tests"
|
||||
}
|
||||
],
|
||||
"reference": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}LOYALTY_EVALUATOR",
|
||||
"file": "reference/loyalty_evaluator.clas.abap",
|
||||
"description": "Loyalty evaluator",
|
||||
"testclasses_file": "reference/loyalty_evaluator.testclasses.abap"
|
||||
}
|
||||
],
|
||||
"craft_checks": [
|
||||
"method_length",
|
||||
"nesting_depth"
|
||||
]
|
||||
}
|
||||
19
tasks_gen/eval/G0008/generation.json
Normal file
19
tasks_gen/eval/G0008/generation.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"id": "G0008",
|
||||
"pool": "eval",
|
||||
"object_type": "CLAS",
|
||||
"category": "G",
|
||||
"attempts": [
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 0,
|
||||
"null": 0
|
||||
},
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 100.0,
|
||||
"null": 0
|
||||
}
|
||||
],
|
||||
"accepted": true
|
||||
}
|
||||
197
tasks_gen/eval/G0008/hidden/t07_hidden.clas.abap
Normal file
197
tasks_gen/eval/G0008/hidden/t07_hidden.clas.abap
Normal file
@@ -0,0 +1,197 @@
|
||||
CLASS {{p}}t07_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
|
||||
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
|
||||
PRIVATE SECTION.
|
||||
DATA mo_cut TYPE REF TO {{p}}if_freight_calc.
|
||||
|
||||
METHODS setup.
|
||||
METHODS assert_amount
|
||||
IMPORTING iv_expected TYPE {{p}}if_freight_calc=>ty_amount
|
||||
iv_actual TYPE {{p}}if_freight_calc=>ty_amount.
|
||||
METHODS assert_weight
|
||||
IMPORTING iv_expected TYPE {{p}}if_freight_calc=>ty_weight
|
||||
iv_actual TYPE {{p}}if_freight_calc=>ty_weight.
|
||||
|
||||
METHODS weight_not_positive FOR TESTING.
|
||||
METHODS volume_negative FOR TESTING.
|
||||
METHODS distance_not_positive FOR TESTING.
|
||||
METHODS fuel_index_negative FOR TESTING.
|
||||
METHODS fuel_index_too_high FOR TESTING.
|
||||
METHODS base_at_weight_limit FOR TESTING.
|
||||
METHODS base_above_weight_limit FOR TESTING.
|
||||
METHODS volumetric_weight_wins FOR TESTING.
|
||||
METHODS distance_component FOR TESTING.
|
||||
METHODS fuel_rounds_half_up FOR TESTING.
|
||||
METHODS full_load FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}t07_hidden IMPLEMENTATION.
|
||||
|
||||
METHOD setup.
|
||||
DATA lo_calc TYPE REF TO {{p}}freight_calc.
|
||||
CREATE OBJECT lo_calc.
|
||||
mo_cut = lo_calc.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD assert_amount.
|
||||
cl_abap_unit_assert=>assert_equals( exp = iv_expected act = iv_actual ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD assert_weight.
|
||||
cl_abap_unit_assert=>assert_equals( exp = iv_expected act = iv_actual ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD weight_not_positive.
|
||||
DATA ls_result TYPE {{p}}if_freight_calc=>ty_result.
|
||||
|
||||
ls_result = mo_cut->calculate( iv_weight_kg = 0
|
||||
iv_volume_m3 = -1
|
||||
iv_distance_km = 0
|
||||
iv_fuel_index = 200 ).
|
||||
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_freight_calc=>c_reason-weight
|
||||
act = ls_result-reason ).
|
||||
assert_amount( iv_expected = 0 iv_actual = ls_result-total ).
|
||||
assert_weight( iv_expected = 0 iv_actual = ls_result-chargeable_weight ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD volume_negative.
|
||||
DATA ls_result TYPE {{p}}if_freight_calc=>ty_result.
|
||||
|
||||
ls_result = mo_cut->calculate( iv_weight_kg = 1000
|
||||
iv_volume_m3 = '-0.5'
|
||||
iv_distance_km = 100
|
||||
iv_fuel_index = 50 ).
|
||||
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_freight_calc=>c_reason-volume
|
||||
act = ls_result-reason ).
|
||||
assert_amount( iv_expected = 0 iv_actual = ls_result-total ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD distance_not_positive.
|
||||
DATA ls_result TYPE {{p}}if_freight_calc=>ty_result.
|
||||
|
||||
ls_result = mo_cut->calculate( iv_weight_kg = 1000
|
||||
iv_volume_m3 = 0
|
||||
iv_distance_km = 0
|
||||
iv_fuel_index = 50 ).
|
||||
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_freight_calc=>c_reason-distance
|
||||
act = ls_result-reason ).
|
||||
assert_amount( iv_expected = 0 iv_actual = ls_result-total ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD fuel_index_negative.
|
||||
DATA ls_result TYPE {{p}}if_freight_calc=>ty_result.
|
||||
|
||||
ls_result = mo_cut->calculate( iv_weight_kg = 1000
|
||||
iv_volume_m3 = 0
|
||||
iv_distance_km = 100
|
||||
iv_fuel_index = '-0.1' ).
|
||||
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_freight_calc=>c_reason-fuel
|
||||
act = ls_result-reason ).
|
||||
assert_amount( iv_expected = 0 iv_actual = ls_result-total ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD fuel_index_too_high.
|
||||
DATA ls_result TYPE {{p}}if_freight_calc=>ty_result.
|
||||
|
||||
ls_result = mo_cut->calculate( iv_weight_kg = 1000
|
||||
iv_volume_m3 = 0
|
||||
iv_distance_km = 100
|
||||
iv_fuel_index = '100.1' ).
|
||||
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_freight_calc=>c_reason-fuel
|
||||
act = ls_result-reason ).
|
||||
assert_amount( iv_expected = 0 iv_actual = ls_result-total ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD base_at_weight_limit.
|
||||
DATA ls_result TYPE {{p}}if_freight_calc=>ty_result.
|
||||
|
||||
ls_result = mo_cut->calculate( iv_weight_kg = 1000
|
||||
iv_volume_m3 = 0
|
||||
iv_distance_km = 100
|
||||
iv_fuel_index = 0 ).
|
||||
|
||||
assert_weight( iv_expected = 1000 iv_actual = ls_result-chargeable_weight ).
|
||||
assert_amount( iv_expected = 45 iv_actual = ls_result-base ).
|
||||
assert_amount( iv_expected = 0 iv_actual = ls_result-distance ).
|
||||
assert_amount( iv_expected = 0 iv_actual = ls_result-fuel ).
|
||||
assert_amount( iv_expected = 45 iv_actual = ls_result-total ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_freight_calc=>c_reason-ok
|
||||
act = ls_result-reason ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD base_above_weight_limit.
|
||||
DATA ls_result TYPE {{p}}if_freight_calc=>ty_result.
|
||||
|
||||
ls_result = mo_cut->calculate( iv_weight_kg = '1100.5'
|
||||
iv_volume_m3 = 0
|
||||
iv_distance_km = 100
|
||||
iv_fuel_index = 0 ).
|
||||
|
||||
assert_weight( iv_expected = '1100.5' iv_actual = ls_result-chargeable_weight ).
|
||||
assert_amount( iv_expected = 58 iv_actual = ls_result-base ).
|
||||
assert_amount( iv_expected = 58 iv_actual = ls_result-total ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD volumetric_weight_wins.
|
||||
DATA ls_result TYPE {{p}}if_freight_calc=>ty_result.
|
||||
|
||||
ls_result = mo_cut->calculate( iv_weight_kg = 500
|
||||
iv_volume_m3 = '4.5'
|
||||
iv_distance_km = 100
|
||||
iv_fuel_index = 0 ).
|
||||
|
||||
assert_weight( iv_expected = 1125 iv_actual = ls_result-chargeable_weight ).
|
||||
assert_amount( iv_expected = 58 iv_actual = ls_result-base ).
|
||||
assert_amount( iv_expected = 58 iv_actual = ls_result-total ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD distance_component.
|
||||
DATA ls_result TYPE {{p}}if_freight_calc=>ty_result.
|
||||
|
||||
ls_result = mo_cut->calculate( iv_weight_kg = 1000
|
||||
iv_volume_m3 = 0
|
||||
iv_distance_km = 350
|
||||
iv_fuel_index = 0 ).
|
||||
|
||||
assert_amount( iv_expected = 45 iv_actual = ls_result-base ).
|
||||
assert_amount( iv_expected = 27 iv_actual = ls_result-distance ).
|
||||
assert_amount( iv_expected = 72 iv_actual = ls_result-total ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD fuel_rounds_half_up.
|
||||
DATA ls_result TYPE {{p}}if_freight_calc=>ty_result.
|
||||
|
||||
ls_result = mo_cut->calculate( iv_weight_kg = 1000
|
||||
iv_volume_m3 = 0
|
||||
iv_distance_km = 100
|
||||
iv_fuel_index = '12.3' ).
|
||||
|
||||
assert_amount( iv_expected = 45 iv_actual = ls_result-base ).
|
||||
assert_amount( iv_expected = 0 iv_actual = ls_result-distance ).
|
||||
assert_amount( iv_expected = '5.54' iv_actual = ls_result-fuel ).
|
||||
assert_amount( iv_expected = '50.54' iv_actual = ls_result-total ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD full_load.
|
||||
DATA ls_result TYPE {{p}}if_freight_calc=>ty_result.
|
||||
|
||||
ls_result = mo_cut->calculate( iv_weight_kg = 2000
|
||||
iv_volume_m3 = 1
|
||||
iv_distance_km = 500
|
||||
iv_fuel_index = 100 ).
|
||||
|
||||
assert_weight( iv_expected = 2000 iv_actual = ls_result-chargeable_weight ).
|
||||
assert_amount( iv_expected = 110 iv_actual = ls_result-base ).
|
||||
assert_amount( iv_expected = 36 iv_actual = ls_result-distance ).
|
||||
assert_amount( iv_expected = 146 iv_actual = ls_result-fuel ).
|
||||
assert_amount( iv_expected = 292 iv_actual = ls_result-total ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_freight_calc=>c_reason-ok
|
||||
act = ls_result-reason ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
149
tasks_gen/eval/G0008/reference/freight_calc.clas.abap
Normal file
149
tasks_gen/eval/G0008/reference/freight_calc.clas.abap
Normal file
@@ -0,0 +1,149 @@
|
||||
CLASS {{p}}freight_calc DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
||||
PUBLIC SECTION.
|
||||
INTERFACES {{p}}if_freight_calc.
|
||||
|
||||
PRIVATE SECTION.
|
||||
TYPES ty_quantity TYPE p LENGTH 13 DECIMALS 3.
|
||||
|
||||
CONSTANTS:
|
||||
c_base_amount TYPE {{p}}if_freight_calc=>ty_amount VALUE '45.00',
|
||||
c_base_weight TYPE ty_quantity VALUE '1000.000',
|
||||
c_weight_rate TYPE {{p}}if_freight_calc=>ty_amount VALUE '6.50',
|
||||
c_free_km TYPE i VALUE 100,
|
||||
c_km_rate TYPE {{p}}if_freight_calc=>ty_amount VALUE '9.00',
|
||||
c_step TYPE ty_quantity VALUE '100.000',
|
||||
c_volume_factor TYPE i VALUE 250,
|
||||
c_percent TYPE p LENGTH 3 DECIMALS 2 VALUE '0.01'.
|
||||
|
||||
METHODS check_input
|
||||
IMPORTING iv_weight_kg TYPE {{p}}if_freight_calc=>ty_weight
|
||||
iv_volume_m3 TYPE {{p}}if_freight_calc=>ty_volume
|
||||
iv_distance_km TYPE {{p}}if_freight_calc=>ty_distance
|
||||
iv_fuel_index TYPE {{p}}if_freight_calc=>ty_fuel
|
||||
RETURNING VALUE(rv_reason) TYPE {{p}}if_freight_calc=>ty_reason.
|
||||
|
||||
METHODS chargeable_weight
|
||||
IMPORTING iv_weight_kg TYPE {{p}}if_freight_calc=>ty_weight
|
||||
iv_volume_m3 TYPE {{p}}if_freight_calc=>ty_volume
|
||||
RETURNING VALUE(rv_weight) TYPE ty_quantity.
|
||||
|
||||
METHODS base_freight
|
||||
IMPORTING iv_weight_kg TYPE ty_quantity
|
||||
RETURNING VALUE(rv_amount) TYPE {{p}}if_freight_calc=>ty_amount.
|
||||
|
||||
METHODS distance_freight
|
||||
IMPORTING iv_distance_km TYPE {{p}}if_freight_calc=>ty_distance
|
||||
RETURNING VALUE(rv_amount) TYPE {{p}}if_freight_calc=>ty_amount.
|
||||
|
||||
METHODS fuel_freight
|
||||
IMPORTING iv_amount TYPE {{p}}if_freight_calc=>ty_amount
|
||||
iv_fuel_index TYPE {{p}}if_freight_calc=>ty_fuel
|
||||
RETURNING VALUE(rv_amount) TYPE {{p}}if_freight_calc=>ty_amount.
|
||||
|
||||
METHODS started_steps
|
||||
IMPORTING iv_excess TYPE ty_quantity
|
||||
RETURNING VALUE(rv_steps) TYPE i.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}freight_calc IMPLEMENTATION.
|
||||
|
||||
METHOD {{p}}if_freight_calc~calculate.
|
||||
DATA lv_reason TYPE {{p}}if_freight_calc=>ty_reason.
|
||||
DATA lv_weight TYPE ty_quantity.
|
||||
DATA lv_net TYPE {{p}}if_freight_calc=>ty_amount.
|
||||
|
||||
CLEAR rs_result.
|
||||
lv_reason = check_input( iv_weight_kg = iv_weight_kg
|
||||
iv_volume_m3 = iv_volume_m3
|
||||
iv_distance_km = iv_distance_km
|
||||
iv_fuel_index = iv_fuel_index ).
|
||||
IF lv_reason <> {{p}}if_freight_calc=>c_reason-ok.
|
||||
rs_result-reason = lv_reason.
|
||||
RETURN.
|
||||
ENDIF.
|
||||
|
||||
lv_weight = chargeable_weight( iv_weight_kg = iv_weight_kg
|
||||
iv_volume_m3 = iv_volume_m3 ).
|
||||
rs_result-chargeable_weight = lv_weight.
|
||||
rs_result-base = base_freight( lv_weight ).
|
||||
rs_result-distance = distance_freight( iv_distance_km ).
|
||||
lv_net = rs_result-base + rs_result-distance.
|
||||
rs_result-fuel = fuel_freight( iv_amount = lv_net
|
||||
iv_fuel_index = iv_fuel_index ).
|
||||
rs_result-total = rs_result-base + rs_result-distance + rs_result-fuel.
|
||||
rs_result-reason = {{p}}if_freight_calc=>c_reason-ok.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD check_input.
|
||||
IF iv_weight_kg <= 0.
|
||||
rv_reason = {{p}}if_freight_calc=>c_reason-weight.
|
||||
ELSEIF iv_volume_m3 < 0.
|
||||
rv_reason = {{p}}if_freight_calc=>c_reason-volume.
|
||||
ELSEIF iv_distance_km <= 0.
|
||||
rv_reason = {{p}}if_freight_calc=>c_reason-distance.
|
||||
ELSEIF iv_fuel_index < 0 OR iv_fuel_index > 100.
|
||||
rv_reason = {{p}}if_freight_calc=>c_reason-fuel.
|
||||
ELSE.
|
||||
rv_reason = {{p}}if_freight_calc=>c_reason-ok.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD chargeable_weight.
|
||||
DATA lv_volumetric TYPE ty_quantity.
|
||||
|
||||
lv_volumetric = iv_volume_m3 * c_volume_factor.
|
||||
IF lv_volumetric > iv_weight_kg.
|
||||
rv_weight = lv_volumetric.
|
||||
ELSE.
|
||||
rv_weight = iv_weight_kg.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD base_freight.
|
||||
DATA lv_excess TYPE ty_quantity.
|
||||
DATA lv_steps TYPE i.
|
||||
|
||||
lv_excess = iv_weight_kg - c_base_weight.
|
||||
IF lv_excess <= 0.
|
||||
rv_amount = c_base_amount.
|
||||
ELSE.
|
||||
lv_steps = started_steps( lv_excess ).
|
||||
rv_amount = c_base_amount + lv_steps * c_weight_rate.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD distance_freight.
|
||||
DATA lv_excess TYPE ty_quantity.
|
||||
DATA lv_steps TYPE i.
|
||||
|
||||
lv_excess = iv_distance_km - c_free_km.
|
||||
IF lv_excess <= 0.
|
||||
rv_amount = 0.
|
||||
ELSE.
|
||||
lv_steps = started_steps( lv_excess ).
|
||||
rv_amount = lv_steps * c_km_rate.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD fuel_freight.
|
||||
DATA lv_scaled TYPE p LENGTH 15 DECIMALS 3.
|
||||
DATA lv_fuel TYPE p LENGTH 15 DECIMALS 5.
|
||||
|
||||
lv_scaled = iv_amount * iv_fuel_index.
|
||||
lv_fuel = lv_scaled * c_percent.
|
||||
rv_amount = lv_fuel.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD started_steps.
|
||||
DATA lv_excess_scaled TYPE p LENGTH 16 DECIMALS 0.
|
||||
DATA lv_step_scaled TYPE p LENGTH 16 DECIMALS 0.
|
||||
|
||||
lv_excess_scaled = iv_excess * 1000.
|
||||
lv_step_scaled = c_step * 1000.
|
||||
rv_steps = lv_excess_scaled DIV lv_step_scaled.
|
||||
IF lv_excess_scaled MOD lv_step_scaled > 0.
|
||||
rv_steps = rv_steps + 1.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
114
tasks_gen/eval/G0008/reference/freight_calc.testclasses.abap
Normal file
114
tasks_gen/eval/G0008/reference/freight_calc.testclasses.abap
Normal file
@@ -0,0 +1,114 @@
|
||||
CLASS ltc_freight_calc DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
PRIVATE SECTION.
|
||||
DATA mo_cut TYPE REF TO {{p}}if_freight_calc.
|
||||
|
||||
METHODS setup.
|
||||
METHODS assert_amount
|
||||
IMPORTING iv_expected TYPE {{p}}if_freight_calc=>ty_amount
|
||||
iv_actual TYPE {{p}}if_freight_calc=>ty_amount.
|
||||
METHODS assert_weight
|
||||
IMPORTING iv_expected TYPE {{p}}if_freight_calc=>ty_weight
|
||||
iv_actual TYPE {{p}}if_freight_calc=>ty_weight.
|
||||
|
||||
METHODS base_and_distance FOR TESTING.
|
||||
METHODS fuel_is_rounded FOR TESTING.
|
||||
METHODS volume_sets_chargeable_weight FOR TESTING.
|
||||
METHODS weight_zero_is_rejected FOR TESTING.
|
||||
METHODS distance_zero_is_rejected FOR TESTING.
|
||||
METHODS fuel_index_out_of_range FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS ltc_freight_calc IMPLEMENTATION.
|
||||
|
||||
METHOD setup.
|
||||
DATA lo_calc TYPE REF TO {{p}}freight_calc.
|
||||
CREATE OBJECT lo_calc.
|
||||
mo_cut = lo_calc.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD assert_amount.
|
||||
cl_abap_unit_assert=>assert_equals( exp = iv_expected act = iv_actual ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD assert_weight.
|
||||
cl_abap_unit_assert=>assert_equals( exp = iv_expected act = iv_actual ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD base_and_distance.
|
||||
DATA ls_result TYPE {{p}}if_freight_calc=>ty_result.
|
||||
|
||||
ls_result = mo_cut->calculate( iv_weight_kg = 2000
|
||||
iv_volume_m3 = 1
|
||||
iv_distance_km = 500
|
||||
iv_fuel_index = 100 ).
|
||||
|
||||
assert_amount( iv_expected = 110 iv_actual = ls_result-base ).
|
||||
assert_amount( iv_expected = 36 iv_actual = ls_result-distance ).
|
||||
assert_amount( iv_expected = 146 iv_actual = ls_result-fuel ).
|
||||
assert_amount( iv_expected = 292 iv_actual = ls_result-total ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD fuel_is_rounded.
|
||||
DATA ls_result TYPE {{p}}if_freight_calc=>ty_result.
|
||||
|
||||
ls_result = mo_cut->calculate( iv_weight_kg = 1000
|
||||
iv_volume_m3 = 0
|
||||
iv_distance_km = 100
|
||||
iv_fuel_index = '12.3' ).
|
||||
|
||||
assert_amount( iv_expected = '5.54' iv_actual = ls_result-fuel ).
|
||||
assert_amount( iv_expected = '50.54' iv_actual = ls_result-total ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD volume_sets_chargeable_weight.
|
||||
DATA ls_result TYPE {{p}}if_freight_calc=>ty_result.
|
||||
|
||||
ls_result = mo_cut->calculate( iv_weight_kg = 500
|
||||
iv_volume_m3 = '4.5'
|
||||
iv_distance_km = 100
|
||||
iv_fuel_index = 0 ).
|
||||
|
||||
assert_weight( iv_expected = 1125 iv_actual = ls_result-chargeable_weight ).
|
||||
assert_amount( iv_expected = 58 iv_actual = ls_result-base ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD weight_zero_is_rejected.
|
||||
DATA ls_result TYPE {{p}}if_freight_calc=>ty_result.
|
||||
|
||||
ls_result = mo_cut->calculate( iv_weight_kg = 0
|
||||
iv_volume_m3 = 2
|
||||
iv_distance_km = 300
|
||||
iv_fuel_index = 10 ).
|
||||
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_freight_calc=>c_reason-weight
|
||||
act = ls_result-reason ).
|
||||
assert_amount( iv_expected = 0 iv_actual = ls_result-total ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD distance_zero_is_rejected.
|
||||
DATA ls_result TYPE {{p}}if_freight_calc=>ty_result.
|
||||
|
||||
ls_result = mo_cut->calculate( iv_weight_kg = 1500
|
||||
iv_volume_m3 = 0
|
||||
iv_distance_km = 0
|
||||
iv_fuel_index = 10 ).
|
||||
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_freight_calc=>c_reason-distance
|
||||
act = ls_result-reason ).
|
||||
assert_amount( iv_expected = 0 iv_actual = ls_result-total ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD fuel_index_out_of_range.
|
||||
DATA ls_result TYPE {{p}}if_freight_calc=>ty_result.
|
||||
|
||||
ls_result = mo_cut->calculate( iv_weight_kg = 1500
|
||||
iv_volume_m3 = 0
|
||||
iv_distance_km = 300
|
||||
iv_fuel_index = 120 ).
|
||||
|
||||
cl_abap_unit_assert=>assert_equals( exp = {{p}}if_freight_calc=>c_reason-fuel
|
||||
act = ls_result-reason ).
|
||||
assert_amount( iv_expected = 0 iv_actual = ls_result-total ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
34
tasks_gen/eval/G0008/seed/if_freight_calc.intf.abap
Normal file
34
tasks_gen/eval/G0008/seed/if_freight_calc.intf.abap
Normal file
@@ -0,0 +1,34 @@
|
||||
INTERFACE {{p}}if_freight_calc PUBLIC.
|
||||
TYPES ty_reason TYPE c LENGTH 10.
|
||||
TYPES ty_amount TYPE p LENGTH 9 DECIMALS 2.
|
||||
TYPES ty_weight TYPE p LENGTH 9 DECIMALS 3.
|
||||
TYPES ty_volume TYPE p LENGTH 9 DECIMALS 3.
|
||||
TYPES ty_distance TYPE i.
|
||||
TYPES ty_fuel TYPE p LENGTH 5 DECIMALS 1.
|
||||
|
||||
CONSTANTS:
|
||||
BEGIN OF c_reason,
|
||||
ok TYPE ty_reason VALUE 'OK',
|
||||
weight TYPE ty_reason VALUE 'WEIGHT',
|
||||
volume TYPE ty_reason VALUE 'VOLUME',
|
||||
distance TYPE ty_reason VALUE 'DISTANCE',
|
||||
fuel TYPE ty_reason VALUE 'FUEL',
|
||||
END OF c_reason.
|
||||
|
||||
TYPES:
|
||||
BEGIN OF ty_result,
|
||||
chargeable_weight TYPE ty_weight,
|
||||
base TYPE ty_amount,
|
||||
distance TYPE ty_amount,
|
||||
fuel TYPE ty_amount,
|
||||
total TYPE ty_amount,
|
||||
reason TYPE ty_reason,
|
||||
END OF ty_result.
|
||||
|
||||
METHODS calculate
|
||||
IMPORTING iv_weight_kg TYPE ty_weight
|
||||
iv_volume_m3 TYPE ty_volume
|
||||
iv_distance_km TYPE ty_distance
|
||||
iv_fuel_index TYPE ty_fuel
|
||||
RETURNING VALUE(rs_result) TYPE ty_result.
|
||||
ENDINTERFACE.
|
||||
61
tasks_gen/eval/G0008/spec.md
Normal file
61
tasks_gen/eval/G0008/spec.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# 1. Goal
|
||||
A freight forwarder calculates the price of an outbound shipment. The
|
||||
calculation service receives the gross weight, the volume, the transport
|
||||
distance, and the current fuel index. It returns the freight components and a
|
||||
reason code.
|
||||
|
||||
# 2. Open questions
|
||||
None.
|
||||
|
||||
# 3. Context
|
||||
- The interface {{P}}IF_FREIGHT_CALC exists in package $TMP. It is active.
|
||||
- The interface defines the types TY_REASON, TY_AMOUNT, TY_WEIGHT, TY_VOLUME,
|
||||
TY_DISTANCE, TY_FUEL, and TY_RESULT.
|
||||
- The interface defines the constant structure C_REASON with the components
|
||||
OK, WEIGHT, VOLUME, DISTANCE, and FUEL.
|
||||
- The interface defines the instance method CALCULATE with the importing
|
||||
parameters IV_WEIGHT_KG, IV_VOLUME_M3, IV_DISTANCE_KM, IV_FUEL_INDEX and the
|
||||
returning parameter RS_RESULT.
|
||||
|
||||
# 4. Contract
|
||||
- Create the class {{P}}FREIGHT_CALC in package $TMP.
|
||||
- The class is public, final, and has a public constructor without parameters.
|
||||
- The class implements {{P}}IF_FREIGHT_CALC.
|
||||
- Do not add other public methods.
|
||||
|
||||
# 5. Business rules
|
||||
All amounts are in euro. Apply the rules in this sequence.
|
||||
1. If IV_WEIGHT_KG is less than or equal to 0, return the reason WEIGHT. Set
|
||||
all amounts and the chargeable weight to 0.
|
||||
2. If IV_VOLUME_M3 is less than 0, return the reason VOLUME. Set all amounts
|
||||
and the chargeable weight to 0.
|
||||
3. If IV_DISTANCE_KM is less than or equal to 0, return the reason DISTANCE.
|
||||
Set all amounts and the chargeable weight to 0.
|
||||
4. If IV_FUEL_INDEX is less than 0 or greater than 100, return the reason FUEL.
|
||||
Set all amounts and the chargeable weight to 0.
|
||||
5. The volumetric weight is IV_VOLUME_M3 multiplied by 250.
|
||||
6. The chargeable weight is the greater of IV_WEIGHT_KG and the volumetric
|
||||
weight.
|
||||
7. The base freight is 45.00. For each full or partial 100 kg by which the
|
||||
chargeable weight is greater than 1000 kg, add 6.50.
|
||||
8. The distance freight is 0.00 for a distance up to and including 100 km. For
|
||||
each full or partial 100 km by which IV_DISTANCE_KM is greater than 100 km,
|
||||
add 9.00.
|
||||
9. The fuel freight is the sum of the base freight and the distance freight,
|
||||
multiplied by IV_FUEL_INDEX, divided by 100. Round the result to 2 decimal
|
||||
places. Round a half away from zero.
|
||||
10. The total is the sum of the base freight, the distance freight, and the
|
||||
fuel freight.
|
||||
11. Return the reason OK.
|
||||
|
||||
# 6. Constraints
|
||||
- Release target: 7.40 SP05. Do not use syntax that is newer than 7.40 SP05.
|
||||
For example, do not use COND, SWITCH, REDUCE, FOR, or LET.
|
||||
- Coding standards: Clean ABAP. Method length below 40 statements. No global
|
||||
variables. No comment that restates the code.
|
||||
- Out of scope: do not change {{P}}IF_FREIGHT_CALC.
|
||||
|
||||
# 7. Acceptance
|
||||
- The class is active and has no syntax error.
|
||||
- The hidden tests pass.
|
||||
- Write your own ABAP Unit tests for the class.
|
||||
51
tasks_gen/eval/G0008/task.json
Normal file
51
tasks_gen/eval/G0008/task.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"id": "G0008",
|
||||
"category": "G",
|
||||
"object_type": "CLAS",
|
||||
"difficulty": 2,
|
||||
"release_target": "v740sp05",
|
||||
"expected_outcome": "implement",
|
||||
"budget": {
|
||||
"max_tool_calls": 45,
|
||||
"max_activations": 12
|
||||
},
|
||||
"seed": [
|
||||
{
|
||||
"type": "INTF",
|
||||
"name": "{{P}}IF_FREIGHT_CALC",
|
||||
"file": "seed/if_freight_calc.intf.abap",
|
||||
"description": "Freight calculation contract with result type and reason codes"
|
||||
}
|
||||
],
|
||||
"contract": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}FREIGHT_CALC",
|
||||
"implements": "{{P}}IF_FREIGHT_CALC"
|
||||
}
|
||||
],
|
||||
"out_of_scope": [
|
||||
"{{P}}IF_FREIGHT_CALC"
|
||||
],
|
||||
"hidden_tests": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}T07_HIDDEN",
|
||||
"file": "hidden/t07_hidden.clas.abap",
|
||||
"description": "T07 hidden tests for the freight calculation"
|
||||
}
|
||||
],
|
||||
"reference": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}FREIGHT_CALC",
|
||||
"file": "reference/freight_calc.clas.abap",
|
||||
"description": "Freight surcharge calculator",
|
||||
"testclasses_file": "reference/freight_calc.testclasses.abap"
|
||||
}
|
||||
],
|
||||
"craft_checks": [
|
||||
"method_length",
|
||||
"nesting_depth"
|
||||
]
|
||||
}
|
||||
19
tasks_gen/eval/G0009/generation.json
Normal file
19
tasks_gen/eval/G0009/generation.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"id": "G0009",
|
||||
"pool": "eval",
|
||||
"object_type": "CLAS",
|
||||
"category": "I",
|
||||
"attempts": [
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 0,
|
||||
"null": 0
|
||||
},
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 100.0,
|
||||
"null": 0
|
||||
}
|
||||
],
|
||||
"accepted": true
|
||||
}
|
||||
111
tasks_gen/eval/G0009/hidden/t02_hidden.clas.abap
Normal file
111
tasks_gen/eval/G0009/hidden/t02_hidden.clas.abap
Normal file
@@ -0,0 +1,111 @@
|
||||
CLASS {{p}}t02_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
|
||||
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
|
||||
PRIVATE SECTION.
|
||||
DATA mo_cut TYPE REF TO {{p}}if_shipping_cost.
|
||||
|
||||
METHODS setup.
|
||||
|
||||
METHODS zero_weight_is_free FOR TESTING.
|
||||
METHODS light_parcel_gets_minimum FOR TESTING.
|
||||
METHODS tier_limit_one_kg FOR TESTING.
|
||||
METHODS tier_limit_five_kg FOR TESTING.
|
||||
METHODS tier_limit_ten_kg FOR TESTING.
|
||||
METHODS bulk_weight_limit FOR TESTING.
|
||||
METHODS bulk_weight_started_kg FOR TESTING.
|
||||
METHODS express_surcharge FOR TESTING.
|
||||
METHODS express_below_minimum FOR TESTING.
|
||||
METHODS bulk_express FOR TESTING.
|
||||
|
||||
METHODS assert_cost
|
||||
IMPORTING iv_weight TYPE {{p}}if_shipping_cost=>ty_weight
|
||||
iv_express TYPE abap_bool
|
||||
iv_exp TYPE {{p}}if_shipping_cost=>ty_amount
|
||||
iv_msg TYPE string.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}t02_hidden IMPLEMENTATION.
|
||||
METHOD setup.
|
||||
mo_cut = NEW {{p}}shipping_cost_calc( ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD assert_cost.
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = iv_exp
|
||||
act = mo_cut->calculate( iv_weight = iv_weight iv_express = iv_express )
|
||||
msg = iv_msg ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD zero_weight_is_free.
|
||||
assert_cost( iv_weight = '0.000' iv_express = abap_false
|
||||
iv_exp = '0.00' iv_msg = `zero weight` ).
|
||||
assert_cost( iv_weight = '-2.500' iv_express = abap_true
|
||||
iv_exp = '0.00' iv_msg = `negative weight` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD light_parcel_gets_minimum.
|
||||
assert_cost( iv_weight = '0.100' iv_express = abap_false
|
||||
iv_exp = '6.00' iv_msg = `0.100 kg` ).
|
||||
assert_cost( iv_weight = '0.500' iv_express = abap_false
|
||||
iv_exp = '6.00' iv_msg = `0.500 kg` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD tier_limit_one_kg.
|
||||
assert_cost( iv_weight = '1.000' iv_express = abap_false
|
||||
iv_exp = '6.00' iv_msg = `1.000 kg` ).
|
||||
assert_cost( iv_weight = '1.001' iv_express = abap_false
|
||||
iv_exp = '8.50' iv_msg = `1.001 kg` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD tier_limit_five_kg.
|
||||
assert_cost( iv_weight = '5.000' iv_express = abap_false
|
||||
iv_exp = '8.50' iv_msg = `5.000 kg` ).
|
||||
assert_cost( iv_weight = '5.001' iv_express = abap_false
|
||||
iv_exp = '12.00' iv_msg = `5.001 kg` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD tier_limit_ten_kg.
|
||||
assert_cost( iv_weight = '10.000' iv_express = abap_false
|
||||
iv_exp = '12.00' iv_msg = `10.000 kg` ).
|
||||
assert_cost( iv_weight = '10.001' iv_express = abap_false
|
||||
iv_exp = '18.75' iv_msg = `10.001 kg` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD bulk_weight_limit.
|
||||
assert_cost( iv_weight = '31.500' iv_express = abap_false
|
||||
iv_exp = '18.75' iv_msg = `31.500 kg` ).
|
||||
assert_cost( iv_weight = '31.501' iv_express = abap_false
|
||||
iv_exp = '19.40' iv_msg = `31.501 kg` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD bulk_weight_started_kg.
|
||||
assert_cost( iv_weight = '32.000' iv_express = abap_false
|
||||
iv_exp = '19.40' iv_msg = `32.000 kg` ).
|
||||
assert_cost( iv_weight = '33.000' iv_express = abap_false
|
||||
iv_exp = '20.05' iv_msg = `33.000 kg` ).
|
||||
assert_cost( iv_weight = '100.000' iv_express = abap_false
|
||||
iv_exp = '63.60' iv_msg = `100.000 kg` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD express_surcharge.
|
||||
assert_cost( iv_weight = '2.000' iv_express = abap_true
|
||||
iv_exp = '11.48' iv_msg = `2.000 kg express` ).
|
||||
assert_cost( iv_weight = '20.000' iv_express = abap_true
|
||||
iv_exp = '25.31' iv_msg = `20.000 kg express` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD express_below_minimum.
|
||||
assert_cost( iv_weight = '0.500' iv_express = abap_true
|
||||
iv_exp = '6.62' iv_msg = `0.500 kg express` ).
|
||||
assert_cost( iv_weight = '1.000' iv_express = abap_true
|
||||
iv_exp = '6.62' iv_msg = `1.000 kg express` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD bulk_express.
|
||||
assert_cost( iv_weight = '32.000' iv_express = abap_true
|
||||
iv_exp = '26.19' iv_msg = `32.000 kg express` ).
|
||||
assert_cost( iv_weight = '100.000' iv_express = abap_true
|
||||
iv_exp = '85.86' iv_msg = `100.000 kg express` ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
56
tasks_gen/eval/G0009/reference/shipping_cost_calc.clas.abap
Normal file
56
tasks_gen/eval/G0009/reference/shipping_cost_calc.clas.abap
Normal file
@@ -0,0 +1,56 @@
|
||||
CLASS {{p}}shipping_cost_calc DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
||||
PUBLIC SECTION.
|
||||
INTERFACES {{p}}if_shipping_cost.
|
||||
|
||||
PRIVATE SECTION.
|
||||
METHODS base_rate
|
||||
IMPORTING iv_weight TYPE {{p}}if_shipping_cost=>ty_weight
|
||||
RETURNING VALUE(rv_rate) TYPE {{p}}if_shipping_cost=>ty_amount.
|
||||
|
||||
METHODS express_surcharge
|
||||
IMPORTING iv_rate TYPE {{p}}if_shipping_cost=>ty_amount
|
||||
RETURNING VALUE(rv_surcharge) TYPE {{p}}if_shipping_cost=>ty_amount.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}shipping_cost_calc IMPLEMENTATION.
|
||||
METHOD {{p}}if_shipping_cost~calculate.
|
||||
IF iv_weight <= 0.
|
||||
rv_cost = 0.
|
||||
RETURN.
|
||||
ENDIF.
|
||||
|
||||
DATA(lv_cost) = base_rate( iv_weight ).
|
||||
|
||||
IF iv_express = abap_true.
|
||||
lv_cost = lv_cost + express_surcharge( lv_cost ).
|
||||
ENDIF.
|
||||
|
||||
IF lv_cost < {{p}}if_shipping_cost=>c_rate-min_charge.
|
||||
rv_cost = {{p}}if_shipping_cost=>c_rate-min_charge.
|
||||
ELSE.
|
||||
rv_cost = lv_cost.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD base_rate.
|
||||
IF iv_weight <= 1.
|
||||
rv_rate = '4.90'.
|
||||
ELSEIF iv_weight <= 5.
|
||||
rv_rate = '8.50'.
|
||||
ELSEIF iv_weight <= 10.
|
||||
rv_rate = '12.00'.
|
||||
ELSEIF iv_weight <= {{p}}if_shipping_cost=>c_rate-bulk_weight.
|
||||
rv_rate = '18.75'.
|
||||
ELSE.
|
||||
DATA(lv_started_kg) = CONV i(
|
||||
ceil( iv_weight - {{p}}if_shipping_cost=>c_rate-bulk_weight ) ).
|
||||
rv_rate = '18.75' + lv_started_kg * {{p}}if_shipping_cost=>c_rate-bulk_step.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD express_surcharge.
|
||||
rv_surcharge = round( val = iv_rate * {{p}}if_shipping_cost=>c_rate-express_percent / 100
|
||||
dec = 2 ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
@@ -0,0 +1,68 @@
|
||||
CLASS ltc_shipping_cost_calc DEFINITION FINAL FOR TESTING
|
||||
DURATION SHORT RISK LEVEL HARMLESS.
|
||||
|
||||
PRIVATE SECTION.
|
||||
DATA mo_cut TYPE REF TO {{p}}if_shipping_cost.
|
||||
|
||||
METHODS setup.
|
||||
METHODS zero_weight_is_free FOR TESTING.
|
||||
METHODS minimum_charge FOR TESTING.
|
||||
METHODS tier_limits FOR TESTING.
|
||||
METHODS bulk_weight FOR TESTING.
|
||||
METHODS express_surcharge FOR TESTING.
|
||||
METHODS express_below_minimum FOR TESTING.
|
||||
|
||||
METHODS assert_cost
|
||||
IMPORTING iv_weight TYPE {{p}}if_shipping_cost=>ty_weight
|
||||
iv_express TYPE abap_bool
|
||||
iv_exp TYPE {{p}}if_shipping_cost=>ty_amount
|
||||
iv_msg TYPE string.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS ltc_shipping_cost_calc IMPLEMENTATION.
|
||||
METHOD setup.
|
||||
mo_cut = NEW {{p}}shipping_cost_calc( ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD assert_cost.
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = iv_exp
|
||||
act = mo_cut->calculate( iv_weight = iv_weight iv_express = iv_express )
|
||||
msg = iv_msg ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD zero_weight_is_free.
|
||||
assert_cost( iv_weight = '0.000' iv_express = abap_true
|
||||
iv_exp = '0.00' iv_msg = `zero weight` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD minimum_charge.
|
||||
assert_cost( iv_weight = '0.500' iv_express = abap_false
|
||||
iv_exp = '6.00' iv_msg = `minimum charge` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD tier_limits.
|
||||
assert_cost( iv_weight = '1.000' iv_express = abap_false
|
||||
iv_exp = '6.00' iv_msg = `1.000 kg` ).
|
||||
assert_cost( iv_weight = '1.001' iv_express = abap_false
|
||||
iv_exp = '8.50' iv_msg = `1.001 kg` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD bulk_weight.
|
||||
assert_cost( iv_weight = '31.500' iv_express = abap_false
|
||||
iv_exp = '18.75' iv_msg = `31.500 kg` ).
|
||||
assert_cost( iv_weight = '32.000' iv_express = abap_false
|
||||
iv_exp = '19.40' iv_msg = `32.000 kg` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD express_surcharge.
|
||||
assert_cost( iv_weight = '2.000' iv_express = abap_true
|
||||
iv_exp = '11.48' iv_msg = `2.000 kg express` ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD express_below_minimum.
|
||||
assert_cost( iv_weight = '0.500' iv_express = abap_true
|
||||
iv_exp = '6.62' iv_msg = `0.500 kg express` ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
17
tasks_gen/eval/G0009/seed/if_shipping_cost.intf.abap
Normal file
17
tasks_gen/eval/G0009/seed/if_shipping_cost.intf.abap
Normal file
@@ -0,0 +1,17 @@
|
||||
INTERFACE {{p}}if_shipping_cost PUBLIC.
|
||||
TYPES ty_weight TYPE p LENGTH 13 DECIMALS 3.
|
||||
TYPES ty_amount TYPE p LENGTH 15 DECIMALS 2.
|
||||
|
||||
CONSTANTS:
|
||||
BEGIN OF c_rate,
|
||||
min_charge TYPE ty_amount VALUE '6.00',
|
||||
express_percent TYPE i VALUE 35,
|
||||
bulk_weight TYPE ty_weight VALUE '31.500',
|
||||
bulk_step TYPE ty_amount VALUE '0.65',
|
||||
END OF c_rate.
|
||||
|
||||
METHODS calculate
|
||||
IMPORTING iv_weight TYPE ty_weight
|
||||
iv_express TYPE abap_bool
|
||||
RETURNING VALUE(rv_cost) TYPE ty_amount.
|
||||
ENDINTERFACE.
|
||||
53
tasks_gen/eval/G0009/seed/shipping_cost_calc.clas.abap
Normal file
53
tasks_gen/eval/G0009/seed/shipping_cost_calc.clas.abap
Normal file
@@ -0,0 +1,53 @@
|
||||
CLASS {{p}}shipping_cost_calc DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
||||
PUBLIC SECTION.
|
||||
INTERFACES {{p}}if_shipping_cost.
|
||||
|
||||
PRIVATE SECTION.
|
||||
METHODS base_rate
|
||||
IMPORTING iv_weight TYPE {{p}}if_shipping_cost=>ty_weight
|
||||
RETURNING VALUE(rv_rate) TYPE {{p}}if_shipping_cost=>ty_amount.
|
||||
|
||||
METHODS express_surcharge
|
||||
IMPORTING iv_rate TYPE {{p}}if_shipping_cost=>ty_amount
|
||||
RETURNING VALUE(rv_surcharge) TYPE {{p}}if_shipping_cost=>ty_amount.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}shipping_cost_calc IMPLEMENTATION.
|
||||
METHOD {{p}}if_shipping_cost~calculate.
|
||||
IF iv_weight <= 0.
|
||||
RETURN.
|
||||
ENDIF.
|
||||
|
||||
rv_cost = base_rate( iv_weight ).
|
||||
|
||||
IF rv_cost < {{p}}if_shipping_cost=>c_rate-min_charge.
|
||||
rv_cost = {{p}}if_shipping_cost=>c_rate-min_charge.
|
||||
ENDIF.
|
||||
|
||||
IF iv_express = abap_true.
|
||||
rv_cost = rv_cost + express_surcharge( rv_cost ).
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD base_rate.
|
||||
IF iv_weight < 1.
|
||||
rv_rate = '4.90'.
|
||||
ELSEIF iv_weight < 5.
|
||||
rv_rate = '8.50'.
|
||||
ELSEIF iv_weight < 10.
|
||||
rv_rate = '12.00'.
|
||||
ELSEIF iv_weight < {{p}}if_shipping_cost=>c_rate-bulk_weight.
|
||||
rv_rate = '18.75'.
|
||||
ELSE.
|
||||
DATA lv_started_kg TYPE i.
|
||||
lv_started_kg = iv_weight - {{p}}if_shipping_cost=>c_rate-bulk_weight.
|
||||
rv_rate = '18.75' + lv_started_kg * {{p}}if_shipping_cost=>c_rate-bulk_step.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD express_surcharge.
|
||||
rv_surcharge = round( val = iv_rate * {{p}}if_shipping_cost=>c_rate-express_percent / 100
|
||||
dec = 2 ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
53
tasks_gen/eval/G0009/seed/shipping_cost_legacy.clas.abap
Normal file
53
tasks_gen/eval/G0009/seed/shipping_cost_legacy.clas.abap
Normal file
@@ -0,0 +1,53 @@
|
||||
CLASS {{p}}shipping_cost_legacy DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
||||
PUBLIC SECTION.
|
||||
INTERFACES {{p}}if_shipping_cost.
|
||||
|
||||
PRIVATE SECTION.
|
||||
METHODS base_rate
|
||||
IMPORTING iv_weight TYPE {{p}}if_shipping_cost=>ty_weight
|
||||
RETURNING VALUE(rv_rate) TYPE {{p}}if_shipping_cost=>ty_amount.
|
||||
|
||||
METHODS express_surcharge
|
||||
IMPORTING iv_rate TYPE {{p}}if_shipping_cost=>ty_amount
|
||||
RETURNING VALUE(rv_surcharge) TYPE {{p}}if_shipping_cost=>ty_amount.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}shipping_cost_legacy IMPLEMENTATION.
|
||||
METHOD {{p}}if_shipping_cost~calculate.
|
||||
IF iv_weight <= 0.
|
||||
RETURN.
|
||||
ENDIF.
|
||||
|
||||
rv_cost = base_rate( iv_weight ).
|
||||
|
||||
IF rv_cost < {{p}}if_shipping_cost=>c_rate-min_charge.
|
||||
rv_cost = {{p}}if_shipping_cost=>c_rate-min_charge.
|
||||
ENDIF.
|
||||
|
||||
IF iv_express = abap_true.
|
||||
rv_cost = rv_cost + express_surcharge( rv_cost ).
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD base_rate.
|
||||
IF iv_weight < 1.
|
||||
rv_rate = '4.90'.
|
||||
ELSEIF iv_weight < 5.
|
||||
rv_rate = '8.50'.
|
||||
ELSEIF iv_weight < 10.
|
||||
rv_rate = '12.00'.
|
||||
ELSEIF iv_weight < {{p}}if_shipping_cost=>c_rate-bulk_weight.
|
||||
rv_rate = '18.75'.
|
||||
ELSE.
|
||||
DATA lv_started_kg TYPE i.
|
||||
lv_started_kg = iv_weight - {{p}}if_shipping_cost=>c_rate-bulk_weight.
|
||||
rv_rate = '18.75' + lv_started_kg * {{p}}if_shipping_cost=>c_rate-bulk_step.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD express_surcharge.
|
||||
rv_surcharge = round( val = iv_rate * {{p}}if_shipping_cost=>c_rate-express_percent / 100
|
||||
dec = 2 ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
63
tasks_gen/eval/G0009/spec.md
Normal file
63
tasks_gen/eval/G0009/spec.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# 1. Goal
|
||||
Calculate the shipping cost of a parcel. A webshop calls the calculation before
|
||||
it shows the total price to the customer. The cost depends on the weight of the
|
||||
parcel and on an express flag. The class that the webshop uses today returns
|
||||
wrong costs. A corrected class must replace it.
|
||||
|
||||
# 2. Open questions
|
||||
None.
|
||||
|
||||
# 3. Context
|
||||
- The interface {{P}}IF_SHIPPING_COST exists in package $TMP. It is active.
|
||||
- The interface contains the types TY_WEIGHT (packed, 3 decimals) and TY_AMOUNT
|
||||
(packed, 2 decimals), the constant structure C_RATE, and one method:
|
||||
CALCULATE IMPORTING iv_weight TYPE ty_weight, iv_express TYPE abap_bool
|
||||
RETURNING VALUE(rv_cost) TYPE ty_amount.
|
||||
- C_RATE has the components MIN_CHARGE (6.00), EXPRESS_PERCENT (35),
|
||||
BULK_WEIGHT (31.500), and BULK_STEP (0.65).
|
||||
- The class {{P}}SHIPPING_COST_LEGACY exists in package $TMP. It is active and
|
||||
it implements the interface, but the calculated costs are wrong.
|
||||
- Reported symptoms of {{P}}SHIPPING_COST_LEGACY:
|
||||
1. A parcel with a weight that is exactly on a weight limit is charged with
|
||||
the rate of the next weight limit.
|
||||
2. A parcel that is heavier than C_RATE-BULK_WEIGHT is charged as if its
|
||||
weight were exactly C_RATE-BULK_WEIGHT.
|
||||
3. An express parcel that is lighter than the minimum charge gets a total
|
||||
that is too high.
|
||||
|
||||
# 4. Contract
|
||||
- Create the class {{P}}SHIPPING_COST_CALC in package $TMP.
|
||||
- The class is public and final and has a public constructor without parameters.
|
||||
- The class implements {{P}}IF_SHIPPING_COST.
|
||||
- Do not add other public methods.
|
||||
- Do not change {{P}}IF_SHIPPING_COST and {{P}}SHIPPING_COST_LEGACY.
|
||||
|
||||
# 5. Business rules
|
||||
Apply the rules in this sequence.
|
||||
1. If iv_weight is zero or negative, the cost is 0. Stop.
|
||||
2. Else calculate the base rate from the weight:
|
||||
- weight up to and including 1.000 kg: 4.90
|
||||
- weight up to and including 5.000 kg: 8.50
|
||||
- weight up to and including 10.000 kg: 12.00
|
||||
- weight up to and including 31.500 kg: 18.75
|
||||
- weight above 31.500 kg: 18.75 plus 0.65 for each started kilogram above
|
||||
31.500 kg. A started kilogram is a kilogram that is not complete.
|
||||
Example: 31.501 kg is 0.001 kg above 31.500 kg, so there is one started
|
||||
kilogram. 33.000 kg is 1.500 kg above 31.500 kg, so there are two started
|
||||
kilograms.
|
||||
3. If iv_express is abap_true, add 35 percent of the base rate to the base rate.
|
||||
Round this surcharge to 2 decimals. Round a half away from zero.
|
||||
4. If the result is below 6.00, the result is 6.00.
|
||||
5. The result has 2 decimals.
|
||||
|
||||
# 6. Constraints
|
||||
- Release target: 8.16.
|
||||
- Coding standards: Clean ABAP. Methods below 40 statements. No global variables.
|
||||
No comment that restates the code.
|
||||
- Out of scope: do not change {{P}}IF_SHIPPING_COST and
|
||||
{{P}}SHIPPING_COST_LEGACY.
|
||||
|
||||
# 7. Acceptance
|
||||
- The class is active and has no syntax error.
|
||||
- The hidden tests pass.
|
||||
- Write your own ABAP Unit tests for the class.
|
||||
59
tasks_gen/eval/G0009/task.json
Normal file
59
tasks_gen/eval/G0009/task.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"id": "G0009",
|
||||
"category": "I",
|
||||
"object_type": "CLAS",
|
||||
"difficulty": 2,
|
||||
"release_target": "v816",
|
||||
"expected_outcome": "implement",
|
||||
"budget": {
|
||||
"max_tool_calls": 50,
|
||||
"max_activations": 12
|
||||
},
|
||||
"seed": [
|
||||
{
|
||||
"type": "INTF",
|
||||
"name": "{{P}}IF_SHIPPING_COST",
|
||||
"file": "seed/if_shipping_cost.intf.abap",
|
||||
"description": "Shipping cost contract with the weight and amount types, the rate constants and the CALCULATE method"
|
||||
},
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}SHIPPING_COST_LEGACY",
|
||||
"file": "seed/shipping_cost_legacy.clas.abap",
|
||||
"description": "Active class that implements the interface but calculates wrong costs"
|
||||
}
|
||||
],
|
||||
"contract": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}SHIPPING_COST_CALC",
|
||||
"implements": "{{P}}IF_SHIPPING_COST"
|
||||
}
|
||||
],
|
||||
"out_of_scope": [
|
||||
"{{P}}IF_SHIPPING_COST",
|
||||
"{{P}}SHIPPING_COST_LEGACY"
|
||||
],
|
||||
"hidden_tests": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}T02_HIDDEN",
|
||||
"file": "hidden/t02_hidden.clas.abap",
|
||||
"description": "T02 hidden tests for the shipping cost calculation"
|
||||
}
|
||||
],
|
||||
"reference": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}SHIPPING_COST_CALC",
|
||||
"file": "reference/shipping_cost_calc.clas.abap",
|
||||
"description": "Correct shipping cost calculation",
|
||||
"testclasses_file": "reference/shipping_cost_calc.testclasses.abap"
|
||||
}
|
||||
],
|
||||
"craft_checks": [
|
||||
"method_length",
|
||||
"cyclomatic_complexity",
|
||||
"no_global_variables"
|
||||
]
|
||||
}
|
||||
25
tasks_gen/eval/G0010/generation.json
Normal file
25
tasks_gen/eval/G0010/generation.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"id": "G0010",
|
||||
"pool": "eval",
|
||||
"object_type": "CLAS",
|
||||
"category": "C",
|
||||
"attempts": [
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 0,
|
||||
"null": 0
|
||||
},
|
||||
{
|
||||
"stage": "bundle",
|
||||
"errors": [
|
||||
"invalid JSON: Extra data: line 1 column 24591 (char 24590)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 100.0,
|
||||
"null": 0
|
||||
}
|
||||
],
|
||||
"accepted": true
|
||||
}
|
||||
173
tasks_gen/eval/G0010/hidden/t02_hidden.clas.abap
Normal file
173
tasks_gen/eval/G0010/hidden/t02_hidden.clas.abap
Normal file
@@ -0,0 +1,173 @@
|
||||
CLASS {{p}}t02_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
|
||||
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
PRIVATE SECTION.
|
||||
DATA mo_cut TYPE REF TO {{p}}if_stock_cover.
|
||||
METHODS setup.
|
||||
METHODS all_ok FOR TESTING.
|
||||
METHODS shortage_is_reported FOR TESTING.
|
||||
METHODS stock_used_in_order FOR TESTING.
|
||||
METHODS negative_stock_ignored FOR TESTING.
|
||||
METHODS zero_requirement_invalid FOR TESTING.
|
||||
METHODS negative_req_invalid FOR TESTING.
|
||||
METHODS missing_stock_is_short FOR TESTING.
|
||||
METHODS case_and_blanks_ignored FOR TESTING.
|
||||
METHODS stock_items_are_summed FOR TESTING.
|
||||
METHODS no_requirements FOR TESTING.
|
||||
METHODS shortages_filter_sort FOR TESTING.
|
||||
METHODS shortages_empty_all_ok FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}t02_hidden IMPLEMENTATION.
|
||||
METHOD setup.
|
||||
mo_cut = NEW {{p}}stock_cover_checker( ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD all_ok.
|
||||
DATA(lt_stock) = VALUE {{p}}if_stock_cover=>tt_stock_item(
|
||||
( material = 'MAT-A' plant = 'P1' quantity = 100 )
|
||||
( material = 'MAT-B' plant = 'P1' quantity = 50 ) ).
|
||||
DATA(lt_req) = VALUE {{p}}if_stock_cover=>tt_requirement(
|
||||
( material = 'MAT-A' plant = 'P1' required = 30 )
|
||||
( material = 'MAT-B' plant = 'P1' required = 50 ) ).
|
||||
DATA(lt_exp) = VALUE {{p}}if_stock_cover=>tt_result(
|
||||
( material = 'MAT-A' plant = 'P1' required = 30 available = 100 shortage = 0 status = 'OK' )
|
||||
( material = 'MAT-B' plant = 'P1' required = 50 available = 50 shortage = 0 status = 'OK' ) ).
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = lt_exp
|
||||
act = mo_cut->check( it_stock = lt_stock it_requirement = lt_req ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD shortage_is_reported.
|
||||
DATA(lt_stock) = VALUE {{p}}if_stock_cover=>tt_stock_item(
|
||||
( material = 'MAT-A' plant = 'P1' quantity = 30 ) ).
|
||||
DATA(lt_req) = VALUE {{p}}if_stock_cover=>tt_requirement(
|
||||
( material = 'MAT-A' plant = 'P1' required = 50 ) ).
|
||||
DATA(lt_exp) = VALUE {{p}}if_stock_cover=>tt_result(
|
||||
( material = 'MAT-A' plant = 'P1' required = 50 available = 30 shortage = 20 status = 'SHORT' ) ).
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = lt_exp
|
||||
act = mo_cut->check( it_stock = lt_stock it_requirement = lt_req ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD stock_used_in_order.
|
||||
DATA(lt_stock) = VALUE {{p}}if_stock_cover=>tt_stock_item(
|
||||
( material = 'MAT-A' plant = 'P1' quantity = 100 ) ).
|
||||
DATA(lt_req) = VALUE {{p}}if_stock_cover=>tt_requirement(
|
||||
( material = 'MAT-A' plant = 'P1' required = 60 )
|
||||
( material = 'MAT-A' plant = 'P1' required = 60 ) ).
|
||||
DATA(lt_exp) = VALUE {{p}}if_stock_cover=>tt_result(
|
||||
( material = 'MAT-A' plant = 'P1' required = 60 available = 100 shortage = 0 status = 'OK' )
|
||||
( material = 'MAT-A' plant = 'P1' required = 60 available = 40 shortage = 20 status = 'SHORT' ) ).
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = lt_exp
|
||||
act = mo_cut->check( it_stock = lt_stock it_requirement = lt_req ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD negative_stock_ignored.
|
||||
DATA(lt_stock) = VALUE {{p}}if_stock_cover=>tt_stock_item(
|
||||
( material = 'MAT-A' plant = 'P1' quantity = 100 )
|
||||
( material = 'MAT-A' plant = 'P1' quantity = -40 ) ).
|
||||
DATA(lt_req) = VALUE {{p}}if_stock_cover=>tt_requirement(
|
||||
( material = 'MAT-A' plant = 'P1' required = 100 ) ).
|
||||
DATA(lt_exp) = VALUE {{p}}if_stock_cover=>tt_result(
|
||||
( material = 'MAT-A' plant = 'P1' required = 100 available = 100 shortage = 0 status = 'OK' ) ).
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = lt_exp
|
||||
act = mo_cut->check( it_stock = lt_stock it_requirement = lt_req ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD zero_requirement_invalid.
|
||||
DATA(lt_stock) = VALUE {{p}}if_stock_cover=>tt_stock_item(
|
||||
( material = 'MAT-A' plant = 'P1' quantity = 50 ) ).
|
||||
DATA(lt_req) = VALUE {{p}}if_stock_cover=>tt_requirement(
|
||||
( material = 'MAT-A' plant = 'P1' required = 0 )
|
||||
( material = 'MAT-A' plant = 'P1' required = 50 ) ).
|
||||
DATA(lt_exp) = VALUE {{p}}if_stock_cover=>tt_result(
|
||||
( material = 'MAT-A' plant = 'P1' required = 0 available = 50 shortage = 0 status = 'INVALID' )
|
||||
( material = 'MAT-A' plant = 'P1' required = 50 available = 50 shortage = 0 status = 'OK' ) ).
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = lt_exp
|
||||
act = mo_cut->check( it_stock = lt_stock it_requirement = lt_req ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD negative_req_invalid.
|
||||
DATA(lt_stock) = VALUE {{p}}if_stock_cover=>tt_stock_item(
|
||||
( material = 'MAT-A' plant = 'P1' quantity = 50 ) ).
|
||||
DATA(lt_req) = VALUE {{p}}if_stock_cover=>tt_requirement(
|
||||
( material = 'MAT-A' plant = 'P1' required = -10 ) ).
|
||||
DATA(lt_exp) = VALUE {{p}}if_stock_cover=>tt_result(
|
||||
( material = 'MAT-A' plant = 'P1' required = -10 available = 50 shortage = 0 status = 'INVALID' ) ).
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = lt_exp
|
||||
act = mo_cut->check( it_stock = lt_stock it_requirement = lt_req ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD missing_stock_is_short.
|
||||
DATA(lt_stock) = VALUE {{p}}if_stock_cover=>tt_stock_item( ).
|
||||
DATA(lt_req) = VALUE {{p}}if_stock_cover=>tt_requirement(
|
||||
( material = 'MAT-X' plant = 'P9' required = 25 ) ).
|
||||
DATA(lt_exp) = VALUE {{p}}if_stock_cover=>tt_result(
|
||||
( material = 'MAT-X' plant = 'P9' required = 25 available = 0 shortage = 25 status = 'SHORT' ) ).
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = lt_exp
|
||||
act = mo_cut->check( it_stock = lt_stock it_requirement = lt_req ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD case_and_blanks_ignored.
|
||||
DATA(lt_stock) = VALUE {{p}}if_stock_cover=>tt_stock_item(
|
||||
( material = 'mat-a' plant = 'p1' quantity = 80 ) ).
|
||||
DATA(lt_req) = VALUE {{p}}if_stock_cover=>tt_requirement(
|
||||
( material = ' MAT-A ' plant = 'P1 ' required = 80 ) ).
|
||||
DATA(lt_exp) = VALUE {{p}}if_stock_cover=>tt_result(
|
||||
( material = ' MAT-A ' plant = 'P1 ' required = 80 available = 80 shortage = 0 status = 'OK' ) ).
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = lt_exp
|
||||
act = mo_cut->check( it_stock = lt_stock it_requirement = lt_req ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD stock_items_are_summed.
|
||||
DATA(lt_stock) = VALUE {{p}}if_stock_cover=>tt_stock_item(
|
||||
( material = 'MAT-A' plant = 'P1' quantity = 10 )
|
||||
( material = 'MAT-A' plant = 'P1' quantity = 20 )
|
||||
( material = 'MAT-A' plant = 'P2' quantity = 5 ) ).
|
||||
DATA(lt_req) = VALUE {{p}}if_stock_cover=>tt_requirement(
|
||||
( material = 'MAT-A' plant = 'P1' required = 30 )
|
||||
( material = 'MAT-A' plant = 'P2' required = 5 ) ).
|
||||
DATA(lt_exp) = VALUE {{p}}if_stock_cover=>tt_result(
|
||||
( material = 'MAT-A' plant = 'P1' required = 30 available = 30 shortage = 0 status = 'OK' )
|
||||
( material = 'MAT-A' plant = 'P2' required = 5 available = 5 shortage = 0 status = 'OK' ) ).
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = lt_exp
|
||||
act = mo_cut->check( it_stock = lt_stock it_requirement = lt_req ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD no_requirements.
|
||||
DATA(lt_stock) = VALUE {{p}}if_stock_cover=>tt_stock_item(
|
||||
( material = 'MAT-A' plant = 'P1' quantity = 10 ) ).
|
||||
DATA(lt_req) = VALUE {{p}}if_stock_cover=>tt_requirement( ).
|
||||
DATA(lt_act) = mo_cut->check( it_stock = lt_stock it_requirement = lt_req ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_act ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD shortages_filter_sort.
|
||||
DATA(lt_in) = VALUE {{p}}if_stock_cover=>tt_result(
|
||||
( material = 'MAT-C' plant = 'P1' required = 10 available = 0 shortage = 10 status = 'SHORT' )
|
||||
( material = 'MAT-A' plant = 'P2' required = 10 available = 0 shortage = 10 status = 'SHORT' )
|
||||
( material = 'MAT-B' plant = 'P1' required = 10 available = 10 shortage = 0 status = 'OK' )
|
||||
( material = 'MAT-A' plant = 'P1' required = 10 available = 0 shortage = 10 status = 'SHORT' )
|
||||
( material = 'MAT-D' plant = 'P1' required = 10 available = 10 shortage = 0 status = 'INVALID' ) ).
|
||||
DATA(lt_exp) = VALUE {{p}}if_stock_cover=>tt_result(
|
||||
( material = 'MAT-A' plant = 'P1' required = 10 available = 0 shortage = 10 status = 'SHORT' )
|
||||
( material = 'MAT-A' plant = 'P2' required = 10 available = 0 shortage = 10 status = 'SHORT' )
|
||||
( material = 'MAT-C' plant = 'P1' required = 10 available = 0 shortage = 10 status = 'SHORT' ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = lt_exp act = mo_cut->shortages( lt_in ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD shortages_empty_all_ok.
|
||||
DATA(lt_in) = VALUE {{p}}if_stock_cover=>tt_result(
|
||||
( material = 'MAT-A' plant = 'P1' required = 10 available = 10 shortage = 0 status = 'OK' )
|
||||
( material = 'MAT-B' plant = 'P1' required = 10 available = 10 shortage = 0 status = 'INVALID' ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( mo_cut->shortages( lt_in ) ) ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
128
tasks_gen/eval/G0010/reference/stock_cover_checker.clas.abap
Normal file
128
tasks_gen/eval/G0010/reference/stock_cover_checker.clas.abap
Normal file
@@ -0,0 +1,128 @@
|
||||
CLASS {{p}}stock_cover_checker DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
||||
PUBLIC SECTION.
|
||||
INTERFACES {{p}}if_stock_cover.
|
||||
|
||||
PRIVATE SECTION.
|
||||
TYPES:
|
||||
BEGIN OF ty_stock,
|
||||
material TYPE {{p}}if_stock_cover=>ty_material,
|
||||
plant TYPE {{p}}if_stock_cover=>ty_plant,
|
||||
remaining TYPE {{p}}if_stock_cover=>ty_quantity,
|
||||
END OF ty_stock.
|
||||
TYPES tt_stock TYPE HASHED TABLE OF ty_stock WITH UNIQUE KEY material plant.
|
||||
|
||||
METHODS aggregate
|
||||
IMPORTING it_stock TYPE {{p}}if_stock_cover=>tt_stock_item
|
||||
RETURNING VALUE(rt_stock) TYPE tt_stock.
|
||||
|
||||
METHODS free_quantity
|
||||
IMPORTING it_stock TYPE tt_stock
|
||||
iv_material TYPE {{p}}if_stock_cover=>ty_material
|
||||
iv_plant TYPE {{p}}if_stock_cover=>ty_plant
|
||||
RETURNING VALUE(rv_quantity) TYPE {{p}}if_stock_cover=>ty_quantity.
|
||||
|
||||
METHODS consume
|
||||
IMPORTING iv_material TYPE {{p}}if_stock_cover=>ty_material
|
||||
iv_plant TYPE {{p}}if_stock_cover=>ty_plant
|
||||
iv_quantity TYPE {{p}}if_stock_cover=>ty_quantity
|
||||
CHANGING ct_stock TYPE tt_stock.
|
||||
|
||||
METHODS normalize_material
|
||||
IMPORTING iv_material TYPE {{p}}if_stock_cover=>ty_material
|
||||
RETURNING VALUE(rv_material) TYPE {{p}}if_stock_cover=>ty_material.
|
||||
|
||||
METHODS normalize_plant
|
||||
IMPORTING iv_plant TYPE {{p}}if_stock_cover=>ty_plant
|
||||
RETURNING VALUE(rv_plant) TYPE {{p}}if_stock_cover=>ty_plant.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}stock_cover_checker IMPLEMENTATION.
|
||||
METHOD {{p}}if_stock_cover~check.
|
||||
DATA(lt_stock) = aggregate( it_stock ).
|
||||
|
||||
LOOP AT it_requirement INTO DATA(ls_requirement).
|
||||
DATA(lv_free) = free_quantity( it_stock = lt_stock
|
||||
iv_material = ls_requirement-material
|
||||
iv_plant = ls_requirement-plant ).
|
||||
DATA(ls_result) = VALUE {{p}}if_stock_cover=>ty_result(
|
||||
material = ls_requirement-material
|
||||
plant = ls_requirement-plant
|
||||
required = ls_requirement-required
|
||||
available = lv_free ).
|
||||
|
||||
IF ls_requirement-required <= 0.
|
||||
ls_result-status = {{p}}if_stock_cover=>c_status-invalid.
|
||||
ELSEIF lv_free >= ls_requirement-required.
|
||||
ls_result-status = {{p}}if_stock_cover=>c_status-ok.
|
||||
consume( EXPORTING iv_material = ls_requirement-material
|
||||
iv_plant = ls_requirement-plant
|
||||
iv_quantity = ls_requirement-required
|
||||
CHANGING ct_stock = lt_stock ).
|
||||
ELSE.
|
||||
ls_result-status = {{p}}if_stock_cover=>c_status-short.
|
||||
ls_result-shortage = ls_requirement-required - lv_free.
|
||||
consume( EXPORTING iv_material = ls_requirement-material
|
||||
iv_plant = ls_requirement-plant
|
||||
iv_quantity = lv_free
|
||||
CHANGING ct_stock = lt_stock ).
|
||||
ENDIF.
|
||||
|
||||
APPEND ls_result TO rt_result.
|
||||
ENDLOOP.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD {{p}}if_stock_cover~shortages.
|
||||
rt_shortage = it_result.
|
||||
DELETE rt_shortage WHERE status <> {{p}}if_stock_cover=>c_status-short.
|
||||
SORT rt_shortage BY material plant.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD aggregate.
|
||||
LOOP AT it_stock INTO DATA(ls_stock).
|
||||
IF ls_stock-quantity < 0.
|
||||
CONTINUE.
|
||||
ENDIF.
|
||||
DATA(lv_material) = normalize_material( ls_stock-material ).
|
||||
DATA(lv_plant) = normalize_plant( ls_stock-plant ).
|
||||
ASSIGN rt_stock[ material = lv_material plant = lv_plant ] TO FIELD-SYMBOL(<ls_stock_sum>).
|
||||
IF sy-subrc = 0.
|
||||
<ls_stock_sum>-remaining = <ls_stock_sum>-remaining + ls_stock-quantity.
|
||||
ELSE.
|
||||
INSERT VALUE #( material = lv_material
|
||||
plant = lv_plant
|
||||
remaining = ls_stock-quantity ) INTO TABLE rt_stock.
|
||||
ENDIF.
|
||||
ENDLOOP.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD free_quantity.
|
||||
ASSIGN it_stock[ material = normalize_material( iv_material )
|
||||
plant = normalize_plant( iv_plant ) ] TO FIELD-SYMBOL(<ls_stock>).
|
||||
IF sy-subrc = 0.
|
||||
rv_quantity = <ls_stock>-remaining.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD consume.
|
||||
ASSIGN ct_stock[ material = normalize_material( iv_material )
|
||||
plant = normalize_plant( iv_plant ) ] TO FIELD-SYMBOL(<ls_stock>).
|
||||
IF sy-subrc = 0.
|
||||
<ls_stock>-remaining = <ls_stock>-remaining - iv_quantity.
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD normalize_material.
|
||||
DATA lv_text TYPE string.
|
||||
lv_text = iv_material.
|
||||
REPLACE ALL OCCURRENCES OF ` ` IN lv_text WITH ``.
|
||||
rv_material = to_upper( lv_text ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD normalize_plant.
|
||||
DATA lv_text TYPE string.
|
||||
lv_text = iv_plant.
|
||||
REPLACE ALL OCCURRENCES OF ` ` IN lv_text WITH ``.
|
||||
rv_plant = to_upper( lv_text ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
@@ -0,0 +1,93 @@
|
||||
CLASS ltc_stock_cover DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
PRIVATE SECTION.
|
||||
DATA mo_cut TYPE REF TO {{p}}if_stock_cover.
|
||||
METHODS setup.
|
||||
METHODS covered_req_is_ok FOR TESTING.
|
||||
METHODS shortage_is_reported FOR TESTING.
|
||||
METHODS stock_shared_in_order FOR TESTING.
|
||||
METHODS invalid_req_keeps_stock FOR TESTING.
|
||||
METHODS blanks_and_case_ignored FOR TESTING.
|
||||
METHODS shortages_are_sorted FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS ltc_stock_cover IMPLEMENTATION.
|
||||
METHOD setup.
|
||||
mo_cut = NEW {{p}}stock_cover_checker( ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD covered_req_is_ok.
|
||||
DATA(lt_stock) = VALUE {{p}}if_stock_cover=>tt_stock_item(
|
||||
( material = 'MAT-A' plant = 'P1' quantity = 40 ) ).
|
||||
DATA(lt_req) = VALUE {{p}}if_stock_cover=>tt_requirement(
|
||||
( material = 'MAT-A' plant = 'P1' required = 40 ) ).
|
||||
DATA(lt_exp) = VALUE {{p}}if_stock_cover=>tt_result(
|
||||
( material = 'MAT-A' plant = 'P1' required = 40 available = 40 shortage = 0 status = 'OK' ) ).
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = lt_exp
|
||||
act = mo_cut->check( it_stock = lt_stock it_requirement = lt_req ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD shortage_is_reported.
|
||||
DATA(lt_stock) = VALUE {{p}}if_stock_cover=>tt_stock_item(
|
||||
( material = 'MAT-A' plant = 'P1' quantity = 5 ) ).
|
||||
DATA(lt_req) = VALUE {{p}}if_stock_cover=>tt_requirement(
|
||||
( material = 'MAT-A' plant = 'P1' required = 12 ) ).
|
||||
DATA(lt_exp) = VALUE {{p}}if_stock_cover=>tt_result(
|
||||
( material = 'MAT-A' plant = 'P1' required = 12 available = 5 shortage = 7 status = 'SHORT' ) ).
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = lt_exp
|
||||
act = mo_cut->check( it_stock = lt_stock it_requirement = lt_req ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD stock_shared_in_order.
|
||||
DATA(lt_stock) = VALUE {{p}}if_stock_cover=>tt_stock_item(
|
||||
( material = 'MAT-A' plant = 'P1' quantity = 10 ) ).
|
||||
DATA(lt_req) = VALUE {{p}}if_stock_cover=>tt_requirement(
|
||||
( material = 'MAT-A' plant = 'P1' required = 6 )
|
||||
( material = 'MAT-A' plant = 'P1' required = 6 ) ).
|
||||
DATA(lt_exp) = VALUE {{p}}if_stock_cover=>tt_result(
|
||||
( material = 'MAT-A' plant = 'P1' required = 6 available = 10 shortage = 0 status = 'OK' )
|
||||
( material = 'MAT-A' plant = 'P1' required = 6 available = 4 shortage = 2 status = 'SHORT' ) ).
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = lt_exp
|
||||
act = mo_cut->check( it_stock = lt_stock it_requirement = lt_req ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD invalid_req_keeps_stock.
|
||||
DATA(lt_stock) = VALUE {{p}}if_stock_cover=>tt_stock_item(
|
||||
( material = 'MAT-A' plant = 'P1' quantity = 9 ) ).
|
||||
DATA(lt_req) = VALUE {{p}}if_stock_cover=>tt_requirement(
|
||||
( material = 'MAT-A' plant = 'P1' required = 0 )
|
||||
( material = 'MAT-A' plant = 'P1' required = 9 ) ).
|
||||
DATA(lt_exp) = VALUE {{p}}if_stock_cover=>tt_result(
|
||||
( material = 'MAT-A' plant = 'P1' required = 0 available = 9 shortage = 0 status = 'INVALID' )
|
||||
( material = 'MAT-A' plant = 'P1' required = 9 available = 9 shortage = 0 status = 'OK' ) ).
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = lt_exp
|
||||
act = mo_cut->check( it_stock = lt_stock it_requirement = lt_req ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD blanks_and_case_ignored.
|
||||
DATA(lt_stock) = VALUE {{p}}if_stock_cover=>tt_stock_item(
|
||||
( material = 'mat-a' plant = 'p1' quantity = 7 ) ).
|
||||
DATA(lt_req) = VALUE {{p}}if_stock_cover=>tt_requirement(
|
||||
( material = ' MAT-A ' plant = 'P1' required = 7 ) ).
|
||||
DATA(lt_exp) = VALUE {{p}}if_stock_cover=>tt_result(
|
||||
( material = ' MAT-A ' plant = 'P1' required = 7 available = 7 shortage = 0 status = 'OK' ) ).
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = lt_exp
|
||||
act = mo_cut->check( it_stock = lt_stock it_requirement = lt_req ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD shortages_are_sorted.
|
||||
DATA(lt_in) = VALUE {{p}}if_stock_cover=>tt_result(
|
||||
( material = 'MAT-B' plant = 'P1' required = 1 available = 0 shortage = 1 status = 'SHORT' )
|
||||
( material = 'MAT-A' plant = 'P2' required = 1 available = 0 shortage = 1 status = 'SHORT' )
|
||||
( material = 'MAT-A' plant = 'P1' required = 1 available = 1 shortage = 0 status = 'OK' ) ).
|
||||
DATA(lt_exp) = VALUE {{p}}if_stock_cover=>tt_result(
|
||||
( material = 'MAT-A' plant = 'P2' required = 1 available = 0 shortage = 1 status = 'SHORT' )
|
||||
( material = 'MAT-B' plant = 'P1' required = 1 available = 0 shortage = 1 status = 'SHORT' ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = lt_exp act = mo_cut->shortages( lt_in ) ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
46
tasks_gen/eval/G0010/seed/if_stock_cover.intf.abap
Normal file
46
tasks_gen/eval/G0010/seed/if_stock_cover.intf.abap
Normal file
@@ -0,0 +1,46 @@
|
||||
INTERFACE {{p}}if_stock_cover PUBLIC.
|
||||
TYPES ty_material TYPE c LENGTH 18.
|
||||
TYPES ty_plant TYPE c LENGTH 4.
|
||||
TYPES ty_quantity TYPE p LENGTH 13 DECIMALS 3.
|
||||
TYPES ty_status TYPE c LENGTH 10.
|
||||
|
||||
TYPES: BEGIN OF ty_stock_item,
|
||||
material TYPE ty_material,
|
||||
plant TYPE ty_plant,
|
||||
quantity TYPE ty_quantity,
|
||||
END OF ty_stock_item.
|
||||
TYPES tt_stock_item TYPE STANDARD TABLE OF ty_stock_item WITH EMPTY KEY.
|
||||
|
||||
TYPES: BEGIN OF ty_requirement,
|
||||
material TYPE ty_material,
|
||||
plant TYPE ty_plant,
|
||||
required TYPE ty_quantity,
|
||||
END OF ty_requirement.
|
||||
TYPES tt_requirement TYPE STANDARD TABLE OF ty_requirement WITH EMPTY KEY.
|
||||
|
||||
TYPES: BEGIN OF ty_result,
|
||||
material TYPE ty_material,
|
||||
plant TYPE ty_plant,
|
||||
required TYPE ty_quantity,
|
||||
available TYPE ty_quantity,
|
||||
shortage TYPE ty_quantity,
|
||||
status TYPE ty_status,
|
||||
END OF ty_result.
|
||||
TYPES tt_result TYPE STANDARD TABLE OF ty_result WITH EMPTY KEY.
|
||||
|
||||
CONSTANTS:
|
||||
BEGIN OF c_status,
|
||||
ok TYPE ty_status VALUE 'OK',
|
||||
short TYPE ty_status VALUE 'SHORT',
|
||||
invalid TYPE ty_status VALUE 'INVALID',
|
||||
END OF c_status.
|
||||
|
||||
METHODS check
|
||||
IMPORTING it_stock TYPE tt_stock_item
|
||||
it_requirement TYPE tt_requirement
|
||||
RETURNING VALUE(rt_result) TYPE tt_result.
|
||||
|
||||
METHODS shortages
|
||||
IMPORTING it_result TYPE tt_result
|
||||
RETURNING VALUE(rt_shortage) TYPE tt_result.
|
||||
ENDINTERFACE.
|
||||
70
tasks_gen/eval/G0010/spec.md
Normal file
70
tasks_gen/eval/G0010/spec.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# 1. Goal
|
||||
A warehouse planner checks if the free stock covers the requirements of a
|
||||
production run. The planner sends a list of stock items and a list of
|
||||
requirements to a class. The class returns one result row for each requirement.
|
||||
|
||||
# 2. Open questions
|
||||
None.
|
||||
|
||||
# 3. Context
|
||||
- The interface {{P}}IF_STOCK_COVER exists in package $TMP. It is active.
|
||||
- The interface contains the types TY_MATERIAL (CHAR 18), TY_PLANT (CHAR 4),
|
||||
TY_QUANTITY (PACKED 13, 3) and TY_STATUS (CHAR 10).
|
||||
- The interface contains the structures TY_STOCK_ITEM, TY_REQUIREMENT and
|
||||
TY_RESULT and the table types TT_STOCK_ITEM, TT_REQUIREMENT and TT_RESULT.
|
||||
- The interface contains the constant structure C_STATUS with the values OK,
|
||||
SHORT and INVALID.
|
||||
- The interface has the method CHECK. It imports a stock table and a requirement
|
||||
table and returns a result table.
|
||||
- The interface has the method SHORTAGES. It imports a result table and returns a
|
||||
result table.
|
||||
|
||||
# 4. Contract
|
||||
- Create the class {{P}}STOCK_COVER_CHECKER in package $TMP.
|
||||
- The class is public and final.
|
||||
- The class implements {{P}}IF_STOCK_COVER.
|
||||
- Do not add other public methods.
|
||||
- The hidden tests create the class with NEW {{P}}STOCK_COVER_CHECKER( ).
|
||||
|
||||
# 5. Business rules
|
||||
|
||||
## Stock
|
||||
1. Each stock item has a material, a plant and a quantity. Ignore every stock
|
||||
item whose quantity is less than zero.
|
||||
2. Sum the quantities of the remaining stock items for each combination of
|
||||
material and plant. This sum is the free stock of the material and plant.
|
||||
3. Compare material and plant without regard to case. Remove all blanks before
|
||||
you compare them.
|
||||
|
||||
## Requirements
|
||||
4. Process the requirements in the given sequence. The result contains one row
|
||||
for each requirement in the same sequence.
|
||||
5. The available quantity of a requirement is the free stock of its material and
|
||||
plant at the time of the requirement. If no stock exists for the material and
|
||||
plant, the available quantity is 0.
|
||||
6. If the required quantity is less than or equal to 0, the status is INVALID.
|
||||
The requirement does not use free stock. The shortage is 0.
|
||||
7. Else if the available quantity is greater than or equal to the required
|
||||
quantity, the status is OK. The shortage is 0. The requirement uses the
|
||||
required quantity of the free stock.
|
||||
8. Else the status is SHORT. The requirement uses all free stock. The shortage is
|
||||
the required quantity minus the available quantity.
|
||||
|
||||
## Result
|
||||
9. A result row contains the material and the plant of the requirement without a
|
||||
change, the required quantity, the available quantity, the shortage and the
|
||||
status.
|
||||
10. The method SHORTAGES returns all rows of the result table whose status is
|
||||
SHORT. Sort these rows by material in ascending order and then by plant in
|
||||
ascending order. The result can be empty.
|
||||
|
||||
# 6. Constraints
|
||||
- Release target: 8.16 (SAP_BASIS 816).
|
||||
- Coding standards: Clean ABAP. Method length below 40 statements. No global
|
||||
variables. No comment that restates the code.
|
||||
- Out of scope: do not change {{P}}IF_STOCK_COVER.
|
||||
|
||||
# 7. Acceptance
|
||||
- The class is active and has no syntax error.
|
||||
- The hidden tests pass.
|
||||
- Write your own ABAP Unit tests for the class.
|
||||
52
tasks_gen/eval/G0010/task.json
Normal file
52
tasks_gen/eval/G0010/task.json
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"id": "G0010",
|
||||
"category": "C",
|
||||
"object_type": "CLAS",
|
||||
"difficulty": 2,
|
||||
"release_target": "v816",
|
||||
"expected_outcome": "implement",
|
||||
"budget": {
|
||||
"max_tool_calls": 50,
|
||||
"max_activations": 12
|
||||
},
|
||||
"seed": [
|
||||
{
|
||||
"type": "INTF",
|
||||
"name": "{{P}}IF_STOCK_COVER",
|
||||
"file": "seed/if_stock_cover.intf.abap",
|
||||
"description": "Stock cover interface with types, constants and two methods"
|
||||
}
|
||||
],
|
||||
"contract": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}STOCK_COVER_CHECKER",
|
||||
"implements": "{{P}}IF_STOCK_COVER"
|
||||
}
|
||||
],
|
||||
"out_of_scope": [
|
||||
"{{P}}IF_STOCK_COVER"
|
||||
],
|
||||
"hidden_tests": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}T02_HIDDEN",
|
||||
"file": "hidden/t02_hidden.clas.abap",
|
||||
"description": "T02 hidden tests for the stock cover checker"
|
||||
}
|
||||
],
|
||||
"reference": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}STOCK_COVER_CHECKER",
|
||||
"file": "reference/stock_cover_checker.clas.abap",
|
||||
"description": "Stock cover checker with hashed stock table",
|
||||
"testclasses_file": "reference/stock_cover_checker.testclasses.abap"
|
||||
}
|
||||
],
|
||||
"craft_checks": [
|
||||
"table_kind",
|
||||
"access_path",
|
||||
"method_length"
|
||||
]
|
||||
}
|
||||
29
tasks_gen/eval/G0011/generation.json
Normal file
29
tasks_gen/eval/G0011/generation.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"id": "G0011",
|
||||
"pool": "eval",
|
||||
"object_type": "FUNC",
|
||||
"category": "A",
|
||||
"attempts": [
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 0,
|
||||
"null": 0
|
||||
},
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 92.5,
|
||||
"null": 0
|
||||
},
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 0,
|
||||
"null": 0
|
||||
},
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 92.5,
|
||||
"null": 0
|
||||
}
|
||||
],
|
||||
"accepted": false
|
||||
}
|
||||
117
tasks_gen/eval/G0011/hidden/t14_hidden.clas.abap
Normal file
117
tasks_gen/eval/G0011/hidden/t14_hidden.clas.abap
Normal file
@@ -0,0 +1,117 @@
|
||||
CLASS {{p}}t14_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
|
||||
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
PRIVATE SECTION.
|
||||
METHODS call
|
||||
IMPORTING
|
||||
iv_stock TYPE decfloat34
|
||||
iv_reorder_point TYPE decfloat34
|
||||
iv_target_stock TYPE decfloat34
|
||||
iv_pack_size TYPE decfloat34
|
||||
iv_min_qty TYPE decfloat34 DEFAULT 0
|
||||
EXPORTING
|
||||
ev_qty TYPE decfloat34
|
||||
ev_subrc TYPE sysubrc.
|
||||
METHODS no_replenishment_when_stock_ok FOR TESTING.
|
||||
METHODS replenish_to_target FOR TESTING.
|
||||
METHODS pack_rounding_up FOR TESTING.
|
||||
METHODS exact_multiple_not_rounded FOR TESTING.
|
||||
METHODS minimum_quantity_applied FOR TESTING.
|
||||
METHODS minimum_quantity_rounded_up FOR TESTING.
|
||||
METHODS negative_minimum_is_zero FOR TESTING.
|
||||
METHODS target_below_stock_is_zero FOR TESTING.
|
||||
METHODS invalid_pack_size FOR TESTING.
|
||||
METHODS class_calculates_directly FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}t14_hidden IMPLEMENTATION.
|
||||
METHOD call.
|
||||
ev_qty = 0.
|
||||
CALL FUNCTION '{{P}}REPL_CALC'
|
||||
EXPORTING
|
||||
iv_stock = iv_stock
|
||||
iv_reorder_point = iv_reorder_point
|
||||
iv_target_stock = iv_target_stock
|
||||
iv_pack_size = iv_pack_size
|
||||
iv_min_qty = iv_min_qty
|
||||
IMPORTING
|
||||
ev_qty = ev_qty
|
||||
EXCEPTIONS
|
||||
invalid_pack_size = 1
|
||||
OTHERS = 2.
|
||||
ev_subrc = sy-subrc.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD no_replenishment_when_stock_ok.
|
||||
DATA lv_qty TYPE decfloat34.
|
||||
call( EXPORTING iv_stock = 50 iv_reorder_point = 20 iv_target_stock = 100 iv_pack_size = 10
|
||||
IMPORTING ev_qty = lv_qty ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_qty ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD replenish_to_target.
|
||||
DATA lv_qty TYPE decfloat34.
|
||||
call( EXPORTING iv_stock = 10 iv_reorder_point = 20 iv_target_stock = 50 iv_pack_size = 12
|
||||
IMPORTING ev_qty = lv_qty ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 48 ) act = lv_qty ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD pack_rounding_up.
|
||||
DATA lv_qty TYPE decfloat34.
|
||||
call( EXPORTING iv_stock = 0 iv_reorder_point = 10 iv_target_stock = 25 iv_pack_size = 6
|
||||
IMPORTING ev_qty = lv_qty ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 30 ) act = lv_qty ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD exact_multiple_not_rounded.
|
||||
DATA lv_qty TYPE decfloat34.
|
||||
call( EXPORTING iv_stock = 0 iv_reorder_point = 10 iv_target_stock = 24 iv_pack_size = 12
|
||||
IMPORTING ev_qty = lv_qty ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 24 ) act = lv_qty ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD minimum_quantity_applied.
|
||||
DATA lv_qty TYPE decfloat34.
|
||||
call( EXPORTING iv_stock = 10 iv_reorder_point = 20 iv_target_stock = 12 iv_pack_size = 1 iv_min_qty = 7
|
||||
IMPORTING ev_qty = lv_qty ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 7 ) act = lv_qty ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD minimum_quantity_rounded_up.
|
||||
DATA lv_qty TYPE decfloat34.
|
||||
call( EXPORTING iv_stock = 10 iv_reorder_point = 20 iv_target_stock = 12 iv_pack_size = 5 iv_min_qty = 7
|
||||
IMPORTING ev_qty = lv_qty ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 10 ) act = lv_qty ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD negative_minimum_is_zero.
|
||||
DATA lv_qty TYPE decfloat34.
|
||||
call( EXPORTING iv_stock = 0 iv_reorder_point = 10 iv_target_stock = 10 iv_pack_size = 4 iv_min_qty = -5
|
||||
IMPORTING ev_qty = lv_qty ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 12 ) act = lv_qty ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD target_below_stock_is_zero.
|
||||
DATA lv_qty TYPE decfloat34.
|
||||
call( EXPORTING iv_stock = 10 iv_reorder_point = 20 iv_target_stock = 5 iv_pack_size = 4
|
||||
IMPORTING ev_qty = lv_qty ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_qty ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD invalid_pack_size.
|
||||
DATA lv_rc TYPE sysubrc.
|
||||
call( EXPORTING iv_stock = 0 iv_reorder_point = 10 iv_target_stock = 20 iv_pack_size = 0
|
||||
IMPORTING ev_subrc = lv_rc ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD class_calculates_directly.
|
||||
DATA(lv_qty) = NEW {{p}}cl_repl( )->calculate(
|
||||
iv_stock = 10
|
||||
iv_reorder_point = 20
|
||||
iv_target_stock = 50
|
||||
iv_pack_size = 12
|
||||
iv_min_qty = 0 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 48 ) act = lv_qty ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
38
tasks_gen/eval/G0011/reference/cl_repl.clas.abap
Normal file
38
tasks_gen/eval/G0011/reference/cl_repl.clas.abap
Normal file
@@ -0,0 +1,38 @@
|
||||
CLASS {{p}}cl_repl DEFINITION PUBLIC FINAL CREATE PUBLIC.
|
||||
PUBLIC SECTION.
|
||||
METHODS calculate
|
||||
IMPORTING
|
||||
iv_stock TYPE decfloat34
|
||||
iv_reorder_point TYPE decfloat34
|
||||
iv_target_stock TYPE decfloat34
|
||||
iv_pack_size TYPE decfloat34
|
||||
iv_min_qty TYPE decfloat34
|
||||
RETURNING
|
||||
VALUE(rv_qty) TYPE decfloat34.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}cl_repl IMPLEMENTATION.
|
||||
METHOD calculate.
|
||||
IF iv_stock >= iv_reorder_point.
|
||||
rv_qty = 0.
|
||||
RETURN.
|
||||
ENDIF.
|
||||
|
||||
DATA(lv_need) = iv_target_stock - iv_stock.
|
||||
DATA(lv_min_qty) = iv_min_qty.
|
||||
IF lv_min_qty < 0.
|
||||
lv_min_qty = 0.
|
||||
ENDIF.
|
||||
IF lv_need < lv_min_qty.
|
||||
lv_need = lv_min_qty.
|
||||
ENDIF.
|
||||
|
||||
IF lv_need <= 0.
|
||||
rv_qty = 0.
|
||||
RETURN.
|
||||
ENDIF.
|
||||
|
||||
rv_qty = ceil( lv_need / iv_pack_size ) * iv_pack_size.
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
24
tasks_gen/eval/G0011/reference/repl_calc.func.abap
Normal file
24
tasks_gen/eval/G0011/reference/repl_calc.func.abap
Normal file
@@ -0,0 +1,24 @@
|
||||
FUNCTION {{p}}repl_calc
|
||||
IMPORTING
|
||||
iv_stock TYPE decfloat34
|
||||
iv_reorder_point TYPE decfloat34
|
||||
iv_target_stock TYPE decfloat34
|
||||
iv_pack_size TYPE decfloat34
|
||||
iv_min_qty TYPE decfloat34
|
||||
EXPORTING
|
||||
ev_qty TYPE decfloat34
|
||||
EXCEPTIONS
|
||||
invalid_pack_size.
|
||||
|
||||
IF iv_pack_size <= 0.
|
||||
RAISE invalid_pack_size.
|
||||
ENDIF.
|
||||
|
||||
ev_qty = NEW {{p}}cl_repl( )->calculate(
|
||||
iv_stock = iv_stock
|
||||
iv_reorder_point = iv_reorder_point
|
||||
iv_target_stock = iv_target_stock
|
||||
iv_pack_size = iv_pack_size
|
||||
iv_min_qty = iv_min_qty ).
|
||||
|
||||
ENDFUNCTION.
|
||||
100
tasks_gen/eval/G0011/reference/t14_test.clas.abap
Normal file
100
tasks_gen/eval/G0011/reference/t14_test.clas.abap
Normal file
@@ -0,0 +1,100 @@
|
||||
CLASS {{p}}t14_test DEFINITION PUBLIC FINAL CREATE PUBLIC
|
||||
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
PRIVATE SECTION.
|
||||
METHODS qty_for
|
||||
IMPORTING
|
||||
iv_stock TYPE decfloat34
|
||||
iv_reorder_point TYPE decfloat34
|
||||
iv_target_stock TYPE decfloat34
|
||||
iv_pack_size TYPE decfloat34
|
||||
iv_min_qty TYPE decfloat34 DEFAULT 0
|
||||
RETURNING
|
||||
VALUE(rv_qty) TYPE decfloat34.
|
||||
METHODS replenish_to_target FOR TESTING.
|
||||
METHODS no_replenishment FOR TESTING.
|
||||
METHODS minimum_quantity FOR TESTING.
|
||||
METHODS pack_rounding FOR TESTING.
|
||||
METHODS invalid_pack_size FOR TESTING.
|
||||
METHODS class_direct_call FOR TESTING.
|
||||
METHODS class_no_replenishment FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}t14_test IMPLEMENTATION.
|
||||
METHOD qty_for.
|
||||
CALL FUNCTION '{{P}}REPL_CALC'
|
||||
EXPORTING
|
||||
iv_stock = iv_stock
|
||||
iv_reorder_point = iv_reorder_point
|
||||
iv_target_stock = iv_target_stock
|
||||
iv_pack_size = iv_pack_size
|
||||
iv_min_qty = iv_min_qty
|
||||
IMPORTING
|
||||
ev_qty = rv_qty
|
||||
EXCEPTIONS
|
||||
invalid_pack_size = 1
|
||||
OTHERS = 2.
|
||||
cl_abap_unit_assert=>assert_subrc( exp = 0 ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD replenish_to_target.
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = CONV decfloat34( 48 )
|
||||
act = qty_for( iv_stock = 10 iv_reorder_point = 20 iv_target_stock = 50 iv_pack_size = 12 ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD no_replenishment.
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = CONV decfloat34( 0 )
|
||||
act = qty_for( iv_stock = 30 iv_reorder_point = 20 iv_target_stock = 50 iv_pack_size = 12 ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD minimum_quantity.
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = CONV decfloat34( 10 )
|
||||
act = qty_for( iv_stock = 10 iv_reorder_point = 20 iv_target_stock = 12 iv_pack_size = 5 iv_min_qty = 7 ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD pack_rounding.
|
||||
cl_abap_unit_assert=>assert_equals(
|
||||
exp = CONV decfloat34( 30 )
|
||||
act = qty_for( iv_stock = 0 iv_reorder_point = 10 iv_target_stock = 25 iv_pack_size = 6 ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD invalid_pack_size.
|
||||
DATA lv_qty TYPE decfloat34.
|
||||
CALL FUNCTION '{{P}}REPL_CALC'
|
||||
EXPORTING
|
||||
iv_stock = 0
|
||||
iv_reorder_point = 10
|
||||
iv_target_stock = 20
|
||||
iv_pack_size = 0
|
||||
iv_min_qty = 0
|
||||
IMPORTING
|
||||
ev_qty = lv_qty
|
||||
EXCEPTIONS
|
||||
invalid_pack_size = 1
|
||||
OTHERS = 2.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD class_direct_call.
|
||||
DATA(lv_qty) = NEW {{p}}cl_repl( )->calculate(
|
||||
iv_stock = 0
|
||||
iv_reorder_point = 10
|
||||
iv_target_stock = 24
|
||||
iv_pack_size = 12
|
||||
iv_min_qty = 0 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 24 ) act = lv_qty ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD class_no_replenishment.
|
||||
DATA(lv_qty) = NEW {{p}}cl_repl( )->calculate(
|
||||
iv_stock = 40
|
||||
iv_reorder_point = 20
|
||||
iv_target_stock = 100
|
||||
iv_pack_size = 10
|
||||
iv_min_qty = 0 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_qty ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
9
tasks_gen/eval/G0011/seed/repl_sample.func.abap
Normal file
9
tasks_gen/eval/G0011/seed/repl_sample.func.abap
Normal file
@@ -0,0 +1,9 @@
|
||||
FUNCTION {{p}}repl_sample
|
||||
IMPORTING
|
||||
iv_bin TYPE string
|
||||
EXPORTING
|
||||
ev_info TYPE string.
|
||||
|
||||
ev_info = |Storage bin { iv_bin }|.
|
||||
|
||||
ENDFUNCTION.
|
||||
66
tasks_gen/eval/G0011/spec.md
Normal file
66
tasks_gen/eval/G0011/spec.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# 1. Goal
|
||||
A warehouse system needs the quantity for a replenishment order of a storage
|
||||
bin. The calling program calls the function module {{P}}REPL_CALC. The
|
||||
calculation logic must be in the class {{P}}CL_REPL, because other programs
|
||||
also use this logic.
|
||||
|
||||
# 2. Open questions
|
||||
None.
|
||||
|
||||
# 3. Context
|
||||
- Function group {{P}}FG_REPL exists in package $TMP.
|
||||
- Function module {{P}}REPL_SAMPLE exists in this function group. It shows bin
|
||||
information. Do not change it.
|
||||
- The class {{P}}CL_REPL does not exist. Create it first.
|
||||
|
||||
# 4. Contract
|
||||
|
||||
## 4.1 Class {{P}}CL_REPL
|
||||
- Package: $TMP.
|
||||
- The class is instantiable without parameters.
|
||||
- Public instance method calculate:
|
||||
- importing iv_stock, type decfloat34
|
||||
- importing iv_reorder_point, type decfloat34
|
||||
- importing iv_target_stock, type decfloat34
|
||||
- importing iv_pack_size, type decfloat34
|
||||
- importing iv_min_qty, type decfloat34
|
||||
- returning rv_qty, type decfloat34
|
||||
- The method has no exception. Call it only with a positive pack size.
|
||||
|
||||
## 4.2 Function module {{P}}REPL_CALC
|
||||
- Function group: {{P}}FG_REPL.
|
||||
- Importing parameters:
|
||||
- iv_stock, type decfloat34
|
||||
- iv_reorder_point, type decfloat34
|
||||
- iv_target_stock, type decfloat34
|
||||
- iv_pack_size, type decfloat34
|
||||
- iv_min_qty, type decfloat34
|
||||
- Exporting parameters:
|
||||
- ev_qty, type decfloat34
|
||||
- Exceptions: invalid_pack_size.
|
||||
- The function module {{P}}REPL_CALC must call the method calculate of the class
|
||||
{{P}}CL_REPL. Create and activate the class before you activate the function
|
||||
module.
|
||||
|
||||
# 5. Business rules
|
||||
1. If the pack size is not positive, the function module raises the exception
|
||||
INVALID_PACK_SIZE. The class method is not called in this case.
|
||||
2. If the stock is greater than or equal to the reorder point, the
|
||||
replenishment quantity is 0.
|
||||
3. Otherwise, the requirement is the target stock minus the stock.
|
||||
4. If the requirement is less than the minimum quantity, the requirement is the
|
||||
minimum quantity. A minimum quantity below 0 counts as 0.
|
||||
5. If the requirement is not positive, the replenishment quantity is 0.
|
||||
6. Otherwise, round the requirement up to the next full multiple of the pack
|
||||
size. The replenishment quantity is this multiple multiplied by the pack
|
||||
size.
|
||||
|
||||
# 6. Constraints
|
||||
- Release target: 7.58.
|
||||
- Use Clean ABAP. Keep each method below 40 statements.
|
||||
- Out of scope: do not change {{P}}REPL_SAMPLE. Do not create database tables.
|
||||
|
||||
# 7. Acceptance
|
||||
- The function module and the class are active and have no syntax error.
|
||||
- The hidden tests pass.
|
||||
- Write ABAP Unit tests for the function module in a global test class.
|
||||
99
tasks_gen/eval/G0011/task.json
Normal file
99
tasks_gen/eval/G0011/task.json
Normal file
@@ -0,0 +1,99 @@
|
||||
{
|
||||
"id": "G0011",
|
||||
"category": "A",
|
||||
"object_type": "FUNC",
|
||||
"difficulty": 2,
|
||||
"release_target": "v758",
|
||||
"expected_outcome": "implement",
|
||||
"budget": {
|
||||
"max_tool_calls": 40,
|
||||
"max_activations": 10
|
||||
},
|
||||
"seed": [
|
||||
{
|
||||
"type": "FUGR",
|
||||
"name": "{{P}}FG_REPL",
|
||||
"description": "Replenishment function group"
|
||||
},
|
||||
{
|
||||
"type": "FUNC",
|
||||
"name": "{{P}}REPL_SAMPLE",
|
||||
"functionGroup": "{{P}}FG_REPL",
|
||||
"file": "seed/repl_sample.func.abap",
|
||||
"description": "Sample bin information module"
|
||||
}
|
||||
],
|
||||
"contract": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}CL_REPL"
|
||||
},
|
||||
{
|
||||
"type": "FUNC",
|
||||
"name": "{{P}}REPL_CALC",
|
||||
"functionGroup": "{{P}}FG_REPL",
|
||||
"params": [
|
||||
{
|
||||
"name": "iv_stock",
|
||||
"type": "decfloat34"
|
||||
},
|
||||
{
|
||||
"name": "iv_reorder_point",
|
||||
"type": "decfloat34"
|
||||
},
|
||||
{
|
||||
"name": "iv_target_stock",
|
||||
"type": "decfloat34"
|
||||
},
|
||||
{
|
||||
"name": "iv_pack_size",
|
||||
"type": "decfloat34"
|
||||
},
|
||||
{
|
||||
"name": "iv_min_qty",
|
||||
"type": "decfloat34"
|
||||
},
|
||||
{
|
||||
"name": "ev_qty",
|
||||
"type": "decfloat34"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"out_of_scope": [
|
||||
"{{P}}REPL_SAMPLE"
|
||||
],
|
||||
"hidden_tests": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}T14_HIDDEN",
|
||||
"file": "hidden/t14_hidden.clas.abap",
|
||||
"description": "T14 hidden tests"
|
||||
}
|
||||
],
|
||||
"reference": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}CL_REPL",
|
||||
"file": "reference/cl_repl.clas.abap",
|
||||
"description": "Replenishment quantity calculator"
|
||||
},
|
||||
{
|
||||
"type": "FUNC",
|
||||
"name": "{{P}}REPL_CALC",
|
||||
"functionGroup": "{{P}}FG_REPL",
|
||||
"file": "reference/repl_calc.func.abap",
|
||||
"description": "Replenishment function module"
|
||||
},
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}T14_TEST",
|
||||
"file": "reference/t14_test.clas.abap",
|
||||
"description": "T14 own tests"
|
||||
}
|
||||
],
|
||||
"craft_checks": [
|
||||
"method_length",
|
||||
"clean_abap"
|
||||
]
|
||||
}
|
||||
14
tasks_gen/eval/G0012/generation.json
Normal file
14
tasks_gen/eval/G0012/generation.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"id": "G0012",
|
||||
"pool": "eval",
|
||||
"object_type": "FUNC",
|
||||
"category": "F",
|
||||
"attempts": [
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 100.0,
|
||||
"null": 0
|
||||
}
|
||||
],
|
||||
"accepted": true
|
||||
}
|
||||
120
tasks_gen/eval/G0012/hidden/t14_hidden.clas.abap
Normal file
120
tasks_gen/eval/G0012/hidden/t14_hidden.clas.abap
Normal file
@@ -0,0 +1,120 @@
|
||||
CLASS {{p}}t14_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
|
||||
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
PRIVATE SECTION.
|
||||
METHODS call
|
||||
IMPORTING iv_quantity TYPE decfloat34
|
||||
iv_scrap TYPE decfloat34
|
||||
iv_rework TYPE decfloat34 DEFAULT 0
|
||||
EXPORTING ev_pct TYPE decfloat34
|
||||
ev_severity TYPE string
|
||||
ev_rework TYPE decfloat34
|
||||
ev_subrc TYPE sysubrc.
|
||||
METHODS percent_rounds_half_up FOR TESTING.
|
||||
METHODS zero_scrap_is_ok FOR TESTING.
|
||||
METHODS low_upper_boundary FOR TESTING.
|
||||
METHODS high_lower_boundary FOR TESTING.
|
||||
METHODS high_upper_boundary FOR TESTING.
|
||||
METHODS critical_above_twenty FOR TESTING.
|
||||
METHODS rework_unchanged_for_low FOR TESTING.
|
||||
METHODS rework_set_for_high FOR TESTING.
|
||||
METHODS quantity_zero_raises FOR TESTING.
|
||||
METHODS scrap_negative_raises FOR TESTING.
|
||||
METHODS scrap_above_quantity_raises FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}t14_hidden IMPLEMENTATION.
|
||||
METHOD call.
|
||||
ev_pct = 0.
|
||||
ev_severity = ``.
|
||||
ev_rework = iv_rework.
|
||||
CALL FUNCTION '{{P}}PRD_SCRAP'
|
||||
EXPORTING
|
||||
iv_quantity = iv_quantity
|
||||
iv_scrap = iv_scrap
|
||||
IMPORTING
|
||||
ev_scrap_pct = ev_pct
|
||||
ev_severity = ev_severity
|
||||
CHANGING
|
||||
cv_rework_qty = ev_rework
|
||||
EXCEPTIONS
|
||||
invalid_quantity = 1
|
||||
scrap_too_high = 2
|
||||
OTHERS = 3.
|
||||
ev_subrc = sy-subrc.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD percent_rounds_half_up.
|
||||
call( EXPORTING iv_quantity = 800 iv_scrap = 7
|
||||
IMPORTING ev_pct = DATA(lv_pct) ev_severity = DATA(lv_sev) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '0.9' ) act = lv_pct ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `LOW` act = lv_sev ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD zero_scrap_is_ok.
|
||||
call( EXPORTING iv_quantity = 100 iv_scrap = 0
|
||||
IMPORTING ev_pct = DATA(lv_pct) ev_severity = DATA(lv_sev) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = lv_pct ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `OK` act = lv_sev ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD low_upper_boundary.
|
||||
call( EXPORTING iv_quantity = 100 iv_scrap = 5
|
||||
IMPORTING ev_pct = DATA(lv_pct) ev_severity = DATA(lv_sev) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 5 ) act = lv_pct ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `LOW` act = lv_sev ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD high_lower_boundary.
|
||||
call( EXPORTING iv_quantity = 100 iv_scrap = '5.1'
|
||||
IMPORTING ev_pct = DATA(lv_pct) ev_severity = DATA(lv_sev) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '5.1' ) act = lv_pct ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `HIGH` act = lv_sev ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD high_upper_boundary.
|
||||
call( EXPORTING iv_quantity = 100 iv_scrap = 20
|
||||
IMPORTING ev_pct = DATA(lv_pct) ev_severity = DATA(lv_sev) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 20 ) act = lv_pct ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `HIGH` act = lv_sev ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD critical_above_twenty.
|
||||
call( EXPORTING iv_quantity = 100 iv_scrap = '20.1'
|
||||
IMPORTING ev_pct = DATA(lv_pct) ev_severity = DATA(lv_sev) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '20.1' ) act = lv_pct ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `CRITICAL` act = lv_sev ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD rework_unchanged_for_low.
|
||||
call( EXPORTING iv_quantity = 200 iv_scrap = 4 iv_rework = 99
|
||||
IMPORTING ev_rework = DATA(lv_rework) ev_severity = DATA(lv_sev) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `LOW` act = lv_sev ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 99 ) act = lv_rework ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD rework_set_for_high.
|
||||
call( EXPORTING iv_quantity = 100 iv_scrap = 10 iv_rework = 99
|
||||
IMPORTING ev_rework = DATA(lv_rework) ev_severity = DATA(lv_sev) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `HIGH` act = lv_sev ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 10 ) act = lv_rework ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD quantity_zero_raises.
|
||||
call( EXPORTING iv_quantity = 0 iv_scrap = 0
|
||||
IMPORTING ev_subrc = DATA(lv_rc) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD scrap_negative_raises.
|
||||
call( EXPORTING iv_quantity = 100 iv_scrap = -1
|
||||
IMPORTING ev_subrc = DATA(lv_rc) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_rc ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD scrap_above_quantity_raises.
|
||||
call( EXPORTING iv_quantity = 100 iv_scrap = 101
|
||||
IMPORTING ev_subrc = DATA(lv_rc) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_rc ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
41
tasks_gen/eval/G0012/reference/prd_scrap.func.abap
Normal file
41
tasks_gen/eval/G0012/reference/prd_scrap.func.abap
Normal file
@@ -0,0 +1,41 @@
|
||||
FUNCTION {{p}}prd_scrap
|
||||
IMPORTING
|
||||
iv_quantity TYPE decfloat34
|
||||
iv_scrap TYPE decfloat34
|
||||
EXPORTING
|
||||
ev_scrap_pct TYPE decfloat34
|
||||
ev_severity TYPE string
|
||||
CHANGING
|
||||
cv_rework_qty TYPE decfloat34
|
||||
EXCEPTIONS
|
||||
invalid_quantity
|
||||
scrap_too_high.
|
||||
|
||||
IF iv_quantity <= 0 OR iv_scrap < 0.
|
||||
RAISE invalid_quantity.
|
||||
ENDIF.
|
||||
|
||||
IF iv_scrap > iv_quantity.
|
||||
RAISE scrap_too_high.
|
||||
ENDIF.
|
||||
|
||||
ev_scrap_pct = round( val = iv_scrap / iv_quantity * 100
|
||||
dec = 1
|
||||
mode = cl_abap_math=>round_half_up ).
|
||||
|
||||
IF ev_scrap_pct = 0.
|
||||
ev_severity = 'OK'.
|
||||
ELSEIF ev_scrap_pct <= 5.
|
||||
ev_severity = 'LOW'.
|
||||
ELSEIF ev_scrap_pct <= 20.
|
||||
ev_severity = 'HIGH'.
|
||||
ELSE.
|
||||
ev_severity = 'CRITICAL'.
|
||||
ENDIF.
|
||||
|
||||
CASE ev_severity.
|
||||
WHEN 'HIGH' OR 'CRITICAL'.
|
||||
cv_rework_qty = iv_scrap.
|
||||
ENDCASE.
|
||||
|
||||
ENDFUNCTION.
|
||||
90
tasks_gen/eval/G0012/reference/t14_test.clas.abap
Normal file
90
tasks_gen/eval/G0012/reference/t14_test.clas.abap
Normal file
@@ -0,0 +1,90 @@
|
||||
CLASS {{p}}t14_test DEFINITION PUBLIC FINAL CREATE PUBLIC
|
||||
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
PRIVATE SECTION.
|
||||
METHODS result_for
|
||||
IMPORTING iv_quantity TYPE decfloat34
|
||||
iv_scrap TYPE decfloat34
|
||||
iv_rework TYPE decfloat34 DEFAULT 0
|
||||
EXPORTING ev_pct TYPE decfloat34
|
||||
ev_severity TYPE string
|
||||
ev_rework TYPE decfloat34.
|
||||
METHODS percent_and_severity FOR TESTING.
|
||||
METHODS severity_bands FOR TESTING.
|
||||
METHODS rework_quantity FOR TESTING.
|
||||
METHODS invalid_inputs FOR TESTING.
|
||||
METHODS scrap_too_high FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}t14_test IMPLEMENTATION.
|
||||
METHOD result_for.
|
||||
ev_rework = iv_rework.
|
||||
CALL FUNCTION '{{P}}PRD_SCRAP'
|
||||
EXPORTING
|
||||
iv_quantity = iv_quantity
|
||||
iv_scrap = iv_scrap
|
||||
IMPORTING
|
||||
ev_scrap_pct = ev_pct
|
||||
ev_severity = ev_severity
|
||||
CHANGING
|
||||
cv_rework_qty = ev_rework
|
||||
EXCEPTIONS
|
||||
invalid_quantity = 1
|
||||
scrap_too_high = 2
|
||||
OTHERS = 3.
|
||||
cl_abap_unit_assert=>assert_subrc( exp = 0 ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD percent_and_severity.
|
||||
result_for( EXPORTING iv_quantity = 800 iv_scrap = 7
|
||||
IMPORTING ev_pct = DATA(lv_pct) ev_severity = DATA(lv_sev) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '0.9' ) act = lv_pct ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `LOW` act = lv_sev ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD severity_bands.
|
||||
result_for( EXPORTING iv_quantity = 100 iv_scrap = 0
|
||||
IMPORTING ev_severity = DATA(lv_ok) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `OK` act = lv_ok ).
|
||||
result_for( EXPORTING iv_quantity = 100 iv_scrap = 5
|
||||
IMPORTING ev_severity = DATA(lv_low) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `LOW` act = lv_low ).
|
||||
result_for( EXPORTING iv_quantity = 100 iv_scrap = 20
|
||||
IMPORTING ev_severity = DATA(lv_high) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `HIGH` act = lv_high ).
|
||||
result_for( EXPORTING iv_quantity = 100 iv_scrap = 21
|
||||
IMPORTING ev_severity = DATA(lv_crit) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `CRITICAL` act = lv_crit ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD rework_quantity.
|
||||
result_for( EXPORTING iv_quantity = 100 iv_scrap = 4 iv_rework = 99
|
||||
IMPORTING ev_rework = DATA(lv_keep) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 99 ) act = lv_keep ).
|
||||
result_for( EXPORTING iv_quantity = 100 iv_scrap = 10
|
||||
IMPORTING ev_rework = DATA(lv_set) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 10 ) act = lv_set ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD invalid_inputs.
|
||||
DATA lv_pct TYPE decfloat34.
|
||||
DATA lv_rework TYPE decfloat34.
|
||||
CALL FUNCTION '{{P}}PRD_SCRAP'
|
||||
EXPORTING iv_quantity = 0 iv_scrap = 0
|
||||
IMPORTING ev_scrap_pct = lv_pct
|
||||
CHANGING cv_rework_qty = lv_rework
|
||||
EXCEPTIONS invalid_quantity = 1 OTHERS = 2.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1 act = sy-subrc ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD scrap_too_high.
|
||||
DATA lv_pct TYPE decfloat34.
|
||||
DATA lv_rework TYPE decfloat34.
|
||||
CALL FUNCTION '{{P}}PRD_SCRAP'
|
||||
EXPORTING iv_quantity = 100 iv_scrap = 101
|
||||
IMPORTING ev_scrap_pct = lv_pct
|
||||
CHANGING cv_rework_qty = lv_rework
|
||||
EXCEPTIONS scrap_too_high = 2 OTHERS = 1.
|
||||
cl_abap_unit_assert=>assert_equals( exp = 2 act = sy-subrc ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
18
tasks_gen/eval/G0012/seed/prd_sample.func.abap
Normal file
18
tasks_gen/eval/G0012/seed/prd_sample.func.abap
Normal file
@@ -0,0 +1,18 @@
|
||||
FUNCTION {{p}}prd_sample
|
||||
IMPORTING
|
||||
iv_quantity TYPE decfloat34
|
||||
iv_scrap TYPE decfloat34
|
||||
EXPORTING
|
||||
ev_scrap_pct TYPE decfloat34
|
||||
ev_severity TYPE string
|
||||
CHANGING
|
||||
cv_rework_qty TYPE decfloat34
|
||||
EXCEPTIONS
|
||||
invalid_quantity
|
||||
scrap_too_high.
|
||||
|
||||
" Sample confirmation module. All confirmation modules use this interface.
|
||||
ev_scrap_pct = 0.
|
||||
ev_severity = 'OK'.
|
||||
|
||||
ENDFUNCTION.
|
||||
46
tasks_gen/eval/G0012/spec.md
Normal file
46
tasks_gen/eval/G0012/spec.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# 1. Goal
|
||||
Evaluate a production confirmation. Calculate the scrap percentage and the
|
||||
severity of the scrap. The confirmation program calls one module for each
|
||||
confirmation. The new module must have the same interface as the sample module,
|
||||
because the confirmation program calls all modules in the same way.
|
||||
|
||||
# 2. Open questions
|
||||
None.
|
||||
|
||||
# 3. Context
|
||||
- Function group {{P}}FG_PROD exists in package $TMP.
|
||||
- Function module {{P}}PRD_SAMPLE exists in this group. It is the sample
|
||||
confirmation module. Its interface is the interface that all confirmation
|
||||
modules must use.
|
||||
|
||||
# 4. Contract
|
||||
- Create the function module {{P}}PRD_SCRAP in function group {{P}}FG_PROD.
|
||||
- Use exactly the interface of {{P}}PRD_SAMPLE: the same parameter names, kinds,
|
||||
types, and exceptions.
|
||||
|
||||
# 5. Business rules
|
||||
1. If the confirmed quantity is less than or equal to 0, raise the exception
|
||||
INVALID_QUANTITY.
|
||||
2. If the scrap quantity is less than 0, raise the exception INVALID_QUANTITY.
|
||||
3. If the scrap quantity is greater than the confirmed quantity, raise the
|
||||
exception SCRAP_TOO_HIGH.
|
||||
4. The scrap percentage is the scrap quantity divided by the confirmed quantity,
|
||||
multiplied by 100. Round the result half up to 1 decimal. Write the result to
|
||||
the scrap percentage parameter.
|
||||
5. Set the severity from the rounded scrap percentage of rule 4:
|
||||
- equal to 0: `OK`
|
||||
- greater than 0 and less than or equal to 5: `LOW`
|
||||
- greater than 5 and less than or equal to 20: `HIGH`
|
||||
- greater than 20: `CRITICAL`
|
||||
6. If the severity is `HIGH` or `CRITICAL`, set the rework quantity to the scrap
|
||||
quantity. In all other cases, do not change the rework quantity.
|
||||
|
||||
# 6. Constraints
|
||||
- Release target: 7.58.
|
||||
- Coding standards: Clean ABAP. Keep every method below 40 statements.
|
||||
- Out of scope: do not change {{P}}PRD_SAMPLE.
|
||||
|
||||
# 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.
|
||||
84
tasks_gen/eval/G0012/task.json
Normal file
84
tasks_gen/eval/G0012/task.json
Normal file
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"id": "G0012",
|
||||
"category": "F",
|
||||
"object_type": "FUNC",
|
||||
"difficulty": 2,
|
||||
"release_target": "v758",
|
||||
"expected_outcome": "implement",
|
||||
"budget": {
|
||||
"max_tool_calls": 40,
|
||||
"max_activations": 10
|
||||
},
|
||||
"seed": [
|
||||
{
|
||||
"type": "FUGR",
|
||||
"name": "{{P}}FG_PROD",
|
||||
"description": "Production confirmation modules"
|
||||
},
|
||||
{
|
||||
"type": "FUNC",
|
||||
"name": "{{P}}PRD_SAMPLE",
|
||||
"functionGroup": "{{P}}FG_PROD",
|
||||
"file": "seed/prd_sample.func.abap",
|
||||
"description": "Sample confirmation module"
|
||||
}
|
||||
],
|
||||
"contract": [
|
||||
{
|
||||
"type": "FUNC",
|
||||
"name": "{{P}}PRD_SCRAP",
|
||||
"functionGroup": "{{P}}FG_PROD",
|
||||
"params": [
|
||||
{
|
||||
"name": "iv_quantity",
|
||||
"type": "decfloat34"
|
||||
},
|
||||
{
|
||||
"name": "iv_scrap",
|
||||
"type": "decfloat34"
|
||||
},
|
||||
{
|
||||
"name": "ev_scrap_pct",
|
||||
"type": "decfloat34"
|
||||
},
|
||||
{
|
||||
"name": "ev_severity",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "cv_rework_qty",
|
||||
"type": "decfloat34"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"out_of_scope": [
|
||||
"{{P}}PRD_SAMPLE"
|
||||
],
|
||||
"hidden_tests": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}T14_HIDDEN",
|
||||
"file": "hidden/t14_hidden.clas.abap",
|
||||
"description": "T14 hidden tests"
|
||||
}
|
||||
],
|
||||
"reference": [
|
||||
{
|
||||
"type": "FUNC",
|
||||
"name": "{{P}}PRD_SCRAP",
|
||||
"functionGroup": "{{P}}FG_PROD",
|
||||
"file": "reference/prd_scrap.func.abap",
|
||||
"description": "Scrap evaluation module"
|
||||
},
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}T14_TEST",
|
||||
"file": "reference/t14_test.clas.abap",
|
||||
"description": "T14 own tests"
|
||||
}
|
||||
],
|
||||
"craft_checks": [
|
||||
"method_length"
|
||||
]
|
||||
}
|
||||
26
tasks_gen/eval/G0013/generation.json
Normal file
26
tasks_gen/eval/G0013/generation.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "G0013",
|
||||
"pool": "eval",
|
||||
"object_type": "FUNC",
|
||||
"category": "I",
|
||||
"attempts": [
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 70.5,
|
||||
"null": 0
|
||||
},
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 100.0,
|
||||
"null": 0
|
||||
},
|
||||
{
|
||||
"stage": "manual_fix",
|
||||
"by": "Claude review 2026-10-03",
|
||||
"change": "Removed own test class REPL_TEST from the contract and from the spec acceptance (own tests are the model's choice). REPL_TEST stays in the reference as own tests.",
|
||||
"oracle": 100.0,
|
||||
"null": 0
|
||||
}
|
||||
],
|
||||
"accepted": true
|
||||
}
|
||||
108
tasks_gen/eval/G0013/hidden/t21_hidden.clas.abap
Normal file
108
tasks_gen/eval/G0013/hidden/t21_hidden.clas.abap
Normal file
@@ -0,0 +1,108 @@
|
||||
CLASS {{p}}t21_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
|
||||
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
PRIVATE SECTION.
|
||||
TYPES: BEGIN OF ty_result,
|
||||
qty TYPE decfloat34,
|
||||
action TYPE string,
|
||||
subrc TYPE sysubrc,
|
||||
END OF ty_result.
|
||||
METHODS call
|
||||
IMPORTING iv_stock TYPE decfloat34
|
||||
iv_safety TYPE decfloat34
|
||||
iv_lot_size TYPE decfloat34
|
||||
iv_min_order TYPE decfloat34
|
||||
RETURNING VALUE(rs_result) TYPE ty_result.
|
||||
METHODS exact_multiple_lot FOR TESTING.
|
||||
METHODS exact_multiple_single_lot FOR TESTING.
|
||||
METHODS exact_multiple_with_minimum FOR TESTING.
|
||||
METHODS partial_lot_rounds_up FOR TESTING.
|
||||
METHODS stock_above_safety FOR TESTING.
|
||||
METHODS stock_equals_safety FOR TESTING.
|
||||
METHODS no_lot_size_no_rounding FOR TESTING.
|
||||
METHODS minimum_order_applies FOR TESTING.
|
||||
METHODS minimum_order_keeps_quantity FOR TESTING.
|
||||
METHODS negative_lot_size FOR TESTING.
|
||||
METHODS negative_min_order FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}t21_hidden IMPLEMENTATION.
|
||||
METHOD call.
|
||||
CALL FUNCTION '{{P}}REPL_QTY'
|
||||
EXPORTING
|
||||
iv_stock = iv_stock
|
||||
iv_safety = iv_safety
|
||||
iv_lot_size = iv_lot_size
|
||||
iv_min_order = iv_min_order
|
||||
IMPORTING
|
||||
ev_order_qty = rs_result-qty
|
||||
ev_action = rs_result-action
|
||||
EXCEPTIONS
|
||||
invalid_input = 1
|
||||
OTHERS = 2.
|
||||
rs_result-subrc = sy-subrc.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD exact_multiple_lot.
|
||||
DATA(ls_result) = call( iv_stock = 0 iv_safety = 100 iv_lot_size = 50 iv_min_order = 0 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_result-subrc ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 100 ) act = ls_result-qty ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `ORDER` act = ls_result-action ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD exact_multiple_single_lot.
|
||||
DATA(ls_result) = call( iv_stock = 0 iv_safety = 50 iv_lot_size = 50 iv_min_order = 0 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 0 act = ls_result-subrc ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 50 ) act = ls_result-qty ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD exact_multiple_with_minimum.
|
||||
DATA(ls_result) = call( iv_stock = 0 iv_safety = 100 iv_lot_size = 50 iv_min_order = 120 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 120 ) act = ls_result-qty ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `ORDER` act = ls_result-action ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD partial_lot_rounds_up.
|
||||
DATA(ls_result) = call( iv_stock = 0 iv_safety = 110 iv_lot_size = 50 iv_min_order = 0 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 150 ) act = ls_result-qty ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `ORDER` act = ls_result-action ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD stock_above_safety.
|
||||
DATA(ls_result) = call( iv_stock = 200 iv_safety = 100 iv_lot_size = 50 iv_min_order = 50 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = ls_result-qty ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `NONE` act = ls_result-action ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD stock_equals_safety.
|
||||
DATA(ls_result) = call( iv_stock = 100 iv_safety = 100 iv_lot_size = 50 iv_min_order = 0 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = ls_result-qty ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `NONE` act = ls_result-action ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD no_lot_size_no_rounding.
|
||||
DATA(ls_result) = call( iv_stock = 0 iv_safety = '37.5' iv_lot_size = 0 iv_min_order = 0 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( '37.5' ) act = ls_result-qty ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `ORDER` act = ls_result-action ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD minimum_order_applies.
|
||||
DATA(ls_result) = call( iv_stock = 0 iv_safety = 10 iv_lot_size = 0 iv_min_order = 25 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 25 ) act = ls_result-qty ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD minimum_order_keeps_quantity.
|
||||
DATA(ls_result) = call( iv_stock = 0 iv_safety = 100 iv_lot_size = 0 iv_min_order = 25 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 100 ) act = ls_result-qty ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD negative_lot_size.
|
||||
DATA(ls_result) = call( iv_stock = 0 iv_safety = 100 iv_lot_size = -1 iv_min_order = 0 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_result-subrc ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD negative_min_order.
|
||||
DATA(ls_result) = call( iv_stock = 0 iv_safety = 100 iv_lot_size = 0 iv_min_order = -1 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_result-subrc ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
38
tasks_gen/eval/G0013/reference/repl_qty.func.abap
Normal file
38
tasks_gen/eval/G0013/reference/repl_qty.func.abap
Normal file
@@ -0,0 +1,38 @@
|
||||
FUNCTION {{p}}repl_qty
|
||||
IMPORTING
|
||||
iv_stock TYPE decfloat34
|
||||
iv_safety TYPE decfloat34
|
||||
iv_lot_size TYPE decfloat34
|
||||
iv_min_order TYPE decfloat34
|
||||
EXPORTING
|
||||
ev_order_qty TYPE decfloat34
|
||||
ev_action TYPE string
|
||||
EXCEPTIONS
|
||||
invalid_input.
|
||||
|
||||
IF iv_lot_size < 0 OR iv_min_order < 0.
|
||||
RAISE invalid_input.
|
||||
ENDIF.
|
||||
|
||||
DATA lv_required TYPE decfloat34.
|
||||
lv_required = iv_safety - iv_stock.
|
||||
|
||||
IF lv_required <= 0.
|
||||
ev_order_qty = 0.
|
||||
ev_action = 'NONE'.
|
||||
RETURN.
|
||||
ENDIF.
|
||||
|
||||
IF iv_lot_size > 0.
|
||||
ev_order_qty = ceil( lv_required / iv_lot_size ) * iv_lot_size.
|
||||
ELSE.
|
||||
ev_order_qty = lv_required.
|
||||
ENDIF.
|
||||
|
||||
IF ev_order_qty < iv_min_order.
|
||||
ev_order_qty = iv_min_order.
|
||||
ENDIF.
|
||||
|
||||
ev_action = 'ORDER'.
|
||||
|
||||
ENDFUNCTION.
|
||||
65
tasks_gen/eval/G0013/reference/t21_test.clas.abap
Normal file
65
tasks_gen/eval/G0013/reference/t21_test.clas.abap
Normal file
@@ -0,0 +1,65 @@
|
||||
CLASS {{p}}repl_test DEFINITION PUBLIC FINAL CREATE PUBLIC
|
||||
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
PRIVATE SECTION.
|
||||
TYPES: BEGIN OF ty_result,
|
||||
qty TYPE decfloat34,
|
||||
action TYPE string,
|
||||
subrc TYPE sysubrc,
|
||||
END OF ty_result.
|
||||
METHODS call
|
||||
IMPORTING iv_stock TYPE decfloat34
|
||||
iv_safety TYPE decfloat34
|
||||
iv_lot_size TYPE decfloat34
|
||||
iv_min_order TYPE decfloat34
|
||||
RETURNING VALUE(rs_result) TYPE ty_result.
|
||||
METHODS exact_multiple FOR TESTING.
|
||||
METHODS partial_lot FOR TESTING.
|
||||
METHODS no_order FOR TESTING.
|
||||
METHODS minimum_order FOR TESTING.
|
||||
METHODS invalid_input FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}repl_test IMPLEMENTATION.
|
||||
METHOD call.
|
||||
CALL FUNCTION '{{P}}REPL_QTY'
|
||||
EXPORTING
|
||||
iv_stock = iv_stock
|
||||
iv_safety = iv_safety
|
||||
iv_lot_size = iv_lot_size
|
||||
iv_min_order = iv_min_order
|
||||
IMPORTING
|
||||
ev_order_qty = rs_result-qty
|
||||
ev_action = rs_result-action
|
||||
EXCEPTIONS
|
||||
invalid_input = 1
|
||||
OTHERS = 2.
|
||||
rs_result-subrc = sy-subrc.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD exact_multiple.
|
||||
DATA(ls_result) = call( iv_stock = 0 iv_safety = 100 iv_lot_size = 50 iv_min_order = 0 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 100 ) act = ls_result-qty ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD partial_lot.
|
||||
DATA(ls_result) = call( iv_stock = 0 iv_safety = 101 iv_lot_size = 50 iv_min_order = 0 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 150 ) act = ls_result-qty ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD no_order.
|
||||
DATA(ls_result) = call( iv_stock = 300 iv_safety = 100 iv_lot_size = 50 iv_min_order = 0 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 0 ) act = ls_result-qty ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `NONE` act = ls_result-action ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD minimum_order.
|
||||
DATA(ls_result) = call( iv_stock = 0 iv_safety = 5 iv_lot_size = 0 iv_min_order = 20 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = CONV decfloat34( 20 ) act = ls_result-qty ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD invalid_input.
|
||||
DATA(ls_result) = call( iv_stock = 0 iv_safety = 100 iv_lot_size = -5 iv_min_order = 0 ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_result-subrc ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
38
tasks_gen/eval/G0013/seed/repl_qty.func.abap
Normal file
38
tasks_gen/eval/G0013/seed/repl_qty.func.abap
Normal file
@@ -0,0 +1,38 @@
|
||||
FUNCTION {{p}}repl_qty
|
||||
IMPORTING
|
||||
iv_stock TYPE decfloat34
|
||||
iv_safety TYPE decfloat34
|
||||
iv_lot_size TYPE decfloat34
|
||||
iv_min_order TYPE decfloat34
|
||||
EXPORTING
|
||||
ev_order_qty TYPE decfloat34
|
||||
ev_action TYPE string
|
||||
EXCEPTIONS
|
||||
invalid_input.
|
||||
|
||||
IF iv_lot_size < 0.
|
||||
RAISE invalid_input.
|
||||
ENDIF.
|
||||
|
||||
DATA lv_required TYPE decfloat34.
|
||||
lv_required = iv_safety - iv_stock.
|
||||
|
||||
IF lv_required <= 0.
|
||||
ev_order_qty = 0.
|
||||
ev_action = 'NONE'.
|
||||
RETURN.
|
||||
ENDIF.
|
||||
|
||||
IF iv_lot_size > 0.
|
||||
ev_order_qty = ( floor( lv_required / iv_lot_size ) + 1 ) * iv_lot_size.
|
||||
ELSE.
|
||||
ev_order_qty = lv_required.
|
||||
ENDIF.
|
||||
|
||||
IF ev_order_qty < iv_min_order.
|
||||
ev_order_qty = iv_min_order.
|
||||
ENDIF.
|
||||
|
||||
ev_action = 'ORDER'.
|
||||
|
||||
ENDFUNCTION.
|
||||
38
tasks_gen/eval/G0013/seed/repl_qty_old.func.abap
Normal file
38
tasks_gen/eval/G0013/seed/repl_qty_old.func.abap
Normal file
@@ -0,0 +1,38 @@
|
||||
FUNCTION {{p}}repl_qty_old
|
||||
IMPORTING
|
||||
iv_stock TYPE decfloat34
|
||||
iv_safety TYPE decfloat34
|
||||
iv_lot_size TYPE decfloat34
|
||||
iv_min_order TYPE decfloat34
|
||||
EXPORTING
|
||||
ev_order_qty TYPE decfloat34
|
||||
ev_action TYPE string
|
||||
EXCEPTIONS
|
||||
invalid_input.
|
||||
|
||||
IF iv_lot_size < 0.
|
||||
RAISE invalid_input.
|
||||
ENDIF.
|
||||
|
||||
DATA lv_required TYPE decfloat34.
|
||||
lv_required = iv_safety - iv_stock.
|
||||
|
||||
IF lv_required <= 0.
|
||||
ev_order_qty = 0.
|
||||
ev_action = 'NONE'.
|
||||
RETURN.
|
||||
ENDIF.
|
||||
|
||||
IF iv_lot_size > 0.
|
||||
ev_order_qty = ( floor( lv_required / iv_lot_size ) + 1 ) * iv_lot_size.
|
||||
ELSE.
|
||||
ev_order_qty = lv_required.
|
||||
ENDIF.
|
||||
|
||||
IF ev_order_qty < iv_min_order.
|
||||
ev_order_qty = iv_min_order.
|
||||
ENDIF.
|
||||
|
||||
ev_action = 'ORDER'.
|
||||
|
||||
ENDFUNCTION.
|
||||
48
tasks_gen/eval/G0013/spec.md
Normal file
48
tasks_gen/eval/G0013/spec.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# 1. Goal
|
||||
The function module {{P}}REPL_QTY_OLD calculates the replenishment order quantity for a material. Users report two defects:
|
||||
- When the required quantity is exactly a multiple of the lot size, the module orders one lot too many.
|
||||
- The module accepts a negative minimum order quantity. It must report an error instead.
|
||||
The module {{P}}REPL_QTY_OLD is used by a released interface and must not change. Deliver the corrected calculation in the new function module {{P}}REPL_QTY.
|
||||
|
||||
# 2. Open questions
|
||||
None.
|
||||
|
||||
# 3. Context
|
||||
- Function group {{P}}FG_REPL exists in package $TMP.
|
||||
- Function module {{P}}REPL_QTY_OLD exists in this group. It is the module in production. It has the two defects above.
|
||||
- The replenishment program will call {{P}}REPL_QTY.
|
||||
|
||||
# 4. Contract
|
||||
Create the function module {{P}}REPL_QTY in function group {{P}}FG_REPL with this interface:
|
||||
|
||||
IMPORTING
|
||||
iv_stock TYPE decfloat34
|
||||
iv_safety TYPE decfloat34
|
||||
iv_lot_size TYPE decfloat34
|
||||
iv_min_order TYPE decfloat34
|
||||
EXPORTING
|
||||
ev_order_qty TYPE decfloat34
|
||||
ev_action TYPE string
|
||||
EXCEPTIONS
|
||||
invalid_input
|
||||
|
||||
ev_action contains the value 'ORDER' or the value 'NONE'.
|
||||
|
||||
# 5. Business rules
|
||||
1. If iv_lot_size is less than 0, or if iv_min_order is less than 0, raise the exception INVALID_INPUT.
|
||||
2. The required quantity is iv_safety minus iv_stock.
|
||||
3. If the required quantity is less than or equal to 0, ev_order_qty is 0 and ev_action is 'NONE'. Rules 4 to 6 do not apply then.
|
||||
4. If iv_lot_size is greater than 0, round the required quantity up to the next multiple of iv_lot_size. A required quantity that is already a multiple of iv_lot_size does not change.
|
||||
5. If iv_lot_size is 0, do not round.
|
||||
6. If ev_order_qty is less than iv_min_order, set ev_order_qty to iv_min_order.
|
||||
7. If ev_order_qty is greater than 0, ev_action is 'ORDER'. Otherwise ev_action is 'NONE'.
|
||||
|
||||
# 6. Constraints
|
||||
- Release target: 8.16.
|
||||
- Coding standards: Clean ABAP. Keep methods below 40 statements.
|
||||
- Out of scope: do not change the function group {{P}}FG_REPL. Do not change {{P}}REPL_QTY_OLD.
|
||||
|
||||
# 7. Acceptance
|
||||
- The function module {{P}}REPL_QTY is active and has no syntax error.
|
||||
- The hidden tests pass.
|
||||
- Write ABAP Unit tests for the function module in a global test class.
|
||||
89
tasks_gen/eval/G0013/task.json
Normal file
89
tasks_gen/eval/G0013/task.json
Normal file
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"id": "G0013",
|
||||
"category": "I",
|
||||
"object_type": "FUNC",
|
||||
"difficulty": 2,
|
||||
"release_target": "v816",
|
||||
"expected_outcome": "implement",
|
||||
"budget": {
|
||||
"max_tool_calls": 40,
|
||||
"max_activations": 10
|
||||
},
|
||||
"seed": [
|
||||
{
|
||||
"type": "FUGR",
|
||||
"name": "{{P}}FG_REPL",
|
||||
"description": "Replenishment function group"
|
||||
},
|
||||
{
|
||||
"type": "FUNC",
|
||||
"name": "{{P}}REPL_QTY_OLD",
|
||||
"functionGroup": "{{P}}FG_REPL",
|
||||
"file": "seed/repl_qty_old.func.abap",
|
||||
"description": "Faulty replenishment order quantity module"
|
||||
}
|
||||
],
|
||||
"contract": [
|
||||
{
|
||||
"type": "FUNC",
|
||||
"name": "{{P}}REPL_QTY",
|
||||
"functionGroup": "{{P}}FG_REPL",
|
||||
"params": [
|
||||
{
|
||||
"name": "iv_stock",
|
||||
"type": "decfloat34"
|
||||
},
|
||||
{
|
||||
"name": "iv_safety",
|
||||
"type": "decfloat34"
|
||||
},
|
||||
{
|
||||
"name": "iv_lot_size",
|
||||
"type": "decfloat34"
|
||||
},
|
||||
{
|
||||
"name": "iv_min_order",
|
||||
"type": "decfloat34"
|
||||
},
|
||||
{
|
||||
"name": "ev_order_qty",
|
||||
"type": "decfloat34"
|
||||
},
|
||||
{
|
||||
"name": "ev_action",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"out_of_scope": [
|
||||
"{{P}}FG_REPL",
|
||||
"{{P}}REPL_QTY_OLD"
|
||||
],
|
||||
"hidden_tests": [
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}T21_HIDDEN",
|
||||
"file": "hidden/t21_hidden.clas.abap",
|
||||
"description": "T21 hidden tests"
|
||||
}
|
||||
],
|
||||
"reference": [
|
||||
{
|
||||
"type": "FUNC",
|
||||
"name": "{{P}}REPL_QTY",
|
||||
"functionGroup": "{{P}}FG_REPL",
|
||||
"file": "reference/repl_qty.func.abap",
|
||||
"description": "Corrected replenishment order quantity module"
|
||||
},
|
||||
{
|
||||
"type": "CLAS",
|
||||
"name": "{{P}}REPL_TEST",
|
||||
"file": "reference/t21_test.clas.abap",
|
||||
"description": "Own tests of the reference (not contract)"
|
||||
}
|
||||
],
|
||||
"craft_checks": [
|
||||
"method_length"
|
||||
]
|
||||
}
|
||||
19
tasks_gen/eval/G0014/generation.json
Normal file
19
tasks_gen/eval/G0014/generation.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"id": "G0014",
|
||||
"pool": "eval",
|
||||
"object_type": "PROG",
|
||||
"category": "B",
|
||||
"attempts": [
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 93.3,
|
||||
"null": 0
|
||||
},
|
||||
{
|
||||
"stage": "validate",
|
||||
"oracle": 100.0,
|
||||
"null": 0
|
||||
}
|
||||
],
|
||||
"accepted": true
|
||||
}
|
||||
112
tasks_gen/eval/G0014/hidden/t15_hidden.clas.abap
Normal file
112
tasks_gen/eval/G0014/hidden/t15_hidden.clas.abap
Normal file
@@ -0,0 +1,112 @@
|
||||
CLASS {{p}}t15_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
|
||||
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
|
||||
PRIVATE SECTION.
|
||||
TYPES tt_plant TYPE RANGE OF {{p}}movmt-plant.
|
||||
METHODS run
|
||||
IMPORTING iv_from TYPE d
|
||||
iv_to TYPE d
|
||||
it_plant TYPE tt_plant 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_materials_in_range FOR TESTING.
|
||||
METHODS receipt_and_issue_split FOR TESTING.
|
||||
METHODS sorted_by_balance FOR TESTING.
|
||||
METHODS plant_filter FOR TESTING.
|
||||
METHODS date_range_filter FOR TESTING.
|
||||
METHODS ignored_move_types FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
|
||||
CLASS {{p}}t15_hidden IMPLEMENTATION.
|
||||
METHOD run.
|
||||
cl_salv_bs_runtime_info=>set( display = abap_false metadata = abap_false data = abap_true ).
|
||||
SUBMIT {{p}}stock_bal
|
||||
WITH p_from = iv_from
|
||||
WITH p_to = iv_to
|
||||
WITH s_plant IN it_plant
|
||||
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> ) ).
|
||||
" The ALV data can show a negative number with a trailing sign.
|
||||
DATA(lv_length) = strlen( rv_value ).
|
||||
IF lv_length > 1 AND substring( val = rv_value off = lv_length - 1 len = 1 ) = '-'.
|
||||
rv_value = '-' && substring( val = rv_value off = 0 len = lv_length - 1 ).
|
||||
ENDIF.
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD all_materials_in_range.
|
||||
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `MAT-001` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `MATERIAL` ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `120` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `BALANCE` ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `MAT-002` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `MATERIAL` ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `-20` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `BALANCE` ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD receipt_and_issue_split.
|
||||
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131' ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `150` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `RECEIPT_QTY` ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `30` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `ISSUE_QTY` ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `40` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `RECEIPT_QTY` ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `60` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `ISSUE_QTY` ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD sorted_by_balance.
|
||||
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 = `MAT-001` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `MATERIAL` ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `MAT-004` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `MATERIAL` ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `MAT-002` act = value_of( ir_data = lr_data iv_row = 3 iv_col = `MATERIAL` ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD plant_filter.
|
||||
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131'
|
||||
it_plant = VALUE #( ( sign = 'I' option = 'EQ' low = '1000' ) ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `MAT-001` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `MATERIAL` ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `70` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `BALANCE` ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD date_range_filter.
|
||||
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260109' ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines_of( lr_data ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `100` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `BALANCE` ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `40` act = value_of( ir_data = lr_data iv_row = 2 iv_col = `BALANCE` ) ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD ignored_move_types.
|
||||
DATA(lr_data) = run( iv_from = '20260101' iv_to = '20260131'
|
||||
it_plant = VALUE #( ( sign = 'I' option = 'EQ' low = '2000' ) ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines_of( lr_data ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `MAT-001` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `MATERIAL` ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `50` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `RECEIPT_QTY` ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = `0` act = value_of( ir_data = lr_data iv_row = 1 iv_col = `ISSUE_QTY` ) ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
127
tasks_gen/eval/G0014/reference/stock_bal.prog.abap
Normal file
127
tasks_gen/eval/G0014/reference/stock_bal.prog.abap
Normal file
@@ -0,0 +1,127 @@
|
||||
REPORT {{p}}stock_bal.
|
||||
|
||||
DATA gv_plant TYPE {{p}}movmt-plant.
|
||||
|
||||
PARAMETERS p_from TYPE d OBLIGATORY.
|
||||
PARAMETERS p_to TYPE d OBLIGATORY.
|
||||
SELECT-OPTIONS s_plant FOR gv_plant.
|
||||
|
||||
CLASS lcl_report DEFINITION FINAL.
|
||||
PUBLIC SECTION.
|
||||
TYPES:
|
||||
BEGIN OF ty_line,
|
||||
material TYPE {{p}}movmt-material,
|
||||
receipt_qty TYPE i,
|
||||
issue_qty TYPE i,
|
||||
balance TYPE i,
|
||||
END OF ty_line,
|
||||
tt_line TYPE STANDARD TABLE OF ty_line WITH EMPTY KEY,
|
||||
tt_plant TYPE RANGE OF {{p}}movmt-plant.
|
||||
METHODS select_balances
|
||||
IMPORTING iv_from TYPE d
|
||||
iv_to TYPE d
|
||||
it_plant TYPE tt_plant
|
||||
RETURNING VALUE(rt_lines) TYPE tt_line.
|
||||
METHODS display
|
||||
CHANGING ct_lines TYPE tt_line.
|
||||
ENDCLASS.
|
||||
|
||||
CLASS lcl_report IMPLEMENTATION.
|
||||
METHOD select_balances.
|
||||
SELECT material,
|
||||
SUM( CASE WHEN move_type = '101' OR move_type = '201'
|
||||
THEN quantity
|
||||
ELSE 0 END ) AS receipt_qty,
|
||||
SUM( CASE WHEN move_type = '261' OR move_type = '262'
|
||||
THEN quantity
|
||||
ELSE 0 END ) AS issue_qty
|
||||
FROM {{p}}movmt
|
||||
WHERE move_date >= @iv_from
|
||||
AND move_date <= @iv_to
|
||||
AND plant IN @it_plant
|
||||
AND move_type IN ( '101', '201', '261', '262' )
|
||||
GROUP BY material
|
||||
INTO CORRESPONDING FIELDS OF TABLE @rt_lines.
|
||||
|
||||
LOOP AT rt_lines ASSIGNING FIELD-SYMBOL(<ls_line>).
|
||||
<ls_line>-balance = <ls_line>-receipt_qty - <ls_line>-issue_qty.
|
||||
ENDLOOP.
|
||||
|
||||
SORT rt_lines BY balance DESCENDING.
|
||||
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_balances( iv_from = p_from
|
||||
iv_to = p_to
|
||||
it_plant = s_plant[] ).
|
||||
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 balances_per_material FOR TESTING.
|
||||
METHODS plant_filter FOR TESTING.
|
||||
METHODS negative_balance FOR TESTING.
|
||||
ENDCLASS.
|
||||
|
||||
CLASS ltc_report IMPLEMENTATION.
|
||||
METHOD class_setup.
|
||||
go_env = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( '{{P}}MOVMT' ) ) ).
|
||||
DATA lt_movmt TYPE STANDARD TABLE OF {{p}}movmt WITH EMPTY KEY.
|
||||
lt_movmt = VALUE #(
|
||||
( move_id = 1 material = 'MAT-A' plant = '1000' move_date = '20260105' move_type = '101' quantity = 100 )
|
||||
( move_id = 2 material = 'MAT-A' plant = '1000' move_date = '20260110' move_type = '261' quantity = 30 )
|
||||
( move_id = 3 material = 'MAT-B' plant = '2000' move_date = '20260112' move_type = '201' quantity = 40 )
|
||||
( move_id = 4 material = 'MAT-B' plant = '2000' move_date = '20260113' move_type = '262' quantity = 90 )
|
||||
( move_id = 5 material = 'MAT-C' plant = '2000' move_date = '20260112' move_type = '999' quantity = 500 ) ).
|
||||
go_env->insert_test_data( lt_movmt ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD class_teardown.
|
||||
go_env->destroy( ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD balances_per_material.
|
||||
DATA(lt_lines) = NEW lcl_report( )->select_balances(
|
||||
iv_from = '20260101' iv_to = '20260131' it_plant = VALUE #( ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_lines ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 'MAT-A' act = lt_lines[ 1 ]-material ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 70 act = lt_lines[ 1 ]-balance ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 'MAT-B' act = lt_lines[ 2 ]-material ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = -50 act = lt_lines[ 2 ]-balance ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD plant_filter.
|
||||
DATA(lt_lines) = NEW lcl_report( )->select_balances(
|
||||
iv_from = '20260101'
|
||||
iv_to = '20260131'
|
||||
it_plant = VALUE #( ( sign = 'I' option = 'EQ' low = '1000' ) ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 'MAT-A' act = lt_lines[ 1 ]-material ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 30 act = lt_lines[ 1 ]-issue_qty ).
|
||||
ENDMETHOD.
|
||||
|
||||
METHOD negative_balance.
|
||||
DATA(lt_lines) = NEW lcl_report( )->select_balances(
|
||||
iv_from = '20260101'
|
||||
iv_to = '20260131'
|
||||
it_plant = VALUE #( ( sign = 'I' option = 'EQ' low = '2000' ) ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_lines ) ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = 'MAT-B' act = lt_lines[ 1 ]-material ).
|
||||
cl_abap_unit_assert=>assert_equals( exp = -50 act = lt_lines[ 1 ]-balance ).
|
||||
ENDMETHOD.
|
||||
ENDCLASS.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user