feat(contacts): cari kişiler, hesap ↔ cari transferleri, cari ekstresi ve vade uyarısı, 007 migration
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,8 @@ export const AUDIT_ACTIONS = [
|
||||
"transaction.delete",
|
||||
"transaction.restore",
|
||||
"recurring.delete",
|
||||
"contact.archive",
|
||||
"contact.unarchive",
|
||||
] as const;
|
||||
export type AuditAction = (typeof AUDIT_ACTIONS)[number];
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { model, models, Schema, type InferSchemaType, type Model } from "mongoose";
|
||||
import { CONTACT_RELATIONS } from "@/shared/contacts";
|
||||
|
||||
/**
|
||||
* Cari kişi (Bölüm 6.4). Hareketler `transactions` içinde transfer olarak tutulur (`contactId`);
|
||||
* bakiye: açılış + çıkışlar − girişler (+ bana borçlu, − ben borçluyum).
|
||||
* "Yalnızca ben" işaretli cari ve hareketleri yalnızca sahibine görünür.
|
||||
*/
|
||||
const contactSchema = new Schema(
|
||||
{
|
||||
householdId: { type: Schema.Types.ObjectId, required: true },
|
||||
name: { type: String, required: true, trim: true, maxlength: 80 },
|
||||
relation: { type: String, enum: CONTACT_RELATIONS, default: "other" },
|
||||
phone: { type: String, default: "", maxlength: 30 },
|
||||
note: { type: String, default: "", maxlength: 500 },
|
||||
/** Uygulamaya başlamadan önceki bakiye (+ bana borçlu, − ben borçluyum). */
|
||||
openingBalanceMinor: { type: Number, default: 0 },
|
||||
openingDate: { type: Date, required: true },
|
||||
/** Kalan bakiyenin vadesi ("YYYY-MM-DD") ve kaç gün önce uyarılacağı. */
|
||||
dueDate: { type: String, default: null },
|
||||
reminderDays: { type: Number, default: 3, min: 0, max: 60 },
|
||||
isPrivate: { type: Boolean, default: false },
|
||||
ownerUserId: { type: Schema.Types.ObjectId, default: null },
|
||||
archivedAt: { type: Date, default: null },
|
||||
seedRef: { type: String, default: null },
|
||||
createdBy: { type: Schema.Types.ObjectId, required: true },
|
||||
updatedBy: { type: Schema.Types.ObjectId, required: true },
|
||||
},
|
||||
{ timestamps: true, collection: "contacts" },
|
||||
);
|
||||
|
||||
contactSchema.index({ householdId: 1, archivedAt: 1, name: 1 });
|
||||
contactSchema.index(
|
||||
{ householdId: 1, seedRef: 1 },
|
||||
{ unique: true, partialFilterExpression: { seedRef: { $type: "string" } } },
|
||||
);
|
||||
|
||||
export type Contact = InferSchemaType<typeof contactSchema>;
|
||||
|
||||
export const ContactModel: Model<Contact> =
|
||||
(models.Contact as Model<Contact> | undefined) ?? model<Contact>("Contact", contactSchema);
|
||||
@@ -1,4 +1,5 @@
|
||||
import { model, models, Schema, type InferSchemaType, type Model } from "mongoose";
|
||||
import { CONTACT_ENTRY_KINDS } from "@/shared/contacts";
|
||||
import { TRANSACTION_STATUSES, TRANSACTION_TYPES } from "@/shared/transactions";
|
||||
|
||||
/**
|
||||
@@ -21,7 +22,8 @@ const transactionSchema = new Schema(
|
||||
status: { type: String, enum: TRANSACTION_STATUSES, default: "posted" },
|
||||
/** İstanbul gününün başlangıç anı (UTC). */
|
||||
date: { type: Date, required: true },
|
||||
accountId: { type: Schema.Types.ObjectId, required: true },
|
||||
/** Kaynak hesap. Cariden girişte (borç alma, tahsilat) null; hedef `toAccountId`. */
|
||||
accountId: { type: Schema.Types.ObjectId, default: null },
|
||||
toAccountId: { type: Schema.Types.ObjectId, default: null },
|
||||
amountMinor: { type: Number, required: true, min: 1 },
|
||||
/** Bölünmemiş gelir/giderde kategori; bölünmüşte null ve kalemler `splits`te. */
|
||||
@@ -53,7 +55,9 @@ const transactionSchema = new Schema(
|
||||
default: null,
|
||||
},
|
||||
statementId: { type: Schema.Types.ObjectId, default: null },
|
||||
/** Cari transferi: karşı taraf kişi (hesabın biri boş). */
|
||||
contactId: { type: Schema.Types.ObjectId, default: null },
|
||||
contactKind: { type: String, enum: [...CONTACT_ENTRY_KINDS, null], default: null },
|
||||
/**
|
||||
* Mutabakatı yapılmış hesaplar (Bölüm 6.1). Transfer iki hesaba dokunduğu için hesap bazında
|
||||
* tutulur. Tutar, tarih, tür veya hesap değişirse işaret kalkar.
|
||||
@@ -76,6 +80,7 @@ transactionSchema.index({ householdId: 1, accountId: 1, date: -1 });
|
||||
transactionSchema.index({ householdId: 1, toAccountId: 1, date: -1 });
|
||||
transactionSchema.index({ householdId: 1, effectiveCategoryIds: 1, date: -1 });
|
||||
transactionSchema.index({ householdId: 1, tags: 1 });
|
||||
transactionSchema.index({ householdId: 1, contactId: 1, date: 1 }, { sparse: true });
|
||||
transactionSchema.index({ "installment.planId": 1 }, { sparse: true });
|
||||
transactionSchema.index(
|
||||
{ recurringRuleId: 1, occurrenceIndex: 1 },
|
||||
|
||||
Reference in New Issue
Block a user