import type { Types } from "mongoose"; import { createRule } from "@/features/recurring/service"; import { AccountModel } from "@/models/account"; import { CategoryModel } from "@/models/category"; import { RecurringRuleModel } from "@/models/recurring-rule"; import { todayIstanbul } from "@/shared/dates"; import { addDays, addMonths } from "@/shared/recurrence"; import { recurringRuleFormSchema, type RecurringRuleFormInput } from "@/shared/schemas/recurring"; import { formatMoneyInput } from "@/shared/money"; /** * Örnek düzenli işlemler (Faz 7a). Seed işlemleri bugüne kadarki günleri doldurur; kurallar bir * sonraki gerçekleşme gününden başlar (çakışma yok). "Spor salonu" kasıtlı olarak 3 gün önce * başlar: vadesi geçmiş planlı kayıt örneği. İdempotent (`seedRef`). */ type SeedRule = { ref: string; by: string; type: "income" | "expense" | "transfer"; amountMinor: number; account: string; category?: string; payee: string; /** Ayın günü (bir sonraki gerçekleşme) veya bugüne göre gün farkı. */ day?: number; offsetDays?: number; isEstimate?: boolean; count?: number; }; const RULES: Record = { "Çakır Ailesi": [ { ref: "maas", by: "admin", type: "income", amountMinor: 52_000_00, account: "Ziraat Maaş", category: "maas", payee: "Anadolu Yazılım A.Ş.", day: 15, }, { ref: "kira", by: "admin", type: "expense", amountMinor: 17_500_00, account: "Ziraat Maaş", category: "kira", payee: "Ev sahibi", day: 3, }, { ref: "aidat", by: "admin", type: "expense", amountMinor: 1_250_00, account: "Ziraat Maaş", category: "aidat", payee: "Site yönetimi", day: 5, }, { ref: "elektrik", by: "ayse", type: "expense", amountMinor: 1_200_00, account: "Ziraat Maaş", category: "faturalar.elektrik", payee: "Enerjisa", day: 10, isEstimate: true, }, { ref: "su", by: "ayse", type: "expense", amountMinor: 450_00, account: "Ziraat Maaş", category: "faturalar.su", payee: "İSKİ", day: 12, isEstimate: true, }, { ref: "dogalgaz", by: "ayse", type: "expense", amountMinor: 2_500_00, account: "Ziraat Maaş", category: "faturalar.dogalgaz", payee: "İGDAŞ", day: 14, isEstimate: true, }, { ref: "internet", by: "admin", type: "expense", amountMinor: 499_90, account: "Garanti Bonus", category: "faturalar.internet", payee: "Türk Telekom", day: 20, }, { ref: "netflix", by: "admin", type: "expense", amountMinor: 229_99, account: "Garanti Bonus", category: "abonelikler", payee: "Netflix", day: 8, }, { ref: "spor", by: "emre", type: "expense", amountMinor: 1_250_00, account: "Garanti Bonus", category: "eglence", payee: "Spor salonu", offsetDays: -3, count: 12, }, ], Annemler: [ { ref: "emekli", by: "fatma", type: "income", amountMinor: 24_350_00, account: "Vakıfbank Emekli", category: "maas", payee: "SGK", day: 17, }, { ref: "elektrik", by: "fatma", type: "expense", amountMinor: 700_00, account: "Vakıfbank Emekli", category: "faturalar.elektrik", payee: "Enerjisa", day: 11, isEstimate: true, }, ], }; /** Bugünden sonraki ilk "ayın N. günü". */ function nextMonthDay(today: string, day: number): string { const thisMonth = addMonths(today, 0, day); return thisMonth > today ? thisMonth : addMonths(today, 1, day); } export async function seedRecurring( householdIds: Map, userIds: Map, ): Promise { const today = todayIstanbul(); let created = 0; for (const [household, rules] of Object.entries(RULES)) { const householdId = householdIds.get(household)!; const [accounts, categories] = await Promise.all([ AccountModel.find({ householdId }).select({ name: 1 }).lean(), CategoryModel.find({ householdId, defaultKey: { $ne: null } }) .select({ defaultKey: 1 }) .lean(), ]); for (const r of rules) { const seedRef = `rule:${r.ref}`; if (await RecurringRuleModel.exists({ householdId, seedRef })) continue; const input: RecurringRuleFormInput = { type: r.type, amount: formatMoneyInput(r.amountMinor), isEstimate: r.isEstimate ?? false, accountId: String(accounts.find((a) => a.name === r.account)!._id), categoryId: r.category ? String(categories.find((c) => c.defaultKey === r.category)!._id) : null, payee: r.payee, startDate: r.day ? nextMonthDay(today, r.day) : addDays(today, r.offsetDays ?? 0), frequency: "monthly", endKind: r.count ? "count" : "never", count: r.count ?? null, }; const result = await createRule( userIds.get(r.by)!, String(householdId), recurringRuleFormSchema.parse(input), ); if (!result.ok) throw new Error(`Seed kuralı oluşturulamadı (${seedRef}): ${result.error}`); await RecurringRuleModel.updateOne({ _id: result.data.id }, { $set: { seedRef } }); created++; } } return created; }