diff --git a/scripts/seed/contacts.ts b/scripts/seed/contacts.ts new file mode 100644 index 0000000..d0ac2d6 --- /dev/null +++ b/scripts/seed/contacts.ts @@ -0,0 +1,158 @@ +import type { Types } from "mongoose"; +import { createContact, createContactEntry } from "@/features/contacts/service"; +import { AccountModel } from "@/models/account"; +import { ContactModel } from "@/models/contact"; +import type { ContactEntryKind, ContactRelation } from "@/shared/contacts"; +import { todayIstanbul } from "@/shared/dates"; +import { formatMoneyInput } from "@/shared/money"; +import { addDays } from "@/shared/recurrence"; +import { contactEntrySchema, contactFormSchema } from "@/shared/schemas/contact"; + +/** + * Örnek cariler (Faz 8a): vadesi yaklaşan alacak, kısmen ödenen borç, vadesi geçmiş küçük alacak + * ve "yalnızca ben" cari. İdempotent: cari `seedRef` ile; hareketler yalnızca cari ilk + * oluşturulduğunda eklenir. + */ +type SeedContact = { + ref: string; + household: string; + by: string; + name: string; + relation: ContactRelation; + phone?: string; + isPrivate?: boolean; + dueInDays?: number; + reminderDays?: number; + entries: { + kind: ContactEntryKind; + account: string; + amountMinor: number; + daysAgo: number; + description?: string; + }[]; +}; + +const CONTACTS: SeedContact[] = [ + { + ref: "ahmet", + household: "Çakır Ailesi", + by: "admin", + name: "Ahmet Yılmaz", + relation: "friend", + phone: "0532 000 00 01", + dueInDays: 5, + reminderDays: 7, + entries: [ + { + kind: "lend", + account: "Ziraat Maaş", + amountMinor: 5_000_00, + daysAgo: 70, + description: "Araba tamiri için", + }, + { kind: "collect", account: "Cüzdan", amountMinor: 2_000_00, daysAgo: 30 }, + ], + }, + { + ref: "hasan", + household: "Çakır Ailesi", + by: "admin", + name: "Hasan Çakır", + relation: "family", + dueInDays: 60, + entries: [ + { + kind: "borrow", + account: "Ziraat Maaş", + amountMinor: 20_000_00, + daysAgo: 120, + description: "Ev eşyası için", + }, + { kind: "repay", account: "Ziraat Maaş", amountMinor: 5_000_00, daysAgo: 60 }, + { kind: "repay", account: "Ziraat Maaş", amountMinor: 5_000_00, daysAgo: 10 }, + ], + }, + { + ref: "elif", + household: "Çakır Ailesi", + by: "ayse", + name: "Elif (komşu)", + relation: "neighbor", + dueInDays: -2, + entries: [ + { + kind: "lend", + account: "Cüzdan", + amountMinor: 300_00, + daysAgo: 12, + description: "Pazar alışverişi", + }, + ], + }, + { + ref: "selin", + household: "Çakır Ailesi", + by: "ayse", + name: "Selin (iş arkadaşı)", + relation: "work", + isPrivate: true, + entries: [{ kind: "lend", account: "Cüzdan", amountMinor: 1_000_00, daysAgo: 8 }], + }, + { + ref: "ali", + household: "Annemler", + by: "fatma", + name: "Ali Demir", + relation: "family", + dueInDays: 45, + entries: [{ kind: "borrow", account: "Vakıfbank Emekli", amountMinor: 3_000_00, daysAgo: 25 }], + }, +]; + +export async function seedContacts( + householdIds: Map, + userIds: Map, +): Promise { + const today = todayIstanbul(); + let created = 0; + for (const c of CONTACTS) { + const householdId = householdIds.get(c.household)!; + const seedRef = `contact:${c.ref}`; + if (await ContactModel.exists({ householdId, seedRef })) continue; + const by = userIds.get(c.by)!; + const oldest = Math.max(...c.entries.map((e) => e.daysAgo)); + const r = await createContact( + by, + String(householdId), + contactFormSchema.parse({ + name: c.name, + relation: c.relation, + phone: c.phone ?? "", + openingDate: addDays(today, -oldest - 1), + isPrivate: c.isPrivate ?? false, + dueDate: c.dueInDays !== undefined ? addDays(today, c.dueInDays) : null, + reminderDays: c.reminderDays ?? 3, + }), + ); + if (!r.ok) throw new Error(`Seed carisi oluşturulamadı (${c.name}): ${r.error}`); + await ContactModel.updateOne({ _id: r.data.id }, { $set: { seedRef } }); + for (const e of c.entries) { + const account = await AccountModel.findOne({ householdId, name: e.account }).lean(); + const x = await createContactEntry( + by, + String(householdId), + r.data.id, + contactEntrySchema.parse({ + kind: e.kind, + accountId: String(account!._id), + amount: formatMoneyInput(e.amountMinor), + date: addDays(today, -e.daysAgo), + description: e.description ?? "", + }), + ); + if (!x.ok) throw new Error(`Seed cari hareketi oluşturulamadı (${c.name}): ${x.error}`); + } + created++; + } + return created; +} diff --git a/scripts/seed/run.int.test.ts b/scripts/seed/run.int.test.ts index c84b071..6ca440a 100644 --- a/scripts/seed/run.int.test.ts +++ b/scripts/seed/run.int.test.ts @@ -47,6 +47,7 @@ describe("seed", () => { transactions: 0, recurringRules: 0, installmentPlans: 0, + contacts: 0, }); expect(await getDb().collection("user").countDocuments()).toBe(SEED_USERS.length + 1); expect(await HouseholdModel.countDocuments()).toBe(SEED_HOUSEHOLDS.length); diff --git a/scripts/seed/run.ts b/scripts/seed/run.ts index d1ba35a..11b2f40 100644 --- a/scripts/seed/run.ts +++ b/scripts/seed/run.ts @@ -10,6 +10,7 @@ import { HouseholdModel } from "@/models/household"; import { HouseholdMemberModel } from "@/models/household-member"; import { storedEmail } from "@/shared/account-email"; import { runMigrations } from "../migrations"; +import { seedContacts } from "./contacts"; import { seedInstallments } from "./installments"; import { seedRecurring } from "./recurring"; import { seedTransactions } from "./transactions"; @@ -47,6 +48,7 @@ export type SeedSummary = { transactions: number; recurringRules: number; installmentPlans: number; + contacts: number; }; accounts: SeedAccountRow[]; }; @@ -64,6 +66,7 @@ const DOMAIN_COLLECTIONS = [ "reconciliations", "recurringRules", "statements", + "contacts", ]; type AuthApi = ReturnType["api"]; @@ -115,6 +118,7 @@ export async function runSeed({ transactions: 0, recurringRules: 0, installmentPlans: 0, + contacts: 0, }, accounts: [], }; @@ -289,6 +293,8 @@ export async function runSeed({ ); if (summary.created.installmentPlans) log(`✓ Örnek taksitli alışverişler: ${summary.created.installmentPlans}`); + summary.created.contacts = await seedContacts(householdIds, userIdMap); + if (summary.created.contacts) log(`✓ Örnek cariler: ${summary.created.contacts}`); // 7) Geliştirme hesapları tablosu const all = [