# Para Birimi ve Kupon Kancaları

https://dev.wisecp.com/tr/para-birimi-kupon-kancalari

Kur çevrimi, tutar biçimi ve kuponlara dokunan on yedi kanca.

## Genel Bakış

Bu alandaki kancalar **her ekranda** çalışır. Kur çevrimi ve tutar biçimi sitenin her fiyat gösteriminden geçer, o yüzden burada yapılan ağır iş **bütün sitenin** yükü olur.

Kuponlar ise farklıdır: seyrek çalışır ama **doğrudan paraya** dokunur. Uygulama kapısı sepette, kayıt kapısı ise operatör kupon tanımlarken çalışır.

## Referans

### Kupon uygulamasını durdurma

gatemoney.coupon_apply

`Coupon::validate()` misafirde sıfır

Kupon sepete uygulanmadan önce çalışır.

Parametreler 3

$couponarrayKupon kaydı: kod, tip (`percent`, `amount`, `fixed`), oran ya da tutar, para birimi, kendiliğinden uygulanma ve birleşme işaretleri, ürün kısıtları.

$uidintSepetin sahibi. **Misafir alışverişte `0`** gelir; hesap üzerinden kural yazarken bunu unutmayın.

$contextarrayFiyatlama bağlamı: `items` (fiyatlanmış sepet kalemleri), `subtotal`, `user_currency`.

Dönüş 1

stringBoş olmayan bir string kuponu **reddeder**; metin müşteriye hata olarak gösterilir.

Dinleyici PHP

```php
Hook::add('gate:money.coupon_apply', 10, function ($coupon, $uid, $context) {
    // Misafirde uid 0 gelir: hesaba bagli kurali once ona gore ayirin.
    if ($uid === 0) return 'Bu kupon icin giris yapmaniz gerekiyor.';

    if (Acme::alreadyUsed($uid, $coupon['code'] ?? '')) return 'Kupon zaten kullanildi.';

    return null;
});
```

### Kupon kaydını durdurma

gatemoney.coupon.save

`AdminMoney` operatör tanımlıyor

Operatör kupon kaydetmeden önce çalışır.

Parametreler 4

$codestringKupon kodu.

$statearrayFormdaki **tüm** değerlerin anlık görüntüsü: tip, oran, tutar, para birimi, kısıtlar. Veritabanına yazılacak dizinin kendisi **değildir**; ham form durumudur.

$isEditboolDüzenleme mi, yeni kayıt mı.

$idintDüzenlemede kuponun kimliği, yeni kayıtta `0`.

Dönüş 1

stringBoş olmayan bir string kaydı **durdurur**.

Dinleyici PHP

```php
Hook::add('gate:money.coupon.save', 10, function ($code, $state, $isEdit, $id) {
    // Yuzde 90 uzeri indirim genelde yazim hatasidir.
    if (($state['type'] ?? '') === 'percent' && (float) ($state['rate'] ?? 0) > 90)
        return 'Yuzde 90 uzeri indirim onay ister.';

    return null;
});
```

### Kaydedilecek kuponu değiştirme

filtermoney.coupon.save_data

`Hook::runRefs` bazı alanlar ezilir

Kupon verisi veritabanına yazılmadan önce çalışır.

Parametreler 3

$dataarrayrefYazılacak kupon verisi: kod, tip, oran, tutar, para birimi, kısıtlar. Yeni kayıtta `status` ve oluşturma tarihi bu filtreden **sonra** eklenir; buraya yazdığınız değerler **ezilir**.

$isEditboolDüzenleme mi, yeni kayıt mı.

$idintKupon kimliği ya da `0`.

Dönüş 1

voidDeğer **referansla** değişir; dönüş kullanılmaz.

Dinleyici PHP

```php
Hook::add('filter:money.coupon.save_data', 10, function (&$data, $isEdit, $id) {
    // Yeni kayitta status yazmayin: bu filtreden SONRA uzerine yazilir.
    $data['code'] = strtoupper((string) ($data['code'] ?? ''));
});
```

### Kupon durumunu izleme

actionmoney.coupon.status_changed

`AdminMoney` kopyalamada kaynak gelir

Kupon durumu değiştikten sonra çalışır.

Parametreler 3

$coupon_idintEtkilenen kuponun kimliği.

$source_idintKopyalama kaynağının kimliği. Kopyalama değilse `0`; bir kuponun kopya olup olmadığını buradan anlarsınız.

$statusstringYeni durum.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:money.coupon.status_changed', 10,
    function ($coupon_id, $source_id, $status) {
        // Kaynak dolu ise bu kupon bir kopyadir.
        if ($source_id > 0) Acme::linkCopy($coupon_id, $source_id);
    });
```

### Kur çevrimini değiştirme

filtermoney.exchange_rate

`Money::exChange()` çok sık çalışır

Bir tutar başka para birimine çevrildikten sonra çalışır. **Her çevrimde** çalışır.

Parametreler 4

$convertedfloatrefHesaplanan sonuç. Buraya yazdığınız değer **nihai** tutardır.

$amountfloatKaynak tutar.

$fromarrayKaynak para birimi: kod, kur, önek, sonek.

$toarrayHedef para birimi: kod, kur, önek, sonek.

Dönüş 1

voidDeğer **referansla** değişir; dönüş kullanılmaz.

Dinleyici PHP

```php
Hook::add('filter:money.exchange_rate', 10,
    function (&$converted, $amount, $from, $to) {
        // HER cevrimde calisir: sorgu ya da uzak cagri koymayin.
        $converted = round($converted * (1 + Acme::MARGIN), 4);
    });
```

### Tutar biçimini değiştirme

filtermoney.format_output

`Money::formatter()` her fiyatta

Bir tutar metne çevrildikten sonra çalışır.

Parametreler 2

$outputstringrefBiçimlenmiş metin. Sunucuda değiştirdiğiniz biçim, ekranda tarayıcı tarafının ürettiğiyle **aynı olmalıdır**; yoksa sayfa yüklenince rakam gözle görülür biçimde değişir.

$amountfloatBiçimlenen ham tutar.

Dönüş 1

voidDeğer **referansla** değişir; dönüş kullanılmaz.

Dinleyici PHP

```php
Hook::add('filter:money.format_output', 10, function (&$output, $amount) {
    // Tarayici tarafi ayni bicimi uretmiyorsa sayfa yuklenince rakam titrer.
    if ((float) $amount === 0.0) $output = Acme::freeLabel();
});
```

### Kur güncellemesini izleme

actionmoney.exchange_rates_updated

`cronjobs/ExchangeRates` yalnız değişenler

Kurlar güncellendikten sonra çalışır.

Parametreler 2

$changesarrayBu koşuda **değişen** kurlar: para birimi koduna göre yeni kur. Hiçbir kur değişmediyse **boş dizi** gelir; kanca yine de çalışır.

$localCodestringSistemin ana para birimi kodu. Kurlar buna göre ifade edilir.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:money.exchange_rates_updated', 10, function ($changes, $localCode) {
    if (!$changes) return;                        // bu koşuda degisiklik yok

    Ops::note('fx', $localCode . ': ' . implode(',', array_keys($changes)));
});
```

### Para birimi durumunu durdurma

gatemoney.currency.status_change

`AdminMoney` geniş etki

Bir para birimi açılıp kapatılmadan önce çalışır.

Parametreler 2

$currencyarrayPara birimi kaydı.

$statusstringHedef durum.

Dönüş 1

stringBoş olmayan bir string geçişi **durdurur**.

Dinleyici PHP

```php
Hook::add('gate:money.currency.status_change', 10, function ($currency, $status) {
    // Kullanimda olan para birimini kapatmak fiyatlari cozumsuz birakir.
    if ($status !== 'active' && Acme::inUse($currency['id'] ?? 0))
        return 'Bu para biriminde acik hizmet var; kapatilamaz.';

    return null;
});
```

### Kupon kaydını izleme

actioncoupon.saved

`AdminMoney` ekleme ve düzenleme

Bir kupon oluşturulduğunda ya da düzenlendiğinde çalışır. İki durum da aynı kancaya düşer; hangisi olduğunu bir parametre söyler.

Parametreler 4

$idintKuponun kimliği.

$codestringKupon kodu.

$isEditboolDoğruysa var olan kupon düzenlendi, yanlışsa yenisi oluşturuldu.

$dataarrayKaydedilen alanlar: tipi, oranı, tutarı, para birimi, geçerlilik döngüsü ve kullanım sınırı.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:coupon.saved', 10, function ($id, $code, $isEdit, $data) {
    // Yalniz yeni kuponu kampanya sistemine tanitin.
    if (!$isEdit) Acme::publishCampaign($code, $data);
});
```

### Kuponun silinmesini izleme

actionmoney.coupon.deleted

`AdminMoney` silme sonrası

Bir kupon silindikten sonra çalışır.

Parametreler 2

$coupon_idintSilinen kuponun kimliği.

$couponarraySilme öncesi kayıt. Kupon artık yok: koda ihtiyacınız varsa buradan alın.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:money.coupon.deleted', 10, function ($coupon_id, $coupon) {
    Acme::retireCampaign($coupon['code'] ?? '');
});
```

### Ana para biriminin değişmesini izleme

actionmoney.currency.local_changed

`AdminMoney` ana para birimi

Sistemin ana para birimi değiştiğinde çalışır. Bu, kurumsal ölçekte bir değişimdir: bütün kurlar artık yeni birime göre okunur.

Parametreler 2

$idintAna olan para biriminin kimliği.

$currencyarrayAna olan para birimi kaydı, **değişiklik öncesi** okunmuş hâliyle.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:money.currency.local_changed', 10, function ($id, $currency) {
    // Butun kurlar artik yeni birime gore okunur.
    Acme::rebaseReports($id);
});
```

### Vergi kuralının kaydını durdurma

gatemoney.tax_rule.save

`AdminMoney` yazımdan önce

Bir vergi kuralı kaydedilmeden önce çalışır. Yanlış bir oran bütün yeni faturalara işleyeceği için buradaki denetim ucuzdur.

Parametreler 3

$country_idintKuralın ülkesi.

$state_idintEyalet ya da şehir; **sıfır** ise kural ülke genelidir.

$ratefloatHesaplanan toplam oran.

Dönüş 1

string|null**Boş olmayan bir metin kaydı engeller** ve hata olarak gösterilir. Boş dönüş devam ettirir.

Dinleyici PHP

```php
Hook::add('gate:money.tax_rule.save', 10, function ($country_id, $state_id, $rate) {
    // Makul araligin disindaki oran genelde parmak hatasidir.
    if ($rate < 0 || $rate > 40) return 'Vergi orani beklenen araligin disinda.';

    return null;
});
```

### Vergi oranı değişimini izleme

actionmoney.tax_rates_changed

`AdminMoney` yazımdan sonra

Bir vergi kuralı kaydedildikten sonra çalışır.

Parametreler 3

$country_idintKuralın ülkesi.

$state_idintEyalet ya da şehir; sıfır ise ülke geneli.

$ratefloatYeni toplam oran.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:money.tax_rates_changed', 10, function ($country_id, $state_id, $rate) {
    Acme::syncTaxTable($country_id, $state_id, $rate);
});
```

### Para birimi değişikliğini düzenleme

filtermoney.currency.save_data

`AdminMoney` bağla geçer

Bir para birimi kaydedilirken, **yalnız değişen alanlar** yazılmadan önce çalışır. Elinizdeki kaydın tamamı değil, farktır.

Parametreler 3

$setsarraybağlaYazılacak fark. Hiçbir alan değişmediyse **boş gelebilir**; buna hazır olun.

$idintPara biriminin kimliği.

$currencyarrayMevcut kayıt, fark uygulanmadan önceki değerleriyle.

Dönüş 1

voidDönüş yoksayılır; farkın üzerine yazarsınız. Ana para birimi geçişi bu filtreden **sonra** uygulanır ve o dalda farka yeni alanlar eklenir: burada gördüğünüz son hâli değildir.

Dinleyici PHP

```php
Hook::add('filter:money.currency.save_data', 10, function (&$sets, $id, $currency) {
    // Fark bos gelebilir: once bakin.
    if (!$sets) return;

    // Kuru kendi kaynaginizdan sabitleyin.
    if (isset($sets['rate'])) $sets['rate'] = Acme::officialRate($currency['code'] ?? '');
});
```

### Tutarın yazım biçimini değiştirme

filtermoney.digit

`Money::format` son dönüş kazanır

Bir tutar biçimlendirilirken çalışır. Sistemdeki **her** para gösterimi buradan geçer: fatura, sepet, liste, belge.

Parametreler 4

$amountfloatHam tutar.

$currencyarrayÇözülmüş para birimi kaydı.

$symbolboolSembol gösterilecek mi.

$exchangemixedDönüşüm hedefi, yoksa yanlış.

Dönüş 1

string|nullDöndürdüğünüz değer biçimi **değiştirir** ve son dönüş kazanır. İlgilenmediğiniz çağrıda **hiçbir şey döndürmeyin**: `null` güvenle atlanır. Ama `''`, `0` ve `false` atlanmaz, atanır ve tutar **boş görünür**.

Dinleyici PHP

```php
Hook::add('filter:money.digit', 10, function ($amount, $currency, $symbol, $exchange) {
    if (($currency['code'] ?? '') !== 'BTC') return;   // '' DONDURMEYIN: tutari bosaltir

    return number_format($amount, 8, '.', '');
});
```

### Çekilen kurları değiştirme

filtermoney.exchange_rates_fetch

`Money` bağla geçer

Kurlar dış kaynaktan alındıktan sonra, kaydedilmeden önce çalışır. Kur ekleyebilir, değiştirebilir ya da güvenmediğinizi listeden çıkarabilirsiniz.

Parametreler 3

$ratesarraybağlaKod karşılık kur listesi, ana para birimine göre.

$localCodestringAna para biriminin kodu; kurlar buna göre okunur.

$targetsarrayEşitlenecek para birimleri.

Dönüş 1

voidDönüş yoksayılır; listenin üzerine yazarsınız.

Dinleyici PHP

```php
Hook::add('filter:money.exchange_rates_fetch', 10,
    function (&$rates, $localCode, $targets) {
        // Kendi resmi kaynaginizi dis servisin uzerine yazin.
        $own = Acme::officialRates($localCode);
        foreach ($own as $code => $rate) $rates[$code] = $rate;
    });
```

### Düzenli gider kaydını izleme

actionexpense.recurring_recorded

`cronjobs` tur başına bir kez

Düzenli gider kuralları işlendikten sonra çalışır. Tek gider için değil, **turun tamamı** için bir kez çalışır.

Parametreler 2

$entriesarrayBu turda işlenen kurallar; her biri kural ve kayıt kimliği, açıklama, tutar, para birimi ve durum taşır.

$recordedintBu turda **başarıyla** eklenen gider sayısı. Hiçbir gider eklenmediyse kanca zaten çalışmaz: bu değer daima birden büyüktür.

Dönüş 1

voidDönüş yoksayılır.

Dinleyici PHP

```php
Hook::add('action:expense.recurring_recorded', 10, function ($entries, $recorded) {
    // Tur basina tek cagri: dis muhasebeye toplu gonderin.
    Acme::pushExpenses($entries);
});
```

## Tuzaklar

> **Kur ve biçim kancaları her fiyatta çalışır**
> 
> Bir katalog sayfası yüzlerce fiyat gösterir ve bu iki kanca **her biri için** çalışır. İçlerine sorgu, dosya okuma ya da uzak çağrı koymak sayfayı yüzlerce kez yavaşlatır. Gereken veriyi **bir kez** hazırlayıp durağan bir değişkende tutun.

> **Misafir sepetinde hesap kimliği sıfırdır**
> 
> Kupon kapısındaki hesap kimliği, giriş yapılmamış alışverişte `0` gelir. "Bu müşteri kuponu daha önce kullandı mı" gibi bir kural, **bütün misafirleri aynı kişi** sayar. Hesaba bağlı kuralı yazmadan önce sıfır durumunu ayırın.

> **Kupon filtresinde bazı alanlar sonradan ezilir**
> 
> Yeni kupon kaydında `status` ve oluşturma tarihi **filtreden sonra** eklenir. Filtrede bu alanlara yazdığınız değer sessizce kaybolur — hata çıkmaz, yalnız etki görülmez. Durum belirlemek istiyorsanız kaydın ardından ayrı bir kancaya yazın.

> **Sunucu ve tarayıcı aynı biçimi üretmeli**
> 
> Biçim filtresi **sunucu tarafında** çalışır. Sayfadaki bazı rakamlar tarayıcı tarafında yeniden yazılır; iki taraf farklı biçim üretirse müşteri sayfa yüklenirken rakamın **gözle görülür biçimde değiştiğini** görür. Biçimi değiştiriyorsanız her iki tarafı da değiştirin.

## İlgili Makaleler

- [Fatura Tutar Kancaları](https://dev.wisecp.com/tr/fatura-tutar-kancalari)
- Sipariş Akışı Kancaları
- Fatura ve Ödeme Kancaları
