Commit Graph

23 Commits

Author SHA1 Message Date
a6fb15cd41 Add tenant move-out with permanent archive instead of deletion
Landlords can now remove a tenant from their room via a "Mieter
ausziehen lassen" button on the contract card. This never deletes
anything: POST /contracts/:id/move-out sets Contract.isActive=false
(with an endDate), frees the room (User.roomId=null,
Room.status=VACANT) so it can be re-assigned via the existing invite
flow, and leaves the contract row — along with every linked payment,
ticket, rating, handover protocol, and contract document — untouched
and permanently queryable.

GET /contracts/documents now only lists active contracts; the new
GET /contracts/archive (landlord/admin only) lists deactivated ones,
rendered in a new "Archiv — ausgezogene Mieter" section on the
Verträge-page with their historical documents still downloadable.
2026-08-13 10:59:16 +00:00
410db93c30 Restructure generated contract as a full-format rental agreement
Rewrites contractDocumentGenerator.ts from a short summary into a
proper Wohnraummietvertrag layout: numbered §§ for Vertragsparteien,
Mieträume, Mietzeit, Miete/Nebenkosten, Kaution, Instandhaltung/
Schönheitsreparaturen, Hausordnung, Tierhaltung/Untervermietung,
Mietanpassung, sonstige Vereinbarungen, salvatorische Klausel, plus
signature lines for both landlord and tenant (with the drawn signature
embedded once signed) and a page-footer disclaimer instead of a
top-of-document warning box.

Adds Contract.landlordName/landlordAddress/propertyAddress so the
document can show a real letterhead — addresses default to a
"please fill in" placeholder rather than a fabricated address, since
this is a real WG's data. Landlord fills them once via new fields in
the contract generator form; persisted per contract like the other
template options.
2026-08-13 10:37:57 +00:00
bba4fc318b Add click-based contract generator + digital tenant signature
Landlords configure a contract via checkboxes/selects (WLAN included,
furnished, utility billing model, notice period, deposit, rent
adjustment clause) and generate a PDF (pdfkit) that's automatically
attached to the contract's document list. Tenants sign their own
contract in-app by drawing a signature on a canvas; signing embeds the
signature image into a freshly generated final PDF and marks the
contract as signed with a timestamp.

New: Contract.wifiIncluded/furnished/utilityBillingModel (+ new
UtilityBillingModel enum), Contract.tenantSignatureUrl/signedAt.
New endpoints: POST /contracts/:id/generate-document (landlord/admin),
POST /contracts/:id/sign (tenant on own contract, or landlord/admin).

Explicitly a documentation template generated from app data, not a
legally reviewed contract — noted in the PDF itself.
2026-08-13 10:23:27 +00:00
0179517979 Scope inventory list to own room + shared items for tenants
Tenants could previously see every other tenant's private room
inventory including purchase prices. Now matches the app's standard
visibility rule: own room + roomId=NULL (shared) items only; landlord/
admin unchanged (see everything).
2026-08-13 10:08:17 +00:00
a8dc999180 Add multi-file contract document uploads (Verträge & Abrechnung)
Replaces the unused, never-wired-up single contractPdfUrl field with
contractDocumentUrls (String[]) so landlords can attach several files
per contract (signed lease, addenda, ...) instead of just one. New
endpoints: GET /contracts/documents (tenant sees own contract only,
landlord sees all — same visibility rule as the rest of the app),
POST/DELETE /contracts/:id/documents (landlord/admin only). Frontend
reuses the existing FileReader-to-data-URL upload pattern already used
for inventory photos, so files are stored inline like everywhere else
in this app rather than introducing a new storage mechanism.
2026-08-13 09:52:56 +00:00
aa59ab452a Replace flat trash list with a real month-calendar view (FullCalendar)
Rebuilds the "Müll-Kalender" as a proper color-coded FullCalendar (v6,
MIT-licensed, CDN, no build step) month grid instead of a bare list of
type+date rows. Adds a manual "Kalender aktualisieren" action for
landlords (POST /v1/trash-schedule/sync) that re-runs the same KAW
sync job as the daily cron, instead of letting them delete individual
calendar entries by clicking — accidental single-click deletion of
official data was too easy and had no real justification, since a
missing date just gets re-added by the next sync anyway.
2026-08-13 09:20:54 +00:00
d69e07f00d Add Homematic IP smart lock integration (Access Point + App setup)
The lock in this WG is a Homematic IP door lock (keypad + alarm), not
Nuki/Tuya as originally specced — confirmed with the user. Homematic IP
has no official self-service partner API for Access-Point-only setups
(no local CCU), so this uses the actively-maintained open-source
`homematicip` Python library (implements the same reverse-engineered
cloud protocol used by the official app) via two scripts rather than
reimplementing the HTTP/HMAC handshake from scratch — lower risk of
subtly wrong protocol details.

- scripts/hmip_register.py: non-interactive pairing (connection
  request -> wait for the physical blue-button press on the Access
  Point -> auth token). Writes progress to a status file so the API
  can poll it instead of blocking a request for up to ~6 minutes.
- scripts/hmip_control.py: list-devices / lock / unlock via
  HMIP_ACCESS_POINT + HMIP_AUTH_TOKEN env vars (not CLI args, so they
  don't leak into process listings).
- routes/smartLock.ts: POST /smartlock/pairing/start + GET .../status
  (LANDLORD/ADMIN, one-time setup), GET /smartlock/devices
  (LANDLORD/ADMIN), POST /smartlock/devices/:id/:lock|unlock (any
  authenticated user — this is the tenant-facing "keyless door" use
  case from the spec). All lock/unlock routes 409 until pairing has
  produced HMIP_ACCESS_POINT/HMIP_AUTH_TOKEN.

Does NOT cover temporary guest PIN codes on the keypad itself — that's
handled through Homematic IP's own "eSchlüssel" app feature, which
isn't exposed by this API; guest codes remain DB-only as before.
2026-08-13 08:21:19 +00:00
762e17a864 Wire up OneSignal push delivery: register endpoint + reminder jobs
sendPushNotification() (services/notificationService.ts) already called
the real OneSignal API correctly, but two things made it unreachable:
no endpoint ever set User.pushToken, and no job existed for the two
push-based requirements beyond rent dunning — cleaning check-in
reminders and the trash-calendar evening push. Both CleaningTask and
TrashSchedule already had an unused `reminderSentAt` column, so the
schema anticipated this and was just never wired up.

- PUT /v1/auth/push-token: registers the current device's OneSignal
  Player ID.
- jobs/cleaningReminderJob.ts, jobs/trashReminderJob.ts: same
  find-due/send-once/mark-reminderSentAt pattern as the existing
  dailyDunningJob.ts. Safe to run without OneSignal credentials
  configured — sendPushNotification degrades to an in-app Notification
  log when ONESIGNAL_APP_ID/API_KEY or a user's pushToken are absent.

Not done here (needs real third-party setup from the user, same as the
Cloudflare tunnel token pattern): ONESIGNAL_APP_ID/API_KEY in .env, and
a frontend OneSignal Web SDK integration (service worker, VAPID keys)
to actually populate pushToken from a real browser.
2026-08-13 07:47:42 +00:00
791b9bb01e Add peer rating system for tenants
Implements the previously-deferred "Bewertungssystem für Zwischenmieter"
requirement, which existed only as a vague idea with no model, route, or
UI. Design (confirmed with user): any authenticated user can rate any
tenant (1-5 + optional comment), freestanding (not tied to a contract,
creatable any time), visible only to LANDLORD/ADMIN — tenants can submit
ratings but not view them, to keep WG-internal friction out of the open.

- prisma: TenantRating model + User relations. Also pins the Prisma
  Client `output` path explicitly: since schema.prisma lives at the repo
  root (no package.json there) while node_modules only exists under
  backend/, `prisma generate`'s root-inference walked up past the repo
  and wrote into an unrelated ancestor directory when invoked from a
  fresh checkout. The explicit relative output keeps repo-root schema +
  backend-only deps working the same locally and in Docker.
- backend: GET/POST /v1/ratings (role-gated read), GET /v1/ratings/tenants
  (name+room only, any authenticated user, for the picker).
- dashboard: rating form for everyone, landlord-only ratings/summary view.
2026-08-13 07:39:32 +00:00
4ba0100d31 Add 1-click PDF export for tickets (craftsman handoff)
Implements the "1-Klick-PDF-Export für Handwerker" requirement from
the ticketing module spec, which existed only as a schema stub
(HandoverProtocol.pdfUrl) with no actual generator anywhere in the
codebase. Adds a server-side PDF endpoint (pdfkit, no headless
browser needed) summarizing a ticket's category, priority, status,
room, reporter and description, plus a "PDF" button in the landlord
cockpit that downloads it via an authenticated blob fetch (the API
uses a Bearer token, not cookies, so a plain <a href> wouldn't carry
auth).
2026-08-13 07:27:01 +00:00
ca6d851a70 Dateien nach "backend/src/routes" hochladen 2026-08-12 21:55:35 +00:00
1dc1eade8f Dateien nach "backend/src/routes" hochladen 2026-08-12 21:38:33 +00:00
e33aadaa90 Dateien nach "backend/src/routes" hochladen 2026-08-12 21:38:03 +00:00
9c99d3b631 Add cleaning-calendar and cleaning-absences endpoints 2026-08-12 19:37:04 +00:00
669aed188c Add tenants list endpoint for cleaning-task assignment 2026-08-12 18:56:47 +00:00
57ee85292c Add Putzplan cleaning-tasks route 2026-08-12 18:50:23 +00:00
efcbcab0d8 Restrict cockpit summary to own data + shared areas for tenants 2026-08-12 18:24:28 +00:00
ffea782e96 Add tickets API with room-scoped access control for tenants 2026-08-12 18:22:42 +00:00
bd96bb2db1 add backend/src/routes/invitations.ts 2026-08-12 17:48:25 +00:00
b20eec366f update backend/src/routes/auth.ts 2026-08-12 17:45:55 +00:00
b7df0455a5 update backend/src/routes/cockpit.ts 2026-08-12 17:45:53 +00:00
b0989f31f2 add backend/src/routes/cockpit.ts 2026-08-12 16:28:51 +00:00
17abbf2a33 add backend/src/routes/bankingWebhook.ts 2026-08-12 16:28:50 +00:00