# Veri Alışverişi
Package: @dataliva/livalib
Description: @dataliva/livalib DataExchange modülü — sözleşme tabanlı Excel içe/dışa aktarma, içe aktarma sihirbazı ve adaptörler.

Şirket genelinde ortak olan içe/dışa aktarma standardının önyüz tarafı.
Grid'e bir **process key** verirsiniz; kolon şemasını, zorunlu alanları, lookup
eşlemelerini ve doğrulama kurallarını sunucu bildirir. Livalib bu sözleşmeyi
okuyup şablon indirme, doğrulama, önizleme ve içe aktarma adımlarını kendisi
yürütür — sayfada elle Excel ayrıştırma kodu kalmaz.

```tsx
import {
  LivaGridConfigProvider,
  LivaImportWizard,
  useDataExchange,
  useContract,
  type DataExchangeContract,
} from "@dataliva/livalib";
```

## Kurulum — `LivaGridConfigProvider`

Uygulama kökünde bir kez sarmalayın. Modülün tüm HTTP çağrıları buradaki
yapılandırmayı kullanır: `token` `Authorization: Bearer …`, `lang` ise
`Accept-Language` başlığına yazılır.

```tsx
<LivaGridConfigProvider
  dataExchange={{ baseUrl: `${API_URL}/data-exchange` }}
  token={() => getToken()}
  lang="TR"
>
  <App />
</LivaGridConfigProvider>
```

| Alan | Tip | Açıklama |
|------|------|---------|
| `dataExchange.baseUrl` | `string` | Uç nokta kökü. Varsayılan `/api/data-exchange`. |
| `dataExchange.endpointsResolver` | `(processKey) => DataExchangeEndpoints` | Rota şeması standarda uymuyorsa altı uç noktayı elle üretin. |
| `dataExchange.headerPolicy` | `'tech-suffix' \| 'header-only'` | Şablon başlıklarında teknik alan adının gösterilip gösterilmeyeceği. |
| `token` | `string \| (() => string \| undefined)` | Fonksiyon verin; token yenilendiğinde provider'ı yeniden render etmeye gerek kalmaz. |
| `lang` | `string` | `Accept-Language` başlığı (ör. `"TR"`). |
| `fetch` | `typeof fetch` | Test veya interceptor için `fetch` değişimi. |

`baseUrl` verildiğinde uç noktalar şu kalıptan türetilir:

| Uç nokta | Rota |
|---|---|
| `definition` | `{base}/{processKey}/definition` |
| `template` | `{base}/{processKey}/template` |
| `export` | `{base}/{processKey}/export` |
| `import` | `{base}/{processKey}/import` |
| `validate` | `{base}/{processKey}/validate` |
| `status` | `{base}/{processKey}/status` |

`validate` ve `import` rotalarının sonundaki ek yollar (`/validate-rows`,
`/validate-source`, `/import-rows`, `/import-source`) bu iki taban üzerinden
otomatik türetilir. Adaptör kataloğu ise `{base}/adapters?capability=read`
adresinden okunur.

## Grid'e bağlama — bildirimsel yol

`LivaDataGrid` ve `LivaTreeList`, `importExportSettings.processKey` verildiğinde
içe aktarma sihirbazını **kendiliğinden** monte eder. Ayrıca araç çubuğundaki
menü de sözleşmeye göre değişir: *Boş Şablon İndir* ve *Verili Şablon İndir*
öğeleri çıkar, eski *SQL Export / SQL Import* öğeleri gizlenir.

```tsx
<LivaDataGrid
  {...getGridProps()}
  keyExpr="id"
  columns={kolonlar}
  dataSource={ds}
  importExportSettings={{
    enabled: true,
    processKey: "expense-definition",
    routeParams: { budgetYear, companyId },
    wizardTitle: "Masraf Tanımı İçe Aktarma",
  }}
/>
```

| Alan | Tip | Açıklama |
|------|------|---------|
| `enabled` | `boolean` | Araç çubuğundaki içe/dışa aktarma butonunu açar. |
| `processKey` | `string` | Sözleşme anahtarı. **Verildiğinde sihirbaz devreye girer.** |
| `baseUrl` | `string` | Yalnızca bu grid için kök adres değişimi. |
| `endpoints` | `Partial<DataExchangeEndpoints>` | Tek tek uç nokta ezme. |
| `headerPolicy` | `'tech-suffix' \| 'header-only'` | Başlık politikası ezme. |
| `contract` | `DataExchangeContract` | Sözleşme elde varsa `definition` isteği atlanır. |
| `autoProvision` | `Record<string, boolean>` | Eksik kayıtların otomatik oluşturulmasını alan bazında aç/kapat. |
| `routeParams` | `Record<string, string \| number \| boolean \| null \| undefined>` | Tüm isteklere query string olarak eklenir. |
| `config` | `Partial<LivaGridConfig>` | Provider yapılandırmasını bu grid için ezer. |
| `wizardTitle` | `string` | Sihirbaz başlığı. |
| `closeOnComplete` | `boolean` | Varsayılan `false` — sihirbaz rapor adımında açık kalır. |

> `routeParams`'ı eksik bırakmak en sık yapılan hatadır: yıl/şirket gibi bağlam
> sunucuya gitmezse şablon boş, doğrulama ise "kayıt bulunamadı" hatalarıyla
> döner.

### Eski kancalar (escape hatch)

Standart sihirbazdan çıkmak için `onImportExcel` verin — bu alan doluyken
sihirbaz **devre dışı kalır** ve klasik dosya seçici açılır. Aynı şekilde
`onExportExcel`, `onDownloadTemplate`, `onImportSettings` ve `onExportSettings`
kendi akışınızı bağlamak içindir. Yeni sayfalarda bunlara ihtiyaç olmamalıdır.

## İçe aktarma sihirbazı — `LivaImportWizard`

Grid dışında (ör. bir sayfa butonundan) elle de monte edilebilir:

```tsx
const [acik, setAcik] = useState(false);

<LivaImportWizard
  processKey="expense-definition"
  routeParams={{ budgetYear }}
  visible={acik}
  onHiding={() => setAcik(false)}
  onCompleted={(rapor) => {
    notifications.show({ title: "İçe aktarma", message: `${rapor.insertedCount} kayıt eklendi` });
    gridRef.current?.instance().refresh();
  }}
  title="Masraf Tanımı İçe Aktarma"
  translate={t}
/>
```

| Prop | Tip | Varsayılan |
|------|------|---------|
| `processKey` | `string` | — (zorunlu) |
| `visible` | `boolean` | — (zorunlu) |
| `onHiding` | `() => void` | — (zorunlu) |
| `baseUrl` | `string` | — |
| `endpoints` | `Partial<DataExchangeEndpoints>` | — |
| `contract` | `DataExchangeContract` | — (verilirse `definition` isteği atlanır) |
| `routeParams` | `RouteParams` | — |
| `config` | `Partial<LivaGridConfig>` | — |
| `onCompleted` | `(report: DataExchangeReport) => void` | — |
| `title` | `string` | — |
| `translate` | `(key: string) => string` | — |

### Adımlar

1. **Kaynak** — adaptör kataloğu `capability=read` ile çekilir. Katalogda tek
   adaptör varsa kendiliğinden seçilir. Adaptörün `transport` değeri akışı
   belirler: `multipart` dosya yükler, `source` ise adaptörün kendi alanlarını
   (`fields`) doldurtup sunucudan okur.
2. **Önizleme** — `validate` çağrılır; dönen `DataExchangePreview` satırları ve
   `errors` listesini tablo üzerinde gösterir. Hücreler burada **düzenlenebilir**;
   düzenleme yapıldığında satırlar `validate-rows` ile yeniden doğrulanır.
3. **Onay** — hata kalmadıysa açılır. Planlanan ekleme/güncelleme sayıları
   (`plannedInsertCount`, `plannedUpdateCount`) burada gösterilir.
4. **Rapor** — `DataExchangeReport` özeti. `closeOnComplete` verilmedikçe
   sihirbaz bu adımda açık kalır.

> Önizlemede satır düzenlendiyse içe aktarma, kaynak adaptörü ne olursa olsun
> `import-rows` üzerinden gönderilir — kullanıcının düzelttiği değerler böyle
> korunur.

## Serbest kullanım — `useDataExchange`

Sihirbaz dışında kendi arayüzünüzü kurmak için düşük seviyeli kanca. Uç nokta
çözümlemesi, başlıklar ve query string birleştirme sizin yerinize yapılır.

```tsx
const dx = useDataExchange({ processKey: "expense-definition", routeParams: { budgetYear } });

const blob = await dx.downloadTemplate(true);   // verili şablon
saveBlobAs(blob, "masraf-tanimi.xlsx");

const onizleme = await dx.validate(file);
if (onizleme.errors.length === 0) {
  const rapor = await dx.importFile(file);
}
```

| Metot | Döner | Açıklama |
|------|------|---------|
| `fetchContract()` | `DataExchangeContract` | Sözleşmeyi okur. |
| `downloadTemplate(withData?)` | `Blob` | `withData: true` verili şablon indirir. |
| `exportData(payload?)` | `Blob` | Dışa aktarma. |
| `validate(file)` | `DataExchangePreview` | Dosyayı doğrular (multipart). |
| `validateRows(rows)` | `DataExchangePreview` | Düzenlenmiş satırları doğrular. |
| `validateSource(kind, options)` | `DataExchangePreview` | Adaptör kaynağını doğrular. |
| `importFile(file)` | `DataExchangeReport` | Dosyayı içe aktarır. |
| `importRows(rows)` | `DataExchangeReport` | Satırları içe aktarır. |
| `importSource(kind, options)` | `DataExchangeReport` | Adaptör kaynağından içe aktarır. |
| `fetchAdapters(capability)` | `DataExchangeAdapterDescriptor[]` | `'read'` veya `'write'` kataloğu. |
| `endpoints` | `DataExchangeEndpoints` | Çözümlenmiş uç noktalar. |

`useContract(opts)` yalnızca sözleşmeyi getirir ve `{ contract, loading, error }`
döner — `opts.contract` verilmişse istek atılmaz. `saveBlobAs(blob, filename)`
dönen blob'u tarayıcıya indirtir.

## Sözleşme tipleri

`DataExchangeContract` sunucunun bildirdiği şemadır:

| Alan | Tip | Açıklama |
|------|------|---------|
| `processKey` | `string` | Süreç anahtarı |
| `displayNameKey` | `string \| null` | Çeviri anahtarı |
| `headerPolicy` | `{ technicalSuffix, suffixFormat }` | Şablon başlık biçimi |
| `upsertKey` | `string[]` | Kaydın kimliğini oluşturan alanlar |
| `columns` | `ColumnContract[]` | Kolon şeması |
| `autoProvisions` | `AutoProvisionContract[]` | Eksik kayıt otomatik oluşturma kuralları |

`ColumnContract`: `name`, `header`, `dataType`, `required`, `order`,
`groupHeader?`, `lookup?` (`registry` / `codeField` / `idField` / `labelField`),
`monthSeries?` (`prefix` / `count` / `countFrom` / `labelKey`), `enum?`
(`enumName` / `values` / `labelResource`) ve `autoProvision?`.

`DataExchangePreview`: `contract`, `rows`, `errors`, `totalRows`,
`plannedInsertCount?`, `plannedUpdateCount?`.

`DataExchangeReport`: `totalRows`, `insertedCount`, `updatedCount`,
`errorCount`, `autoProvisionedCount`, `recordsBefore?`, `recordsAfter?`,
`errors`, `messages`.

`CellError`: `rowNumber`, `column?`, `bucket`, `message` — `bucket` hatanın
sınıfıdır (biçim, lookup, zorunluluk vb.) ve önizlemede gruplama için kullanılır.

## Adaptörler

Bir adaptör, verinin nereden okunacağını tanımlar. Katalog sunucudan gelir;
`AdapterPicker` ve `AdapterFieldsForm` bileşenleri sihirbazın ilk adımını
oluşturur ve dışa da açılmıştır.

| Alan | Tip | Açıklama |
|------|------|---------|
| `key` | `string` | Adaptör kimliği |
| `displayNameKey` / `descriptionKey` | `string` | Çeviri anahtarları |
| `icon` | `string \| null` | DevExtreme ikon adı |
| `capabilities` | `string[]` | `["read"]` veya `["read", "write"]` |
| `transport` | `'multipart' \| 'json' \| 'source'` | Kullanılacak HTTP yüzeyi |
| `selectable` | `boolean` | Katalogda kullanıcıya gösterilir mi |
| `fields` | `AdapterField[]` | Kaynak adımında doldurulacak alanlar |

`AdapterField`: `name`, `labelKey`, `type` (`'file' \| 'text' \| 'lookup'`),
`required`, `lookupKey?` ve `lookupContext?`. `lookupContext` değerleri
`"${processKey}"` biçiminde şablon değişkeni içerebilir; sihirbaz bunları kendi
durumundan (`processKey` + `routeParams`) doldurur.

`transport: "json"` yalnızca sihirbazın kendi iç satır akışı içindir; kullanıcıya
sunulan bir adaptör için `multipart` ya da `source` kullanılır.
