feat(ekstre): banka/kart ekstresi içe aktarma altyapısı (arşiv dönemleri, iade, öğrenen kurallar)

- Arşiv dönemi durumu: geçmiş aylar için; kapatılmaz, devir zincirinde atlanır, bütçe önerilerini besler
- POST /bank-import/preview ve /commit: harcama / iade (eksi) / gelir (plan) / yoksay
- Tekrar tespiti (import_key), açıklamadan öğrenen kategori kuralları, ödeme/virman ipuçları
- Kapalı dönem ve gelecek tarih satır hatası; iade düzenlemede eksi kalır

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-13 08:33:07 +03:00
parent 4d764689e2
commit d1a24afc3c
28 changed files with 1422 additions and 13 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ export function PeriodHeading({ period, actions }: { period: Period; actions?: R
<span
className={cn(
'rounded-full px-2.5 py-0.5 text-xs font-medium',
period.status === 'closed' ? 'bg-muted text-muted-foreground' : 'bg-warning/15',
period.status === 'correcting' ? 'bg-warning/15' : 'bg-muted text-muted-foreground',
)}
>
{t.periods.status[period.status]}
+4 -2
View File
@@ -52,8 +52,10 @@ export function ClosePeriodPage() {
// Açık dönem ya da `?donem=` ile düzeltmedeki dönem (yeniden kapatma)
<SelectedPeriod empty={empty}>
{(p) =>
p.status === 'closed' ? (
<p className="text-muted-foreground">{s.alreadyClosed}</p>
p.status === 'closed' || p.status === 'archived' ? (
<p className="text-muted-foreground">
{p.status === 'closed' ? s.alreadyClosed : s.archived}
</p>
) : (
<Wizard key={p.id} period={p} onClosed={setResult} />
)
@@ -20,6 +20,14 @@ const s = t.review
*/
export function ClosingSection({ period, periods }: { period: Period; periods: Period[] }) {
if (period.status === 'closed') return <ClosedInfo period={period} periods={periods} />
if (period.status === 'archived') {
return (
<section className="space-y-1 rounded-2xl border bg-card p-5">
<h2 className="font-semibold">{s.archivedTitle}</h2>
<p className="text-sm text-muted-foreground">{s.archivedHint}</p>
</section>
)
}
const correcting = period.status === 'correcting'
return (
<section
+10 -1
View File
@@ -348,6 +348,9 @@ export const t = {
correctingHint:
'Kayıtları ve planı düzelt, bitince yeniden kapat; sonraki döneme devir güncellenir.',
recloseAction: '🔒 Yeniden Kapat',
archivedTitle: 'Arşiv dönemi',
archivedHint:
'Ekstreden aktarılan geçmiş bir ay. Kayıtları düzeltebilirsin; kapatılmaz, devir üretmez ve bütçe önerilerinde (önceki dönem, son 3 ay) kullanılır.',
fixInLedger: 'Defter’de düzelt',
fixInPlan: 'Plan’da düzelt',
},
@@ -611,7 +614,12 @@ export const t = {
periods: {
picker: 'Dönem',
status: { open: 'Açık', closed: '🔒 Kapalı', correcting: '✏️ Düzeltmede' },
status: {
open: 'Açık',
closed: '🔒 Kapalı',
correcting: '✏️ Düzeltmede',
archived: '🗄️ Arşiv',
},
notFound: 'Dönem bulunamadı. Listeden başka bir dönem seç.',
history: 'Geçmiş dönemler',
toSavings: (x: string) => `Birikime ${x}`,
@@ -655,6 +663,7 @@ export const t = {
toDashboard: 'Panele dön',
noOpen: 'Kapatılacak açık dönem yok.',
alreadyClosed: 'Bu dönem zaten kapalı.',
archived: 'Arşiv dönemi kapatılmaz; yalnızca geçmiş kayıtları tutar.',
addToSavings: (x: string) => `🐷 Birikime ekle (${x})`,
addToSavingsHint:
'Onaylarsan bu tutar Birikimler’de, dönemin son günü tarihli Türk Lirası alımı olarak görünür.',
+7
View File
@@ -0,0 +1,7 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type BankArchive = { name: string, start_date: string,
/**
* Son gün, dahil
*/
last_date: string, };
+8
View File
@@ -0,0 +1,8 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { BankRowIn } from "./BankRowIn";
export type BankCommitRequest = {
/**
* Kayıtlara yazılacak ödeme türü (ör. Kredi Kartı)
*/
payment_method_id?: string, rows: Array<BankRowIn>, };
+8
View File
@@ -0,0 +1,8 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { BankRowError } from "./BankRowError";
export type BankImportReport = { entries: number, refunds: number, incomes: number, ignored: number, duplicates: number,
/**
* Açılan arşiv dönemleri
*/
archives: Array<string>, errors: Array<BankRowError>, };
+9
View File
@@ -0,0 +1,9 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { BankArchive } from "./BankArchive";
import type { BankPreviewRow } from "./BankPreviewRow";
export type BankPreview = { rows: Array<BankPreviewRow>,
/**
* Açılacak arşiv dönemleri
*/
archives: Array<BankArchive>, };
+4
View File
@@ -0,0 +1,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { BankRowIn } from "./BankRowIn";
export type BankPreviewRequest = { source: "card" | "account", rows: Array<BankRowIn>, };
+23
View File
@@ -0,0 +1,23 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type BankPreviewRow = { kind: "expense" | "refund" | "income" | "ignore", category_id: string | null,
/**
* Öneri hanenin öğrendiği kuraldan
*/
from_rule: boolean,
/**
* Daha önce aktarılmış (atlanacak)
*/
duplicate: boolean,
/**
* `existing` · `archive` · `blocked`
*/
period: "existing" | "archive" | "blocked",
/**
* Dönem adı (var olan ya da açılacak arşiv)
*/
period_name: string | null,
/**
* Engel nedeni
*/
message: string | null, };
+7
View File
@@ -0,0 +1,7 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type BankRowError = {
/**
* Satır sırası (0'dan)
*/
index: number, message: string, };
+15
View File
@@ -0,0 +1,15 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type BankRowIn = {
/**
* `"YYYY-MM-DD"`
*/
date: string, description: string,
/**
* Kuruş, > 0
*/
amount: number, direction: "in" | "out",
/**
* Yalnızca aktarmada (kullanıcının seçimi)
*/
kind?: "expense" | "refund" | "income" | "ignore", category_id?: string, };
+1 -1
View File
@@ -1,3 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type PeriodStatus = "open" | "closed" | "correcting";
export type PeriodStatus = "open" | "closed" | "correcting" | "archived";