From 72008ac59b2535c45b290918238b196dc0875b10 Mon Sep 17 00:00:00 2001 From: bernd Date: Thu, 13 Aug 2026 12:02:03 +0000 Subject: [PATCH] Auto-create a contract when a room-assigned invitation is accepted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accepting an invitation with a roomId previously only set User.roomId — no Contract row was created. Since every tenant-facing feature (Miet-Ampel, Vertragsdokumente, Kündigungsfristen, ...) is driven by Contract, not User.roomId, the tenant would silently show up nowhere despite "having" a room. Now the accept transaction also creates an active Contract (rent/deposit defaulted from the room's baseRent/ utilityPauschal, same defaults already used by the "Wieder aufnehmen" flow) and marks the room OCCUPIED, unless the room already has an active contract (guards against a stale/duplicate invite race). --- backend/src/routes/invitations.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/backend/src/routes/invitations.ts b/backend/src/routes/invitations.ts index 76aba7c..7fc82a2 100644 --- a/backend/src/routes/invitations.ts +++ b/backend/src/routes/invitations.ts @@ -220,6 +220,34 @@ invitationsRouter.post('/invitations/:token/accept', async (req: Request, res: R where: { id: invitation.id }, data: { status: 'ACCEPTED', acceptedAt: new Date() }, }); + + // War der Einladung ein Zimmer zugeordnet, entsteht daraus jetzt auch ein + // Mietvertrag — sonst taucht der Mieter zwar mit roomId beim User auf, + // aber nirgendwo in Miet-Ampel/Vertragsdokumenten/Kündigungsfristen, da + // die überall an Contract hängen, nicht an User.roomId. Warmmiete/Kaution + // werden aus den bereits am Zimmer hinterlegten Werten übernommen (wie + // beim „Wieder aufnehmen“-Formular) — der Vermieter kann sie danach über + // den Vertragsdokumente-Bereich jederzeit anpassen. + if (invitation.roomId && invitation.role === UserRole.TENANT) { + const room = await tx.room.findUnique({ where: { id: invitation.roomId } }); + const activeContract = room + ? await tx.contract.findFirst({ where: { roomId: room.id, isActive: true } }) + : null; + if (room && !activeContract) { + await tx.contract.create({ + data: { + userId: created.id, + roomId: room.id, + startDate: new Date(), + totalWarmRent: Number(room.baseRent) + Number(room.utilityPauschal), + depositAmount: Number(room.baseRent) * 3, + isActive: true, + }, + }); + await tx.room.update({ where: { id: room.id }, data: { status: 'OCCUPIED' } }); + } + } + return created; });