Files
lmnp-organizer/docs/spec/lmnp-organizer-prd.md
nessar c21e5670a0 Refactor GDPR data retention and archiving strategy; enhance candidate application lifecycle management
- Updated the GDPR retention and purging cycle for candidate applications, clarifying the eligibility for purge and retention rules for supporting documents.
- Revised the archiving process for terminated leases, specifying retention periods for tenant and guarantor documents.
- Introduced a new ADR for sharing a single landlord vault across applications, tenants, and leases, ensuring consistent encryption and access management.
- Enhanced the document generation process to ensure confidential data remains encrypted and only processed in the browser.
- Improved the candidate application access flow, allowing for more flexibility in managing tenant information and supporting documents.
- Updated the QR code styling for public application links to enhance visual branding and usability.
- Added a new specification for simplifying the candidate application process, focusing on clarity and user experience.
2026-09-05 13:34:21 +02:00

26 KiB
Raw Permalink Blame History

Cahier des charges & Spécifications – LMNP Organizer

Problem Statement

Self-managing buy-to-let properties under the French LMNP (Location Meublée Non Professionnelle) tax status involves complex administrative, financial, and tax obligations. Landlords must track leases, generate rent receipts, document property conditions and furniture inventories, monitor mortgage schedules, classify expenses for tax optimization, compute complex component-based depreciation, and carry forward tax deficits. Doing this manually or via standard spreadsheets is highly error-prone, risks tax non-compliance, and fails to optimize tax savings.

Solution

A containerized web application comprising a Java 25 / Spring Boot 4 backend and an Angular 22 frontend that centralizes the management of properties, tenants, leases, and mortgage loans. The application provides guided property inspection checklists with photo sequences, automates expense classification, calculates component-based depreciation schedules, simulates French tax forms (Form 2033 & 2042-C-PRO), and automatically tracks tax loss carry-forwards and deferred depreciations. The system is designed to maintain a low storage footprint through image compression and adheres to GDPR regulations by auto-purging sensitive files.

User Stories

  1. As a Landlord (Bailleur), I want to manage apartments in my portfolio, so that I can centralize their descriptions, locations, and asset values.
  2. As a Landlord (Bailleur), I want to add tenant records with contact information and dossier status, so that I can easily reach them and check their application completeness.
  3. As a Landlord (Bailleur), I want to create a Lease (Bail) linking an apartment to one or more Tenants (Locataires), so that the legal relationship and rental parameters (start date, end date, rent, charges) are documented.
  4. As a Landlord (Bailleur), I want to generate a lease contract document based on a template, so that I can print it or send it by email to the tenants.
  5. As a Landlord (Bailleur), I want to record a Security Deposit (Dépôt de garantie) paid by the tenants at the beginning of the lease, so that I can track the balance to be returned later.
  6. As a Landlord (Bailleur), I want to associate one or more physical-person Lease Guarantees (Garanties du bail) to an explicitly selected Tenant (Locataire) of a Lease (Bail), so that I can contact them in case that tenant defaults.
  7. As a Landlord (Bailleur), I want to generate monthly Rent Receipts (Quittances de loyer) for active leases, so that I can send them as PDFs to tenants who paid their rent.
  8. As a Landlord (Bailleur), I want to record rent payments already received and see how they settle monthly rent dues, so that I can monitor my rental income flow without manually marking dues as paid.
  9. As a Landlord (Bailleur), I want to record and view a Furniture Inventory (Inventaire du mobilier) for each apartment, so that I can meet LMNP legal requirements.
  10. As a Landlord (Bailleur), I want to establish a Property Condition Report (État des lieux) at check-in or check-out, so that I can record the state of each room and furniture item.
  11. As a Landlord (Bailleur), I want to be guided by a Photo Sequence (Parcours de photos) during the property inspection, so that I don't forget to take crucial pictures (kitchen, bathroom, air conditioner) to use as proof.
  12. As a Landlord (Bailleur), I want to compare the check-in and check-out Property Condition Reports, so that the system automatically calculates deductions for damages to subtract from the Security Deposit (Dépôt de garantie).
  13. As a Landlord (Bailleur), I want to record property-related expenses and upload their corresponding invoices as Attachments (Pièces jointes), so that I can keep records of all expenses.
  14. As a Landlord (Bailleur), I want the system to automatically categorize my expenses as either a Deductible Expense (Charge déductible) or a Capitalized Asset (Immobilisation) based on a 500 € HT threshold and category, so that I make fewer tax mistakes.
  15. As a Landlord (Bailleur), I want to create a Mortgage (Prêt immobilier) with its principal, interest rate, duration, and insurance rate, so that the system automatically generates my Mortgage Amortization Schedule (Tableau d'amortissement).
  16. As a Landlord (Bailleur), I want to manually adjust details on my Mortgage Amortization Schedule, so that I can fix minor rounding differences compared to the bank's schedule.
  17. As a Landlord (Bailleur), I want to configure the Component Amortization (Amortissement par composants) of my apartment, including a non-depreciable Land Share (Quote-part de terrain), so that I can establish a compliant LMNP tax depreciation plan.
  18. As a Landlord (Bailleur), I want the application to suggest a default component breakdown grid for my apartment, so that I can set it up in one click without being an accountant.
  19. As a Landlord (Bailleur), I want to see a pre-filled simulation of my LMNP Tax Declaration Pack (Liasse fiscale Form 2033 & 2042-C-PRO) at the end of the Fiscal Year (Exercice fiscal), so that I can easily copy-paste the values to the official tax website.
  20. As a Landlord (Bailleur), I want the system to automatically compute and carry forward my Tax Loss Carryforward (Déficit reportable) and Deferred Amortization (Amortissement différé) to the next Fiscal Year (Exercice fiscal), so that I can optimize my tax savings indefinitely.
  21. As a Landlord (Bailleur), I want to view a central Calendar (Calendrier) aggregating all Events (Événements) like rent collections, tenant arrivals/departures, loan deadlines, and insurance renewals, so that I have a clear schedule.
  22. As a Landlord (Bailleur), I want to add manual custom Events (Événements) like plumber appointments or general assemblies to the Calendar, so that I can manage all tasks in one place.
  23. As a Landlord (Bailleur), I want my uploaded files (PDFs, photos) to be compressed and saved as Attachments (Pièces jointes) in the server file system and referenced in the database, so that the application maintains a low storage footprint.
  24. As a Landlord (Bailleur), I want candidate personal data and supporting files to be encrypted in the candidate browser before upload and purged 90 days after an application is selected, rejected, or withdrawn, or immediately when an open application expires after 90 days without modification, and explicitly retained tenant and guarantor supporting files to be archived for 3 years after the tenant's accounts close in direct management, unless a specific retention need is justified, then purged, so that I limit exposure and comply with GDPR retention requirements.
  25. As a Landlord (Bailleur), I want to log in securely using OIDC (OpenID Connect) configured dynamically, so that I can authenticate using voidAuth in both development and production environments.
  26. As an Administrator, I want an administration panel to manage global parameters, system configurations, and users, so that I can control access to the application.
  27. As a Landlord (Bailleur), I want to view a Dashboard (Tableau de bord) displaying key metrics (revenus, occupancy rate, cash-flow, outstanding unpaid rent) at a glance, so that I can monitor my portfolio's status.
  28. As a Landlord (Bailleur), I want to view interactive charts (monthly cash flow trend and expense breakdown by category), so that I can analyze my financial performance visually.

Implementation Decisions

Candidate Application Access

  • One application belongs to one candidate and one apartment. Several individually selected applications for the same apartment may supply the tenants of one lease without merging their sensitive data. Selection explicitly creates a tenant or associates an existing one, with no automatic matching by name or contact. A new tenant is prefilled with the confirmed first name, last name, e-mail, telephone and postal address already supplied, requiring at least e-mail or telephone; associating an existing tenant preserves their current contacts. The landlord may complete or edit current information. Postal address may remain absent until lease preparation requires a complete contractual profile and freezes it in the contract snapshot. Selection closes the candidate form and private resume link; subsequent exchanges take place directly by e-mail or telephone.
  • An application may contain zero or more proposed individual or organization guarantors together with, or without, proposed Visale information and evidence. This combination does not block submission; if selected, preparing the lease guarantees applies the existing non-blocking legal warning. Proposed guarantees do not create another candidate or an active lease guarantee; their useful facts may prepare the future lease guarantees. Evidence explicitly retained for a continuing rental purpose stays encrypted and follows its own retention rule under #146; the rest follows the application purge.
  • Candidates have no account or OIDC session. Opening a public apartment link first loads the application and privacy notice through a side-effect-free GET; the running client then issues an automatic POST, without another click, to create a new DRAFT application and immediately redirects to its private resume link. Link-preview clients that do not run the application create nothing. Creation exposes no existing application, and server-side rate limiting per public link and source IP returns 429 when exceeded, without CAPTCHA. At most one public link is active per apartment, and reopening applications later creates a new public link.
  • An application is candidate-editable while DRAFT and remains editable during review after it is explicitly SUBMITTED. Submission shares it with the landlord once first name, last name, and at least one contact channel—e-mail or telephone—are present, even when any other expected data or evidence is missing. Completeness is informative and never blocks submission. The candidate explicitly saves each form step, which encrypts and writes its fields atomically; each evidence upload has independent progress. The UI confirms a successful save and warns before leaving with local changes. Every write carries the expected clear technical revision, and a stale browser or device is refused rather than overwriting newer ciphertext. A draft created by opening the public link stays absent from the landlord view until its first saved data. Before submission, the authenticated landlord then sees only that it exists, its creation date, and its last activity, never its identity or contents. Submission exposes the application data, and subsequent saved corrections stay visible to the landlord without returning to DRAFT or resubmitting. A successful candidate save or submission updates activity; opening, reading, downloading, or decrypting never does. The application provides no messaging system. Only a submitted application may be selected or rejected, while either non-terminal state may be withdrawn or expire.
  • The submitted-application worklist uses compact summaries with candidate first and last name, state, submission date, declared professional situation, and declared total monthly resources. After a saved candidate change, the summary displays “Mis à jour le …” with its date. Guarantees and supporting documents are available in details collapsed by default and expandable within the list. It can be sorted locally by date or resources without calculating a score. From this list, the landlord may manually reject an application without opening its supporting files; the application leaves the review queue and offers a generic rejection response through copy or native mailto:, without storing a rejection reason or producing an automatic rejection.
  • Each draft or submitted application has a private bearer resume link containing two independent high-entropy secrets in its URL fragment, which the browser does not send with the initial request: an access capability that the client forwards alone in an authorization header and the server stores only as a hash, and a browser-side encryption secret that never reaches the server. Selection, rejection, withdrawal, or expiration immediately invalidates the server capability and prevents future server access, but cannot erase ciphertext and keys already copied outside the application; the privacy notice states this limitation and no browser DRM is attempted. Neither the server nor the landlord can reconstruct a lost complete link without weakening the encryption boundary; the candidate must create a new application from the public link, and the abandoned draft follows its normal 90-day inactivity expiry.
  • The landlord shares the public link through a QR code, copy action, or a native mailto: template. The application does not send or track e-mail. The QR uses rounded patterns, theme colors and the existing house brand mark through qr-code-styling, replacing uqr. Compact layouts, concise copy and scan verification are specified in the post-#135 polish scope.
  • Public and private application pages expose a full privacy notice, with a concise summary before the first save. It identifies the landlord as controller and states the purposes, data and evidence categories, recipients, retention periods, rights process, private-link secrecy and irrecoverability, and encryption and revocation limits. No mandatory consent checkbox is used because the collection supports pre-contractual steps.
  • Candidate uploads use a national, typed, closed, and versioned catalogue limited to the identity, domicile, professional-activity, and resource evidence allowed by the current legal list. Only PDF, JPEG, and PNG files whose binary signature matches the declared format are accepted; the browser checks before encryption and after decryption, while archives, Office documents, SVG, HTML, and executables are rejected. A source file is limited to 25 MB and one application to 250 MB of stored ciphertext; the client checks the source before encryption, while the server enforces the per-object limit plus versioned cryptographic overhead and the stored total. The server cannot run antivirus inspection over ciphertext. The candidate's declared situation, including explicit absence of activity or resources, determines the applicable expected items and an informative completeness indicator; landlords cannot configure a checklist per apartment, and generic attachments are not accepted.
  • Candidate personal fields and every supporting file are encrypted completely in the candidate browser before upload, including file bytes, names, business document types, and thumbnails. The server's clear application metadata is a closed allowlist: technical application, apartment, and landlord identifiers; lifecycle state, technical revision, and timestamps; purge deadline; encrypted-object count and byte sizes; and cryptographic-format version. It separately stores only a hash of the access capability and encrypted key envelopes. Source IP is transient rate-limit data, not application metadata. The server never receives plaintext candidate identity, contact, professional, resource, document type, filename, thumbnail, or file content. Under #146, selection preserves encryption when retaining personal facts or evidence with tenants and leases. Sensitive routes use Cache-Control: no-store and Referrer-Policy: no-referrer; URL fragments, capabilities, authorization headers, personal data, decrypted content, and sensitive payloads are excluded from logs and telemetry. After OIDC authentication, the landlord separately unlocks and decrypts submitted applications in the browser with private key material that the server cannot recover. Worklist display, completeness, and sorting are therefore client-side.
  • The initial versioned native Web Crypto suite uses AES-256-GCM with a fresh random 96-bit nonce and 128-bit tag for every structured or binary object, RSA-OAEP 3072 with SHA-256 to encrypt each random application key to the landlord public key, and HKDF-SHA-256 over each passkey PRF result followed by AES-256-GCM for the private-key envelope. Authenticated additional data binds the suite version, landlord, application, object, and technical revision. Nonce reuse is forbidden, and no custom cryptography or new cryptographic dependency is introduced.
  • One vault unlock covers applications, tenant personal data and leases and shares the non-exportable Web Crypto key in memory through a per-authenticated-HTTP-session BroadcastChannel with LMNP Organizer tabs of the same browser profile and origin after each tab verifies its OIDC session. Anonymous tabs never join the channel. A new tab reuses an unlocked tab without another passkey prompt; logout or OIDC expiry broadcasts reference clearing and loses the channel token, while closing every tab or restarting the browser requires a new unlock. The application never stores the unlocked key in localStorage, sessionStorage, or IndexedDB.
  • Selection retains application evidence with the tenant only on an explicit landlord choice under #146. Each retained file receives a continuing rental purpose and retention rule through reassignment or re-enveloping without duplicating its ciphertext bytes. Unretained application evidence follows its original purge deadline; #39 coordinates actual deletion across storage layers.
  • The single landlord vault is mandatory before the first public application link or tenant record, including manual entry. Vault passkeys are separate from OIDC authentication. A dialog explains browser-side encryption, passkey-based access and irrecoverability if every passkey is lost; cancellation or failed registration leaves the requested link or tenant record uncreated. ADR-0034 defines the cryptographic access mechanism and ADR-0036 extends its scope. The landlord may then register multiple LMNP Organizer WebAuthn passkeys for the same OIDC identity, including independent credentials on Windows, Android, and macOS. Each supported passkey's local PRF output wraps the same landlord private key in a separate server-stored envelope; neither the PRF output nor plaintext private key reaches the server. Registration must verify PRF support at runtime. When PRF is unavailable, the requested link or tenant record is not created and compatibility guidance is shown; no PIN or server-held-key downgrade is offered, while unrelated features remain usable. The management view shows a landlord-chosen label plus creation and last-use timestamps for each credential. Because synchronization independence cannot be guaranteed, it only recommends another device or manager. One passkey is sufficient. Adding or revoking a passkey requires both OIDC authentication and an unlock by an already registered credential other than the one being revoked; addition unwraps the private-key bytes only long enough to produce the new envelope, then discards them. Losing all registered passkeys makes the shared vault contents irrecoverable. Ordinary revocation requires another registered passkey. An explicit irreversible reset closes application links, revokes the passkeys and makes old protected personal data and documents immediately purge-eligible, while preserving tenant and lease references and financial history. A new vault allows re-entry of current identities and contacts without restoring historical documents or replacing frozen contract data.
  • The shared vault encrypts tenant and guarantor identities, contacts, private information and supporting documents, including personal contents of leases and rental documents. Rents, charges, lease dates, rent dues, payments and technical relationships stay server-readable for operational calculations. With the vault locked, financial views remain usable and protected content displays an explicit locked state; actions needing that content offer local unlock. Confidential PDFs are generated, assembled and previewed in the browser, then encrypted as complete documents before server storage, as decided in ADR-0037. Reference documents without protected personal data may still be generated by the server.
  • This encryption protects already stored content from an attacker with full host control only until the landlord unlocks it through the compromised web client. Such an attacker can alter that client to capture the unlock secret or plaintext and can substitute the public key for future submissions; protection during use would require an independently trusted client or hardware and remains outside this scope.

Technical Stack

  • Backend: Java 25 LTS, Spring Boot 4, Spring Security OAuth2. Exposes REST APIs documented using OpenAPI/Swagger.
  • Frontend: Angular 22, SPA built using modern vanilla CSS stylesheets. Interactive charts implemented via ngx-echarts (optimized custom bundle). Built and packaged using Bun. The user interface must be fully responsive, adopting a fluid mobile-first design suitable for smartphones, tablets, and desktop displays.
  • Database: MongoDB (latest stable version).
  • Authentication: OpenID Connect (OIDC) dynamically configured to plug into voidAuth for both development and production.

Data Model & Multi-Tenancy preparedness (ADR-0001)

  • The database schema binds all core entities (apartments, leases, loans, events) to a BailleurId. Initially, the app is deployed in single-portfolio mode with a single default Bailleur record, but the schema remains ready for future SaaS/multi-tenant partitioning.

Attachment Storage & Optimization (ADR-0003, ADR-0006)

  • Binary files (PDFs, photos) are stored on the server's local file system (mounted via persistent Docker volumes). The MongoDB database holds file metadata (Attachment).
  • Uploaded photos are compressed backend-side: resized to a maximum of 1920x1080 and converted to WebP with 75-80% quality (reducing size to ~250 KB per photo).

GDPR & Archiving Automated Schedules (ADR-0006)

  • Candidate structured personal data and supporting documents are encrypted in the browser before upload and become eligible for permanent purge 90 days after application selection, rejection, or withdrawal. A draft or submitted application expires after 90 days without a successful candidate save or submission and becomes immediately eligible; reads and decryptions never postpone it. Issue #39 performs the coordinated database-and-filesystem deletion.
  • Terminated leases transition to an Archived state. Tenant and guarantor supporting documents explicitly retained for a rental purpose remain in active use during the lease until the tenant's accounts close. In direct management, those still needed are then archived for 3 years before purge, unless a specific retention need is justified. The account-closure date, rather than lease termination alone, starts this default archival period (see ADR-0006). Property condition photos are purged 5 years after lease end. Financial/rent ledgers are archived and kept for 10 years to comply with commercial tax auditing codes.

Tax Calculations & Automation

  • Expense Classification: Expenses under 500 € HT or belonging to operations categories (insurance, tax) are categorized as Deductible Expenses (Charge déductible). Renovation or furniture expenses over 500 € HT are categorized as Capitalized Assets (CapitalizedAsset) and trigger an amortization line.
  • Component Amortization: Pre-filled template configurations for property components (Terrain, Gros œuvre, Toiture, Équipements). Land Share (Quote-part de terrain) is excluded from depreciation.
  • Carry-Forwards: Automatically calculates and rolls over Tax Loss Carryforwards (Déficit reportable) and Deferred Amortizations (Amortissement différé) at fiscal year closure.

Testing Decisions

Code Coverage Rule

  • A minimum code coverage threshold of 80% will be strictly enforced on both the backend (JUnit/JaCoCo) and frontend (Karma/Jest/Istanbul) testing pipelines. Builds with coverage under 80% must fail the GitLab CI test stage.

Testing Seams

  • Backend Seam (API Layer): Integration tests using @SpringBootTest and MockMvc to hit REST endpoints, verify controller behaviors, model validations, and security configurations. A test MongoDB profile (using Testcontainers) will be used. OIDC token validation will be mocked.
  • Frontend Seam (Component/Service Layer): Angular component and service testing via TestBed and HttpTestingController to mock HTTP interactions and verify form inputs, component state updates, and calendar/tax rendering.
  • End-to-End Seam (Playwright): Full browser automated testing covering OIDC login flows, guided Property Condition photo captures, and Tax Declaration Form simulations.

Out of Scope

  • CD (Continuous Deployment) Stage in CI/CD (ADR-0002): The GitLab CI/CD pipeline compiles, tests, builds Docker containers, and pushes them to the GitLab registry. Deploying the new versions onto the production server is done manually (via ssh/pull/compose restart) and is excluded from the pipeline's automated steps.
  • Live Bank Sync/Imports: Users enter bank loans and operational expenses manually or adjust pre-computed mortgage lines. Direct parsing of Excel/CSV statements or API bank integrations is out of scope.
  • Multi-Tenant UI Isolation: The interface will only expose the default active landlord's portfolio. Designing role-based multi-landlord separation in the Angular client is out of scope for Phase 1.

Further Notes

  • Localization: All backend logs, codes, fields, and internal database keys use English (Lease, SecurityDeposit, Landlord). The UI labels, PDFs, and categories shown to the landlord are purely in French.
  • Documentation: Coding agents should reference the project glossary in CONTEXT.md and respect active architectural decisions in docs/adr/.