feat(kakeibo): dört harcama grubu, banka maliyeti ve geri alınabilir migration

- shared/kakeibo: grup devralma, banka maliyeti, kayıt bazında grup, grup özeti (saf, testli)
- kategori ve işlem/kalem modellerine kakeiboGroup ve bankCost; form şemaları
- migration 016 varsayılanları doldurur; npm run migrate -- --down <ad> ile geri alma
- Kakeibo rapor servisi: tutarlar bütçeyle aynı kaynaktan, grup değiştirmeler taşınır

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-07 04:51:35 +03:00
parent 18c892bdcd
commit afc6da1606
17 changed files with 831 additions and 13 deletions
+22
View File
@@ -15,6 +15,7 @@ import { migration as m012 } from "./012-stock-counts";
import { migration as m013 } from "./013-stores-products";
import { migration as m014 } from "./014-product-prices";
import { migration as m015 } from "./015-shopping-lists";
import { migration as m016 } from "./016-kakeibo-groups";
import type { Migration } from "./types";
/** Sıralı migration listesi. Yeni migration: dosyayı ekle ve buraya sırayla ekle. */
@@ -34,6 +35,7 @@ export const MIGRATIONS: Migration[] = [
m013,
m014,
m015,
m016,
];
const COLLECTION = "migrations";
@@ -63,3 +65,23 @@ export async function runMigrations(log: (line: string) => void = console.log):
if (!ran.length) log("• Bekleyen migration yok.");
return ran;
}
/**
* Bir migration'ı geri alır (yalnızca `down` tanımlıysa) ve kaydını siler; bir sonraki
* `runMigrations` onu yeniden uygular. Kullanım: npm run migrate -- --down 016-kakeibo-groups
*/
export async function revertMigration(
name: string,
log: (line: string) => void = console.log,
): Promise<void> {
const m = MIGRATIONS.find((x) => x.name === name);
if (!m) throw new Error(`Migration bulunamadı: ${name}`);
if (!m.down) throw new Error(`${name} geri alınamaz (down tanımlı değil).`);
const db = getDb();
await connectMongoose();
const col = db.collection<{ name: string }>(COLLECTION);
if (!(await col.findOne({ name }))) throw new Error(`${name} uygulanmamış.`);
await m.down(db);
await col.deleteOne({ name });
log(`↩ Geri alındı: ${name}`);
}