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:
2026-11-05 10:55:05 +03:00
parent eaafe5dd9b
commit 04a17d2775
13 changed files with 1493 additions and 7 deletions
+9 -6
View File
@@ -130,7 +130,7 @@ export async function loadContext(
export type Ctx = NonNullable<Awaited<ReturnType<typeof loadContext>>>;
/** Gizli hesaplara dokunan işlemleri dışlayan temel filtre. */
function baseFilter(ctx: Ctx): Record<string, unknown> {
export function baseFilter(ctx: Ctx): Record<string, unknown> {
const f: Record<string, unknown> = { householdId: ctx.m.householdId, deletedAt: null };
if (ctx.hiddenAccountIds.length) {
f.accountId = { $nin: ctx.hiddenAccountIds };
@@ -206,7 +206,7 @@ type Existing = {
splits?: { categoryId: Types.ObjectId }[];
};
function buildDoc(
export function buildDoc(
ctx: Ctx,
input: TransactionForm,
existing?: Existing,
@@ -280,7 +280,7 @@ function buildDoc(
}
/** Satıcı kuralını günceller (son kategori ve hesap) ve kimliğini döner. */
async function touchPayee(
export async function touchPayee(
householdId: Types.ObjectId,
name: string,
type: TransactionType,
@@ -384,7 +384,7 @@ async function attach(
);
}
async function loadOwnTransaction(
export async function loadOwnTransaction(
ctx: Ctx,
transactionId: string,
{ includeDeleted = false } = {},
@@ -441,6 +441,8 @@ export async function updateTransaction(
payeeId,
updatedBy: new Types.ObjectId(actorId),
...(balanceChanged ? { reconciledAccountIds: [] } : {}),
// Serinin planlı tekrarı tek başına düzenlendi ("yalnızca bu").
...(current.recurringRuleId && current.status === "planned" ? { detached: true } : {}),
},
},
{ session },
@@ -582,7 +584,8 @@ function filterQuery(ctx: Ctx, f: TransactionFilters): Record<string, unknown> {
};
}
if (f.type) q.type = f.type;
if (f.durum) q.status = f.durum;
// Planlı kayıtlar (12 ay ileriye kadar) yalnızca açıkça istenince listelenir (/planli sayfası).
q.status = f.durum ?? { $ne: "planned" };
if (f.hesap) {
const id = toObjectId(f.hesap);
// Gizli hesap filtresi sonuç vermez (baseFilter zaten dışlar).
@@ -630,7 +633,7 @@ export async function listTransactions(
{
$match: {
...query,
status: query.status ?? "posted",
status: filters.durum ?? "posted",
type: query.type ?? { $in: ["income", "expense"] },
},
},