19 KiB
Validation Parity Checklist
This document accounts for the application's user-editable Angular forms and mutable rental HTTP commands for Epic #42 and Issue #59. It records the implemented contracts; it is not a claim that unrecorded CI or local test gates passed.
The backend remains authoritative. Angular mirrors only important save-time constraints that can
give immediate, actionable guidance. The implementation uses the existing Jakarta validation,
domain rules, Angular forms, and SaveFailure contract; it does not add a generated schema, shared
validation framework, state-management mechanism, or dependency.
Shared failure and interaction contract
- Request validation failures use HTTP 400 and
{ "code": "validation_failed", "errors": [{ "field": "...", "code": "..." }] }. - Other safely rejected requests use the stable HTTP 400 code
invalid_request. - Conflicts use HTTP 409. Controller-level file limits use structured HTTP 400
too_largefield errors; multipart requests rejected before controller validation use coded HTTP 413attachment_too_large. Unavailable IRL data uses HTTP 503 and unexpected failures use HTTP 500. HTTP 422 is not part of this contract. - Angular maps known field/code pairs to French inline guidance. Unknown fields/codes, network failures, conflicts without recognized guidance, and unexpected failures retain a safe visible operation-level fallback.
- Save forms retain entered values after failure, prevent duplicate submission while their own operation is pending, and clear a server field error when that field is edited.
- Required markers, touched-or-submit timing, and first-invalid focus are implemented by each form because the controls and nested paths differ; there is no generated or cross-stack validation layer.
1. Landlord profile and settings
- Angular:
LandlordProfileForm,LandlordProfileCompletionDialog,SettingsPage - Mutable command:
PUT /api/landlord-profile - Backend:
LandlordProfileController,LandlordProfileApi.Request - Authoritative constraints:
firstName,lastName,email,phone, andaddressare@NotBlank.emailis@Email.phoneValidis an@AssertTruevalidation of the sharedPhoneNumberdomain type.
- Angular mirror: the five required controls, email syntax, and phone syntax are checked before save; known server errors are shown inline and focused. Settings and the completion dialog keep operation-local loading/saving state.
There is no /api/landlord/profile route and no 100-character DTO limit on the name fields.
2. Apartments, legal profile, and private areas
- Angular:
ApartmentForm,ApartmentLegalProfileForm,PrivateAreaEditor,ApartmentsPage - Mutable commands:
POST /api/apartmentsPUT /api/apartments/{apartmentId}DELETE /api/apartments/{apartmentId}
- Backend:
ApartmentController,ApartmentApi.Request - Authoritative constraints:
nameandaddressare@NotBlank.purchasePriceis@NotNull @Positive.privateAreasis non-null; every item has a non-blankidandlabel, non-nullfamilyandsubtype, and a family/subtype compatibility rule.- Legal-profile surface and annual energy cost are positive or zero when present. Providing an energy cost requires a reference year between 1900 and 2100. Construction period, energy rating, and French department code use explicit validity rules.
- Angular mirror: required core fields, strictly positive purchase price, legal-profile numeric
bounds and department code, and private-area indexed constraints are checked before save.
Structured paths such as
privateAreas[0].labelfocus and clear the matching row.
Legal profile and private areas are part of the apartment request. There are no separate
/legal-profile or /private-areas write endpoints, and the contract has no buildingYear,
coproprietyLots, or private-area surfaceArea field.
3. Apartment documents
- Angular:
ApartmentDocumentManager - Mutable commands:
POST /api/apartments/{apartmentId}/documents(multipart)DELETE /api/apartments/{apartmentId}/documents/{documentId}
- Backend:
ApartmentDocumentController,ApartmentDocumentApi.UploadRequest - Multipart contract:
file: required, non-empty PDF whose content is recognized as PDF.type: required validApartmentDocumentTypecode.establishedOn: required ISO local date.expiresOn: optional ISO local date, not beforeestablishedOn.- The attachment limit is configurable through
lmnp.attachments.max-sizeand defaults to 250 MB; Spring's multipart request limit defaults to 251 MB.
- Angular mirror: PDF content, required type/file/establishment date, and date ordering are checked before upload. Upload and delete expose operation-local pending and error states.
Apartment documents are PDF-only; image uploads belong to condition reports.
4. Tenants and attachments
- Angular:
TenantForm,TenantsPage,TenantAttachmentTransferService - Mutable commands:
POST /api/tenantsPUT /api/tenants/{tenantId}DELETE /api/tenants/{tenantId}POST /api/tenants/{tenantId}/attachments(multipart)DELETE /api/tenants/{tenantId}/attachments/{attachmentId}
- Backend:
TenantController,TenantApi.Request - Authoritative constraints:
firstName,lastName,email, andaddressare@NotBlank.emailis@Email.phoneis optional; when present it must satisfy the sharedPhoneNumberrule.- Attachment
fileis required and non-empty. Its content type is not restricted to PDF, and its size is bounded bylmnp.attachments.max-size(250 MB by default).
- Angular mirror: required identity/address fields, email, and optional-phone syntax are checked before save. Tenant values survive a failed request. Attachment upload reports per-tenant progress; upload, download, and delete use action-local pending/error state.
TenantAttachmentTransferService coordinates upload, download, and delete state; there is no
attachment-transfer endpoint or targetTenantId request.
5. Lease drafts, amendments, clauses, and document revisions
- Angular:
LeaseForm,LeaseGuaranteeForm,LeaseClauseEditor,LeaseLegalTerms,LeaseAmendmentForm,LeasesPage - Mutable commands:
POST /api/leasesPUT /api/leases/{leaseId}DELETE /api/leases/{leaseId}POST /api/leases/{leaseId}/amendmentsPOST /api/leases/{leaseId}/revisions
- Backend:
LeaseController,LeaseApi.DraftRequest,LeaseApi.AmendmentRequest,LeaseNestedRequestValidator - Authoritative draft constraints:
apartmentIdis non-blank andtenantIdsis a non-empty list of distinct, non-blank IDs.startDate,monthlyRent,monthlyCharges, andsecurityDepositare non-null.- Rent is strictly positive; charges and deposit are positive or zero;
dueDayis 1 through 28. - Each
guarantees[]entry names onetenantIdalready present intenantIds; first name, last name, and email are required, and email syntax is validated. - At most 20 special clauses and 20 parameters per clause are accepted. Parameter values are at most 500 characters, custom clause text 4,000, supplement justification 2,000, IRL series ID 100, recent-work description 4,000, and other terms 8,000.
- Domain services verify referenced resources, lease-type/legal-term coherence, special-clause catalog rules, and occupancy conflicts.
- Authoritative amendment constraints:
effectiveDate, IANAtimeZone, distinct non-emptytenantIds,endDate,monthlyRent, andmonthlyChargesare required.effectiveDatecannot be in the past;endDatecannot precede it.- Amount and due-day constraints match the draft request.
- Angular mirror: important required fields, amount/day bounds, dates, the selected tenant and guarantee contact data, clause inputs, and conditional legal terms are checked before submit. Known nested server paths are routed to the core form, guarantee, legal-terms, or clause editor without clearing the draft.
- Document revision command: generating a revision has no editable request payload; its operation-local pending/error state is separate from lease editing and downloads.
6. Furniture inventory
- Angular:
InventoryPage,InventoryLineCard,inventoryLineErrors - Mutable command:
PUT /api/leases/{leaseId}/inventory - Backend:
FurnitureInventoryController,FurnitureInventoryRequestValidator - Authoritative constraints:
versionandlinesare required; version is non-negative.- Every line requires a unique non-blank ID, valid apartment
privateAreaId, non-blank designation, and non-negative integer quantity. - A regulatory category is unique; present, absent, and undeclared quantities must agree with presence. Its inventory line carries the only furniture observation in the condition report, without a derived equipment zone.
- Angular mirror: the same editable line constraints produce indexed errors such as
lines[0].quantity; the first invalid control is focused and editing clears that line's server error. Save is disabled only for the active inventory request.
7. Lease finalization and guarantee deeds
- Angular:
LeaseFinalizationPage,LeaseFinalizationForm,GuaranteeDeedList - Mutable commands:
POST /api/leases/{leaseId}/guarantee-deeds(multipart)POST /api/leases/{leaseId}/finalization(multipart)
- Backend:
LeaseFinalizationController,LeaseFinalizationApi - Guarantee-deed multipart contract: optional request parameters are validated together as
requirementIndex,guaranteeId,tenantId, and trueattestation;fileis a required, valid, non-empty PDF. - Finalization multipart contract:
signedLeaseis a required valid PDF.- JSON part
metadatacontains positiverevisionNumber,lastSignatureDate,allPartiesSigned,revisionMatches,confirmedWarningCodes,entryInventoryDiscrepancies,inventoryCorrections, andallInventoryCorrectionsInitialed. - Boolean attestations and inventory corrections must be internally consistent; the service checks signature date, required deeds, warnings, and revision identity.
- PDF size uses
lmnp.attachments.max-size(250 MB by default).
- Angular mirror: file validity, attestations, missing deeds, warning confirmations, and conditional inventory corrections are checked before the confirmation dialog. Upload and final submission use separate pending/error state and preserve correction input after failure.
8. Lease ending, evidence, and resolution
- Angular:
LeaseEndPage,LeaseEndNoticeForm,LeaseEndNoticeCard,LeaseEndResolutionForms - Mutable commands:
POST /api/leases/{leaseId}/end/notices(multipart)POST /api/leases/{leaseId}/end/notices/{noticeId}/evidence(multipart)POST /api/leases/{leaseId}/end/notices/{noticeId}/withdrawalsPOST /api/leases/{leaseId}/end/notices/{noticeId}/invalidationsPOST /api/leases/{leaseId}/end/agreed-early-endPOST /api/leases/{leaseId}/end/closure
- Authoritative constraints:
- A notice supplies IANA
timeZone,receivedOn,requestedDepartureOn, delivery mode, and the applicable confirmations. Receipt cannot be in the future and departure cannot precede it. - Evidence is optional only with explicit missing-evidence confirmation; uploaded evidence is non-empty and bounded by the configured attachment limit.
- Withdrawal, invalidation, and agreed-end reasons are non-blank and at most 500 characters.
- Closure requires a valid time zone and true
confirmed. - Domain rules enforce the lease phase, notice history, eligible dates, and exit-report prerequisites.
- A notice supplies IANA
- Angular mirror: conditional delivery/evidence fields, dates, reasons, and closure confirmation are validated where their controls live. Each notice/action key has independent confirmation, pending, structured field-error, and operation-error state.
9. State of the premises
- Angular:
ConditionReportPage,ConditionReportEditor,ConditionReportDetailsSection,ConditionReportInventorySection,ConditionReportEvidenceSection - Mutable commands:
PUT /api/leases/{leaseId}/condition-reports/{type}POST /api/leases/{leaseId}/condition-reports/{type}/photos(multipart)DELETE /api/leases/{leaseId}/condition-reports/{type}/photos/{reference}?version=...POST /api/leases/{leaseId}/condition-reports/{type}/revisions?version=...POST /api/leases/{leaseId}/condition-reports/{type}/finalization(multipart)POST /api/leases/{leaseId}/condition-reports/{type}/delivery-attestationsPOST /api/leases/{leaseId}/condition-reports/ENTRY/supplements(multipart)
- Draft authority:
- Non-negative
version,occurredOn, IANAtimeZone, and every nested list are required. - Participants, zones, observations, meters, access means, inventory splits, photo absences, and entry-photo dispositions have explicit required/type/non-negative constraints.
- The draft validator enforces conditional observations, represented-party details, forwarding addresses on exit, and exact inventory split totals. Finalization readiness separately requires one photo or audited absence per zone and, for exit reports, a disposition for every entry photo.
- Non-negative
- Photo multipart contract:
- Required non-blank
zoneIdparameter and requiredfilepart. - Supported content: JPEG, PNG, WebP, HEIC, or HEIF, identified from its content signature rather than filename extension or client-supplied MIME type.
- The configurable photo limit defaults to 40 MB and is returned by the report response for frontend pre-checks.
- Required non-blank
- Finalization multipart contract: required valid PDF
fileplus JSONmetadatacontaining a non-negativeversionand trueattestationandallPartiesSigned; the PDF limit defaults to 250 MB and is also returned by the report response. - Supplement multipart contract: entry reports only; required valid PDF
signedPdf, optional photo parts, and JSONmetadatacontaining matchingphotoZoneIds,requestedOn, non-blankacceptedAdditions,supplementType,lateWarningConfirmed, optionalheatingPeriodStartedOn, and optionalreason. The request date cannot precede the report or be in the future; a heating-period start cannot follow the request. Photo/PDF limits match the upload types above. - Angular mirror: the editor checks the important nested draft rules before save; evidence controls pre-check types and response-provided limits. Structured server paths are mapped to the owning section, preserve the draft, focus the first correction, and clear independently. Saving, photo work, revision generation, finalization, delivery attestation, supplement upload, comparison loading, and downloads use separate busy states.
This is the Issue #40 overlap: the existing decomposed condition-report editor and section components own the UI; Issue #59 adds no duplicate state-of-the-premises implementation.
10. Rent payments, allocations, reversal, and refunds
- Angular:
RentPaymentValidationDialog,RentPaymentAllocationDialog,RentPaymentReversalDialog,RentPaymentRefundDialog,RentDueCorrection - Mutable commands:
POST /api/rent-dues/refreshPOST /api/rent-paymentsPOST /api/rent-payments/{paymentId}/allocationsPOST /api/rent-payments/{paymentId}/reversalPOST /api/rent-payments/{paymentId}/refunds
- Backend:
RentDueController,RentPaymentController,RentPaymentApi - Authoritative constraints:
- Record: non-blank operation/due IDs and time zone, non-negative due version, required
receivedAt, and strictly positive amount. - Allocation: non-blank operation/due IDs, non-negative payment/due versions, and strictly positive amount.
- Reversal: non-negative payment version, non-blank reason of at most 500 characters, and non-blank time zone.
- Refund: non-blank operation ID/time zone, non-negative payment version, required
refundedAt, and strictly positive amount. - Application rules reject future receipt/refund dates, cross-lease or duplicate allocations, and amounts exceeding payment, due, or unallocated balances.
- Record: non-blank operation/due IDs and time zone, non-negative due version, required
- Angular mirror: dialogs validate the editable date, amount, due, and reason fields, display known server guidance inline, focus the first invalid control, and disable only their active submit. Stale/concurrent responses prompt data reconciliation without silently accepting a payment.
The reversal route is singular. Amount DTOs use @Positive, not @DecimalMin("0.01").
11. Rent revisions
- Angular:
RentRevisionPreviewComponent,RentRevisionProposalsComponent,RentRevisionApplicationComponent - Mutable commands:
POST /api/leases/{leaseId}/rent-revisions/previewPOST /api/leases/{leaseId}/rent-revisionsPOST /api/leases/{leaseId}/rent-revisions/{revisionId}/apply(multipart)
- Backend:
RentRevisionController,RentRevisionApi - Authoritative constraints:
- Preview requires IANA
timeZone; optional series override is at most 9 characters and manual indices are strictly positive. - Proposal requires time zone,
requestedOn, strictly positive chosen rent, non-negative chosen charges when present, and non-blank warning codes. - Application multipart parameters are
timeZone,sentOn,deliveryMethod, anddeliveryConfirmed; optionalproofmust be a valid PDF within the attachment limit. - Policy/domain rules enforce request windows, supported IRL series, confirmation of overrides, ceilings, delivery date, and idempotent application.
- Preview requires IANA
- Angular mirror: manual-index confirmation and positivity, proposal values/warnings, delivery mode/confirmation, and PDF proof are validated locally. Preview, proposal, application, and downloads each expose their own pending/error state.
- Unavailable index contract:
IRL_INDEX_UNAVAILABLEis HTTP 503 Service Unavailable and switches the preview to the guided manual-index path.
12. Session command
- Angular:
SessionService, settings logout action - Mutable command:
POST /logout - Payload validation: none.
- Interaction: duplicate logout requests are prevented while pending; failure restores the available action with a safe visible error.
Evidence and verification gates
Source-level evidence is kept beside behavior in focused frontend and backend tests. The critical
browser journey in frontend/e2e/app.spec.ts verifies private-area client blocking, structured
indexed server guidance, first-invalid focus, independent clearing, preserved values, and a
successful retry. The same suite exercises profile completion, tenant/attachment creation, lease
creation, rent-payment recording, and the native condition-photo control.
Local verification recorded on 2026-07-26:
- Prettier passed for every frontend/documentation file changed since the epic baseline.
- Frontend lint, application/e2e type checking, production build, and all 333 unit tests passed.
- All 36 Playwright journeys passed across mobile, tablet, and desktop viewports.
./mvnw spotless:check verifypassed: 417 unit/web tests, 16 Docker-backed integration tests, packaging, and coverage checks.