# İSKUR Lojistik Kontrol Merkezi — PHP Sürümü Kurulum Kılavuzu

Bu paket, Next.js + Cloudflare D1 üzerinde çalışan sistemin **saf PHP + MySQL**
karşılığıdır. Composer, Node.js veya derleme adımı gerektirmez: klasörü sunucuya
kopyalayıp veritabanını kurmanız yeterlidir.

---

## 1. Gereksinimler

| Bileşen | Sürüm / Not |
|---|---|
| PHP | 8.0 veya üzeri (8.2+ önerilir) |
| PHP eklentileri | `pdo_mysql`, `openssl`, `mbstring` (hepsi standart kurulumda gelir) |
| Veritabanı | MySQL 5.5+ veya MariaDB 5.5+ (şema en geniş uyumlulukta yazıldı) |
| Web sunucusu | Apache (`mod_rewrite` açık) veya Nginx |

---

## 2. Dosyaları yükleyin

Paketi sunucuya açın. Önerilen yerleşim:

```
/var/www/iskur/
├── app/        → uygulama kodu (web'den erişilemez olmalı)
├── views/      → ekran şablonları
├── sql/        → kurulum şemaları
├── bin/        → cron betiği
├── storage/    → (yalnız SQLite kullanılırsa gerekir)
└── public/     → BELGE KÖKÜ (document root) burası olmalı
```

**Belge kökünü `public/` klasörüne ayarlayın.** Bunu yapamıyorsanız (bazı paylaşımlı
hostingler), kök dizindeki `.htaccess` dosyası istekleri otomatik olarak `public/`
altına yönlendirir; o zaman tüm klasörü `public_html` içine kopyalamanız yeterlidir.

---

## 3. Kurulumu çalıştırın (önerilen yol)

Tarayıcıdan **`http://siteniz.com/kurulum.php`** adresini açın. Sihirbaz sırasıyla:

1. Sunucu gereksinimlerini kontrol eder (PHP sürümü, eklentiler, yazma izni),
2. Veritabanı bağlantısını sınar ve hata varsa MySQL'in **kendi hata mesajını** gösterir,
3. Tabloları oluşturur (phpMyAdmin'den içe aktarmaya gerek kalmaz),
4. Yönetici hesabınızı oluşturur,
5. `app/config.php` dosyasını otomatik yazar (şifreleme anahtarı dâhil).

Sizin sunucunuz için alanlar şöyle doldurulacak:

| Alan | Değer |
|---|---|
| Sunucu | `localhost` |
| Port | `3306` |
| Veritabanı adı | `iskurlojistik_hf` |
| Veritabanı kullanıcısı | `iskurlojistik_hf` |
| Veritabanı parolası | `FEdakar.4646` |
| Alt klasör yolu | Site kökündeyse boş bırakın, alt klasördeyse örn. `/lojistik` |

Yönetici bölümünde kendi ad-soyad, e-posta ve parolanızı girin — sistem bu hesapla açılır.

> **Ön koşul:** Veritabanı ve kullanıcı hosting panelinizden (cPanel → MySQL
> Veritabanları) önceden oluşturulmuş, kullanıcı veritabanına **tüm yetkilerle**
> eklenmiş olmalıdır. Sihirbaz veritabanı oluşturmaz, yalnız tabloları kurar.

**Kurulum bitince `public/kurulum.php` dosyasını sunucudan silin.**

---

## 4. Alternatif: SQL dosyasını elle içe aktarma

Sihirbazı kullanmak istemezseniz:

1. phpMyAdmin'de **önce `iskurlojistik_hf` veritabanını seçin** (sol menüden tıklayın),
2. **İçe Aktar** sekmesinden `sql/iskur-lojistik-mysql.sql` dosyasını yükleyin,
3. `app/config.sample.php` dosyasını `app/config.php` adıyla kopyalayıp doldurun:

```php
'db' => [
    'driver'   => 'mysql',
    'host'     => 'localhost',
    'port'     => 3306,
    'database' => 'iskurlojistik_hf',
    'username' => 'iskurlojistik_hf',
    'password' => 'FEdakar.4646',
    'charset'  => 'utf8mb4',
],
'app' => [
    'secret_key' => '...',  // aşağıdaki komutla üretin
    'debug'      => false,
    'base_path'  => '',
],
```

`secret_key` alanını **boş/yer tutucu olarak bırakabilirsiniz**: sistem ilk SMTP
kaydında güçlü bir anahtar üretip `app/config.php` dosyasına (yazılamıyorsa
`storage/secret.key` dosyasına) kendisi yazar.

Kendiniz belirlemek isterseniz:

```sh
php -r "echo bin2hex(random_bytes(32)), PHP_EOL;"
```

> Anahtar bir kez oluştuktan sonra **değiştirmeyin**. Değiştirilirse kayıtlı SMTP
> parolası çözülemez ve yeniden girilmesi gerekir.

Bu yolla kurarsanız giriş bilgileri: `admin@iskur.com` / `Iskur2026!`
(ilk girişte Ekip & Yetkiler ekranından değiştirin).

---

## 4b. Mevcut kurulumu güncelleme

Sistemi daha önce kurduysanız ve yeni sürüme geçiyorsanız (finans alanları,
ödeme şekli, müşteri temsilcisi, dosya notları, **yük miktarı birimi**, **Lojistik
Takip kayıtları**), veritabanına yeni kolonların eklenmesi gerekir.
**Veriler korunur, hiçbir kayıt silinmez.**

Bu sürümde eklenenler:

| Ne | Nerede |
|---|---|
| `shipments.tonnage_unit` | Yük miktarı birimi (ton / kg / metre / m³) — eski dosyalar `ton` olur |
| `closure_entries.status` · `note` · `updated_at` · `updated_by` | Lojistik Takip kayıt durumu ve düzeltme izi |
| `page.logistics` · `logistics.edit` yetkileri | Müdür, Lojistik Uzmanı, Kambiyo Uzmanı, Finans (+ görüntüleme: Havale Yetkilisi, İşlem Kullanıcısı) |
| `closure_entries.net_weight` · `gross_weight` · `meters` · `package_count` | Kapanışta girilen ağırlık ve metraj |
| `note_cc_email` ayarı | Kurumsal bilgi (CC) adresi — varsayılan `kambiyo@iskur.com` |
| `meters_required_products` ayarı | Kapanışta metre zorunlu ürün cinsleri — varsayılan `denim, kumaş, örme, dokuma` |
| `release_statuses` · `limit_statuses` · `payment_methods` · `logistics_statuses` | Süpervizörün düzenlediği durum listeleri (boş başlar, varsayılanlar koddan gelir) |
| `finance_status` = `hold` | Yeni değer: **finans beklemeye aldı**. Kolon zaten VARCHAR, şema değişikliği gerekmez |
| `shipments.approved_by` · `approved_by_email` · `approved_at` · `approval_note` | Sevkiyat onayını kimin verdiği — geri alma yetkisi buna bakar |
| `shipments.stage_updated_by_email` | Aşamayı son değiştiren — geriye alma yetkisi buna bakar |
| `approvals.approved_by_email` | Karar sahibinin e-postası |
| `approval.revoke` · `finance.revoke` · `shipment.reopen` · `shipment.unmerge` · `assignment.revoke` · `stage.revert` yetkileri | Geri alma yetkileri (Müdür, Onay Yetkilisi, Finans, Lojistik, Havale Yetkilisi) |
| `logistics.delete` · `note.delete` · `email.delete` yetkileri | Ekran bazlı silme yetkileri |
| `maintenance.bypass` yetkisi | **Bakım modunda giriş yapabilir** (Müdür'e verilir) |
| `maintenance_mode` · `maintenance_message` · `maintenance_until` ayarları | Bakım modu (kapalı başlar) |
| `block_close_without_delivery` ayarı | **Silinir** — evrak kapanışı artık teslimden bağımsızdır |
| `user_notifications` tablosu | **Kişiye özel bildirim kutusu** (üst çubuktaki zil) — okundu bilgisiyle |
| `email_queue.category` | Gizlilik sınıfı; eski parola/hesap e-postaları konularından tanınıp `security` damgalanır |
| `email.queue.sensitive` yetkisi | **Parola sıfırlama e-postalarını görebilir** (Müdür'e verilir) |
| `mail_instant_send` ayarı | E-posta kuyruğa girer girmez gönderilsin — **açık başlar** |
| `brand_logo_hidden` ayarı | Logo tüm ekranlarda ve e-postalarda gizlensin — **kapalı başlar** |
| `shipments.approval_hold_label` | Sevkiyat onayında "Beklemeye al" / "Diğer" başlığı |
| `shipments.finance_hold_label` | Finans kararında "Diğer" başlığı |
| `approval_status` = `hold` | Yeni değer: **sevkiyat beklemeye alındı**. Kolon zaten VARCHAR, şema değişikliği gerekmez |
| `role_cc_map` ayarı | Role göre CC adresleri — işlem kullanıcısı ve kambiyo uzmanı `kambiyo@iskur.com` ile dolu gelir |
| `activity_log` tablosu | **Denetim İzi** — kim, ne zaman, ne yaptı |
| `shipment_files` tablosu | **Dosya ekleri** (konşimento, fatura, ATR…) |
| `tracking_links` tablosu | **Müşteri takip bağlantıları** (oturumsuz, süreli, iptal edilebilir) |
| `login_codes` tablosu | **İki adımlı doğrulama** kodları (yalnız SHA-256 özeti) |
| `trusted_devices` tablosu | **Güvenilir cihazlar** ("bu cihaza güven") |
| `user_sessions` tablosu | **Açık oturumlar** — kullanıcı uzaktan kapatabilir |
| `team_users.twofactor_enabled` | Kullanıcının iki adımlı doğrulama tercihi |
| `twofactor_policy` · `twofactor_trust_days` ayarları | Doğrulama politikası — **isteğe bağlı** ve **30 gün** ile başlar |
| `digest_enabled` · `digest_recipients` ayarları | Günlük operasyon özeti — **kapalı** başlar |
| `page.activity` · `activity.viewip` · `page.reports` · `file.upload` · `file.delete` · `shipment.copy` · `link.share` yetkileri | Yeni ekran ve işlem yetkileri; güncellemede rollere dağıtılır |
| `periods` tablosu · `period.manage` yetkisi · `active_period` ayarı | **Operasyon dönemi (yıl)** — müdür yeni dönem açar, dosya numarası o yıla göre verilir |
| `program_format` ayarı | Yükleme programı postasının biçimi — **tablo** ile başlar |
| `shipments.merged_moved` | Birleştirmede hedefe taşınan alt kayıtların dökümü; **ayırmada geri alınır** |
| `team_users.on_leave` · `leave_from` · `leave_to` · `leave_note` · `leave_delegate_email` | **İzin ve vekâlet** — izinli kullanıcının dosyaları ekibe açılır; tüm kullanıcılar **izinsiz** başlar |
| `shipment_lines` tablosu | **Yükleme satırları** — bir dosya birden çok yerden, birden çok malla ve her mal için ayrı tonajla yüklenebilir. Mevcut her dosya için satır **kendiliğinden üretilir**; hiçbir dosyanın toplamı değişmez |
| **Eksik indeksler** | `shipments` üzerinde `loading_date`, `created_at`, `loading_place+loading_date`, `consignee`, `carrier`, `mode+status`; ayrıca `closure_entries.status` ve `email_queue.shipment_id` |

> **İndeksler neden ayrı yazıldı.** `CREATE TABLE IF NOT EXISTS` **var olan
> tabloya dokunmaz**: şemaya sonradan eklenen bir indeks yalnız sıfırdan
> kurulan sistemlere gidiyor, çalışan kurulumlar onu hiç almıyordu. Birkaç bin
> dosyadan sonra listeler yavaşlıyor ve kimse nedenini bilmiyordu. Güncelleme
> artık indeksleri de eksikse ekler; ikinci kez çalıştırmak zararsızdır.
> İndeks oluşturulamazsa (paylaşımlı barındırmada yetki yoksa) güncelleme
> **durmaz**: sistem yavaş ama çalışır kalır.

**Yol 1 (önerilen):** dosyaları sunucuya kopyaladıktan sonra tarayıcıdan
`http://siteniz.com/guncelle.php` adresini açın, süpervizör e-posta ve
parolanızı girip **Güncellemeyi çalıştır** deyin. Sayfa yalnız eksik olanları
ekler; iki kez çalıştırsanız da sorun çıkmaz.

**Yol 2:** phpMyAdmin'de veritabanınızı seçip **İçe Aktar** sekmesinden
`sql/guncelleme-guvenli-mysql.sql` dosyasını yükleyin. Bu dosya **defalarca
çalıştırılabilir**: var olan kolon ve tablolara dokunmaz, "Duplicate column name"
hatası vermez, rolleri ve yetkilerini de ekler. İçe aktarma bittiğinde ekranda
bir kontrol tablosu görürsünüz:

| Alan | Beklenen |
|---|---|
| Roller | 9 |
| Rol_Yetkileri | 84 |
| Yukleme_Kolonlari | 3 |
| Eposta_Kolonlari | 3 |
| Parola_Tablosu | 1 |
| Takip_Kolonlari | 11 |

Sayılar bu değerlerden küçükse dosyayı bir kez daha çalıştırın ya da `guncelle.php`
sayfasını kullanın.

> **Şifreli alanlar:** doğrulama kodu, güvenilir cihaz belirteci ve oturum
> kimliği veritabanında **ham hâlde saklanmaz**; yalnız SHA-256 özetleri
> tutulur. Bu tablolardaki kayıtları elle okuyarak kimsenin oturumu ele
> geçirilemez.

Güncellemeden sonra:

1. **Ekip & Yetkiler** ekranından kullanıcılara rollerini verin (Müdür, Kambiyo
   Uzmanı, Lojistik Uzmanı, Müşteri Temsilcisi, Finans…).
2. Gerekiyorsa kişiye özel yetkileri kalkan simgesinden düzenleyin.
3. **Sistem Ayarları → Kurumsal Bilgi (CC) Adresi** bölümünden adresi kontrol edin
   (varsayılan `kambiyo@iskur.com`). Sistemin gönderdiği tüm operasyon bildirimleri
   bu adrese kopyalanır.
4. **Sistem Ayarları → Kurumsal Logo** bölümünden kendi logonuzu yükleyin
   (isteğe bağlı). Bunun için `storage/branding/` klasörünün **yazılabilir** olması
   gerekir; klasör yoksa sistem ilk yüklemede kendisi oluşturur.
5. **Lojistik Takip** ekranını açıp daha önce kapatılmış dosyaların teslimat, SAP ve
   fatura numaralarının listelendiğini doğrulayın. Eski kayıtlar *Kayıt girildi*
   durumuyla gelir.
6. Eski dosyaların yük miktarı **ton** kabul edilir; farklı birimde olanları dosya
   ayrıntı çekmecesindeki *Dosyayı düzenle* bölümünden düzeltebilirsiniz.
7. **Sistem Ayarları → Durum Listeleri** bölümünden kendi durumlarınızı ekleyin
   (örn. "Akreditif teyidi bekleniyor"). Serbest bırakma listesinde **bekletir**
   işaretli durumlar dosyayı beklemeye alır.
8. **Sistem Ayarları → Kapanışta Metre Zorunluluğu** listesini kendi ürün
   adlandırmanıza göre düzenleyin.
9. **Sistem Ayarları → Operasyon Politikaları** ekranında **"E-postaları anında
   gönder"** kutusunun işaretli olduğunu doğrulayın; e-postaların geç ulaşma
   sorunu bu anahtarla çözülür (bkz. bölüm 8).
10. **Ekip & Yetkiler** ekranından, parola sıfırlama e-postalarını görmesi gereken
    kullanıcılara **E-posta → "PAROLA SIFIRLAMA ve hesap güvenliği e-postalarını
    görebilir"** yetkisini verin. Varsayılan olarak yalnız süpervizör ve müdür görür.
11. İsterseniz **Sistem Ayarları → Kurumsal Logo → "Logo görünmesin"** kutusuyla
    logoyu tüm sitede ve e-postalarda kaldırabilirsiniz (bkz. bölüm 9b).
9. **Güncellemeden önce kapatılmış dosyaların** net/brüt kg ve metraj alanları boştur;
   Lojistik Takip'teki **Eksik bilgi** filtresiyle bulup *Düzenle* ile
   tamamlayabilirsiniz. Bu kayıtlar sistemin çalışmasını engellemez.

---

## 4c. Deneme verilerini silme (tek kullanımlık)

Sistem canlıya alınmadan önce deneme dosyalarıyla çalışılır. Gerçek operasyona
geçerken bunlar temizlenir ve **dosya numarası yeniden `0001`'den başlar**.

**Tarayıcıdan:** `https://siteniz/sifirla.php`
**Komut satırından:** `php bin/veri-sifirla.php`

### Neye dokunmaz

| Korunan | Neden |
| --- | --- |
| Kullanıcılar, roller, yetkiler | İnsanları yeniden tanımlamak günler alır |
| Sistem ayarları, SMTP, kurumsal logo, durum listeleri | Yapılandırma emeği kaybolmaz |
| Operasyon dönemleri | Açık dönem ve yılı korunur |

Bu liste `Repo\Reset` içinde bir **güvenlik kilidiyle** korunur: bir kod
değişikliğinde bu tablolardan biri yanlışlıkla silinecek kümelere eklenirse
işlem **hiç başlamaz**.

### Ne silinebilir

Kümeler tek tek seçilir; ilk açılışta **yalnız yükleme dosyaları** işaretlidir:

| Küme | İçerik |
| --- | --- |
| Yükleme dosyaları | Dosyalar, kapanış kayıtları, onaylar, havale geçmişi, notlar, yüklenen belgeler (diskteki dosyalar dâhil), takip bağlantıları |
| E-posta kuyruğu | Gönderilmiş / bekleyen / başarısız postalar (SMTP ayarı kalır) |
| Bildirim kutuları | Zil kutusundaki bildirimler |
| Denetim izi | Kim ne zaman ne yaptı kayıtları |
| Müşteri ve nakliyeci kartları | Cari kart listesi |
| Yükleme yerleri | Tesis listesi ve e-posta adresleri |
| Açık oturumlar | Herkes yeniden giriş yapar (parolalar değişmez) |

### Üç adımlı akış

1. **Süpervizör doğrulaması** — başka hiçbir rol çalıştıramaz.
2. **Sayım ve yedek** — hangi tablodan kaç satır silineceği yazılır; **yedeği
   indir** düğmesi silinecek her satırın JSON dökümünü verir.
3. **Onay sözü** — kutuya tam olarak `VERILERI SIL` yazılmadan silme yapılmaz.

Silme tek bir işlem (transaction) içinde yapılır: yarıda kalırsa **hiçbir şey
silinmez**. Diskteki belgeler veritabanı temizlendikten *sonra* silinir, böylece
işlem geri sarılırsa dosyalar da yerinde kalır.

> **Sıfırlamanın kendisi iz bırakır.** İşlem denetim izine `system.reset`
> türüyle, **silmeden sonra** yazılır. Denetim izi de silinmiş olsa bile bu
> kayıt yeni izin ilk satırı olur: *"burada bir sıfırlama yapıldı"* bilgisi
> hiçbir zaman kaybolmaz.

### Komut satırı

```
php bin/veri-sifirla.php                                    # yalnız SAYAR, silmez
php bin/veri-sifirla.php --kume=operasyon,posta             # başka kümeleri de say
php bin/veri-sifirla.php --kume=hepsi --yedek=yedek.json    # yedeği dosyaya yaz
php bin/veri-sifirla.php --sil --onay="VERILERI SIL"        # SİL
```

Varsayılan davranış **silmemektir**; `--sil` ve doğru onay sözü birlikte
verilmedikçe hiçbir satıra dokunulmaz.

> ⚠ **`sifirla.php` tek kullanımlıktır.** İşiniz bittiğinde bu dosyayı
> sunucudan **silin**. Sunucuda durduğu sürece, süpervizör parolasını bilen biri
> verileri yeniden silebilir. Sayfa işlem sonunda sizi bu konuda uyarır.

> **Sürüm güncellemeleri veri silmez.** `guncelle.php` ve
> `iskur-guncelleme-guvenli.sql` yalnız eksik tablo, kolon ve indeks ekler;
> hiçbir satıra dokunmazlar. Silme **yalnız** bu bölümdeki araçla yapılır.

## 5. Web sunucusu ayarı

### Apache

`public/.htaccess` hazır gelir; tek gereken `mod_rewrite` ve `AllowOverride All`:

```apache
<VirtualHost *:80>
    ServerName lojistik.iskur.com.tr
    DocumentRoot /var/www/iskur/public

    <Directory /var/www/iskur/public>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
```

### Nginx

```nginx
server {
    listen 80;
    server_name lojistik.iskur.com.tr;
    root /var/www/iskur/public;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known) { deny all; }
}
```

---

## 6. İlk giriş

Kurulum sihirbazını kullandıysanız orada belirlediğiniz e-posta ve parolayla girin.

SQL dosyasını elle içe aktardıysanız varsayılan hesap:

| Alan | Değer |
|---|---|
| E-posta | `admin@iskur.com` |
| Parola | `Iskur2026!` |

**İlk girişten sonra parolayı ve e-posta adresini mutlaka değiştirin:** sol alttaki
kullanıcı menüsünden **Hesabım** ekranını açın; ad, giriş e-postanız ve parolanızı
buradan güncelleyebilirsiniz (mevcut parolanızı yazmanız istenir). Böylece süpervizör
hesabı `admin@iskur.com` yerine kendi kurumsal adresinizle çalışır.

Başka bir kullanıcının parolasını sıfırlamak için Ekip & Yetkiler ekranında kullanıcı
satırındaki anahtar simgesini kullanabilir veya kullanıcıdan giriş ekranındaki
**Parolamı unuttum** bağlantısını kullanmasını isteyebilirsiniz.

Parolayı doğrudan veritabanından değiştirmek isterseniz:

```sh
php -r "echo password_hash('YeniParola', PASSWORD_DEFAULT), PHP_EOL;"
```

```sql
UPDATE team_users SET password_hash = '<üretilen-hash>' WHERE email = 'admin@iskur.com';
```

---

## 7. SMTP ayarları

Cloudflare sürümünde canlı ortam doğrudan SMTP bağlantısı açamadığı için ayrı bir
Node.js "relay" servisi gerekiyordu. **PHP sürümünde buna gerek yoktur** — uygulama
SMTP sunucusuna doğrudan bağlanır (`deployment/smtp-relay` klasörü kaldırılmıştır).

1. **E-posta Merkezi → SMTP Sunucu Ayarları** sekmesini açın (süpervizör yetkisi gerekir).
2. Sunucu, port, şifreleme (SSL/465 veya STARTTLS/587), kullanıcı adı, parola ve
   gönderen bilgilerini girin, kaydedin.
3. Sağdaki **Bağlantıyı test et ve gönder** düğmesiyle gerçek bir test e-postası yollayın.

Parola veritabanında AES-256-GCM ile şifreli tutulur; hiçbir ekranda ve API
yanıtında açık biçimde görünmez.

---

## 8. E-posta gönderimi: anında gönderim + cron güvencesi

### E-postalar neden geç ulaşıyordu?

Önceki sürümde e-posta yalnız **kuyruğa yazılıyor**, gerçek gönderim cron'un bir
sonraki turunu bekliyordu. Cron beş dakikada bir çalışıyorsa parola sıfırlama
bağlantısı da beş dakika sonra ulaşıyordu.

### Şimdi nasıl çalışıyor?

**Anında gönderim** varsayılan olarak açıktır: e-posta kuyruğa girer girmez
gönderilir. Gönderim, **sayfa yanıtı tarayıcıya iletildikten sonra** yapılır
(`fastcgi_finish_request`), bu yüzden kullanıcı SMTP'nin 1–3 saniyesini beklemez.
Gönderim başarısız olursa kayıt kuyrukta kalır ve cron yeniden dener —
yani anında gönderim kuyruğun **yerine değil, önüne** eklenmiştir.

Anahtar: **Sistem Ayarları → Operasyon Politikaları → "E-postaları anında gönder"**.
Kapatılırsa sistem eski davranışa döner (yalnız cron gönderir).

### Cron görevi yine de kurulmalıdır

Cron; anında gönderimi başarısız olan kayıtları yeniden dener, sunucu geçici
olarak erişilemezken biriken kuyruğu boşaltır ve **bakım temizliğini** yapar:
süresi dolmuş parola/davet bağlantıları, okunmuş eski bildirimler, eskiyen
denetim kayıtları, süresi geçmiş müşteri takip bağlantıları, kullanılmış
doğrulama kodları ve ölü oturum kayıtları silinir.

```
*/5 * * * * /usr/bin/php /var/www/iskur/bin/kuyruk-gonder.php >> /var/log/iskur-kuyruk.log 2>&1
```

cPanel kullanıyorsanız **Cron Jobs** ekranına aynı komutu ekleyin.

### Cron kuramıyorsanız: iç zamanlayıcı

Paylaşımlı hostinglerin çoğunda cron ya hiç kurulamaz, ya yanlış PHP sürümüyle
kurulur, ya da sunucu taşındıktan sonra unutulur. Sistem bu duruma hazırdır:
**iç zamanlayıcı** aynı işleri web isteklerinin sırtında yapar ve **kurulumda
açık gelir**.

- İş, yanıt tarayıcıya bırakıldıktan **sonra** çalışır; sayfa açılışı yavaşlamaz.
- İstek başına en fazla bir iş yapılır, kilit atomiktir.
- Sunucuda gerçek cron çalışıyorsa zamanlayıcı **kendiliğinden susar**.
- Günlük özet ve otomatik program, **Sistem Ayarları**'nda belirlediğiniz saatten
  sonraki ilk erişimde gönderilir.

Kapatmak için: **Sistem Ayarları → Sistem Sağlığı → İç zamanlayıcı**. Aynı
şeritteki **"Şimdi çalıştır"** düğmeleri, kurulumdan sonra her şeyin çalıştığını
görmenin en kısa yoludur.

> **Yine de cron kurun.** Zamanlayıcı cron'un yerine değil, yokluğuna çözümdür:
> kimsenin siteye girmediği bir sabah günlük özet atlanır.

**Cron kurulup kurulmadığını sistem artık kendisi söyler.** Üç betik de
çalıştığında bir nabız bırakır; **Sistem Ayarları → Sistem Sağlığı** panelinde
"en son ne zaman çalıştı" yazar. Hiç çalışmamışsa panel doğru crontab satırını
kopyalanabilir biçimde verir, ayrıca Kontrol Merkezi'nin üstünde uyarı çıkar.
Kurulumdan sonra bu panele bir kez bakmanız önerilir.

Cron kuramıyorsanız E-posta Merkezi'ndeki **Kuyruğu işle** düğmesi aynı işi elle yapar.
Bir gönderim üç kez başarısız olursa kayıt "başarısız" olarak işaretlenir ve
**Başarısızları yeniden kuyruğa al** düğmesiyle tekrar denenebilir.

> **Not.** Anında gönderim açıkken bile e-postalar geç ulaşıyorsa sorun sistemde
> değil SMTP sunucusundadır: E-posta Merkezi → SMTP Sunucu Ayarları → **Bağlantıyı
> test et** ile yanıt süresini ölçün.

### İlk girişte: operasyon dönemi

Sistem kurulur kurulmaz içinde bulunulan yıl **operasyon dönemi** olarak
kaydedilir ve dosya numaraları ondan üretilir (`ISK-2026-0001`). Yıl dönümünde
**müdür** üst çubuktaki yıl rozetinden *Yeni dönem aç* der; o andan sonra açılan
dosyalar yeni yılın numarasını alır.

Eski bir kurulum güncellendiğinde dosya numaralarında **fiilen geçen yıllar** da
dönem olarak kaydedilir; geçmişe rozetten bakılır. Ayrı bir işlem gerekmez.

### İsteğe bağlı iki cron görevi daha

Bu ikisi zorunlu değildir; ilgili ayar **Sistem Ayarları**'nda açılmadan hiçbir
şey göndermezler.

```
# Günlük operasyon özeti — "bugün neye bakmalı?" e-postası
30 7 * * * /usr/bin/php /var/www/iskur/bin/gunluk-ozet.php >> /var/log/iskur-ozet.log 2>&1

# Otomatik yükleme programı — her tesise kendi programı
30 7 * * * /usr/bin/php /var/www/iskur/bin/program-gonder.php >> /var/log/iskur-program.log 2>&1
```

Yükleme programının saati ve **postanın biçimi** (tablo / madde listesi)
**Sistem Ayarları → Otomatik Yükleme Programı** ekranında belirlenir; cron
satırının oradaki saatle aynı olması gerekir — betik
kendi başına saat beklemez, çalıştırıldığı anda gönderir. Ayarlar ekranı doğru
cron satırını seçilen saate göre yazar, oradan kopyalayabilirsiniz.

Göndermeden denemek için:

```
php bin/gunluk-ozet.php --dene
php bin/program-gonder.php --dene
```

---

## 8b. Kullanıcı daveti ve parola sıfırlama

Bu iki akış e-posta gönderimine bağlıdır; SMTP ayarlarını (bölüm 7) tamamladıktan sonra
çalışır.

**Bağlantı adresi ayarı — önemli.** E-postalardaki bağlantıların doğru adrese gitmesi
için `app/config.php` içindeki `app.url` alanını sitenizin tam adresiyle doldurun:

```php
'url' => 'https://lojistik.siteniz.com',
```

Boş bırakılırsa isteğin geldiği adres kullanılır; cron'dan gönderilen e-postalarda
adres bilinemeyeceği için bu alanın doldurulması önerilir.

**Yeni kullanıcı daveti.** Ekip & Yetkiler → *Kullanıcı ekle* ekranında parola alanını
boş bırakabilirsiniz. "Davet e-postası gönder" işaretliyse:

* kullanıcıya logolu bir hoş geldiniz e-postası gider, içindeki **tek kullanımlık**
  bağlantıyla (72 saat geçerli) kendi parolasını belirler;
* süpervizör ve müdürlere *"Yeni kullanıcı eklendi"* bildirimi gönderilir.

**Parolamı unuttum.** Giriş ekranındaki bağlantı `/parola-sifirla` adresini açar.
Kullanıcı e-posta adresini yazar, tek kullanımlık ve **2 saat** geçerli bir bağlantı
gönderilir; bağlantı yeni parola ekranını açar ve kullanıldıktan sonra geçersiz olur.
Güvenlik gereği sistemde kayıtlı olmayan adresler için de aynı başarı mesajı gösterilir.

SMTP henüz ayarlı değilse bu e-postalar kuyruğa alınır; ayarları tamamladıktan sonra
**Kuyruğu işle** ile gönderilir.

---

## 9. Roller ve yetkiler

Sistem **rol + kişiye özel yetki** mantığıyla çalışır. Her rolün hangi ekranları
görebileceği ve neler yapabileceği **Ekip & Yetkiler** ekranından tik işaretleriyle
belirlenir. Kurulumla gelen roller:

| Rol | Varsayılan yetkiler |
|---|---|
| **Süpervizör** | Her şey. Rol şeması, SMTP ayarları, kullanıcı silme ve sistem ayarları yalnız bu roldedir ve hiçbir role devredilemez. |
| **Müdür** | Tüm operasyon ekranları, kullanıcı ekleme ve çalışanlara özel yetki verme. Sistem ayarlarına ve SMTP'ye giremez; süpervizör hesaplarını listede göremez ve süpervizör rolü atayamaz. |
| **Havale Yetkilisi** | Dosya açar, havale eder, kapatır |
| **Onay Yetkilisi** | Sevkiyat onayı verir/reddeder |
| **Finans** | Finans onayı, limit durumu ve ödeme şeklini düzenler; **Lojistik Takip** kayıtlarını görür ve düzenler |
| **Kambiyo Uzmanı** | Finans alanları, dosya izleme, e-posta gönderimi ve **Lojistik Takip** kayıtları |
| **Lojistik Uzmanı** | Dosya açma, yük takibi, kapanış ve **Lojistik Takip** kayıtları |
| **Müşteri Temsilcisi** | Dosyalara atanır; not bildirimleri bu kişiye gider |
| **İşlem Kullanıcısı** | Yalnız kendisine havale edilen dosyaları yürütür; Lojistik Takip'i görüntüleyebilir |

**Yeni rol ekleme:** Ekip & Yetkiler → *Roller ve Sayfa Yetkileri* → **+ Rol ekle**
(yalnız süpervizör). Rol adını yazıp hangi ekranlara ve işlemlere yetkisi olacağını
işaretlemeniz yeterlidir.

**Kişiye özel yetki:** Kullanıcı satırındaki kalkan simgesi, o kişiye rolünün dışında
yetki eklemenizi veya rolünden gelen bir yetkiyi kaldırmanızı sağlar. Müdür de
çalışanlarına bu şekilde yetki verebilir; ancak süpervizör hesaplarını ve süpervizöre
özel yetkileri göremez.

**E-posta kuyruğu:** Her kullanıcı yalnız kendisine gelen ve kendi işlemlerinden doğan
e-postaları görür. Tümünü görmek için *Tüm gönderim kuyruğunu görebilir* yetkisi gerekir.

Yetki denetimi hem arayüzde hem de sunucu tarafındaki her API çağrısında uygulanır;
ekran gizlemekle yetinilmez.

---

## 9a-2. İzin ve vekâlet

Bir kullanıcı izne çıktığında dosyaları onunla birlikte "izne çıkmasın" diye,
**Ekip & Yetkiler** ekranındaki **İZİN** sütunundan işaretlenir.

**Nasıl yapılır:** kişinin satırındaki **İzin** düğmesine basın → *Bu kullanıcı izinli*
kutusunu işaretleyin → başlangıç/bitiş tarihini ve (isterseniz) vekili seçin → **Kaydet**.

| Seçim | Sonuç |
|---|---|
| **Vekil seçilirse** | Dosyalar **yalnız o kişiye** açılır; sorumluluk tek kişide kalır |
| **Vekil boş bırakılırsa** | Dosyalar **aynı roldeki** herkese açılır (işlem kullanıcısının dosyaları öbür işlem kullanıcılarına) |
| **Tarih girilirse** | Erişim **yalnız o aralıkta** açıktır; işaret kaldırılmasa da izin kendiliğinden biter |
| **Tarih girilmezse** | İşaret kaldırılana kadar sürer |

**Dosyanın sahibi değişmez.** Havale olduğu gibi durur, vekâleten bakılan satırlarda
**Vekâleten** rozeti görünür ve işaret kaldırıldığında erişim kendiliğinden kapanır.
Rolün işi rolde kalır: başka roldeki bir kullanıcıya bu yolla dosya açılmaz.

**Kim verir:** ekip yönetme yetkisi olan kullanıcılar (Müdür, Süpervizör), yalnız
yönetebildikleri kişiler için. Kullanıcı kendisine vekâlet veremez; vekil ekipte
tanımlı ve etkin olmalıdır. Her değişiklik **Denetim İzi**'ne `user.leave` türüyle
yazılır.

**Güncellemede:** `team_users` tablosuna `on_leave`, `leave_from`, `leave_to`,
`leave_note`, `leave_delegate_email` kolonları eklenir; mevcut kullanıcıların hepsi
**izinsiz** başlar, hiçbir erişim kendiliğinden açılmaz.

---

## 9a-3. Menü sayaçları

Kenar çubuğundaki rozetler (Yüklemeler 14, Onaylar 3…) **oturumdaki kullanıcının o
ekranı açtığında göreceği satır sayısıdır**; sistemdeki toplam değildir. Bir işlem
kullanıcısı "Yüklemeler 12" görüp ekranda 3 dosya bulmaz.

Ayar gerektirmez: sayım, ekranın kullandığı görünürlük kapsamının aynısıyla ve
`COUNT(*)` sorgularıyla yapılır. Tek istisna **Havale Merkezi**'dir — oradaki sayı
"henüz kimseye havale edilmemiş dosya" demektir ve zaten kapsam dışıdır.

---

## 9a-4. Yükleme satırları

Bir dosya tek bir yerden, tek bir malla ve tek bir tonajla yüklenmek zorunda
değildir. Yükleme ayrıntısı **satır satır** girilir:

| Gün | Yükleme yeri | Mal tanımı | Tonaj |
|---|---|---|---|
| 12.10.2026 | İSKUR-1 | %100 pamuk denim | 18 ton |
| 12.10.2026 | İSKUR-1 | Astar kumaş | 4 ton |
| 14.10.2026 | İSKUR-2 | Pamuk iplik | 6 ton |

**Ayar gerektirmez.** Dosyadaki yükleme yeri, ürün cinsi, mal tanımı, toplam
tonaj ve yükleme günü artık bu satırlardan **hesaplanır**; elle girilmez, bu
yüzden toplamla satırlar çelişemez. Dosya geneli kalan üç şey **ölçü birimi**
(ton/kg/metre/m³), **yük tipi** (konteyner/tır/parsiyel) ve **araç / konteyner
sayısı**'dır. Araç sayısı satırda tutulmaz: bir aracın içinde birkaç ayrı mal
bulunabilir, satır başına araç yazmak aracı iki kez saymak olurdu. Sayı
pencerenin üst kısmında dosyanın tamamı için bir kez girilir.

Bir dosyada en az **1**, en çok **60** satır bulunabilir. Var olan satırların
tamamı düzenlemeyle silinemez: yükleme bilgisini tümüyle kaldırmak yerine
ilgili satır düzeltilir. (Dosya, bilgisi henüz yokken **satırsız açılabilir**;
o zaman eksik listesinde görünür.)

**Yükleme programı:** tesise gönderilen programda her satır ayrı satırdır ve
kendi tonajını taşır; yer süzgeci satıra bakar, yani İSKUR-1'in kâğıdında
İSKUR-2'nin yükü görünmez.

**Güncellemede ne olur:**

1. `shipment_lines` tablosu oluşturulur (boş).
2. Satırı olmayan **her dosya için** kendi alanlarından satır üretilir. Çok günlü
   dosyalarda her gün kendi satırına açılır ve **tonaj yalnız ilk satıra** yazılır.
3. Böylece **hiçbir dosyanın toplamı değişmez**; raporlardaki rakamlar aynı kalır.

İkinci kez çalıştırmak zararsızdır: yalnız hiç satırı olmayan dosyalara dokunulur,
kullanıcının girdiği satırlar asla ezilmez.

> **SQL ile güncelleyenler — bu adımı atlamayın.** `iskur-guncelleme-guvenli.sql`
> tabloyu kurar ve her dosya için **tek** satır üretir; saf SQL, `loading_plan`
> içindeki JSON diziyi açamaz. Bu yüzden SQL'i çalıştırdıktan sonra **bir kez
> `guncelle.php`'yi açın**: güncelleme, tek satırı olup planı birden çok gün
> taşıyan dosyaları bulur ve satırlarını plandan tamamlar.
>
> Bu adım atlanırsa dosya ekranlarda doğru görünmeyi sürdürür, ama kullanıcı
> dosyayı ilk kez düzenlediğinde başlık tek satırdan yeniden hesaplanır ve
> **ikinci yükleme günü sessizce kaybolur.**

---

## 9b. Kurumsal logo (yalnız süpervizör)

**Sistem Ayarları → Kurumsal Logo** bölümünde iki yuva vardır:

| Yuva | Nerede kullanılır | Öneri |
|---|---|---|
| **Koyu zemin logosu** | Sol menü, giriş ekranı, e-posta başlığı | Saydam arka planlı, **açık renk yazılı** PNG veya SVG |
| **Açık zemin logosu** | Haftalık operasyon yazdırma çıktısı | Özgün renkli PNG veya SVG |

- Kabul edilen türler: **PNG, JPG, WEBP, SVG** · en fazla **2 MB**.
- Dosyanın türü uzantıdan değil **içeriğinden** doğrulanır; sahte uzantılı dosyalar reddedilir.
- Yüklenen dosya web'den doğrudan erişilemeyen **`storage/branding/`** klasörüne yazılır ve
  `/marka/light` · `/marka/dark` adreslerinden sunulur. Bu klasörün **yazılabilir** olması
  gerekir (`chmod 775 storage`); klasör yoksa sistem ilk yüklemede kendisi oluşturur.
- Değişiklik kaydedildiği anda **tüm ekranlarda, çıktıda ve e-postalarda** geçerli olur.
- **Varsayılana dön** düğmesi pakete gömülü logoya geri alır ve yüklenen dosyayı siler.
- Logo değişikliği CSRF ile korunur ve yalnız süpervizör yapabilir.

### "Logo görünmesin" anahtarı

Aynı panelin üstündeki **Logo görünmesin** kutusu işaretlendiğinde logo
**tüm sitede ve gönderilen e-postalarda** gizlenir; yerine yazı biçiminde
**İSKUR LOJİSTİK** görünür.

| Etkilenen yer | Sonuç |
|---|---|
| Sol menü, giriş ekranı, parola ekranı, bakım ekranı | Yazı biçiminde kurum adı |
| Haftalık operasyon yazdırma çıktısı | Yazı biçiminde kurum adı (koyu renk) |
| **Gönderilen e-postaların başlığı** | Logo gömülmez; yazı biçiminde kurum adı |

- Yüklediğiniz logo dosyası **silinmez**; kutuyu kaldırdığınızda aynı logo geri gelir.
- Değişiklik anında geçerlidir; tarayıcı eski logoyu gösteriyorsa Ctrl+F5 yapın.

---

## 9c. Lojistik Takip

Evrak kapanışında girilen **teslimat, nakliye, SAP ve fatura numaraları** bu ekranda
dosyadan bağımsız olarak izlenir.

- Kayıtlar kapanışla **kendiliğinden** oluşur; ayrıca veri girişi gerekmez.
- Kapanışta numaraların yanında **net kg**, **brüt kg**, **metre** ve **kap adedi**
  girilir. Net ve brüt zorunludur, brüt netten küçük olamaz. **Dara** otomatik
  hesaplanır. Ekranın üstünde toplam net, brüt ve metraj görünür.
- **Denim ve kumaş çıkışlarında metre zorunludur**; ürün cinsi listesi Sistem
  Ayarları → *Kapanışta Metre Zorunluluğu* bölümünden düzenlenir. Ölçü birimi
  "metre" olan dosyalarda metre zaten zorunludur.
- Her kaydın kendi durumu vardır: *Kayıt girildi → Fatura kesildi → SAP'a işlendi →
  Tahsil edildi → Kapandı*. Bu akış dosyanın durumundan ayrıdır.
- Yanlış girilen numara **Düzenle** ile düzeltilir; değişiklik kimin yaptığıyla
  saklanır ve dosyanın not akışına sistem kaydı olarak düşer.
- **Eksik bilgi** filtresi teslimat/SAP/fatura numarası boş olan kayıtları,
  **Teslim bekleyen** filtresi malı henüz teslim edilmemiş kayıtları listeler.
- Arama ve filtre seçiminiz **CSV çıktısına** birebir yansır.
- Yetkiler: ekran `page.logistics`, düzenleme `logistics.edit`.

---

## 9c-3. Yük miktarı: plan ve gerçekleşen

Dosya açılırken girilen **yük miktarı** bir tahmindir. Fatura ve gümrük ise
evrak kapanışında girilen **net kilogram** üzerinden yürür.

- Kapanışta her teslimat satırı için **net kg ve brüt kg zorunludur**;
  metraj bazı ürün cinslerinde zorunlu, kap adedi opsiyoneldir.
- Kapanıştan sonra dosya ayrıntısında, müşteri takip sayfasında,
  e-postalarda ve raporlarda **gerçekleşen ölçü** görünür; plan rakamı
  karşılaştırma için altta kalır.
- Kapanış penceresinde satırlar doldukça **canlı toplam** ve **plana göre
  sapma** hesaplanır. Sapma ±%20'yi aşarsa kutu kırmızı yanar — genellikle
  bir hane fazla ya da eksik basılmıştır.
- Plan **metre** veya **m³** ile girilmişse kilogram karşılaştırması
  yapılmaz; ekran bunu yazar, uydurma bir yüzde göstermez.

Ek bir kurulum adımı gerekmez: veriler zaten `closure_entries` tablosunda
tutuluyordu, bu sürümde ekranlara taşındı.

---

## 9c-2. Finans onayı, beklemeye alma ve durum listeleri

Finans ekranında **üç karar** vardır: *Onayı kaydet*, *Beklemeye al*, *Reddet*.

- **Bekleten durumlar.** Serbest bırakma listesinde "bekletir" işaretli bir durum
  (*Ödeme bekleniyor*, *Bankalı · vesaik bankada*…) seçilirse finans "onayla" dese bile
  dosya **beklemeye alınır**: onay kaydedilir ama yük durumu değiştirilemez.
- **Serbest bırakma.** Koşul gerçekleştiğinde durum bekletmeyen bir seçenekle
  güncellenir; Finans Onayı ekranındaki *Beklemedeki dosyalar* bölümünde her satırın
  yanında **Serbest bırak** düğmesi vardır.
- **Sevkiyat onayı verilmiş dosyalar** finans kaydını beklemeden ilerleyebilir;
  beklemeye alma bunu da durdurur.
- **Durum listeleri Sistem Ayarları'ndan yönetilir** (yalnız süpervizör): dört
  katalog (serbest bırakma, limit, ödeme şekli, lojistik takip), her durumun adı,
  rozet rengi ve — serbest bırakmada — "bekletir" işareti. Paketle gelen durumlar
  silinemez; kendi eklediğiniz durumlar silinebilir. Her satırda o durumu kaç kaydın
  kullandığı yazar.

---

## 9d. Yük miktarı birimi

Yeni dosya formundaki **Yük miktarı** alanı miktar + birim olarak girilir:
**ton · kg · metre · m³**.

- Miktar tüm ekranlarda, e-postalarda ve çıktılarda kendi birimiyle yazılır.
- **Birimi farklı dosyalar toplanmaz**; haftalık ve panel toplamları
  *"29,5 ton · 2.275 metre"* biçiminde birim birim gösterilir.
- **Birimi farklı iki dosya birleştirilemez**; sistem hangi birimlerin çakıştığını söyler.
- Güncellemeden gelen eski dosyalar **ton** kabul edilir; birim dosya ayrıntı
  çekmecesindeki *Dosyayı düzenle* bölümünden değiştirilebilir.

---

## 9e. Bakım modu

Sürüm geçişi veya veri düzeltmesi sırasında sistemi kullanıcılara kapatın:
**Sistem Ayarları → Bakım Modu** (yalnız süpervizör).

1. Kullanıcılara gösterilecek **mesajı** ve **tahmini bitişi** yazın.
2. **Bakım modunu aç** deyin; onay istenir.
3. Bakım bitince aynı düğmeyle **Bakımı bitir ve sistemi aç** deyin.

Bakım modunda:

- **Süpervizör her zaman** girebilir. Diğerleri için Ekip & Yetkiler > kalkan
  simgesinden **"Bakım modunda giriş yapabilir"** yetkisi verilmelidir; panel o anda
  girebilecek kullanıcıları listeler.
- Bakımın açılıp kapanması sistemsel bildirim üretir.

### Bakımda ziyaretçi ne görür?

| Kim | Ne olur |
|---|---|
| **Oturumsuz ziyaretçi** | Siteye girdiğinde doğrudan **"Bakım modundayız"** ekranını görür; giriş formu açılmaz. Ekranda **"Yönetici girişi"** düğmesi vardır. |
| **Yetkili kullanıcı** | "Yönetici girişi" düğmesinden giriş yapar ve sisteme normal biçimde girer. |
| **Yetkisiz kullanıcı** | Kullanıcı adı ve parolası doğru olsa bile girişi kabul edilmez; bakım ekranına döner ve şu uyarıyı görür: *"Giriş kabul edilmedi — bakım modu yetkiniz bulunmamakta olup bakım modu bitiminde giriş yapabilirsiniz."* |
| **Oturumu zaten açık yetkisiz kullanıcı** | Bakım ekranına düşer; **oturumu kapatılmaz**, bakım bitince kaldığı yerden devam eder. |

Bakım ekranında tahmini bitiş, başlangıç zamanı ve bakımı yürüten kişi de
yazar. Sayfa 60 saniyede bir kendini yeniler; bakım bitince ziyaretçi
kendiliğinden sisteme (veya giriş ekranına) döner.

> **Dikkat:** Bakım modunu açmadan önce en az bir yedek yetkili kullanıcıya
> `maintenance.bypass` yetkisi vermeniz önerilir. Süpervizör hesabına
> erişemezseniz bakımı veritabanından kapatmanız gerekir:
> `UPDATE app_settings SET setting_value = '0' WHERE setting_key = 'maintenance_mode';`

---

## 9f. Geri alma ve silme yetkileri

Verilmiş bir karar geri alınabilir. Kural basittir: **kararı veren kişi kendi
kararını her zaman geri alabilir**; başkasınınkini geri almak için ilgili yetki
gerekir. Süpervizör her zaman yetkilidir.

| Yetki | Ne yapar | Varsayılan roller |
|---|---|---|
| `approval.revoke` | Sevkiyat onayını geri alır | Müdür, Onay Yetkilisi |
| `finance.revoke` | Finans kararını geri alır | Müdür, Finans |
| `shipment.reopen` | Kapatılmış dosyayı yeniden açar | Müdür, Lojistik Uzmanı |
| `shipment.unmerge` | Birleştirmeyi geri alır | Müdür |
| `assignment.revoke` | Havaleyi geri alır | Müdür, Havale Yetkilisi |
| `stage.revert` | Yük durumunu geriye alır | Müdür, Lojistik Uzmanı |
| `shipment.delete` | Dosyayı siler | Müdür |
| `logistics.delete` | Lojistik Takip kaydı siler | Müdür, Lojistik Uzmanı |
| `note.delete` | Dosya notu siler | Müdür |
| `email.delete` | E-posta kuyruğu kaydı siler | Müdür |

Her geri almada **gerekçe zorunludur**; dosyanın not akışına yazılır, ilgililere
e-posta gider ve sistemsel bildirim üretilir.

---

## 9f-2. Karar verme, düzenleme ve silme

### Dört seçenekli karar

Sevkiyat ve finans onayında karar dört biçimde verilir:

| Seçenek | Sonuç |
|---|---|
| **Onayla** | Dosya ilerler; yüklemeye ve kapanışa hazır olur |
| **Beklemeye al** | Dosya durur: yük durumu ilerletilemez, kapatılamaz |
| **Reddet** | Dosya reddedilenler listesine düşer |
| **Diğer** | Beklemeye alır, ama **başlığını siz yazarsınız** (örn. "Numune onayı bekleniyor") |

Bekletme ve ret için **gerekçe zorunludur**. Beklemeye alınan dosyalar
Sevkiyat Onayları ekranında **"Beklemedeki Dosyalar"** bölümünde ayrıca
listelenir.

### Düzenle / Sil

Eski **"Kararı geri al"** düğmesi kaldırıldı; yerine iki açık işlem geldi:

| Düğme | Ne yapar |
|---|---|
| **Düzenle** | Kararı başka bir duruma çevirir (onay → beklemede, beklemede → onay, ret → onay…). Karar silinmez, iş akışı yeni duruma göre yeniden işler. Finans kararında serbest bırakma, limit ve ödeme şekli de aynı pencereden güncellenir. |
| **Sil** | Kararı kaldırır; dosya **doğrudan onay kuyruğuna** döner ve yeniden karar bekler. |

- İkisi de aynı yetkiye bakar: kararı **veren kişi** kendi kararına her zaman
  dokunabilir; başkasınınki için `approval.revoke` / `finance.revoke` gerekir.
- Her ikisinde de **gerekçe zorunludur**; değişiklik dosyanın not akışına
  sistem kaydı olarak düşer, ilgililere e-posta gider, bildirim kutusuna işlenir.
- **Karar geçmişi silinmez**: `approvals` tablosundaki tüm kararlar durur.
- Mal yola çıkmışsa karar ne düzenlenebilir ne silinebilir; önce Yük Takibi
  ekranından aşama geriye alınır.

---

## 9g. Bildirim kutusu ve profil menüsü

### Üst çubuk

Kullanıcı girişinden sonra sağ üst köşede soldan sağa üç öğe bulunur:

| Sıra | Öğe | İşlevi |
|---|---|---|
| 1 | **Yeni Yükleme** | Dosya açma penceresi (yalnız `shipment.create` yetkisi olanlarda) |
| 2 | **Bildirimler (zil)** | Bildirim kutusu; okunmamış sayısı turuncu rozetle |
| 3 | **Profil** | Tıklanınca **Hesabım** ve **Çıkış yap** menüsü |

Profil bloğu artık sol menünün altında değildir.

### Bildirim kutusu

Zil paneli iki bölümdür:

* **SİZE ÖZEL** — kişiye özel bildirimler. Okunmamış olanlar turuncu zeminle ve
  noktayla işaretlidir. Bildirime **tıklandığında okundu** olur ve kullanıcı ilgili
  ekrana gider. "Tümünü okundu yap" düğmesi rozeti tek işlemde sıfırlar.
* **OPERASYON UYARILARI** — herkese aynı görünen ekran uyarıları.

Hangi olaylar kişisel bildirim üretir:

| Olay | Kime düşer |
|---|---|
| **Dosyaya not eklendi** | Notu müşteri temsilcisi yazdıysa **dosya ilgilisine**, başkası yazdıysa **müşteri temsilcisine** |
| **Dosya havale edildi / devredildi** | Yeni ilgiliye; devirde ayrıca eski ilgiliye |
| **Sevkiyat onaylandı / reddedildi** | Dosya ilgilisine |
| **Finans kararı verildi / beklemeye alındı** | Dosya ilgilisine, müşteri temsilcisine ve havaleyi yapana |
| **Bir karar geri alındı** | Aynı taraflara |

- Bildirim, işlemi yapan kişinin **kendisine düşmez**.
- Kayıtlar kullanıcının **e-posta adresine** bağlanır; kullanıcı kaydı silinip
  yeniden açılsa bile bildirimler doğru kişiye gider.
- Bir kullanıcı yalnız **kendi** bildirimini okuyabilir, okundu yapabilir, silebilir.
- Aynı olay için ayrıca **e-posta** gider ve kopyası `kambiyo@iskur.com` adresine düşer.
- Okunmuş ve 60 günden eski bildirimler cron görevinde otomatik temizlenir.

---

## 9g-2. Gönderim kuyruğunda arama

E-posta Merkezi'ndeki kuyruk bir **gönderim defteridir**. "Bu dosya için
müşteriye mail gitmiş mi?" sorusunu buradan yanıtlarsınız.

| Ne yazarsanız | Ne bulunur |
|---|---|
| `ISK-2026-0016` | O dosyaya ait bütün gönderimler |
| `JORDANIAN` | O müşteriye ait bütün gönderimler |
| `BLG26-0061` | O siparişe ait gönderimler |
| `bilge@iskur.com` | O kişiye giden gönderimler |
| Konudan veya metinden bir söz | İçinde geçen gönderimler |

Arama konu, alıcı, CC, gönderen, **e-postanın gövdesi**, dosya referansı,
müşteri adı ve sipariş numaralarında birlikte yapılır.

- Üstteki **durum şeridi** hem sayaç hem süzgeçtir: *Kuyrukta* sayısına
  tıklarsanız yalnız bekleyen gönderimler listelenir.
- **Süpervizör kuyruğun tamamını görür** — parola sıfırlama kayıtları dâhil.
  Diğer kullanıcılar yetkilerine göre sınırlı görür.
- Sayaçlar arka planda tazelenir; yeni kayıt geldiğinde listenin üstünde
  "Kuyrukta yeni hareket var" şeridi çıkar.

---

## 9h. Role göre bilgi (CC) adresleri

**Sistem Ayarları → Role Göre Bilgi (CC) Adresleri** (yalnız süpervizör).

Kurumsal CC adresinin (`kambiyo@iskur.com`) **üzerine** çalışan ikinci bir
katmandır. Kural: e-postayı **tetikleyen kullanıcının rolüne** bakılır.

**Örnek.** *Kambiyo Uzmanı* satırına `kambiyo@iskur.com` yazarsanız, kambiyo
uzmanının yaptığı **her işlem** o adrese de düşer:

* dosya kapanışı ve kapanışta girilen teslimat / SAP / nakliye / fatura numaraları,
* yük durumu değişiklikleri,
* dosya notları,
* havale ve devir işlemleri,
* verdiği kararlar.

- Kurulumda **İşlem Kullanıcısı** ve **Kambiyo Uzmanı** rolleri
  `kambiyo@iskur.com` adresiyle dolu gelir.
- Bir role birden fazla adres yazabilirsiniz (virgülle ayırın).
- Boş bıraktığınız rol için **ek kopya gönderilmez**; kurumsal CC her koşulda geçerlidir.
- Adres zaten alıcıda veya CC'de varsa ikinci kez eklenmez.
- Parola sıfırlama ve hesap güvenliği e-postaları bu kapsamın **dışındadır**
  (kişiye özel bağlantı taşırlar).

---

## 9i. Eksik bilgiyle dosya açma ve eksiklik kapısı

Dosya artık **yalnız ALICI (müşteri)** girilerek kaydedilebilir. Ayrı bir ayar
yoktur; davranış sistemde yerleşiktir.

**Kaydederken.** Eksik varsa dosya yine de kaydedilir ve *"Dosya kaydedildi —
eksik bilgi var"* penceresi açılır. Pencere eksikleri tek tek yazar, altında
yalnız o alanları içeren bir form açar. Kullanıcı doldurur ya da *"Sonra
tamamlarım"* der. Dosyanın not akışına eksik listesiyle bir sistem kaydı düşer.

**Havale ederken.** Eksik dosya havale **edilemez**. Havale penceresi
*"Eksiklik işlemini gideriniz"* der, eksikleri yazar ve aynı pencereden
doldurtur; kaydedildiğinde havale kaldığı yerden sürer.

**İki istisna:**

1. Dosya açılırken denetim yapılmaz (yalnız uyarı).
2. **Devirde** denetim yapılmaz: ilgilisi olan bir dosya başka bir kullanıcıya
   geçerken eksikliğe bakılmaz. Yürüyen dosya, izne çıkan kullanıcının üstünde
   kilitli kalmamalıdır.

**Listelerde.** Eksik dosyalar Yüklemeler tablosunda ve Havale Merkezi
kartlarında `N eksik` rozetiyle görünür. **Rozete tıklayınca** tamamlama
penceresi açılır.

**Kurulum notu.** Bu davranış için `shipments` tablosunda iki kolonun NOT NULL
kısıtı gevşetilmiştir (`loading_date` NULL olabilir, `mode` varsayılanı boş
dizge). Yeni kurulumda şema zaten böyledir; **mevcut kurulumda**
`sql/guncelleme-guvenli-mysql.sql` dosyasını çalıştırmanız yeterlidir; dosya
kolonları yalnız gerekiyorsa değiştirir ve defalarca çalıştırılabilir.

**Aranan alanlar:** alıcı, çıkış firması, ürün cinsi, sipariş numarası,
nakliyeci, yükleme yeri, teslim yeri, yükleme günü, yük miktarı, araç/konteyner
adedi (parsiyelde aranmaz), müşteri temsilcisi (ad **ve e-posta**), taşıma modu;
denizyolunda ayrıca talimat ve beyanname cut-off, havayolunda beyanname cut-off.

Ekipte müşteri temsilcisi ya da Sistem Ayarları'nda çıkış firması tanımlı
değilse pencere o alanları **serbest yazıma** açar; boş bir liste gösterip
kullanıcıyı çıkmaza sokmaz.

---

## 9j. Komut paleti ve kısayollar

Ayar gerektirmez; kurulumla birlikte gelir.

* <kbd>Ctrl</kbd>+<kbd>K</kbd> (macOS: <kbd>⌘</kbd>+<kbd>K</kbd>) komut paletini açar.
* Palet **yetkiye göre** doldurulur: kullanıcının açamadığı ekran ve
  yapamadığı eylem listede görünmez. Ayrı bir yetki tanımlamanız gerekmez.
* <kbd>/</kbd> aramaya odaklanır, <kbd>Y</kbd> yeni yükleme açar,
  <kbd>B</kbd> bildirimleri gösterir, <kbd>?</kbd> kısayol listesini açar.
* Arama Türkçe harfleri sadeleştirir; "sisli" yazan ŞİŞLİ'yi bulur. Bu
  dönüşüm SQL tarafında yapıldığı için MySQL ve SQLite'ta aynı çalışır ve
  ek bir kolon ya da dizin gerektirmez.

---

## 9j. Modüller ve süpervizör görünürlüğü

**Modüller (Sistem Ayarları → Modüller, yalnız süpervizör).** Kurumda
kullanılmayan ekranlar sistem genelinde kapatılabilir. Kapalı ekran menüde hiç
görünmez ve adresi yazılsa da açılmaz; **yetkiyle ilgisi yoktur**, yetkisi
olanlarda bile kapalıdır. Süpervizör kapalı ekranı görmeye devam eder (menüde
`kapalı` rozetiyle), yoksa geri açamazdı. Kontrol Merkezi, Sistem Ayarları,
Hesabım ve Yardım kapatılamaz. **Veri silinmez**; ekran geri açıldığında her şey
yerindedir. Yeni bir tablo ya da kolon gerektirmez — tek ayar anahtarıdır
(`modules_disabled`).

**Süpervizörün işlemleri.** Süpervizörün adı zaten her ekranda "Süpervizör",
e-postalarda "Admin" olarak görünür. Bu sürümle birlikte **işlemleri de**
diğer kullanıcılara görünmez: denetim izi kayıtları, dosya notları, zaman
çizelgesi satırları, havale geçmişi ve gönderim kuyruğu kayıtları süzülür.
İşin **sonucu** gizlenmez (onaylı dosya onaylı görünür); gizlenen, "kim ne yaptı"
izidir. Bu davranış koda gömülüdür, ayrı bir ayarı yoktur.

## 9k. Denetim İzi, Raporlar ve Dosya Ekleri

**Yeni tablolar.** Bu sürümle iki tablo gelir: `activity_log` (denetim izi) ve
`shipment_files` (dosya ekleri künyesi). Mevcut kurulumda
`sql/guncelleme-guvenli-mysql.sql` çalıştırılması yeterlidir; dosya kolonlara
ya da verilere dokunmaz, yalnız eksik olanı ekler.

**Klasör izni (ÖNEMLİ).** Belgeler `storage/uploads` altında tutulur. Bu klasöre
**yazma izni** verilmelidir (genellikle `755`, bazı sunucularda `775`):

```bash
mkdir -p storage/uploads
chmod 775 storage/uploads
```

**Belge Merkezi.** Bu sürümle birlikte belgelerin dosyadan bağımsız listesi
(`/belgeler`) gelir. Yeni tablo ya da kolon GEREKTİRMEZ — aynı `shipment_files`
tablosunu okur. Yalnız yeni bir ekran yetkisi (`page.documents`) eklenir;
`guncelleme-guvenli-mysql.sql` ve kurulum betiği bunu ilgili rollere kendiliğinden
dağıtır, elle bir işlem gerekmez. Yetkiyi başka bir role vermek isterseniz
**Ekip & Yetkiler → Ekran erişimleri → Belge Merkezi** kutusunu işaretleyin.

Klasör yoksa sistem kendisi oluşturur ve içine `Deny from all` taşıyan bir
`.htaccess` yazar. Nginx kullanıyorsanız `storage/` dizinine dışarıdan erişimi
ayrıca kapatın:

```nginx
location ^~ /storage/ { deny all; return 404; }
```

**PHP yükleme sınırları.** Belge başına 20 MB sınırı vardır; sunucunun da izin
vermesi gerekir:

```ini
upload_max_filesize = 20M
post_max_size       = 24M
```

**Yeni yetkiler** güncellemeyle birlikte MÜDÜR rolüne ve ilgili operasyon
rollerine dağıtılır: `page.activity`, `activity.viewip`, `page.reports`,
`file.upload`, `file.delete`, `shipment.copy`. Süpervizör her zaman tam
yetkilidir. Dağılımı Ekip & Yetkiler ekranından değiştirebilirsiniz.

**Denetim izi ne kadar saklanır?** 18 ay. Kayıtlar ekrandan silinemez;
eskiyenleri temizlemek için `Repo\Activity::prune()` çağrılır (istenirse
zamanlanmış görevle).

---

## 9l. Günlük operasyon özeti (zamanlanmış görev)

Her sabah "bugün neye bakmalı?" e-postası gönderir. **Kurulumda kapalıdır.**

1. **Sistem Ayarları → Günlük Operasyon Özeti** bölümünden açın ve alıcı
   adreslerini yazın (virgülle ayırın).
2. Sunucuda zamanlanmış görevi tanımlayın:

```
30 7 * * * /usr/bin/php /var/www/iskur-php/bin/gunluk-ozet.php >> /var/log/iskur-ozet.log 2>&1
```

cPanel kullanıyorsanız **Cron Jobs** ekranına aynı komutu girin.

**Denemek için:**

```bash
php bin/gunluk-ozet.php --dene    # hiçbir şey göndermez, ne gideceğini yazar
php bin/gunluk-ozet.php --zorla   # ayar kapalı olsa bile gönderir
```

Ayarlar ekranındaki **"Şimdi bir deneme gönder"** düğmesi de aynı işi yapar.

Özet, hiçbir başlıkta kayıt yoksa **gönderilmez**; adres tanımlı değilse de
gönderilmez. Bu yüzden görev her sabah çalışsa bile kimse gereksiz e-posta
almaz.

---

## 9m. İki adımlı doğrulama (2FA)

Parola doğru olsa bile, hesabın e-posta adresine gönderilen **altı haneli
kod** girilmeden oturum açılmaz.

### Açmadan önce

> **SMTP mutlaka çalışıyor olmalı.** E-posta gönderilemiyorsa kimse giriş
> yapamaz. Önce **E-posta Merkezi → SMTP Sunucu Ayarları** ekranından test
> gönderimi yapın.

### Ayarlama (yalnız süpervizör)

**Sistem Ayarları → İki Adımlı Doğrulama** bölümünden politikayı seçin:

| Politika | Anlamı |
|---|---|
| Kapalı | Hiç kimse kullanamaz |
| İsteğe bağlı | Her kullanıcı kendi hesabından açar *(kurulum varsayılanı)* |
| Yetkili roller için zorunlu | Süpervizör, müdür, finans, kambiyo |
| Herkes için zorunlu | Bütün kullanıcılar |

Aynı ekrandaki **güvenilir cihaz süresi** (varsayılan 30 gün), kullanıcı
"Bu cihaza güven" dediğinde o tarayıcıda kaç gün kod sorulmayacağını
belirler. **0** yazarsanız her girişte kod istenir.

### Kullanıcı tarafı

**Hesabım → Hesap Güvenliği** bölümünde kullanıcı:

- iki adımlı doğrulamayı açıp kapatır (zorunlu değilse),
- güvenilir cihazlarını görür ve kaldırır,
- hesabının açık olduğu tarayıcıları görür ve **uzaktan kapatır**.

### Giriş akışı

1. E-posta + parola girilir.
2. Doğrulama gerekiyorsa `/dogrulama` ekranına gidilir, koda ait e-posta
   gönderilir.
3. Kod 10 dakika geçerlidir, 5 yanlış denemede yanar. "Kod gelmedi,
   yenisini gönder" yeni kod üretir (eskisi geçersizleşir).
4. "Vazgeç" bekleyen girişi iptal eder.

### Sorun giderme

| Belirti | Neden / çözüm |
|---|---|
| Kod e-postası gelmiyor | SMTP ayarlarını test edin; kayıt **E-posta Merkezi → kuyruk** içinde "queued" kalıyorsa gönderim çalışmıyordur |
| "Geçerli bir kod yok" | Aynı hesap için yeni kod istendiğinde eskisi yanar; son gelen kodu kullanın |
| Herkes dışarıda kaldı | Veritabanından `UPDATE app_settings SET setting_value='off' WHERE setting_key='twofactor_policy';` ve `UPDATE team_users SET twofactor_enabled=0;` |
| Güvenilir cihaz hatırlanmıyor | Tarayıcı çerezleri siliniyor olabilir; özel (gizli) pencerede çerez kalıcı değildir |

---

## 9n. Karanlık mod

Üst çubuktaki ay/güneş düğmesi, <kbd>Shift</kbd>+<kbd>D</kbd> kısayolu ve
komut paletindeki "Karanlık modu aç/kapat" komutu aynı işi yapar. Kullanıcı
seçim yapmazsa **işletim sisteminin tercihi** geçerlidir.

Karanlık tema dosyası `public/assets/koyu.css` **elle yazılmaz**,
`public/assets/app.css`'ten türetilir:

```bash
php bin/koyu-tema.php
```

> **CSS'e dokunduysanız bu komutu çalıştırın.** Aksi hâlde yeni renkler
> aydınlık temada görünür ama karanlıkta eski hâliyle kalır. Paketleme
> betiği bunu kendiliğinden yapar; sunucuda elle CSS değiştirmiyorsanız
> ayrıca bir şey yapmanız gerekmez.

Elle ayarlanmış birkaç ayrıntı `bin/koyu-tema-ek.css` dosyasındadır ve
üretilen dosyanın sonuna eklenir.

---

## 9o. Giriş güvenliği (kaba kuvvet koruması)

Ek bir kurulum adımı gerekmez; sistem kendiliğinden çalışır.

- Aynı hesapta son 15 dakikada **5 hatalı deneme** → 1 dakika bekleme,
  **8 deneme** → 5 dakika, **12 deneme** → 15 dakika.
- Aynı **IP adresinden 15 deneme** → 15 dakika bekleme (parola püskürtme
  saldırısına karşı).
- Kilit hesabı **kapatmaz**; süre dolunca giriş normal biçimde sürer.
- Başarılı girişten sonra sayaç sıfırlanır.
- 5. hatalı denemede hesap sahibine **bir kez** uyarı e-postası gider.

**Vekil sunucu / CDN kullanıyorsanız:** IP sayacının doğru çalışması için
gerçek istemci adresi `CF-Connecting-IP` veya `X-Forwarded-For` başlığında
gelmelidir. Cloudflare ve çoğu panel bunu kendiliğinden gönderir. Bu başlık
yoksa bütün istekler sunucunun kendi adresinden geliyormuş gibi görünür ve
IP sayacı yanlış kilitleme yapabilir.

### Bir kullanıcı kilitlendiyse

Kilidi elle açmak için bekleme süresini doldurmak yeterlidir. Acil durumda
süpervizör, veritabanından o hesabın son başarısız kayıtlarını geriye
alabilir:

```sql
UPDATE activity_log
   SET created_at = DATE_SUB(NOW(), INTERVAL 1 HOUR)
 WHERE type = 'auth.failed' AND actor_email = 'kullanici@iskur.com';
```

Denetim İzi ekranında **Güvenlik ve yönetim** kümesi süzülerek kimin,
nereden, kaç kez denediği görülebilir.

---

## 9p. E-posta gizliliği

Sistemin gönderdiği bildirimler, operasyon izlenebilsin diye **kurumsal
bilgi (CC) adresine** de düşer (Sistem Ayarları > Kurumsal Bilgi (CC)
Adresi). **Güvenlik postaları bunun dışındadır:**

- giriş doğrulama kodu,
- parola sıfırlama ve davet bağlantısı,
- parola/e-posta değişikliği bildirimi,
- hatalı giriş denemesi uyarısı.

Bunlar **yalnız hesabın sahibine** gider; CC eklenmez, verilmiş olsa bile
silinir ve engel listesi uygulanmaz.

> **Önemli:** Giriş kodunun bir kopyası ortak bir kutuya düşerse iki adımlı
> doğrulama anlamını yitirir. Bu yüzden kural kodda tek bir yerde ve
> **postanın sınıfına** göre uygulanır; ayar ekranından gevşetilemez.

---

## 9r. Site 500 hatası verirse

Beklenmeyen bir hata oluştuğunda ziyaretçi artık boş bir `HTTP ERROR 500`
değil, **referans numarası** taşıyan sade bir hata sayfası görür.

**Nereye bakılır:**

1. `http://siteniz.com/tani.php` — sayfanın altında **son 5 hata**
   (zaman, adres, mesaj) listelenir.
2. Tamamı (dosya, satır, yığın izi) **`storage/hata.log`** dosyasındadır.
   Bu dosya web'den açılamaz; FTP veya dosya yöneticisiyle indirilir.
3. Hata sayfasındaki referans numarasını günlükte aratın.

**Günlük büyümez:** 1 MB'ı aşarsa kendini sıfırlar.

**Sık görülen neden:** güncelleme sonrası eksik kalan bir tablo/kolon.
`http://siteniz.com/guncelle.php` adresini çalıştırmak çoğu durumda
yeterlidir.

---

## 10. Cloudflare sürümünden farklar

| Konu | Cloudflare (Next.js) | PHP sürümü |
|---|---|---|
| Veritabanı | Cloudflare D1 (SQLite) | MySQL / MariaDB (PDO) |
| Kimlik doğrulama | ChatGPT Sites kimlik başlıkları | E-posta + parola, PHP oturumu |
| E-posta gönderimi | Ayrı Node.js SMTP relay servisi | Yerleşik SMTP istemcisi (bağımlılıksız) |
| Ön yüz | React (istemci tarafı) | PHP şablonları + sade JavaScript |
| Tasarım | `globals.css` | Aynı CSS dosyası birebir korunmuştur |
| Kurulum | `npm ci` + build + wrangler | Dosyaları kopyala + SQL'i içe aktar |
| Güvenlik | — | CSRF koruması, rol denetimi, parola hash'i (bcrypt) |

### Sistemsel bildirimler ve görünürlük (bu sürümde geldi)

- Sistem olaylarının e-postası artık **yalnız süpervizörün Sistem Ayarları >
  Sistemsel Bildirimler panelinde tanımladığı adreslere** gider. Kurulum hesabı
  `admin@iskur.com` varsayılan olarak **engel listesindedir**; bu listedeki adreslere
  sistemin hiçbir yerinden e-posta gönderilmez.
- Müşteri temsilcisi, **müşteri temsilcisi olarak seçildiği** tüm dosyaları görür.
- Bir kullanıcı **müşteri temsilcisi yardımcısı** olarak işaretlenip bir veya birden
  fazla temsilci seçilirse, o temsilcilerin dosyalarını da görür.

### Yük durumu akışı

Dosya artık açılır açılmaz Yük Takibi ekranına düşmez ve tek düğmeyle bitmez:

1. Dosya açılır (**Dosya açıldı**) — henüz takipte değildir.
2. Yük Takibi ekranında **Takibe al** veya **Durumu güncelle → Yükleme planlandı** denir.
3. Sevkiyat onayı alındıktan sonra **Yüklendi** (yükleme zamanı + araç/konteyner bilgisi),
   **Yolda** (çıkış zamanı), **Varış limanı**, **Teslim edildi** (teslim zamanı + teslim alan)
   aşamaları menüden istenildiği zaman seçilir.
4. Teslim bilgisi girildikten sonra dosya **Kapat** ekranından teslimat/SAP/fatura
   numaralarıyla kapatılır.

Her değişiklik denetim izine yazılır ve istenirse müşteri temsilcisine e-posta gider.

İş kuralları birebir korunmuştur: haftalık devir mantığı, onaysız kapanış engeli,
zorunlu teslimat/SAP/fatura numaraları, cut-off zorunlulukları (denizyolu: talimat +
beyanname, havayolu: beyanname), parsiyel yüklerde adet istenmemesi, otomatik havale
bildirimi ve denetim izi.

---

## 11. Güvenlik kontrol listesi

- [ ] `app/config.php` içindeki `debug` değeri `false`
- [ ] `secret_key` rastgele ve uzun
- [ ] Varsayılan yönetici parolası değiştirildi
- [ ] Belge kökü `public/` klasörüne ayarlı (veya kök `.htaccess` yerinde)
- [ ] Site HTTPS üzerinden yayında (oturum çerezi güvenliği için)
- [ ] Veritabanı kullanıcısı yalnız kendi veritabanına yetkili
- [ ] `storage/` klasörü yalnız SQLite kullanılıyorsa var ve web'den erişilemez

---

## 11b. Tanı sayfası (giriş yapılamadığında)

Sunucuya kurulumdan sonra giriş yapılamıyorsa tarayıcıdan **`siteniz.com/tani.php`**
adresini açın. Sayfa şunları sırayla kontrol eder ve sorunlu satırı kırmızı
gösterip ne yapılması gerektiğini yazar:

- PHP sürümü ve gerekli eklentiler
- **Oturum klasörünün yazılabilirliği** (en sık giriş hatası nedeni)
- Oturum çerezinin tarayıcıya yazılıp istekler arasında korunması
  (ilk ziyarette *Çerez tarayıcıdan geri geldi* satırı kırmızıdır; sayfayı
  bir kez yenileyin, yeşile dönmelidir)
- `base_path` ve `app.url` ayarları
- `storage/` ve `app/` klasörlerinin yazma izni
- Veritabanı bağlantısı, aktif kullanıcı sayısı, parolası olmayan kullanıcılar
- SMTP tanımı ve kuyrukta bekleyen e-posta sayısı

Sayfa parola, şifreleme anahtarı veya veritabanı bilgisi **göstermez**.
Sorun çözüldükten sonra `tani.php` dosyasını sunucudan silebilirsiniz.

---

## 12. Sorun giderme

| Belirti | Çözüm |
|---|---|
| **SQL içe aktarırken hata** | En sık nedeni: phpMyAdmin'de veritabanı seçilmeden içe aktarmak. Önce sol menüden `iskurlojistik_hf` veritabanına tıklayın, sonra İçe Aktar deyin. Dosya artık `CREATE DATABASE`/`USE` komutu içermiyor, yani paylaşımlı hostingde yetki hatası vermez. En kolayı `kurulum.php` sihirbazını kullanmaktır — hata çıkarsa MySQL'in kendi mesajını gösterir. |
| "#1044 Access denied" / "#1046 No database selected" | Veritabanını panelden oluşturup kullanıcıyı tüm yetkilerle eklediğinizden ve phpMyAdmin'de o veritabanını seçtiğinizden emin olun |
| Dosya açılır açılmaz Yük Takibi'ne düşüyor | Artık düşmüyor: dosya ancak yük durumu "Yükleme planlandı" yapıldığında takibe girer. Bekleyenler aynı ekranda *Takibe alınmayı bekleyen dosyalar* listesindedir |
| **Müşteri yanlış görünüyor** | Müşteri = dosyadaki **alıcı firma**dır. Çıkış firması kendi firmanızdır ve ayrı gösterilir. Eski dosyalarda alıcı alanı boşsa düzenleyip doldurun |
| Çıkış firması yazamıyorum | Süpervizör Sistem Ayarları > **Çıkış Firmaları** listesini tanımladıysa firma listeden seçilir. Yeni bir firma için önce listeye ekleyin; liste boş bırakılırsa serbest metin olarak yazılabilir |
| "Çıkış firması tanımlı listede yok" hatası | Girdiğiniz firma listede yok. Sistem Ayarları > Çıkış Firmaları bölümüne ekleyin veya listeden seçin |
| Haftalık çıktıda istediğim liste yok | Haftalık Operasyon ekranında sağdaki **"Başka bir liste yazdır"** menüsünden seçin: yüklenmeyenler, onay bekleyenler, kullanıcı bekleyenler, yolda olanlar, teslim edilenler, devredenler, evrakı kapananlar |
| Kapanan dosya birleştirme listesinde çıkıyor | Çıkmaz: evrak kapanışı yapılmış ve daha önce birleştirilmiş dosyalar listeye alınmaz |
| "Dosya birleştir" düğmesi görünmüyor | Kullanıcıda **İki dosyayı birleştirebilir** (`shipment.merge`) yetkisi yoktur. Varsayılan olarak Lojistik Uzmanı ve Müdür rollerindedir; Ekip & Yetkiler > kalkan simgesinden başka rollere de verebilirsiniz |
| Birleştirmede yanlış dosya numarası kaldı | Kalan numara **önce açılan dosyanın** numarasıdır; seçim sırası sonucu değiştirmez. Pencere, birleştirmeden önce hangi numaranın kalacağını yazar |
| Birleştirilen dosya kayboldu | Silinmedi: "birleştirildi" durumuna geçti, numarası ve geçmişi korunuyor. Kayıtları hedef dosyada görürsünüz; hedef dosyanın altında hangi dosyanın birleştirildiği yazar |
| Birleştirmeden sonra finans onayı yeniden istendi | Beklenen davranış: iki dosyadan biri onaysızsa hedefe onaylanmamış yük eklenmiş olur, bu yüzden onay yenilenir |
| **Dosya yükleme planına geçmiyor** | Finans onayı bekliyordur. **Finans Onayı** ekranından onay verin; onaysız dosya "Yükleme planlandı" aşamasına alınamaz |
| Finans Onayı ekranı menüde yok | Kullanıcıya **Finans Onayı** ekran yetkisi (`page.finance`) ve **Finans onayı verebilir** yetkisi verin (Ekip & Yetkiler > kalkan simgesi) |
| **Dosyanın ilgilisini değiştirmek istiyorum** | Dosya Havale Merkezi > **Havale Edilmiş Dosyalar** listesinden ilgili satırda **"İlgiliyi değiştir"** deyin, yeni kullanıcıyı seçip kaydedin. Dosya numarası ve geçmişi değişmez; eski ve yeni ilgiliye bildirim gider |
| "İlgiliyi değiştir" düğmesi yok | Kullanıcıda **Dosya havale edebilir** (`assignment.assign`) yetkisi yoktur; Ekip & Yetkiler > kalkan simgesinden verin |
| "… bu dosyanın zaten ilgilisi" hatası | Seçtiğiniz kişi dosyanın mevcut ilgilisidir; farklı bir kullanıcı seçin |
| Dosya havale merkezine düşmüyor | Dosya açılırken işlem kullanıcısı olarak **"Kullanıcı bekleniyor"** seçilmelidir; bir kullanıcı seçilirse dosya otomatik havale edilir |
| **"Eksik: Yükleme günü" hatası (çok günlü yükleme)** | Giderildi. "Farklı günlerde yüklenecek" işaretliyken ana yükleme günü plandan otomatik alınır; alan salt okunur olur ve doldurmanız gerekmez |
| **Birleştirmeden sonra iki gün tek güne düşüyor** | Giderildi. Planı olmayan eski dosyalarda plan artık yükleme gününden türetiliyor; birleşmede iki dosyanın günleri korunuyor |
| **Dosyayı sonradan çok günlü yapamıyorum** | Dosya ayrıntı çekmecesi > *Dosyayı düzenle* bölümünde "Farklı günlerde yüklenecek" kutusu vardır; günleri ve adetleri oradan girip kaydedin. Yetki: **Dosya bilgilerini düzenleyebilir** (`shipment.edit`) |
| "Dosyayı düzenle" bölümü görünmüyor | Kullanıcıda `shipment.edit` yetkisi yoktur ya da dosya işlem kapsamında değildir. Ekip & Yetkiler > kalkan simgesinden verin |
| Çok günlü / birleştirilmiş dosya haftalık planda tek günde görünüyor | Giderildi. Dosya yüklendiği her günde ayrı kart olarak çıkar ve kartta "1/2. yükleme · 1 konteyner" yazar. Hafta özeti yine dosya başına sayar |
| Çok günlü yükleme kaydedilmiyor | Plandaki adetlerin toplamı, **araç/konteyner sayısı** ile aynı olmalıdır; ekranda "Toplam: x / y" bunu gösterir |
| Farklı günlerdeki yüklemeler ayrı dosya oluyor | Olmuyor: hepsi **aynı dosya referansı** altındadır. Listede dosya numarasının altında *bölünmüş yükleme* yazar |
| Kambiyo uzmanı dosya kapatamıyor | Bu sürümde kambiyo uzmanı işlem yetkilisiyle aynı kapsamdadır; güncellemeden sonra yetkiler otomatik eklenir. Eksikse Ekip & Yetkiler'den verin |
| Yük durumunu değiştiremiyorum | Kullanıcıya *Yük durumunu (takip aşamasını) değiştirebilir* yetkisi verin. "Yüklendi" ve sonrası için ayrıca sevkiyat onayı gerekir |
| "İşlem yap" dediğimde dosyayı kapatamıyorum | Teslimat şartı kaldırıldı: mal yoldayken de kapanış yapılır. Geriye kalan tek şart **sevkiyat onayı** ve her satırda teslimat/SAP/fatura numarasıdır. Onay şartı Sistem Ayarları > *Onaysız sevkiyatların kapanmasını engelle* ile kaldırılabilir |
| Dosya kapandı ama mal hâlâ yolda | Doğru davranış: dosya *Tamamlandı* olur, Yük Takibi ekranında **Evrakı kapandı · teslim bekleniyor** rozetiyle kalır. Teslim gerçekleşince **Durumu güncelle → Teslim edildi** deyin |
| Kapanan dosyanın durumunu güncelleyemiyorum | Yalnız **teslim edilmiş** ve **birleştirilmiş** dosyalarda aşama kapalıdır. Kapanmış dosyada "Durum" düğmesi Yüklemeler ve Yük Takibi ekranında, haftalık kartta ise **Teslim takibi** olarak durur |
| Logo yüklenmiyor | `storage/branding/` klasörü yazılabilir mi? `chmod 775 storage` deneyin. Dosya 2 MB'ı aşıyorsa veya PNG/JPG/WEBP/SVG değilse reddedilir |
| Logoyu değiştirdim ama eski görünüyor | Tarayıcı önbelleği; sayfayı **Ctrl+F5** ile yenileyin. Adres sonundaki `?v=` değeri her yüklemede değişir |
| Lojistik Takip ekranı menüde yok | Kullanıcıda `page.logistics` yetkisi yoktur; güncellemeden sonra Ekip & Yetkiler > kalkan simgesinden verin |
| Lojistik Takip boş | Kayıtlar **evrak kapanışında** oluşur; henüz kapatılmış dosya yoksa liste boştur |
| Yük miktarı birimi seçilemiyor | Güncelleme çalıştırılmamış olabilir: `shipments.tonnage_unit` kolonu eksikse `guncelle.php` sayfasını çalıştırın |
| Farklı birimdeki dosyalar birleşmiyor | Kasıtlıdır: ton ile metre toplanamaz. Önce dosyalardan birinin birimini düzenleyin |
| Dosya "beklemede" kaldı, ilerleyemiyorum | Finans beklemeye almıştır. Finans Onayı > *Beklemedeki dosyalar* bölümünden **Serbest bırak** deyin ya da serbest bırakma durumunu bekletmeyen bir seçenekle güncelleyin |
| Onay verdim ama dosya yine beklemede | Seçtiğiniz serbest bırakma durumu "bekletir" işaretlidir. Sistem Ayarları > Durum Listeleri'nden işareti kaldırabilir ya da başka bir durum seçebilirsiniz |
| Dosya kapanmıyor: "net kg zorunludur" | Kapanışta her satırda net ve brüt kg istenir; brüt netten küçük olamaz |
| Dosya kapanmıyor: "metre zorunludur" | Ürün cinsi denim/kumaş listesindedir. Metreyi girin ya da Sistem Ayarları > Kapanışta Metre Zorunluluğu listesini düzenleyin |
| Eklediğim durum listede yok | Listeyi kaydettiğinizden emin olun; adı boş satırlar kaydedilmez |
| Varsayılan durumu silemiyorum | Kasıtlıdır: paketle gelen durumlara koda gömülü kurallar dayanır. Adını ve rengini değiştirebilirsiniz |
| Bakım modunu açtım, kendim de giremiyorum | Süpervizör her zaman girebilir. Süpervizör hesabına erişemiyorsanız veritabanından kapatın: `UPDATE app_settings SET setting_value='0' WHERE setting_key='maintenance_mode';` |
| Bakımda çalışması gereken kullanıcı giremiyor | Ekip & Yetkiler > kalkan simgesinden **"Bakım modunda giriş yapabilir"** yetkisini verin |
| "Bu kararı X verdi" diyor, geri alamıyorum | Başkasının kararını geri almak için ilgili yetki gerekir (örn. `approval.revoke`). Kendi kararınızı her zaman geri alabilirsiniz |
| Onayı geri alamıyorum: "mal yola çıkmışken…" | Önce Yük Takibi'nden aşamayı geriye alın (`stage.revert` yetkisi), sonra onayı geri alın |
| Kapalı dosyada karar değiştiremiyorum | Önce Yüklemeler ekranından **Yeniden aç** deyin; kapanış kayıtları silinmez |
| Lojistik kaydını silemiyorum | Kapatılmış dosyanın **tek** kapanış kaydı silinemez; önce dosyayı yeniden açın |
| Bildirimler kambiyo adresine gitmiyor | Sistem Ayarları > **Kurumsal Bilgi (CC) Adresi** boş olabilir ya da adres engel listesindedir |
| Outlook'ta e-posta bozuk görünüyor | Şablon Outlook 2007–2016 için uyarlandı. Eski bir sürümden geliyorsanız `app/MailTemplate.php` dosyasının güncellendiğinden emin olun |
| Kullanıcı başkasının dosyasında işlem yapabiliyor | Ekip & Yetkiler > kalkan simgesi > **İşlem kapsamı** bölümünden *Tüm dosyalarda işlem yapabilir* tikini kaldırın |
| Kullanıcı boştaki dosyaları görmüyor | Aynı bölümden *Havale edilmemiş (boştaki) dosyalarda işlem yapabilir* tikini işaretleyin |
| Müşteri temsilcisi değişiklik yapabiliyor | Müşteri Temsilcisi rolünde hiçbir "İşlem kapsamı" yetkisi olmamalıdır |
| Gönderdiğim mail hiç görünmüyor | Kuyrukta "Engellendi" olarak listelenir; Sistem Ayarları > Sistemsel Bildirimler > engel listesini kontrol edin |
| E-posta önizlemesi açılmıyor | Önizleme kutusundaki **Yeni sekmede aç** düğmesini kullanın |
| **"Tarayıcınız oturum çerezini kaydetmedi"** | Sunucunun `php.ini` ayarları (yanlış `session.cookie_path` / `cookie_domain` / `cookie_secure`) yüzünden çerez tarayıcı tarafından atılıyordu. Bu sürümde çerezi PHP değil uygulama yazar; sunucu ayarı artık oturumu etkilemez. `tani.php` sayfasındaki *Oturum çerezi tarayıcıya yazıldı* satırı Set-Cookie başlığını olduğu gibi gösterir |
| **tani.php'de "Çerez tarayıcıdan geri geldi" kırmızı** | İlk ziyarette normaldir (çerez o istekte yeni yazılır). Sayfayı **bir kez yenileyin**; yeşile dönmüyorsa tarayıcıda çerezler kapalıdır ya da sayfa önbellekten (LiteSpeed/Cloudflare) geliyordur |
| Site adresi / e-posta bağlantıları yanlış | **Sistem Ayarları > Site Adresi** alanına sitenizin tam adresini yazın (dosya düzenlemeye gerek yok) |
| Kullanıcı giriş yapamıyor, parolası yok | Ekip & Yetkiler ekranında satırda **Davet gönder** düğmesi çıkar; parola belirleme bağlantısı yollar |
| E-postalar kuyrukta kalıyor | Sistem Ayarları → Operasyon Politikaları → **"E-postaları anında gönder"** işaretli mi? Değilse yalnız cron gönderir. İşaretliyken de kalıyorsa SMTP'ye ulaşılamıyordur: kaydın **Hata** sütununa bakın |
| E-postalar geç ulaşıyor | Aynı anahtarı açın. Açıkken de gecikiyorsa SMTP sunucusunun yanıt süresini **Bağlantıyı test et** ile ölçün |
| Parola sıfırlama e-postaları kuyrukta görünmüyor | Bu kayıtlar `email.queue.sensitive` yetkisiyle korunur. Ekip & Yetkiler → kullanıcının yetkileri → **E-posta** grubundan verin |
| **"Oturum doğrulaması başarısız" (parola doğru olsa bile)** | `siteniz.com/tani.php` sayfasını açın. En sık neden: PHP'nin oturum klasörü yazılamıyor. Bu sürümde sistem otomatik olarak `storage/sessions` klasörüne geçer; o klasöre 775 izni verin |
| Parolamı unuttum çalışmıyor | SMTP tanımlı değilse ekran bunu söyler. Tanımlıysa parola e-postası engel listesinden etkilenmez, mutlaka gönderilir |
| Oturum sürekli düşüyor | `app/config.php` içindeki `base_path` alanı yanlış olabilir; site kök dizindeyse boş olmalıdır (çerez artık her zaman `/` yoluna yazılır) |
| Süpervizör adı bir yerde görünüyor | Bu sürümde adınız yalnız kendi oturumunuzda görünür. Görünüyorsa kullanıcının rolü süpervizör olabilir (Ekip & Yetkiler'den kontrol edin) |
| **Mailde kullanıcının adı yerine "Admin" yazıyor** | Giderildi. Süpervizörle aynı ada sahip bir kullanıcı eklendiğinde onun adı da maskeleniyordu. Maskeleme artık ada değil, hesabın e-posta adresine bakıyor: yeni kullanıcının adı kendi adıyla, işlemi yapan süpervizör "Admin" olarak yazılır |
| Kendime dosya havale edemiyorum | Süpervizör olarak giriş yaptığınızda listede kendinizi görürsünüz; başka kimse sizi göremez |
| Logo bazı yerlerde soluk görünüyor | `assets/iskur-logo-light.png` (beyaz yazılı) ve `assets/iskur-logo.png` (özgün) dosyalarının yüklendiğinden emin olun |
| admin@iskur.com'a hâlâ mail gidiyor | Sistem Ayarları > Sistemsel Bildirimler > engel listesinde olduğundan emin olun (varsayılan olarak oradadır) |
| Sistemsel bildirim almıyorum | Aynı panelde bildirim adresinizi yazın ve ilgili olayları tikleyin; "Deneme bildirimi gönder" ile sınayın |
| Müşteri temsilcisi tüm dosyaları görüyor | Ekip & Yetkiler > Roller > Müşteri Temsilcisi > *Tüm dosyaları görebilir* tikini kaldırın. Yeni kurulumlarda bu yetki gelmez |
| Yardımcı temsilcinin dosyalarını görmüyor | Kullanıcı düzenleme ekranında *Müşteri temsilcisi yardımcısı* tiki işaretli ve ilgili temsilci seçili olmalı |
| Reddedilen dosya ne olacak? | Sevkiyat Onayları ekranındaki *Reddedilen Dosyalar* bölümünden düzeltilip yeniden onaya gönderilebilir veya silinebilir |
| Not eklendi ama mail gitmedi | Notu müşteri temsilcisi (veya yardımcısı) yazdıysa bildirim **dosya sorumlusuna** gider: dosya havale edilmiş mi? Sorumlu yazdıysa dosyada **müşteri temsilcisi e-postası** dolu mu? |
| Yardımcıya mail gitmiyor | Ekip & Yetkiler ekranında kullanıcı **"Müşteri temsilcisi yardımcısı"** olarak işaretli ve **ilgili temsilciye bağlı** olmalı; ayrıca hesabı **aktif** olmalıdır. Yardımcılar ayrı mail almaz: temsilciye giden **tek e-postanın CC satırında** yer alırlar |
| Telefonda menü açılmıyor | Üst çubuğun solundaki **☰** düğmesine dokunun; 1024px altındaki ekranlarda menü açılır panel olarak çalışır |
| Ekranın sol üst/alt köşesinde kayma | Bu sürümde giderildi. Sürüyorsa tarayıcı önbelleğini temizleyin (Ctrl+F5); `app.css` dosyasının güncel sürümü yüklenmelidir |
| Kuyrukta dosyayı bulamıyorum | Arama kutusuna dosya numarasını (ISK-2026-0016) veya müşteri adını yazın; arama e-postanın gövdesinde de çalışır |
| Zil simgesinde bildirim çıkmıyor | Bildirim, notu yazan kişinin kendisine düşmez. Karşı tarafın hesabıyla bakın; kayıtlar kullanıcının **e-posta adresine** bağlanır |
| Logo kaldırıldı ama hâlâ görünüyor | Tarayıcı önbelleğini temizleyin (Ctrl+F5). Anahtar: Sistem Ayarları → Kurumsal Logo → **Logo görünmesin** |
| "Kararı düzenle" düğmesi çıkmıyor | Kararı başkası vermişse `approval.revoke` / `finance.revoke` yetkisi gerekir. Mal yola çıkmışsa önce Yük Takibi'nden aşamayı geriye alın |
| Beklemeye alınan dosya ilerlemiyor | Doğru davranıştır: bekletme bilinçli bir durdurmadır. Sevkiyat Onayları → **Beklemedeki Dosyalar** → *Kararı düzenle* ile onaya çevirin |
| Kambiyoya mail gitmiyor | Sistem Ayarları → **Role Göre Bilgi (CC) Adresleri** ekranında ilgili rolün satırı dolu mu? Kopya, işlemi **yapan kullanıcının rolüne** göre eklenir |
| Bakım modunda giriş formu açılmıyor | Doğru davranıştır: oturumsuz ziyaretçi bakım ekranını görür. Giriş için ekrandaki **"Yönetici girişi"** düğmesini kullanın |
| Finans alanları düzenlenemiyor | Kullanıcıya *Finans onayı, limit ve ödeme şeklini düzenleyebilir* yetkisi verin |
| "secret_key tanımlı değil" hatası | Yeni sürümde anahtar otomatik üretilir. Hata sürüyorsa `app/` klasörüne veya `storage/` klasörüne yazma izni (755/775) verin |
| Kullanıcı hiçbir ekranı göremiyor | Rolünün ekran yetkileri işaretli mi? Elle SQL çalıştırdıysanız `guncelle.php` sayfasını bir kez çalıştırın |
| "app/config.php bulunamadı" | `kurulum.php` sihirbazını çalıştırın veya `app/config.sample.php` dosyasını `config.php` adıyla kopyalayın |
| "kurulum.php açılmıyor / 404" | Dosya `public/kurulum.php` içindedir; belge kökü `public/` değilse adres `siteniz.com/public/kurulum.php` olur |
| "Veritabanına bağlanılamadı" | `config.php` içindeki host/kullanıcı/parola bilgilerini kontrol edin |
| Sayfalar 404 veriyor | Apache'de `mod_rewrite` ve `AllowOverride All` açık mı? |
| Stil bozuk görünüyor | Belge kökü `public/` mü, yoksa `base_path` doğru ayarlı mı? |
| SMTP testi hata veriyor | Port/şifreleme eşleşmesine bakın (465→SSL, 587→STARTTLS); sunucunun giden bağlantı izni gerekir |
| "Şifreli değer çözülemedi" | `secret_key` değişmiş: SMTP parolasını yeniden kaydedin |
| Parola/davet bağlantısı yanlış adrese gidiyor | `app/config.php` içindeki `app.url` alanını sitenizin tam adresiyle doldurun |
| "Bağlantı geçersiz veya süresi dolmuş" | Bağlantılar tek kullanımlıktır (parola 2 saat, davet 72 saat). Ekrandaki **Yeni bağlantı iste** düğmesiyle yenisini isteyin |
| Davet veya parola e-postası gelmiyor | SMTP ayarlı mı? E-posta Merkezi > **Kuyruğu işle** deyin; spam klasörünü kontrol edin |
| E-postada logo görünmüyor | Logo gömülü gönderilir; `public/assets/iskur-logo-white.png` dosyasının yüklendiğinden emin olun |

---

## 13. Yerel geliştirme

```sh
php -S 127.0.0.1:8000 -t public public/router.php
```

MySQL'in olmadığı bir makinede denemek için `config.php` içinde
`'driver' => 'sqlite'` yapıp `sql/iskur-lojistik-sqlite.sql` şemasını yükleyebilirsiniz;
aynı kod her iki veritabanında da çalışır.
