> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flextell.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Rehber: Satış ve tahsilat kaydı

> Bir hastaya ürün veya tedavi satışı yaratma, ardından kısmi/tam tahsilat ekleme.

Flextell'de satış (`sale`), bir veya birden fazla **kalem** (tedavi veya ürün) ve opsiyonel olarak bir **randevu** ile ilişkilendirilir. Ödeme kaydı ayrı bir aksiyondur; tek bir POST ile satış + ödeme birleştirilmez.

<Note>
  Gerekli scope'lar: `sales:write`, ödeme eklemek için yine `sales:write`. Satışı listelemek için `sales:read`.
</Note>

## 1. Satış oluştur

```bash theme={null}
curl --request POST \
  --url "https://dev.flextell.ai/api/v1/sales" \
  --header "Authorization: Bearer $TOKEN" \
  --header "X-Tenant: 12" \
  --header "Content-Type: application/json" \
  --data '{
    "customer_id": 391,
    "appointment_id": 1102,
    "currency_id": 1,
    "exchange_rate": 1,
    "status": "completed",
    "notes": "Kontrol + röntgen",
    "items": [
      {
        "staff_id": 7,
        "itemable_type": "Treatment",
        "itemable_id": 22,
        "quantity": 1,
        "unit_price": 500,
        "tax_rate": 20,
        "discount_amount": 0,
        "discount_percentage": 0
      },
      {
        "staff_id": 7,
        "itemable_type": "Treatment",
        "itemable_id": 35,
        "quantity": 1,
        "unit_price": 300,
        "tax_rate": 20,
        "discount_amount": 0,
        "discount_percentage": 0
      }
    ]
  }'
```

### Alan açıklamaları

<ParamField body="customer_id" type="integer" required>
  Satışın hangi hastaya olduğunu belirtir. Aynı tenant'ta olmalıdır.
</ParamField>

<ParamField body="appointment_id" type="integer">
  Satışı bir randevuya bağlamak opsiyoneldir ama raporlama için önerilir. Randevu aynı hastaya ait olmalıdır.
</ParamField>

<ParamField body="currency_id" type="integer" required>
  Satış para birimi. `GET /v1/account` altındaki desteklenen listeden alın.
</ParamField>

<ParamField body="exchange_rate" type="number" required>
  Satış anındaki kur. Ana para birimi cinsinden satış yapıyorsanız `1` gönderin.
</ParamField>

<ParamField body="status" type="enum" required>
  `draft`, `completed`, `cancelled`, `invoiced`, `pending`.
</ParamField>

<ParamField body="items" type="array" required>
  En az bir kalem zorunludur. `itemable_type` `Treatment` veya `Product` olabilir. `tax_rate` yalnızca `0, 1, 10, 20` değerlerinden biri olabilir.
</ParamField>

<Warning>
  `sub_total`, `total_discount`, `total_tax`, `total_price` alanlarını göndermeyin — sunucu `quantity × unit_price` üzerinden hesaplar. Gönderirseniz dikkatli olun; hesaplamanız yanlışsa rapor tutarlılığı bozulur.
</Warning>

## 2. Yanıt

```json theme={null}
{
  "success": true,
  "data": {
    "id": 701,
    "customer_id": 391,
    "status": "completed",
    "items": [
      { "id": 1501, "itemable_type": "Treatment", "itemable_id": 22, "total_price": 600 },
      { "id": 1502, "itemable_type": "Treatment", "itemable_id": 35, "total_price": 360 }
    ],
    "total_amount": 960,
    "paid_amount": 0,
    "balance": 960,
    "created_at": "2026-04-19T11:00:00+03:00"
  }
}
```

## 3. Tahsilat ekle

Hasta ödeme yaptığında:

```bash theme={null}
curl --request POST \
  --url "https://dev.flextell.ai/api/v1/sales/701/payments" \
  --header "Authorization: Bearer $TOKEN" \
  --header "X-Tenant: 12" \
  --header "Content-Type: application/json" \
  --data '{
    "amount": 500,
    "type": "collection",
    "account_id": 2,
    "transaction_date": "2026-04-19",
    "description": "Nakit ödeme"
  }'
```

Kısmi ödemeyse birden fazla kez çağırabilirsiniz; her biri yeni bir `customer_transaction` kaydı yaratır ve satışın `balance` değerini düşürür.

### Alan açıklamaları

<ParamField body="amount" type="number" required>
  Tahsilat tutarı. Para birimi varsayılan olarak satışın para birimidir.
</ParamField>

<ParamField body="type" type="enum">
  `collection` (tahsilat) veya `payment` (iade/geri ödeme). Varsayılan `collection`.
</ParamField>

<ParamField body="account_id" type="integer">
  Hangi kasa/hesap. Aynı tenant'ta olmalı.
</ParamField>

<ParamField body="currency_id" type="integer">
  Satış para birimi dışında bir para biriminde ödeme alınıyorsa belirtilir; `exchange_rate` de gönderilmelidir.
</ParamField>

## 4. Stok düşümü

Satıştaki bir kalem `itemable_type=Product` ise, satış oluşturduğunuzda stok düşümü otomatik yapılmaz. Manuel tetiklemek için:

```bash theme={null}
curl --request POST \
  --url "https://dev.flextell.ai/api/v1/sales/701/stock-deduct" \
  --header "Authorization: Bearer $TOKEN" \
  --header "X-Tenant: 12"
```

Bu işlem, kalemdeki `warehouse_id` değerini esas alarak `stock_movement` kaydı oluşturur.

<Warning>
  Aynı satış için `stock-deduct` birden fazla kez çalıştırılırsa **tekrar tekrar** stoktan düşer. Satış ile stok düşümü ayrı adım olduğu için bu çağrıyı mutlaka idempotent sargılayın (bkz. [Idempotency](/requests/idempotency)).
</Warning>

## 5. Satışı güncelleme

Kalemlerde veya notlarda değişiklik yapmak için `PUT /v1/sales/701` kullanabilirsiniz. Kalem listesini bütün halinde göndermeniz gerekir — eksik kalem silinmiş sayılır.

## Yaygın hatalar

| Hata                                                                                                          | Sebep                               | Çözüm                              |
| ------------------------------------------------------------------------------------------------------------- | ----------------------------------- | ---------------------------------- |
| `422` — "The selected customer does not belong to this tenant."                                               | Yanlış tenant                       | `X-Tenant`'ı doğrula               |
| `422` — "The selected appointment does not exist or does not belong to the provided customer in this tenant." | `appointment_id` farklı hastaya ait | Doğru randevuyu seç veya boş bırak |
| `422` — "The selected product or treatment does not exist or does not belong to this tenant."                 | `itemable_id` başka tenant'ta       | Doğru ID                           |
| `422` — "The tax rate must be 0, 1, 10 or 20."                                                                | Geçersiz KDV oranı                  | Listeden bir değer kullan          |

## Sonraki adımlar

<CardGroup cols={2}>
  <Card title="Randevu oluşturma" icon="calendar" href="/guides/create-appointment">
    Satışı bir randevuya bağlamak için önce randevu açın.
  </Card>

  <Card title="Rate limiting" icon="gauge" href="/requests/rate-limiting">
    Toplu satış girişi yaparken limit yönetimi.
  </Card>
</CardGroup>
