@open3cl/engine 1.8.7 → 1.8.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/6.2_surface_sud_equivalente.js +77 -6
- package/6.2_surface_sud_equivalente.spec.js +138 -2
- package/README.md +14 -14
- package/engine.js +1 -1
- package/features/engine/domain/apport_et_besoin/surface-sud-equivalente.service.js +136 -70
- package/features/engine/domain/apport_et_besoin/surface-sud-equivalente.service.spec.js +280 -0
- package/features/engine/domain/engine.service.js +4 -1
- package/features/engine/domain/engine.service.spec.js +14 -0
- package/features/engine/domain/enveloppe/deperdition-enveloppe.service.js +8 -7
- package/features/engine/domain/enveloppe/deperdition-enveloppe.service.spec.js +230 -0
- package/package.json +1 -1
|
@@ -14,11 +14,6 @@ export function calc_sse_j(bv_list, ets, ca, zc, mois) {
|
|
|
14
14
|
return sseBaiesExt;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
// Certaines vérandas sont dupliqués dans les DPE.
|
|
18
|
-
if (Array.isArray(ets)) {
|
|
19
|
-
ets = ets[0];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
17
|
/**
|
|
23
18
|
* 6.3 Traitement des espaces tampons solarisés
|
|
24
19
|
* Sse_ver n'est calculée que pour les baies vitrées qui séparent le logement de l'espace tampon
|
|
@@ -28,6 +23,82 @@ export function calc_sse_j(bv_list, ets, ca, zc, mois) {
|
|
|
28
23
|
return sseBaiesExt;
|
|
29
24
|
}
|
|
30
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Plusieurs espaces tampons solarisés (issue #101) : T, bver, Sst et Ssd sont propres à chaque
|
|
28
|
+
* véranda. Sse = Sse_ext + Σ(v) Sse_veranda_v, chaque baie adjacence 10 n'étant comptée que dans
|
|
29
|
+
* la véranda sur laquelle elle donne (baie.reference_lnc = ets.reference).
|
|
30
|
+
*/
|
|
31
|
+
const etsList = dedoublonnerEts(Array.isArray(ets) ? ets : [ets]);
|
|
32
|
+
const baiesParEts = rattacherBaiesAuxEts(baiesAdjVeranda, etsList);
|
|
33
|
+
|
|
34
|
+
return etsList.reduce(
|
|
35
|
+
(acc, etsItem, idx) => acc + calc_sse_veranda_j(etsItem, baiesParEts[idx], zc, mois),
|
|
36
|
+
sseBaiesExt
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Certaines vérandas sont dupliquées dans les DPE : une même véranda (même référence, ou à défaut
|
|
42
|
+
* même contenu) ne doit être comptée qu'une seule fois.
|
|
43
|
+
*
|
|
44
|
+
* @param etsList {EtsItem[]}
|
|
45
|
+
* @returns {EtsItem[]}
|
|
46
|
+
*/
|
|
47
|
+
export function dedoublonnerEts(etsList) {
|
|
48
|
+
const vues = new Set();
|
|
49
|
+
return etsList.filter((etsItem) => {
|
|
50
|
+
if (!etsItem) return false;
|
|
51
|
+
const reference = etsItem.donnee_entree?.reference;
|
|
52
|
+
const cle = reference ? `ref:${reference}` : `json:${JSON.stringify(etsItem)}`;
|
|
53
|
+
if (vues.has(cle)) return false;
|
|
54
|
+
vues.add(cle);
|
|
55
|
+
return true;
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Rattache chaque baie vitrée adjacence 10 à l'espace tampon sur lequel elle donne
|
|
61
|
+
* (baie.donnee_entree.reference_lnc = ets.donnee_entree.reference).
|
|
62
|
+
*
|
|
63
|
+
* Une baie sans lien exploitable (reference_lnc absente ou ne correspondant à aucune véranda) est
|
|
64
|
+
* rattachée à la première véranda : c'est le comportement historique, et le seul possible quand le
|
|
65
|
+
* DPE ne comporte qu'une véranda. La méthode ne permet pas de faire mieux pour une donnée manquante.
|
|
66
|
+
*
|
|
67
|
+
* @param baiesAdjVeranda {BaieVitreeItem[]}
|
|
68
|
+
* @param etsList {EtsItem[]}
|
|
69
|
+
* @returns {BaieVitreeItem[][]} baies par véranda, dans l'ordre de etsList
|
|
70
|
+
*/
|
|
71
|
+
export function rattacherBaiesAuxEts(baiesAdjVeranda, etsList) {
|
|
72
|
+
const baiesParEts = etsList.map(() => []);
|
|
73
|
+
// Aucune véranda exploitable : les baies adjacence 10 ne peuvent être rattachées à aucun ETS
|
|
74
|
+
if (etsList.length === 0) {
|
|
75
|
+
return baiesParEts;
|
|
76
|
+
}
|
|
77
|
+
baiesAdjVeranda.forEach((bv) => {
|
|
78
|
+
const referenceLnc = bv.donnee_entree.reference_lnc;
|
|
79
|
+
const idx = referenceLnc
|
|
80
|
+
? etsList.findIndex((etsItem) => etsItem.donnee_entree?.reference === referenceLnc)
|
|
81
|
+
: -1;
|
|
82
|
+
baiesParEts[idx === -1 ? 0 : idx].push(bv);
|
|
83
|
+
});
|
|
84
|
+
return baiesParEts;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Surface sud équivalente apportée par une véranda sur le mois j (§6.3).
|
|
89
|
+
*
|
|
90
|
+
* @param ets {EtsItem}
|
|
91
|
+
* @param baiesAdjVeranda {BaieVitreeItem[]} baies séparant le logement de cette véranda
|
|
92
|
+
* @param zc {string}
|
|
93
|
+
* @param mois {string}
|
|
94
|
+
* @returns {number}
|
|
95
|
+
*/
|
|
96
|
+
function calc_sse_veranda_j(ets, baiesAdjVeranda, zc, mois) {
|
|
97
|
+
// Véranda sans baie vitrée vers le logement : aucun apport solaire
|
|
98
|
+
if (baiesAdjVeranda.length === 0) {
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
31
102
|
const bver = ets.donnee_intermediaire.bver;
|
|
32
103
|
const T = ets.donnee_intermediaire.coef_transparence_ets;
|
|
33
104
|
|
|
@@ -80,7 +151,7 @@ export function calc_sse_j(bv_list, ets, ca, zc, mois) {
|
|
|
80
151
|
*/
|
|
81
152
|
const SseVerandaj = Ssdj + ssIndj * bver;
|
|
82
153
|
|
|
83
|
-
return
|
|
154
|
+
return SseVerandaj;
|
|
84
155
|
}
|
|
85
156
|
|
|
86
157
|
/**
|
|
@@ -8,7 +8,7 @@ import { describe, expect, test, vi } from 'vitest';
|
|
|
8
8
|
*/
|
|
9
9
|
vi.mock('./enums.js', () => ({
|
|
10
10
|
default: {
|
|
11
|
-
orientation: { 1: 'sud', 2: 'horizontal' },
|
|
11
|
+
orientation: { 1: 'sud', 2: 'horizontal', 3: 'est', 4: 'nord' },
|
|
12
12
|
inclinaison_vitrage: { 3: 'verticale', 4: 'horizontal' }
|
|
13
13
|
}
|
|
14
14
|
}));
|
|
@@ -18,6 +18,11 @@ vi.mock('./tv.js', () => ({
|
|
|
18
18
|
c1: {
|
|
19
19
|
h1a: {
|
|
20
20
|
Janvier: { 'sud verticale': 0.5, horizontal: 0.3 }
|
|
21
|
+
},
|
|
22
|
+
// Coefficients C1 de janvier en h1a (sud = 1, est = 0,40, nord = 0,31) pour les cas
|
|
23
|
+
// de test d'Olivier (issue #101)
|
|
24
|
+
h1b: {
|
|
25
|
+
Janvier: { 'sud verticale': 1, 'est verticale': 0.4, 'nord verticale': 0.31 }
|
|
21
26
|
}
|
|
22
27
|
}
|
|
23
28
|
}
|
|
@@ -28,7 +33,8 @@ vi.mock('./utils.js', () => ({
|
|
|
28
33
|
mois_liste: ['Janvier']
|
|
29
34
|
}));
|
|
30
35
|
|
|
31
|
-
const { calc_sse_j, calc_sse } =
|
|
36
|
+
const { calc_sse_j, calc_sse, dedoublonnerEts, rattacherBaiesAuxEts } =
|
|
37
|
+
await import('./6.2_surface_sud_equivalente.js');
|
|
32
38
|
|
|
33
39
|
/** Fabrique une baie vitrée avec les données d'entrée/intermédiaires utiles au calcul. */
|
|
34
40
|
function baie({ adjacence, surface, orientationId = '1', inclinaisonId = '3', sw = 1 }) {
|
|
@@ -212,3 +218,133 @@ describe('calc_sse - surface sud équivalente annuelle', () => {
|
|
|
212
218
|
expect(calc_sse('ca1', 'h1a', bvList, null)).toBeCloseTo(0.4, 10);
|
|
213
219
|
});
|
|
214
220
|
});
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* 6.3 Plusieurs espaces tampons solarisés (issue #101) : T, bver, Sst et Ssd sont propres à chaque
|
|
224
|
+
* véranda ; Sse = Sse_ext + Σ(v) Sse_veranda_v. Une baie adjacence 10 n'entre que dans la véranda
|
|
225
|
+
* sur laquelle elle donne (reference_lnc = ets.reference).
|
|
226
|
+
* Cas de test fournis par Olivier (thermicien), zone h1b mockée avec C1 sud = 1, est = 0,40,
|
|
227
|
+
* nord = 0,31 ; Fe = 1.
|
|
228
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §6.3
|
|
229
|
+
*/
|
|
230
|
+
describe('calc_sse_j - plusieurs espaces tampons solarisés (issue #101)', () => {
|
|
231
|
+
const SUD = '1';
|
|
232
|
+
const EST = '3';
|
|
233
|
+
const NORD = '4';
|
|
234
|
+
|
|
235
|
+
function baieInt(surface, orientationId, referenceLnc) {
|
|
236
|
+
const bv = baie({ adjacence: '10', surface, orientationId, sw: 0.47 });
|
|
237
|
+
if (referenceLnc !== undefined) bv.donnee_entree.reference_lnc = referenceLnc;
|
|
238
|
+
return bv;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function veranda(reference, T, bver, baiesExt) {
|
|
242
|
+
return {
|
|
243
|
+
donnee_entree: { reference },
|
|
244
|
+
donnee_intermediaire: { bver, coef_transparence_ets: T },
|
|
245
|
+
baie_ets_collection: {
|
|
246
|
+
baie_ets: baiesExt.map(([surface, orientationId]) =>
|
|
247
|
+
baie({ adjacence: '1', surface, orientationId })
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Baie extérieure 10 m² sud, Sw = 0,5 => Sse_ext = 5
|
|
254
|
+
const baieExt = baie({ adjacence: '1', surface: 10, orientationId: SUD, sw: 0.5 });
|
|
255
|
+
// V1 : T = 0,62, bver = 0,55, baies vers l'extérieur 8 m² sud + 4 m² est
|
|
256
|
+
const V1 = veranda('V1', 0.62, 0.55, [
|
|
257
|
+
[8, SUD],
|
|
258
|
+
[4, EST]
|
|
259
|
+
]);
|
|
260
|
+
// V2 : T = 0,45, bver = 0,85, baie vers l'extérieur 6 m² nord
|
|
261
|
+
const V2 = veranda('V2', 0.45, 0.85, [[6, NORD]]);
|
|
262
|
+
const baiesV1 = [baieInt(4, SUD, 'V1'), baieInt(2, SUD, 'V1')];
|
|
263
|
+
const baiesV2 = [baieInt(2, NORD, 'V2'), baieInt(1, NORD, 'V2')];
|
|
264
|
+
|
|
265
|
+
// SseV1 = 1,7484 + (4,992 - 1,7484) * 0,55 = 3,53238
|
|
266
|
+
const SSE_V1 = 3.53238;
|
|
267
|
+
// SseV2 = 0,196695 + (0,71424 - 0,196695) * 0,85 = 0,63660825
|
|
268
|
+
const SSE_V2 = 0.63660825;
|
|
269
|
+
|
|
270
|
+
test('CT1 - cas de l’issue : Sse = Sse_ext + SseV1 + SseV2 avec T et bver propres', () => {
|
|
271
|
+
const bvList = [baieExt, ...baiesV1, ...baiesV2];
|
|
272
|
+
expect(calc_sse_j(bvList, [V1, V2], 'ca1', 'h1b', 'Janvier')).toBeCloseTo(
|
|
273
|
+
5 + SSE_V1 + SSE_V2,
|
|
274
|
+
9
|
|
275
|
+
);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test('CT2 - seules les baies de V2 : le Sst de V1 n’est pas appliqué', () => {
|
|
279
|
+
const bvList = [baieExt, ...baiesV2];
|
|
280
|
+
expect(calc_sse_j(bvList, [V1, V2], 'ca1', 'h1b', 'Janvier')).toBeCloseTo(5 + SSE_V2, 9);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test('CT3 - seules les baies de V1 : V2 sans baie vers le logement apporte 0', () => {
|
|
284
|
+
const bvList = [baieExt, ...baiesV1];
|
|
285
|
+
expect(calc_sse_j(bvList, [V1, V2], 'ca1', 'h1b', 'Janvier')).toBeCloseTo(5 + SSE_V1, 9);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test('CT4 - véranda dupliquée : pas de double comptage', () => {
|
|
289
|
+
const bvList = [baieExt, ...baiesV1];
|
|
290
|
+
expect(calc_sse_j(bvList, [V1, structuredClone(V1)], 'ca1', 'h1b', 'Janvier')).toBeCloseTo(
|
|
291
|
+
5 + SSE_V1,
|
|
292
|
+
9
|
|
293
|
+
);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test('baies sans reference_lnc : rattachées à la première véranda (comportement historique)', () => {
|
|
297
|
+
const bvList = [baieExt, baieInt(4, SUD), baieInt(2, SUD)];
|
|
298
|
+
expect(calc_sse_j(bvList, [V1, V2], 'ca1', 'h1b', 'Janvier')).toBeCloseTo(5 + SSE_V1, 9);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test('l’ordre des vérandas dans la collection est sans effet', () => {
|
|
302
|
+
const bvList = [baieExt, ...baiesV1, ...baiesV2];
|
|
303
|
+
expect(calc_sse_j(bvList, [V2, V1], 'ca1', 'h1b', 'Janvier')).toBeCloseTo(
|
|
304
|
+
5 + SSE_V1 + SSE_V2,
|
|
305
|
+
9
|
|
306
|
+
);
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
describe('dedoublonnerEts / rattacherBaiesAuxEts', () => {
|
|
311
|
+
test('dédoublonnage par référence, à défaut par contenu identique ; éléments vides ignorés', () => {
|
|
312
|
+
const a = { donnee_entree: { reference: 'A' }, x: 1 };
|
|
313
|
+
const aBis = { donnee_entree: { reference: 'A' }, x: 2 };
|
|
314
|
+
const sansRef = { donnee_intermediaire: { bver: 1 } };
|
|
315
|
+
const autreSansRef = { donnee_intermediaire: { bver: 2 } };
|
|
316
|
+
expect(
|
|
317
|
+
dedoublonnerEts([a, aBis, sansRef, structuredClone(sansRef), autreSansRef, null])
|
|
318
|
+
).toEqual([a, sansRef, autreSansRef]);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test('rattachement par reference_lnc ; référence inconnue => première véranda', () => {
|
|
322
|
+
const ets = [{ donnee_entree: { reference: 'A' } }, { donnee_entree: { reference: 'B' } }];
|
|
323
|
+
const bA = { donnee_entree: { reference_lnc: 'A' } };
|
|
324
|
+
const bB = { donnee_entree: { reference_lnc: 'B' } };
|
|
325
|
+
const bInconnue = { donnee_entree: { reference_lnc: 'Z' } };
|
|
326
|
+
const bSans = { donnee_entree: {} };
|
|
327
|
+
expect(rattacherBaiesAuxEts([bA, bB, bInconnue, bSans], ets)).toEqual([
|
|
328
|
+
[bA, bInconnue, bSans],
|
|
329
|
+
[bB]
|
|
330
|
+
]);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
test('aucune véranda exploitable (ets = [null]) : baies non rattachées, seules les baies extérieures comptent', () => {
|
|
334
|
+
const bA = { donnee_entree: { reference_lnc: 'A' } };
|
|
335
|
+
expect(rattacherBaiesAuxEts([bA], [])).toEqual([]);
|
|
336
|
+
const bvList = [
|
|
337
|
+
baie({ adjacence: '1', surface: 10, sw: 0.5 }),
|
|
338
|
+
baie({ adjacence: '10', surface: 4 })
|
|
339
|
+
];
|
|
340
|
+
// c1 h1a janvier sud = 0,5 => 10 × 0,5 × 0,5
|
|
341
|
+
expect(calc_sse_j(bvList, [null], 'ca1', 'h1a', 'Janvier')).toBeCloseTo(2.5, 9);
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
test('véranda sans donnee_entree : seules les baies sans lien lui sont rattachées', () => {
|
|
345
|
+
const ets = [{}, { donnee_entree: { reference: 'B' } }];
|
|
346
|
+
const bB = { donnee_entree: { reference_lnc: 'B' } };
|
|
347
|
+
const bSans = { donnee_entree: {} };
|
|
348
|
+
expect(rattacherBaiesAuxEts([bB, bSans], ets)).toEqual([[bSans], [bB]]);
|
|
349
|
+
});
|
|
350
|
+
});
|
package/README.md
CHANGED
|
@@ -370,7 +370,7 @@ Ces résultats sont **générés automatiquement** à la fin de `npm run test:co
|
|
|
370
370
|
|
|
371
371
|
<!-- CORPUS:START -->
|
|
372
372
|
|
|
373
|
-
> **Version `1.8.
|
|
373
|
+
> **Version `1.8.7`** · branche `main` · généré le 2026-10-02
|
|
374
374
|
> Seuil de tolérance **5%**
|
|
375
375
|
|
|
376
376
|
<table>
|
|
@@ -403,24 +403,24 @@ Ces résultats sont **générés automatiquement** à la fin de `npm run test:co
|
|
|
403
403
|
|
|
404
404
|
<table>
|
|
405
405
|
<tr>
|
|
406
|
-
<td align="center"><strong>4,
|
|
407
|
-
<td align="center"><strong>4,
|
|
408
|
-
<td align="center"><strong>0,
|
|
409
|
-
<td align="center"><strong>
|
|
406
|
+
<td align="center"><strong>4,80 ms</strong><br/><sub>moyenne</sub></td>
|
|
407
|
+
<td align="center"><strong>4,20 ms</strong><br/><sub>médiane</sub></td>
|
|
408
|
+
<td align="center"><strong>0,11 ms</strong><br/><sub>min</sub></td>
|
|
409
|
+
<td align="center"><strong>518,4 ms</strong><br/><sub>max</sub></td>
|
|
410
410
|
</tr>
|
|
411
411
|
</table>
|
|
412
412
|
|
|
413
413
|
| Corpus | Moyenne | Médiane | Min | Max | p95 | p99 |
|
|
414
414
|
| :--------------------------------------------- | ------: | ------: | ------: | -------: | ------: | ------: |
|
|
415
|
-
| **Généraliste** | 4,
|
|
416
|
-
| **Appartement · chauffage individuel (2025)** | 3,
|
|
417
|
-
| **Logement individuel (2025)** | 4,
|
|
418
|
-
| **Maison individuelle (2025)** | 5,
|
|
419
|
-
| **Immeuble · chauffage individuel** |
|
|
420
|
-
| **Appartement · chauffage collectif (2025)** | 3,
|
|
421
|
-
| **Immeuble · chauffage collectif** | 5,
|
|
422
|
-
| **Immeuble · chauffage mixte** |
|
|
423
|
-
| **Individuel généré depuis l'immeuble (2026)** | 5,
|
|
415
|
+
| **Généraliste** | 4,33 ms | 3,90 ms | 0,20 ms | 36,9 ms | 7,87 ms | 11,1 ms |
|
|
416
|
+
| **Appartement · chauffage individuel (2025)** | 3,48 ms | 3,15 ms | 1,06 ms | 34,8 ms | 6,09 ms | 7,83 ms |
|
|
417
|
+
| **Logement individuel (2025)** | 4,86 ms | 4,30 ms | 1,30 ms | 29,8 ms | 9,41 ms | 13,1 ms |
|
|
418
|
+
| **Maison individuelle (2025)** | 5,48 ms | 4,96 ms | 1,65 ms | 35,8 ms | 9,87 ms | 12,8 ms |
|
|
419
|
+
| **Immeuble · chauffage individuel** | 5,89 ms | 5,04 ms | 0,11 ms | 75,0 ms | 11,9 ms | 18,7 ms |
|
|
420
|
+
| **Appartement · chauffage collectif (2025)** | 3,86 ms | 3,58 ms | 1,18 ms | 31,7 ms | 6,29 ms | 7,87 ms |
|
|
421
|
+
| **Immeuble · chauffage collectif** | 5,21 ms | 4,43 ms | 0,11 ms | 110,0 ms | 9,58 ms | 16,9 ms |
|
|
422
|
+
| **Immeuble · chauffage mixte** | 4,89 ms | 4,01 ms | 0,20 ms | 518,4 ms | 10,0 ms | 17,0 ms |
|
|
423
|
+
| **Individuel généré depuis l'immeuble (2026)** | 5,22 ms | 4,47 ms | 1,61 ms | 40,6 ms | 9,86 ms | 15,4 ms |
|
|
424
424
|
|
|
425
425
|
<sub>La moyenne est tirée vers le haut par les DPE collectifs, dont le coût atteint plusieurs
|
|
426
426
|
dizaines de fois la médiane : c’est la médiane qui décrit le cas courant, et p95/p99 le cas
|
package/engine.js
CHANGED
|
@@ -33,7 +33,7 @@ import getFicheTechnique from './ficheTechnique.js';
|
|
|
33
33
|
import { ProductionENR } from './16.2_production_enr.js';
|
|
34
34
|
import DpeSanitizerService from './dpe-sanitizer.service.js';
|
|
35
35
|
|
|
36
|
-
const LIB_VERSION = '1.8.
|
|
36
|
+
const LIB_VERSION = '1.8.8';
|
|
37
37
|
|
|
38
38
|
const dpeSanitizerService = new DpeSanitizerService();
|
|
39
39
|
|
|
@@ -42,88 +42,154 @@ export class SurfaceSudEquivalenteService {
|
|
|
42
42
|
*/
|
|
43
43
|
ssdMois(ctx, enveloppe, mois) {
|
|
44
44
|
const baiesVitrees = enveloppe.baie_vitree_collection?.baie_vitree || [];
|
|
45
|
-
|
|
45
|
+
const zc = ctx.zoneClimatique.id;
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Baies vitrées donnant directement sur l'extérieur
|
|
49
|
+
*/
|
|
50
|
+
const sseBaiesExt = baiesVitrees
|
|
51
|
+
.filter((bv) => parseInt(bv.donnee_entree.enum_type_adjacence_id) === 1)
|
|
52
|
+
.reduce((acc, bv) => acc + this.ssdBaieMois(bv, zc, mois), 0);
|
|
48
53
|
|
|
49
54
|
/**
|
|
50
|
-
* Si une véranda est présente, on calcul l'mpact de l’espace tampon solarisé sur les apports solaires à travers
|
|
51
|
-
* les baies vitrées qui séparent le logement de l'espace tampon
|
|
52
|
-
*
|
|
53
55
|
* 6.3 Traitement des espaces tampons solarisés
|
|
54
56
|
* 10 - 'espace tampon solarisé (véranda,loggia fermée)'
|
|
57
|
+
*
|
|
58
|
+
* Plusieurs espaces tampons solarisés (issue #101) : T, bver, Sst et Ssd sont propres à chaque
|
|
59
|
+
* véranda. Sse = Sse_ext + Σ(v) Sse_veranda_v, chaque baie adjacence 10 n'étant comptée que dans
|
|
60
|
+
* la véranda sur laquelle elle donne (baie.reference_lnc = ets.reference).
|
|
61
|
+
* Sse_ver n'est calculée que pour les baies vitrées qui séparent le logement de l'espace tampon :
|
|
62
|
+
* sans aucune de ces baies, les apports de l'espace tampon n'entrent pas dans le logement.
|
|
55
63
|
*/
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (!Array.isArray(baies)) {
|
|
82
|
-
baies = [baies];
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Apports totaux à travers l'espace tampon
|
|
87
|
-
* @type {number}
|
|
88
|
-
*/
|
|
89
|
-
const Sstj = baies.reduce((acc, bv) => {
|
|
90
|
-
return acc + this.ssdBaieMois(bv, ctx.zoneClimatique.id, mois, 0.8 * T + 0.024);
|
|
91
|
-
}, 0);
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Surface sud équivalente représentant l’impact des apports solaires indirects associés au rayonnement
|
|
95
|
-
* solaire entrant dans la partie habitable du logement après de multiples réflexions dans l’espace tampon solarisé
|
|
96
|
-
* @type {number}
|
|
97
|
-
*/
|
|
98
|
-
const Ssindj = Sstj - Ssdj;
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Impact de l’espace tampon solarisé sur les apports solaires à travers les baies vitrées qui séparent le logement
|
|
102
|
-
* de l'espace tampon
|
|
103
|
-
* @type {number}
|
|
104
|
-
*/
|
|
105
|
-
SseVerandaj = Ssdj + Ssindj * bver;
|
|
106
|
-
}
|
|
64
|
+
const etsList = this.dedoublonnerEts(this.getEtsList(enveloppe));
|
|
65
|
+
const baiesParEts = this.rattacherBaiesAuxEts(
|
|
66
|
+
this.getBaiesSurEspaceTampon(baiesVitrees),
|
|
67
|
+
etsList
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
return etsList.reduce(
|
|
71
|
+
(acc, ets, idx) => acc + this.sseVerandaMois(ets, baiesParEts[idx], zc, mois),
|
|
72
|
+
sseBaiesExt
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Surface sud équivalente apportée par une véranda sur un mois donné (§6.3)
|
|
78
|
+
*
|
|
79
|
+
* @param ets {Ets}
|
|
80
|
+
* @param baiesAdjVeranda {BaieVitree[]} baies vitrées séparant le logement de cette véranda
|
|
81
|
+
* @param zc {string} zone climatique du logement
|
|
82
|
+
* @param mois {string}
|
|
83
|
+
* @returns {number}
|
|
84
|
+
*/
|
|
85
|
+
sseVerandaMois(ets, baiesAdjVeranda, zc, mois) {
|
|
86
|
+
// Véranda sans baie vitrée vers le logement : aucun apport solaire
|
|
87
|
+
if (baiesAdjVeranda.length === 0) {
|
|
88
|
+
return 0;
|
|
107
89
|
}
|
|
108
90
|
|
|
109
|
-
|
|
110
|
-
|
|
91
|
+
const bver = ets.donnee_intermediaire.bver;
|
|
92
|
+
const T = ets.donnee_intermediaire.coef_transparence_ets;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Surface sud équivalente représentant l’impact des apports solaires associés au rayonnement solaire
|
|
96
|
+
* traversant directement l’espace tampon pour arriver dans la partie habitable du logement
|
|
97
|
+
* Calculés pour les baies vitrées qui séparent le logement de cet espace tampon
|
|
98
|
+
* @type {number}
|
|
99
|
+
*/
|
|
100
|
+
const Ssdj = baiesAdjVeranda.reduce((acc, bv) => acc + T * this.ssdBaieMois(bv, zc, mois), 0);
|
|
111
101
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (typeAdjacence === 10 && ets) {
|
|
117
|
-
return acc + SseVerandaj;
|
|
118
|
-
}
|
|
102
|
+
/**
|
|
103
|
+
* Baies de l'espace tampon donnant sur l'extérieur (propres à chaque véranda)
|
|
104
|
+
*/
|
|
105
|
+
let baies = ets.baie_ets_collection?.baie_ets || [];
|
|
119
106
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
107
|
+
if (!Array.isArray(baies)) {
|
|
108
|
+
baies = [baies];
|
|
109
|
+
}
|
|
124
110
|
|
|
125
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Apports totaux à travers l'espace tampon
|
|
113
|
+
* @type {number}
|
|
114
|
+
*/
|
|
115
|
+
const Sstj = baies.reduce((acc, bv) => {
|
|
116
|
+
return acc + this.ssdBaieMois(bv, zc, mois, 0.8 * T + 0.024);
|
|
126
117
|
}, 0);
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Surface sud équivalente représentant l’impact des apports solaires indirects associés au rayonnement
|
|
121
|
+
* solaire entrant dans la partie habitable du logement après de multiples réflexions dans l’espace tampon solarisé
|
|
122
|
+
* @type {number}
|
|
123
|
+
*/
|
|
124
|
+
const Ssindj = Sstj - Ssdj;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Impact de l’espace tampon solarisé sur les apports solaires à travers les baies vitrées qui séparent le logement
|
|
128
|
+
* de l'espace tampon
|
|
129
|
+
*/
|
|
130
|
+
return Ssdj + Ssindj * bver;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Liste des espaces tampons solarisés du logement (la collection peut contenir un objet unique,
|
|
135
|
+
* un tableau, ou être absente selon les DPE)
|
|
136
|
+
*
|
|
137
|
+
* @param enveloppe {Enveloppe}
|
|
138
|
+
* @returns {Ets[]}
|
|
139
|
+
*/
|
|
140
|
+
getEtsList(enveloppe) {
|
|
141
|
+
const ets = enveloppe.ets_collection?.ets;
|
|
142
|
+
if (!ets) {
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
return Array.isArray(ets) ? ets : [ets];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Certaines vérandas sont dupliquées dans les DPE : une même véranda (même référence, ou à défaut
|
|
150
|
+
* même contenu) ne doit être comptée qu'une seule fois.
|
|
151
|
+
*
|
|
152
|
+
* @param etsList {Ets[]}
|
|
153
|
+
* @returns {Ets[]}
|
|
154
|
+
*/
|
|
155
|
+
dedoublonnerEts(etsList) {
|
|
156
|
+
const vues = new Set();
|
|
157
|
+
return etsList.filter((ets) => {
|
|
158
|
+
if (!ets) return false;
|
|
159
|
+
const reference = ets.donnee_entree?.reference;
|
|
160
|
+
const cle = reference ? `ref:${reference}` : `json:${JSON.stringify(ets)}`;
|
|
161
|
+
if (vues.has(cle)) return false;
|
|
162
|
+
vues.add(cle);
|
|
163
|
+
return true;
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Rattache chaque baie vitrée adjacence 10 à l'espace tampon sur lequel elle donne
|
|
169
|
+
* (baie.donnee_entree.reference_lnc = ets.donnee_entree.reference).
|
|
170
|
+
*
|
|
171
|
+
* Une baie sans lien exploitable (reference_lnc absente ou ne correspondant à aucune véranda) est
|
|
172
|
+
* rattachée à la première véranda : c'est le comportement historique, et le seul possible quand le
|
|
173
|
+
* DPE ne comporte qu'une véranda.
|
|
174
|
+
*
|
|
175
|
+
* @param baiesAdjVeranda {BaieVitree[]}
|
|
176
|
+
* @param etsList {Ets[]}
|
|
177
|
+
* @returns {BaieVitree[][]} baies par véranda, dans l'ordre de etsList
|
|
178
|
+
*/
|
|
179
|
+
rattacherBaiesAuxEts(baiesAdjVeranda, etsList) {
|
|
180
|
+
const baiesParEts = etsList.map(() => []);
|
|
181
|
+
// Aucune véranda exploitable : les baies adjacence 10 ne peuvent être rattachées à aucun ETS
|
|
182
|
+
if (etsList.length === 0) {
|
|
183
|
+
return baiesParEts;
|
|
184
|
+
}
|
|
185
|
+
baiesAdjVeranda.forEach((bv) => {
|
|
186
|
+
const referenceLnc = bv.donnee_entree.reference_lnc;
|
|
187
|
+
const idx = referenceLnc
|
|
188
|
+
? etsList.findIndex((ets) => ets.donnee_entree?.reference === referenceLnc)
|
|
189
|
+
: -1;
|
|
190
|
+
baiesParEts[idx === -1 ? 0 : idx].push(bv);
|
|
191
|
+
});
|
|
192
|
+
return baiesParEts;
|
|
127
193
|
}
|
|
128
194
|
|
|
129
195
|
/**
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
|
2
|
+
import { SurfaceSudEquivalenteService } from './surface-sud-equivalente.service.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Orientations utilisées dans les tests (identifiants arbitraires : le store est un double de test)
|
|
6
|
+
*/
|
|
7
|
+
const SUD = '1';
|
|
8
|
+
const NORD = '2';
|
|
9
|
+
const EST = '3';
|
|
10
|
+
const HORIZONTAL = '5';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Coefficients C1 de janvier (sud = 1, est = 0,40, nord = 0,31, horizontal = 0,3) utilisés pour les
|
|
14
|
+
* cas de test d'Olivier (issue #101). Indexés par orientation.
|
|
15
|
+
*/
|
|
16
|
+
const C1 = { [SUD]: 1, [NORD]: 0.31, [EST]: 0.4, [HORIZONTAL]: 0.3 };
|
|
17
|
+
|
|
18
|
+
/** @type {{ getCoefficientBaieVitree: import('vitest').Mock }} */
|
|
19
|
+
let tvStore;
|
|
20
|
+
|
|
21
|
+
/** @type {SurfaceSudEquivalenteService} */
|
|
22
|
+
let service;
|
|
23
|
+
|
|
24
|
+
/** @type {Contexte} */
|
|
25
|
+
const ctx = { zoneClimatique: { id: '2', value: 'h1b' } };
|
|
26
|
+
|
|
27
|
+
/** Fabrique une baie vitrée avec les données d'entrée/intermédiaires utiles au calcul. */
|
|
28
|
+
function baie({ adjacence = '1', surface, orientationId = SUD, sw = 1, referenceLnc }) {
|
|
29
|
+
const bv = {
|
|
30
|
+
donnee_entree: {
|
|
31
|
+
enum_type_adjacence_id: adjacence,
|
|
32
|
+
enum_orientation_id: orientationId,
|
|
33
|
+
enum_inclinaison_vitrage_id: '3',
|
|
34
|
+
surface_totale_baie: surface
|
|
35
|
+
},
|
|
36
|
+
donnee_intermediaire: { sw }
|
|
37
|
+
};
|
|
38
|
+
if (referenceLnc !== undefined) bv.donnee_entree.reference_lnc = referenceLnc;
|
|
39
|
+
return bv;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Fabrique une baie de l'espace tampon donnant sur l'extérieur (pas de donnée intermédiaire). */
|
|
43
|
+
function baieEts(surface, orientationId) {
|
|
44
|
+
return {
|
|
45
|
+
donnee_entree: {
|
|
46
|
+
enum_orientation_id: orientationId,
|
|
47
|
+
enum_inclinaison_vitrage_id: '3',
|
|
48
|
+
surface_totale_baie: surface
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Fabrique une véranda avec ses propres T, bver et baies extérieures. */
|
|
54
|
+
function veranda(reference, T, bver, baiesExt) {
|
|
55
|
+
return {
|
|
56
|
+
donnee_entree: { reference },
|
|
57
|
+
donnee_intermediaire: { bver, coef_transparence_ets: T },
|
|
58
|
+
baie_ets_collection: {
|
|
59
|
+
baie_ets: baiesExt.map(([surface, orientationId]) => baieEts(surface, orientationId))
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function enveloppe(baiesVitrees, ets) {
|
|
65
|
+
return {
|
|
66
|
+
baie_vitree_collection: { baie_vitree: baiesVitrees },
|
|
67
|
+
ets_collection: ets === undefined ? {} : { ets }
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
describe('Calcul de la surface sud équivalente (§6.2 / §6.3)', () => {
|
|
72
|
+
beforeEach(() => {
|
|
73
|
+
tvStore = {
|
|
74
|
+
getCoefficientBaieVitree: vi.fn((orientation) => C1[orientation])
|
|
75
|
+
};
|
|
76
|
+
service = new SurfaceSudEquivalenteService(tvStore);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('ssdBaieMois', () => {
|
|
80
|
+
test('interroge la table C1 avec orientation, inclinaison, zone climatique et mois', () => {
|
|
81
|
+
const bv = baie({ surface: 10, orientationId: EST, sw: 0.5 });
|
|
82
|
+
// 10 * 0,40 * 0,5
|
|
83
|
+
expect(service.ssdBaieMois(bv, '2', 'Janvier')).toBeCloseTo(2, 9);
|
|
84
|
+
expect(tvStore.getCoefficientBaieVitree).toHaveBeenCalledWith(3, 3, 2, 'Janvier');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('inclinaison absente : vitrage vertical (3) par défaut ; facteurs fe1/fe2 appliqués', () => {
|
|
88
|
+
const bv = baie({ surface: 10, sw: 0.5 });
|
|
89
|
+
delete bv.donnee_entree.enum_inclinaison_vitrage_id;
|
|
90
|
+
bv.donnee_intermediaire.fe1 = 0.8;
|
|
91
|
+
bv.donnee_intermediaire.fe2 = 0.5;
|
|
92
|
+
expect(service.ssdBaieMois(bv, '2', 'Janvier')).toBeCloseTo(10 * 0.5 * 0.8 * 0.5, 9);
|
|
93
|
+
expect(tvStore.getCoefficientBaieVitree).toHaveBeenCalledWith(1, 3, 2, 'Janvier');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('un coefficient explicite remplace le facteur solaire sw de la baie', () => {
|
|
97
|
+
expect(service.ssdBaieMois(baieEts(10, SUD), '2', 'Janvier', 0.3)).toBeCloseTo(3, 9);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe('ssdMois - sans espace tampon solarisé', () => {
|
|
102
|
+
test('somme des baies donnant sur l’extérieur ; les autres adjacences sont ignorées', () => {
|
|
103
|
+
const bvList = [
|
|
104
|
+
baie({ surface: 10, sw: 0.5 }),
|
|
105
|
+
baie({ adjacence: '10', surface: 4 }),
|
|
106
|
+
baie({ adjacence: '3', surface: 4 })
|
|
107
|
+
];
|
|
108
|
+
expect(service.ssdMois(ctx, enveloppe(bvList), 'Janvier')).toBeCloseTo(5, 9);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('ets_collection vide (tableau) ou absente : seules les baies extérieures comptent', () => {
|
|
112
|
+
const bvList = [baie({ surface: 10, sw: 0.5 }), baie({ adjacence: '10', surface: 4 })];
|
|
113
|
+
expect(service.ssdMois(ctx, enveloppe(bvList, []), 'Janvier')).toBeCloseTo(5, 9);
|
|
114
|
+
expect(
|
|
115
|
+
service.ssdMois(ctx, { baie_vitree_collection: { baie_vitree: bvList } }, 'Janvier')
|
|
116
|
+
).toBeCloseTo(5, 9);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('aucune baie vitrée : surface sud équivalente nulle', () => {
|
|
120
|
+
expect(service.ssdMois(ctx, {}, 'Janvier')).toBe(0);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('ssdMois - un espace tampon solarisé', () => {
|
|
125
|
+
test('apports directs + indirects : Sse = Sse_ext + Ssd + (Sst - Ssd) × bver', () => {
|
|
126
|
+
// T = 0,5, bver = 0,8, baie extérieure ETS 10 m² sud (unique, non tableau)
|
|
127
|
+
const ets = {
|
|
128
|
+
donnee_entree: {},
|
|
129
|
+
donnee_intermediaire: { bver: 0.8, coef_transparence_ets: 0.5 },
|
|
130
|
+
baie_ets_collection: { baie_ets: baieEts(10, SUD) }
|
|
131
|
+
};
|
|
132
|
+
const bvList = [baie({ surface: 10, sw: 0.5 }), baie({ adjacence: '10', surface: 2 })];
|
|
133
|
+
// Ssd = 0,5 × 2 × 1 × 1 = 1 ; Sst = 10 × (0,8 × 0,5 + 0,024) = 4,24
|
|
134
|
+
// Sse_ver = 1 + (4,24 - 1) × 0,8 = 3,592
|
|
135
|
+
expect(service.ssdMois(ctx, enveloppe(bvList, ets), 'Janvier')).toBeCloseTo(5 + 3.592, 9);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test('ETS sans baie adjacence 10 : bver et T sans effet, Sse_ver non ajoutée', () => {
|
|
139
|
+
const ets = veranda('V', 0.5, 0.8, [[10, SUD]]);
|
|
140
|
+
const bvList = [baie({ surface: 10, sw: 0.5 })];
|
|
141
|
+
expect(service.ssdMois(ctx, enveloppe(bvList, ets), 'Janvier')).toBeCloseTo(5, 9);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('ETS sans baie extérieure : Sst = 0', () => {
|
|
145
|
+
const ets = {
|
|
146
|
+
donnee_entree: {},
|
|
147
|
+
donnee_intermediaire: { bver: 0.5, coef_transparence_ets: 0.5 }
|
|
148
|
+
};
|
|
149
|
+
const bvList = [baie({ adjacence: '10', surface: 2 })];
|
|
150
|
+
// Ssd = 1 ; Sse_ver = 1 + (0 - 1) × 0,5 = 0,5
|
|
151
|
+
expect(service.ssdMois(ctx, enveloppe(bvList, ets), 'Janvier')).toBeCloseTo(0.5, 9);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test('la Sse_ver n’est comptée qu’une fois quel que soit le nombre de baies adjacence 10', () => {
|
|
155
|
+
const ets = veranda('V', 0.5, 0.8, [[10, SUD]]);
|
|
156
|
+
const uneBaie = [baie({ adjacence: '10', surface: 2 })];
|
|
157
|
+
const deuxBaies = [
|
|
158
|
+
baie({ adjacence: '10', surface: 1 }),
|
|
159
|
+
baie({ adjacence: '10', surface: 1 })
|
|
160
|
+
];
|
|
161
|
+
expect(service.ssdMois(ctx, enveloppe(deuxBaies, ets), 'Janvier')).toBeCloseTo(
|
|
162
|
+
service.ssdMois(ctx, enveloppe(uneBaie, ets), 'Janvier'),
|
|
163
|
+
9
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 6.3 Plusieurs espaces tampons solarisés (issue #101) : T, bver, Sst et Ssd sont propres à chaque
|
|
170
|
+
* véranda ; Sse = Sse_ext + Σ(v) Sse_veranda_v. Une baie adjacence 10 n'entre que dans la véranda
|
|
171
|
+
* sur laquelle elle donne (reference_lnc = ets.reference).
|
|
172
|
+
* Cas de test fournis par Olivier (thermicien) : C1 sud = 1, est = 0,40, nord = 0,31 ; Fe = 1.
|
|
173
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §6.3
|
|
174
|
+
*/
|
|
175
|
+
describe('ssdMois - plusieurs espaces tampons solarisés (issue #101)', () => {
|
|
176
|
+
// Baie extérieure 10 m² sud, Sw = 0,5 => Sse_ext = 5
|
|
177
|
+
const baieExt = baie({ surface: 10, sw: 0.5 });
|
|
178
|
+
// V1 : T = 0,62, bver = 0,55, baies vers l'extérieur 8 m² sud + 4 m² est
|
|
179
|
+
const V1 = veranda('V1', 0.62, 0.55, [
|
|
180
|
+
[8, SUD],
|
|
181
|
+
[4, EST]
|
|
182
|
+
]);
|
|
183
|
+
// V2 : T = 0,45, bver = 0,85, baie vers l'extérieur 6 m² nord
|
|
184
|
+
const V2 = veranda('V2', 0.45, 0.85, [[6, NORD]]);
|
|
185
|
+
const baiesV1 = [
|
|
186
|
+
baie({ adjacence: '10', surface: 4, sw: 0.47, referenceLnc: 'V1' }),
|
|
187
|
+
baie({ adjacence: '10', surface: 2, sw: 0.47, referenceLnc: 'V1' })
|
|
188
|
+
];
|
|
189
|
+
const baiesV2 = [
|
|
190
|
+
baie({ adjacence: '10', surface: 2, orientationId: NORD, sw: 0.47, referenceLnc: 'V2' }),
|
|
191
|
+
baie({ adjacence: '10', surface: 1, orientationId: NORD, sw: 0.47, referenceLnc: 'V2' })
|
|
192
|
+
];
|
|
193
|
+
|
|
194
|
+
// SseV1 = 1,7484 + (4,992 - 1,7484) × 0,55 = 3,53238
|
|
195
|
+
const SSE_V1 = 3.53238;
|
|
196
|
+
// SseV2 = 0,196695 + (0,71424 - 0,196695) × 0,85 = 0,63660825
|
|
197
|
+
const SSE_V2 = 0.63660825;
|
|
198
|
+
|
|
199
|
+
test('CT1 - cas de l’issue : Sse = Sse_ext + SseV1 + SseV2 avec T et bver propres', () => {
|
|
200
|
+
const env = enveloppe([baieExt, ...baiesV1, ...baiesV2], [V1, V2]);
|
|
201
|
+
expect(service.ssdMois(ctx, env, 'Janvier')).toBeCloseTo(5 + SSE_V1 + SSE_V2, 9);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test('CT2 - seules les baies de V2 : le Sst de V1 n’est pas appliqué', () => {
|
|
205
|
+
const env = enveloppe([baieExt, ...baiesV2], [V1, V2]);
|
|
206
|
+
expect(service.ssdMois(ctx, env, 'Janvier')).toBeCloseTo(5 + SSE_V2, 9);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test('CT3 - seules les baies de V1 : V2 sans baie vers le logement apporte 0', () => {
|
|
210
|
+
const env = enveloppe([baieExt, ...baiesV1], [V1, V2]);
|
|
211
|
+
expect(service.ssdMois(ctx, env, 'Janvier')).toBeCloseTo(5 + SSE_V1, 9);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test('CT4 - véranda dupliquée : pas de double comptage', () => {
|
|
215
|
+
const env = enveloppe([baieExt, ...baiesV1], [V1, structuredClone(V1)]);
|
|
216
|
+
expect(service.ssdMois(ctx, env, 'Janvier')).toBeCloseTo(5 + SSE_V1, 9);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test('baies sans reference_lnc : rattachées à la première véranda (comportement historique)', () => {
|
|
220
|
+
const env = enveloppe(
|
|
221
|
+
[
|
|
222
|
+
baieExt,
|
|
223
|
+
baie({ adjacence: '10', surface: 4, sw: 0.47 }),
|
|
224
|
+
baie({ adjacence: '10', surface: 2, sw: 0.47 })
|
|
225
|
+
],
|
|
226
|
+
[V1, V2]
|
|
227
|
+
);
|
|
228
|
+
expect(service.ssdMois(ctx, env, 'Janvier')).toBeCloseTo(5 + SSE_V1, 9);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test('l’ordre des vérandas dans la collection est sans effet', () => {
|
|
232
|
+
const env = enveloppe([baieExt, ...baiesV1, ...baiesV2], [V2, V1]);
|
|
233
|
+
expect(service.ssdMois(ctx, env, 'Janvier')).toBeCloseTo(5 + SSE_V1 + SSE_V2, 9);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test('execute somme la surface sud équivalente de chaque mois', () => {
|
|
237
|
+
const env = enveloppe([baieExt, ...baiesV1, ...baiesV2], [V1, V2]);
|
|
238
|
+
// C1 indépendant du mois dans le double de test : 12 × valeur de janvier
|
|
239
|
+
expect(service.execute(ctx, env)).toBeCloseTo(12 * (5 + SSE_V1 + SSE_V2), 9);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe('dedoublonnerEts / rattacherBaiesAuxEts / getEtsList', () => {
|
|
244
|
+
test('getEtsList : objet unique, tableau ou absence', () => {
|
|
245
|
+
const ets = { donnee_entree: {} };
|
|
246
|
+
expect(service.getEtsList({ ets_collection: { ets } })).toEqual([ets]);
|
|
247
|
+
expect(service.getEtsList({ ets_collection: { ets: [ets] } })).toEqual([ets]);
|
|
248
|
+
expect(service.getEtsList({})).toEqual([]);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test('dédoublonnage par référence, à défaut par contenu identique ; éléments vides ignorés', () => {
|
|
252
|
+
const a = { donnee_entree: { reference: 'A' }, x: 1 };
|
|
253
|
+
const aBis = { donnee_entree: { reference: 'A' }, x: 2 };
|
|
254
|
+
const sansRef = { donnee_intermediaire: { bver: 1 } };
|
|
255
|
+
const autreSansRef = { donnee_intermediaire: { bver: 2 } };
|
|
256
|
+
expect(
|
|
257
|
+
service.dedoublonnerEts([a, aBis, sansRef, structuredClone(sansRef), autreSansRef, null])
|
|
258
|
+
).toEqual([a, sansRef, autreSansRef]);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
test('rattachement par reference_lnc ; référence inconnue ou absente => première véranda', () => {
|
|
262
|
+
const ets = [{ donnee_entree: { reference: 'A' } }, { donnee_entree: { reference: 'B' } }];
|
|
263
|
+
const bA = { donnee_entree: { reference_lnc: 'A' } };
|
|
264
|
+
const bB = { donnee_entree: { reference_lnc: 'B' } };
|
|
265
|
+
const bInconnue = { donnee_entree: { reference_lnc: 'Z' } };
|
|
266
|
+
const bSans = { donnee_entree: {} };
|
|
267
|
+
expect(service.rattacherBaiesAuxEts([bA, bB, bInconnue, bSans], ets)).toEqual([
|
|
268
|
+
[bA, bInconnue, bSans],
|
|
269
|
+
[bB]
|
|
270
|
+
]);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test('véranda sans donnee_entree : seules les baies sans lien lui sont rattachées', () => {
|
|
274
|
+
const ets = [{}, { donnee_entree: { reference: 'B' } }];
|
|
275
|
+
const bB = { donnee_entree: { reference_lnc: 'B' } };
|
|
276
|
+
const bSans = { donnee_entree: {} };
|
|
277
|
+
expect(service.rattacherBaiesAuxEts([bB, bSans], ets)).toEqual([[bSans], [bB]]);
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
});
|
|
@@ -138,7 +138,10 @@ export class EngineService {
|
|
|
138
138
|
delete m.baie_vitree_double_fenetre?.donnee_intermediaire;
|
|
139
139
|
delete m.donnee_intermediaire;
|
|
140
140
|
});
|
|
141
|
-
|
|
141
|
+
// Un DPE peut comporter un ou plusieurs espaces tampons solarisés (objet ou tableau)
|
|
142
|
+
[dpe.logement.enveloppe.ets_collection.ets]
|
|
143
|
+
.flat()
|
|
144
|
+
.forEach((ets) => delete ets?.donnee_intermediaire);
|
|
142
145
|
dpe.logement.enveloppe.plancher_bas_collection.plancher_bas?.map(
|
|
143
146
|
(m) => delete m.donnee_intermediaire
|
|
144
147
|
);
|
|
@@ -227,4 +227,18 @@ describe('EngineService (orchestration de la méthode 3CL)', () => {
|
|
|
227
227
|
// Aucun tableau interne ni production_elec_enr : les `?.` court-circuitent
|
|
228
228
|
expect(() => service.execute(dpeMinimal())).not.toThrow();
|
|
229
229
|
});
|
|
230
|
+
|
|
231
|
+
test('nettoie les données intermédiaires de chaque espace tampon solarisé (issue #101)', () => {
|
|
232
|
+
const dpe = dpeComplet();
|
|
233
|
+
dpe.logement.enveloppe.ets_collection.ets = [
|
|
234
|
+
{ donnee_intermediaire: { bver: 0.55 } },
|
|
235
|
+
{ donnee_intermediaire: { bver: 0.85 } },
|
|
236
|
+
null
|
|
237
|
+
];
|
|
238
|
+
const resultat = service.execute(dpe);
|
|
239
|
+
const [v1, v2, vide] = resultat.logement.enveloppe.ets_collection.ets;
|
|
240
|
+
expect(v1.donnee_intermediaire).toBeUndefined();
|
|
241
|
+
expect(v2.donnee_intermediaire).toBeUndefined();
|
|
242
|
+
expect(vide).toBeNull();
|
|
243
|
+
});
|
|
230
244
|
});
|
|
@@ -258,14 +258,15 @@ export class DeperditionEnveloppeService {
|
|
|
258
258
|
}
|
|
259
259
|
});
|
|
260
260
|
|
|
261
|
-
|
|
261
|
+
/**
|
|
262
|
+
* Un DPE peut comporter plusieurs espaces tampons solarisés (issue #101) : bver et le coefficient
|
|
263
|
+
* de transparence sont propres à chacun d'eux.
|
|
264
|
+
*/
|
|
265
|
+
const ets = enveloppe.ets_collection?.ets;
|
|
262
266
|
if (ets) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
ets.donnee_intermediaire = this.#espaceTamponService.execute(ctx, ets);
|
|
267
|
+
(Array.isArray(ets) ? ets : [ets]).forEach((e) => {
|
|
268
|
+
e.donnee_intermediaire = this.#espaceTamponService.execute(ctx, e);
|
|
269
|
+
});
|
|
269
270
|
}
|
|
270
271
|
|
|
271
272
|
enveloppe.pont_thermique_collection.pont_thermique?.forEach((pt) => {
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
|
2
|
+
import { DeperditionEnveloppeService } from './deperdition-enveloppe.service.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tests unitaires isolés de `DeperditionEnveloppeService`.
|
|
6
|
+
*
|
|
7
|
+
* Les services de déperdition par type de paroi sont remplacés par des doubles de test
|
|
8
|
+
* (spies `vi.fn`) : seule la logique d'agrégation propre au service est vérifiée
|
|
9
|
+
* (sommes b × S × U, surfaces déperditives / isolées / menuiseries, espaces tampons).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
let murService;
|
|
13
|
+
let porteService;
|
|
14
|
+
let plancherBasService;
|
|
15
|
+
let plancherHautService;
|
|
16
|
+
let baieVitreeService;
|
|
17
|
+
let espaceTamponService;
|
|
18
|
+
let pontThermiqueService;
|
|
19
|
+
let ventilationService;
|
|
20
|
+
|
|
21
|
+
/** @type {DeperditionEnveloppeService} */
|
|
22
|
+
let service;
|
|
23
|
+
|
|
24
|
+
const ctx = { zoneClimatique: { value: 'h1b' } };
|
|
25
|
+
|
|
26
|
+
/** Logement sans aucun élément d'enveloppe ni ventilation. */
|
|
27
|
+
function logementVide() {
|
|
28
|
+
return {
|
|
29
|
+
enveloppe: {
|
|
30
|
+
mur_collection: {},
|
|
31
|
+
porte_collection: {},
|
|
32
|
+
plancher_bas_collection: {},
|
|
33
|
+
plancher_haut_collection: {},
|
|
34
|
+
baie_vitree_collection: {},
|
|
35
|
+
ets_collection: {},
|
|
36
|
+
pont_thermique_collection: {}
|
|
37
|
+
},
|
|
38
|
+
ventilation_collection: {}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe('DeperditionEnveloppeService (§3 déperditions de l’enveloppe)', () => {
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
murService = { execute: vi.fn() };
|
|
45
|
+
porteService = { execute: vi.fn() };
|
|
46
|
+
plancherBasService = { execute: vi.fn() };
|
|
47
|
+
plancherHautService = { execute: vi.fn() };
|
|
48
|
+
baieVitreeService = { execute: vi.fn() };
|
|
49
|
+
espaceTamponService = { execute: vi.fn() };
|
|
50
|
+
pontThermiqueService = { execute: vi.fn() };
|
|
51
|
+
ventilationService = { execute: vi.fn() };
|
|
52
|
+
service = new DeperditionEnveloppeService(
|
|
53
|
+
murService,
|
|
54
|
+
porteService,
|
|
55
|
+
plancherBasService,
|
|
56
|
+
plancherHautService,
|
|
57
|
+
baieVitreeService,
|
|
58
|
+
espaceTamponService,
|
|
59
|
+
pontThermiqueService,
|
|
60
|
+
ventilationService
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('logement sans aucun élément : toutes les déperditions sont nulles', () => {
|
|
65
|
+
const logement = logementVide();
|
|
66
|
+
expect(service.deperditions(ctx, logement)).toEqual({
|
|
67
|
+
deperdition_mur: 0,
|
|
68
|
+
deperdition_plancher_bas: 0,
|
|
69
|
+
deperdition_plancher_haut: 0,
|
|
70
|
+
deperdition_baie_vitree: 0,
|
|
71
|
+
deperdition_pont_thermique: 0,
|
|
72
|
+
deperdition_porte: 0,
|
|
73
|
+
hperm: 0,
|
|
74
|
+
hvent: 0,
|
|
75
|
+
deperdition_enveloppe: 0
|
|
76
|
+
});
|
|
77
|
+
expect(espaceTamponService.execute).not.toHaveBeenCalled();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('agrège b × S × U par paroi et transmet les surfaces à la ventilation', () => {
|
|
81
|
+
const logement = logementVide();
|
|
82
|
+
const env = logement.enveloppe;
|
|
83
|
+
env.mur_collection.mur = [
|
|
84
|
+
// déperditif, non isolé (type 2)
|
|
85
|
+
{ donnee_entree: { surface_paroi_opaque: 10, enum_type_isolation_id: '2' } },
|
|
86
|
+
// déperditif, isolé, mais sur local non déperditif (22) : hors surface déperditive
|
|
87
|
+
{
|
|
88
|
+
donnee_entree: {
|
|
89
|
+
surface_paroi_opaque: 5,
|
|
90
|
+
enum_type_isolation_id: '3',
|
|
91
|
+
enum_type_adjacence_id: '22'
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
// b = 0 : ni surface déperditive, ni isolée
|
|
95
|
+
{ donnee_entree: { surface_paroi_opaque: 100, enum_type_isolation_id: '3' } }
|
|
96
|
+
];
|
|
97
|
+
murService.execute
|
|
98
|
+
.mockReturnValueOnce({ b: 1, umur: 2 })
|
|
99
|
+
.mockReturnValueOnce({ b: 0.5, umur: 1 })
|
|
100
|
+
.mockReturnValueOnce({ b: 0, umur: 1 });
|
|
101
|
+
|
|
102
|
+
env.porte_collection.porte = [
|
|
103
|
+
{ donnee_entree: { surface_porte: 2, presence_joint: true } },
|
|
104
|
+
{ donnee_entree: { surface_porte: 3, presence_joint: false } }
|
|
105
|
+
];
|
|
106
|
+
porteService.execute
|
|
107
|
+
.mockReturnValueOnce({ b: 1, uporte: 3 })
|
|
108
|
+
.mockReturnValueOnce({ b: 0, uporte: 3 });
|
|
109
|
+
|
|
110
|
+
env.plancher_bas_collection.plancher_bas = [{ donnee_entree: { surface_paroi_opaque: 20 } }];
|
|
111
|
+
plancherBasService.execute.mockReturnValue({ b: 1, upb_final: 0.5 });
|
|
112
|
+
|
|
113
|
+
env.plancher_haut_collection.plancher_haut = [
|
|
114
|
+
{ donnee_entree: { surface_paroi_opaque: 30, enum_type_isolation_id: '1' } },
|
|
115
|
+
{
|
|
116
|
+
donnee_entree: {
|
|
117
|
+
surface_paroi_opaque: 7,
|
|
118
|
+
enum_type_isolation_id: '4',
|
|
119
|
+
enum_type_adjacence_id: '22'
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
{ donnee_entree: { surface_paroi_opaque: 50, enum_type_isolation_id: '4' } }
|
|
123
|
+
];
|
|
124
|
+
plancherHautService.execute
|
|
125
|
+
.mockReturnValueOnce({ b: 1, uph: 1 })
|
|
126
|
+
.mockReturnValueOnce({ b: 1, uph: 1 })
|
|
127
|
+
.mockReturnValueOnce({ b: 0, uph: 1 });
|
|
128
|
+
|
|
129
|
+
env.baie_vitree_collection.baie_vitree = [
|
|
130
|
+
{ donnee_entree: { surface_totale_baie: 4, presence_joint: true } },
|
|
131
|
+
{ donnee_entree: { surface_totale_baie: 6 } }
|
|
132
|
+
];
|
|
133
|
+
baieVitreeService.execute
|
|
134
|
+
.mockReturnValueOnce({ b: 1, u_menuiserie: 2 })
|
|
135
|
+
.mockReturnValueOnce({ b: 0, u_menuiserie: 2 });
|
|
136
|
+
|
|
137
|
+
env.pont_thermique_collection.pont_thermique = [
|
|
138
|
+
{ donnee_entree: { l: 10, pourcentage_valeur_pont_thermique: 0.5 } },
|
|
139
|
+
{ donnee_entree: { l: 4 } }
|
|
140
|
+
];
|
|
141
|
+
pontThermiqueService.execute.mockReturnValue({ k: 0.5 });
|
|
142
|
+
|
|
143
|
+
logement.ventilation_collection.ventilation = [{ donnee_entree: { id: 'v' } }];
|
|
144
|
+
ventilationService.execute.mockReturnValue({ hvent: 10, hperm: 5 });
|
|
145
|
+
|
|
146
|
+
const resultat = service.deperditions(ctx, logement);
|
|
147
|
+
|
|
148
|
+
expect(resultat).toEqual({
|
|
149
|
+
deperdition_mur: 10 * 2 + 0.5 * 5 * 1,
|
|
150
|
+
deperdition_porte: 2 * 3,
|
|
151
|
+
deperdition_plancher_bas: 20 * 0.5,
|
|
152
|
+
deperdition_plancher_haut: 30 + 7,
|
|
153
|
+
deperdition_baie_vitree: 4 * 2,
|
|
154
|
+
deperdition_pont_thermique: 10 * 0.5 * 0.5 + 4 * 0.5,
|
|
155
|
+
hvent: 10,
|
|
156
|
+
hperm: 5,
|
|
157
|
+
deperdition_enveloppe: 22.5 + 6 + 10 + 37 + 8 + 4.5 + 10 + 5
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Délégation aux services de paroi
|
|
161
|
+
expect(plancherBasService.execute).toHaveBeenCalledWith(
|
|
162
|
+
ctx,
|
|
163
|
+
env.plancher_bas_collection.plancher_bas[0].donnee_entree,
|
|
164
|
+
env.plancher_bas_collection.plancher_bas
|
|
165
|
+
);
|
|
166
|
+
expect(pontThermiqueService.execute).toHaveBeenCalledWith(
|
|
167
|
+
ctx,
|
|
168
|
+
env,
|
|
169
|
+
env.pont_thermique_collection.pont_thermique[0].donnee_entree
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
// Surfaces transmises à la ventilation :
|
|
173
|
+
// déperditive = mur 10 + porte 2 + plancher haut 30 + baie 4 = 46
|
|
174
|
+
// isolée = mur 5 + plancher haut 7 = 12 ; non isolée = mur 10 + plancher haut 30 = 40
|
|
175
|
+
// menuiseries avec joint = porte 2 + baie 4 = 6 ; sans joint = porte 3 + baie 6 = 9
|
|
176
|
+
expect(ventilationService.execute).toHaveBeenCalledWith(ctx, { id: 'v' }, 46, 12, 40, 6, 9);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('les surfaces sont réinitialisées à chaque calcul', () => {
|
|
180
|
+
const logement = logementVide();
|
|
181
|
+
logement.enveloppe.porte_collection.porte = [{ donnee_entree: { surface_porte: 2 } }];
|
|
182
|
+
porteService.execute.mockReturnValue({ b: 1, uporte: 1 });
|
|
183
|
+
logement.ventilation_collection.ventilation = [{ donnee_entree: {} }];
|
|
184
|
+
ventilationService.execute.mockReturnValue({ hvent: 0, hperm: 0 });
|
|
185
|
+
|
|
186
|
+
service.deperditions(ctx, logement);
|
|
187
|
+
service.deperditions(ctx, logement);
|
|
188
|
+
|
|
189
|
+
expect(ventilationService.execute).toHaveBeenLastCalledWith(ctx, {}, 2, 0, 0, 0, 2);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe('espaces tampons solarisés', () => {
|
|
193
|
+
test('un ETS unique (objet) reçoit ses données intermédiaires', () => {
|
|
194
|
+
const logement = logementVide();
|
|
195
|
+
const ets = { donnee_entree: { tv_coef_transparence_ets_id: '2' } };
|
|
196
|
+
logement.enveloppe.ets_collection.ets = ets;
|
|
197
|
+
espaceTamponService.execute.mockReturnValue({ bver: 0.55, coef_transparence_ets: 0.62 });
|
|
198
|
+
|
|
199
|
+
service.deperditions(ctx, logement);
|
|
200
|
+
|
|
201
|
+
expect(espaceTamponService.execute).toHaveBeenCalledWith(ctx, ets);
|
|
202
|
+
expect(ets.donnee_intermediaire).toEqual({ bver: 0.55, coef_transparence_ets: 0.62 });
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test('plusieurs ETS : bver et T calculés pour chacun d’eux (issue #101)', () => {
|
|
206
|
+
const logement = logementVide();
|
|
207
|
+
const V1 = { donnee_entree: { reference: 'V1' } };
|
|
208
|
+
const V2 = { donnee_entree: { reference: 'V2' } };
|
|
209
|
+
logement.enveloppe.ets_collection.ets = [V1, V2];
|
|
210
|
+
espaceTamponService.execute
|
|
211
|
+
.mockReturnValueOnce({ bver: 0.55, coef_transparence_ets: 0.62 })
|
|
212
|
+
.mockReturnValueOnce({ bver: 0.85, coef_transparence_ets: 0.45 });
|
|
213
|
+
|
|
214
|
+
service.deperditions(ctx, logement);
|
|
215
|
+
|
|
216
|
+
expect(espaceTamponService.execute).toHaveBeenCalledTimes(2);
|
|
217
|
+
expect(espaceTamponService.execute).toHaveBeenNthCalledWith(1, ctx, V1);
|
|
218
|
+
expect(espaceTamponService.execute).toHaveBeenNthCalledWith(2, ctx, V2);
|
|
219
|
+
expect(V1.donnee_intermediaire).toEqual({ bver: 0.55, coef_transparence_ets: 0.62 });
|
|
220
|
+
expect(V2.donnee_intermediaire).toEqual({ bver: 0.85, coef_transparence_ets: 0.45 });
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test('sans ets_collection : aucun calcul d’espace tampon', () => {
|
|
224
|
+
const logement = logementVide();
|
|
225
|
+
delete logement.enveloppe.ets_collection;
|
|
226
|
+
service.deperditions(ctx, logement);
|
|
227
|
+
expect(espaceTamponService.execute).not.toHaveBeenCalled();
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
});
|