feat(recurring): düzenli işlem kuralları, planlı kayıt üretimi, onay/atla/ertele ve seri düzenleme
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import { model, models, Schema, type InferSchemaType, type Model } from "mongoose";
|
||||
import { FREQUENCIES, MONTHLY_MODES } from "@/shared/recurrence";
|
||||
import { TRANSACTION_TYPES } from "@/shared/transactions";
|
||||
|
||||
/**
|
||||
* Düzenli işlem kuralı (Bölüm 6.7). Her tekrar için `planned` durumunda bir işlem üretilir
|
||||
* (`transactions.recurringRuleId` + `occurrenceIndex`, benzersiz). Bitiş tarihsiz kurallarda
|
||||
* ufuk 12 ay; okuma sırasında ilerletilir.
|
||||
*/
|
||||
const recurringRuleSchema = new Schema(
|
||||
{
|
||||
householdId: { type: Schema.Types.ObjectId, required: true },
|
||||
// İşlem şablonu (bölme yok).
|
||||
type: { type: String, enum: TRANSACTION_TYPES, required: true },
|
||||
amountMinor: { type: Number, required: true, min: 1 },
|
||||
/** Tutar tahmini mi (ör. fatura)? Onaylarken düzeltilir. */
|
||||
isEstimate: { type: Boolean, default: false },
|
||||
accountId: { type: Schema.Types.ObjectId, required: true },
|
||||
toAccountId: { type: Schema.Types.ObjectId, default: null },
|
||||
categoryId: { type: Schema.Types.ObjectId, default: null },
|
||||
payeeName: { type: String, default: "", maxlength: 80 },
|
||||
description: { type: String, default: "", maxlength: 500 },
|
||||
tags: { type: [String], default: [] },
|
||||
// Tekrar.
|
||||
frequency: { type: String, enum: FREQUENCIES, required: true },
|
||||
interval: { type: Number, default: 1, min: 1, max: 12 },
|
||||
monthlyMode: { type: String, enum: MONTHLY_MODES, default: "day" },
|
||||
/** "YYYY-MM-DD" (İstanbul günü). */
|
||||
startDate: { type: String, required: true },
|
||||
endDate: { type: String, default: null },
|
||||
count: { type: Number, default: null, min: 1 },
|
||||
/** Üretilen son ufuk ("YYYY-MM-DD"); null → hiç üretilmedi. */
|
||||
generatedUntil: { type: String, default: null },
|
||||
deletedAt: { 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: "recurringRules" },
|
||||
);
|
||||
|
||||
recurringRuleSchema.index({ householdId: 1, deletedAt: 1, generatedUntil: 1 });
|
||||
recurringRuleSchema.index(
|
||||
{ householdId: 1, seedRef: 1 },
|
||||
{ unique: true, partialFilterExpression: { seedRef: { $type: "string" } } },
|
||||
);
|
||||
|
||||
export type RecurringRule = InferSchemaType<typeof recurringRuleSchema>;
|
||||
|
||||
export const RecurringRuleModel: Model<RecurringRule> =
|
||||
(models.RecurringRule as Model<RecurringRule> | undefined) ??
|
||||
model<RecurringRule>("RecurringRule", recurringRuleSchema);
|
||||
Reference in New Issue
Block a user