@open3cl/engine 1.11.1 → 1.11.3
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_ecs.spec.js +17 -1
- package/11_rdim_ecs_individuelle_unique.js +78 -0
- package/11_rdim_ecs_individuelle_unique.spec.js +136 -0
- package/13.2_generateur_combustion.spec.js +24 -1
- package/13.2_generateur_combustion_bouilleur.spec.js +8 -0
- package/13.2_generateur_combustion_ch.spec.js +41 -0
- package/14_generateur_ecs.spec.js +72 -0
- package/15_conso_aux.spec.js +20 -0
- package/3.1_b.spec.js +23 -0
- package/3.2.1_mur.spec.js +99 -0
- package/3.2.3_plancher_haut.spec.js +29 -0
- package/3.3_baie_vitree.spec.js +10 -0
- package/3.4_pont_thermique.spec.js +27 -0
- package/4_ventilation.spec.js +12 -0
- package/7_inertie.spec.js +10 -0
- package/9_besoin_ch.spec.js +4 -0
- package/9_conso_ch.js +1 -1
- package/9_emetteur_ch.spec.js +3 -1
- package/9_generateur_ch.spec.js +11 -0
- package/README.md +13 -13
- package/apport_et_besoin.spec.js +5 -1
- package/core/assets/domain/add-additionnal-ue-values-tables.js +0 -19
- package/core/assets/domain/add-additionnal-ue-values-tables.spec.js +3 -4
- package/core/assets/domain/synchronize-dpe-ges-limit-values-tables.spec.js +25 -0
- package/core/assets/domain/synchronize-valeur-tables.spec.js +43 -0
- package/core/util/logger/log-service.spec.js +11 -0
- package/dpe-sanitizer.service.spec.js +9 -0
- package/engine.js +10 -2
- package/engine.spec.js +66 -5
- package/features/dpe/domain/models/baie-ets.model.ts +8 -11
- package/features/dpe/domain/models/baie-vitree.model.ts +24 -96
- package/features/dpe/domain/models/climatisation.model.ts +13 -23
- package/features/dpe/domain/models/dpe-xsd.generated.ts +2797 -0
- package/features/dpe/domain/models/dpe.model.ts +102 -171
- package/features/dpe/domain/models/ets.model.ts +10 -18
- package/features/dpe/domain/models/installation-chauffage.model.ts +49 -102
- package/features/dpe/domain/models/installation-ecs.model.ts +27 -77
- package/features/dpe/domain/models/mur.model.ts +10 -38
- package/features/dpe/domain/models/plancher.model.ts +22 -63
- package/features/dpe/domain/models/pont-thermique.model.ts +10 -20
- package/features/dpe/domain/models/porte.model.ts +10 -30
- package/features/dpe/domain/models/production-elec-enr.model.ts +13 -27
- package/features/dpe/domain/models/sortie.model.ts +32 -176
- package/features/dpe/domain/models/ventilation.model.ts +15 -25
- package/features/normalizer/domain/dpe-normalizer.service.spec.js +5 -3
- package/package.json +27 -45
- package/utils.js +1 -2
- package/utils.spec.js +16 -0
package/11_ecs.spec.js
CHANGED
|
@@ -13,7 +13,8 @@ vi.mock('./enums.js', () => ({
|
|
|
13
13
|
type_installation: {
|
|
14
14
|
1: 'installation individuelle',
|
|
15
15
|
2: 'installation collective',
|
|
16
|
-
3: 'installation collective multi-bâtiment'
|
|
16
|
+
3: 'installation collective multi-bâtiment',
|
|
17
|
+
4: 'installation de type inconnu'
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
20
|
}));
|
|
@@ -566,6 +567,21 @@ describe('calc_ecs - rendement de distribution', () => {
|
|
|
566
567
|
});
|
|
567
568
|
});
|
|
568
569
|
|
|
570
|
+
test('type d’installation ni individuelle ni collective : aucun critère de configuration', () => {
|
|
571
|
+
const de = { enum_type_installation_id: '4' };
|
|
572
|
+
const ecs = makeEcs(de, [
|
|
573
|
+
{
|
|
574
|
+
donnee_entree: { position_volume_chauffe: 1 },
|
|
575
|
+
donnee_intermediaire: { conso_ecs: 0, conso_ecs_depensier: 0 }
|
|
576
|
+
}
|
|
577
|
+
]);
|
|
578
|
+
calc_ecs({}, ecs, 100, 200, 1, 'ca1', 'h1a', 'maison', false, null, null, false);
|
|
579
|
+
|
|
580
|
+
// Le matcher reste vide : ni configuration_logement ni type_reseau_collectif
|
|
581
|
+
expect(tv).toHaveBeenCalledWith('rendement_distribution_ecs', {});
|
|
582
|
+
expect(requestInput).not.toHaveBeenCalled();
|
|
583
|
+
});
|
|
584
|
+
|
|
569
585
|
test('aucune ligne forfaitaire trouvée : rendement de distribution non renseigné', () => {
|
|
570
586
|
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
571
587
|
vi.mocked(tv).mockReturnValue(undefined);
|
|
@@ -60,3 +60,81 @@ export function reconstituerRdimEcsIndividuelleUnique(th, instal_ecs, surfaceHab
|
|
|
60
60
|
instal_ecs[0].donnee_entree.rdim_reconstitue = true;
|
|
61
61
|
return true;
|
|
62
62
|
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* DPE immeuble avec plusieurs installations ECS individuelles, chacune décrite pour un logement type.
|
|
66
|
+
*
|
|
67
|
+
* Même convention d'export que l'installation unique (3.0.x, 2.0.x, 1.1.x) : pour chaque
|
|
68
|
+
* installation i, `surface_habitable` = Sh_i d'un logement type et `nombre_logement` = N_i, sans
|
|
69
|
+
* `rdim`, avec Σ Sh_i × N_i = SH. Tribu traite chaque installation indépendamment avec Rdim_i = N_i :
|
|
70
|
+
* Becs_i = Becs_immeuble × Sh_i × Rdim_i / SH / Rdim_i, puis Cecs = Σ Cecs_i × Rdim_i. L'export ADEME
|
|
71
|
+
* le confirme : besoin_ecs_i = Becs_immeuble × Sh_i / SH et conso_ecs = Σ conso_ecs_i × N_i.
|
|
72
|
+
*
|
|
73
|
+
* Sans rdim, Σ Sh_i ≠ SH : Open3CL se replie sur Becs_immeuble / nombre_appartement par
|
|
74
|
+
* installation et ne remultiplie pas la conso par N_i.
|
|
75
|
+
*
|
|
76
|
+
* Gardes :
|
|
77
|
+
* - Σ Sh_i × N_i ≈ SH (TOLERANCE_SURFACE_LOGEMENT_TYPE) : exclut les exports où Sh_i est déjà
|
|
78
|
+
* la surface totale desservie (Σ Sh_i = SH, ex. N_i fractionnaires) ;
|
|
79
|
+
* - au moins un N_i > 1 : sinon rdim = 1 partout, sans effet (LICIEL, nombre_logement = 1).
|
|
80
|
+
*
|
|
81
|
+
* @see Methode_de_calcul_3CL_DPE_2021-338.pdf - § 11.4 et § 17.1.3.2
|
|
82
|
+
* @see Tribu : Calcul_batiment.cs l. 581-588 (boucle par installation, Ratio = Sh·Rdim/SH,
|
|
83
|
+
* Becs / Rdim), l. 1660-1662 (Cecs_i / Rdim puis × Rdim) ; Calcul_installation_ECS.cs l. 75
|
|
84
|
+
* @see Guide Tribu TE-211208 « Particularités d'une répétition de systèmes d'ECS individuel dans un
|
|
85
|
+
* immeuble collectif » (Sh_ecs = surface d'un logement, Rdim = nombre d'installations identiques)
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Rdim implicites des installations ECS individuelles multiples d'un immeuble (dans l'ordre des
|
|
90
|
+
* installations), ou null si non concernées.
|
|
91
|
+
*
|
|
92
|
+
* @param th {string} type de bâtiment (maison, appartement, immeuble)
|
|
93
|
+
* @param instal_ecs {Array} installation_ecs
|
|
94
|
+
* @param surfaceHabitableImmeuble {number|string} surface habitable de l'immeuble (SH)
|
|
95
|
+
* @return {number[]|null}
|
|
96
|
+
*/
|
|
97
|
+
export function rdimsImplicitesEcsIndividuellesMultiples(th, instal_ecs, surfaceHabitableImmeuble) {
|
|
98
|
+
if (th !== 'immeuble' || !Array.isArray(instal_ecs) || instal_ecs.length < 2) return null;
|
|
99
|
+
|
|
100
|
+
const des = instal_ecs.map((inst) => inst?.donnee_entree);
|
|
101
|
+
const concernees = des.every(
|
|
102
|
+
(de) => de && String(de.enum_type_installation_id) === '1' && !de.rdim
|
|
103
|
+
);
|
|
104
|
+
if (!concernees) return null;
|
|
105
|
+
|
|
106
|
+
const SH = Number(surfaceHabitableImmeuble);
|
|
107
|
+
if (!(SH > 0)) return null;
|
|
108
|
+
|
|
109
|
+
const surfaces = des.map((de) => Number(de.surface_habitable));
|
|
110
|
+
const rdims = des.map((de) => Number(de.nombre_logement) || 1);
|
|
111
|
+
if (!surfaces.every((sh) => sh > 0) || !rdims.every((n) => n > 0)) return null;
|
|
112
|
+
if (!rdims.some((n) => n > 1)) return null;
|
|
113
|
+
|
|
114
|
+
const sommeShN = surfaces.reduce((acc, sh, i) => acc + sh * rdims[i], 0);
|
|
115
|
+
return Math.abs(sommeShN - SH) <= TOLERANCE_SURFACE_LOGEMENT_TYPE * SH ? rdims : null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Reconstitue le rdim des installations ECS individuelles multiples d'un immeuble décrites pour un
|
|
120
|
+
* logement type. Chaque installation reçoit `rdim` = nombre_logement (1 si absent) et le flag
|
|
121
|
+
* `rdim_reconstitue`.
|
|
122
|
+
*
|
|
123
|
+
* @param th {string} type de bâtiment
|
|
124
|
+
* @param instal_ecs {Array} installation_ecs
|
|
125
|
+
* @param surfaceHabitableImmeuble {number|string} surface habitable de l'immeuble (SH)
|
|
126
|
+
* @return {boolean} true si les rdim ont été reconstitués
|
|
127
|
+
*/
|
|
128
|
+
export function reconstituerRdimEcsIndividuellesMultiples(
|
|
129
|
+
th,
|
|
130
|
+
instal_ecs,
|
|
131
|
+
surfaceHabitableImmeuble
|
|
132
|
+
) {
|
|
133
|
+
const rdims = rdimsImplicitesEcsIndividuellesMultiples(th, instal_ecs, surfaceHabitableImmeuble);
|
|
134
|
+
if (rdims === null) return false;
|
|
135
|
+
instal_ecs.forEach((inst, i) => {
|
|
136
|
+
inst.donnee_entree.rdim = rdims[i];
|
|
137
|
+
inst.donnee_entree.rdim_reconstitue = true;
|
|
138
|
+
});
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { describe, expect, test } from 'vitest';
|
|
2
2
|
import {
|
|
3
3
|
rdimImpliciteEcsIndividuelleUnique,
|
|
4
|
+
rdimsImplicitesEcsIndividuellesMultiples,
|
|
4
5
|
reconstituerRdimEcsIndividuelleUnique,
|
|
6
|
+
reconstituerRdimEcsIndividuellesMultiples,
|
|
5
7
|
TOLERANCE_SURFACE_LOGEMENT_TYPE
|
|
6
8
|
} from './11_rdim_ecs_individuelle_unique.js';
|
|
7
9
|
|
|
@@ -111,3 +113,137 @@ describe("Rdim de l'installation ECS individuelle unique d'un immeuble", () => {
|
|
|
111
113
|
});
|
|
112
114
|
});
|
|
113
115
|
});
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @see Methode_de_calcul_3CL_DPE_2021-338.pdf - § 11.4 et § 17.1.3.2
|
|
119
|
+
* @see Tribu : Calcul_batiment.cs l. 581-588 et l. 1660-1662 ; Calcul_installation_ECS.cs l. 75
|
|
120
|
+
*/
|
|
121
|
+
describe("Rdim des installations ECS individuelles multiples d'un immeuble", () => {
|
|
122
|
+
/** Installation ECS individuelle décrite pour un logement type */
|
|
123
|
+
const inst = (surface_habitable, nombre_logement, de = {}) => ({
|
|
124
|
+
donnee_entree: { enum_type_installation_id: '1', surface_habitable, nombre_logement, ...de }
|
|
125
|
+
});
|
|
126
|
+
/** Cas type 2244E3039157K : 48,82 × 2 + 78,09 × 6 = 566,18 ≈ SH = 566,16 */
|
|
127
|
+
const ecs = () => [inst(48.82, 2), inst(78.09, 6)];
|
|
128
|
+
|
|
129
|
+
describe('rdimsImplicitesEcsIndividuellesMultiples', () => {
|
|
130
|
+
test('installations individuelles, Σ Sh × N = SH, sans rdim : rdim_i = nombre_logement_i', () => {
|
|
131
|
+
expect(rdimsImplicitesEcsIndividuellesMultiples('immeuble', ecs(), 566.16)).toEqual([2, 6]);
|
|
132
|
+
expect(
|
|
133
|
+
rdimsImplicitesEcsIndividuellesMultiples(
|
|
134
|
+
'immeuble',
|
|
135
|
+
[
|
|
136
|
+
inst('48.82', '2', { enum_type_installation_id: 1 }),
|
|
137
|
+
inst('78.09', '6', { enum_type_installation_id: 1 })
|
|
138
|
+
],
|
|
139
|
+
'566.16'
|
|
140
|
+
)
|
|
141
|
+
).toEqual([2, 6]);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('nombre_logement absent sur une installation : rdim = 1 pour celle-ci', () => {
|
|
145
|
+
// 100 × 4 + 50 × 1 = 450
|
|
146
|
+
expect(
|
|
147
|
+
rdimsImplicitesEcsIndividuellesMultiples(
|
|
148
|
+
'immeuble',
|
|
149
|
+
[inst(100, 4), inst(50, undefined)],
|
|
150
|
+
450
|
|
151
|
+
)
|
|
152
|
+
).toEqual([4, 1]);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('tolérance de 5 % sur Σ Sh × N ≈ SH', () => {
|
|
156
|
+
// Σ = 100 × 4 + 50 × 2 = 500 ; |500 − SH| ≤ 0,05 × SH ⇔ 476,19 ≤ SH ≤ 526,31
|
|
157
|
+
const deux = () => [inst(100, 4), inst(50, 2)];
|
|
158
|
+
expect(rdimsImplicitesEcsIndividuellesMultiples('immeuble', deux(), 526)).toEqual([4, 2]);
|
|
159
|
+
expect(rdimsImplicitesEcsIndividuellesMultiples('immeuble', deux(), 477)).toEqual([4, 2]);
|
|
160
|
+
expect(rdimsImplicitesEcsIndividuellesMultiples('immeuble', deux(), 527)).toBeNull();
|
|
161
|
+
expect(rdimsImplicitesEcsIndividuellesMultiples('immeuble', deux(), 476)).toBeNull();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test('surfaces exportées comme surface totale desservie (Σ Sh = SH) : non concerné', () => {
|
|
165
|
+
// Cas type 2224E1374434V : 74,4 × 3 + 31 × 1 = 254,2 ≠ SH = 105,4 = Σ Sh
|
|
166
|
+
expect(
|
|
167
|
+
rdimsImplicitesEcsIndividuellesMultiples('immeuble', [inst(74.4, 3), inst(31, 1)], 105.4)
|
|
168
|
+
).toBeNull();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test('tous les nombre_logement à 1 (LICIEL) : non concerné', () => {
|
|
172
|
+
expect(
|
|
173
|
+
rdimsImplicitesEcsIndividuellesMultiples('immeuble', [inst(60, 1), inst(40, 1)], 100)
|
|
174
|
+
).toBeNull();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('un rdim exporté sur une installation : on conserve la saisie', () => {
|
|
178
|
+
expect(
|
|
179
|
+
rdimsImplicitesEcsIndividuellesMultiples(
|
|
180
|
+
'immeuble',
|
|
181
|
+
[inst(48.82, 2, { rdim: 2 }), inst(78.09, 6)],
|
|
182
|
+
566.16
|
|
183
|
+
)
|
|
184
|
+
).toBeNull();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test('une installation collective : non concerné', () => {
|
|
188
|
+
expect(
|
|
189
|
+
rdimsImplicitesEcsIndividuellesMultiples(
|
|
190
|
+
'immeuble',
|
|
191
|
+
[inst(48.82, 2), inst(78.09, 6, { enum_type_installation_id: '2' })],
|
|
192
|
+
566.16
|
|
193
|
+
)
|
|
194
|
+
).toBeNull();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test('maison ou appartement : non concerné', () => {
|
|
198
|
+
expect(rdimsImplicitesEcsIndividuellesMultiples('maison', ecs(), 566.16)).toBeNull();
|
|
199
|
+
expect(rdimsImplicitesEcsIndividuellesMultiples('appartement', ecs(), 566.16)).toBeNull();
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test('installation unique : non concerné (cf. rdimImpliciteEcsIndividuelleUnique)', () => {
|
|
203
|
+
expect(rdimsImplicitesEcsIndividuellesMultiples('immeuble', [inst(64, 10)], 640)).toBeNull();
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test('données manquantes ou invalides : non concerné', () => {
|
|
207
|
+
expect(rdimsImplicitesEcsIndividuellesMultiples('immeuble', undefined, 566.16)).toBeNull();
|
|
208
|
+
expect(rdimsImplicitesEcsIndividuellesMultiples('immeuble', [], 566.16)).toBeNull();
|
|
209
|
+
expect(
|
|
210
|
+
rdimsImplicitesEcsIndividuellesMultiples('immeuble', [inst(48.82, 2), {}], 566.16)
|
|
211
|
+
).toBeNull();
|
|
212
|
+
expect(
|
|
213
|
+
rdimsImplicitesEcsIndividuellesMultiples('immeuble', [inst(48.82, 2), null], 566.16)
|
|
214
|
+
).toBeNull();
|
|
215
|
+
expect(rdimsImplicitesEcsIndividuellesMultiples('immeuble', ecs(), undefined)).toBeNull();
|
|
216
|
+
expect(rdimsImplicitesEcsIndividuellesMultiples('immeuble', ecs(), 0)).toBeNull();
|
|
217
|
+
expect(
|
|
218
|
+
rdimsImplicitesEcsIndividuellesMultiples(
|
|
219
|
+
'immeuble',
|
|
220
|
+
[inst(48.82, 2), inst(undefined, 6)],
|
|
221
|
+
566.16
|
|
222
|
+
)
|
|
223
|
+
).toBeNull();
|
|
224
|
+
expect(
|
|
225
|
+
rdimsImplicitesEcsIndividuellesMultiples('immeuble', [inst(100, 4), inst(50, -2)], 300)
|
|
226
|
+
).toBeNull();
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
describe('reconstituerRdimEcsIndividuellesMultiples', () => {
|
|
231
|
+
test('installations concernées : rdim et flag positionnés sur chacune', () => {
|
|
232
|
+
const installations = ecs();
|
|
233
|
+
expect(reconstituerRdimEcsIndividuellesMultiples('immeuble', installations, 566.16)).toBe(
|
|
234
|
+
true
|
|
235
|
+
);
|
|
236
|
+
expect(installations[0].donnee_entree).toMatchObject({ rdim: 2, rdim_reconstitue: true });
|
|
237
|
+
expect(installations[1].donnee_entree).toMatchObject({ rdim: 6, rdim_reconstitue: true });
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test('installations non concernées : inchangées', () => {
|
|
241
|
+
const installations = [inst(60, 1), inst(40, 1)];
|
|
242
|
+
expect(reconstituerRdimEcsIndividuellesMultiples('immeuble', installations, 100)).toBe(false);
|
|
243
|
+
installations.forEach((i) => {
|
|
244
|
+
expect(i.donnee_entree.rdim).toBeUndefined();
|
|
245
|
+
expect(i.donnee_entree.rdim_reconstitue).toBeUndefined();
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
});
|
|
@@ -28,7 +28,8 @@ vi.mock('./enums.js', () => ({
|
|
|
28
28
|
type_generateur_ch: {
|
|
29
29
|
89: 'chaudière gaz standard 2001-2015',
|
|
30
30
|
X: 'chaudière fioul',
|
|
31
|
-
Y: 'chaudière gaz'
|
|
31
|
+
Y: 'chaudière gaz',
|
|
32
|
+
Z: 'générateur sans équivalent ECS'
|
|
32
33
|
},
|
|
33
34
|
type_generateur_ecs: {
|
|
34
35
|
50: 'chaudière gaz standard',
|
|
@@ -563,6 +564,28 @@ describe('tv_generateur_combustion - génération mixte (checkEcsVsChauffageForM
|
|
|
563
564
|
errSpy.mockRestore();
|
|
564
565
|
});
|
|
565
566
|
|
|
567
|
+
test('type CH sans équivalent ECS : type de générateur ECS conservé sans message d’alignement', () => {
|
|
568
|
+
state.bug = true;
|
|
569
|
+
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
570
|
+
tv.mockReturnValue({ ...ROW_DEFAUT });
|
|
571
|
+
const di = { pn: 20000 };
|
|
572
|
+
// CH mixte 'Z' : aucun type ECS portant le même libellé -> pas d'alignement possible
|
|
573
|
+
const de = {
|
|
574
|
+
enum_type_generateur_ecs_id: 'A',
|
|
575
|
+
enum_usage_generateur_id: '3',
|
|
576
|
+
tv_generateur_combustion_id: '42',
|
|
577
|
+
ratio_virtualisation: 1,
|
|
578
|
+
presence_ventouse: 0
|
|
579
|
+
};
|
|
580
|
+
tv_generateur_combustion(dpeAvecChMixte('Z'), di, de, 'ecs', 200, -9, 1);
|
|
581
|
+
expect(tv).toHaveBeenCalledWith(
|
|
582
|
+
'generateur_combustion',
|
|
583
|
+
expect.objectContaining({ enum_type_generateur_ecs_id: 'A' })
|
|
584
|
+
);
|
|
585
|
+
expect(errSpy).not.toHaveBeenCalled();
|
|
586
|
+
errSpy.mockRestore();
|
|
587
|
+
});
|
|
588
|
+
|
|
566
589
|
test('types ECS et CH identiques : type de générateur ECS conservé', () => {
|
|
567
590
|
state.bug = true;
|
|
568
591
|
tv.mockReturnValue({ ...ROW_DEFAUT });
|
|
@@ -58,6 +58,14 @@ describe('updateGenerateurBouilleurs - substitution par une chaudière bois équ
|
|
|
58
58
|
expect(de.enum_type_generateur_ch_id).toBe(attendu);
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
+
test('année numérique antérieure à 1948 : aucune période correspondante, valeur par défaut conservée', () => {
|
|
62
|
+
getFicheTechnique.mockReturnValue({ valeur: '1900' });
|
|
63
|
+
const de = { enum_type_generateur_ch_id: '48' };
|
|
64
|
+
updateGenerateurBouilleurs({}, de, 'ch');
|
|
65
|
+
// Aucun seuil atteint : on garde la chaudière bois par défaut 2004-2012 (values[2004])
|
|
66
|
+
expect(de.enum_type_generateur_ch_id).toBe('58');
|
|
67
|
+
});
|
|
68
|
+
|
|
61
69
|
test('type ECS (granulés 115) : utilise la table ECS des chaudières bois', () => {
|
|
62
70
|
getFicheTechnique.mockReturnValue({ valeur: '2016' });
|
|
63
71
|
const de = { enum_type_generateur_ecs_id: '115' };
|
|
@@ -173,6 +173,27 @@ describe('tv_temp_fonc_30_100 - températures de fonctionnement', () => {
|
|
|
173
173
|
expect(di.temp_fonc_100).toBe(75);
|
|
174
174
|
});
|
|
175
175
|
|
|
176
|
+
test('une température à 100 % plus basse issue d’un second émetteur ne remplace pas la valeur retenue', () => {
|
|
177
|
+
tv.mockImplementation((table, matcher) => {
|
|
178
|
+
if (table !== 'temp_fonc_100') return null;
|
|
179
|
+
// Le premier émetteur renvoie 75, le second 60 (plus basse)
|
|
180
|
+
return matcher.enum_temp_distribution_ch_id === '3'
|
|
181
|
+
? { tv_temp_fonc_100_id: 'a', temp_fonc_100: '75' }
|
|
182
|
+
: { tv_temp_fonc_100_id: 'b', temp_fonc_100: '60' };
|
|
183
|
+
});
|
|
184
|
+
const di = {};
|
|
185
|
+
const de = { enum_type_generateur_ch_id: '89' };
|
|
186
|
+
tv_temp_fonc_30_100(
|
|
187
|
+
di,
|
|
188
|
+
de,
|
|
189
|
+
{},
|
|
190
|
+
[emetteur(), emetteur({ enum_temp_distribution_ch_id: '4' })],
|
|
191
|
+
1975
|
|
192
|
+
);
|
|
193
|
+
expect(di.temp_fonc_100).toBe(75);
|
|
194
|
+
expect(de.tv_temp_fonc_100_id).toBe('a');
|
|
195
|
+
});
|
|
196
|
+
|
|
176
197
|
test('aucune ligne forfaitaire trouvée : erreurs signalées pour temp_fonc_30 et temp_fonc_100', () => {
|
|
177
198
|
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
178
199
|
tv.mockReturnValue(null);
|
|
@@ -709,6 +730,26 @@ describe('calc_generateur_combustion_ch - rendement de génération', () => {
|
|
|
709
730
|
warnSpy.mockRestore();
|
|
710
731
|
});
|
|
711
732
|
|
|
733
|
+
test('bug_for_bug_compat : qp0 déjà exprimé en W (>= 1) conservé sans correction', () => {
|
|
734
|
+
state.bug = true;
|
|
735
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
736
|
+
const di = { pn: 20000, rpn: 0.9, rpint: 0.85, qp0: 200, temp_fonc_30: 40, temp_fonc_100: 70 };
|
|
737
|
+
const de = {
|
|
738
|
+
enum_type_generateur_ch_id: '89',
|
|
739
|
+
enum_type_energie_id: '2',
|
|
740
|
+
type_energie: 'gaz naturel',
|
|
741
|
+
presence_regulation_combustion: false,
|
|
742
|
+
description: 't'
|
|
743
|
+
};
|
|
744
|
+
calc_generateur_combustion_ch({}, di, de, { cdimref: 0.8, cdimrefDep: 0.6 });
|
|
745
|
+
// qp0 >= 1 -> pas de multiplication par 1000, pas d'avertissement
|
|
746
|
+
expect(di.qp0).toBe(200);
|
|
747
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
748
|
+
// même résultat que la correction d'un qp0 de 0,2 kW (référence de régression ci-dessus)
|
|
749
|
+
expect(di.rg).toBeCloseTo(0.8557877790761849, 9);
|
|
750
|
+
warnSpy.mockRestore();
|
|
751
|
+
});
|
|
752
|
+
|
|
712
753
|
test('la présence de régulation modifie le rendement (tf30 vs tf100)', () => {
|
|
713
754
|
const base = () => ({
|
|
714
755
|
pn: 25000,
|
|
@@ -559,6 +559,23 @@ describe('calc_gen_ecs - consommation par générateur', () => {
|
|
|
559
559
|
expect(g.donnee_intermediaire.Qgw).toBe(0);
|
|
560
560
|
});
|
|
561
561
|
|
|
562
|
+
test('installation collective : volume de la fiche non numérique ignoré, volume saisi conservé', () => {
|
|
563
|
+
// « ba » (texte avant le premier « l ») n'est pas un nombre -> VsFiche = NaN, rejeté
|
|
564
|
+
vi.mocked(getFicheTechnique).mockReturnValue({ valeur: 'ballon' });
|
|
565
|
+
ecs_de.enum_type_installation_id = '2';
|
|
566
|
+
ecs_de.ratio_virtualisation = 1;
|
|
567
|
+
const g = gen({
|
|
568
|
+
type_energie: 'gaz',
|
|
569
|
+
enum_type_generateur_ecs_id: 'autre',
|
|
570
|
+
type_stockage_ecs: 'ballon',
|
|
571
|
+
volume_stockage: 150
|
|
572
|
+
});
|
|
573
|
+
calc_gen_ecs({}, g, ecs_di, ecs_de, 0, '1', '1', 'immeuble');
|
|
574
|
+
|
|
575
|
+
// Vs = volume saisi (150), identique au cas d'une fiche « 150 L »
|
|
576
|
+
expect(g.donnee_intermediaire.Qgw).toBeCloseTo(1064620.987190969, 9);
|
|
577
|
+
});
|
|
578
|
+
|
|
562
579
|
test('bug_for_bug_compat : volume proratisé recalé sur la description', () => {
|
|
563
580
|
state.bug = true;
|
|
564
581
|
vi.mocked(getVolumeStockageFromDescription).mockReturnValue(100);
|
|
@@ -852,6 +869,61 @@ describe('calc_gen_ecs - consommation par générateur', () => {
|
|
|
852
869
|
expect(g.donnee_entree.enum_type_generateur_ecs_id).toBe('84');
|
|
853
870
|
});
|
|
854
871
|
|
|
872
|
+
test('type 84 combustion : ligne combustion sans type ECS, type 84 conservé', () => {
|
|
873
|
+
vi.mocked(getChaudiereFioulDefautId).mockReturnValue(undefined);
|
|
874
|
+
vi.mocked(tv).mockReturnValue({ tv_generateur_combustion_id: '42' });
|
|
875
|
+
const g = gen({
|
|
876
|
+
type_energie: 'gaz',
|
|
877
|
+
enum_type_generateur_ecs_id: '84',
|
|
878
|
+
tv_generateur_combustion_id: '42',
|
|
879
|
+
enum_methode_saisie_carac_sys_id: '1'
|
|
880
|
+
});
|
|
881
|
+
calc_gen_ecs({}, g, ecs_di, ecs_de, 0, '1', '1', 'maison');
|
|
882
|
+
|
|
883
|
+
// Aucune liste de types ECS exploitable dans la ligne : pas de redressement
|
|
884
|
+
expect(g.donnee_entree.enum_type_generateur_ecs_id).toBe('84');
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
test('type 84 sans combustion ni pompe à chaleur : ni calcul SCOP ni redressement du type', () => {
|
|
888
|
+
const g = gen({
|
|
889
|
+
type_energie: 'gaz',
|
|
890
|
+
enum_type_generateur_ecs_id: '84',
|
|
891
|
+
enum_methode_saisie_carac_sys_id: '1'
|
|
892
|
+
});
|
|
893
|
+
calc_gen_ecs({}, g, ecs_di, ecs_de, 0, '1', '1', 'maison');
|
|
894
|
+
|
|
895
|
+
expect(scopOrCop).not.toHaveBeenCalled();
|
|
896
|
+
expect(tv_generateur_combustion).not.toHaveBeenCalled();
|
|
897
|
+
expect(g.donnee_entree.enum_type_generateur_ecs_id).toBe('84');
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
test('bug_for_bug_compat : qp0 déjà en W (>= 1) conservé sans correction', () => {
|
|
901
|
+
state.bug = true;
|
|
902
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
903
|
+
vi.mocked(tvColumnIDs).mockImplementation((table) =>
|
|
904
|
+
table === 'generateur_combustion' ? ['chaud'] : []
|
|
905
|
+
);
|
|
906
|
+
vi.mocked(tv_generateur_combustion).mockImplementation((dpe, di) => {
|
|
907
|
+
di.rpn = 0.9;
|
|
908
|
+
di.qp0 = 100;
|
|
909
|
+
di.pn = 20000;
|
|
910
|
+
di.pveilleuse = 1;
|
|
911
|
+
di.pveil = 0;
|
|
912
|
+
});
|
|
913
|
+
const g = gen({
|
|
914
|
+
type_energie: 'gaz',
|
|
915
|
+
enum_type_generateur_ecs_id: 'chaud',
|
|
916
|
+
enum_methode_saisie_carac_sys_id: '1'
|
|
917
|
+
});
|
|
918
|
+
calc_gen_ecs({}, g, ecs_di, ecs_de, 0, '1', '1', 'maison');
|
|
919
|
+
|
|
920
|
+
expect(g.donnee_intermediaire.qp0).toBe(100);
|
|
921
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
922
|
+
// même rendement que pour un qp0 de 0,1 kW corrigé en 100 W (cf. test de correction)
|
|
923
|
+
expect(g.donnee_intermediaire.rendement_generation).toBeCloseTo(0.3446955189582535, 9);
|
|
924
|
+
warnSpy.mockRestore();
|
|
925
|
+
});
|
|
926
|
+
|
|
855
927
|
test('type 84 pompe à chaleur : déduit via tv_scop_id', () => {
|
|
856
928
|
vi.mocked(scopOrCop).mockImplementation((di) => {
|
|
857
929
|
di.scop = 3;
|
package/15_conso_aux.spec.js
CHANGED
|
@@ -355,6 +355,26 @@ describe('conso_aux_distribution_ch - auxiliaires de distribution', () => {
|
|
|
355
355
|
expect(di.conso_auxiliaire_distribution_ch).toBeCloseTo(372.082418731648, 9);
|
|
356
356
|
});
|
|
357
357
|
|
|
358
|
+
test('émetteur hors listes (type 1, « autre ») : perte de charge par défaut deltaPem=35, Fcot=0,802', () => {
|
|
359
|
+
const conso = (type) => {
|
|
360
|
+
const em = [
|
|
361
|
+
{
|
|
362
|
+
donnee_entree: {
|
|
363
|
+
enum_type_emission_distribution_id: type,
|
|
364
|
+
enum_temp_distribution_ch_id: '3'
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
];
|
|
368
|
+
const di = {};
|
|
369
|
+
conso_aux_distribution_ch(em, {}, di, {}, 100, 1, 1, '0', 100000);
|
|
370
|
+
return di.conso_auxiliaire_distribution_ch;
|
|
371
|
+
};
|
|
372
|
+
// Type 24 (monotube) : deltaPem=30, même Fcot (0,802) -> seul deltaPem diffère
|
|
373
|
+
expect(conso('1')).toBeGreaterThan(conso('24'));
|
|
374
|
+
// Type 10 (radiateurs autres) : deltaPem=10
|
|
375
|
+
expect(conso('24')).toBeGreaterThan(conso('10'));
|
|
376
|
+
});
|
|
377
|
+
|
|
358
378
|
/**
|
|
359
379
|
* Seuls les émetteurs reliés au générateur (même enum_lien_generateur_emetteur_id) dimensionnent
|
|
360
380
|
* son circulateur. Autotest CSTB IC5-0-3 : chaudière (lien 1) sur plancher chauffant + convecteurs
|
package/3.1_b.spec.js
CHANGED
|
@@ -365,4 +365,27 @@ describe('b - calcul du coefficient de réduction des déperditions', () => {
|
|
|
365
365
|
});
|
|
366
366
|
expect(di.b).toBe(0.7);
|
|
367
367
|
});
|
|
368
|
+
|
|
369
|
+
test('adjacence hors liste aiu/aue (type 10 absent, type inconnu) : seul le type est apparié', () => {
|
|
370
|
+
tv.mockReturnValue({ b: '0.5', tv_coef_reduction_deperdition_id: '7' });
|
|
371
|
+
const di = {};
|
|
372
|
+
const de = { enum_type_adjacence_id: '1' };
|
|
373
|
+
b(di, de, {}, 1);
|
|
374
|
+
expect(tv).toHaveBeenCalledWith(TABLE, { enum_type_adjacence_id: '1' });
|
|
375
|
+
expect(di.b).toBe(0.5);
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
test('rapport inférieur à la plus petite borne : seule la borne max est renseignée', () => {
|
|
379
|
+
tv.mockReturnValue({ b: '0.9', tv_coef_reduction_deperdition_id: '3' });
|
|
380
|
+
const de = {
|
|
381
|
+
enum_type_adjacence_id: '8',
|
|
382
|
+
enum_cfg_isolation_lnc_id: '2',
|
|
383
|
+
surface_aiu: 1,
|
|
384
|
+
surface_aue: 10
|
|
385
|
+
};
|
|
386
|
+
b({}, de, {}, 1);
|
|
387
|
+
const matcher = tv.mock.calls[0][1];
|
|
388
|
+
expect(matcher.aiu_aue_max).toBe('≤ 0,25');
|
|
389
|
+
expect(matcher.aiu_aue_min).toBeUndefined();
|
|
390
|
+
});
|
|
368
391
|
});
|
package/3.2.1_mur.spec.js
CHANGED
|
@@ -226,6 +226,63 @@ describe('calc_mur - calcul de Umur0 par table forfaitaire (tv_umur0)', () => {
|
|
|
226
226
|
error.mockRestore();
|
|
227
227
|
});
|
|
228
228
|
|
|
229
|
+
test('bug_for_bug : épaisseur absente et ligne tv_umur0_id sans épaisseur : matcher inchangé', () => {
|
|
230
|
+
bugForBugCompat = true;
|
|
231
|
+
const error = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
232
|
+
tv.mockImplementation((table, matcher) => {
|
|
233
|
+
if (table === 'umur0' && matcher.tv_umur0_id) return { tv_umur0_id: '1' };
|
|
234
|
+
if (table === 'umur0') return { ...ROW_UMUR0 };
|
|
235
|
+
return null;
|
|
236
|
+
});
|
|
237
|
+
const mur = {
|
|
238
|
+
donnee_entree: {
|
|
239
|
+
methode_saisie_u: 'non isolé',
|
|
240
|
+
methode_saisie_u0:
|
|
241
|
+
'déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire',
|
|
242
|
+
enum_materiaux_structure_mur_id: '5',
|
|
243
|
+
description: 'mur sans épaisseur',
|
|
244
|
+
tv_umur0_id: 1,
|
|
245
|
+
enum_type_doublage_id: '2'
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
calc_mur(mur, 'h1a', '1', '0');
|
|
250
|
+
|
|
251
|
+
// Aucune épaisseur à reprendre : pas d'avertissement et épaisseur nulle conservée
|
|
252
|
+
expect(error).not.toHaveBeenCalled();
|
|
253
|
+
expect(tv).toHaveBeenLastCalledWith('umur0', {
|
|
254
|
+
enum_materiaux_structure_mur_id: '5',
|
|
255
|
+
epaisseur_structure: 0
|
|
256
|
+
});
|
|
257
|
+
error.mockRestore();
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test('bug_for_bug : ligne tv_umur0_id introuvable : matcher inchangé', () => {
|
|
261
|
+
bugForBugCompat = true;
|
|
262
|
+
tv.mockImplementation((table, matcher) => {
|
|
263
|
+
if (table === 'umur0' && matcher.tv_umur0_id) return undefined;
|
|
264
|
+
if (table === 'umur0') return { ...ROW_UMUR0 };
|
|
265
|
+
return null;
|
|
266
|
+
});
|
|
267
|
+
const mur = {
|
|
268
|
+
donnee_entree: {
|
|
269
|
+
methode_saisie_u: 'non isolé',
|
|
270
|
+
methode_saisie_u0:
|
|
271
|
+
'déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire',
|
|
272
|
+
enum_materiaux_structure_mur_id: '5',
|
|
273
|
+
tv_umur0_id: 1,
|
|
274
|
+
enum_type_doublage_id: '2'
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
calc_mur(mur, 'h1a', '1', '0');
|
|
279
|
+
|
|
280
|
+
expect(tv).toHaveBeenLastCalledWith('umur0', {
|
|
281
|
+
enum_materiaux_structure_mur_id: '5',
|
|
282
|
+
epaisseur_structure: 0
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
229
286
|
test('bug_for_bug : épaisseur > 80 recalculée en la divisant par 10 (valeur en millimètres)', () => {
|
|
230
287
|
bugForBugCompat = true;
|
|
231
288
|
const error = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
@@ -423,6 +480,48 @@ describe('calc_mur - prise en compte du doublage', () => {
|
|
|
423
480
|
}
|
|
424
481
|
);
|
|
425
482
|
|
|
483
|
+
test('bug_for_bug : doublage de type 1 sans description : type de doublage inchangé', () => {
|
|
484
|
+
bugForBugCompat = true;
|
|
485
|
+
const mur = {
|
|
486
|
+
donnee_entree: {
|
|
487
|
+
methode_saisie_u: 'non isolé',
|
|
488
|
+
methode_saisie_u0:
|
|
489
|
+
'déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire',
|
|
490
|
+
enum_materiaux_structure_mur_id: '5',
|
|
491
|
+
epaisseur_structure: 30,
|
|
492
|
+
enum_type_doublage_id: '1'
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
calc_mur(mur, 'h1a', '1', '0');
|
|
497
|
+
|
|
498
|
+
// Type 1 (pas de doublage) : Umur0 de la table non modifié
|
|
499
|
+
expect(mur.donnee_intermediaire.umur0).toBe(2);
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
test('bug_for_bug : type de doublage > 2 inconnu (6) : aucune résistance de doublage ajoutée', () => {
|
|
503
|
+
bugForBugCompat = true;
|
|
504
|
+
const error = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
505
|
+
const mur = {
|
|
506
|
+
donnee_entree: {
|
|
507
|
+
methode_saisie_u: 'non isolé',
|
|
508
|
+
methode_saisie_u0:
|
|
509
|
+
'déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire',
|
|
510
|
+
enum_materiaux_structure_mur_id: '5',
|
|
511
|
+
epaisseur_structure: 30,
|
|
512
|
+
enum_type_doublage_id: '6'
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
calc_mur(mur, 'h1a', '1', '0');
|
|
517
|
+
|
|
518
|
+
// Le type 6 est hors énumération type_doublage (1 à 5) : aucune résistance de doublage n'est
|
|
519
|
+
// définie, ni dans le calcul ni dans la vérification de minoration. Comportement actuel figé :
|
|
520
|
+
// Umur0 devient NaN (donnée d'entrée invalide, non protégée par le moteur).
|
|
521
|
+
expect(mur.donnee_intermediaire.umur0).toBeNaN();
|
|
522
|
+
error.mockRestore();
|
|
523
|
+
});
|
|
524
|
+
|
|
426
525
|
test('bug_for_bug : DPE minorant Umur0 à 2,5 avant doublage : avertissement (Umur0 avant)', () => {
|
|
427
526
|
bugForBugCompat = true;
|
|
428
527
|
const error = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
@@ -342,6 +342,35 @@ describe('calc_ph - compatibilité bug for bug (récupération depuis tv_uph_id)
|
|
|
342
342
|
error.mockRestore();
|
|
343
343
|
});
|
|
344
344
|
|
|
345
|
+
test('ligne tv_uph_id introuvable : type_toiture et effet_joule calculés conservés', () => {
|
|
346
|
+
const error = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
347
|
+
tv.mockImplementation((table, matcher) => {
|
|
348
|
+
if (table === 'uph0') return { ...ROW_UPH0 };
|
|
349
|
+
if (table === 'uph' && matcher.tv_uph_id) return undefined;
|
|
350
|
+
if (table === 'uph') return { ...ROW_UPH };
|
|
351
|
+
return null;
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
const ph = {
|
|
355
|
+
donnee_entree: {
|
|
356
|
+
methode_saisie_u: 'isolation inconnue (table forfaitaire)',
|
|
357
|
+
methode_saisie_u0: 'type de paroi inconnu (valeur par défaut)',
|
|
358
|
+
type_adjacence: 'extérieur',
|
|
359
|
+
type_plancher_haut: 'terrasse',
|
|
360
|
+
tv_uph_id: 7,
|
|
361
|
+
description: 'PH test'
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
calc_ph(ph, 'h1a', '1', '0');
|
|
366
|
+
|
|
367
|
+
// Aucune correction issue du DPE : pas d'avertissement, matcher final en 'terrasse'
|
|
368
|
+
expect(error).not.toHaveBeenCalled();
|
|
369
|
+
expect(tv).toHaveBeenCalledWith('uph', expect.objectContaining({ type_toiture: 'terrasse' }));
|
|
370
|
+
expect(ph.donnee_intermediaire.uph).toBe(0.3);
|
|
371
|
+
error.mockRestore();
|
|
372
|
+
});
|
|
373
|
+
|
|
345
374
|
test('effet_joule du DPE différent : la valeur du DPE est conservée dans le matcher', () => {
|
|
346
375
|
const error = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
347
376
|
tv.mockImplementation((table, matcher) => {
|