@open3cl/engine 1.8.50 → 1.8.51
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/README.md +14 -14
- package/conso.js +115 -37
- package/conso.spec.js +188 -10
- package/engine.js +13 -5
- package/engine.spec.js +18 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -370,7 +370,7 @@ Ces résultats sont **générés automatiquement** à la fin de `npm run test:co
|
|
|
370
370
|
|
|
371
371
|
<!-- CORPUS:START -->
|
|
372
372
|
|
|
373
|
-
> **Version `1.8.
|
|
373
|
+
> **Version `1.8.50`** · branche `main` · généré le 2026-10-12
|
|
374
374
|
> Seuil de tolérance **5%**
|
|
375
375
|
|
|
376
376
|
<table>
|
|
@@ -403,24 +403,24 @@ Ces résultats sont **générés automatiquement** à la fin de `npm run test:co
|
|
|
403
403
|
|
|
404
404
|
<table>
|
|
405
405
|
<tr>
|
|
406
|
-
<td align="center"><strong>5,
|
|
407
|
-
<td align="center"><strong>
|
|
408
|
-
<td align="center"><strong>0,
|
|
409
|
-
<td align="center"><strong>
|
|
406
|
+
<td align="center"><strong>5,86 ms</strong><br/><sub>moyenne</sub></td>
|
|
407
|
+
<td align="center"><strong>5,18 ms</strong><br/><sub>médiane</sub></td>
|
|
408
|
+
<td align="center"><strong>0,12 ms</strong><br/><sub>min</sub></td>
|
|
409
|
+
<td align="center"><strong>549,4 ms</strong><br/><sub>max</sub></td>
|
|
410
410
|
</tr>
|
|
411
411
|
</table>
|
|
412
412
|
|
|
413
413
|
| Corpus | Moyenne | Médiane | Min | Max | p95 | p99 |
|
|
414
414
|
| :--------------------------------------------- | ------: | ------: | ------: | -------: | ------: | ------: |
|
|
415
|
-
| **Généraliste** | 4,
|
|
416
|
-
| **Appartement · chauffage individuel (2025)** | 4,
|
|
417
|
-
| **Logement individuel (2025)** | 5,
|
|
418
|
-
| **Maison individuelle (2025)** | 6,
|
|
419
|
-
| **Immeuble · chauffage individuel** |
|
|
420
|
-
| **Appartement · chauffage collectif (2025)** | 4,
|
|
421
|
-
| **Immeuble · chauffage collectif** |
|
|
422
|
-
| **Immeuble · chauffage mixte** | 5,
|
|
423
|
-
| **Individuel généré depuis l'immeuble (2026)** | 6,
|
|
415
|
+
| **Généraliste** | 4,94 ms | 4,45 ms | 0,23 ms | 35,1 ms | 9,08 ms | 13,0 ms |
|
|
416
|
+
| **Appartement · chauffage individuel (2025)** | 4,79 ms | 4,42 ms | 1,59 ms | 39,2 ms | 8,31 ms | 11,2 ms |
|
|
417
|
+
| **Logement individuel (2025)** | 5,40 ms | 4,84 ms | 1,68 ms | 42,3 ms | 10,0 ms | 13,4 ms |
|
|
418
|
+
| **Maison individuelle (2025)** | 6,47 ms | 5,87 ms | 1,94 ms | 55,6 ms | 11,4 ms | 14,6 ms |
|
|
419
|
+
| **Immeuble · chauffage individuel** | 7,31 ms | 6,23 ms | 0,12 ms | 153,7 ms | 14,9 ms | 23,5 ms |
|
|
420
|
+
| **Appartement · chauffage collectif (2025)** | 4,94 ms | 4,79 ms | 1,61 ms | 34,7 ms | 7,75 ms | 9,99 ms |
|
|
421
|
+
| **Immeuble · chauffage collectif** | 6,38 ms | 5,54 ms | 0,13 ms | 119,4 ms | 11,9 ms | 21,2 ms |
|
|
422
|
+
| **Immeuble · chauffage mixte** | 5,90 ms | 4,82 ms | 0,22 ms | 549,4 ms | 12,7 ms | 20,4 ms |
|
|
423
|
+
| **Individuel généré depuis l'immeuble (2026)** | 6,63 ms | 5,69 ms | 1,75 ms | 67,7 ms | 12,3 ms | 20,8 ms |
|
|
424
424
|
|
|
425
425
|
<sub>La moyenne est tirée vers le haut par les DPE collectifs, dont le coût atteint plusieurs
|
|
426
426
|
dizaines de fois la médiane : c’est la médiane qui décrit le cas courant, et p95/p99 le cas
|
package/conso.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import enums from './enums.js';
|
|
2
2
|
import calc_conso_eclairage from './16_conso_eclairage.js';
|
|
3
3
|
import tvs from './tv.js';
|
|
4
|
-
import { tv } from './utils.js';
|
|
4
|
+
import { bug_for_bug_compat, tv } from './utils.js';
|
|
5
5
|
|
|
6
6
|
export const COEFF_EP_2_3 = 2.3;
|
|
7
7
|
export const COEFF_EP_1_9 = 1.9;
|
|
@@ -311,53 +311,130 @@ function getConso(coef, type_energie, conso) {
|
|
|
311
311
|
return conso;
|
|
312
312
|
}
|
|
313
313
|
|
|
314
|
+
/**
|
|
315
|
+
* Arrêtés « réseaux de chaleur » et table de valeurs associée (modèle de données de l'observatoire
|
|
316
|
+
* DPE, `modele_donnee/arrete_reseau_chaleur.json`). Le nom de la table ne correspond pas à l'année
|
|
317
|
+
* de l'arrêté (ex. l'arrêté du 05/07/2024 publie la table `reseau_chaleur_2023`).
|
|
318
|
+
* - date_arrete : valeur attendue dans `date_arrete_reseau_chaleur` ;
|
|
319
|
+
* - date_application : date à partir de laquelle l'arrêté s'applique (DPE sans date d'arrêté).
|
|
320
|
+
*/
|
|
321
|
+
export const ARRETES_RESEAU_CHALEUR = [
|
|
322
|
+
{ table: 'reseau_chaleur_2020', date_arrete: '2020-10-12', date_application: '2020-10-17' },
|
|
323
|
+
{ table: 'reseau_chaleur_2021', date_arrete: '2021-10-21', date_application: '2022-01-18' },
|
|
324
|
+
{ table: 'reseau_chaleur_2022', date_arrete: '2023-03-16', date_application: '2023-04-16' },
|
|
325
|
+
{ table: 'reseau_chaleur_2023', date_arrete: '2024-07-05', date_application: '2024-08-09' },
|
|
326
|
+
{ table: 'reseau_chaleur_2024', date_arrete: '2025-04-11', date_application: '2025-05-26' },
|
|
327
|
+
{ table: 'reseau_chaleur_2025', date_arrete: '2026-04-25', date_application: '2026-05-25' }
|
|
328
|
+
];
|
|
329
|
+
|
|
330
|
+
/** Facteur « AUTRES RESEAUX DE CHALEUR » (réseau non répertorié ou inconnu), kgCO2/kWh */
|
|
331
|
+
export const COEF_GES_AUTRES_RESEAUX_CHALEUR = 0.385;
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Normalise une date (chaîne ISO ou Date) au format AAAA-MM-JJ, null si invalide.
|
|
335
|
+
* @param date {string|Date|undefined}
|
|
336
|
+
* @returns {string|null}
|
|
337
|
+
*/
|
|
338
|
+
function isoDate(date) {
|
|
339
|
+
if (typeof date !== 'string' && !(date instanceof Date)) return null;
|
|
340
|
+
const d = new Date(date);
|
|
341
|
+
return isNaN(d.getTime()) ? null : d.toISOString().slice(0, 10);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Index (dans ARRETES_RESEAU_CHALEUR) de l'arrêté réseau de chaleur utilisé par le générateur :
|
|
346
|
+
* - `date_arrete_reseau_chaleur` renseignée : dernier arrêté publié à cette date ou avant ;
|
|
347
|
+
* - sinon : dernier arrêté applicable à la date d'établissement du DPE ;
|
|
348
|
+
* - date antérieure au premier arrêté ou illisible : premier arrêté.
|
|
349
|
+
* @param dateArrete {string|undefined} date_arrete_reseau_chaleur du générateur
|
|
350
|
+
* @param dateDpe {string|undefined} date d'établissement du DPE
|
|
351
|
+
* @returns {number}
|
|
352
|
+
*/
|
|
353
|
+
export function indexArreteReseauChaleur(dateArrete, dateDpe) {
|
|
354
|
+
const arrete = isoDate(dateArrete);
|
|
355
|
+
const ref = arrete ?? isoDate(dateDpe);
|
|
356
|
+
const champ = arrete ? 'date_arrete' : 'date_application';
|
|
357
|
+
let index = 0;
|
|
358
|
+
if (ref) {
|
|
359
|
+
ARRETES_RESEAU_CHALEUR.forEach((a, i) => {
|
|
360
|
+
if (a[champ] <= ref) index = i;
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
return index;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Contenu CO2 d'un réseau de chaleur répertorié (kgCO2/kWh).
|
|
368
|
+
*
|
|
369
|
+
* Le facteur réglementaire est le contenu CO2 en analyse de cycle de vie (`contenu_co2_acv`) de la
|
|
370
|
+
* table publiée par l'arrêté utilisé. Si l'identifiant n'y figure pas (réseau retiré d'un arrêté plus
|
|
371
|
+
* récent), on retient la table antérieure la plus récente qui le contient. Réseau introuvable :
|
|
372
|
+
* facteur « autres réseaux de chaleur » (0,385).
|
|
373
|
+
*
|
|
374
|
+
* Mode bug_for_bug_compat : avec l'arrêté du 05/07/2024 (table reseau_chaleur_2023), LICIEL
|
|
375
|
+
* transmet au moteur le contenu CO2 hors ACV (`contenu_co2`) : 1 226/1 234 générateurs LICIEL des
|
|
376
|
+
* corpus immeuble, alors que DPEWIN, Pleiades et 4.1.1 exportent la valeur ACV (ex. réseau 6403C
|
|
377
|
+
* de Pau : 0,047 au lieu de 0,061).
|
|
378
|
+
*
|
|
379
|
+
* @param identifiant_reseau {string}
|
|
380
|
+
* @param dateArrete {string|undefined}
|
|
381
|
+
* @param dateDpe {string|undefined}
|
|
382
|
+
* @param versionLogiciel {string|undefined} administratif.diagnostiqueur.version_logiciel
|
|
383
|
+
* @returns {number}
|
|
384
|
+
*/
|
|
385
|
+
export function getContenuCo2ReseauChaleur(
|
|
386
|
+
identifiant_reseau,
|
|
387
|
+
dateArrete,
|
|
388
|
+
dateDpe,
|
|
389
|
+
versionLogiciel
|
|
390
|
+
) {
|
|
391
|
+
const index = indexArreteReseauChaleur(dateArrete, dateDpe);
|
|
392
|
+
|
|
393
|
+
for (let i = index; i >= 0; i--) {
|
|
394
|
+
const { table } = ARRETES_RESEAU_CHALEUR[i];
|
|
395
|
+
const row = tv(table, { identifiant_reseau });
|
|
396
|
+
if (row) {
|
|
397
|
+
const contenuHorsAcv =
|
|
398
|
+
bug_for_bug_compat &&
|
|
399
|
+
table === 'reseau_chaleur_2023' &&
|
|
400
|
+
/liciel/i.test(versionLogiciel || '');
|
|
401
|
+
return Number(contenuHorsAcv ? row.contenu_co2 : row.contenu_co2_acv);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
console.error(
|
|
406
|
+
`Aucun coefficient GES trouvé pour le réseau de chaleur ${identifiant_reseau} : valeur « autres réseaux de chaleur » ${COEF_GES_AUTRES_RESEAUX_CHALEUR}`
|
|
407
|
+
);
|
|
408
|
+
return COEF_GES_AUTRES_RESEAUX_CHALEUR;
|
|
409
|
+
}
|
|
410
|
+
|
|
314
411
|
/**
|
|
315
412
|
* Récupération du coefficient d'émission GES pour le générateur
|
|
316
413
|
* @param gen_ch {GenerateurChauffageItem||GenerateurEcsItem}
|
|
317
414
|
* @param suffix {'ch'|'ecs'}
|
|
318
415
|
* @param dateDpe {string} date d'établissement du DPE
|
|
416
|
+
* @param versionLogiciel {string} version du logiciel de saisie
|
|
319
417
|
* @returns {number}
|
|
320
418
|
*/
|
|
321
|
-
function getGesCoeffForGenerateur(gen_ch, suffix, dateDpe) {
|
|
419
|
+
function getGesCoeffForGenerateur(gen_ch, suffix, dateDpe, versionLogiciel) {
|
|
322
420
|
const typeEnergie = getTypeEnergie(gen_ch.donnee_entree, suffix);
|
|
323
421
|
|
|
324
|
-
let anneeDpe;
|
|
325
|
-
try {
|
|
326
|
-
if (gen_ch.donnee_entree.date_arrete_reseau_chaleur) {
|
|
327
|
-
anneeDpe = new Date(gen_ch.donnee_entree.date_arrete_reseau_chaleur).getFullYear() - 1;
|
|
328
|
-
} else {
|
|
329
|
-
anneeDpe = new Date(dateDpe).getFullYear() - 1;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
if (anneeDpe < 2024) {
|
|
333
|
-
anneeDpe = 2022;
|
|
334
|
-
}
|
|
335
|
-
} catch (e) {
|
|
336
|
-
anneeDpe = 2022;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
const key_reseau_chaleur = `reseau_chaleur_${anneeDpe}`;
|
|
340
|
-
|
|
341
422
|
/**
|
|
342
423
|
* Cas spécifique des réseaux de chaleur urbain qui ont des coefficients différents en fonction de réseau de distribution
|
|
343
424
|
*/
|
|
344
425
|
if (typeEnergie === 'réseau de chauffage urbain') {
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
if (
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
Aucun coefficient GES trouvé pour le réseau de chaleur ${identifiant_reseau}
|
|
355
|
-
`);
|
|
356
|
-
}
|
|
357
|
-
} else {
|
|
358
|
-
// Prendre la valeur pour "AUTRES RESEAUX DE CHALEUR"
|
|
359
|
-
return 0.385;
|
|
426
|
+
const { identifiant_reseau_chaleur, date_arrete_reseau_chaleur } = gen_ch.donnee_entree;
|
|
427
|
+
|
|
428
|
+
if (identifiant_reseau_chaleur) {
|
|
429
|
+
return getContenuCo2ReseauChaleur(
|
|
430
|
+
identifiant_reseau_chaleur,
|
|
431
|
+
date_arrete_reseau_chaleur,
|
|
432
|
+
dateDpe,
|
|
433
|
+
versionLogiciel
|
|
434
|
+
);
|
|
360
435
|
}
|
|
436
|
+
// Prendre la valeur pour "AUTRES RESEAUX DE CHALEUR"
|
|
437
|
+
return COEF_GES_AUTRES_RESEAUX_CHALEUR;
|
|
361
438
|
}
|
|
362
439
|
|
|
363
440
|
return coef_ges[typeEnergie] ?? 1;
|
|
@@ -377,7 +454,8 @@ export default function calc_conso(
|
|
|
377
454
|
coeffEp,
|
|
378
455
|
nbLogements = 1,
|
|
379
456
|
ratioSurfaceCollectif = 1,
|
|
380
|
-
facteursAc = {}
|
|
457
|
+
facteursAc = {},
|
|
458
|
+
versionLogiciel = undefined
|
|
381
459
|
) {
|
|
382
460
|
const gen_ch = ch.reduce((acc, ch) => {
|
|
383
461
|
const generateur_chauffage = ch.generateur_chauffage_collection.generateur_chauffage;
|
|
@@ -393,7 +471,7 @@ export default function calc_conso(
|
|
|
393
471
|
ch.donnee_entree.enum_methode_calcul_conso_id;
|
|
394
472
|
value.donnee_entree.enum_type_installation_id = ch.donnee_entree.enum_type_installation_id;
|
|
395
473
|
(value.donnee_utilisateur = value.donnee_utilisateur || {}).coeffEmissionGes =
|
|
396
|
-
getGesCoeffForGenerateur(value, 'ch', dateDpe);
|
|
474
|
+
getGesCoeffForGenerateur(value, 'ch', dateDpe, versionLogiciel);
|
|
397
475
|
});
|
|
398
476
|
return acc.concat(generateur_chauffage);
|
|
399
477
|
}, []);
|
|
@@ -408,7 +486,7 @@ export default function calc_conso(
|
|
|
408
486
|
value.donnee_entree.cle_repartition_ecs =
|
|
409
487
|
(ecs.donnee_entree.cle_repartition_ecs || 1) * (ecs.donnee_entree.rdim || 1);
|
|
410
488
|
(value.donnee_utilisateur = value.donnee_utilisateur || {}).coeffEmissionGes =
|
|
411
|
-
getGesCoeffForGenerateur(value, 'ecs', dateDpe);
|
|
489
|
+
getGesCoeffForGenerateur(value, 'ecs', dateDpe, versionLogiciel);
|
|
412
490
|
});
|
|
413
491
|
}
|
|
414
492
|
return acc.concat(generateur_ecs);
|
package/conso.spec.js
CHANGED
|
@@ -37,13 +37,21 @@ vi.mock('./tv.js', () => ({
|
|
|
37
37
|
}
|
|
38
38
|
}));
|
|
39
39
|
|
|
40
|
+
/** Mode bug_for_bug_compat piloté par les tests (export « live » du module utils mocké). */
|
|
41
|
+
const utilsState = vi.hoisted(() => ({ bugForBugCompat: false }));
|
|
42
|
+
|
|
43
|
+
/** Lecture des tables par défaut : réseau de chaleur "R1" connu, "R2" inconnu. */
|
|
44
|
+
const tvParDefaut = (table, matcher) => {
|
|
45
|
+
if (matcher?.identifiant_reseau === 'R1') return { contenu_co2_acv: 0.15 };
|
|
46
|
+
return null;
|
|
47
|
+
};
|
|
48
|
+
|
|
40
49
|
vi.mock('./utils.js', () => ({
|
|
41
50
|
set_bug_for_bug_compat: vi.fn(),
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
})
|
|
51
|
+
get bug_for_bug_compat() {
|
|
52
|
+
return utilsState.bugForBugCompat;
|
|
53
|
+
},
|
|
54
|
+
tv: vi.fn((table, matcher) => tvParDefaut(table, matcher))
|
|
47
55
|
}));
|
|
48
56
|
|
|
49
57
|
const {
|
|
@@ -58,8 +66,13 @@ const {
|
|
|
58
66
|
getAuxGenerationEcs,
|
|
59
67
|
DATE_BAREME_COUT_2024,
|
|
60
68
|
appointBijonction,
|
|
61
|
-
coutBijonction
|
|
69
|
+
coutBijonction,
|
|
70
|
+
ARRETES_RESEAU_CHALEUR,
|
|
71
|
+
COEF_GES_AUTRES_RESEAUX_CHALEUR,
|
|
72
|
+
indexArreteReseauChaleur,
|
|
73
|
+
getContenuCo2ReseauChaleur
|
|
62
74
|
} = await import('./conso.js');
|
|
75
|
+
const { tv } = await import('./utils.js');
|
|
63
76
|
|
|
64
77
|
/**
|
|
65
78
|
* Classe énergétique (bilan DPE)
|
|
@@ -432,7 +445,7 @@ describe('calc_conso - agrégation des consommations', () => {
|
|
|
432
445
|
expect(res.emission_ges.emission_ges_ch).toBeCloseTo(150, 9);
|
|
433
446
|
});
|
|
434
447
|
|
|
435
|
-
test('réseau de chaleur inconnu (identifiant sans ligne) : erreur signalée', () => {
|
|
448
|
+
test('réseau de chaleur inconnu (identifiant sans ligne) : erreur signalée, facteur « autres réseaux » 0,385', () => {
|
|
436
449
|
const ch = [
|
|
437
450
|
installCh({ cle_repartition_ch: 1 }, [
|
|
438
451
|
genCh(
|
|
@@ -443,8 +456,10 @@ describe('calc_conso - agrégation des consommations', () => {
|
|
|
443
456
|
])
|
|
444
457
|
];
|
|
445
458
|
|
|
446
|
-
calc_conso(100, 1, 1, [], ch, [], [], 1, 1, DATE_DPE, coef_ep);
|
|
459
|
+
const res = calc_conso(100, 1, 1, [], ch, [], [], 1, 1, DATE_DPE, coef_ep);
|
|
447
460
|
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('R2'));
|
|
461
|
+
// Plus de repli sur 1 kgCO2/kWh (GES = consommation) : 1000 * 0,385
|
|
462
|
+
expect(res.emission_ges.emission_ges_ch).toBeCloseTo(385, 9);
|
|
448
463
|
});
|
|
449
464
|
|
|
450
465
|
test('réseau de chaleur sans identifiant : coefficient GES forfaitaire 0,385', () => {
|
|
@@ -459,8 +474,8 @@ describe('calc_conso - agrégation des consommations', () => {
|
|
|
459
474
|
expect(res.emission_ges.emission_ges_ch).toBeCloseTo(385, 9);
|
|
460
475
|
});
|
|
461
476
|
|
|
462
|
-
test("date de
|
|
463
|
-
// date_arrete_reseau_chaleur = Symbol
|
|
477
|
+
test("date d'arrêté réseau de chaleur invalide : aucune exception", () => {
|
|
478
|
+
// date_arrete_reseau_chaleur = Symbol : ignorée (repli sur la date du DPE)
|
|
464
479
|
const ch = [
|
|
465
480
|
installCh({ cle_repartition_ch: 1 }, [
|
|
466
481
|
genCh(
|
|
@@ -1169,3 +1184,166 @@ describe('appointBijonction / coutBijonction', () => {
|
|
|
1169
1184
|
);
|
|
1170
1185
|
});
|
|
1171
1186
|
});
|
|
1187
|
+
|
|
1188
|
+
/**
|
|
1189
|
+
* Facteur d'émission des réseaux de chaleur urbains : table de l'arrêté utilisé
|
|
1190
|
+
* (modele_donnee/arrete_reseau_chaleur.json de l'observatoire DPE).
|
|
1191
|
+
*/
|
|
1192
|
+
describe('réseaux de chaleur - sélection de l’arrêté et du contenu CO2', () => {
|
|
1193
|
+
let errorSpy;
|
|
1194
|
+
|
|
1195
|
+
beforeEach(() => {
|
|
1196
|
+
errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
1197
|
+
vi.mocked(tv).mockReset();
|
|
1198
|
+
utilsState.bugForBugCompat = false;
|
|
1199
|
+
});
|
|
1200
|
+
|
|
1201
|
+
afterEach(() => {
|
|
1202
|
+
errorSpy.mockRestore();
|
|
1203
|
+
vi.mocked(tv).mockImplementation(tvParDefaut);
|
|
1204
|
+
utilsState.bugForBugCompat = false;
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1207
|
+
/** Tables mockées : identifiant -> { table: ligne } */
|
|
1208
|
+
const mockTables = (lignes) =>
|
|
1209
|
+
vi
|
|
1210
|
+
.mocked(tv)
|
|
1211
|
+
.mockImplementation((table, matcher) => lignes[matcher.identifiant_reseau]?.[table] ?? null);
|
|
1212
|
+
|
|
1213
|
+
test('six arrêtés, dans l’ordre chronologique, table ≠ année de l’arrêté', () => {
|
|
1214
|
+
expect(ARRETES_RESEAU_CHALEUR.map((a) => a.table)).toEqual([
|
|
1215
|
+
'reseau_chaleur_2020',
|
|
1216
|
+
'reseau_chaleur_2021',
|
|
1217
|
+
'reseau_chaleur_2022',
|
|
1218
|
+
'reseau_chaleur_2023',
|
|
1219
|
+
'reseau_chaleur_2024',
|
|
1220
|
+
'reseau_chaleur_2025'
|
|
1221
|
+
]);
|
|
1222
|
+
// L'arrêté du 05/07/2024 publie la table 2023 (cause du bug : 2024 - 1 = 2023 forcé à 2022)
|
|
1223
|
+
expect(ARRETES_RESEAU_CHALEUR[3]).toMatchObject({
|
|
1224
|
+
table: 'reseau_chaleur_2023',
|
|
1225
|
+
date_arrete: '2024-07-05'
|
|
1226
|
+
});
|
|
1227
|
+
const dates = ARRETES_RESEAU_CHALEUR.map((a) => a.date_arrete);
|
|
1228
|
+
expect([...dates].sort()).toEqual(dates);
|
|
1229
|
+
ARRETES_RESEAU_CHALEUR.forEach((a) => expect(a.date_application >= a.date_arrete).toBe(true));
|
|
1230
|
+
});
|
|
1231
|
+
|
|
1232
|
+
test.each([
|
|
1233
|
+
['2020-10-12', 0],
|
|
1234
|
+
['2021-10-21', 1],
|
|
1235
|
+
['2022-01-18', 1], // date hors liste : dernier arrêté publié avant
|
|
1236
|
+
['2023-03-16', 2],
|
|
1237
|
+
['2024-07-05', 3],
|
|
1238
|
+
['2024-07-10', 3],
|
|
1239
|
+
['2025-04-11', 4],
|
|
1240
|
+
['2026-04-25', 5],
|
|
1241
|
+
['2030-01-01', 5],
|
|
1242
|
+
['2019-01-01', 0]
|
|
1243
|
+
])('date_arrete_reseau_chaleur %s : arrêté n°%i', (dateArrete, index) => {
|
|
1244
|
+
// La date du DPE est ignorée quand la date d'arrêté est renseignée
|
|
1245
|
+
expect(indexArreteReseauChaleur(dateArrete, '2030-01-01')).toBe(index);
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
test.each([
|
|
1249
|
+
['2022-01-17', 0],
|
|
1250
|
+
['2022-01-18', 1],
|
|
1251
|
+
['2023-04-15', 1], // arrêté du 16/03/2023 applicable au 16/04/2023 seulement
|
|
1252
|
+
['2023-04-16', 2],
|
|
1253
|
+
['2024-08-09', 3],
|
|
1254
|
+
[undefined, 0],
|
|
1255
|
+
['date invalide', 0]
|
|
1256
|
+
])('sans date d’arrêté, DPE du %s : arrêté applicable n°%i', (dateDpe, index) => {
|
|
1257
|
+
expect(indexArreteReseauChaleur(undefined, dateDpe)).toBe(index);
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1260
|
+
test('date d’arrêté illisible (non chaîne) : repli sur la date du DPE', () => {
|
|
1261
|
+
expect(indexArreteReseauChaleur(Symbol('invalide'), '2024-08-09')).toBe(3);
|
|
1262
|
+
expect(indexArreteReseauChaleur(new Date('2024-07-05'), undefined)).toBe(3);
|
|
1263
|
+
});
|
|
1264
|
+
|
|
1265
|
+
test('arrêté du 05/07/2024 : lecture de reseau_chaleur_2023 et contenu ACV (cas Pau 6403C)', () => {
|
|
1266
|
+
mockTables({
|
|
1267
|
+
'6403C': { reseau_chaleur_2023: { contenu_co2: '0.047', contenu_co2_acv: '0.061' } }
|
|
1268
|
+
});
|
|
1269
|
+
expect(getContenuCo2ReseauChaleur('6403C', '2024-07-05', '2025-01-24', 'Pleiades')).toBe(0.061);
|
|
1270
|
+
expect(tv).toHaveBeenCalledTimes(1);
|
|
1271
|
+
expect(tv).toHaveBeenCalledWith('reseau_chaleur_2023', { identifiant_reseau: '6403C' });
|
|
1272
|
+
});
|
|
1273
|
+
|
|
1274
|
+
test('bug_for_bug_compat + LICIEL + table 2023 : contenu CO2 hors ACV transmis par le logiciel', () => {
|
|
1275
|
+
utilsState.bugForBugCompat = true;
|
|
1276
|
+
mockTables({
|
|
1277
|
+
'6403C': {
|
|
1278
|
+
reseau_chaleur_2023: { contenu_co2: '0.047', contenu_co2_acv: '0.061' },
|
|
1279
|
+
reseau_chaleur_2022: { contenu_co2: '0.1', contenu_co2_acv: '0.2' }
|
|
1280
|
+
}
|
|
1281
|
+
});
|
|
1282
|
+
const liciel = 'LICIEL Diagnostics v4 [Version XML:305]';
|
|
1283
|
+
expect(getContenuCo2ReseauChaleur('6403C', '2024-07-05', '2025-01-24', liciel)).toBe(0.047);
|
|
1284
|
+
// Autre logiciel : valeur ACV
|
|
1285
|
+
expect(getContenuCo2ReseauChaleur('6403C', '2024-07-05', '2025-01-24', 'DPEWIN V5.2.5')).toBe(
|
|
1286
|
+
0.061
|
|
1287
|
+
);
|
|
1288
|
+
expect(getContenuCo2ReseauChaleur('6403C', '2024-07-05', '2025-01-24', undefined)).toBe(0.061);
|
|
1289
|
+
// LICIEL avec un autre arrêté (16/03/2023 -> table 2022) : valeur ACV
|
|
1290
|
+
expect(getContenuCo2ReseauChaleur('6403C', '2023-03-16', '2023-06-01', liciel)).toBe(0.2);
|
|
1291
|
+
});
|
|
1292
|
+
|
|
1293
|
+
test('sans bug_for_bug_compat, LICIEL lit aussi le contenu ACV', () => {
|
|
1294
|
+
mockTables({
|
|
1295
|
+
R: { reseau_chaleur_2023: { contenu_co2: '0.047', contenu_co2_acv: '0.061' } }
|
|
1296
|
+
});
|
|
1297
|
+
expect(getContenuCo2ReseauChaleur('R', '2024-07-05', undefined, 'LICIEL')).toBe(0.061);
|
|
1298
|
+
});
|
|
1299
|
+
|
|
1300
|
+
test('réseau absent de la table de l’arrêté : table antérieure la plus récente qui le contient', () => {
|
|
1301
|
+
mockTables({
|
|
1302
|
+
R: {
|
|
1303
|
+
reseau_chaleur_2021: { contenu_co2: '0.3', contenu_co2_acv: '0.33' },
|
|
1304
|
+
reseau_chaleur_2020: { contenu_co2: '0.4', contenu_co2_acv: '0.44' }
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
expect(getContenuCo2ReseauChaleur('R', '2024-07-05', undefined, undefined)).toBe(0.33);
|
|
1308
|
+
expect(vi.mocked(tv).mock.calls.map((c) => c[0])).toEqual([
|
|
1309
|
+
'reseau_chaleur_2023',
|
|
1310
|
+
'reseau_chaleur_2022',
|
|
1311
|
+
'reseau_chaleur_2021'
|
|
1312
|
+
]);
|
|
1313
|
+
});
|
|
1314
|
+
|
|
1315
|
+
test('réseau introuvable dans toutes les tables : facteur « autres réseaux de chaleur » 0,385 et erreur', () => {
|
|
1316
|
+
mockTables({});
|
|
1317
|
+
expect(COEF_GES_AUTRES_RESEAUX_CHALEUR).toBe(0.385);
|
|
1318
|
+
expect(getContenuCo2ReseauChaleur('XXXX', '2024-07-05', undefined, undefined)).toBe(0.385);
|
|
1319
|
+
expect(tv).toHaveBeenCalledTimes(4);
|
|
1320
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('XXXX'));
|
|
1321
|
+
});
|
|
1322
|
+
|
|
1323
|
+
test('calc_conso : facteur réseau appliqué au chauffage et à l’ECS, version logiciel transmise', () => {
|
|
1324
|
+
utilsState.bugForBugCompat = true;
|
|
1325
|
+
mockTables({
|
|
1326
|
+
'6403C': { reseau_chaleur_2023: { contenu_co2: '0.047', contenu_co2_acv: '0.061' } }
|
|
1327
|
+
});
|
|
1328
|
+
const de = { identifiant_reseau_chaleur: '6403C', date_arrete_reseau_chaleur: '2024-07-05' };
|
|
1329
|
+
const ch = [
|
|
1330
|
+
installCh({ cle_repartition_ch: 1 }, [
|
|
1331
|
+
genCh('3', { conso_ch: 1000, conso_ch_depensier: 1200 }, de)
|
|
1332
|
+
])
|
|
1333
|
+
];
|
|
1334
|
+
const ecs = [
|
|
1335
|
+
installEcs({ cle_repartition_ecs: 1 }, [
|
|
1336
|
+
genEcs('3', { conso_ecs: 500, conso_ecs_depensier: 600 }, de)
|
|
1337
|
+
])
|
|
1338
|
+
];
|
|
1339
|
+
const args = [100, 1, 1, [], ch, ecs, [], 1, 1, '2025-01-24', coef_ep, 1, 1, {}];
|
|
1340
|
+
|
|
1341
|
+
const licielRes = calc_conso(...args, 'LICIEL Diagnostics v4');
|
|
1342
|
+
// 1000 * 0,047 et 500 * 0,047 (valeur ADEME du DPE 2564E0255485Z) — avant : 1000 * 1
|
|
1343
|
+
expect(licielRes.emission_ges.emission_ges_ch).toBeCloseTo(47, 9);
|
|
1344
|
+
expect(licielRes.emission_ges.emission_ges_ecs).toBeCloseTo(23.5, 9);
|
|
1345
|
+
|
|
1346
|
+
const autreRes = calc_conso(...args);
|
|
1347
|
+
expect(autreRes.emission_ges.emission_ges_ch).toBeCloseTo(61, 9);
|
|
1348
|
+
});
|
|
1349
|
+
});
|
package/engine.js
CHANGED
|
@@ -33,7 +33,7 @@ import getFicheTechnique from './ficheTechnique.js';
|
|
|
33
33
|
import { ProductionENR, ratioProrataPvLogement } 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.51';
|
|
37
37
|
|
|
38
38
|
const dpeSanitizerService = new DpeSanitizerService();
|
|
39
39
|
|
|
@@ -85,6 +85,7 @@ export function calcul_3cl(inputDpe, options) {
|
|
|
85
85
|
}
|
|
86
86
|
const modele = enums.modele_dpe[dpe.administratif.enum_modele_dpe_id];
|
|
87
87
|
const dateDpe = dpe.administratif.date_etablissement_dpe;
|
|
88
|
+
const versionLogiciel = dpe.administratif.diagnostiqueur?.version_logiciel;
|
|
88
89
|
if (modele !== 'dpe 3cl 2021 méthode logement') {
|
|
89
90
|
console.error('Moteur dpe non implémenté pour le modèle: ' + modele);
|
|
90
91
|
return null;
|
|
@@ -606,7 +607,9 @@ export function calcul_3cl(inputDpe, options) {
|
|
|
606
607
|
dateDpe,
|
|
607
608
|
coef_ep,
|
|
608
609
|
th === 'immeuble' ? Nb_lgt : 1,
|
|
609
|
-
ratioSurfaceCoutCollectif
|
|
610
|
+
ratioSurfaceCoutCollectif,
|
|
611
|
+
{},
|
|
612
|
+
versionLogiciel
|
|
610
613
|
);
|
|
611
614
|
|
|
612
615
|
// Proratisation au logement d'une installation PV d'immeuble (appartement)
|
|
@@ -648,7 +651,8 @@ export function calcul_3cl(inputDpe, options) {
|
|
|
648
651
|
coef_ep,
|
|
649
652
|
th === 'immeuble' ? Nb_lgt : 1,
|
|
650
653
|
ratioSurfaceCoutCollectif,
|
|
651
|
-
facteursAc
|
|
654
|
+
facteursAc,
|
|
655
|
+
versionLogiciel
|
|
652
656
|
);
|
|
653
657
|
conso.emission_ges = consoNette.emission_ges;
|
|
654
658
|
// Ligne électricité de sortie_par_energie nette de l'autoconsommation (issue #34)
|
|
@@ -669,7 +673,9 @@ export function calcul_3cl(inputDpe, options) {
|
|
|
669
673
|
dateDpe,
|
|
670
674
|
coef_ep_1_7,
|
|
671
675
|
th === 'immeuble' ? Nb_lgt : 1,
|
|
672
|
-
ratioSurfaceCoutCollectif
|
|
676
|
+
ratioSurfaceCoutCollectif,
|
|
677
|
+
{},
|
|
678
|
+
versionLogiciel
|
|
673
679
|
);
|
|
674
680
|
|
|
675
681
|
productionENR.calculateEnr(
|
|
@@ -695,7 +701,9 @@ export function calcul_3cl(inputDpe, options) {
|
|
|
695
701
|
dateDpe,
|
|
696
702
|
coef_ep_2_3,
|
|
697
703
|
th === 'immeuble' ? Nb_lgt : 1,
|
|
698
|
-
ratioSurfaceCoutCollectif
|
|
704
|
+
ratioSurfaceCoutCollectif,
|
|
705
|
+
{},
|
|
706
|
+
versionLogiciel
|
|
699
707
|
);
|
|
700
708
|
|
|
701
709
|
productionENR.calculateEnr(
|
package/engine.spec.js
CHANGED
|
@@ -518,6 +518,24 @@ describe('calcul_3cl - run intégral (maison)', () => {
|
|
|
518
518
|
expect(dpe.logement.sortie.cout).toEqual({ cout_5_usages: 2 });
|
|
519
519
|
});
|
|
520
520
|
|
|
521
|
+
test('version du logiciel de saisie transmise à tous les calculs de conso (facteur GES réseau de chaleur)', () => {
|
|
522
|
+
const dpe = makeDpe();
|
|
523
|
+
dpe.administratif.diagnostiqueur = { version_logiciel: 'LICIEL Diagnostics v4' };
|
|
524
|
+
const facteurs = { ch: 0.8 };
|
|
525
|
+
vi.mocked(calculateEnr).mockImplementationOnce((enr, conso, Sh, th, zc, coef, ratio, f) => {
|
|
526
|
+
Object.assign(f, facteurs);
|
|
527
|
+
return { pv: 1 };
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
calcul_3cl(dpe, { sanitize: false });
|
|
531
|
+
|
|
532
|
+
// Lue avant l'écrasement de `diagnostiqueur` par la version du moteur
|
|
533
|
+
expect(calc_conso).toHaveBeenCalledTimes(4);
|
|
534
|
+
calc_conso.mock.calls.forEach((call) => expect(call[14]).toBe('LICIEL Diagnostics v4'));
|
|
535
|
+
expect(calc_conso.mock.calls[0][13]).toEqual({});
|
|
536
|
+
expect(calc_conso.mock.calls[1][13]).toEqual(facteurs);
|
|
537
|
+
});
|
|
538
|
+
|
|
521
539
|
test('bâtiment matériaux anciens => ilpa = 1 ; incohérence inertie => erreur loggée', () => {
|
|
522
540
|
const dpe = makeDpe();
|
|
523
541
|
dpe.logement.meteo.batiment_materiaux_anciens = 1;
|