# Geçmiş veri — spec

*14.08.2026 · Sets'in tanımı · EGT-108*

İzmir'de ayrı bir şirket olarak çalışan Akademi'nin geçmişte verdiği eğitimler merkez şirkete devrediliyor. Elde karmakarışık bir Excel var; bu belge onu sisteme sokmanın yoludur.

Buradaki her satır Sets'in 14.08.2026 tarihli tanımından ya da **dosyanın kendisinden ölçülen** bir sayıdan gelir. Varsayımla doldurulmuş satır yoktur; açık kalanlar §11'de işaretlidir.

⛔ **Bu belgedeki hiçbir sayı bugünkü veritabanından okunmadı.** Ortamda demo veri var ve pazartesi sıfırlanacak *(Sets, 14.08.2026)*. Ölçümlerin tamamı Tayfun'un `tüm isimler.xlsx` dosyasından, yani gerçek veridendir.

---

## 1. Ne işe yarıyor

Üç şey aynı anda:

1. **Uyum takibi.** Geçmişte eğitim almış bir kişi bugün sistemde "hiç sertifikası yok" görünüyor. Belgeleri yerine oturunca sertifikası dolan kişiler ortaya çıkar — ki bu doğrudan ticari fırsattır.
2. **Müşteri kazanımı.** Firma paneline geçen bir müşteriye *"sizden şu kadar personel bizden eğitim almış"* diyebilmek, ilişkinin ilk gününde elimizde duran tek somut şeydir.
3. **İkinci aşamada iş zekâsı** (§10): kaç eğitim verdik, kaç kişiye, kaç firmaya, kaçı eşleşti, kaçı bekliyor.

## 2. Verinin ölçülmüş profili

`tüm isimler.xlsx` — kolonlar: Kimlik No · Adı Soyadı · Belge Türü · İletişim Bilgileri · Şirket · Tarih · Tarih2 · Yer · E-Mail Adresi

| | |
|---|---|
| Satır / kişi | **2.381 satır · 1.040 benzersiz TCKN** — satır kişi değil, **belge**; bir kişi 10 satıra kadar çıkıyor |
| Kimlik | 2.380 satır algoritmaya göre geçerli TCKN, **1 bozuk** |
| E-posta | **hiç yok (0 satır)** |
| Telefon | **hepsinde var (2.381)** |
| Firma (Şirket kolonu) | 1.469 satırda yazılı, **64 benzersiz firma**; **912 satır firmasız**. Kişi bazında **565 / 1.040** kişinin firması biliniyor |
| Yer | eğitimin yapıldığı yer — firma **değil** (§4.4) |
| Tarih / Tarih2 | eğitimin **başlangıcı** ve **bitişi** *(Sets)*. Başlangıç 2.226 satırda var (**155 boş**), 2024-01-16 … 2026-05-06; bitiş 1.386 satırda dolu |
| Geçerlilik | Excel'de **hiç yok** — hesaplanacak (§8) |
| Belge türü | **36 distinct değer**, gerçekte 8 eğitim (§4.2) |

⚠ **Yakında 10 yıllık ikinci bir dosya gelecek** ve bugünkü iki yılın satırlarını da içerecek. Tekilleştirme §5.3'te.

## 3. Akış — sıra sabittir

1. **Önce müşteri doğar, normal süreciyle.** 3E temas eder, firma paneli kullanmak ister, hesap açılır, **müşteri yöneticisi giriş yapar**.

   ⛔ Bu, geçmiş verinin parçası **değildir** ve geçmiş veri firma açmaz *(Sets)*: *"geçerli müşteri olmadan bizim Excel'deki kişileri bağlayabileceğimiz ve onları düzenleyip onaylayacak kimse yok zaten."*

2. **Backoffice → Geçmiş veri → belge türü eşleme** (§6.1). 36 ham değerin her biri bir eğitim tanımına ve **ilk / yenileme**ye bağlanır.

3. **Backoffice → ana liste** (§6.2). Yetkili, **müşteriyle konuşarak ve teyit ederek**, TCKN'yle arar ve seçtiği satırları bir firmaya **taslak** bağlar. Kişi burada doğmaz.

4. **Müşteri panelinde «3E'den daha önce eğitim almış personelleriniz»** (§7). Firma saha/ofis seçer, e-posta ve lokasyon girer, eksik tarihleri tamamlar, gerekirse **reddeder**. Kaydettiğinde kişiler doğar, sertifikalar yazılır, davetler gider.

5. Satır **doğrulanmış** olur: artık o kişinin o firmada olduğunu biliyoruz.

## 4. Altı kural

### 4.1 ⛔ Hiçbir otomasyon yok

*(Sets, 14.08.2026: "sakın böyle bir şeye kalkışma! varsayımla ilerlersin sonrasında çok ciddi sorunlar yaşarız")*

Sistem eşleştirme **önermez**, satır **seçili getirmez**, benzerlik puanı **hesaplamaz**, «en son eğitimin firması» gibi bir kural **kurmaz**. Aynı TCKN iki firmada görünüyorsa (ölçüldü: 33 kişi) ekran bunu **bilgi olarak yazar**, kararı vermez. Eşleştirmeyi ofisten bir yetkili, müşteriyle konuşarak yapar.

⚠ **Arama ve süzme serbesttir — öneri değildir.** TCKN/ad/firma ile arama, sıralama, «firması bilinmeyenler» süzgeci insanın araçlarıdır. Sistemin kendiliğinden işaretlediği hiçbir şey olmaz.

### 4.2 Belge türü ham hâliyle sunulur

36 distinct değerin hepsi **Excel'de yazdığı gibi** listelenir. Gerçekte 8 eğitim var — Gözetim · IMSBC KOD · Grain Kod · IBC KOD · CSS KOD · IGC KOD · MARPOL 73/78 · TDC kod — ve «Y-» öneki yenilemeyi anlatıyor. ⛔ Sistem bu öneki **kural olarak kullanmaz**: insana bilgi olarak görünür, ilk/yenileme seçimini insan yapar.

⚠ Dosyada «MARPOL 73/79»dan «73/98»e kadar **21 bozuk varyant** var (Excel'de sürükleyerek doldurulmuş), her biri 1-2 satır. Eşleme ekranının varlık sebebi budur: hepsi tek eğitime bağlanabilmeli.

### 4.3 Hiçbir satır tüketilmez

Satır eşleştirildikten sonra da yerinde durur; üzerine yalnız **durum** ve **iz** yazılır. Firma adı Excel'de nasıl yazıyorsa öyle korunur — çünkü *"kaç firmaya eğitim vermişiz"* sorusu bir gün ona sorulacak (§10) ve normalize edip atmak o cevabı yok eder. Normalleştirme yalnız **arama** için yapılır, saklama için değil.

### 4.4 «Yer» firma değildir

**Eğitimin yapıldığı yerdir** *(Sets)*. Bu tür eğitimler çoğunlukla firmanın kendi yerinde veriliyor, dolayısıyla orada bir firma adı yazıyor **olabilir — ama olmak zorunda değil**. Olduğu gibi saklanır ve eşleştiren insana **gösterilir**; sistem onu firma olarak kullanmaz. «KARIŞIK» değeri de bu kolondadır: karışık bir yerde yapılmış eğitim demek.

### 4.5 Firmasız havuz kalıcı bir arama alanıdır

⛔ Firması bilinmeyen **912 satır** (475 kişi) çöp değildir, **bekleyen sorudur** *(Sets)*. Bugün yaşanmış örnek: bir firma *"4 kişi eksik"* demiş, listeyi göndermiş, o dört kişi **firmasızlar arasında** TCKN ile bulunmuş. Havuz silinmez, arşive kaldırılmaz; talep geldikçe çözülür.

### 4.6 Bu veri tenant'lı tablolara girmez

Excel satırları **kendi tablolarında** durur. Kişi ancak müşteri «Kaydet» dediğinde, gerçek firmasıyla `customer_users`'a doğar.

⛔ Sebep sert: bu üründe her satırın bir tenant'ı var ve satır güvenliği buna dayanıyor. Firması belirsiz satırları tenant'lı tabloların içine koymak ya scope'suz sorgu ya veri sızıntısı üretir (kırmızı çizgi 1 ve 4).

## 5. Tablolar

### 5.1 `legacy_training_records` — Excel'in kendisi

Bir satır = bir belge. Tenant'lı **değildir**; satır güvenliği yoktur, erişim backoffice iznine bağlıdır (§9).

| alan | not |
|---|---|
| `national_id`, `national_id_hash` | ⛔ Şifreli saklanır, arama hash üzerinden. Uygulama anahtarıyla — müşterinin anahtarı yok, çünkü henüz müşterisi yok (AD-05'in bu tabloya uymayan tek yeri, sebebiyle birlikte) |
| `person_name`, `phone`, `email` | Excel'de yazdığı gibi. E-posta bugün hep boş, kolon yine de taşınır |
| `document_type_raw` | «Y-Gözetim» gibi ham değer; `legacy_document_types`'a bağlanır |
| `company_name_raw` | **Şirket** kolonu, ham. Boşsa firmasız havuz |
| `venue_raw` | **Yer** kolonu, ham (§4.4) |
| `trained_on`, `trained_on_2` | eğitimin **başlangıç** ve **bitiş** günü *(Sets, 14.08.2026)*; ikisi de boş olabilir |
| `source_file`, `source_row` | Hangi dosyanın kaçıncı satırı — 10 yıllık dosya geldiğinde tek izlenebilirlik budur |
| `fingerprint` | **unique** · §5.3 |
| `state` | `unmatched` · `draft_linked` · `confirmed` · `rejected` |
| `customer_id` | taslak bağlandığı firma (nullable) |
| `customer_user_id` | doğrulanınca oluşan **ya da eşleşen** kişi (nullable) |
| `certificate_record_id` | yazılan sertifika (nullable) |
| `linked_by`, `linked_at`, `confirmed_at` | iz |

⚠ `state` alanı §10'un sayacıdır: «eşleştirdiğimiz / bekleyen» sayıları başka hiçbir yerden çıkarılamaz.

### 5.2 `legacy_document_types` — 36 ham değerin eşlemesi

`raw_name` (unique) · `training_definition_id` (nullable) · `course_kind` = `initial` | `renewal` (nullable) · `mapped_by`, `mapped_at`.

Yükleyici distinct ham değerleri **eşlemesiz** olarak yazar; eşlemeyi insan yapar. ⛔ Eşlemesi olmayan bir satır sertifikaya dönüşemez.

### 5.3 Mükerrer engelleme

`fingerprint` = **TCKN hash + ham belge türü + eğitim tarihi** *(Sets)*. İkinci dosyada aynı üçlü gelirse satır **atlanır**; mevcut satır, durumu ve bağlantısı korunur.

⚠ Tarihi olmayan satırlarda üçlünün son parçası boştur; o satırlar aynı TCKN ve aynı belge türüyle **tek** sayılır. Yüklemeden sonra atlanan/eklenen satır sayısı raporlanır.

### 5.4 `legacy_record_rejections` — kim reddetti

`legacy_training_record_id` · `customer_id` · `rejected_at` · `rejected_by` (müşteri kullanıcısı).

Bir satır zamanla birden fazla firma tarafından reddedilebilir; o yüzden ayrı tablo.

### 5.5 Yükleme — ne ekran, ne de betik yazılacak

⛔ **Hiçbir içe aktarma kodu yazılmayacak** *(Sets, 14.08.2026)*: ne backoffice'te yükleme ekranı, ne tekrar koşulabilir bir konsol komutu. Sets'in gerekçesi: *"herhangi bir kod ya da script yaparsak yarın bir tutarsızlık durumunda corrupt ederiz. Sen elle yapıyormuş gibi yap daha sağlıklı olur."*

Yükleme **oturumda, denetimli** yapılır: dosya okunur, sayılar çıkarılır, ne yazılacağı **önce raporlanır**, Sets görür, ondan sonra yazılır. Her adımda sayı doğrulanır.

⚠ Yalnız **şema** migration ile kurulur (tablolar); **veri** hiçbir zaman migration'a gömülmez. Sebebi ayrıca güvenlik: içinde 2.381 kişinin kimlik numarası olan bir dosya depoya konmaz.

⚠ Yakında gelecek **10 yıllık** dosya da aynı şekilde: önce fark raporu (kaç satır aynı, kaç yeni, kaç çelişkili), sonra yazma. §5.3'teki parmak izi bu raporu üretmek için var — kendiliğinden çalışan bir tekilleştirme için değil.

## 6. Backoffice ekranları

### 6.1 Belge türü eşleme

36 satır: ham ad · kaç satırı etkilediği · eğitim tanımı seçimi · **ilk / yenileme** seçimi. Hiçbiri önceden doldurulmaz.

⚠ Kaç satırı etkilediği yazar, böylece 1 satırlık bozuk varyantlar da göze çarpar.

### 6.2 Ana liste

Sütunlar: ad · TCKN (maskeli) · telefon (maskeli) · belge türü · tarih · **Şirket (ham)** · **Yer (ham)** · durum · bağlı olduğu firma.

- **Arama:** TCKN, ad, firma adı. Süzgeçler: durum, firması bilinmeyenler, belge türü, tarih aralığı.
- ⛔ **Toplu TCKN araması:** müşterinin gönderdiği listeyi yapıştırıp bir kerede aratabilmek. *(Sets: backoffice yetkilisi "önce liste alarak" ilerler.)* Tek tek aramak kırk kişilik listede iş görmez.
- **Firmaya bağla:** seçilen satırlar, sistemde kayıtlı bir firmaya taslak bağlanır. Firma seçimi yalnız **mevcut** müşteriler arasından; bu ekran firma açmaz (§3.1).
- **Bağı kaldır:** müşteri onaylamadan önce geri alınabilir.
- ⚠ Aynı TCKN başka bir firmada da görünüyorsa satırda **bilgi olarak** yazar; sistem seçim yapmaz.

## 7. Müşteri ekranı — «3E'den daha önce eğitim almış personelleriniz»

⛔ **Kişi bazında gruplanır**, satır bazında değil: 2.381 belge 1.040 kişiye ait ve bir kişi 10 belge taşıyabiliyor. Firma aynı adı sekiz kez görürse hangisini işaretleyeceğini bilemez.

Her kişi için firma şunları yapar:

| | |
|---|---|
| **Saha / ofis** | seçer |
| **Eğitim alacak** | ⚠ **seçili gelir** — zaten eğitim almışlar |
| **E-posta** | ⛔ zorunlu. Listede hiç yok ve kişi panele girecek (§8.3) |
| **Lokasyon** | seçer |
| **Eksik tarih** | tarihi olmayan belgede tarihi girer (§8.2) |
| **Sertifika no** | gerçek numarayı girer. ⚠ Ekran **takip için son sertifikanın yeterli** olduğunu söyler |

⛔ **Evrak bu ekranda yüklenmez** *(Sets, 14.08.2026)*: kişi henüz doğmamışken evrak yüklenemez. Evrak, kişi doğup davet gittikten sonra **mevcut** yoldan gelir — firma personel ekranından yükler ya da kişi kendisi getirir. Bu ekran yalnız kişiyi doğurmak için gerekenleri sorar.
| **Reddet** | *"bu kişi bizde çalışmıyor"* |

⚠ Kişinin **telefonu** maskeli gösterilir. Elimizdeki tek doğrulama ipucu odur: liste geçmişten geliyor, kişi ayrılmış olabilir.

**Kaydet** dendiğinde (§8).

### 7.2 Kimlik numarası geçersiz olan satır

Bugünkü dosyada bir satır, 10 yıllık dosyada daha çok olabilir.

⛔ Satır **yüklenir, «kimlik geçersiz» diye işaretlenir ve firmaya bağlanabilir** *(Sets, 14.08.2026)*. Müşteri ekranında o kişinin yanında **düzeltmesi gerektiği** yazar ve doğru kimlik numarasını **müşteri girer** — zaten e-postayı, lokasyonu ve tarihleri de o dolduruyor.

⚠ Düzeltilmeden kişi kaydedilemez: kimlik numarası, o kişinin firmanın panelinde zaten kayıtlı olup olmadığını anlamanın (§8.1) tek yoludur.

### 7.1 Reddedilen satır

Satır **firmasız havuza döner** (`state = unmatched`) ve yeniden aranabilir olur; ret `legacy_record_rejections`'a yazılır. Böylece aynı firmaya bir daha önerilmez ve backoffice yanlış eşleştirmeyi görür.

## 8. Kaydetme — kişi ve sertifika

### 8.1 Kişi

⛔ **Aynı TCKN firmada zaten kayıtlıysa yeni kişi açılmaz, mevcut kişiye belge eklenir** *(Sets)*. Kişinin adı, telefonu, e-postası **değişmez**: firmanın kendi kaydı geçmiş listeden daha günceldir.

Kişi yoksa açılır — mevcut kişi ekleme yolunun **aynısıyla** (`AddPortalPerson`), ki audit, şifreleme ve doğrulama tek yerde kalsın.

### 8.2 Sertifika

| alan | değer |
|---|---|
| `source` | **`legacy`** — bugünkü `training` ve `declared`'a eklenen üçüncü kaynak |
| `issued_on` | ⛔ Eğitimin **BİTTİĞİ** gün: `trained_on_2` varsa o, yoksa `trained_on`. Sertifika eğitim tamamlandığında düzenlenir, dolayısıyla iki günlük bir eğitimde belge ikinci günün tarihini taşır *(Sets, 14.08.2026)* |
| `valid_until` | ⛔ `issued_on` + **eğitim tanımının `renewal_months`'u** |
| `certificate_no` | ⚠ Bizim ürettiğimiz iç numara (ör. `GEÇMİŞ-000412`); müşteri gerçek numarayı girince üzerine yazılır |
| `scope_name` | eşlenen eğitim tanımının adı, o günkü hâliyle donar |
| `training_definition_id` | eşlemeden gelir |

⛔ **Geçerlilik boş bırakılamaz.** Bugünkü uyum sorgusu, geçerlilik tarihi olmayan sertifikayı «hiç dolmayan» sayıp kişiyi listeden düşürüyor — boş tarih, takibin sessizce çalışmaması demektir.

⛔ **Rozet: «geçmiş veriden geliyor, lütfen bakanlıktan doğrulayın»** *(Sets)*. Tek ekranda kalmaz — kişi kartı, uyum sorgusu, müşterinin personel listesi, sertifika nerede görünüyorsa orada. Kaynağı `legacy` olduğu için ayrı bir alan gerekmez.

⚠ **Tarihi olmayan 155 satır** firmaya bağlanabilir ve müşteri ekranında görünür, ama «tarih bilinmiyor» işaretlidir; **müşteri tarihi girmeden o kişi kaydedilemez**.

### 8.3 Davet

Kaydedilen herkese davet gider. ⚠ Saha personeline de: katılımcı ekranları `staff_kind`'a değil **kişinin eğitim kaydına** bakarak açılıyor — saha personeli eğitim gününü görür, yoklamasını işaretler, anketini doldurur. Fark yalnız **neye** davet edildiklerinde: ofis personeli firma paneline, saha personeli katılımcı ekranlarına.

## 9. Gizlilik ve iz

- ⛔ **Tam audit** *(Sets: "bu çok önemli")*: yükleme, eşleme, bağlama, bağ kaldırma, ret, kaydetme — hepsi append-only loglanır (AD-06), işlemle aynı transaction'da.
- ⛔ Havuz TCKN taşıyor: ekranlarda **maskeli** görünür (AD-16) ve **TCKN araması loglanır** (AD-21) — uyum sorgusunda uygulanan kuralın aynısı, burada daha kritik çünkü veri henüz kimsenin değil.
- ⚠ Müşteriye gösterilen listede TCKN ve telefon maskelidir.

### 9.1 Kim girebilir

- **Backoffice:** «Geçmiş veri» kendi iznini taşır *(Sets, 14.08.2026)* — mevcut bir izne yamanmaz. Sebep: 2.381 kişinin kimlik numarasını kimin görebildiği, ayrı cevaplanması gereken bir sorudur; başka bir iznin kapsamına kendiliğinden girmemeli. İzin yalnız bu işi yapacak kişilere verilir.
- **Müşteri tarafı:** mevcut **personel yönetimi** yetkisi yeterlidir; yeni izin açılmaz. Zaten personel ekleyen kişi bu listeyi onaylayan kişidir, ve firma yöneticisinin bir izni daha dağıtmayı unutması gereksiz bir engel olurdu.

## 10. İkinci aşama — BI *(kapsam dışı, ama ilk adım buna izin verir)*

Sonraki adımda konsolide bir ekran gelecek *(Sets)*: bugüne kadar eşleştirdiğimiz, eşleştirme bekleyen, toplam verilen eğitim, eğitim verilen kişi — ve buradan **potansiyel müşteriler süzülüp satışçılara iletişim hedefi** verilecek.

⛔ İlk adıma yüklediği tek şart §4.3'tür: hiçbir satır tüketilmez, firma adı ham hâliyle korunur.

## 11. Açık kalanlar

**Yok.** 14.08.2026'da açık kalan dört madde aynı gün karara bağlandı: yükleme mekaniği (§5.5), yetkiler (§9.1), `Tarih2`'nin anlamı (§8.2), geçersiz kimlik taşıyan satır (§7.2).
