feat: finalize lease documents and IRL revisions
This commit is contained in:
1 parent
f127c69a28
commit
1bf6a3449c
196 files changed
+20564
-666
No files matched your search
@@ -1,11 +1,24 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { vi } from 'vitest';
|
||||
import { LeaseCard } from './lease-card';
|
||||
import { emptyApartmentLegalProfile } from '../../services/apartment.service';
|
||||
import { emptyLeaseLegalTerms } from '../../services/lease.service';
|
||||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { WorkspaceService } from '../../services/workspace.service';
|
||||
|
||||
describe('LeaseCard', () => {
|
||||
it('resolves apartment, tenants, and every status label', () => {
|
||||
const fixture = TestBed.createComponent(LeaseCard);
|
||||
fixture.componentRef.setInput('apartments', [
|
||||
{ id: 'apartment-1', name: 'T2', address: '', purchasePrice: 0, description: '' },
|
||||
{
|
||||
id: 'apartment-1',
|
||||
name: 'T2',
|
||||
address: '',
|
||||
purchasePrice: 0,
|
||||
description: '',
|
||||
legalProfile: emptyApartmentLegalProfile(),
|
||||
},
|
||||
]);
|
||||
fixture.componentRef.setInput('tenants', [
|
||||
{
|
||||
@@ -22,11 +35,17 @@ describe('LeaseCard', () => {
|
||||
apartmentId: 'apartment-1',
|
||||
tenantIds: ['tenant-1', 'missing', 'tenant-2'],
|
||||
guarantors: [],
|
||||
specialClauses: [],
|
||||
legalTerms: emptyLeaseLegalTerms(),
|
||||
leaseType: 'STANDARD',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
monthlyRent: 0,
|
||||
monthlyCharges: 0,
|
||||
securityDeposit: 0,
|
||||
dueDay: 5,
|
||||
chargeMethod: 'FLAT_RATE',
|
||||
indexFlatRate: false,
|
||||
status: 'DRAFT',
|
||||
});
|
||||
const component = fixture.componentInstance;
|
||||
@@ -54,14 +73,58 @@ describe('LeaseCard', () => {
|
||||
apartmentId: 'apartment-1',
|
||||
tenantIds: [],
|
||||
guarantors: [],
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2027-01-01',
|
||||
specialClauses: [],
|
||||
legalTerms: emptyLeaseLegalTerms(),
|
||||
leaseType: 'STANDARD',
|
||||
startDate: '2026-07-19',
|
||||
endDate: '2027-07-19',
|
||||
monthlyRent: 800,
|
||||
monthlyCharges: 50,
|
||||
securityDeposit: 800,
|
||||
dueDay: 5,
|
||||
chargeMethod: 'FLAT_RATE',
|
||||
indexFlatRate: false,
|
||||
status: 'ACTIVE',
|
||||
});
|
||||
fixture.componentRef.setInput('downloading', true);
|
||||
fixture.componentRef.setInput('revision', {
|
||||
number: 3,
|
||||
templateVersion: '2026.10',
|
||||
sourceFingerprint: 'source',
|
||||
sha256: 'sha256',
|
||||
generatedAt: '2026-07-19T10:00:00Z',
|
||||
authorId: 'owner-1',
|
||||
annexes: [
|
||||
{
|
||||
category: 'NOTICE',
|
||||
documentType: null,
|
||||
sourceId: '',
|
||||
version: 'FR-NOTICE-2023-03',
|
||||
fileName: 'notice-information.pdf',
|
||||
sha256: 'notice-sha256',
|
||||
establishedOn: null,
|
||||
expiresOn: null,
|
||||
sourceUploadedAt: null,
|
||||
},
|
||||
{
|
||||
category: 'APARTMENT_DOCUMENT',
|
||||
documentType: 'DPE',
|
||||
sourceId: 'document-1',
|
||||
version: '',
|
||||
fileName: 'dpe.pdf',
|
||||
sha256: 'dpe-sha256',
|
||||
establishedOn: '2026-07-19',
|
||||
expiresOn: '2036-07-19',
|
||||
sourceUploadedAt: '2026-07-19T10:00:00Z',
|
||||
},
|
||||
],
|
||||
warningCodes: ['DPE_EXPIRED', 'CO_OWNERSHIP_RULES_EXTRACT_MISSING'],
|
||||
warnings: [
|
||||
{ code: 'DPE_EXPIRED', ruleVersion: '2026-07-01' },
|
||||
{ code: 'CO_OWNERSHIP_RULES_EXTRACT_MISSING', ruleVersion: '2026-07-01' },
|
||||
],
|
||||
});
|
||||
fixture.componentRef.setInput('dossierDownloading', true);
|
||||
fixture.detectChanges();
|
||||
|
||||
const download = fixture.nativeElement.querySelector('.card-actions button');
|
||||
@@ -72,9 +135,39 @@ describe('LeaseCard', () => {
|
||||
fixture.componentRef.setInput('downloadError', 'Impossible de télécharger ce bail.');
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement.textContent).toContain('Impossible de télécharger ce bail.');
|
||||
expect(fixture.nativeElement.textContent).toContain('Révision 3 · modèle 2026.10');
|
||||
expect(fixture.nativeElement.textContent).toContain('Notice d’information');
|
||||
expect(fixture.nativeElement.textContent).toContain(
|
||||
'Diagnostic de performance énergétique (DPE)',
|
||||
);
|
||||
expect(fixture.nativeElement.textContent).toContain('dpe-sha256');
|
||||
expect(fixture.nativeElement.textContent).toContain('19 juillet 2026');
|
||||
expect(fixture.nativeElement.textContent).not.toContain('18 juillet 2026');
|
||||
expect(fixture.nativeElement.textContent).toContain(
|
||||
'Le DPE présent dans le dossier est expiré.',
|
||||
);
|
||||
expect(fixture.nativeElement.textContent).toContain(
|
||||
'L’extrait du règlement de copropriété manque au dossier.',
|
||||
);
|
||||
expect(fixture.nativeElement.textContent).toContain('Version de règle : 2026-07-01');
|
||||
expect(fixture.nativeElement.textContent).not.toContain('DPE_EXPIRED');
|
||||
const dossier: HTMLButtonElement = fixture.nativeElement.querySelector('.dossier-download');
|
||||
expect(dossier.disabled).toBe(true);
|
||||
expect(dossier.textContent).toContain('Préparation du dossier…');
|
||||
expect(download.textContent).toContain('Télécharger le bail');
|
||||
|
||||
fixture.componentRef.setInput('downloadError', '');
|
||||
fixture.componentRef.setInput(
|
||||
'dossierDownloadError',
|
||||
'Impossible de télécharger le dossier complet du bail.',
|
||||
);
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement.textContent).toContain(
|
||||
'Impossible de télécharger le dossier complet du bail.',
|
||||
);
|
||||
});
|
||||
|
||||
it('offers draft lifecycle actions with activation progress and an actionable failure', () => {
|
||||
it('offers draft editing and dedicated finalization actions', () => {
|
||||
const fixture = TestBed.createComponent(LeaseCard);
|
||||
fixture.componentRef.setInput('apartments', []);
|
||||
fixture.componentRef.setInput('tenants', []);
|
||||
@@ -83,36 +176,101 @@ describe('LeaseCard', () => {
|
||||
apartmentId: 'apartment-1',
|
||||
tenantIds: [],
|
||||
guarantors: [],
|
||||
specialClauses: [],
|
||||
legalTerms: emptyLeaseLegalTerms(),
|
||||
leaseType: 'STANDARD',
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2027-01-01',
|
||||
monthlyRent: 800,
|
||||
monthlyCharges: 50,
|
||||
securityDeposit: 800,
|
||||
dueDay: 5,
|
||||
chargeMethod: 'FLAT_RATE',
|
||||
indexFlatRate: false,
|
||||
status: 'DRAFT',
|
||||
});
|
||||
fixture.componentRef.setInput('activating', true);
|
||||
fixture.componentRef.setInput('activationError', 'Un bail actif chevauche cette période.');
|
||||
fixture.detectChanges();
|
||||
const requested = vi.fn();
|
||||
fixture.componentInstance.finalizationRequested.subscribe(requested);
|
||||
|
||||
const text = fixture.nativeElement.textContent.replace(/\s+/g, ' ');
|
||||
expect(text).toContain('Générer / télécharger le bail');
|
||||
expect(text).toContain('Modifier');
|
||||
expect(text).toContain('Supprimer');
|
||||
expect(text).toContain('Activation…');
|
||||
expect(text).toContain('Un bail actif chevauche cette période.');
|
||||
const activation = [...fixture.nativeElement.querySelectorAll('button')].find((button) =>
|
||||
button.textContent.includes('Activation…'),
|
||||
expect(text).toContain('Finaliser le bail');
|
||||
expect(text).not.toContain('Activer');
|
||||
const finalization = [...fixture.nativeElement.querySelectorAll('button')].find((button) =>
|
||||
button.textContent.includes('Finaliser le bail'),
|
||||
);
|
||||
expect(activation.disabled).toBe(true);
|
||||
finalization.click();
|
||||
expect(requested).toHaveBeenCalledWith('lease-1');
|
||||
|
||||
fixture.componentRef.setInput('lease', {
|
||||
...fixture.componentInstance.lease(),
|
||||
status: 'ACTIVE',
|
||||
});
|
||||
fixture.componentRef.setInput('activating', false);
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement.textContent).not.toContain('Modifier');
|
||||
expect(fixture.nativeElement.textContent).not.toContain('Supprimer');
|
||||
expect(fixture.nativeElement.textContent).not.toContain('Activer');
|
||||
expect(fixture.nativeElement.textContent).not.toContain('Finaliser le bail');
|
||||
});
|
||||
|
||||
it('offers an IRL preview only for an active lease with the revision clause enabled', () => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
provideHttpClient(),
|
||||
provideHttpClientTesting(),
|
||||
{ provide: WorkspaceService, useValue: { acceptRentRevisionApplication: vi.fn() } },
|
||||
],
|
||||
});
|
||||
const http = TestBed.inject(HttpTestingController);
|
||||
const initialLegalTerms = emptyLeaseLegalTerms();
|
||||
const legalTerms = {
|
||||
...initialLegalTerms,
|
||||
rentRevision: { ...initialLegalTerms.rentRevision, enabled: true },
|
||||
};
|
||||
const fixture = TestBed.createComponent(LeaseCard);
|
||||
fixture.componentRef.setInput('apartments', []);
|
||||
fixture.componentRef.setInput('tenants', []);
|
||||
fixture.componentRef.setInput('lease', {
|
||||
id: 'lease-1',
|
||||
apartmentId: 'apartment-1',
|
||||
tenantIds: [],
|
||||
guarantors: [],
|
||||
specialClauses: [],
|
||||
legalTerms,
|
||||
leaseType: 'STANDARD',
|
||||
startDate: '2026-07-19',
|
||||
endDate: '2027-07-19',
|
||||
monthlyRent: 800,
|
||||
monthlyCharges: 50,
|
||||
securityDeposit: 800,
|
||||
dueDay: 5,
|
||||
chargeMethod: 'FLAT_RATE',
|
||||
indexFlatRate: false,
|
||||
status: 'ACTIVE',
|
||||
});
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.nativeElement.querySelector('app-rent-revision-preview')).toBeNull();
|
||||
http.expectNone('/api/leases/lease-1/rent-revisions');
|
||||
const openRevision: HTMLButtonElement =
|
||||
fixture.nativeElement.querySelector('.open-rent-revision');
|
||||
expect(openRevision.getAttribute('aria-expanded')).toBe('false');
|
||||
openRevision.click();
|
||||
fixture.detectChanges();
|
||||
expect(openRevision.getAttribute('aria-expanded')).toBe('true');
|
||||
expect(fixture.nativeElement.querySelector('app-rent-revision-preview')).not.toBeNull();
|
||||
http.expectOne('/api/leases/lease-1/rent-revisions').flush([]);
|
||||
fixture.nativeElement.querySelector('.preview-submit').click();
|
||||
http.expectOne('/api/leases/lease-1/rent-revisions/preview').flush({});
|
||||
|
||||
fixture.componentRef.setInput('lease', {
|
||||
...fixture.componentInstance.lease(),
|
||||
status: 'DRAFT',
|
||||
});
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement.querySelector('app-rent-revision-preview')).toBeNull();
|
||||
http.verify();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user