@open3cl/engine 1.8.8 → 1.8.9
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/13.2_generateur_combustion.js +116 -31
- package/13.2_generateur_combustion.spec.js +286 -2
- package/13.2_generateur_combustion_pn.js +248 -0
- package/13.2_generateur_combustion_pn.spec.js +476 -0
- package/README.md +21 -21
- package/conso.js +178 -4
- package/conso.spec.js +187 -1
- package/engine.js +7 -4
- package/package.json +1 -1
- package/utils.js +5 -0
- package/utils.spec.js +53 -0
package/conso.js
CHANGED
|
@@ -122,6 +122,129 @@ function cout_electricite(cef) {
|
|
|
122
122
|
else return 56 + 0.15989 * cef;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Date d'entrée en vigueur du barème des prix des énergies mis à jour.
|
|
127
|
+
* Constatée sur les DPE publiés par l'ADEME (dataset dpe03existant) : barème 2021 jusqu'au
|
|
128
|
+
* 30/06/2024, barème mis à jour pour les DPE établis à partir du 01/07/2024 (et toujours en 2026).
|
|
129
|
+
*/
|
|
130
|
+
export const DATE_BAREME_COUT_2024 = '2024-07-01';
|
|
131
|
+
|
|
132
|
+
function cout_gaz_naturel_2024(cef) {
|
|
133
|
+
if (cef < 5009) return 0.1312 * cef;
|
|
134
|
+
else if (cef < 50055) return 182 + 0.09488 * cef;
|
|
135
|
+
else return 288 + 0.09274 * cef;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function cout_electricite_2024(cef) {
|
|
139
|
+
if (cef < 1000) return 0.34721 * cef;
|
|
140
|
+
else if (cef < 2500) return 158 + 0.18954 * cef;
|
|
141
|
+
else if (cef < 5000) return 158 + 0.18949 * cef;
|
|
142
|
+
else if (cef < 15000) return 119 + 0.19726 * cef;
|
|
143
|
+
else return 78 + 0.20001 * cef;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Barème des prix des énergies applicable aux DPE établis à partir du 01/07/2024.
|
|
148
|
+
* Mêmes tranches qu'en 2021 ; valeurs calées sur les sorties Tribu (moteur CSTB) et les DPE ADEME.
|
|
149
|
+
*/
|
|
150
|
+
const coef_cout_2024 = {
|
|
151
|
+
'fioul domestique': 0.14821,
|
|
152
|
+
'réseau de chauffage urbain': 0.08921,
|
|
153
|
+
propane: 0.15672,
|
|
154
|
+
gpl: 0.15672,
|
|
155
|
+
butane: 0.23429,
|
|
156
|
+
charbon: 0.02787,
|
|
157
|
+
'bois – granulés (pellets) ou briquettes': 0.09897,
|
|
158
|
+
'bois – bûches': 0.042,
|
|
159
|
+
'bois – plaquettes forestières': 0.042,
|
|
160
|
+
'bois – plaquettes d’industrie': 0.042,
|
|
161
|
+
'gaz naturel': cout_gaz_naturel_2024,
|
|
162
|
+
"électricité d'origine renouvelable utilisée dans le bâtiment": cout_electricite_2024,
|
|
163
|
+
'électricité ch': cout_electricite_2024,
|
|
164
|
+
'électricité ecs': cout_electricite_2024,
|
|
165
|
+
'électricité fr': cout_electricite_2024,
|
|
166
|
+
'électricité éclairage': cout_electricite_2024,
|
|
167
|
+
'électricité auxiliaire': cout_electricite_2024
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Sélection du barème des prix des énergies selon la date d'établissement du DPE.
|
|
172
|
+
* @param dateDpe {string} date d'établissement du DPE (AAAA-MM-JJ)
|
|
173
|
+
* @returns {object} barème 2021 (Annexe 7) avant le 01/07/2024, barème mis à jour ensuite
|
|
174
|
+
*/
|
|
175
|
+
export function getCoefCout(dateDpe) {
|
|
176
|
+
return new Date(dateDpe) >= new Date(DATE_BAREME_COUT_2024) ? coef_cout_2024 : coef_cout;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Clé (symbole) portant le barème des générateurs d'installations collectives d'un immeuble. */
|
|
180
|
+
const COUT_COLLECTIF = Symbol('cout_collectif');
|
|
181
|
+
|
|
182
|
+
/** Énergies dont le coût suit un barème par tranches, regroupées par énergie facturée. */
|
|
183
|
+
const ENERGIES_A_TRANCHES = {
|
|
184
|
+
'électricité ch': 'électricité',
|
|
185
|
+
'électricité ecs': 'électricité',
|
|
186
|
+
'électricité fr': 'électricité',
|
|
187
|
+
'électricité éclairage': 'électricité',
|
|
188
|
+
'électricité auxiliaire': 'électricité',
|
|
189
|
+
"électricité d'origine renouvelable utilisée dans le bâtiment": 'électricité renouvelable',
|
|
190
|
+
'gaz naturel': 'gaz naturel'
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Table « masque » : consommation conservée pour les clés du groupe, annulée pour les autres.
|
|
195
|
+
* Permet de totaliser la consommation d'une énergie avec les fonctions d'agrégation existantes.
|
|
196
|
+
* @param groupe {string} énergie facturée (valeur de ENERGIES_A_TRANCHES)
|
|
197
|
+
* @returns {object}
|
|
198
|
+
*/
|
|
199
|
+
export function masqueEnergie(groupe) {
|
|
200
|
+
const cles = Object.values(enums.type_energie)
|
|
201
|
+
.filter((label) => label !== 'électricité')
|
|
202
|
+
.concat(Object.keys(ENERGIES_A_TRANCHES));
|
|
203
|
+
return Object.fromEntries(
|
|
204
|
+
cles.map((cle) => [cle, ENERGIES_A_TRANCHES[cle] === groupe ? (c) => c : () => 0])
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Barème « prix moyen » pour les DPE établis à partir du 01/07/2024 (constaté sur Tribu et sur
|
|
210
|
+
* les DPE publiés par l'ADEME) : pour l'électricité et le gaz naturel, la tranche est déterminée
|
|
211
|
+
* sur la consommation annuelle TOTALE de l'énergie (tous usages) et le coût est réparti entre les
|
|
212
|
+
* postes au prorata de leur consommation.
|
|
213
|
+
* Immeuble : consommation des usages individuels rapportée au logement (N × f(C / N)) ;
|
|
214
|
+
* chauffage / ECS des installations collectives tarifés sur leur total (f(C)).
|
|
215
|
+
*
|
|
216
|
+
* @param base {object} barème (prix unitaires et fonctions par tranches)
|
|
217
|
+
* @param consoGroupe {function(masque): {total: number, collectif: number}} consommations EF
|
|
218
|
+
* @param nbLogements {number} nombre de logements (1 hors immeuble)
|
|
219
|
+
* @param genCollectifs {Set} générateurs de chauffage / ECS d'installations collectives
|
|
220
|
+
* @returns {object}
|
|
221
|
+
*/
|
|
222
|
+
export function coefCoutProrata(base, consoGroupe, nbLogements, genCollectifs) {
|
|
223
|
+
const coef = { ...base };
|
|
224
|
+
const coefCollectif = { ...base };
|
|
225
|
+
new Set(Object.values(ENERGIES_A_TRANCHES)).forEach((groupe) => {
|
|
226
|
+
const { total, collectif } = consoGroupe(masqueEnergie(groupe));
|
|
227
|
+
const individuel = total - collectif;
|
|
228
|
+
Object.keys(ENERGIES_A_TRANCHES)
|
|
229
|
+
.filter((cle) => ENERGIES_A_TRANCHES[cle] === groupe)
|
|
230
|
+
.forEach((cle) => {
|
|
231
|
+
const f = base[cle];
|
|
232
|
+
const prix = individuel > 0 ? (nbLogements * f(individuel / nbLogements)) / individuel : 0;
|
|
233
|
+
const prixCollectif = collectif > 0 ? f(collectif) / collectif : 0;
|
|
234
|
+
coef[cle] = (c) => prix * c;
|
|
235
|
+
coefCollectif[cle] = (c) => prixCollectif * c;
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
coef[COUT_COLLECTIF] = { generateurs: genCollectifs, coef: coefCollectif };
|
|
239
|
+
return coef;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** Barème d'un générateur : barème collectif s'il appartient à une installation collective. */
|
|
243
|
+
function coefPourGenerateur(coef, gen) {
|
|
244
|
+
const collectif = coef?.[COUT_COLLECTIF];
|
|
245
|
+
return collectif?.generateurs.has(gen) ? collectif.coef : coef;
|
|
246
|
+
}
|
|
247
|
+
|
|
125
248
|
function getConso(coef, type_energie, conso) {
|
|
126
249
|
// is coef is a function, execute it
|
|
127
250
|
if (!coef) return conso;
|
|
@@ -197,7 +320,8 @@ export default function calc_conso(
|
|
|
197
320
|
prorataECS,
|
|
198
321
|
prorataChauffage,
|
|
199
322
|
dateDpe,
|
|
200
|
-
coeffEp
|
|
323
|
+
coeffEp,
|
|
324
|
+
nbLogements = 1
|
|
201
325
|
) {
|
|
202
326
|
const gen_ch = ch.reduce((acc, ch) => {
|
|
203
327
|
const generateur_chauffage = ch.generateur_chauffage_collection.generateur_chauffage;
|
|
@@ -237,6 +361,56 @@ export default function calc_conso(
|
|
|
237
361
|
return acc.concat(generateur_ecs);
|
|
238
362
|
}, []);
|
|
239
363
|
|
|
364
|
+
/**
|
|
365
|
+
* Barème des coûts : barème 2021 (par poste) avant le 01/07/2024 ; ensuite barème mis à jour
|
|
366
|
+
* avec tranche déterminée sur la consommation totale de l'énergie (voir coefCoutProrata).
|
|
367
|
+
*/
|
|
368
|
+
const coefCout = () => {
|
|
369
|
+
const base = getCoefCout(dateDpe);
|
|
370
|
+
if (base === coef_cout) return coef_cout;
|
|
371
|
+
const genCollectifs = new Set();
|
|
372
|
+
if (nbLogements > 1) {
|
|
373
|
+
gen_ch
|
|
374
|
+
.filter((g) => g.donnee_entree.enum_type_installation_id !== '1')
|
|
375
|
+
.forEach((g) => genCollectifs.add(g));
|
|
376
|
+
ecs
|
|
377
|
+
.filter((inst) => (inst.donnee_entree.enum_type_installation_id ?? '1') !== '1')
|
|
378
|
+
.forEach((inst) =>
|
|
379
|
+
inst.generateur_ecs_collection.generateur_ecs.forEach((g) => genCollectifs.add(g))
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
const consoGroupe = (masque) => {
|
|
383
|
+
const tot = calc_conso_pond(
|
|
384
|
+
Sh,
|
|
385
|
+
zc_id,
|
|
386
|
+
vt,
|
|
387
|
+
gen_ch,
|
|
388
|
+
gen_ecs,
|
|
389
|
+
fr,
|
|
390
|
+
'c',
|
|
391
|
+
masque,
|
|
392
|
+
prorataECS,
|
|
393
|
+
prorataChauffage,
|
|
394
|
+
ecs
|
|
395
|
+
);
|
|
396
|
+
const coll = calc_conso_pond(
|
|
397
|
+
0,
|
|
398
|
+
zc_id,
|
|
399
|
+
[],
|
|
400
|
+
gen_ch.filter((g) => genCollectifs.has(g)),
|
|
401
|
+
gen_ecs.filter((g) => genCollectifs.has(g)),
|
|
402
|
+
[],
|
|
403
|
+
'c',
|
|
404
|
+
masque,
|
|
405
|
+
prorataECS,
|
|
406
|
+
prorataChauffage,
|
|
407
|
+
[]
|
|
408
|
+
);
|
|
409
|
+
return { total: tot.c_5_usages, collectif: coll.c_ch + coll.c_ecs };
|
|
410
|
+
};
|
|
411
|
+
return coefCoutProrata(base, consoGroupe, nbLogements, genCollectifs);
|
|
412
|
+
};
|
|
413
|
+
|
|
240
414
|
const ret = {
|
|
241
415
|
ef_conso: calc_conso_pond(
|
|
242
416
|
Sh,
|
|
@@ -288,7 +462,7 @@ export default function calc_conso(
|
|
|
288
462
|
gen_ecs,
|
|
289
463
|
fr,
|
|
290
464
|
'cout',
|
|
291
|
-
|
|
465
|
+
coefCout(),
|
|
292
466
|
prorataECS,
|
|
293
467
|
prorataChauffage,
|
|
294
468
|
ecs,
|
|
@@ -424,7 +598,7 @@ function getEcsConso(gen_ecs, field, coef, prorataECS, prefix) {
|
|
|
424
598
|
const conso = gen_ecs.donnee_intermediaire[field];
|
|
425
599
|
const typeEnergie = getTypeEnergie(gen_ecs.donnee_entree, 'ecs');
|
|
426
600
|
|
|
427
|
-
let coeffConsoEcs = { ...coef };
|
|
601
|
+
let coeffConsoEcs = { ...coefPourGenerateur(coef, gen_ecs) };
|
|
428
602
|
|
|
429
603
|
if (prefix === 'emission_ges') {
|
|
430
604
|
coeffConsoEcs[typeEnergie] = gen_ecs.donnee_utilisateur.coeffEmissionGes;
|
|
@@ -458,7 +632,7 @@ function getChauffageConso(gen_ch, field, coef, prorataChauffage, prefix) {
|
|
|
458
632
|
? gen_ch.donnee_entree.cle_repartition_ch || prorataChauffage
|
|
459
633
|
: prorataChauffage;
|
|
460
634
|
|
|
461
|
-
let coeffConsoChauffage = { ...coef };
|
|
635
|
+
let coeffConsoChauffage = { ...coefPourGenerateur(coef, gen_ch) };
|
|
462
636
|
|
|
463
637
|
if (prefix === 'emission_ges') {
|
|
464
638
|
coeffConsoChauffage[typeEnergie] = gen_ch.donnee_utilisateur.coeffEmissionGes;
|
package/conso.spec.js
CHANGED
|
@@ -51,7 +51,11 @@ const {
|
|
|
51
51
|
classe_bilan_dpe,
|
|
52
52
|
classe_emission_ges,
|
|
53
53
|
getCoefKey,
|
|
54
|
-
coef_ep
|
|
54
|
+
coef_ep,
|
|
55
|
+
getCoefCout,
|
|
56
|
+
coefCoutProrata,
|
|
57
|
+
masqueEnergie,
|
|
58
|
+
DATE_BAREME_COUT_2024
|
|
55
59
|
} = await import('./conso.js');
|
|
56
60
|
|
|
57
61
|
/**
|
|
@@ -499,3 +503,185 @@ describe('calc_conso - agrégation des consommations', () => {
|
|
|
499
503
|
expect(ids).toContain('1');
|
|
500
504
|
});
|
|
501
505
|
});
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Barème des prix des énergies selon la date d'établissement du DPE.
|
|
509
|
+
* Barème 2021 : Annexe 7 de l'arrêté du 31/03/2021.
|
|
510
|
+
* Barème mis à jour (DPE à partir du 01/07/2024) : valeurs calées sur les sorties Tribu (CSTB)
|
|
511
|
+
* des autotests et sur les DPE publiés par l'ADEME (dataset dpe03existant), reproduites à ±1 €.
|
|
512
|
+
*/
|
|
513
|
+
describe('getCoefCout - sélection du barème par date de DPE', () => {
|
|
514
|
+
test('date de bascule : 01/07/2024', () => {
|
|
515
|
+
expect(DATE_BAREME_COUT_2024).toBe('2024-07-01');
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
test('DPE établi avant le 01/07/2024 : barème 2021 inchangé', () => {
|
|
519
|
+
const coef = getCoefCout('2024-06-30');
|
|
520
|
+
// Annexe 7 : électricité 1000 kWh => 149 + 0,14066 × 1000
|
|
521
|
+
expect(coef['électricité ch'](1000)).toBeCloseTo(149 + 0.14066 * 1000, 9);
|
|
522
|
+
expect(coef['gaz naturel'](3000)).toBeCloseTo(0.11121 * 3000, 9);
|
|
523
|
+
expect(coef['fioul domestique']).toBe(0.09142);
|
|
524
|
+
expect(coef.propane).toBe(0.14305);
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
test.each(['2024-07-01', '2025-12-31', '2026-01-15'])(
|
|
528
|
+
'DPE établi le %s : barème mis à jour',
|
|
529
|
+
(date) => {
|
|
530
|
+
const coef = getCoefCout(date);
|
|
531
|
+
// Tribu / ADEME : électricité 1000 kWh => 158 + 0,18954 × 1000
|
|
532
|
+
expect(coef['électricité ch'](1000)).toBeCloseTo(158 + 0.18954 * 1000, 9);
|
|
533
|
+
}
|
|
534
|
+
);
|
|
535
|
+
|
|
536
|
+
test('barème mis à jour : prix unitaires constants (fioul, propane, bois, réseau...)', () => {
|
|
537
|
+
const coef = getCoefCout('2026-01-15');
|
|
538
|
+
// Tribu : fioul 0,1482 €/kWh ; propane 0,1567 €/kWh (constants quelle que soit la conso)
|
|
539
|
+
expect(coef['fioul domestique']).toBe(0.14821);
|
|
540
|
+
expect(coef.propane).toBe(0.15672);
|
|
541
|
+
expect(coef.gpl).toBe(0.15672);
|
|
542
|
+
expect(coef.butane).toBe(0.23429);
|
|
543
|
+
expect(coef.charbon).toBe(0.02787);
|
|
544
|
+
expect(coef['réseau de chauffage urbain']).toBe(0.08921);
|
|
545
|
+
expect(coef['bois – granulés (pellets) ou briquettes']).toBe(0.09897);
|
|
546
|
+
expect(coef['bois – bûches']).toBe(0.042);
|
|
547
|
+
expect(coef['bois – plaquettes forestières']).toBe(0.042);
|
|
548
|
+
expect(coef['bois – plaquettes d’industrie']).toBe(0.042);
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
test.each([
|
|
552
|
+
// [conso, coût attendu] électricité, une valeur par tranche + limites
|
|
553
|
+
[500, 0.34721 * 500],
|
|
554
|
+
[999.99, 0.34721 * 999.99],
|
|
555
|
+
[1000, 158 + 0.18954 * 1000],
|
|
556
|
+
[2499.99, 158 + 0.18954 * 2499.99],
|
|
557
|
+
[2500, 158 + 0.18949 * 2500],
|
|
558
|
+
[4999.99, 158 + 0.18949 * 4999.99],
|
|
559
|
+
[5000, 119 + 0.19726 * 5000],
|
|
560
|
+
[14999.99, 119 + 0.19726 * 14999.99],
|
|
561
|
+
[15000, 78 + 0.20001 * 15000],
|
|
562
|
+
[20000, 78 + 0.20001 * 20000]
|
|
563
|
+
])('barème mis à jour - électricité %s kWh => %s €', (conso, attendu) => {
|
|
564
|
+
const coef = getCoefCout('2026-01-15');
|
|
565
|
+
[
|
|
566
|
+
'électricité ch',
|
|
567
|
+
'électricité ecs',
|
|
568
|
+
'électricité fr',
|
|
569
|
+
'électricité éclairage',
|
|
570
|
+
'électricité auxiliaire',
|
|
571
|
+
"électricité d'origine renouvelable utilisée dans le bâtiment"
|
|
572
|
+
].forEach((cle) => expect(coef[cle](conso)).toBeCloseTo(attendu, 9));
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
test.each([
|
|
576
|
+
// [conso, coût attendu] gaz naturel, une valeur par tranche + limites
|
|
577
|
+
[3000, 0.1312 * 3000],
|
|
578
|
+
[5008.99, 0.1312 * 5008.99],
|
|
579
|
+
[5009, 182 + 0.09488 * 5009],
|
|
580
|
+
[50054.99, 182 + 0.09488 * 50054.99],
|
|
581
|
+
[50055, 288 + 0.09274 * 50055],
|
|
582
|
+
[100000, 288 + 0.09274 * 100000]
|
|
583
|
+
])('barème mis à jour - gaz naturel %s kWh => %s €', (conso, attendu) => {
|
|
584
|
+
expect(getCoefCout('2026-01-15')['gaz naturel'](conso)).toBeCloseTo(attendu, 9);
|
|
585
|
+
});
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
describe("masqueEnergie / coefCoutProrata - tranche sur la conso totale de l'énergie", () => {
|
|
589
|
+
test('masque électricité : clés électriques conservées, autres énergies annulées', () => {
|
|
590
|
+
const masque = masqueEnergie('électricité');
|
|
591
|
+
expect(masque['électricité ch'](10)).toBe(10);
|
|
592
|
+
expect(masque['électricité auxiliaire'](10)).toBe(10);
|
|
593
|
+
expect(masque['gaz naturel'](10)).toBe(0);
|
|
594
|
+
expect(masque['réseau de chauffage urbain'](10)).toBe(0);
|
|
595
|
+
expect(masque["électricité d'origine renouvelable utilisée dans le bâtiment"](10)).toBe(0);
|
|
596
|
+
expect(masque['électricité']).toBeUndefined();
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
test('aucune consommation : prix moyen nul', () => {
|
|
600
|
+
const coef = coefCoutProrata(
|
|
601
|
+
getCoefCout('2026-01-15'),
|
|
602
|
+
() => ({ total: 0, collectif: 0 }),
|
|
603
|
+
1,
|
|
604
|
+
new Set()
|
|
605
|
+
);
|
|
606
|
+
expect(coef['électricité ch'](100)).toBe(0);
|
|
607
|
+
expect(coef['gaz naturel'](100)).toBe(0);
|
|
608
|
+
// Prix constants conservés
|
|
609
|
+
expect(coef['fioul domestique']).toBe(0.14821);
|
|
610
|
+
});
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
describe('calc_conso - coût avec le barème mis à jour (DPE 2026)', () => {
|
|
614
|
+
const DATE_DPE_2026 = '2026-01-15';
|
|
615
|
+
let errorSpy;
|
|
616
|
+
|
|
617
|
+
beforeEach(() => {
|
|
618
|
+
errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
afterEach(() => {
|
|
622
|
+
errorSpy.mockRestore();
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
test('DPE antérieur au 01/07/2024 : coût par poste au barème 2021 (inchangé)', () => {
|
|
626
|
+
const ch = [installCh({}, [genCh('1', { conso_ch: 3000, conso_ch_depensier: 3000 })])];
|
|
627
|
+
const res = calc_conso(100, 1, 1, [], ch, [], [], 1, 1, '2024-06-30', coef_ep);
|
|
628
|
+
// Chauffage tarifé seul : 122 + 0,15176 × 3000 ; éclairage seul : 0,29007 × 100
|
|
629
|
+
expect(res.cout.cout_ch).toBeCloseTo(122 + 0.15176 * 3000, 9);
|
|
630
|
+
expect(res.cout.cout_eclairage).toBeCloseTo(0.29007 * 100, 9);
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
test('maison tout électrique : tranche sur la conso électrique totale, coût réparti au prorata', () => {
|
|
634
|
+
const ch = [installCh({}, [genCh('1', { conso_ch: 3000, conso_ch_depensier: 3000 })])];
|
|
635
|
+
const res = calc_conso(100, 1, 1, [], ch, [], [], 1, 1, DATE_DPE_2026, coef_ep);
|
|
636
|
+
// Conso élec totale = 3000 (ch) + 100 (éclairage = 1 × Sh) = 3100 kWh
|
|
637
|
+
const total = 158 + 0.18949 * 3100;
|
|
638
|
+
expect(res.cout.cout_5_usages).toBeCloseTo(total, 9);
|
|
639
|
+
expect(res.cout.cout_ch).toBeCloseTo((total * 3000) / 3100, 9);
|
|
640
|
+
expect(res.cout.cout_eclairage).toBeCloseTo((total * 100) / 3100, 9);
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
test('maison gaz + électricité : chaque énergie tarifée sur sa propre conso totale', () => {
|
|
644
|
+
const ch = [installCh({}, [genCh('2', { conso_ch: 8000, conso_ch_depensier: 8000 })])];
|
|
645
|
+
const ecs = [installEcs({}, [genEcs('2', { conso_ecs: 2000, conso_ecs_depensier: 2000 })])];
|
|
646
|
+
const res = calc_conso(100, 1, 1, [], ch, ecs, [], 1, 1, DATE_DPE_2026, coef_ep);
|
|
647
|
+
// Gaz total 10000 kWh => 182 + 0,09488 × 10000 ; élec (éclairage) 100 kWh => 0,34721 × 100
|
|
648
|
+
const gaz = 182 + 0.09488 * 10000;
|
|
649
|
+
expect(res.cout.cout_ch).toBeCloseTo((gaz * 8000) / 10000, 9);
|
|
650
|
+
expect(res.cout.cout_ecs).toBeCloseTo((gaz * 2000) / 10000, 9);
|
|
651
|
+
expect(res.cout.cout_eclairage).toBeCloseTo(0.34721 * 100, 9);
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
test('immeuble à chauffage individuel : barème appliqué par logement (N × f(C / N))', () => {
|
|
655
|
+
const ch = [installCh({}, [genCh('1', { conso_ch: 8000, conso_ch_depensier: 8000 })])];
|
|
656
|
+
const res = calc_conso(100, 1, 1, [], ch, [], [], 1, 1, DATE_DPE_2026, coef_ep, 4);
|
|
657
|
+
// Élec totale 8100 kWh pour 4 logements : 4 × f(2025) = 4 × (158 + 0,18954 × 2025)
|
|
658
|
+
expect(res.cout.cout_5_usages).toBeCloseTo(4 * (158 + 0.18954 * 2025), 9);
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
test('immeuble à installations collectives : chauffage / ECS collectifs tarifés sur leur total', () => {
|
|
662
|
+
const ch = [
|
|
663
|
+
installCh({ enum_type_installation_id: '2' }, [
|
|
664
|
+
genCh('2', { conso_ch: 60000, conso_ch_depensier: 60000 })
|
|
665
|
+
])
|
|
666
|
+
];
|
|
667
|
+
const ecs = [
|
|
668
|
+
installEcs({ enum_type_installation_id: '2' }, [
|
|
669
|
+
genEcs('1', { conso_ecs: 4000, conso_ecs_depensier: 4000 })
|
|
670
|
+
])
|
|
671
|
+
];
|
|
672
|
+
const res = calc_conso(100, 1, 1, [], ch, ecs, [], 1, 1, DATE_DPE_2026, coef_ep, 4);
|
|
673
|
+
// Gaz collectif 60000 kWh => 288 + 0,09274 × 60000 (pas de division par logement)
|
|
674
|
+
expect(res.cout.cout_ch).toBeCloseTo(288 + 0.09274 * 60000, 9);
|
|
675
|
+
// ECS élec collective 4000 kWh => 158 + 0,18949 × 4000
|
|
676
|
+
expect(res.cout.cout_ecs).toBeCloseTo(158 + 0.18949 * 4000, 9);
|
|
677
|
+
// Éclairage (usage individuel) 100 kWh / 4 logements => 4 × 0,34721 × 25
|
|
678
|
+
expect(res.cout.cout_eclairage).toBeCloseTo(4 * 0.34721 * 25, 9);
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
test("immeuble, ECS sans type d'installation : considérée individuelle", () => {
|
|
682
|
+
const ecs = [installEcs({}, [genEcs('1', { conso_ecs: 4000, conso_ecs_depensier: 4000 })])];
|
|
683
|
+
const res = calc_conso(100, 1, 1, [], [], ecs, [], 1, 1, DATE_DPE_2026, coef_ep, 4);
|
|
684
|
+
// Élec totale 4100 kWh / 4 logements => 4 × (158 + 0,18954 × 1025)
|
|
685
|
+
expect(res.cout.cout_5_usages).toBeCloseTo(4 * (158 + 0.18954 * 1025), 9);
|
|
686
|
+
});
|
|
687
|
+
});
|
package/engine.js
CHANGED
|
@@ -33,7 +33,7 @@ import getFicheTechnique from './ficheTechnique.js';
|
|
|
33
33
|
import { ProductionENR } from './16.2_production_enr.js';
|
|
34
34
|
import DpeSanitizerService from './dpe-sanitizer.service.js';
|
|
35
35
|
|
|
36
|
-
const LIB_VERSION = '1.8.
|
|
36
|
+
const LIB_VERSION = '1.8.9';
|
|
37
37
|
|
|
38
38
|
const dpeSanitizerService = new DpeSanitizerService();
|
|
39
39
|
|
|
@@ -573,7 +573,8 @@ export function calcul_3cl(inputDpe, options) {
|
|
|
573
573
|
prorataECS,
|
|
574
574
|
prorataChauffage,
|
|
575
575
|
dateDpe,
|
|
576
|
-
coef_ep
|
|
576
|
+
coef_ep,
|
|
577
|
+
th === 'immeuble' ? Nb_lgt : 1
|
|
577
578
|
);
|
|
578
579
|
|
|
579
580
|
const production_electricite = productionENR.calculateEnr(
|
|
@@ -595,7 +596,8 @@ export function calcul_3cl(inputDpe, options) {
|
|
|
595
596
|
prorataECS,
|
|
596
597
|
prorataChauffage,
|
|
597
598
|
dateDpe,
|
|
598
|
-
coef_ep_1_7
|
|
599
|
+
coef_ep_1_7,
|
|
600
|
+
th === 'immeuble' ? Nb_lgt : 1
|
|
599
601
|
);
|
|
600
602
|
|
|
601
603
|
productionENR.calculateEnr(
|
|
@@ -618,7 +620,8 @@ export function calcul_3cl(inputDpe, options) {
|
|
|
618
620
|
prorataECS,
|
|
619
621
|
prorataChauffage,
|
|
620
622
|
dateDpe,
|
|
621
|
-
coef_ep_2_3
|
|
623
|
+
coef_ep_2_3,
|
|
624
|
+
th === 'immeuble' ? Nb_lgt : 1
|
|
622
625
|
);
|
|
623
626
|
|
|
624
627
|
productionENR.calculateEnr(
|
package/package.json
CHANGED
package/utils.js
CHANGED
|
@@ -277,10 +277,15 @@ export function requestInput(de, du, field, type) {
|
|
|
277
277
|
return enums[field][de[enum_name]];
|
|
278
278
|
} else {
|
|
279
279
|
// not enums
|
|
280
|
+
// `!type` couvre aussi NaN, 0 et '' (valeurs falsy)
|
|
280
281
|
if (!type) {
|
|
281
282
|
console.error(`requestInput: type is not defined for ${field}`);
|
|
282
283
|
return null;
|
|
283
284
|
}
|
|
285
|
+
if (!['string', 'number', 'boolean'].includes(typeof type)) {
|
|
286
|
+
console.error(`requestInput: type has an unexpected type (${typeof type}) for ${field}`);
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
284
289
|
du[field] = type;
|
|
285
290
|
return de[field];
|
|
286
291
|
}
|
package/utils.spec.js
CHANGED
|
@@ -287,6 +287,59 @@ describe('Utils unit tests', () => {
|
|
|
287
287
|
spy.mockRestore();
|
|
288
288
|
});
|
|
289
289
|
|
|
290
|
+
test.each([
|
|
291
|
+
['NaN', Number.NaN],
|
|
292
|
+
['0', 0],
|
|
293
|
+
['une chaîne vide', '']
|
|
294
|
+
])(
|
|
295
|
+
'requestInput : champ non enum avec %s comme type est traité comme un type absent',
|
|
296
|
+
(_libelle, type) => {
|
|
297
|
+
const spy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
298
|
+
const de = { epaisseur: 5 };
|
|
299
|
+
const du = {};
|
|
300
|
+
expect(requestInput(de, du, 'epaisseur', type)).toBeNull();
|
|
301
|
+
expect(spy).toHaveBeenCalledWith('requestInput: type is not defined for epaisseur');
|
|
302
|
+
// Le type invalide n'est pas publié dans donnee_utilisateur
|
|
303
|
+
expect(du).toStrictEqual({});
|
|
304
|
+
spy.mockRestore();
|
|
305
|
+
}
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
test.each([
|
|
309
|
+
['un objet', { a: 1 }, 'object'],
|
|
310
|
+
['un tableau', ['number'], 'object'],
|
|
311
|
+
['une fonction', () => 'number', 'function'],
|
|
312
|
+
['un symbole', Symbol('number'), 'symbol'],
|
|
313
|
+
['un bigint', 10n, 'bigint']
|
|
314
|
+
])(
|
|
315
|
+
'requestInput : champ non enum avec %s comme type journalise une erreur et retourne null',
|
|
316
|
+
(_libelle, type, typeAttendu) => {
|
|
317
|
+
const spy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
318
|
+
const de = { epaisseur: 5 };
|
|
319
|
+
const du = {};
|
|
320
|
+
expect(requestInput(de, du, 'epaisseur', type)).toBeNull();
|
|
321
|
+
expect(spy).toHaveBeenCalledWith(
|
|
322
|
+
`requestInput: type has an unexpected type (${typeAttendu}) for epaisseur`
|
|
323
|
+
);
|
|
324
|
+
expect(du).toStrictEqual({});
|
|
325
|
+
spy.mockRestore();
|
|
326
|
+
}
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
test.each([
|
|
330
|
+
['une chaîne', 'string'],
|
|
331
|
+
['un nombre', 1],
|
|
332
|
+
['un booléen', true]
|
|
333
|
+
])('requestInput : champ non enum avec %s comme type est accepté', (_libelle, type) => {
|
|
334
|
+
const spy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
335
|
+
const de = { epaisseur: 5 };
|
|
336
|
+
const du = {};
|
|
337
|
+
expect(requestInput(de, du, 'epaisseur', type)).toBe(5);
|
|
338
|
+
expect(du.epaisseur).toBe(type);
|
|
339
|
+
expect(spy).not.toHaveBeenCalled();
|
|
340
|
+
spy.mockRestore();
|
|
341
|
+
});
|
|
342
|
+
|
|
290
343
|
test('requestInputID : sans type publie les clés et retourne l identifiant', () => {
|
|
291
344
|
const de = { enum_materiaux_id: '1' };
|
|
292
345
|
const du = {};
|