import { HttpClient, HttpParams } from '@angular/common/http'; import { Injectable, inject } from '@angular/core'; import { Observable } from 'rxjs'; import { FileDownloadService } from './file-download.service'; export type LeaseEndPhase = 'ACTIVE' | 'NOTICE_PERIOD' | 'AWAITING_CLOSURE' | 'UNAUTHORIZED_OCCUPANCY' | 'ARCHIVED'; export type NoticeDeliveryMode = 'REGISTERED_LETTER' | 'BAILIFF' | 'HAND_DELIVERY' | 'OTHER'; export interface LeaseEndEvidence { version: number; fileName: string; contentType: string; size: number; sha256: string; reason: string | null; authorId: string; recordedAt: string; } export interface LeaseEndAudit { authorId: string; recordedAt: string; } export interface LeaseEndNotice { id: string; status: 'ACTIVE' | 'WITHDRAWN' | 'INVALIDATED'; receivedOn: string; requestedDepartureOn: string; deliveryMode: NoticeDeliveryMode; otherDeliveryMode: string | null; noticeMonths: number; minimumEndOn: string; ruleVersion: string; recordedWithoutEvidenceConfirmed: boolean; authorId: string; recordedAt: string; evidenceVersions: LeaseEndEvidence[]; withdrawal: (LeaseEndAudit & { acceptedOn: string; reason: string }) | null; invalidation: (LeaseEndAudit & { invalidatedOn: string; reason: string }) | null; } export interface LeaseEndState { leaseId: string; supported: boolean; unsupportedReason: string | null; phase: LeaseEndPhase; leaseType: 'STANDARD' | 'STUDENT'; fixedEndOn: string | null; effectiveEndOn: string | null; correctionRequired: boolean; exitConditionReport: { occurredOn: string; finalized: boolean; } | null; notices: LeaseEndNotice[]; agreedEarlyEnds: LeaseEndAgreement[]; agreedEarlyEnd: LeaseEndAgreement | null; closure: { keysReturnedOn: string; authorId: string; recordedAt: string; } | null; } export interface LeaseEndAgreement { endOn: string; reason: string; authorId: string; recordedAt: string; } export interface RecordNoticeRequest { timeZone: string; receivedOn: string; requestedDepartureOn: string; deliveryMode: NoticeDeliveryMode; otherDeliveryMode?: string; lateRegistrationConfirmed: boolean; nonStandardDeliveryConfirmed: boolean; missingEvidenceConfirmed: boolean; evidence?: File; } export interface AcceptWithdrawalRequest { timeZone: string; acceptedOn: string; reason: string; } export interface InvalidateNoticeRequest { timeZone: string; invalidatedOn: string; reason: string; } export interface AppendEvidenceRequest { timeZone: string; evidence: File; reason?: string; } export interface AgreedEarlyEndRequest { timeZone: string; endOn: string; reason: string; } export interface CloseLeaseRequest { timeZone: string; confirmed: true; } @Injectable({ providedIn: 'root' }) export class LeaseEndService { private readonly http = inject(HttpClient); private readonly downloads = inject(FileDownloadService); getState(leaseId: string, timeZone: string): Observable { const params = new HttpParams().set('timeZone', timeZone); return this.http.get(`/api/leases/${leaseId}/end`, { params }); } recordNotice(leaseId: string, request: RecordNoticeRequest): Observable { return this.http.post( `/api/leases/${leaseId}/end/notices`, noticeFormData(request), ); } acceptWithdrawal( leaseId: string, noticeId: string, request: AcceptWithdrawalRequest, ): Observable { return this.http.post( `/api/leases/${leaseId}/end/notices/${noticeId}/withdrawals`, request, ); } invalidateNotice( leaseId: string, noticeId: string, request: InvalidateNoticeRequest, ): Observable { return this.http.post( `/api/leases/${leaseId}/end/notices/${noticeId}/invalidations`, request, ); } appendEvidence( leaseId: string, noticeId: string, request: AppendEvidenceRequest, ): Observable { const formData = new FormData(); formData.append('timeZone', request.timeZone); formData.append('evidence', request.evidence); if (request.reason) formData.append('reason', request.reason); return this.http.post( `/api/leases/${leaseId}/end/notices/${noticeId}/evidence`, formData, ); } downloadEvidence(leaseId: string, noticeId: string, version: number, fileName: string): void { const key = this.evidenceDownloadKey(leaseId, noticeId, version); this.downloads.download( key, this.http.get(`/api/leases/${leaseId}/end/notices/${noticeId}/evidence/${version}`, { responseType: 'blob', }), fileName, 'Impossible de télécharger cette preuve.', ); } evidenceDownloading(leaseId: string, noticeId: string, version: number): boolean { return this.downloads.isPending(this.evidenceDownloadKey(leaseId, noticeId, version)); } evidenceDownloadError(leaseId: string, noticeId: string, version: number): string { return this.downloads.error(this.evidenceDownloadKey(leaseId, noticeId, version)); } recordAgreedEarlyEnd(leaseId: string, request: AgreedEarlyEndRequest): Observable { return this.http.post(`/api/leases/${leaseId}/end/agreed-early-end`, request); } close(leaseId: string, request: CloseLeaseRequest): Observable { return this.http.post(`/api/leases/${leaseId}/end/closure`, request); } private evidenceDownloadKey(leaseId: string, noticeId: string, version: number): string { return `lease-end-evidence:${leaseId}:${noticeId}:${version}`; } } function noticeFormData(request: RecordNoticeRequest): FormData { const formData = new FormData(); formData.append('timeZone', request.timeZone); formData.append('receivedOn', request.receivedOn); formData.append('requestedDepartureOn', request.requestedDepartureOn); formData.append('deliveryMode', request.deliveryMode); if (request.otherDeliveryMode) formData.append('otherDeliveryMode', request.otherDeliveryMode); formData.append('lateRegistrationConfirmed', String(request.lateRegistrationConfirmed)); formData.append('nonStandardDeliveryConfirmed', String(request.nonStandardDeliveryConfirmed)); formData.append('missingEvidenceConfirmed', String(request.missingEvidenceConfirmed)); if (request.evidence) formData.append('evidence', request.evidence); return formData; }