feat(ekstre): banka/kart ekstresi içe aktarma ekranı

- Ayarlar → Ekstre içe aktar: Excel (.xlsx/.xls) ve CSV, dosya tarayıcıda okunur
- Başlık satırı ve sütunlar otomatik bulunur; tek sütun / Borç-Alacak; eşleştirme profil olarak saklanır
- Gözden geçirme: tür + kategori önerisi, aynı açıklamalara otomatik yayılma, tekrar/engelli satırlar
- Öğrenilen kurallarda ilk kelime yedeği (MIGROS İSTANBUL → MIGROS KADIKÖY)
- Defterde iade etiketi; iade düzenlemede eksi kalır
- SheetJS resmi paketi frontend/vendor altında (npm 12 uzak paket indirmeyi engeller)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-13 08:58:04 +03:00
parent d1a24afc3c
commit 233907e3d9
24 changed files with 1493 additions and 8 deletions
+8 -3
View File
@@ -32,7 +32,8 @@ function initialValues(
): EntryForm {
if (entry) {
return {
amount: moneyToText(entry.amount),
// İade (ekstreden, eksi tutar) formda artı gösterilir, eksi kaydedilir
amount: moneyToText(Math.abs(entry.amount)),
group: entry.group,
category_id: entry.category_id,
payment_method_id: entry.payment_method_id ?? '',
@@ -143,7 +144,7 @@ export function EntryForm({
id: entry.id,
version: entry.version,
date: v.date,
amount: toKurus(v.amount),
amount: entry.amount < 0 ? -toKurus(v.amount) : toKurus(v.amount),
category_id: v.category_id,
payment_method_id: v.payment_method_id, // '' → kaldır
description: v.description.trim(), // '' → kaldır
@@ -182,7 +183,11 @@ export function EntryForm({
control={control}
name="amount"
render={({ field }) => (
<FormField label={s.amount} error={errors.amount}>
<FormField
label={s.amount}
error={errors.amount}
hint={entry && entry.amount < 0 ? s.refundHint : undefined}
>
{(f) => (
<MoneyInput
{...f}
+4 -1
View File
@@ -117,7 +117,10 @@ export function EntryList({
{details.slice(1).map((d) => ` · ${d}`)}
</p>
</div>
<span className="money shrink-0 font-medium">{formatMoney(e.amount)}</span>
<span className={cn('money shrink-0 font-medium', e.amount < 0 && 'text-income')}>
{e.amount < 0 && <span className="mr-1 text-xs font-normal">{s.refund}</span>}
{formatMoney(e.amount)}
</span>
{!readOnly && (
<div className="flex shrink-0">
<Button