diff --git a/apps/api/src/lib/entitlements.ts b/apps/api/src/lib/entitlements.ts index 50731b7..d340ff4 100644 --- a/apps/api/src/lib/entitlements.ts +++ b/apps/api/src/lib/entitlements.ts @@ -8,22 +8,41 @@ import { type SubscriptionSnapshot, type TrialSnapshot, } from "@app/subscriptions"; -import { currentMonth } from "./helpers.js"; +import { currentMonth, getActiveHouseholdId } from "./helpers.js"; import { errors } from "./errors.js"; +/** Vem "äger" kvoten? Hela hushållet delar ägarens plan + pott. + * Ensam användare = sig själv (äger sitt eget hushåll). */ +async function billingUserFor(db: Database, userId: string): Promise { + const householdId = await getActiveHouseholdId(db, userId); + if (!householdId) return userId; + const [owner] = await db + .select({ userId: schema.householdMembers.userId }) + .from(schema.householdMembers) + .where( + and( + eq(schema.householdMembers.householdId, householdId), + eq(schema.householdMembers.role, "owner"), + ), + ) + .limit(1); + return owner?.userId ?? userId; +} + /** Ladda och beräkna entitlements för en användare. Backend = source of truth (spec §61.14). */ export async function loadEntitlements(db: Database, userId: string): Promise { + const billingUserId = await billingUserFor(db, userId); const [subRow] = await db .select() .from(schema.subscriptions) - .where(eq(schema.subscriptions.userId, userId)) + .where(eq(schema.subscriptions.userId, billingUserId)) .orderBy(desc(schema.subscriptions.updatedAt)) .limit(1); const [trialRow] = await db .select() .from(schema.trials) - .where(eq(schema.trials.userId, userId)) + .where(eq(schema.trials.userId, billingUserId)) .limit(1); const [usageRow] = await db @@ -31,7 +50,7 @@ export async function loadEntitlements(db: Database, userId: string): Promise { - const ent = await loadEntitlements(db, userId); + const billingUserId = await billingUserFor(db, userId); + const ent = await loadEntitlements(db, billingUserId); if (ent.aiScansUsedThisMonth >= ent.aiScansPerMonth) { throw errors.quotaExceeded( `Månadens AI-skanningar är slut (${ent.aiScansPerMonth} st). Uppgradera för fler.`, @@ -78,7 +98,7 @@ export async function consumeAiScan(db: Database, userId: string): Promise const month = currentMonth(); await db .insert(schema.aiUsageCounters) - .values({ userId, month, aiScans: 1 }) + .values({ userId: billingUserId, month, aiScans: 1 }) .onConflictDoUpdate({ target: [schema.aiUsageCounters.userId, schema.aiUsageCounters.month], set: { aiScans: (await import("drizzle-orm")).sql`${schema.aiUsageCounters.aiScans} + 1` },