initial commit
This commit is contained in:
134
convex/lib/helpers.ts
Normal file
134
convex/lib/helpers.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import { getAuthUserId } from "@convex-dev/auth/server";
|
||||
import type { MutationCtx, QueryCtx } from "../_generated/server";
|
||||
import type { Id } from "../_generated/dataModel";
|
||||
import { categorize, roundEur } from "./categorize";
|
||||
import { computeEffectiveMonth, resolveAssignedAndEffective } from "./month";
|
||||
import { computeDedupHash } from "./comdirectMap";
|
||||
|
||||
export async function requireUserId(ctx: QueryCtx | MutationCtx): Promise<Id<"users">> {
|
||||
const userId = await getAuthUserId(ctx);
|
||||
if (!userId) throw new Error("Nicht angemeldet");
|
||||
return userId;
|
||||
}
|
||||
|
||||
export async function getAppSettings(ctx: QueryCtx | MutationCtx, userId: Id<"users">) {
|
||||
return await ctx.db
|
||||
.query("appSettings")
|
||||
.withIndex("by_user", (q) => q.eq("userId", userId))
|
||||
.unique();
|
||||
}
|
||||
|
||||
export async function getCategoryMap(ctx: QueryCtx | MutationCtx, userId: Id<"users">) {
|
||||
const categories = await ctx.db
|
||||
.query("categories")
|
||||
.withIndex("by_user", (q) => q.eq("userId", userId))
|
||||
.collect();
|
||||
const byName = new Map<string, Id<"categories">>();
|
||||
for (const cat of categories) {
|
||||
byName.set(cat.name, cat._id);
|
||||
}
|
||||
return byName;
|
||||
}
|
||||
|
||||
export async function resolveCategoryId(
|
||||
ctx: QueryCtx | MutationCtx,
|
||||
userId: Id<"users">,
|
||||
categoryName: string,
|
||||
): Promise<Id<"categories"> | undefined> {
|
||||
const cat = await ctx.db
|
||||
.query("categories")
|
||||
.withIndex("by_user_name", (q) => q.eq("userId", userId).eq("name", categoryName))
|
||||
.unique();
|
||||
return cat?._id;
|
||||
}
|
||||
|
||||
export type TransactionInput = {
|
||||
accountId?: Id<"accounts">;
|
||||
categoryId?: Id<"categories">;
|
||||
categoryName?: string;
|
||||
bookingDate?: string;
|
||||
valueDate?: string;
|
||||
description: string;
|
||||
counterparty?: string;
|
||||
amount: number;
|
||||
vorgang?: string;
|
||||
isPending: boolean;
|
||||
notes?: string;
|
||||
rawText?: string;
|
||||
importId?: Id<"imports">;
|
||||
assignedMonth?: string;
|
||||
externalRef?: string;
|
||||
};
|
||||
|
||||
export async function enrichTransactionFields(
|
||||
ctx: MutationCtx,
|
||||
userId: Id<"users">,
|
||||
input: TransactionInput,
|
||||
) {
|
||||
const settings = await getAppSettings(ctx, userId);
|
||||
const salaryShift = settings?.salaryShift ?? {
|
||||
enabled: true,
|
||||
categoryNames: ["Gehalt & Besoldung"],
|
||||
dayThreshold: 25,
|
||||
};
|
||||
const ownNames = settings?.ownNames ?? [];
|
||||
|
||||
let categoryId = input.categoryId;
|
||||
let categoryName = input.categoryName;
|
||||
if (!categoryId && !categoryName && input.rawText) {
|
||||
categoryName = categorize(
|
||||
input.rawText,
|
||||
input.amount,
|
||||
input.vorgang ?? "",
|
||||
ownNames,
|
||||
);
|
||||
}
|
||||
if (!categoryId && categoryName) {
|
||||
categoryId = await resolveCategoryId(ctx, userId, categoryName);
|
||||
}
|
||||
if (categoryId && !categoryName) {
|
||||
const cat = await ctx.db.get("categories", categoryId);
|
||||
categoryName = cat?.name;
|
||||
}
|
||||
|
||||
const { assignedMonth, effectiveMonth } = resolveAssignedAndEffective(
|
||||
input.bookingDate,
|
||||
input.amount,
|
||||
categoryName,
|
||||
salaryShift,
|
||||
input.assignedMonth,
|
||||
);
|
||||
|
||||
const dedupHash = await computeDedupHash({
|
||||
accountId: input.accountId,
|
||||
bookingDate: input.bookingDate,
|
||||
amount: roundEur(input.amount),
|
||||
description: input.description,
|
||||
vorgang: input.vorgang,
|
||||
});
|
||||
|
||||
return {
|
||||
categoryId,
|
||||
assignedMonth,
|
||||
effectiveMonth,
|
||||
dedupHash,
|
||||
amount: roundEur(input.amount),
|
||||
};
|
||||
}
|
||||
|
||||
export async function assertOwned<T extends { userId: Id<"users"> }>(
|
||||
doc: T | null,
|
||||
userId: Id<"users">,
|
||||
label: string,
|
||||
): Promise<T> {
|
||||
if (!doc) throw new Error(`${label} nicht gefunden`);
|
||||
if (doc.userId !== userId) throw new Error("Nicht autorisiert");
|
||||
return doc;
|
||||
}
|
||||
|
||||
export function recomputeEffectiveMonth(
|
||||
bookingDate: string | undefined,
|
||||
assignedMonth: string | undefined,
|
||||
) {
|
||||
return computeEffectiveMonth(bookingDate, assignedMonth);
|
||||
}
|
||||
Reference in New Issue
Block a user