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:
@@ -0,0 +1,43 @@
|
||||
import { defaultKakeibo } from "@/features/categories/service";
|
||||
import { CategoryModel } from "@/models/category";
|
||||
import { TransactionModel } from "@/models/transaction";
|
||||
import type { Migration } from "./types";
|
||||
|
||||
/**
|
||||
* Kakeibo dört grup (Faz 16): gider kategorilerine `kakeiboGroup` ve `bankCost`. Varsayılan
|
||||
* kategoriler beceri eşlemesine göre doldurulur; kullanıcının kategorileri grupsuz kalır (arayüz
|
||||
* "grup seç" uyarısı gösterir). Alanı zaten olan kategoriye dokunulmaz. Hiçbir kategori silinmez.
|
||||
* Geri alma: alanlar kaldırılır (npm run migrate -- --down 016-kakeibo-groups).
|
||||
*/
|
||||
export const migration: Migration = {
|
||||
name: "016-kakeibo-groups",
|
||||
description: "gider kategorilerine Kakeibo grubu ve banka maliyeti; varsayılanlar dolduruldu",
|
||||
async up() {
|
||||
const defaults = await CategoryModel.find({
|
||||
kind: "expense",
|
||||
defaultKey: { $type: "string" },
|
||||
kakeiboGroup: { $exists: false },
|
||||
})
|
||||
.select({ defaultKey: 1 })
|
||||
.lean<{ _id: unknown; defaultKey: string }[]>();
|
||||
const ops = defaults.map((c) => ({
|
||||
updateOne: { filter: { _id: c._id }, update: { $set: defaultKakeibo(c.defaultKey) } },
|
||||
}));
|
||||
if (ops.length) await CategoryModel.bulkWrite(ops);
|
||||
await CategoryModel.updateMany(
|
||||
{ kakeiboGroup: { $exists: false } },
|
||||
{ $set: { kakeiboGroup: null, bankCost: false } },
|
||||
);
|
||||
},
|
||||
async down() {
|
||||
await CategoryModel.updateMany({}, { $unset: { kakeiboGroup: "", bankCost: "" } });
|
||||
await TransactionModel.updateMany(
|
||||
{ kakeiboGroup: { $exists: true } },
|
||||
{ $unset: { kakeiboGroup: "" } },
|
||||
);
|
||||
await TransactionModel.updateMany(
|
||||
{ "splits.kakeiboGroup": { $exists: true } },
|
||||
{ $unset: { "splits.$[].kakeiboGroup": "" } },
|
||||
);
|
||||
},
|
||||
};
|
||||
@@ -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}`);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import type { Db } from "mongodb";
|
||||
|
||||
/** Bir migration: adı dosya adıyla aynı olmalı (ör. "001-initial-indexes"). Geri alma yok; ileri yönlü. */
|
||||
/**
|
||||
* Bir migration: adı dosya adıyla aynı olmalı (ör. "001-initial-indexes"). İleri yönlü; Faz 16'dan
|
||||
* itibaren veri değiştirenler `down` ile geri alınabilir (npm run migrate -- --down <ad>).
|
||||
*/
|
||||
export type Migration = {
|
||||
name: string;
|
||||
description: string;
|
||||
up: (db: Db) => Promise<void>;
|
||||
down?: (db: Db) => Promise<void>;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user