# Review — docs/plans/egitim-belgesi-plan.md v1.0 — 2026-10-20

> **Review type:** plan-review
> **Previous review:** N/A (first review)
> **Upstream document(s) read:** `design/52 Egitim Belgesi.dc.html` (tamamı, 301 satır), `docs/design/katilim-belgesi-brief.md`, `proje-haritasi.md` «EGT-218 kararları» madde 5, «EGT-225 belge içeriği» (üstü çizili madde dahil), «EGT-225 tasarım 52 kararları» 1–5 (satır 254)
> **Other cross-referenced documents:** `CLAUDE.md` (17 kırmızı çizgi), kod doğrulaması: `app/Actions/Trainings/CompleteTraining.php`, `app/Actions/Trainings/RecordCertificate.php`, `app/Domain/Invoicing/InvoiceNumber.php`, `app/Support/Audit/AccessLogger.php`, `app/Actions/Office/UploadDocumentTemplate.php`, migration'lar `2026_11_04_000100`, `2026_11_01_000400`, `2026_09_17_000100` (document_templates), `2026_08_30_000100` + `2026_10_02_000100` (certificate_records), `resources/js/pages/Portal/Participant/Panel.vue`, `resources/js/organisms/InvoiceDocument.vue`
> **Stage locks in effect:** Tasarım 52 + Sets kararları (harita) locked — plan bunlara uyumluluk açısından değerlendirildi

---

## Upstream Coverage Matrix

| Upstream § / madde | Reviewed doc'ta durumu | Kanıt |
|---|---|---|
| Harita k.5 (EGT-218): düzenleyen 3E/marka + şablon yüklenirse sistem üretir; dışarıda hazırlanır bugünkü kayıt | ✓ | plan B-1, B-5; `generated` yalnız `institution`/`brand` (migration `2026_11_04_000100:54` tür adlarıyla uyumlu) |
| EGT-225 belge içeriği: T.C. kimlik no **açık** | ✓ | plan B-9 («kimlik no açık»), B-11 erişim kaydı |
| EGT-225 belge içeriği: numarayı **sistem verir, düzenleyen başına sıralı, boşluksuz seri** | ⚠ | plan B-2 kurgusu var; ancak önek kuralı Sets'in örneğiyle ve dayandığı `InvoiceNumber::format` ile çelişiyor → **F-1** |
| EGT-225 belge içeriği: ~~QR + herkese açık doğrulama~~ → yerine tasarım 52 kararı 2 | ✓ | plan B-8 + «Kapsam dışı» (girişsiz doğrulama ve `dogrula.3egrp.com` dışarıda) |
| Tasarım 52 kararı 1: ayrı belge seri öneki (marka + 3E şirketi), fatura serisinden bağımsız (**ör. WNX, 3EA**) | ⚠ | plan veri modeli CHECK `^[A-Z]{2,4}$` — Sets'in verdiği örnek `3EA` rakam içerir ve reddedilir → **F-1** |
| Tasarım 52 kararı 2: doğrulama panel adresinde yol, giriş zorunlu | ✓ | plan B-8 (`/dogrula`, müşteri oturumu + ofis); QR panel adresi |
| Tasarım 52 kararı 3: PNG ya da tek sayfalık PDF; canlıya `poppler-utils` | ✓ | plan B-4, Görev 6, Rollout ön koşulu, `ortamlar.md` güncellemesi |
| Tasarım 52 kararı 4: yeni ofis izni düzeltme/iptal; gerekçe zorunlu; denetim kaydı; düzeltmede yeni numara, eski «geçersiz» | ⚠ | plan B-13 kararı taşıyor; ancak «yeni numara ile yeni kayıt» mevcut `certificate_records_one_per_enrolment` tekil indeksine çarpar, plan bu indeksi değiştirmiyor → **F-2** |
| Tasarım 52 kararı 5 (süreç: plan + heyet) | ✓ | bu review |
| Tasarım Ekran 1: üç kart seçici, düzenleyen + «seri öneki · son no», geçerlilik (+süresiz notu), şablon yükleme/önizleme/sürüm etiketi, «Örnekle önizle», «Şablonu değiştir = yeni sürüm», kural metni | ⚠ | plan B-3/B-14 öğeleri sayıyor; ancak sürümleme `document_templates`'ın **tür-başına** iki tekil indeksiyle (`…_version_per_kind`, `…_one_current`) tanım-başına şablonda çelişir, plan değişikliği içermiyor → **F-3** |
| Tasarım Ekran 1: dört yükleme hata/uyarı kartı (çözünürlük, oran, okunamadı, şablon yok—form kaydedilir) | ✓ | plan B-4 («hata metinleri tasarımın üç kartı») + B-12 (form kaydedilir, uyarır); testlerde üç ret hâli |
| Tasarım Ekran 2: SAYIN · ad · kimlik no · eğitim · tarih·süre·yer · künye üçlüsü · imzalar şablondan · künye şeridi + QR + doğrulama adresi | ✓ | plan B-9, B-10 (adres panel yolu olur — karar 2; mockup metni güncel değil notuyla uyumlu) |
| Tasarım «Basım bloğunun kuralları»: uzun ad 32→32px / 44→26px, eğitim adı ≤2 satır, marka yoksa «İçerik» düşer, düzenleyen=veren tek hücre, künye konumu üst/alt tek ayar | ✓ | plan B-9, B-3 (`certificate_imprint_position`), B-14; testlerde iki hâl |
| Tasarım iptal/düzeltme kartı: taramalı üst kutu, «GEÇERSİZ» damga, QR çalışır, doğrulama «geçersiz» der, yeni numara, seri boşluksuz | ✓ | plan B-10, B-13, B-7 |
| Tasarım Ekran 3a: Belge sütunu dört hâl, «Belgeyi aç / PDF», «Tümünü PDF olarak aç (n)», erişim günlüğü dipnotu | ✓ | plan B-11 |
| Tasarım 3a dipnot + «sınav notu girilince üretilir» hâli | ⚠ | plan B-5 üretimi yalnız `CompleteTraining` + şablon yüklemesine bağlıyor; tamamlama yazıldıktan **sonra** girilen geçer sonucu (yeniden sınav — sistemde var) hiçbir tetikleyici kapsamıyor → **F-4** |
| Tasarım «Şablon yok» ofis şeridi: sayı + «Eğitim tanımına git» | ✓ | plan B-12 |
| Tasarım Ekran 3b portal: personel sertifikaları satırı, «Belge hazırlanıyor», «Belge düzenlenmedi» | ✓ | plan B-11 |
| Brief §2.3: belgeyi açma — ofis, portal firma ekranı **ve katılımcının kendi ekranı** | ❌ | plan B-11 yalnız ofis + portal personel satırı; katılımcının kendi ekranı (bugün `Portal/Participant/Panel.vue:381-386` kendi sertifikalarını listeliyor) planda yok → **F-5** |
| Tasarım Ekran 4: geçerli/geçersiz/bulunamadı hâlleri, sorgu formu, «kimlik no gösterilmez», iletişim e-postası, mobil tek kolon | ✓ | plan B-8 (e-posta `settings`'ten — settings tablosu var: `2026_07_28_080000`), tarayıcı testleri |
| Brief §3: belgede yazanlar (tam liste) | ✓ | plan B-9 birebir |
| Brief §4: hâller (şablon yüklü/yok, tamamlamamış, iptal/düzeltilmiş, 375px yatay kayar) | ✓ | plan B-10, B-11, B-12; test satırı |
| Brief §5: fatura belgesiyle aynı yazdırma yolu (`@page` A4, tarayıcı) | ✓ | plan B-10, mevcut durum tablosu (`organisms/InvoiceDocument.vue` doğrulandı) |
| AD-06 / kırmızı çizgi 13: kritik işlem audit'li, aynı işlemde | ⚠ | B-13 düzeltme/iptal için açık; **üretim** için plan yalnız doğrulama kaydını sayıyor, denetim (audit_writes) kaydı yazılı değil → **F-6** |
| AD-11: silinmezlik | ✓ | plan B-13 («ikisi de silmez»), Rollback (üretilmiş belge varsa `down` durur) |
| AD-16: doğrulama kaydında/sayfasında kimlik no yok | ✓ | plan B-7, B-8; test satırı |
| AD-21: her açılış + her sorgu erişim kaydı | ✓ | plan B-8 (`recordQuery` — `AccessLogger::recordQuery` mevcut, `AccessLogger.php:95`), B-11 |
| AD-04/AD-05/RLS: `certificate_verifications` kiracısız + muafiyet gerekçeli; `printed` şifreli | ✓ | plan B-6 (`encrypted:array`), B-7, veri modeli |
| Mevcut durum tablosu iddiaları | ✓ | tümü kodda doğrulandı: `certificate_mode` CHECK (`2026_11_04_000100:51`), `certificate_records` RLS'li (`2026_08_30_000100:336`), `RecordCertificate` tamamlanma+geçme şartı, `document_templates` sürüm/`is_current`/`issuing_company_id`, `invoice_number_sequences` (şirket×yıl, `last_number`), `InvoiceNumber::format`, `bacon/bacon-qr-code` (`composer.json:13`), `trainings.cost_branch_id` (`2026_11_01_000600`), `IssuingCompanies::forBranch`, `HouseFile::stream`, `organisms/InvoiceDocument.vue` + `resources/css/invoice.css` |

---

## ✅ What works

- **Mevcut durum tablosu dürüst ve doğru:** dokuz iddianın tamamı kodda birebir doğrulandı; plan gerçek koda dayanıyor, bellekten yazılmamış.
- **Sets'in iki sapma kararı eksiksiz işlenmiş:** girişli doğrulama + panel yolu (B-8, kapsam dışı bölümü `dogrula.3egrp.com`'u açıkça dışarıda bırakıyor) ve PDF şablonu için `poppler-utils` (B-4, deploy ön koşulu + `ortamlar.md` + araç yoksa açık hata).
- **Numara boşluksuzluğu doğru kurgulanmış:** satır kilidi, üretimle aynı işlem, düzeltmede yeni numara — eşzamanlılık testi de yazılmış.
- **Kişisel veri sınırı düşünülmüş:** kiracısız doğrulama tablosunda kimlik no yok (AD-16), `printed` kopyası şifreli (AD-05), RLS muafiyeti gerekçeli, her açılış/sorgu erişim kaydına bağlanmış (AD-21).
- **Kopyadan basım ilkesi** (fatura dili) belge değişmezliğini doğru kuruyor; testlerde «kişinin adı sonradan değişse belge değişmez» satırı var.
- **Rollback** üretilmiş belgeyi koruyor (AD-11) ve kararı Sets'e bırakıyor.

## ⚠️ Findings & Risks (in-scope for this review type)

**F-1 — Seri öneki kuralı Sets'in kararıyla ve dayandığı kodla çelişiyor.** Plan veri modeli (satır 42) öneki `CHECK ^[A-Z]{2,4}$` ile sınırlar; Sets'in kararı örnek olarak **`3EA`** verir (`proje-haritasi.md:254`) — rakamla başlayan bu önek CHECK'e takılır. Ayrıca plan B-2 «Format `InvoiceNumber::format` ile aynı» der; o metot **tam 3 karakter** `^[A-Z0-9]{3}$` ister ve aksi hâlde exception atar (`app/Domain/Invoicing/InvoiceNumber.php:21-28`) — plan 2 ve 4 karakterli öneke izin verirken bu metodu kullanamaz. Plan kendi içinde ve upstream'le üç yönlü tutarsız.

**F-2 — Düzeltme akışı mevcut tekil indekse çarpar; plan indeks değişikliğini içermiyor.** B-13 düzeltmede «yeni numara ile **yeni kayıt**» der; `certificate_records` üzerinde `certificate_records_one_per_enrolment` kısmi tekil indeksi (enrolment başına tek satır, `2026_10_02_000100:82-86`) ikinci satırı reddeder. Plan'ın veri modeli bölümü (satır 46) yalnız yeni kolonları sayıyor; bu indeksin düzeltme senaryosuyla nasıl bağdaşacağı plansız. (`RecordCertificate.php:107-115`'teki «one certificate per place» kontrolü de aynı varsayımı taşır.)

**F-3 — `document_templates`'ın tür-başına tekillik indeksleri tanım-başına şablonla çelişir; plan bunları ele almıyor.** B-3 yeni tür `certificate_template` + `training_definition_id` ekler ve tanım başına sürümlü şablon öngörür; ancak tabloda `document_templates_version_per_kind (kind, version)` ve `document_templates_one_current (kind) WHERE is_current` indeksleri vardır (`2026_09_17_000100:81-117`) — iki eğitim tanımının her birinin «kullanımda» şablonu olduğu anda ikinci indeks, v1'den başlayan sürümlerinde birinci indeks ihlal edilir. Plan veri modeli (satır 44) yalnız «FK + kind CHECK genişler» diyor; migration data-loss/kısıt riski değerlendirilmemiş.

**F-4 — Üretim tetikleyicisi, tamamlama sonrası geçilen sınavı (yeniden sınav) kapsamıyor.** B-5 üretimi iki ana bağlar: `CompleteTraining` anı ve şablon yüklemesi. `CompleteTraining` kapısı sonucun **var olmasını** ister, geçilmesini değil (`CompleteTraining.php:59`); kalan katılımcının tamamlaması yazılır, belge üretilmez. Sistem yeniden sınavı destekler ve son deneme esastır (`RecordCertificate.php:71-78`, `RecordExamResult` mevcut): tamamlaması yazılmış katılımcı sonraki denemede geçtiğinde B-5'in iki tetikleyicisinin hiçbiri bir daha çalışmaz ve `generated` tanımda elle giriş de kapalı olduğundan belge **hiç üretilemez**. Tasarımın «sınav notu girilince üretilir» hâli (3a, satır 213) bu yolu da kapsar; plan kapsamıyor.

**F-5 — Brief §2.3'ün üçüncü yüzeyi planda yok: katılımcının kendi ekranı.** Brief belgeyi açma yüzeylerini «ofiste eğitim sayfası, portalda firmanın personel/sertifika ekranı **ve katılımcının kendi ekranı**» olarak sayar; plan B-11 yalnız ofis tablosu ve portal personel satırını kapsar. Katılımcının kendi sertifika listesi bugün mevcuttur (`resources/js/pages/Portal/Participant/Panel.vue:381-386`) ve plan bu ekranda üretilen belgenin açılıp açılmayacağını hiç söylemiyor — ne kapsam içi ne «Kapsam dışı» bölümünde.

**F-6 — Belge üretiminin denetim (audit) kaydı planda yazılı değil.** B-13 düzeltme/iptal için «denetim kaydı aynı işlemde (AD-06)» der; üretim için B-5/B-7 yalnız doğrulama kaydını ve numarayı sayar, Görev 3 ve test listesi de üretim audit'ini içermez. Sertifika oluşturma kritik işlemdir (kırmızı çizgi 13); bugünkü elle giriş bile audit yazar (`RecordCertificate.php:144-158`). Planın audit yazma noktaları üretim için eksik.

## ❓ Open Questions (to be answered before next stage)

1. **Katılımcı oturumu doğrulayabilir mi?** Sets kararı 2 «panele giriş yapmış kullanıcılar» der; plan B-8 yalnız «müşteri oturumu» ve ofis sayar. `/katilimci` oturumu (magic link dahil) `/dogrula`ya erişir mi, erişmez mi — açıkça yazılmalı.
2. **«Bulunamadı» sorgusunun erişim kaydı** hangi kapsamda yazılır? B-8 «sahibinin kiracısı» der; var olmayan numaranın sahibi yok.
3. **Önek çakışması yalnız form doğrulamasıyla engelleniyor** (iki tablo arasında, plan satır 42). DB düzeyinde garanti yok; aynı önek iki düzenleyende oluşursa `certificate_verifications` tekil numarası üretim anında patlar. Bu artık risk bilinçli mi kabul ediliyor?
4. **B-5 «kayıt 'bekliyor' kalır»** — B-6'nın `status` kümesinde (`valid`/`corrected`/`cancelled`) «bekliyor» yok. Bekleyen, satırsız bir durum mu (kayıt hiç açılmaz) yoksa dördüncü bir statü mü?

## Verdict: ❌ REJECTED

Matrix'te 4 ⚠ + 1 ❌ satır var; altı bulgu açık. İkisi (F-2, F-3) mevcut veritabanı kısıtlarıyla doğrudan çelişen, migration planına girmemiş değişiklikler; F-1 Sets'in verdiği örneği reddeden bir doğrulama kuralı; F-4 `generated` tanımda belgesiz kalan bir katılımcı sınıfı üretir. Kararların kendisi (girişli doğrulama, poppler-utils, yeni izin, boşluksuz seri) sağlam — eksik olan bunların mevcut şemayla ve sınav/yeniden-sınav akışıyla bağdaştırılmasıdır.
