@open3cl/engine 1.8.63 → 1.8.65
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/11_stockage_ecs_volume_chauffe.js +31 -0
- package/11_stockage_ecs_volume_chauffe.spec.js +35 -0
- package/4_ventilation.js +24 -3
- package/4_ventilation.spec.js +102 -1
- package/9_besoin_ch.js +2 -4
- package/9_besoin_ch.spec.js +58 -1
- package/README.md +26 -26
- package/engine.js +1 -1
- package/features/dpe/infrastructure/tv.store.js +10 -0
- package/features/dpe/infrastructure/tv.store.spec.js +11 -0
- package/features/engine/domain/ecs/installation-ecs.service.js +3 -5
- package/features/engine/domain/enveloppe/deperdition-enveloppe.service.js +20 -1
- package/features/engine/domain/enveloppe/deperdition-enveloppe.service.spec.js +23 -2
- package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.js +18 -3
- package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.spec.js +100 -0
- package/package.json +1 -1
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Le stockage ECS d'un générateur est-il en volume chauffé ?
|
|
3
|
+
*
|
|
4
|
+
* Tribu porte l'information au niveau de l'installation (`installation_ECS.Is_volume_chauffee`,
|
|
5
|
+
* Calcul_batiment.cs l.375-377). Dans l'export ADEME, elle se lit, par ordre de priorité :
|
|
6
|
+
* 1. `position_volume_chauffe_stockage` du générateur, s'il est renseigné ;
|
|
7
|
+
* 2. `tv_rendement_distribution_ecs_id` de l'installation : 1 ou 2 (production en volume
|
|
8
|
+
* habitable) → oui, 3 (production hors volume habitable) → non ;
|
|
9
|
+
* 3. `position_volume_chauffe` du générateur.
|
|
10
|
+
*
|
|
11
|
+
* @see Methode_de_calcul_3CL_DPE_2021-338.pdf - §9.1.1 et §11.3 (rendement de distribution)
|
|
12
|
+
* @param deGenerateur {object} donnee_entree du générateur ECS
|
|
13
|
+
* @param deInstallation {object} donnee_entree de l'installation ECS
|
|
14
|
+
* @returns {boolean}
|
|
15
|
+
*/
|
|
16
|
+
export function isStockageEcsVolumeChauffe(deGenerateur, deInstallation) {
|
|
17
|
+
const positionStockage = Number.parseInt(deGenerateur.position_volume_chauffe_stockage);
|
|
18
|
+
if (positionStockage === 0 || positionStockage === 1) {
|
|
19
|
+
return positionStockage === 1;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const rdId = Number.parseInt(deInstallation?.tv_rendement_distribution_ecs_id);
|
|
23
|
+
if (rdId === 1 || rdId === 2) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
if (rdId === 3) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return Number.parseInt(deGenerateur.position_volume_chauffe) !== 0;
|
|
31
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import { isStockageEcsVolumeChauffe } from './11_stockage_ecs_volume_chauffe.js';
|
|
3
|
+
|
|
4
|
+
describe('isStockageEcsVolumeChauffe - stockage ECS en volume chauffé (#293)', () => {
|
|
5
|
+
/**
|
|
6
|
+
* Tribu : installation_ECS.Is_volume_chauffee (Calcul_batiment.cs l.375-377), lue dans l'export
|
|
7
|
+
* par position_volume_chauffe_stockage, puis tv_rendement_distribution_ecs_id, puis
|
|
8
|
+
* position_volume_chauffe.
|
|
9
|
+
*/
|
|
10
|
+
test.each([
|
|
11
|
+
// [position stockage, rendement distribution, position générateur, attendu]
|
|
12
|
+
[1, '3', 0, true], // stockage renseigné prioritaire (oui)
|
|
13
|
+
[0, '1', 1, false], // stockage renseigné prioritaire (non)
|
|
14
|
+
['1', undefined, 0, true], // valeur texte acceptée
|
|
15
|
+
[undefined, '1', 0, true], // production en volume habitable
|
|
16
|
+
[undefined, 2, 0, true], // production en volume habitable, pièces non contiguës
|
|
17
|
+
[undefined, '3', 1, false], // production hors volume habitable
|
|
18
|
+
[undefined, '4', 0, false], // ligne collective : repli sur la position du générateur
|
|
19
|
+
[undefined, undefined, 1, true], // repli : générateur en volume chauffé
|
|
20
|
+
[undefined, undefined, 0, false], // repli : générateur hors volume chauffé
|
|
21
|
+
[undefined, undefined, undefined, true], // aucune information : récupération conservée
|
|
22
|
+
[null, null, 0, false] // champs nuls traités comme absents
|
|
23
|
+
])('stockage %s, rd %s, générateur %s => %s', (pvcs, rd, pvc, attendu) => {
|
|
24
|
+
expect(
|
|
25
|
+
isStockageEcsVolumeChauffe(
|
|
26
|
+
{ position_volume_chauffe_stockage: pvcs, position_volume_chauffe: pvc },
|
|
27
|
+
{ tv_rendement_distribution_ecs_id: rd }
|
|
28
|
+
)
|
|
29
|
+
).toBe(attendu);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('installation absente : repli sur la position du générateur', () => {
|
|
33
|
+
expect(isStockageEcsVolumeChauffe({ position_volume_chauffe: 0 }, undefined)).toBe(false);
|
|
34
|
+
});
|
|
35
|
+
});
|
package/4_ventilation.js
CHANGED
|
@@ -17,6 +17,18 @@ function tv_debits_ventilation(di, de, du) {
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Vrai si le DPE comporte au moins une menuiserie (baie ou porte) et qu'aucune n'exporte
|
|
22
|
+
* le champ `presence_joint` (issue #294).
|
|
23
|
+
*/
|
|
24
|
+
export function presenceJointNonRenseignee(porte_list, bv_list) {
|
|
25
|
+
const menuiseries = (porte_list || []).concat(bv_list || []);
|
|
26
|
+
return (
|
|
27
|
+
menuiseries.length > 0 &&
|
|
28
|
+
menuiseries.every((m) => [undefined, null, ''].includes(m.donnee_entree?.presence_joint))
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
20
32
|
function tv_q4pa_conv(di, de, cg, mur_list, ph_list, porte_list, bv_list) {
|
|
21
33
|
const surfaces = mur_list.concat(ph_list);
|
|
22
34
|
const surface_isolee = surfaces.reduce((acc, s) => {
|
|
@@ -82,14 +94,23 @@ function tv_q4pa_conv(di, de, cg, mur_list, ph_list, porte_list, bv_list) {
|
|
|
82
94
|
if (rowQ4paConv.isolation_surfaces !== isolation_surfaces) {
|
|
83
95
|
console.error(
|
|
84
96
|
`Calcul de hperm pour la ventilation ${de.description}. Le DPE considère isolation_surfaces = ${rowQ4paConv.isolation_surfaces}
|
|
85
|
-
alors que ce devrait être isolation_surfaces = ${isolation_surfaces}. La valeur
|
|
97
|
+
alors que ce devrait être isolation_surfaces = ${isolation_surfaces}. La valeur recalculée isolation_surfaces = ${isolation_surfaces} est utilisée.`
|
|
86
98
|
);
|
|
87
99
|
}
|
|
88
100
|
|
|
89
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Issue #294 : aucune menuiserie n'exporte `presence_joint`. Aucune donnée ne contredit alors
|
|
103
|
+
* la ligne q4pa_conv retenue par le logiciel (ex. ligne 12 « avant 1948 avec joints », 2,5) :
|
|
104
|
+
* on reprend sa valeur `presence_joints_menuiserie` au lieu de considérer les menuiseries
|
|
105
|
+
* sans joint.
|
|
106
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §4
|
|
107
|
+
*/
|
|
108
|
+
if (presenceJointNonRenseignee(porte_list, bv_list)) {
|
|
109
|
+
pjt = rowQ4paConv.presence_joints_menuiserie === '1' ? '1' : '0';
|
|
110
|
+
} else if (rowQ4paConv.presence_joints_menuiserie !== pjt) {
|
|
90
111
|
console.error(
|
|
91
112
|
`Calcul de hperm pour la ventilation ${de.description}. Le DPE considère presence_joints_menuiserie = ${rowQ4paConv.presence_joints_menuiserie}
|
|
92
|
-
alors que ce devrait être presence_joints_menuiserie = ${pjt}. La valeur
|
|
113
|
+
alors que ce devrait être presence_joints_menuiserie = ${pjt}. La valeur recalculée presence_joints_menuiserie = ${pjt} est utilisée.`
|
|
93
114
|
);
|
|
94
115
|
}
|
|
95
116
|
}
|
package/4_ventilation.spec.js
CHANGED
|
@@ -19,7 +19,11 @@ vi.mock('./5_conso_ventilation.js', () => ({
|
|
|
19
19
|
default: vi.fn()
|
|
20
20
|
}));
|
|
21
21
|
|
|
22
|
-
const {
|
|
22
|
+
const {
|
|
23
|
+
default: calc_ventilation,
|
|
24
|
+
calc_hperm,
|
|
25
|
+
presenceJointNonRenseignee
|
|
26
|
+
} = await import('./4_ventilation.js');
|
|
23
27
|
const { tv } = await import('./utils.js');
|
|
24
28
|
const { default: calc_pvent } = await import('./5_conso_ventilation.js');
|
|
25
29
|
|
|
@@ -404,4 +408,101 @@ describe('calc_ventilation - compatibilité bug_for_bug_compat activée', () =>
|
|
|
404
408
|
expect(errorSpy).not.toHaveBeenCalled();
|
|
405
409
|
errorSpy.mockRestore();
|
|
406
410
|
});
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Issue #294 : presence_joint absent sur toutes les menuiseries => la valeur
|
|
414
|
+
* presence_joints_menuiserie de la ligne q4pa_conv du DPE est reprise.
|
|
415
|
+
*/
|
|
416
|
+
describe('presence_joint non exporté (issue #294)', () => {
|
|
417
|
+
/** Avant 1948, surfaces non isolées : seul le critère des joints départage les lignes 1 et 12. */
|
|
418
|
+
const murNonIsole = [
|
|
419
|
+
{
|
|
420
|
+
donnee_intermediaire: { b: 1 },
|
|
421
|
+
donnee_entree: { enum_type_isolation_id: '2', surface_paroi_opaque: 100 }
|
|
422
|
+
}
|
|
423
|
+
];
|
|
424
|
+
const vtLigne12 = () => ({
|
|
425
|
+
donnee_entree: { enum_type_ventilation_id: '1', tv_q4pa_conv_id: 12, description: 'v' }
|
|
426
|
+
});
|
|
427
|
+
const ROW_12 = {
|
|
428
|
+
q4pa_conv: '2.5',
|
|
429
|
+
tv_q4pa_conv_id: '12',
|
|
430
|
+
isolation_surfaces: '0',
|
|
431
|
+
presence_joints_menuiserie: '1'
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
test('aucune menuiserie ne renseigne le champ : joints du DPE repris, sans message', async () => {
|
|
435
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
436
|
+
const { calc_ventilation_bug, tvBug } = await chargerAvecBug(ROW_12);
|
|
437
|
+
const baies = [{ donnee_entree: { surface_totale_baie: 10 } }];
|
|
438
|
+
const portes = [{ donnee_entree: { surface_porte: 2, presence_joint: null } }];
|
|
439
|
+
|
|
440
|
+
calc_ventilation_bug(vtLigne12(), CG, 'th', 80, 100, murNonIsole, [], portes, baies);
|
|
441
|
+
|
|
442
|
+
expect(tvBug).toHaveBeenLastCalledWith(
|
|
443
|
+
'q4pa_conv',
|
|
444
|
+
expect.objectContaining({ isolation_surfaces: '0', presence_joints_menuiserie: '1' })
|
|
445
|
+
);
|
|
446
|
+
expect(errorSpy).not.toHaveBeenCalled();
|
|
447
|
+
errorSpy.mockRestore();
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
test('ligne du DPE sans joints : la valeur "0" est conservée', async () => {
|
|
451
|
+
const { calc_ventilation_bug, tvBug } = await chargerAvecBug({
|
|
452
|
+
...ROW_12,
|
|
453
|
+
tv_q4pa_conv_id: '1',
|
|
454
|
+
presence_joints_menuiserie: undefined
|
|
455
|
+
});
|
|
456
|
+
const baies = [{ donnee_entree: { surface_totale_baie: 10 } }];
|
|
457
|
+
|
|
458
|
+
calc_ventilation_bug(vtLigne12(), CG, 'th', 80, 100, murNonIsole, [], [], baies);
|
|
459
|
+
|
|
460
|
+
expect(tvBug).toHaveBeenLastCalledWith(
|
|
461
|
+
'q4pa_conv',
|
|
462
|
+
expect.objectContaining({ presence_joints_menuiserie: '0' })
|
|
463
|
+
);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
test('presence_joint renseigné à 0 : la valeur recalculée prime et l’écart est signalé', async () => {
|
|
467
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
468
|
+
const { calc_ventilation_bug, tvBug } = await chargerAvecBug(ROW_12);
|
|
469
|
+
const baies = [{ donnee_entree: { surface_totale_baie: 10, presence_joint: 0 } }];
|
|
470
|
+
|
|
471
|
+
calc_ventilation_bug(vtLigne12(), CG, 'th', 80, 100, murNonIsole, [], [], baies);
|
|
472
|
+
|
|
473
|
+
expect(tvBug).toHaveBeenLastCalledWith(
|
|
474
|
+
'q4pa_conv',
|
|
475
|
+
expect.objectContaining({ presence_joints_menuiserie: '0' })
|
|
476
|
+
);
|
|
477
|
+
expect(errorSpy).toHaveBeenCalledWith(
|
|
478
|
+
expect.stringContaining('La valeur recalculée presence_joints_menuiserie = 0 est utilisée')
|
|
479
|
+
);
|
|
480
|
+
errorSpy.mockRestore();
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
describe('presenceJointNonRenseignee', () => {
|
|
486
|
+
test('aucune menuiserie : faux', () => {
|
|
487
|
+
expect(presenceJointNonRenseignee([], [])).toBe(false);
|
|
488
|
+
expect(presenceJointNonRenseignee(undefined, undefined)).toBe(false);
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
test('champ absent, null ou vide sur toutes les menuiseries : vrai', () => {
|
|
492
|
+
expect(
|
|
493
|
+
presenceJointNonRenseignee(
|
|
494
|
+
[{ donnee_entree: { presence_joint: '' } }],
|
|
495
|
+
[{ donnee_entree: {} }, { donnee_entree: { presence_joint: null } }]
|
|
496
|
+
)
|
|
497
|
+
).toBe(true);
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
test('au moins une menuiserie renseignée (même à 0) : faux', () => {
|
|
501
|
+
expect(
|
|
502
|
+
presenceJointNonRenseignee(
|
|
503
|
+
[{ donnee_entree: { presence_joint: 0 } }],
|
|
504
|
+
[{ donnee_entree: {} }]
|
|
505
|
+
)
|
|
506
|
+
).toBe(false);
|
|
507
|
+
});
|
|
407
508
|
});
|
package/9_besoin_ch.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import { calc_ai_j, calc_as_j } from './6.1_apport_gratuit.js';
|
|
10
10
|
import { calc_sse_j } from './6.2_surface_sud_equivalente.js';
|
|
11
11
|
import { bug_for_bug_compat, mois_liste } from './utils.js';
|
|
12
|
+
import { isStockageEcsVolumeChauffe } from './11_stockage_ecs_volume_chauffe.js';
|
|
12
13
|
|
|
13
14
|
export default function calc_besoin_ch(
|
|
14
15
|
ilpa,
|
|
@@ -67,10 +68,7 @@ export default function calc_besoin_ch(
|
|
|
67
68
|
} else {
|
|
68
69
|
Qgw = gen_ecs.reduce((acc, gen_ecs) => {
|
|
69
70
|
// Pas de récupération de stockage si le ballon est hors volume chauffé
|
|
70
|
-
if (
|
|
71
|
-
gen_ecs.donnee_entree.position_volume_chauffe_stockage === 0 ||
|
|
72
|
-
gen_ecs.donnee_entree.position_volume_chauffe === 0
|
|
73
|
-
) {
|
|
71
|
+
if (!isStockageEcsVolumeChauffe(gen_ecs.donnee_entree, instal_ecs.donnee_entree)) {
|
|
74
72
|
return acc;
|
|
75
73
|
}
|
|
76
74
|
|
package/9_besoin_ch.spec.js
CHANGED
|
@@ -313,7 +313,7 @@ describe('calc_besoin_ch - branches complémentaires', () => {
|
|
|
313
313
|
donnee_intermediaire: { Qgw: 100 }
|
|
314
314
|
},
|
|
315
315
|
{
|
|
316
|
-
// générateur hors volume chauffé =>
|
|
316
|
+
// générateur hors volume chauffé mais stockage en volume chauffé => compté (#293)
|
|
317
317
|
donnee_entree: { position_volume_chauffe_stockage: 1, position_volume_chauffe: 0 },
|
|
318
318
|
donnee_intermediaire: { Qgw: 100 }
|
|
319
319
|
},
|
|
@@ -660,3 +660,60 @@ describe('calc_besoin_ch - immeuble à installations ECS individuelles et collec
|
|
|
660
660
|
expect(ret.pertes_distribution_ecs_recup).toBeCloseTo((0.48 * 100 * total) / 8760, 9);
|
|
661
661
|
});
|
|
662
662
|
});
|
|
663
|
+
|
|
664
|
+
describe('calc_besoin_ch - stockage ECS en volume chauffé (#293)', () => {
|
|
665
|
+
/**
|
|
666
|
+
* Cas des exemples de l'issue (2301E2484046J, 2322E2765016S) : ballon électrique LICIEL
|
|
667
|
+
* exporté avec position_volume_chauffe = 0 mais production en volume habitable (rd 1).
|
|
668
|
+
*/
|
|
669
|
+
test('ballon en volume habitable exporté avec position_volume_chauffe = 0 : pertes récupérées', () => {
|
|
670
|
+
utilState.bug = false;
|
|
671
|
+
vi.mocked(calc_ai_j).mockReset().mockReturnValue(5000);
|
|
672
|
+
vi.mocked(calc_as_j).mockReset().mockReturnValue(2000);
|
|
673
|
+
vi.mocked(calc_sse_j).mockReset().mockReturnValue(10);
|
|
674
|
+
vi.mocked(calc_besoin_ecs_j).mockReset().mockReturnValue(10);
|
|
675
|
+
vi.mocked(calc_Qrec_gen_j).mockReset().mockReturnValue(0);
|
|
676
|
+
const instal = (rd) => [
|
|
677
|
+
{
|
|
678
|
+
donnee_entree: { enum_type_installation_id: '1', tv_rendement_distribution_ecs_id: rd },
|
|
679
|
+
generateur_ecs_collection: {
|
|
680
|
+
generateur_ecs: [
|
|
681
|
+
{ donnee_entree: { position_volume_chauffe: 0 }, donnee_intermediaire: { Qgw: 8760 } }
|
|
682
|
+
]
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
];
|
|
686
|
+
const enVolume = calc_besoin_ch(
|
|
687
|
+
0,
|
|
688
|
+
0,
|
|
689
|
+
0,
|
|
690
|
+
0,
|
|
691
|
+
100,
|
|
692
|
+
100,
|
|
693
|
+
3,
|
|
694
|
+
instal('1'),
|
|
695
|
+
[],
|
|
696
|
+
[],
|
|
697
|
+
null,
|
|
698
|
+
'maison',
|
|
699
|
+
1
|
|
700
|
+
);
|
|
701
|
+
const horsVolume = calc_besoin_ch(
|
|
702
|
+
0,
|
|
703
|
+
0,
|
|
704
|
+
0,
|
|
705
|
+
0,
|
|
706
|
+
100,
|
|
707
|
+
100,
|
|
708
|
+
3,
|
|
709
|
+
instal('3'),
|
|
710
|
+
[],
|
|
711
|
+
[],
|
|
712
|
+
null,
|
|
713
|
+
'maison',
|
|
714
|
+
1
|
|
715
|
+
);
|
|
716
|
+
expect(enVolume.pertes_stockage_ecs_recup).toBeGreaterThan(0);
|
|
717
|
+
expect(horsVolume.pertes_stockage_ecs_recup).toBe(0);
|
|
718
|
+
});
|
|
719
|
+
});
|
package/README.md
CHANGED
|
@@ -375,57 +375,57 @@ Ces résultats sont **générés automatiquement** à la fin de `npm run test:co
|
|
|
375
375
|
|
|
376
376
|
<!-- CORPUS:START -->
|
|
377
377
|
|
|
378
|
-
> **Version `1.8.
|
|
378
|
+
> **Version `1.8.64`** · branche `main` · généré le 2026-10-15
|
|
379
379
|
> Seuil de tolérance **5%**
|
|
380
380
|
|
|
381
381
|
<table>
|
|
382
382
|
<tr>
|
|
383
383
|
<td align="center"><strong>9</strong><br/><sub>corpus</sub></td>
|
|
384
384
|
<td align="center"><strong>89 418</strong><br/><sub>DPE analysés</sub></td>
|
|
385
|
-
<td align="center"><strong>71
|
|
386
|
-
<td align="center"><strong>79,
|
|
385
|
+
<td align="center"><strong>71 309</strong><br/><sub>DPE conformes</sub></td>
|
|
386
|
+
<td align="center"><strong>79,75 %</strong><br/><sub>réussite globale</sub></td>
|
|
387
387
|
</tr>
|
|
388
388
|
</table>
|
|
389
389
|
|
|
390
390
|
| | Corpus | Réussite | | DPE conformes |
|
|
391
391
|
| :-: | :---------------------------------------------------------------------------------------------------------------------- | ----------: | :--------------------- | -------------: |
|
|
392
|
-
| 🔴 | **Généraliste**<br/><sub>`corpus_dpe.csv`</sub> | **47,
|
|
393
|
-
| 🟢 | **Appartement · chauffage individuel (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_individuel_2025.csv`</sub> | **
|
|
394
|
-
| 🟢 | **Appartement · chauffage collectif (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_collectif_2025.csv`</sub> | **93,
|
|
395
|
-
| 🟢 | **Logement individuel (2025)**<br/><sub>`dpe_logement_individuel_2025.csv`</sub> | **91,
|
|
396
|
-
| 🟢 | **Maison individuelle (2025)**<br/><sub>`dpe_maison_individuelle_2025.csv`</sub> | **89,
|
|
397
|
-
| 🟢 | **Immeuble · chauffage collectif**<br/><sub>`dpe_immeuble_chauffage_collectif.csv`</sub> | **88,
|
|
398
|
-
| 🟢 | **Immeuble · chauffage individuel**<br/><sub>`dpe_immeuble_chauffage_individuel.csv`</sub> | **85,
|
|
399
|
-
| 🟡 | **Immeuble · chauffage mixte**<br/><sub>`dpe_immeuble_chauffage_mixte.csv`</sub> | **69,
|
|
400
|
-
| 🔴 | **Individuel généré depuis l'immeuble (2026)**<br/><sub>`dpe_individuel_a_partir_dpe_immeuble_2026.csv`</sub> | **56,
|
|
392
|
+
| 🔴 | **Généraliste**<br/><sub>`corpus_dpe.csv`</sub> | **47,91 %** | `██████████░░░░░░░░░░` | 4 791 / 9 999 |
|
|
393
|
+
| 🟢 | **Appartement · chauffage individuel (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_individuel_2025.csv`</sub> | **94,37 %** | `███████████████████░` | 9 437 / 10 000 |
|
|
394
|
+
| 🟢 | **Appartement · chauffage collectif (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_collectif_2025.csv`</sub> | **93,84 %** | `███████████████████░` | 9 384 / 10 000 |
|
|
395
|
+
| 🟢 | **Logement individuel (2025)**<br/><sub>`dpe_logement_individuel_2025.csv`</sub> | **91,80 %** | `██████████████████░░` | 9 173 / 9 992 |
|
|
396
|
+
| 🟢 | **Maison individuelle (2025)**<br/><sub>`dpe_maison_individuelle_2025.csv`</sub> | **89,93 %** | `██████████████████░░` | 8 992 / 9 999 |
|
|
397
|
+
| 🟢 | **Immeuble · chauffage collectif**<br/><sub>`dpe_immeuble_chauffage_collectif.csv`</sub> | **88,39 %** | `██████████████████░░` | 8 675 / 9 814 |
|
|
398
|
+
| 🟢 | **Immeuble · chauffage individuel**<br/><sub>`dpe_immeuble_chauffage_individuel.csv`</sub> | **85,35 %** | `█████████████████░░░` | 8 533 / 9 998 |
|
|
399
|
+
| 🟡 | **Immeuble · chauffage mixte**<br/><sub>`dpe_immeuble_chauffage_mixte.csv`</sub> | **69,57 %** | `██████████████░░░░░░` | 6 690 / 9 616 |
|
|
400
|
+
| 🔴 | **Individuel généré depuis l'immeuble (2026)**<br/><sub>`dpe_individuel_a_partir_dpe_immeuble_2026.csv`</sub> | **56,34 %** | `███████████░░░░░░░░░` | 5 634 / 10 000 |
|
|
401
401
|
|
|
402
402
|
<sub>🟢 ≥ 85 % · 🟡 ≥ 60 % · 🔴 < 60 %</sub>
|
|
403
403
|
|
|
404
404
|
#### Temps d’exécution
|
|
405
405
|
|
|
406
|
-
> Durée de l’appel à `calcul_3cl` par DPE, sur 89
|
|
406
|
+
> Durée de l’appel à `calcul_3cl` par DPE, sur 89 383 calculs.
|
|
407
407
|
> La copie défensive de l’entrée et la lecture du fichier sont exclues de la mesure.
|
|
408
408
|
|
|
409
409
|
<table>
|
|
410
410
|
<tr>
|
|
411
|
-
<td align="center"><strong>
|
|
412
|
-
<td align="center"><strong>5,
|
|
413
|
-
<td align="center"><strong>0,
|
|
414
|
-
<td align="center"><strong>
|
|
411
|
+
<td align="center"><strong>6,03 ms</strong><br/><sub>moyenne</sub></td>
|
|
412
|
+
<td align="center"><strong>5,41 ms</strong><br/><sub>médiane</sub></td>
|
|
413
|
+
<td align="center"><strong>0,19 ms</strong><br/><sub>min</sub></td>
|
|
414
|
+
<td align="center"><strong>763,7 ms</strong><br/><sub>max</sub></td>
|
|
415
415
|
</tr>
|
|
416
416
|
</table>
|
|
417
417
|
|
|
418
418
|
| Corpus | Moyenne | Médiane | Min | Max | p95 | p99 |
|
|
419
419
|
| :--------------------------------------------- | ------: | ------: | ------: | -------: | ------: | ------: |
|
|
420
|
-
| **Généraliste** |
|
|
421
|
-
| **Appartement · chauffage individuel (2025)** |
|
|
422
|
-
| **Appartement · chauffage collectif (2025)** |
|
|
423
|
-
| **Logement individuel (2025)** |
|
|
424
|
-
| **Maison individuelle (2025)** |
|
|
425
|
-
| **Immeuble · chauffage collectif** |
|
|
426
|
-
| **Immeuble · chauffage individuel** |
|
|
427
|
-
| **Immeuble · chauffage mixte** | 5,
|
|
428
|
-
| **Individuel généré depuis l'immeuble (2026)** | 6,
|
|
420
|
+
| **Généraliste** | 5,69 ms | 5,26 ms | 0,31 ms | 43,3 ms | 9,94 ms | 14,5 ms |
|
|
421
|
+
| **Appartement · chauffage individuel (2025)** | 4,38 ms | 4,08 ms | 1,56 ms | 34,5 ms | 7,30 ms | 9,34 ms |
|
|
422
|
+
| **Appartement · chauffage collectif (2025)** | 5,28 ms | 5,15 ms | 1,71 ms | 45,6 ms | 8,16 ms | 10,4 ms |
|
|
423
|
+
| **Logement individuel (2025)** | 5,57 ms | 5,06 ms | 1,69 ms | 39,2 ms | 10,1 ms | 13,6 ms |
|
|
424
|
+
| **Maison individuelle (2025)** | 6,58 ms | 5,99 ms | 1,96 ms | 45,7 ms | 11,6 ms | 15,3 ms |
|
|
425
|
+
| **Immeuble · chauffage collectif** | 6,51 ms | 5,85 ms | 0,23 ms | 163,5 ms | 11,7 ms | 19,1 ms |
|
|
426
|
+
| **Immeuble · chauffage individuel** | 7,69 ms | 6,64 ms | 0,19 ms | 121,8 ms | 15,4 ms | 24,7 ms |
|
|
427
|
+
| **Immeuble · chauffage mixte** | 5,92 ms | 4,86 ms | 0,19 ms | 763,7 ms | 12,1 ms | 20,2 ms |
|
|
428
|
+
| **Individuel généré depuis l'immeuble (2026)** | 6,65 ms | 5,78 ms | 1,75 ms | 60,9 ms | 12,6 ms | 20,5 ms |
|
|
429
429
|
|
|
430
430
|
<sub>La moyenne est tirée vers le haut par les DPE collectifs, dont le coût atteint plusieurs
|
|
431
431
|
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
|
@@ -35,7 +35,7 @@ import DpeSanitizerService from './dpe-sanitizer.service.js';
|
|
|
35
35
|
import { reconstituerRdimDpeGenereImmeuble } from './17.2_rdim_dpe_genere_immeuble.js';
|
|
36
36
|
import { reconstituerRdimEcsIndividuelleUnique } from './11_rdim_ecs_individuelle_unique.js';
|
|
37
37
|
|
|
38
|
-
const LIB_VERSION = '1.8.
|
|
38
|
+
const LIB_VERSION = '1.8.65';
|
|
39
39
|
|
|
40
40
|
const dpeSanitizerService = new DpeSanitizerService();
|
|
41
41
|
|
|
@@ -340,6 +340,16 @@ export class TvStore {
|
|
|
340
340
|
return debitsVentilation;
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
+
/**
|
|
344
|
+
* Ligne de la table q4pa_conv par son identifiant
|
|
345
|
+
* @param tvQ4paConvId {number|string|undefined}
|
|
346
|
+
* @return {object|undefined}
|
|
347
|
+
*/
|
|
348
|
+
getQ4paConvById(tvQ4paConvId) {
|
|
349
|
+
if (tvQ4paConvId === undefined || tvQ4paConvId === null) return;
|
|
350
|
+
return tv['q4pa_conv'].find((v) => v.tv_q4pa_conv_id === String(tvQ4paConvId));
|
|
351
|
+
}
|
|
352
|
+
|
|
343
353
|
/**
|
|
344
354
|
* Valeur conventionnelle de la perméabilité sous 4Pa
|
|
345
355
|
* @see Chapitre 4 - Calcul des déperditions par renouvellement d’air
|
|
@@ -607,6 +607,17 @@ describe('Lecture des tables de valeurs', () => {
|
|
|
607
607
|
const ug = tvStore.getQ4paConv('2', TypeHabitation.MAISON, '0', '1');
|
|
608
608
|
expect(ug).toBeUndefined();
|
|
609
609
|
});
|
|
610
|
+
|
|
611
|
+
test('lecture par identifiant (issue #294)', () => {
|
|
612
|
+
expect(tvStore.getQ4paConvById(12)).toMatchObject({
|
|
613
|
+
q4pa_conv: '2.5',
|
|
614
|
+
presence_joints_menuiserie: '1'
|
|
615
|
+
});
|
|
616
|
+
expect(tvStore.getQ4paConvById('1')).toMatchObject({ q4pa_conv: '4.6' });
|
|
617
|
+
expect(tvStore.getQ4paConvById(undefined)).toBeUndefined();
|
|
618
|
+
expect(tvStore.getQ4paConvById(null)).toBeUndefined();
|
|
619
|
+
expect(tvStore.getQ4paConvById(999)).toBeUndefined();
|
|
620
|
+
});
|
|
610
621
|
});
|
|
611
622
|
|
|
612
623
|
test('lecture des valeurs de tempBase', () => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { GenerateurEcsService } from './generateur-ecs.service.js';
|
|
2
2
|
import { inject } from 'dioma';
|
|
3
|
+
import { isStockageEcsVolumeChauffe } from '../../../../11_stockage_ecs_volume_chauffe.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Calcul des besoins et pertes des installations ECS
|
|
@@ -142,11 +143,8 @@ export class InstallationEcsService {
|
|
|
142
143
|
if (installationIndividuelle) {
|
|
143
144
|
installationEcs.donnee_utilisateur.QgwRecuperable =
|
|
144
145
|
installationEcs.generateur_ecs_collection?.generateur_ecs
|
|
145
|
-
.filter(
|
|
146
|
-
(generateurEcs)
|
|
147
|
-
parseInt(generateurEcs.donnee_entree.position_volume_chauffe_stockage) === 1 ||
|
|
148
|
-
(generateurEcs.donnee_entree.position_volume_chauffe_stockage === undefined &&
|
|
149
|
-
parseInt(generateurEcs.donnee_entree.position_volume_chauffe) === 1)
|
|
146
|
+
.filter((generateurEcs) =>
|
|
147
|
+
isStockageEcsVolumeChauffe(generateurEcs.donnee_entree, installationEcsDE)
|
|
150
148
|
)
|
|
151
149
|
.reduce((acc, generateurEcs) => {
|
|
152
150
|
return acc + generateurEcs.donnee_utilisateur.Qgw;
|
|
@@ -305,7 +305,8 @@ export class DeperditionEnveloppeService {
|
|
|
305
305
|
this.#surfaceIsolee,
|
|
306
306
|
this.#surfaceNonIsolee,
|
|
307
307
|
this.#surfaceMenuiserieAvecJoint,
|
|
308
|
-
this.#surfaceMenuiserieSansJoint
|
|
308
|
+
this.#surfaceMenuiserieSansJoint,
|
|
309
|
+
this.#presenceJointNonRenseignee(logement.enveloppe)
|
|
309
310
|
);
|
|
310
311
|
deperditions.hvent += ventilation.donnee_intermediaire.hvent;
|
|
311
312
|
deperditions.hperm += ventilation.donnee_intermediaire.hperm;
|
|
@@ -313,4 +314,22 @@ export class DeperditionEnveloppeService {
|
|
|
313
314
|
|
|
314
315
|
return deperditions;
|
|
315
316
|
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Vrai si le logement comporte au moins une menuiserie (baie ou porte) et qu'aucune n'exporte
|
|
320
|
+
* le champ presence_joint (issue #294).
|
|
321
|
+
*
|
|
322
|
+
* @param enveloppe {Enveloppe}
|
|
323
|
+
* @return {boolean}
|
|
324
|
+
*/
|
|
325
|
+
#presenceJointNonRenseignee(enveloppe) {
|
|
326
|
+
const menuiseries = [
|
|
327
|
+
...(enveloppe?.porte_collection?.porte || []),
|
|
328
|
+
...(enveloppe?.baie_vitree_collection?.baie_vitree || [])
|
|
329
|
+
];
|
|
330
|
+
return (
|
|
331
|
+
menuiseries.length > 0 &&
|
|
332
|
+
menuiseries.every((m) => [undefined, null, ''].includes(m.donnee_entree?.presence_joint))
|
|
333
|
+
);
|
|
334
|
+
}
|
|
316
335
|
}
|
|
@@ -173,7 +173,17 @@ describe('DeperditionEnveloppeService (§3 déperditions de l’enveloppe)', ()
|
|
|
173
173
|
// déperditive = mur 10 + porte 2 + plancher haut 30 + baie 4 = 46
|
|
174
174
|
// isolée = mur 5 + plancher haut 7 = 12 ; non isolée = mur 10 + plancher haut 30 = 40
|
|
175
175
|
// menuiseries avec joint = porte 2 + baie 4 = 6 ; sans joint = porte 3 + baie 6 = 9
|
|
176
|
-
|
|
176
|
+
// presence_joint renseigné sur au moins une menuiserie => indicateur #294 à false
|
|
177
|
+
expect(ventilationService.execute).toHaveBeenCalledWith(
|
|
178
|
+
ctx,
|
|
179
|
+
{ id: 'v' },
|
|
180
|
+
46,
|
|
181
|
+
12,
|
|
182
|
+
40,
|
|
183
|
+
6,
|
|
184
|
+
9,
|
|
185
|
+
false
|
|
186
|
+
);
|
|
177
187
|
});
|
|
178
188
|
|
|
179
189
|
test('les surfaces sont réinitialisées à chaque calcul', () => {
|
|
@@ -186,7 +196,18 @@ describe('DeperditionEnveloppeService (§3 déperditions de l’enveloppe)', ()
|
|
|
186
196
|
service.deperditions(ctx, logement);
|
|
187
197
|
service.deperditions(ctx, logement);
|
|
188
198
|
|
|
189
|
-
|
|
199
|
+
// aucune menuiserie ne renseigne presence_joint => indicateur #294 à true
|
|
200
|
+
expect(ventilationService.execute).toHaveBeenLastCalledWith(ctx, {}, 2, 0, 0, 0, 2, true);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test('logement sans menuiserie : indicateur presence_joint non renseigné à false (issue #294)', () => {
|
|
204
|
+
const logement = logementVide();
|
|
205
|
+
logement.ventilation_collection.ventilation = [{ donnee_entree: {} }];
|
|
206
|
+
ventilationService.execute.mockReturnValue({ hvent: 0, hperm: 0 });
|
|
207
|
+
|
|
208
|
+
service.deperditions(ctx, logement);
|
|
209
|
+
|
|
210
|
+
expect(ventilationService.execute).toHaveBeenLastCalledWith(ctx, {}, 0, 0, 0, 0, 0, false);
|
|
190
211
|
});
|
|
191
212
|
|
|
192
213
|
describe('espaces tampons solarisés', () => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DeperditionService } from '../deperdition.service.js';
|
|
2
2
|
|
|
3
3
|
import { logger } from '../../../../../core/util/logger/log-service.js';
|
|
4
|
+
import { bug_for_bug_compat } from '../../../../../utils.js';
|
|
4
5
|
import { TypeHabitation } from '../../../../dpe/domain/models/type-habitation.model.js';
|
|
5
6
|
import { TypeVentilation } from '../../../../dpe/domain/models/type-ventilation.model.js';
|
|
6
7
|
|
|
@@ -22,6 +23,7 @@ export class DeperditionVentilationService extends DeperditionService {
|
|
|
22
23
|
* @param surfaceNonIsolee {number}
|
|
23
24
|
* @param surfaceMenuiserieAvecJoint {number}
|
|
24
25
|
* @param surfaceMenuiserieSansJoint {number}
|
|
26
|
+
* @param presenceJointNonRenseignee {boolean} aucune menuiserie n'exporte presence_joint (issue #294)
|
|
25
27
|
* @return {VentilationDI}
|
|
26
28
|
*/
|
|
27
29
|
execute(
|
|
@@ -31,7 +33,8 @@ export class DeperditionVentilationService extends DeperditionService {
|
|
|
31
33
|
surfaceIsolee,
|
|
32
34
|
surfaceNonIsolee,
|
|
33
35
|
surfaceMenuiserieAvecJoint,
|
|
34
|
-
surfaceMenuiserieSansJoint
|
|
36
|
+
surfaceMenuiserieSansJoint,
|
|
37
|
+
presenceJointNonRenseignee = false
|
|
35
38
|
) {
|
|
36
39
|
const q4paConv = this.q4paConv(
|
|
37
40
|
ventilationDE,
|
|
@@ -39,7 +42,8 @@ export class DeperditionVentilationService extends DeperditionService {
|
|
|
39
42
|
surfaceIsolee,
|
|
40
43
|
surfaceNonIsolee,
|
|
41
44
|
surfaceMenuiserieAvecJoint,
|
|
42
|
-
surfaceMenuiserieSansJoint
|
|
45
|
+
surfaceMenuiserieSansJoint,
|
|
46
|
+
presenceJointNonRenseignee
|
|
43
47
|
);
|
|
44
48
|
const debitsVentilation = this.#debitsVentilation(ventilationDE.enum_type_ventilation_id);
|
|
45
49
|
|
|
@@ -62,6 +66,7 @@ export class DeperditionVentilationService extends DeperditionService {
|
|
|
62
66
|
* @param surfaceNonIsolee {number}
|
|
63
67
|
* @param surfaceMenuiserieAvecJoint {number}
|
|
64
68
|
* @param surfaceMenuiserieSansJoint {number}
|
|
69
|
+
* @param presenceJointNonRenseignee {boolean}
|
|
65
70
|
* @return {number}
|
|
66
71
|
*/
|
|
67
72
|
q4paConv(
|
|
@@ -70,7 +75,8 @@ export class DeperditionVentilationService extends DeperditionService {
|
|
|
70
75
|
surfaceIsolee,
|
|
71
76
|
surfaceNonIsolee,
|
|
72
77
|
surfaceMenuiserieAvecJoint,
|
|
73
|
-
surfaceMenuiserieSansJoint
|
|
78
|
+
surfaceMenuiserieSansJoint,
|
|
79
|
+
presenceJointNonRenseignee = false
|
|
74
80
|
) {
|
|
75
81
|
// Pour les bâtiments qui ont fait l’objet d’une mesure d’étanchéité à l’air moins de deux ans avant le diagnostic, la valeur
|
|
76
82
|
// mesurée de Q4Paconv/m² peut être saisie.
|
|
@@ -92,6 +98,15 @@ export class DeperditionVentilationService extends DeperditionService {
|
|
|
92
98
|
// (si les menuiseries représentant plus de 50% de la surface totale possèdent des joints) => valeur forfaitaire.
|
|
93
99
|
if (surfaceMenuiserieAvecJoint > surfaceMenuiserieSansJoint) {
|
|
94
100
|
presenceJointsMenuiserie = '1';
|
|
101
|
+
} else if (
|
|
102
|
+
bug_for_bug_compat &&
|
|
103
|
+
presenceJointNonRenseignee &&
|
|
104
|
+
this.tvStore.getQ4paConvById(ventilationDE.tv_q4pa_conv_id)
|
|
105
|
+
?.presence_joints_menuiserie === '1'
|
|
106
|
+
) {
|
|
107
|
+
// Issue #294 : aucune menuiserie n'exporte presence_joint, rien ne contredit la ligne
|
|
108
|
+
// q4pa_conv « avant 1948 avec joints » retenue par le DPE => on la reprend.
|
|
109
|
+
presenceJointsMenuiserie = '1';
|
|
95
110
|
}
|
|
96
111
|
}
|
|
97
112
|
} else if (enumPeriodeConstructionId === '2') {
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
/** `bug_for_bug_compat` (utils.js) piloté par le test. */
|
|
4
|
+
const state = vi.hoisted(() => ({ bug: false }));
|
|
5
|
+
vi.mock('../../../../../utils.js', async (importOriginal) => ({
|
|
6
|
+
...(await importOriginal()),
|
|
7
|
+
get bug_for_bug_compat() {
|
|
8
|
+
return state.bug;
|
|
9
|
+
}
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
const { DeperditionVentilationService } = await import('./deperdition-ventilation.service.js');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Tests unitaires isolés du calcul de Q4Paconv (`DeperditionVentilationService.q4paConv`).
|
|
16
|
+
* Le store des tables de valeurs est remplacé par un double de test.
|
|
17
|
+
*
|
|
18
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §4
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/** @type {DeperditionVentilationService} */
|
|
22
|
+
let service;
|
|
23
|
+
let tvStore;
|
|
24
|
+
|
|
25
|
+
const ctx = { enumPeriodeConstructionId: '1', typeHabitation: 'APPARTEMENT' };
|
|
26
|
+
|
|
27
|
+
describe('DeperditionVentilationService.q4paConv', () => {
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
tvStore = {
|
|
30
|
+
getQ4paConv: vi.fn().mockReturnValue({ q4pa_conv: '4.6' }),
|
|
31
|
+
getQ4paConvById: vi.fn()
|
|
32
|
+
};
|
|
33
|
+
service = new DeperditionVentilationService(tvStore);
|
|
34
|
+
state.bug = false;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('Q4Pa mesuré saisi : utilisé directement', () => {
|
|
38
|
+
expect(service.q4paConv({ q4pa_conv_saisi: 1.7 }, ctx, 0, 100, 0, 10, true)).toBe(1.7);
|
|
39
|
+
expect(tvStore.getQ4paConv).not.toHaveBeenCalled();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('avant 1948, menuiseries majoritairement avec joints : ligne « avec joints »', () => {
|
|
43
|
+
service.q4paConv({}, ctx, 0, 100, 8, 2, false);
|
|
44
|
+
expect(tvStore.getQ4paConv).toHaveBeenCalledWith('1', 'APPARTEMENT', '0', '1');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('avant 1948, menuiseries sans joint : ligne sans critère de joints', () => {
|
|
48
|
+
service.q4paConv({ tv_q4pa_conv_id: 12 }, ctx, 0, 100, 0, 10, false);
|
|
49
|
+
expect(tvStore.getQ4paConv).toHaveBeenCalledWith('1', 'APPARTEMENT', '0', undefined);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('presence_joint non exporté (issue #294)', () => {
|
|
53
|
+
test('hors bug_for_bug_compat : la ligne du DPE est ignorée', () => {
|
|
54
|
+
state.bug = false;
|
|
55
|
+
tvStore.getQ4paConvById.mockReturnValue({ presence_joints_menuiserie: '1' });
|
|
56
|
+
|
|
57
|
+
service.q4paConv({ tv_q4pa_conv_id: 12 }, ctx, 0, 100, 0, 10, true);
|
|
58
|
+
|
|
59
|
+
expect(tvStore.getQ4paConv).toHaveBeenCalledWith('1', 'APPARTEMENT', '0', undefined);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('bug_for_bug_compat et ligne DPE « avec joints » : joints repris', () => {
|
|
63
|
+
state.bug = true;
|
|
64
|
+
tvStore.getQ4paConvById.mockReturnValue({ presence_joints_menuiserie: '1' });
|
|
65
|
+
|
|
66
|
+
service.q4paConv({ tv_q4pa_conv_id: 12 }, ctx, 0, 100, 0, 10, true);
|
|
67
|
+
|
|
68
|
+
expect(tvStore.getQ4paConvById).toHaveBeenCalledWith(12);
|
|
69
|
+
expect(tvStore.getQ4paConv).toHaveBeenCalledWith('1', 'APPARTEMENT', '0', '1');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('bug_for_bug_compat mais presence_joint renseigné : pas de reprise', () => {
|
|
73
|
+
state.bug = true;
|
|
74
|
+
tvStore.getQ4paConvById.mockReturnValue({ presence_joints_menuiserie: '1' });
|
|
75
|
+
|
|
76
|
+
service.q4paConv({ tv_q4pa_conv_id: 12 }, ctx, 0, 100, 0, 10, false);
|
|
77
|
+
|
|
78
|
+
expect(tvStore.getQ4paConv).toHaveBeenCalledWith('1', 'APPARTEMENT', '0', undefined);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('bug_for_bug_compat et ligne DPE sans joints : pas de reprise', () => {
|
|
82
|
+
state.bug = true;
|
|
83
|
+
tvStore.getQ4paConvById.mockReturnValue({ presence_joints_menuiserie: '' });
|
|
84
|
+
|
|
85
|
+
service.q4paConv({ tv_q4pa_conv_id: 1 }, ctx, 0, 100, 0, 10, true);
|
|
86
|
+
|
|
87
|
+
expect(tvStore.getQ4paConv).toHaveBeenCalledWith('1', 'APPARTEMENT', '0', undefined);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('surfaces majoritairement isolées : le critère des joints ne s’applique pas', () => {
|
|
91
|
+
state.bug = true;
|
|
92
|
+
tvStore.getQ4paConvById.mockReturnValue({ presence_joints_menuiserie: '1' });
|
|
93
|
+
|
|
94
|
+
service.q4paConv({ tv_q4pa_conv_id: 12 }, ctx, 100, 0, 0, 10, true);
|
|
95
|
+
|
|
96
|
+
expect(tvStore.getQ4paConvById).not.toHaveBeenCalled();
|
|
97
|
+
expect(tvStore.getQ4paConv).toHaveBeenCalledWith('1', 'APPARTEMENT', '1', undefined);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|