@open3cl/engine 1.9.0 → 1.10.0
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/4_ventilation.js +38 -4
- package/4_ventilation.spec.js +172 -15
- package/README.md +34 -27
- package/engine.js +10 -3
- package/engine.spec.js +48 -0
- package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.js +4 -2
- package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.spec.js +25 -5
- package/package.json +1 -1
- package/utils.js +23 -0
- package/utils.spec.js +15 -0
package/4_ventilation.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
tv,
|
|
3
|
+
requestInputID,
|
|
4
|
+
requestInput,
|
|
5
|
+
bug_for_bug_compat,
|
|
6
|
+
compat_for_corpus_analysis
|
|
7
|
+
} from './utils.js';
|
|
2
8
|
import calc_pvent from './5_conso_ventilation.js';
|
|
3
9
|
|
|
4
10
|
function tv_debits_ventilation(di, de, du) {
|
|
@@ -38,6 +44,16 @@ export function isLigneQ4paMemePeriode(rowQ4paConv, cg) {
|
|
|
38
44
|
.includes(String(cg.enum_periode_construction_id));
|
|
39
45
|
}
|
|
40
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Vrai si la ligne de la table `q4pa_conv` considère les surfaces isolées (`isolation_surfaces` = 1)
|
|
49
|
+
* alors que le recalcul à partir des parois les donne non isolées (`isolation_surfaces` = 0).
|
|
50
|
+
*/
|
|
51
|
+
export function isLigneQ4paSurfacesIsolees(rowQ4paConv, isolationSurfacesRecalculee) {
|
|
52
|
+
return (
|
|
53
|
+
String(rowQ4paConv.isolation_surfaces) === '1' && String(isolationSurfacesRecalculee) === '0'
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
41
57
|
function tv_q4pa_conv(di, de, cg, mur_list, ph_list, porte_list, bv_list) {
|
|
42
58
|
const surfaces = mur_list.concat(ph_list);
|
|
43
59
|
const surface_isolee = surfaces.reduce((acc, s) => {
|
|
@@ -108,10 +124,24 @@ function tv_q4pa_conv(di, de, cg, mur_list, ph_list, porte_list, bv_list) {
|
|
|
108
124
|
* 1948, surfaces isolées », 2, alors que toutes les parois sont non isolées → ligne 1, 4,6).
|
|
109
125
|
* Si la ligne saisie correspond à la période de construction du DPE, on la reprend en entier
|
|
110
126
|
* (isolation et joints) pour reproduire le calcul du logiciel.
|
|
127
|
+
*
|
|
128
|
+
* Cette reprise n'est faite qu'avec l'option `compat_for_corpus_analysis` (analyse du corpus
|
|
129
|
+
* ADEME). Par défaut, la ligne contradictoire est seulement signalée et q4pa_conv est
|
|
130
|
+
* recalculé selon la méthode : un contrôle de cohérence du DPE doit détecter l'erreur et un
|
|
131
|
+
* scénario de travaux ne doit pas la propager.
|
|
132
|
+
*
|
|
133
|
+
* Sens unique : la reprise ne s'applique que si la ligne indique des surfaces isolées alors que
|
|
134
|
+
* le recalcul les donne non isolées. Dans le sens inverse (ligne « non isolées », recalcul
|
|
135
|
+
* « isolées »), le DPE a pu être modifié par l'appelant (scénario d'isolation des parois) :
|
|
136
|
+
* la valeur recalculée est utilisée.
|
|
111
137
|
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §4
|
|
112
138
|
*/
|
|
113
139
|
if (rowQ4paConv.isolation_surfaces !== isolation_surfaces) {
|
|
114
|
-
if (
|
|
140
|
+
if (
|
|
141
|
+
compat_for_corpus_analysis &&
|
|
142
|
+
isLigneQ4paSurfacesIsolees(rowQ4paConv, isolation_surfaces) &&
|
|
143
|
+
isLigneQ4paMemePeriode(rowQ4paConv, cg)
|
|
144
|
+
) {
|
|
115
145
|
console.error(
|
|
116
146
|
`Calcul de hperm pour la ventilation ${de.description}. Le DPE considère isolation_surfaces = ${rowQ4paConv.isolation_surfaces} \n alors que ce devrait être isolation_surfaces = ${isolation_surfaces}. La ligne q4pa_conv ${de.tv_q4pa_conv_id} du DPE est conservée.`
|
|
117
147
|
);
|
|
@@ -129,10 +159,14 @@ function tv_q4pa_conv(di, de, cg, mur_list, ph_list, porte_list, bv_list) {
|
|
|
129
159
|
* Issue #294 : aucune menuiserie n'exporte `presence_joint`. Aucune donnée ne contredit alors
|
|
130
160
|
* la ligne q4pa_conv retenue par le logiciel (ex. ligne 12 « avant 1948 avec joints », 2,5) :
|
|
131
161
|
* on reprend sa valeur `presence_joints_menuiserie` au lieu de considérer les menuiseries
|
|
132
|
-
* sans joint.
|
|
162
|
+
* sans joint. Uniquement avec l'option `compat_for_corpus_analysis`.
|
|
133
163
|
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §4
|
|
134
164
|
*/
|
|
135
|
-
if (
|
|
165
|
+
if (
|
|
166
|
+
compat_for_corpus_analysis &&
|
|
167
|
+
!ligneDpeReprise &&
|
|
168
|
+
presenceJointNonRenseignee(porte_list, bv_list)
|
|
169
|
+
) {
|
|
136
170
|
pjt = rowQ4paConv.presence_joints_menuiserie === '1' ? '1' : '0';
|
|
137
171
|
} else if (!ligneDpeReprise && rowQ4paConv.presence_joints_menuiserie !== pjt) {
|
|
138
172
|
console.error(
|
package/4_ventilation.spec.js
CHANGED
|
@@ -4,7 +4,8 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
|
|
4
4
|
* Dépendances mockées pour isoler le module de ventilation :
|
|
5
5
|
* - `tv` : accès aux tables `debits_ventilation` / `q4pa_conv`, on contrôle les lignes ;
|
|
6
6
|
* - `requestInput` / `requestInputID` : passe-plats vers les données d'entrée ;
|
|
7
|
-
* - `bug_for_bug_compat` :
|
|
7
|
+
* - `bug_for_bug_compat` / `compat_for_corpus_analysis` : désactivés pour isoler le comportement
|
|
8
|
+
* nominal ;
|
|
8
9
|
* - `calc_pvent` (5_conso_ventilation.js) : espion, la conso de ventilation est testée ailleurs.
|
|
9
10
|
*/
|
|
10
11
|
vi.mock('./utils.js', () => ({
|
|
@@ -12,7 +13,8 @@ vi.mock('./utils.js', () => ({
|
|
|
12
13
|
tv: vi.fn(),
|
|
13
14
|
requestInput: (de, du, field) => de[field],
|
|
14
15
|
requestInputID: (de, du, field) => de[`enum_${field}_id`],
|
|
15
|
-
bug_for_bug_compat: false
|
|
16
|
+
bug_for_bug_compat: false,
|
|
17
|
+
compat_for_corpus_analysis: false
|
|
16
18
|
}));
|
|
17
19
|
|
|
18
20
|
vi.mock('./5_conso_ventilation.js', () => ({
|
|
@@ -23,7 +25,8 @@ const {
|
|
|
23
25
|
default: calc_ventilation,
|
|
24
26
|
calc_hperm,
|
|
25
27
|
presenceJointNonRenseignee,
|
|
26
|
-
isLigneQ4paMemePeriode
|
|
28
|
+
isLigneQ4paMemePeriode,
|
|
29
|
+
isLigneQ4paSurfacesIsolees
|
|
27
30
|
} = await import('./4_ventilation.js');
|
|
28
31
|
const { tv } = await import('./utils.js');
|
|
29
32
|
const { default: calc_pvent } = await import('./5_conso_ventilation.js');
|
|
@@ -300,10 +303,12 @@ describe('calc_ventilation - orchestration', () => {
|
|
|
300
303
|
*/
|
|
301
304
|
describe('calc_ventilation - compatibilité bug_for_bug_compat activée', () => {
|
|
302
305
|
/**
|
|
303
|
-
* Recharge le module avec `bug_for_bug_compat = true
|
|
304
|
-
*
|
|
306
|
+
* Recharge le module avec `bug_for_bug_compat = true` et `compat_for_corpus_analysis` (vrai par
|
|
307
|
+
* défaut : reprise de la ligne du DPE pour l'analyse de corpus). Le double de `tv` renvoie une
|
|
308
|
+
* ligne `q4pa_conv` distincte selon que le matcher cible l'identifiant déjà saisi ou le matcher
|
|
309
|
+
* complet.
|
|
305
310
|
*/
|
|
306
|
-
async function chargerAvecBug(rowParId) {
|
|
311
|
+
async function chargerAvecBug(rowParId, compat = true) {
|
|
307
312
|
vi.resetModules();
|
|
308
313
|
const tvBug = vi.fn((table, matcher) => {
|
|
309
314
|
if (table === 'debits_ventilation') return { ...ROW_DEBITS };
|
|
@@ -317,7 +322,8 @@ describe('calc_ventilation - compatibilité bug_for_bug_compat activée', () =>
|
|
|
317
322
|
tv: tvBug,
|
|
318
323
|
requestInput: (de, du, field) => de[field],
|
|
319
324
|
requestInputID: (de, du, field) => de[`enum_${field}_id`],
|
|
320
|
-
bug_for_bug_compat: true
|
|
325
|
+
bug_for_bug_compat: true,
|
|
326
|
+
compat_for_corpus_analysis: compat
|
|
321
327
|
}));
|
|
322
328
|
vi.doMock('./5_conso_ventilation.js', () => ({ default: vi.fn() }));
|
|
323
329
|
const { default: calc_ventilation_bug } = await import('./4_ventilation.js');
|
|
@@ -522,25 +528,88 @@ describe('calc_ventilation - compatibilité bug_for_bug_compat activée', () =>
|
|
|
522
528
|
errorSpy.mockRestore();
|
|
523
529
|
});
|
|
524
530
|
|
|
525
|
-
test('ligne
|
|
531
|
+
test('ligne isolée de la même période, joints absents de la ligne : joints « 0 » repris', async () => {
|
|
532
|
+
const { calc_ventilation_bug, tvBug } = await chargerAvecBug(ROW_10);
|
|
533
|
+
vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
534
|
+
const baieAvecJoint = [{ donnee_entree: { surface_totale_baie: 10, presence_joint: 1 } }];
|
|
535
|
+
|
|
536
|
+
calc_ventilation_bug(vtLigne(10), CG, 'th', 80, 100, murNonIsole, [], [], baieAvecJoint);
|
|
537
|
+
|
|
538
|
+
// la ligne reprise prime sur les joints recalculés ('1')
|
|
539
|
+
expect(tvBug).toHaveBeenLastCalledWith(
|
|
540
|
+
'q4pa_conv',
|
|
541
|
+
expect.objectContaining({ isolation_surfaces: '1', presence_joints_menuiserie: '0' })
|
|
542
|
+
);
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
test('ligne isolée de la même période avec joints : joints « 1 » repris malgré des baies sans joint', async () => {
|
|
526
546
|
const { calc_ventilation_bug, tvBug } = await chargerAvecBug({
|
|
527
|
-
|
|
528
|
-
tv_q4pa_conv_id: '
|
|
529
|
-
enum_periode_construction_id: '1',
|
|
530
|
-
isolation_surfaces: '0',
|
|
547
|
+
...ROW_10,
|
|
548
|
+
tv_q4pa_conv_id: '11',
|
|
531
549
|
presence_joints_menuiserie: '1'
|
|
532
550
|
});
|
|
533
551
|
vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
534
|
-
const { murs } = surfacesIsolantes();
|
|
535
552
|
|
|
536
|
-
calc_ventilation_bug(vtLigne(
|
|
553
|
+
calc_ventilation_bug(vtLigne(11), CG, 'th', 80, 100, murNonIsole, [], [], baieSansJoint);
|
|
537
554
|
|
|
538
555
|
expect(tvBug).toHaveBeenLastCalledWith(
|
|
539
556
|
'q4pa_conv',
|
|
540
|
-
expect.objectContaining({ isolation_surfaces: '
|
|
557
|
+
expect.objectContaining({ isolation_surfaces: '1', presence_joints_menuiserie: '1' })
|
|
541
558
|
);
|
|
542
559
|
});
|
|
543
560
|
|
|
561
|
+
/**
|
|
562
|
+
* Sens unique : ligne « non isolées » alors que les parois sont isolées (cas d'un scénario de
|
|
563
|
+
* rénovation où l'appelant isole les murs du DPE). La ligne n'est pas reprise : l'isolation et
|
|
564
|
+
* les joints sont recalculés comme avant #312.
|
|
565
|
+
*/
|
|
566
|
+
describe('ligne non isolée alors que les parois sont isolées (sens inverse)', () => {
|
|
567
|
+
const ROW_12 = {
|
|
568
|
+
q4pa_conv: '2.5',
|
|
569
|
+
tv_q4pa_conv_id: '12',
|
|
570
|
+
enum_periode_construction_id: '1',
|
|
571
|
+
isolation_surfaces: '0',
|
|
572
|
+
presence_joints_menuiserie: '1'
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
test('isolation recalculée utilisée et écart des joints signalé', async () => {
|
|
576
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
577
|
+
const { calc_ventilation_bug, tvBug } = await chargerAvecBug(ROW_12);
|
|
578
|
+
const { murs } = surfacesIsolantes();
|
|
579
|
+
|
|
580
|
+
calc_ventilation_bug(vtLigne(12), CG, 'th', 80, 100, murs, [], [], baieSansJoint);
|
|
581
|
+
|
|
582
|
+
expect(tvBug).toHaveBeenLastCalledWith(
|
|
583
|
+
'q4pa_conv',
|
|
584
|
+
expect.objectContaining({ isolation_surfaces: '1', presence_joints_menuiserie: '0' })
|
|
585
|
+
);
|
|
586
|
+
expect(errorSpy).toHaveBeenCalledWith(
|
|
587
|
+
expect.stringContaining('La valeur recalculée isolation_surfaces = 1 est utilisée')
|
|
588
|
+
);
|
|
589
|
+
expect(errorSpy).toHaveBeenCalledWith(
|
|
590
|
+
expect.stringContaining(
|
|
591
|
+
'La valeur recalculée presence_joints_menuiserie = 0 est utilisée'
|
|
592
|
+
)
|
|
593
|
+
);
|
|
594
|
+
expect(errorSpy).not.toHaveBeenCalledWith(expect.stringContaining('est conservée'));
|
|
595
|
+
errorSpy.mockRestore();
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
test('presence_joint non exporté : seuls les joints de la ligne du DPE sont repris', async () => {
|
|
599
|
+
vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
600
|
+
const { calc_ventilation_bug, tvBug } = await chargerAvecBug(ROW_12);
|
|
601
|
+
const { murs } = surfacesIsolantes();
|
|
602
|
+
const baies = [{ donnee_entree: { surface_totale_baie: 10 } }];
|
|
603
|
+
|
|
604
|
+
calc_ventilation_bug(vtLigne(12), CG, 'th', 80, 100, murs, [], [], baies);
|
|
605
|
+
|
|
606
|
+
expect(tvBug).toHaveBeenLastCalledWith(
|
|
607
|
+
'q4pa_conv',
|
|
608
|
+
expect.objectContaining({ isolation_surfaces: '1', presence_joints_menuiserie: '1' })
|
|
609
|
+
);
|
|
610
|
+
});
|
|
611
|
+
});
|
|
612
|
+
|
|
544
613
|
test('ligne d’une autre période : la valeur recalculée est conservée', async () => {
|
|
545
614
|
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
546
615
|
const { calc_ventilation_bug, tvBug } = await chargerAvecBug({
|
|
@@ -561,6 +630,78 @@ describe('calc_ventilation - compatibilité bug_for_bug_compat activée', () =>
|
|
|
561
630
|
errorSpy.mockRestore();
|
|
562
631
|
});
|
|
563
632
|
});
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Sans l'option `compat_for_corpus_analysis` (comportement par défaut) : la ligne q4pa_conv du DPE
|
|
636
|
+
* n'est jamais reprise, q4pa_conv est recalculé selon la méthode. Les écarts sont seulement
|
|
637
|
+
* signalés (contrôle de cohérence d'un DPE, scénarios de travaux).
|
|
638
|
+
*/
|
|
639
|
+
describe('sans compat_for_corpus_analysis : ligne du DPE jamais reprise', () => {
|
|
640
|
+
const murNonIsole = [
|
|
641
|
+
{
|
|
642
|
+
donnee_intermediaire: { b: 1 },
|
|
643
|
+
donnee_entree: { enum_type_isolation_id: '2', surface_paroi_opaque: 100 }
|
|
644
|
+
}
|
|
645
|
+
];
|
|
646
|
+
const vtLigne = (id) => ({
|
|
647
|
+
donnee_entree: { enum_type_ventilation_id: '1', tv_q4pa_conv_id: id, description: 'v' }
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
test('ligne « surfaces isolées » de la même période (#312) : isolation et joints recalculés', async () => {
|
|
651
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
652
|
+
const { calc_ventilation_bug, tvBug } = await chargerAvecBug(
|
|
653
|
+
{
|
|
654
|
+
q4pa_conv: '2',
|
|
655
|
+
tv_q4pa_conv_id: '11',
|
|
656
|
+
enum_periode_construction_id: '1',
|
|
657
|
+
isolation_surfaces: '1',
|
|
658
|
+
presence_joints_menuiserie: '1'
|
|
659
|
+
},
|
|
660
|
+
false
|
|
661
|
+
);
|
|
662
|
+
const baieSansJoint = [{ donnee_entree: { surface_totale_baie: 10, presence_joint: 0 } }];
|
|
663
|
+
|
|
664
|
+
calc_ventilation_bug(vtLigne(11), CG, 'th', 80, 100, murNonIsole, [], [], baieSansJoint);
|
|
665
|
+
|
|
666
|
+
expect(tvBug).toHaveBeenLastCalledWith(
|
|
667
|
+
'q4pa_conv',
|
|
668
|
+
expect.objectContaining({ isolation_surfaces: '0', presence_joints_menuiserie: '0' })
|
|
669
|
+
);
|
|
670
|
+
expect(errorSpy).toHaveBeenCalledWith(
|
|
671
|
+
expect.stringContaining('La valeur recalculée isolation_surfaces = 0 est utilisée')
|
|
672
|
+
);
|
|
673
|
+
expect(errorSpy).toHaveBeenCalledWith(
|
|
674
|
+
expect.stringContaining('La valeur recalculée presence_joints_menuiserie = 0 est utilisée')
|
|
675
|
+
);
|
|
676
|
+
expect(errorSpy).not.toHaveBeenCalledWith(expect.stringContaining('est conservée'));
|
|
677
|
+
errorSpy.mockRestore();
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
test('presence_joint non exporté (#294) : joints recalculés à « 0 » et écart signalé', async () => {
|
|
681
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
682
|
+
const { calc_ventilation_bug, tvBug } = await chargerAvecBug(
|
|
683
|
+
{
|
|
684
|
+
q4pa_conv: '2.5',
|
|
685
|
+
tv_q4pa_conv_id: '12',
|
|
686
|
+
isolation_surfaces: '0',
|
|
687
|
+
presence_joints_menuiserie: '1'
|
|
688
|
+
},
|
|
689
|
+
false
|
|
690
|
+
);
|
|
691
|
+
const baies = [{ donnee_entree: { surface_totale_baie: 10 } }];
|
|
692
|
+
|
|
693
|
+
calc_ventilation_bug(vtLigne(12), CG, 'th', 80, 100, murNonIsole, [], [], baies);
|
|
694
|
+
|
|
695
|
+
expect(tvBug).toHaveBeenLastCalledWith(
|
|
696
|
+
'q4pa_conv',
|
|
697
|
+
expect.objectContaining({ isolation_surfaces: '0', presence_joints_menuiserie: '0' })
|
|
698
|
+
);
|
|
699
|
+
expect(errorSpy).toHaveBeenCalledWith(
|
|
700
|
+
expect.stringContaining('La valeur recalculée presence_joints_menuiserie = 0 est utilisée')
|
|
701
|
+
);
|
|
702
|
+
errorSpy.mockRestore();
|
|
703
|
+
});
|
|
704
|
+
});
|
|
564
705
|
});
|
|
565
706
|
|
|
566
707
|
describe('presenceJointNonRenseignee', () => {
|
|
@@ -612,3 +753,19 @@ describe('isLigneQ4paMemePeriode', () => {
|
|
|
612
753
|
expect(isLigneQ4paMemePeriode(null, { enum_periode_construction_id: '1' })).toBe(false);
|
|
613
754
|
});
|
|
614
755
|
});
|
|
756
|
+
|
|
757
|
+
describe('isLigneQ4paSurfacesIsolees', () => {
|
|
758
|
+
test('ligne « surfaces isolées » et recalcul « non isolées » : vrai', () => {
|
|
759
|
+
expect(isLigneQ4paSurfacesIsolees({ isolation_surfaces: '1' }, '0')).toBe(true);
|
|
760
|
+
expect(isLigneQ4paSurfacesIsolees({ isolation_surfaces: 1 }, 0)).toBe(true);
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
test('ligne « non isolées » et recalcul « isolées » : faux (sens inverse)', () => {
|
|
764
|
+
expect(isLigneQ4paSurfacesIsolees({ isolation_surfaces: '0' }, '1')).toBe(false);
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
test('valeurs concordantes ou ligne sans critère : faux', () => {
|
|
768
|
+
expect(isLigneQ4paSurfacesIsolees({ isolation_surfaces: '1' }, '1')).toBe(false);
|
|
769
|
+
expect(isLigneQ4paSurfacesIsolees({}, '0')).toBe(false);
|
|
770
|
+
});
|
|
771
|
+
});
|
package/README.md
CHANGED
|
@@ -251,11 +251,18 @@ const c = calcul_3cl(dpeData, { sanitize: false });
|
|
|
251
251
|
// Depuis un XML ADEME
|
|
252
252
|
const d = calcul_3cl_xml(xmlString);
|
|
253
253
|
const e = calcul_3cl_xml(xmlString, { sanitize: false });
|
|
254
|
+
|
|
255
|
+
// Analyse du corpus ADEME uniquement : reproduire les choix du logiciel de saisie
|
|
256
|
+
const f = calcul_3cl(dpeData, { sanitize: true, compat_for_corpus_analysis: true });
|
|
254
257
|
```
|
|
255
258
|
|
|
256
|
-
| Option
|
|
257
|
-
|
|
|
258
|
-
| `sanitize`
|
|
259
|
+
| Option | Défaut | Effet |
|
|
260
|
+
| :--------------------------- | :------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
261
|
+
| `sanitize` | `true` | Normalise et corrige les incohérences du DPE d'entrée avant calcul. Désactivez-le pour un calcul brut. |
|
|
262
|
+
| `compat_for_corpus_analysis` | `false` | Reprend la ligne `q4pa_conv` choisie par le logiciel de saisie même si elle contredit l'isolation des parois ou les joints déclarés, pour reproduire les DPE du corpus ADEME. À n'utiliser que pour l'analyse de corpus. Désactivé, le moteur recalcule `q4pa_conv` selon la méthode (contrôle de cohérence d'un DPE, scénarios de travaux). |
|
|
263
|
+
|
|
264
|
+
> Dès qu'un objet `options` est passé, `sanitize` vaut `false` s'il n'est pas précisé : indiquez
|
|
265
|
+
> `sanitize: true` avec `compat_for_corpus_analysis`.
|
|
259
266
|
|
|
260
267
|
<details>
|
|
261
268
|
<summary><strong>Exemple d'objet JSON d'entrée (partiel)</strong></summary>
|
|
@@ -376,28 +383,28 @@ Ces résultats sont **générés automatiquement** à la fin de `npm run test:co
|
|
|
376
383
|
|
|
377
384
|
<!-- CORPUS:START -->
|
|
378
385
|
|
|
379
|
-
> **Version `1.
|
|
386
|
+
> **Version `1.9.1`** · branche `main` · généré le 2026-10-26
|
|
380
387
|
> Seuil de tolérance **5%**
|
|
381
388
|
|
|
382
389
|
<table>
|
|
383
390
|
<tr>
|
|
384
391
|
<td align="center"><strong>9</strong><br/><sub>corpus</sub></td>
|
|
385
392
|
<td align="center"><strong>88 475</strong><br/><sub>DPE analysés</sub></td>
|
|
386
|
-
<td align="center"><strong>75
|
|
387
|
-
<td align="center"><strong>85,
|
|
393
|
+
<td align="center"><strong>75 700</strong><br/><sub>DPE conformes</sub></td>
|
|
394
|
+
<td align="center"><strong>85,56 %</strong><br/><sub>réussite globale</sub></td>
|
|
388
395
|
</tr>
|
|
389
396
|
</table>
|
|
390
397
|
|
|
391
398
|
| | Corpus | Réussite | | DPE conformes |
|
|
392
399
|
| :-: | :---------------------------------------------------------------------------------------------------------------------- | ----------: | :--------------------- | -------------: |
|
|
393
|
-
| 🟢 | **Appartement · chauffage individuel (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_individuel_2025.csv`</sub> | **95,
|
|
394
|
-
| 🟢 | **Appartement · chauffage collectif (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_collectif_2025.csv`</sub> | **94,
|
|
395
|
-
| 🟢 | **Logement individuel (2025)**<br/><sub>`dpe_logement_individuel_2025.csv`</sub> | **93,
|
|
396
|
-
| 🟢 | **Maison individuelle (2025)**<br/><sub>`dpe_maison_individuelle_2025.csv`</sub> | **92,
|
|
397
|
-
| 🟢 | **Maison individuelle (2026)**<br/><sub>`dpe_maison_individuelle_2026.csv`</sub> | **90,
|
|
398
|
-
| 🟢 | **Immeuble · chauffage collectif**<br/><sub>`dpe_immeuble_chauffage_collectif.csv`</sub> | **89,
|
|
399
|
-
| 🟢 | **Immeuble · chauffage individuel**<br/><sub>`dpe_immeuble_chauffage_individuel.csv`</sub> | **86,
|
|
400
|
-
| 🟡 | **Immeuble · chauffage mixte**<br/><sub>`dpe_immeuble_chauffage_mixte.csv`</sub> | **70,
|
|
400
|
+
| 🟢 | **Appartement · chauffage individuel (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_individuel_2025.csv`</sub> | **95,43 %** | `███████████████████░` | 9 441 / 9 893 |
|
|
401
|
+
| 🟢 | **Appartement · chauffage collectif (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_collectif_2025.csv`</sub> | **94,55 %** | `███████████████████░` | 9 385 / 9 926 |
|
|
402
|
+
| 🟢 | **Logement individuel (2025)**<br/><sub>`dpe_logement_individuel_2025.csv`</sub> | **93,36 %** | `███████████████████░` | 9 180 / 9 833 |
|
|
403
|
+
| 🟢 | **Maison individuelle (2025)**<br/><sub>`dpe_maison_individuelle_2025.csv`</sub> | **92,14 %** | `██████████████████░░` | 9 003 / 9 771 |
|
|
404
|
+
| 🟢 | **Maison individuelle (2026)**<br/><sub>`dpe_maison_individuelle_2026.csv`</sub> | **90,46 %** | `██████████████████░░` | 9 046 / 10 000 |
|
|
405
|
+
| 🟢 | **Immeuble · chauffage collectif**<br/><sub>`dpe_immeuble_chauffage_collectif.csv`</sub> | **89,13 %** | `██████████████████░░` | 8 680 / 9 739 |
|
|
406
|
+
| 🟢 | **Immeuble · chauffage individuel**<br/><sub>`dpe_immeuble_chauffage_individuel.csv`</sub> | **86,04 %** | `█████████████████░░░` | 8 539 / 9 924 |
|
|
407
|
+
| 🟡 | **Immeuble · chauffage mixte**<br/><sub>`dpe_immeuble_chauffage_mixte.csv`</sub> | **70,50 %** | `██████████████░░░░░░` | 6 748 / 9 571 |
|
|
401
408
|
| 🔴 | **Individuel généré depuis l'immeuble (2026)**<br/><sub>`dpe_individuel_a_partir_dpe_immeuble_2026.csv`</sub> | **57,83 %** | `████████████░░░░░░░░` | 5 678 / 9 818 |
|
|
402
409
|
|
|
403
410
|
<sub>🟢 ≥ 85 % · 🟡 ≥ 60 % · 🔴 < 60 %</sub>
|
|
@@ -409,24 +416,24 @@ Ces résultats sont **générés automatiquement** à la fin de `npm run test:co
|
|
|
409
416
|
|
|
410
417
|
<table>
|
|
411
418
|
<tr>
|
|
412
|
-
<td align="center"><strong>
|
|
413
|
-
<td align="center"><strong>
|
|
414
|
-
<td align="center"><strong>0,
|
|
415
|
-
<td align="center"><strong>
|
|
419
|
+
<td align="center"><strong>6,66 ms</strong><br/><sub>moyenne</sub></td>
|
|
420
|
+
<td align="center"><strong>5,79 ms</strong><br/><sub>médiane</sub></td>
|
|
421
|
+
<td align="center"><strong>0,13 ms</strong><br/><sub>min</sub></td>
|
|
422
|
+
<td align="center"><strong>911,0 ms</strong><br/><sub>max</sub></td>
|
|
416
423
|
</tr>
|
|
417
424
|
</table>
|
|
418
425
|
|
|
419
426
|
| Corpus | Moyenne | Médiane | Min | Max | p95 | p99 |
|
|
420
427
|
| :--------------------------------------------- | ------: | ------: | ------: | -------: | ------: | ------: |
|
|
421
|
-
| **Appartement · chauffage individuel (2025)** |
|
|
422
|
-
| **Appartement · chauffage collectif (2025)** |
|
|
423
|
-
| **Logement individuel (2025)** |
|
|
424
|
-
| **Maison individuelle (2025)** |
|
|
425
|
-
| **Maison individuelle (2026)** |
|
|
426
|
-
| **Immeuble · chauffage collectif** |
|
|
427
|
-
| **Immeuble · chauffage individuel** |
|
|
428
|
-
| **Immeuble · chauffage mixte** |
|
|
429
|
-
| **Individuel généré depuis l'immeuble (2026)** |
|
|
428
|
+
| **Appartement · chauffage individuel (2025)** | 5,04 ms | 4,55 ms | 1,68 ms | 58,6 ms | 8,75 ms | 11,6 ms |
|
|
429
|
+
| **Appartement · chauffage collectif (2025)** | 5,96 ms | 5,55 ms | 2,00 ms | 56,7 ms | 9,64 ms | 12,0 ms |
|
|
430
|
+
| **Logement individuel (2025)** | 6,17 ms | 5,46 ms | 1,75 ms | 60,4 ms | 11,5 ms | 16,1 ms |
|
|
431
|
+
| **Maison individuelle (2025)** | 6,97 ms | 6,28 ms | 2,02 ms | 59,3 ms | 12,3 ms | 16,7 ms |
|
|
432
|
+
| **Maison individuelle (2026)** | 7,06 ms | 6,30 ms | 2,00 ms | 45,5 ms | 12,6 ms | 17,6 ms |
|
|
433
|
+
| **Immeuble · chauffage collectif** | 6,94 ms | 5,88 ms | 0,14 ms | 144,5 ms | 12,8 ms | 22,1 ms |
|
|
434
|
+
| **Immeuble · chauffage individuel** | 7,88 ms | 6,68 ms | 0,13 ms | 172,2 ms | 15,7 ms | 25,4 ms |
|
|
435
|
+
| **Immeuble · chauffage mixte** | 6,74 ms | 5,47 ms | 0,22 ms | 911,0 ms | 14,1 ms | 22,5 ms |
|
|
436
|
+
| **Individuel généré depuis l'immeuble (2026)** | 7,21 ms | 5,97 ms | 1,81 ms | 91,1 ms | 13,7 ms | 22,1 ms |
|
|
430
437
|
|
|
431
438
|
<sub>La moyenne est tirée vers le haut par les DPE collectifs, dont le coût atteint plusieurs
|
|
432
439
|
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
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
containsAnySubstring,
|
|
25
25
|
isEffetJoule,
|
|
26
26
|
set_bug_for_bug_compat,
|
|
27
|
+
set_compat_for_corpus_analysis,
|
|
27
28
|
use_enum_as_string,
|
|
28
29
|
useEnumAsString,
|
|
29
30
|
xmlParser
|
|
@@ -35,7 +36,7 @@ import DpeSanitizerService from './dpe-sanitizer.service.js';
|
|
|
35
36
|
import { reconstituerRdimDpeGenereImmeuble } from './17.2_rdim_dpe_genere_immeuble.js';
|
|
36
37
|
import { reconstituerRdimEcsIndividuelleUnique } from './11_rdim_ecs_individuelle_unique.js';
|
|
37
38
|
|
|
38
|
-
const LIB_VERSION = '1.
|
|
39
|
+
const LIB_VERSION = '1.10.0';
|
|
39
40
|
|
|
40
41
|
const dpeSanitizerService = new DpeSanitizerService();
|
|
41
42
|
|
|
@@ -58,7 +59,7 @@ export function getVersion() {
|
|
|
58
59
|
/**
|
|
59
60
|
* Run the engine with a full dpe xml content
|
|
60
61
|
* @param dpeXmlContent {string} A full dpe xml content
|
|
61
|
-
* @param options {{sanitize
|
|
62
|
+
* @param options {{sanitize?: boolean, compat_for_corpus_analysis?: boolean}?} voir `calcul_3cl`
|
|
62
63
|
* @return {FullDpe}
|
|
63
64
|
*/
|
|
64
65
|
export function calcul_3cl_xml(dpeXmlContent, options) {
|
|
@@ -70,12 +71,18 @@ export function calcul_3cl_xml(dpeXmlContent, options) {
|
|
|
70
71
|
/**
|
|
71
72
|
* Run the engine with a javascript plain object dpe
|
|
72
73
|
* @param inputDpe {FullDpe} A full plain object dpe
|
|
73
|
-
* @param options {{sanitize
|
|
74
|
+
* @param options {{sanitize?: boolean, compat_for_corpus_analysis?: boolean}?}
|
|
75
|
+
* - `sanitize` (défaut `true` si `options` est absent) : nettoyage du DPE d'entrée ;
|
|
76
|
+
* - `compat_for_corpus_analysis` (défaut `false`) : reprend les valeurs choisies par le logiciel de
|
|
77
|
+
* saisie qui contredisent les données d'entrée (ligne q4pa_conv, issues #312 et #294), pour
|
|
78
|
+
* reproduire les DPE du corpus ADEME. À réserver à l'analyse de corpus : jamais pour le contrôle
|
|
79
|
+
* d'un DPE ni pour les scénarios de travaux.
|
|
74
80
|
* @return {FullDpe}
|
|
75
81
|
*/
|
|
76
82
|
export function calcul_3cl(inputDpe, options) {
|
|
77
83
|
set_bug_for_bug_compat();
|
|
78
84
|
if (!options) options = { sanitize: true };
|
|
85
|
+
set_compat_for_corpus_analysis(options.compat_for_corpus_analysis === true);
|
|
79
86
|
let dpe;
|
|
80
87
|
if (options.sanitize) {
|
|
81
88
|
dpe = dpeSanitizerService.execute(inputDpe);
|
package/engine.spec.js
CHANGED
|
@@ -21,6 +21,7 @@ let useEnumAsStringFlag = false;
|
|
|
21
21
|
|
|
22
22
|
vi.mock('./utils.js', () => ({
|
|
23
23
|
set_bug_for_bug_compat: vi.fn(),
|
|
24
|
+
set_compat_for_corpus_analysis: vi.fn(),
|
|
24
25
|
add_references: vi.fn(),
|
|
25
26
|
get bug_for_bug_compat() {
|
|
26
27
|
return bugForBugCompat;
|
|
@@ -104,6 +105,7 @@ const {
|
|
|
104
105
|
collectionCanBeEmpty,
|
|
105
106
|
containsAnySubstring,
|
|
106
107
|
isEffetJoule,
|
|
108
|
+
set_compat_for_corpus_analysis,
|
|
107
109
|
useEnumAsString,
|
|
108
110
|
xmlParser
|
|
109
111
|
} = await import('./utils.js');
|
|
@@ -365,6 +367,52 @@ describe('calcul_3cl - garde-fous précoces', () => {
|
|
|
365
367
|
});
|
|
366
368
|
});
|
|
367
369
|
|
|
370
|
+
/**
|
|
371
|
+
* Option `compat_for_corpus_analysis` : reprise des valeurs du logiciel de saisie (ligne q4pa_conv)
|
|
372
|
+
* réservée à l'analyse du corpus ADEME. Désactivée par défaut et repositionnée à chaque appel.
|
|
373
|
+
*/
|
|
374
|
+
describe('calcul_3cl - option compat_for_corpus_analysis', () => {
|
|
375
|
+
const dpeNonSupporte = () => ({ administratif: { enum_modele_dpe_id: '99' } });
|
|
376
|
+
|
|
377
|
+
beforeEach(() => {
|
|
378
|
+
vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
test('sans options : désactivée', () => {
|
|
382
|
+
calcul_3cl(dpeNonSupporte());
|
|
383
|
+
expect(set_compat_for_corpus_analysis).toHaveBeenCalledExactlyOnceWith(false);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
test('options sans le champ : désactivée', () => {
|
|
387
|
+
calcul_3cl(dpeNonSupporte(), { sanitize: false });
|
|
388
|
+
expect(set_compat_for_corpus_analysis).toHaveBeenCalledExactlyOnceWith(false);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
test('valeur non booléenne « truthy » : désactivée (seul true active la reprise)', () => {
|
|
392
|
+
calcul_3cl(dpeNonSupporte(), { sanitize: false, compat_for_corpus_analysis: 'true' });
|
|
393
|
+
expect(set_compat_for_corpus_analysis).toHaveBeenCalledExactlyOnceWith(false);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
test('true : activée, sans changer la sanitisation demandée', () => {
|
|
397
|
+
const dpe = dpeNonSupporte();
|
|
398
|
+
calcul_3cl(dpe, { sanitize: true, compat_for_corpus_analysis: true });
|
|
399
|
+
expect(set_compat_for_corpus_analysis).toHaveBeenCalledExactlyOnceWith(true);
|
|
400
|
+
expect(sanitizeExecute).toHaveBeenCalledWith(dpe);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
test('repositionnée à chaque appel : un appel par défaut après un appel activé la désactive', () => {
|
|
404
|
+
calcul_3cl(dpeNonSupporte(), { sanitize: false, compat_for_corpus_analysis: true });
|
|
405
|
+
calcul_3cl(dpeNonSupporte());
|
|
406
|
+
expect(vi.mocked(set_compat_for_corpus_analysis).mock.calls).toEqual([[true], [false]]);
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
test('calcul_3cl_xml transmet l’option', () => {
|
|
410
|
+
vi.mocked(xmlParser.parse).mockReturnValue({ dpe: dpeNonSupporte() });
|
|
411
|
+
calcul_3cl_xml('<dpe/>', { sanitize: false, compat_for_corpus_analysis: true });
|
|
412
|
+
expect(set_compat_for_corpus_analysis).toHaveBeenCalledExactlyOnceWith(true);
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
|
|
368
416
|
describe('calcul_3cl_xml - garde sur le modèle de DPE', () => {
|
|
369
417
|
test('parse le XML puis retourne null pour un modèle non supporté', () => {
|
|
370
418
|
const dpe = { administratif: { enum_modele_dpe_id: '99' } };
|
|
@@ -1,7 +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
|
+
import { bug_for_bug_compat, compat_for_corpus_analysis } from '../../../../../utils.js';
|
|
5
5
|
import { TypeHabitation } from '../../../../dpe/domain/models/type-habitation.model.js';
|
|
6
6
|
import { TypeVentilation } from '../../../../dpe/domain/models/type-ventilation.model.js';
|
|
7
7
|
|
|
@@ -100,12 +100,14 @@ export class DeperditionVentilationService extends DeperditionService {
|
|
|
100
100
|
presenceJointsMenuiserie = '1';
|
|
101
101
|
} else if (
|
|
102
102
|
bug_for_bug_compat &&
|
|
103
|
+
compat_for_corpus_analysis &&
|
|
103
104
|
presenceJointNonRenseignee &&
|
|
104
105
|
this.tvStore.getQ4paConvById(ventilationDE.tv_q4pa_conv_id)
|
|
105
106
|
?.presence_joints_menuiserie === '1'
|
|
106
107
|
) {
|
|
107
108
|
// 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
|
+
// q4pa_conv « avant 1948 avec joints » retenue par le DPE => on la reprend, uniquement
|
|
110
|
+
// pour l'analyse du corpus ADEME (option compat_for_corpus_analysis).
|
|
109
111
|
presenceJointsMenuiserie = '1';
|
|
110
112
|
}
|
|
111
113
|
}
|
package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.spec.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
|
2
2
|
|
|
3
|
-
/** `bug_for_bug_compat` (utils.js)
|
|
4
|
-
const state = vi.hoisted(() => ({ bug: false }));
|
|
3
|
+
/** `bug_for_bug_compat` et `compat_for_corpus_analysis` (utils.js) pilotés par le test. */
|
|
4
|
+
const state = vi.hoisted(() => ({ bug: false, compat: false }));
|
|
5
5
|
vi.mock('../../../../../utils.js', async (importOriginal) => ({
|
|
6
6
|
...(await importOriginal()),
|
|
7
7
|
get bug_for_bug_compat() {
|
|
8
8
|
return state.bug;
|
|
9
|
+
},
|
|
10
|
+
get compat_for_corpus_analysis() {
|
|
11
|
+
return state.compat;
|
|
9
12
|
}
|
|
10
13
|
}));
|
|
11
14
|
|
|
@@ -32,6 +35,7 @@ describe('DeperditionVentilationService.q4paConv', () => {
|
|
|
32
35
|
};
|
|
33
36
|
service = new DeperditionVentilationService(tvStore);
|
|
34
37
|
state.bug = false;
|
|
38
|
+
state.compat = false;
|
|
35
39
|
});
|
|
36
40
|
|
|
37
41
|
test('Q4Pa mesuré saisi : utilisé directement', () => {
|
|
@@ -52,6 +56,7 @@ describe('DeperditionVentilationService.q4paConv', () => {
|
|
|
52
56
|
describe('presence_joint non exporté (issue #294)', () => {
|
|
53
57
|
test('hors bug_for_bug_compat : la ligne du DPE est ignorée', () => {
|
|
54
58
|
state.bug = false;
|
|
59
|
+
state.compat = true;
|
|
55
60
|
tvStore.getQ4paConvById.mockReturnValue({ presence_joints_menuiserie: '1' });
|
|
56
61
|
|
|
57
62
|
service.q4paConv({ tv_q4pa_conv_id: 12 }, ctx, 0, 100, 0, 10, true);
|
|
@@ -59,8 +64,20 @@ describe('DeperditionVentilationService.q4paConv', () => {
|
|
|
59
64
|
expect(tvStore.getQ4paConv).toHaveBeenCalledWith('1', 'APPARTEMENT', '0', undefined);
|
|
60
65
|
});
|
|
61
66
|
|
|
62
|
-
test('
|
|
67
|
+
test('par défaut (sans compat_for_corpus_analysis) : la ligne du DPE est ignorée', () => {
|
|
63
68
|
state.bug = true;
|
|
69
|
+
state.compat = false;
|
|
70
|
+
tvStore.getQ4paConvById.mockReturnValue({ presence_joints_menuiserie: '1' });
|
|
71
|
+
|
|
72
|
+
service.q4paConv({ tv_q4pa_conv_id: 12 }, ctx, 0, 100, 0, 10, true);
|
|
73
|
+
|
|
74
|
+
expect(tvStore.getQ4paConvById).not.toHaveBeenCalled();
|
|
75
|
+
expect(tvStore.getQ4paConv).toHaveBeenCalledWith('1', 'APPARTEMENT', '0', undefined);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('compat_for_corpus_analysis et ligne DPE « avec joints » : joints repris', () => {
|
|
79
|
+
state.bug = true;
|
|
80
|
+
state.compat = true;
|
|
64
81
|
tvStore.getQ4paConvById.mockReturnValue({ presence_joints_menuiserie: '1' });
|
|
65
82
|
|
|
66
83
|
service.q4paConv({ tv_q4pa_conv_id: 12 }, ctx, 0, 100, 0, 10, true);
|
|
@@ -69,8 +86,9 @@ describe('DeperditionVentilationService.q4paConv', () => {
|
|
|
69
86
|
expect(tvStore.getQ4paConv).toHaveBeenCalledWith('1', 'APPARTEMENT', '0', '1');
|
|
70
87
|
});
|
|
71
88
|
|
|
72
|
-
test('
|
|
89
|
+
test('compat_for_corpus_analysis mais presence_joint renseigné : pas de reprise', () => {
|
|
73
90
|
state.bug = true;
|
|
91
|
+
state.compat = true;
|
|
74
92
|
tvStore.getQ4paConvById.mockReturnValue({ presence_joints_menuiserie: '1' });
|
|
75
93
|
|
|
76
94
|
service.q4paConv({ tv_q4pa_conv_id: 12 }, ctx, 0, 100, 0, 10, false);
|
|
@@ -78,8 +96,9 @@ describe('DeperditionVentilationService.q4paConv', () => {
|
|
|
78
96
|
expect(tvStore.getQ4paConv).toHaveBeenCalledWith('1', 'APPARTEMENT', '0', undefined);
|
|
79
97
|
});
|
|
80
98
|
|
|
81
|
-
test('
|
|
99
|
+
test('compat_for_corpus_analysis et ligne DPE sans joints : pas de reprise', () => {
|
|
82
100
|
state.bug = true;
|
|
101
|
+
state.compat = true;
|
|
83
102
|
tvStore.getQ4paConvById.mockReturnValue({ presence_joints_menuiserie: '' });
|
|
84
103
|
|
|
85
104
|
service.q4paConv({ tv_q4pa_conv_id: 1 }, ctx, 0, 100, 0, 10, true);
|
|
@@ -89,6 +108,7 @@ describe('DeperditionVentilationService.q4paConv', () => {
|
|
|
89
108
|
|
|
90
109
|
test('surfaces majoritairement isolées : le critère des joints ne s’applique pas', () => {
|
|
91
110
|
state.bug = true;
|
|
111
|
+
state.compat = true;
|
|
92
112
|
tvStore.getQ4paConvById.mockReturnValue({ presence_joints_menuiserie: '1' });
|
|
93
113
|
|
|
94
114
|
service.q4paConv({ tv_q4pa_conv_id: 12 }, ctx, 100, 0, 0, 10, true);
|
package/package.json
CHANGED
package/utils.js
CHANGED
|
@@ -40,6 +40,29 @@ export function set_bug_for_bug_compat() {
|
|
|
40
40
|
bug_for_bug_compat = true;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Compatibilité réservée à l'analyse du corpus ADEME.
|
|
45
|
+
*
|
|
46
|
+
* Activée, le moteur reprend certaines valeurs choisies par le logiciel de saisie même
|
|
47
|
+
* lorsqu'elles contredisent les données d'entrée (ex. ligne `tv_q4pa_conv_id` incohérente avec
|
|
48
|
+
* l'isolation des parois ou les joints des menuiseries, issues #312 et #294), dans le seul but de
|
|
49
|
+
* reproduire les résultats des DPE du corpus.
|
|
50
|
+
*
|
|
51
|
+
* Ne jamais l'activer pour contrôler la cohérence d'un DPE ni pour calculer des scénarios de
|
|
52
|
+
* travaux : l'erreur du logiciel de saisie serait alors masquée ou propagée.
|
|
53
|
+
*
|
|
54
|
+
* Positionnée à chaque appel de `calcul_3cl` à partir de l'option du même nom.
|
|
55
|
+
* @type {boolean}
|
|
56
|
+
*/
|
|
57
|
+
export let compat_for_corpus_analysis = false;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param value {boolean} `true` uniquement pour l'analyse du corpus ADEME
|
|
61
|
+
*/
|
|
62
|
+
export function set_compat_for_corpus_analysis(value = false) {
|
|
63
|
+
compat_for_corpus_analysis = value === true;
|
|
64
|
+
}
|
|
65
|
+
|
|
43
66
|
export function useEnumAsString(jsonObj) {
|
|
44
67
|
for (const key in jsonObj) {
|
|
45
68
|
if (jsonObj.hasOwnProperty(key)) {
|
package/utils.spec.js
CHANGED
|
@@ -35,6 +35,7 @@ vi.mock('./tv.js', () => ({
|
|
|
35
35
|
const {
|
|
36
36
|
xmlParser,
|
|
37
37
|
set_bug_for_bug_compat,
|
|
38
|
+
set_compat_for_corpus_analysis,
|
|
38
39
|
useEnumAsString,
|
|
39
40
|
set_use_enum_as_string,
|
|
40
41
|
set_tv_match_optimized_version,
|
|
@@ -171,6 +172,20 @@ describe('Utils unit tests', () => {
|
|
|
171
172
|
expect(utils.bug_for_bug_compat).toBe(true);
|
|
172
173
|
});
|
|
173
174
|
|
|
175
|
+
test('compat_for_corpus_analysis : désactivé par défaut, seul true l’active', () => {
|
|
176
|
+
expect(utils.compat_for_corpus_analysis).toBe(false);
|
|
177
|
+
set_compat_for_corpus_analysis(true);
|
|
178
|
+
expect(utils.compat_for_corpus_analysis).toBe(true);
|
|
179
|
+
set_compat_for_corpus_analysis();
|
|
180
|
+
expect(utils.compat_for_corpus_analysis).toBe(false);
|
|
181
|
+
set_compat_for_corpus_analysis(true);
|
|
182
|
+
set_compat_for_corpus_analysis('true');
|
|
183
|
+
expect(utils.compat_for_corpus_analysis).toBe(false);
|
|
184
|
+
set_compat_for_corpus_analysis(true);
|
|
185
|
+
set_compat_for_corpus_analysis(false);
|
|
186
|
+
expect(utils.compat_for_corpus_analysis).toBe(false);
|
|
187
|
+
});
|
|
188
|
+
|
|
174
189
|
test('set_use_enum_as_string active le drapeau', () => {
|
|
175
190
|
set_use_enum_as_string();
|
|
176
191
|
expect(utils.use_enum_as_string).toBe(true);
|