Files
butcem/scripts/seed/recurring.ts
T
2026-11-05 10:55:05 +03:00

202 lines
5.4 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<string, SeedRule[]> = {
"Ç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<string, Types.ObjectId>,
userIds: Map<string, string>,
): Promise<number> {
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;
}