cc624af69f
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
73 lines
3.2 KiB
TypeScript
73 lines
3.2 KiB
TypeScript
import { model, models, Schema, type InferSchemaType, type Model } from "mongoose";
|
||
import { ACCOUNT_TYPES, DUE_RULE_KINDS, MEAL_CARD_PROVIDERS } from "@/shared/accounts";
|
||
import { ICON_KEYS, SWATCHES } from "@/shared/appearance";
|
||
|
||
/**
|
||
* Hesap (Bölüm 6.1). Tutarlar kuruş. Bakiye işareti: varlık pozitif, kart borcu negatif.
|
||
* Güncel bakiyenin tek doğruluk kaynağı işlemlerdir (Faz 6b); `balanceCacheMinor` yalnızca önbellek.
|
||
*/
|
||
const overdraftSchema = new Schema(
|
||
{
|
||
enabled: { type: Boolean, default: false },
|
||
limitMinor: { type: Number, min: 0, default: 0 },
|
||
/** Aylık faiz oranı, baz puan (4,25% → 425). */
|
||
monthlyRateBp: { type: Number, min: 0, max: 10_000, default: null },
|
||
statementDay: { type: Number, min: 1, max: 31, default: null },
|
||
},
|
||
{ _id: false },
|
||
);
|
||
|
||
const creditCardSchema = new Schema(
|
||
{
|
||
limitMinor: { type: Number, min: 0, default: 0 },
|
||
/** Hesap kesim günü; ayda yoksa (31) ayın son günü kullanılır (Faz 7). */
|
||
statementDay: { type: Number, min: 1, max: 31, default: 1 },
|
||
dueRule: {
|
||
kind: { type: String, enum: DUE_RULE_KINDS, default: "daysAfter" },
|
||
value: { type: Number, min: 1, max: 31, default: 10 },
|
||
},
|
||
/**
|
||
* Asgari ödeme oranı, baz puan (2000 = %20). null → varsayılan kural: limit 50.000 TL altı %20,
|
||
* üstü %40 (Faz 7 kararı).
|
||
*/
|
||
minimumPaymentRateBp: { type: Number, min: 0, max: 10_000, default: null },
|
||
/** Kart borcunun ödeneceği varsayılan hesap. */
|
||
paymentAccountId: { type: Schema.Types.ObjectId, default: null },
|
||
},
|
||
{ _id: false },
|
||
);
|
||
|
||
const accountSchema = new Schema(
|
||
{
|
||
householdId: { type: Schema.Types.ObjectId, required: true },
|
||
name: { type: String, required: true, trim: true, maxlength: 80 },
|
||
type: { type: String, enum: ACCOUNT_TYPES, required: true },
|
||
institution: { type: String, trim: true, maxlength: 80, default: "" },
|
||
mealCardProvider: { type: String, enum: [...MEAL_CARD_PROVIDERS, null], default: null },
|
||
/** İleride döviz hesabı için; şimdilik yalnızca TRY. */
|
||
currency: { type: String, enum: ["TRY"], default: "TRY" },
|
||
openingBalanceMinor: { type: Number, required: true, default: 0 },
|
||
openingDate: { type: Date, required: true },
|
||
color: { type: String, enum: SWATCHES, default: "blue" },
|
||
icon: { type: String, enum: ICON_KEYS, default: "wallet" },
|
||
order: { type: Number, default: 0 },
|
||
archivedAt: { type: Date, default: null },
|
||
/** Hesabın sahibi kişi (hane üyesi). "Yalnızca ben" hesaplarında bakiyeyi yalnızca o görür. */
|
||
ownerUserId: { type: Schema.Types.ObjectId, default: null },
|
||
isPrivate: { type: Boolean, default: false },
|
||
overdraft: { type: overdraftSchema, default: null },
|
||
creditCard: { type: creditCardSchema, default: null },
|
||
balanceCacheMinor: { type: Number, default: null },
|
||
createdBy: { type: Schema.Types.ObjectId, required: true },
|
||
updatedBy: { type: Schema.Types.ObjectId, required: true },
|
||
},
|
||
{ timestamps: true, collection: "accounts" },
|
||
);
|
||
|
||
accountSchema.index({ householdId: 1, archivedAt: 1, order: 1 });
|
||
|
||
export type Account = InferSchemaType<typeof accountSchema>;
|
||
|
||
export const AccountModel: Model<Account> =
|
||
(models.Account as Model<Account> | undefined) ?? model<Account>("Account", accountSchema);
|