@open3cl/engine 1.6.0 → 1.6.2
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/10_besoin_fr.spec.js +122 -0
- package/10_clim.spec.js +116 -0
- package/11_besoin_ecs.spec.js +69 -0
- package/11_ecs.spec.js +282 -0
- package/11_nadeq.spec.js +37 -0
- package/12.4_pac.spec.js +131 -0
- package/13.2_generateur_combustion.js +2 -0
- package/13.2_generateur_combustion.spec.js +537 -0
- package/13.2_generateur_combustion_bouilleur.js +4 -1
- package/13.2_generateur_combustion_bouilleur.spec.js +74 -0
- package/13.2_generateur_combustion_ch.spec.js +323 -0
- package/13.2_generateur_combustion_chaudiere.spec.js +85 -0
- package/13.2_generateur_pac.spec.js +64 -0
- package/13_rendement_distribution_ecs.spec.js +38 -0
- package/14_generateur_ecs.js +4 -4
- package/14_generateur_ecs.spec.js +745 -0
- package/15_conso_aux.spec.js +225 -0
- package/16.2_production_enr.spec.js +188 -0
- package/16_conso_eclairage.spec.js +62 -0
- package/2021_04_13_confort_ete.spec.js +159 -0
- package/2021_04_13_qualite_isolation.js +1 -1
- package/2021_04_13_qualite_isolation.spec.js +286 -0
- package/3.1_b.js +1 -1
- package/3.1_b.spec.js +367 -0
- package/3.2.1_mur.spec.js +812 -39
- package/3.2.2_plancher_bas.spec.js +643 -78
- package/3.2.3_plancher_haut.spec.js +452 -0
- package/3.3.1.4_porte.spec.js +114 -0
- package/3.3_baie_vitree.spec.js +449 -295
- package/3.4_pont_thermique.spec.js +890 -0
- package/3_deperdition.spec.js +397 -0
- package/4_ventilation.js +1 -1
- package/4_ventilation.spec.js +406 -0
- package/5_conso_ventilation.spec.js +186 -0
- package/6.2_surface_sud_equivalente.spec.js +138 -0
- package/8_intermittence.spec.js +33 -0
- package/9_besoin_ch.js +2 -2
- package/9_besoin_ch.spec.js +381 -0
- package/9_chauffage.js +4 -0
- package/9_chauffage.spec.js +478 -92
- package/9_conso_ch.spec.js +502 -248
- package/9_emetteur_ch.spec.js +339 -0
- package/9_generateur_ch.spec.js +467 -81
- package/README.md +18 -9
- package/apport_et_besoin.spec.js +126 -0
- package/conso.js +1 -1
- package/conso.spec.js +471 -82
- package/core/assets/domain/add-additionnal-ue-values-tables.spec.js +53 -0
- package/core/assets/domain/synchronize-solicitations-tables.spec.js +25 -0
- package/core/assets/domain/synchronize-valeur-tables.spec.js +48 -0
- package/core/conf/infrastructure/application.config.spec.js +35 -0
- package/core/file/infrastructure/adapter/csv-parser.store.spec.js +31 -13
- package/core/file/infrastructure/adapter/file.store.spec.js +148 -12
- package/core/tv/infrastructure/assets/additional-ue-values.spec.js +36 -0
- package/core/util/infrastructure/object-util.spec.js +20 -0
- package/core/util/logger/log-service.js +16 -7
- package/core/util/logger/log-service.spec.js +101 -0
- package/dpe-sanitizer.service.spec.js +138 -0
- package/engine.js +7 -5
- package/engine.spec.js +854 -0
- package/enums.spec.js +46 -0
- package/features/dpe/domain/models/type-generateur.model.js +1 -2
- package/features/dpe/domain/models/type-generateur.model.spec.js +25 -0
- package/features/dpe/domain/models/type-habitation.model.spec.js +41 -0
- package/features/dpe/domain/models/type-stockage.model.spec.js +18 -0
- package/features/dpe/domain/models/type-ventilation.model.spec.js +19 -0
- package/features/dpe/infrastructure/ch/chTv.store.spec.js +4 -4
- package/features/dpe/infrastructure/enveloppe/baieVitreeTv.store.js +14 -2
- package/features/dpe/infrastructure/enveloppe/baieVitreeTv.store.spec.js +16 -0
- package/features/dpe/infrastructure/tv.store.js +28 -9
- package/features/dpe/infrastructure/tv.store.spec.js +44 -0
- package/features/engine/domain/apport_et_besoin/apport-et-besoin.service.spec.js +2 -1
- package/features/engine/domain/apport_et_besoin/ch/besoin-ch.service.spec.js +18 -0
- package/features/engine/domain/apport_et_besoin/ch/perte-ch-recup.service.spec.js +33 -1
- package/features/engine/domain/apport_et_besoin/ecs/besoin-ecs.service.spec.js +21 -1
- package/features/engine/domain/apport_et_besoin/ecs/perte-ecs-recup.service.spec.js +41 -1
- package/features/engine/domain/apport_et_besoin/froid/besoin-froid.service.spec.js +44 -1
- package/features/engine/domain/apport_et_besoin/surface-sud-equivalente.service.spec.js +2 -1
- package/features/engine/domain/ch/generateur-ch.service.js +2 -0
- package/features/engine/domain/ch/generateur-ch.service.spec.js +117 -2
- package/features/engine/domain/ch/installation-ch.service.spec.js +14 -0
- package/features/engine/domain/conso/froid/conso-froid.service.spec.js +82 -1
- package/features/engine/domain/constants.spec.js +14 -0
- package/features/engine/domain/contexte.builder.spec.js +21 -0
- package/features/engine/domain/ecs/generateur-ecs.service.spec.js +11 -0
- package/features/engine/domain/ecs/installation-ecs.service.spec.js +55 -2
- package/features/engine/domain/engine.service.spec.js +227 -4
- package/features/engine/domain/enveloppe/baie_vitree/deperdition-baie-vitree.service.spec.js +124 -1
- package/features/engine/domain/enveloppe/deperdition-enveloppe.service.spec.js +270 -2
- package/features/engine/domain/enveloppe/deperdition.service.spec.js +141 -0
- package/features/engine/domain/enveloppe/espace_tampon/espace-tampon.service.spec.js +2 -1
- package/features/engine/domain/enveloppe/mur/deperdition-mur.service.spec.js +117 -1
- package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.spec.js +134 -1
- package/features/engine/domain/enveloppe/plancher_haut/deperdition-plancher-haut.service.spec.js +126 -1
- package/features/engine/domain/enveloppe/pont_thermique/deperdition-pont-thermique.service.spec.js +18 -1
- package/features/engine/domain/enveloppe/porte/deperdition-porte.service.spec.js +2 -1
- package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.spec.js +67 -1
- package/features/normalizer/domain/dpe-normalizer.service.js +1 -1
- package/features/normalizer/domain/dpe-normalizer.service.spec.js +53 -2
- package/ficheTechnique.spec.js +26 -0
- package/index.spec.js +54 -0
- package/package.json +5 -2
- package/preprocessor/index.js +20 -0
- package/preprocessor/index.spec.js +15 -0
- package/tv.spec.js +38 -0
- package/utils.js +43 -1
- package/utils.spec.js +518 -2
- package/output.js +0 -1
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Module purement calculatoire (aucune dépendance externe) : on teste
|
|
5
|
+
* directement les seuils de qualité et l'agrégation des déperditions.
|
|
6
|
+
*/
|
|
7
|
+
const { default: calc_qualite_isolation, qualite_isol } = await import(
|
|
8
|
+
'./2021_04_13_qualite_isolation.js'
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Qualité de l'isolation
|
|
13
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - annexe qualité d'isolation
|
|
14
|
+
*/
|
|
15
|
+
describe('qualite_isol - classement selon les seuils U', () => {
|
|
16
|
+
const seuils = [0.3, 0.45, 0.65];
|
|
17
|
+
|
|
18
|
+
test('déperdition ou surface nulle : U = 0 => très bonne (1)', () => {
|
|
19
|
+
expect(qualite_isol(0, 100, ...seuils)).toBe(1);
|
|
20
|
+
expect(qualite_isol(50, 0, ...seuils)).toBe(1);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('U strictement inférieur au seuil 1 : très bonne (1)', () => {
|
|
24
|
+
// U = 20 / 100 = 0.2 < 0.3
|
|
25
|
+
expect(qualite_isol(20, 100, ...seuils)).toBe(1);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('U dans [seuil1, seuil2[ : bonne (2)', () => {
|
|
29
|
+
// U = 0.4
|
|
30
|
+
expect(qualite_isol(40, 100, ...seuils)).toBe(2);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('U dans [seuil2, seuil3[ : moyenne (3)', () => {
|
|
34
|
+
// U = 0.5
|
|
35
|
+
expect(qualite_isol(50, 100, ...seuils)).toBe(3);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('U supérieur ou égal au seuil 3 : insuffisante (4)', () => {
|
|
39
|
+
// U = 0.9 >= 0.65
|
|
40
|
+
expect(qualite_isol(90, 100, ...seuils)).toBe(4);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('les bornes sont exclusives par le bas (U === seuil => classe suivante)', () => {
|
|
44
|
+
// U === seuil1 (0.3) => n'est pas < seuil1 => classe 2
|
|
45
|
+
expect(qualite_isol(30, 100, ...seuils)).toBe(2);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
/** Paroi générique surface/U. */
|
|
50
|
+
function mur({ adjacence = '1', surface, umur, b = 1 }) {
|
|
51
|
+
return {
|
|
52
|
+
donnee_entree: { enum_type_adjacence_id: adjacence, surface_paroi_opaque: surface },
|
|
53
|
+
donnee_intermediaire: { b, umur }
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function plancherBas({ adjacence = '1', surface, upb }) {
|
|
57
|
+
return {
|
|
58
|
+
donnee_entree: { enum_type_adjacence_id: adjacence, surface_paroi_opaque: surface },
|
|
59
|
+
donnee_intermediaire: { upb }
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function plancherHaut({ adjacence = '1', typePh = '12', description, surface, uph }) {
|
|
63
|
+
return {
|
|
64
|
+
donnee_entree: {
|
|
65
|
+
enum_type_adjacence_id: adjacence,
|
|
66
|
+
enum_type_plancher_haut_id: typePh,
|
|
67
|
+
description,
|
|
68
|
+
surface_paroi_opaque: surface
|
|
69
|
+
},
|
|
70
|
+
donnee_intermediaire: { uph }
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function baie({ surface, u, b = 1 }) {
|
|
74
|
+
return {
|
|
75
|
+
donnee_entree: { surface_totale_baie: surface, u_menuiserie: u },
|
|
76
|
+
donnee_intermediaire: { b, u_menuiserie: u }
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function porte({ surface, u, b = 1 }) {
|
|
80
|
+
return {
|
|
81
|
+
donnee_entree: { surface_porte: surface, uporte: u },
|
|
82
|
+
donnee_intermediaire: { b, uporte: u }
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function enveloppe({ murs = [], pb = [], ph = [], bv = [], portes = [] }) {
|
|
86
|
+
return {
|
|
87
|
+
mur_collection: { mur: murs },
|
|
88
|
+
plancher_haut_collection: { plancher_haut: ph },
|
|
89
|
+
plancher_bas_collection: { plancher_bas: pb },
|
|
90
|
+
baie_vitree_collection: { baie_vitree: bv },
|
|
91
|
+
porte_collection: { porte: portes }
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
describe('calc_qualite_isolation - agrégation et classement', () => {
|
|
96
|
+
test('cas de référence : ubat et classes de qualité', () => {
|
|
97
|
+
const env = enveloppe({
|
|
98
|
+
murs: [mur({ surface: 100, umur: 0.5 })],
|
|
99
|
+
pb: [plancherBas({ surface: 50, upb: 0.4 })],
|
|
100
|
+
ph: [plancherHaut({ surface: 30, uph: 0.2 })],
|
|
101
|
+
bv: [baie({ surface: 20, u: 2 })],
|
|
102
|
+
portes: [porte({ surface: 5, u: 2 })]
|
|
103
|
+
});
|
|
104
|
+
const dp = {
|
|
105
|
+
deperdition_mur: 50,
|
|
106
|
+
deperdition_plancher_bas: 20,
|
|
107
|
+
deperdition_plancher_haut: 6,
|
|
108
|
+
deperdition_baie_vitree: 40,
|
|
109
|
+
deperdition_porte: 10,
|
|
110
|
+
deperdition_pont_thermique: 10
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const ret = calc_qualite_isolation(env, dp);
|
|
114
|
+
|
|
115
|
+
// valeur de référence de régression : 136 / 205
|
|
116
|
+
expect(ret.ubat).toBeCloseTo(0.6634146341463415, 9);
|
|
117
|
+
expect(ret.qualite_isol_enveloppe).toBe(3);
|
|
118
|
+
expect(ret.qualite_isol_mur).toBe(3); // U = 0.5
|
|
119
|
+
expect(ret.qualite_isol_plancher_bas).toBe(2); // U = 0.4
|
|
120
|
+
expect(ret.qualite_isol_menuiserie).toBe(2); // U = 50/25 = 2
|
|
121
|
+
expect(ret.qualite_isol_plancher_haut_comble_amenage).toBe(2); // U = 0.2
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('les locaux non déperditifs (adjacence 22) sont exclus des murs et planchers bas', () => {
|
|
125
|
+
const env = enveloppe({
|
|
126
|
+
murs: [mur({ surface: 100, umur: 0.5 }), mur({ adjacence: '22', surface: 999, umur: 9 })],
|
|
127
|
+
pb: [
|
|
128
|
+
plancherBas({ surface: 50, upb: 0.4 }),
|
|
129
|
+
plancherBas({ adjacence: '22', surface: 999, upb: 9 })
|
|
130
|
+
]
|
|
131
|
+
});
|
|
132
|
+
const dp = {
|
|
133
|
+
deperdition_mur: 50,
|
|
134
|
+
deperdition_plancher_bas: 20,
|
|
135
|
+
deperdition_plancher_haut: 0,
|
|
136
|
+
deperdition_baie_vitree: 0,
|
|
137
|
+
deperdition_porte: 0,
|
|
138
|
+
deperdition_pont_thermique: 0
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const ret = calc_qualite_isolation(env, dp);
|
|
142
|
+
|
|
143
|
+
// surface déperditive = 100 + 50 = 150 ; ubat = 70 / 150
|
|
144
|
+
expect(ret.ubat).toBeCloseTo(70 / 150, 9);
|
|
145
|
+
expect(ret.qualite_isol_mur).toBe(3); // 0.5 seule paroi retenue
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('un mur avec coefficient b nul est exclu du calcul', () => {
|
|
149
|
+
const env = enveloppe({
|
|
150
|
+
murs: [mur({ surface: 100, umur: 0.5 }), mur({ surface: 200, umur: 9, b: 0 })]
|
|
151
|
+
});
|
|
152
|
+
const dp = {
|
|
153
|
+
deperdition_mur: 50,
|
|
154
|
+
deperdition_plancher_bas: 0,
|
|
155
|
+
deperdition_plancher_haut: 0,
|
|
156
|
+
deperdition_baie_vitree: 0,
|
|
157
|
+
deperdition_porte: 0,
|
|
158
|
+
deperdition_pont_thermique: 0
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const ret = calc_qualite_isolation(env, dp);
|
|
162
|
+
|
|
163
|
+
// seul le mur b>0 (surface 100) est compté
|
|
164
|
+
expect(ret.ubat).toBeCloseTo(0.5, 9);
|
|
165
|
+
expect(ret.qualite_isol_mur).toBe(3);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("plancher haut non adjacent à l'extérieur (adjacence !== 1) : classé comble perdu", () => {
|
|
169
|
+
const env = enveloppe({
|
|
170
|
+
ph: [plancherHaut({ adjacence: '5', surface: 40, uph: 0.1 })]
|
|
171
|
+
});
|
|
172
|
+
const dp = {
|
|
173
|
+
deperdition_mur: 0,
|
|
174
|
+
deperdition_plancher_bas: 0,
|
|
175
|
+
deperdition_plancher_haut: 4,
|
|
176
|
+
deperdition_baie_vitree: 0,
|
|
177
|
+
deperdition_porte: 0,
|
|
178
|
+
deperdition_pont_thermique: 0
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const ret = calc_qualite_isolation(env, dp);
|
|
182
|
+
|
|
183
|
+
// U = 4 / 40 = 0.1 < 0.15 => très bonne (1)
|
|
184
|
+
expect(ret.qualite_isol_plancher_haut_comble_perdu).toBe(1);
|
|
185
|
+
expect(ret.qualite_isol_plancher_haut_comble_amenage).toBeUndefined();
|
|
186
|
+
expect(ret.qualite_isol_plancher_haut_toit_terrasse).toBeUndefined();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('plancher haut extérieur sans marqueur combles aménagés : classé toit terrasse', () => {
|
|
190
|
+
const env = enveloppe({
|
|
191
|
+
ph: [
|
|
192
|
+
plancherHaut({
|
|
193
|
+
adjacence: '1',
|
|
194
|
+
typePh: '5',
|
|
195
|
+
description: 'dalle béton',
|
|
196
|
+
surface: 40,
|
|
197
|
+
uph: 0.28
|
|
198
|
+
})
|
|
199
|
+
]
|
|
200
|
+
});
|
|
201
|
+
const dp = {
|
|
202
|
+
deperdition_mur: 0,
|
|
203
|
+
deperdition_plancher_bas: 0,
|
|
204
|
+
deperdition_plancher_haut: 11.2,
|
|
205
|
+
deperdition_baie_vitree: 0,
|
|
206
|
+
deperdition_porte: 0,
|
|
207
|
+
deperdition_pont_thermique: 0
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const ret = calc_qualite_isolation(env, dp);
|
|
211
|
+
|
|
212
|
+
// U = 0.28 : seuils toit terrasse 0.25 / 0.3 / 0.35 => classe 2 (dans [0.25, 0.3[)
|
|
213
|
+
expect(ret.qualite_isol_plancher_haut_toit_terrasse).toBe(2);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test('description contenant « combles aménagés » : classé combles aménagés', () => {
|
|
217
|
+
const env = enveloppe({
|
|
218
|
+
ph: [
|
|
219
|
+
plancherHaut({
|
|
220
|
+
adjacence: '1',
|
|
221
|
+
typePh: '5',
|
|
222
|
+
description: 'Combles aménagés sous rampant',
|
|
223
|
+
surface: 30,
|
|
224
|
+
uph: 0.2
|
|
225
|
+
})
|
|
226
|
+
]
|
|
227
|
+
});
|
|
228
|
+
const dp = {
|
|
229
|
+
deperdition_mur: 0,
|
|
230
|
+
deperdition_plancher_bas: 0,
|
|
231
|
+
deperdition_plancher_haut: 6,
|
|
232
|
+
deperdition_baie_vitree: 0,
|
|
233
|
+
deperdition_porte: 0,
|
|
234
|
+
deperdition_pont_thermique: 0
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const ret = calc_qualite_isolation(env, dp);
|
|
238
|
+
|
|
239
|
+
expect(ret.qualite_isol_plancher_haut_comble_amenage).toBe(2);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test('collections sans tableau interne (undefined) : repli sur des listes vides', () => {
|
|
243
|
+
// Aucune des collections n'expose son tableau => branches `|| []` empruntées.
|
|
244
|
+
const env = {
|
|
245
|
+
mur_collection: {},
|
|
246
|
+
plancher_haut_collection: {},
|
|
247
|
+
plancher_bas_collection: {},
|
|
248
|
+
baie_vitree_collection: {},
|
|
249
|
+
porte_collection: {}
|
|
250
|
+
};
|
|
251
|
+
const dp = {
|
|
252
|
+
deperdition_mur: 0,
|
|
253
|
+
deperdition_plancher_bas: 0,
|
|
254
|
+
deperdition_plancher_haut: 0,
|
|
255
|
+
deperdition_baie_vitree: 0,
|
|
256
|
+
deperdition_porte: 0,
|
|
257
|
+
deperdition_pont_thermique: 0
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const ret = calc_qualite_isolation(env, dp);
|
|
261
|
+
|
|
262
|
+
expect(Number.isFinite(ret.ubat)).toBe(false);
|
|
263
|
+
expect(ret.qualite_isol_mur).toBe(1);
|
|
264
|
+
expect(ret.qualite_isol_plancher_haut_comble_amenage).toBeUndefined();
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test('collections vides : ubat non fini, seules les quatre qualités de base présentes', () => {
|
|
268
|
+
const env = enveloppe({});
|
|
269
|
+
const dp = {
|
|
270
|
+
deperdition_mur: 0,
|
|
271
|
+
deperdition_plancher_bas: 0,
|
|
272
|
+
deperdition_plancher_haut: 0,
|
|
273
|
+
deperdition_baie_vitree: 0,
|
|
274
|
+
deperdition_porte: 0,
|
|
275
|
+
deperdition_pont_thermique: 0
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const ret = calc_qualite_isolation(env, dp);
|
|
279
|
+
|
|
280
|
+
// 0 / 0 => NaN
|
|
281
|
+
expect(Number.isFinite(ret.ubat)).toBe(false);
|
|
282
|
+
expect(ret.qualite_isol_plancher_haut_comble_amenage).toBeUndefined();
|
|
283
|
+
expect(ret.qualite_isol_plancher_haut_toit_terrasse).toBeUndefined();
|
|
284
|
+
expect(ret.qualite_isol_plancher_haut_comble_perdu).toBeUndefined();
|
|
285
|
+
});
|
|
286
|
+
});
|
package/3.1_b.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import enums from './enums.js';
|
|
2
2
|
import { tv, requestInput, requestInputID, bug_for_bug_compat } from './utils.js';
|
|
3
3
|
|
|
4
|
-
function findRanges(inputNumber) {
|
|
4
|
+
export function findRanges(inputNumber) {
|
|
5
5
|
const ranges = [0.25, 0.5, 0.75, 1, 1.25, 2, 2.5, 3, 3.5, 4, 6, 8, 10, 25, 50];
|
|
6
6
|
const result = [];
|
|
7
7
|
|
package/3.1_b.spec.js
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Dépendances mockées pour isoler la logique de `3.1_b.js` :
|
|
5
|
+
* - `tv` : accès à la table de valeurs coef_reduction_deperdition (on contrôle la ligne retournée) ;
|
|
6
|
+
* - `requestInput` / `requestInputID` : simples passe-plats vers les données d'entrée ;
|
|
7
|
+
* - `bug_for_bug_compat` : désactivé pour isoler le comportement nominal ;
|
|
8
|
+
* - `enums` : mapping minimal nécessaire au calcul.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* `bug_for_bug_compat` est exposé via un accesseur adossé à un état hoisté afin de pouvoir
|
|
12
|
+
* l'activer sur les tests reproduisant les comportements bugués des DPE réels.
|
|
13
|
+
*/
|
|
14
|
+
const state = vi.hoisted(() => ({ bug: false }));
|
|
15
|
+
|
|
16
|
+
vi.mock('./utils.js', () => ({
|
|
17
|
+
tv: vi.fn(),
|
|
18
|
+
requestInputID: (de, du, field) => {
|
|
19
|
+
const enumName = `enum_${field}_id`;
|
|
20
|
+
du[enumName] = de[enumName];
|
|
21
|
+
return de[enumName];
|
|
22
|
+
},
|
|
23
|
+
requestInput: (de, du, field) => de[field],
|
|
24
|
+
get bug_for_bug_compat() {
|
|
25
|
+
return state.bug;
|
|
26
|
+
}
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
vi.mock('./enums.js', () => ({
|
|
30
|
+
default: {
|
|
31
|
+
zone_climatique: { 1: 'h1a', 2: 'h1b', 8: 'h3' },
|
|
32
|
+
type_adjacence: { 8: 'garage', 9: 'cellier', 10: 'espace tampon', 12: 'comble', 21: 'autres' },
|
|
33
|
+
cfg_isolation_lnc: { 1: 'local chauffé non accessible', 2: 'lc non isolé + lnc non isolé' }
|
|
34
|
+
}
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
const { default: b, findRanges } = await import('./3.1_b.js');
|
|
38
|
+
const { tv } = await import('./utils.js');
|
|
39
|
+
|
|
40
|
+
const TABLE = 'coef_reduction_deperdition';
|
|
41
|
+
|
|
42
|
+
let errorSpy;
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
tv.mockReset();
|
|
46
|
+
state.bug = false;
|
|
47
|
+
errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
afterEach(() => {
|
|
51
|
+
errorSpy.mockRestore();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 3.1 Coefficient de réduction des déperditions (b)
|
|
56
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §3.1
|
|
57
|
+
*/
|
|
58
|
+
describe('findRanges - encadrement du rapport surface_aiu / surface_aue', () => {
|
|
59
|
+
const ranges = [0.25, 0.5, 0.75, 1, 1.25, 2, 2.5, 3, 3.5, 4, 6, 8, 10, 25, 50];
|
|
60
|
+
|
|
61
|
+
test('valeur inférieure à la plus petite borne : borne inférieure nulle', () => {
|
|
62
|
+
expect(findRanges(0.1)).toEqual([null, 0.25]);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('valeur supérieure à la plus grande borne : borne supérieure nulle', () => {
|
|
66
|
+
expect(findRanges(60)).toEqual([50, null]);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('valeur strictement comprise entre deux bornes : encadrement [inf, sup]', () => {
|
|
70
|
+
expect(findRanges(0.3)).toEqual([0.25, 0.5]);
|
|
71
|
+
expect(findRanges(1.1)).toEqual([1, 1.25]);
|
|
72
|
+
expect(findRanges(9)).toEqual([8, 10]);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('valeur égale à une borne intermédiaire : encadrement [borne précédente, borne]', () => {
|
|
76
|
+
expect(findRanges(0.5)).toEqual([0.25, 0.5]);
|
|
77
|
+
expect(findRanges(1)).toEqual([0.75, 1]);
|
|
78
|
+
expect(findRanges(50)).toEqual([25, 50]);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('valeur égale à la première borne : un seul élément (pas de borne précédente)', () => {
|
|
82
|
+
// i === 0 : la boucle ne pousse pas de borne inférieure
|
|
83
|
+
expect(findRanges(0.25)).toEqual([0.25]);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('toutes les bornes exactes retournent un encadrement cohérent', () => {
|
|
87
|
+
ranges.forEach((value, index) => {
|
|
88
|
+
const result = findRanges(value);
|
|
89
|
+
if (index === 0) {
|
|
90
|
+
expect(result).toEqual([0.25]);
|
|
91
|
+
} else {
|
|
92
|
+
expect(result).toEqual([ranges[index - 1], value]);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('b - calcul du coefficient de réduction des déperditions', () => {
|
|
99
|
+
/**
|
|
100
|
+
* Local chauffé non accessible / non déperditif avec surface_aue nulle (cf. page 10) :
|
|
101
|
+
* b = 0 et aucun accès à la table de valeurs.
|
|
102
|
+
*/
|
|
103
|
+
test.each([
|
|
104
|
+
['8', 'garage'],
|
|
105
|
+
['9', 'cellier'],
|
|
106
|
+
['12', 'comble faiblement ventilé'],
|
|
107
|
+
['21', 'autres dépendances']
|
|
108
|
+
])(
|
|
109
|
+
'surface_aue nulle pour une adjacence de type %s (%s) : b = 0 sans accès table',
|
|
110
|
+
(adjacence) => {
|
|
111
|
+
const di = {};
|
|
112
|
+
const de = { enum_type_adjacence_id: adjacence, surface_aue: 0 };
|
|
113
|
+
b(di, de, {}, 1);
|
|
114
|
+
expect(di.b).toBe(0);
|
|
115
|
+
expect(tv).not.toHaveBeenCalled();
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Espace tampon solarisé (adjacence 10) : le matcher inclut la zone climatique (2 premiers
|
|
121
|
+
* caractères) et la configuration d'isolation du local non chauffé.
|
|
122
|
+
*/
|
|
123
|
+
test('espace tampon solarisé (adjacence 10) : matcher zone climatique + cfg isolation', () => {
|
|
124
|
+
tv.mockReturnValue({ b: '0.58', tv_coef_reduction_deperdition_id: '269' });
|
|
125
|
+
const di = {};
|
|
126
|
+
const de = { enum_type_adjacence_id: '10', enum_cfg_isolation_lnc_id: '7' };
|
|
127
|
+
|
|
128
|
+
b(di, de, {}, 1);
|
|
129
|
+
|
|
130
|
+
expect(tv).toHaveBeenCalledWith(TABLE, {
|
|
131
|
+
enum_type_adjacence_id: '10',
|
|
132
|
+
zone_climatique: 'h1',
|
|
133
|
+
enum_cfg_isolation_lnc_id: '7'
|
|
134
|
+
});
|
|
135
|
+
expect(di.b).toBe(0.58);
|
|
136
|
+
expect(de.tv_coef_reduction_deperdition_id).toBe(269);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Adjacence de type garage avec surface_aiu === surface_aue :
|
|
141
|
+
* bornes par défaut 0,75 < aiu/aue ≤ 1,00.
|
|
142
|
+
*/
|
|
143
|
+
test('garage (adjacence 8), surface_aiu === surface_aue : bornes par défaut', () => {
|
|
144
|
+
tv.mockReturnValue({ b: '0.7', tv_coef_reduction_deperdition_id: '100' });
|
|
145
|
+
const di = {};
|
|
146
|
+
const de = {
|
|
147
|
+
enum_type_adjacence_id: '8',
|
|
148
|
+
enum_cfg_isolation_lnc_id: '2',
|
|
149
|
+
surface_aiu: 20,
|
|
150
|
+
surface_aue: 20
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
b(di, de, {}, 1);
|
|
154
|
+
|
|
155
|
+
expect(tv).toHaveBeenCalledWith(TABLE, {
|
|
156
|
+
enum_type_adjacence_id: '8',
|
|
157
|
+
enum_cfg_isolation_lnc_id: '2',
|
|
158
|
+
aiu_aue_min: '0,75 <',
|
|
159
|
+
aiu_aue_max: '≤ 1,00'
|
|
160
|
+
});
|
|
161
|
+
expect(di.b).toBe(0.7);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Adjacence de type garage avec rapport surface_aiu / surface_aue = 0.5 :
|
|
166
|
+
* findRanges([0.25, 0.5]) => bornes '0,25 <' et '≤ 0,50'.
|
|
167
|
+
*/
|
|
168
|
+
test('garage (adjacence 8), rapport surface_aiu / surface_aue = 0.5 : bornes encadrantes', () => {
|
|
169
|
+
tv.mockReturnValue({ b: '0.8', tv_coef_reduction_deperdition_id: '101' });
|
|
170
|
+
const di = {};
|
|
171
|
+
const de = {
|
|
172
|
+
enum_type_adjacence_id: '8',
|
|
173
|
+
enum_cfg_isolation_lnc_id: '2',
|
|
174
|
+
surface_aiu: 10,
|
|
175
|
+
surface_aue: 20
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
b(di, de, {}, 1);
|
|
179
|
+
|
|
180
|
+
expect(tv).toHaveBeenCalledWith(TABLE, {
|
|
181
|
+
enum_type_adjacence_id: '8',
|
|
182
|
+
enum_cfg_isolation_lnc_id: '2',
|
|
183
|
+
aiu_aue_min: '0,25 <',
|
|
184
|
+
aiu_aue_max: '≤ 0,50'
|
|
185
|
+
});
|
|
186
|
+
expect(di.b).toBe(0.8);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('aucune valeur forfaitaire trouvée : di.b reste indéfini', () => {
|
|
190
|
+
tv.mockReturnValue(null);
|
|
191
|
+
const di = {};
|
|
192
|
+
const de = { enum_type_adjacence_id: '10', enum_cfg_isolation_lnc_id: '7' };
|
|
193
|
+
|
|
194
|
+
b(di, de, {}, 1);
|
|
195
|
+
|
|
196
|
+
expect(di.b).toBeUndefined();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Configuration "local chauffé non accessible" (cfg_isolation_lnc = 1) : le rapport aiu/aue
|
|
201
|
+
* n'est pas connu, l'adjacence est retirée du matcher.
|
|
202
|
+
*/
|
|
203
|
+
test('local chauffé non accessible (cfg 1) : adjacence retirée du matcher', () => {
|
|
204
|
+
tv.mockReturnValue({ b: '0.95', tv_coef_reduction_deperdition_id: '300' });
|
|
205
|
+
const di = {};
|
|
206
|
+
const de = {
|
|
207
|
+
enum_type_adjacence_id: '8',
|
|
208
|
+
enum_cfg_isolation_lnc_id: '1',
|
|
209
|
+
surface_aue: 20
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
b(di, de, {}, 1);
|
|
213
|
+
|
|
214
|
+
expect(tv).toHaveBeenCalledWith(TABLE, { enum_cfg_isolation_lnc_id: '1' });
|
|
215
|
+
expect(di.b).toBe(0.95);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Rapport surface_aiu / surface_aue égal exactement à la plus petite borne (0,25) :
|
|
220
|
+
* seule la borne supérieure aiu_aue_max est renseignée.
|
|
221
|
+
*/
|
|
222
|
+
test('rapport égal à la borne minimale (0,25) : seule aiu_aue_max est renseignée', () => {
|
|
223
|
+
tv.mockReturnValue({ b: '0.85', tv_coef_reduction_deperdition_id: '150' });
|
|
224
|
+
const di = {};
|
|
225
|
+
const de = {
|
|
226
|
+
enum_type_adjacence_id: '8',
|
|
227
|
+
enum_cfg_isolation_lnc_id: '2',
|
|
228
|
+
surface_aiu: 5,
|
|
229
|
+
surface_aue: 20 // rapport = 0,25
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
b(di, de, {}, 1);
|
|
233
|
+
|
|
234
|
+
expect(tv).toHaveBeenCalledWith(TABLE, {
|
|
235
|
+
enum_type_adjacence_id: '8',
|
|
236
|
+
enum_cfg_isolation_lnc_id: '2',
|
|
237
|
+
aiu_aue_max: '≤ 0,25'
|
|
238
|
+
});
|
|
239
|
+
expect(di.b).toBe(0.85);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Compatibilité bug : surface_aiu === surface_aue mais le DPE a renseigné des bornes aiu/aue
|
|
244
|
+
* via tv_coef_reduction_deperdition_id différentes des bornes par défaut. Ces bornes sont
|
|
245
|
+
* conservées (avec émission d'une erreur explicative).
|
|
246
|
+
*/
|
|
247
|
+
test('compat bug : bornes aiu/aue issues du DPE conservées malgré aiu === aue', () => {
|
|
248
|
+
state.bug = true;
|
|
249
|
+
tv.mockImplementation((table, matcher) => {
|
|
250
|
+
// 1er appel : lecture de la ligne référencée par tv_coef_reduction_deperdition_id
|
|
251
|
+
if (matcher.tv_coef_reduction_deperdition_id) {
|
|
252
|
+
return { aiu_aue_min: '0,50 <', aiu_aue_max: '≤ 0,75' };
|
|
253
|
+
}
|
|
254
|
+
// 2e appel : lecture de la valeur de b avec le matcher final
|
|
255
|
+
return { b: '0.8', tv_coef_reduction_deperdition_id: '200' };
|
|
256
|
+
});
|
|
257
|
+
const di = {};
|
|
258
|
+
const de = {
|
|
259
|
+
enum_type_adjacence_id: '8',
|
|
260
|
+
enum_cfg_isolation_lnc_id: '2',
|
|
261
|
+
surface_aiu: 20,
|
|
262
|
+
surface_aue: 20,
|
|
263
|
+
tv_coef_reduction_deperdition_id: 5,
|
|
264
|
+
description: 'paroi test'
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
b(di, de, {}, 1);
|
|
268
|
+
|
|
269
|
+
expect(tv).toHaveBeenLastCalledWith(TABLE, {
|
|
270
|
+
enum_type_adjacence_id: '8',
|
|
271
|
+
enum_cfg_isolation_lnc_id: '2',
|
|
272
|
+
aiu_aue_min: '0,50 <',
|
|
273
|
+
aiu_aue_max: '≤ 0,75'
|
|
274
|
+
});
|
|
275
|
+
expect(errorSpy).toHaveBeenCalled();
|
|
276
|
+
expect(di.b).toBe(0.8);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Compat bug : la ligne référencée ne fournit pas de bornes (valeurs absentes) : les bornes
|
|
281
|
+
* par défaut '0,75 <' et '≤ 1,00' sont conservées et aucune erreur n'est émise.
|
|
282
|
+
*/
|
|
283
|
+
test('compat bug : bornes absentes dans le DPE, bornes par défaut conservées', () => {
|
|
284
|
+
state.bug = true;
|
|
285
|
+
tv.mockImplementation((table, matcher) => {
|
|
286
|
+
if (matcher.tv_coef_reduction_deperdition_id) {
|
|
287
|
+
return { aiu_aue_min: undefined, aiu_aue_max: undefined };
|
|
288
|
+
}
|
|
289
|
+
return { b: '0.7', tv_coef_reduction_deperdition_id: '201' };
|
|
290
|
+
});
|
|
291
|
+
const di = {};
|
|
292
|
+
const de = {
|
|
293
|
+
enum_type_adjacence_id: '8',
|
|
294
|
+
enum_cfg_isolation_lnc_id: '2',
|
|
295
|
+
surface_aiu: 20,
|
|
296
|
+
surface_aue: 20,
|
|
297
|
+
tv_coef_reduction_deperdition_id: 5
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
b(di, de, {}, 1);
|
|
301
|
+
|
|
302
|
+
expect(tv).toHaveBeenLastCalledWith(TABLE, {
|
|
303
|
+
enum_type_adjacence_id: '8',
|
|
304
|
+
enum_cfg_isolation_lnc_id: '2',
|
|
305
|
+
aiu_aue_min: '0,75 <',
|
|
306
|
+
aiu_aue_max: '≤ 1,00'
|
|
307
|
+
});
|
|
308
|
+
expect(errorSpy).not.toHaveBeenCalled();
|
|
309
|
+
expect(di.b).toBe(0.7);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Compat bug : aucune ligne référencée retournée (tv_coef_reduction_deperdition_id présent
|
|
314
|
+
* mais table vide) : les bornes par défaut sont conservées.
|
|
315
|
+
*/
|
|
316
|
+
test('compat bug : aucune ligne référencée, bornes par défaut conservées', () => {
|
|
317
|
+
state.bug = true;
|
|
318
|
+
tv.mockImplementation((table, matcher) => {
|
|
319
|
+
if (matcher.tv_coef_reduction_deperdition_id) return null;
|
|
320
|
+
return { b: '0.72', tv_coef_reduction_deperdition_id: '202' };
|
|
321
|
+
});
|
|
322
|
+
const di = {};
|
|
323
|
+
const de = {
|
|
324
|
+
enum_type_adjacence_id: '8',
|
|
325
|
+
enum_cfg_isolation_lnc_id: '2',
|
|
326
|
+
surface_aiu: 20,
|
|
327
|
+
surface_aue: 20,
|
|
328
|
+
tv_coef_reduction_deperdition_id: 5
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
b(di, de, {}, 1);
|
|
332
|
+
|
|
333
|
+
expect(tv).toHaveBeenLastCalledWith(TABLE, {
|
|
334
|
+
enum_type_adjacence_id: '8',
|
|
335
|
+
enum_cfg_isolation_lnc_id: '2',
|
|
336
|
+
aiu_aue_min: '0,75 <',
|
|
337
|
+
aiu_aue_max: '≤ 1,00'
|
|
338
|
+
});
|
|
339
|
+
expect(di.b).toBe(0.72);
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Compat bug activée mais tv_coef_reduction_deperdition_id absent : le bloc de reprise n'est
|
|
344
|
+
* pas exécuté, les bornes par défaut sont utilisées directement.
|
|
345
|
+
*/
|
|
346
|
+
test('compat bug sans tv_coef_reduction_deperdition_id : bornes par défaut', () => {
|
|
347
|
+
state.bug = true;
|
|
348
|
+
tv.mockReturnValue({ b: '0.7', tv_coef_reduction_deperdition_id: '203' });
|
|
349
|
+
const di = {};
|
|
350
|
+
const de = {
|
|
351
|
+
enum_type_adjacence_id: '8',
|
|
352
|
+
enum_cfg_isolation_lnc_id: '2',
|
|
353
|
+
surface_aiu: 20,
|
|
354
|
+
surface_aue: 20
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
b(di, de, {}, 1);
|
|
358
|
+
|
|
359
|
+
expect(tv).toHaveBeenCalledWith(TABLE, {
|
|
360
|
+
enum_type_adjacence_id: '8',
|
|
361
|
+
enum_cfg_isolation_lnc_id: '2',
|
|
362
|
+
aiu_aue_min: '0,75 <',
|
|
363
|
+
aiu_aue_max: '≤ 1,00'
|
|
364
|
+
});
|
|
365
|
+
expect(di.b).toBe(0.7);
|
|
366
|
+
});
|
|
367
|
+
});
|