@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/3.3_baie_vitree.spec.js
CHANGED
|
@@ -234,6 +234,16 @@ describe('tv_uw - coefficient de transmission de la fenêtre', () => {
|
|
|
234
234
|
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('Pas de valeur forfaitaire uw'));
|
|
235
235
|
});
|
|
236
236
|
|
|
237
|
+
test('Ug exactement tabulé sans ligne uw : uw indéfini et erreur émise', () => {
|
|
238
|
+
// delta_ug = 0 (bornes identiques) mais aucune ligne uw pour ce Ug
|
|
239
|
+
rows.uw = null;
|
|
240
|
+
const bv = baseBv();
|
|
241
|
+
calc_bv(bv, 1);
|
|
242
|
+
|
|
243
|
+
expect(bv.donnee_intermediaire.uw).toBeUndefined();
|
|
244
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('Pas de valeur forfaitaire uw'));
|
|
245
|
+
});
|
|
246
|
+
|
|
237
247
|
test('interpolation entre deux Ug tabulés (delta_ug ≠ 0)', () => {
|
|
238
248
|
rows.ug = { ug: '3', tv_ug_id: '5' };
|
|
239
249
|
getRange.mockReturnValue([2.8, 4]);
|
|
@@ -810,6 +810,33 @@ describe('calc_pont_thermique - récupération du mur via la baie vitrée (compa
|
|
|
810
810
|
expect(errorSpy).toHaveBeenCalled();
|
|
811
811
|
});
|
|
812
812
|
|
|
813
|
+
test('hors compatibilité bug : la baie vitrée n’est pas consultée pour retrouver le mur', () => {
|
|
814
|
+
state.bug = false;
|
|
815
|
+
tv.mockReturnValue({ k: '0.5', tv_pont_thermique_id: '42' });
|
|
816
|
+
const mur = [
|
|
817
|
+
{
|
|
818
|
+
donnee_entree: { reference: 'M1', enum_type_adjacence_id: '1', type_isolation: 'iti' },
|
|
819
|
+
donnee_utilisateur: {}
|
|
820
|
+
}
|
|
821
|
+
];
|
|
822
|
+
// Même configuration que le cas bug_for_bug : reference_paroi = index du mur (0)
|
|
823
|
+
const bv = [{ donnee_entree: { reference: 'BV1', reference_paroi: '0' } }];
|
|
824
|
+
const pt = pontThermique({
|
|
825
|
+
type_liaison: 'refend / mur',
|
|
826
|
+
reference_1: 'BV1',
|
|
827
|
+
description: 'refend'
|
|
828
|
+
});
|
|
829
|
+
|
|
830
|
+
calc_pont_thermique(pt, '1', logement({ mur, bv }));
|
|
831
|
+
|
|
832
|
+
// Mur non retrouvé via la baie : isolation ITI par défaut, aucun message de récupération
|
|
833
|
+
expect(tv).toHaveBeenCalledWith('pont_thermique', {
|
|
834
|
+
enum_type_liaison_id: '12',
|
|
835
|
+
isolation_mur: '^iti'
|
|
836
|
+
});
|
|
837
|
+
expect(errorSpy).not.toHaveBeenCalledWith(expect.stringContaining('Aucun mur trouvé'));
|
|
838
|
+
});
|
|
839
|
+
|
|
813
840
|
test('reference_paroi ne correspondant pas à l’index du mur : aucun mur retrouvé (isolation ITI par défaut)', () => {
|
|
814
841
|
state.bug = true;
|
|
815
842
|
tv.mockReturnValue({ k: '0.5', tv_pont_thermique_id: '42' });
|
package/4_ventilation.spec.js
CHANGED
|
@@ -146,6 +146,18 @@ describe('calc_ventilation - orchestration', () => {
|
|
|
146
146
|
);
|
|
147
147
|
});
|
|
148
148
|
|
|
149
|
+
test('surface ventilée égale à Sh : conservée sans application de clé de répartition', () => {
|
|
150
|
+
const vt = ventilation({
|
|
151
|
+
surface_ventile: 100,
|
|
152
|
+
cle_repartition_ventilation: 2,
|
|
153
|
+
q4pa_conv_saisi: 1.3
|
|
154
|
+
});
|
|
155
|
+
calc_ventilation(vt, CG, 'th', 80, 100, [], [], [], []);
|
|
156
|
+
|
|
157
|
+
// surface_ventile = Sh = 100 (clé ignorée) => Hvent = 0,34 * 1,2 * 100
|
|
158
|
+
expect(vt.donnee_intermediaire.hvent).toBeCloseTo(40.8, 9);
|
|
159
|
+
});
|
|
160
|
+
|
|
149
161
|
test('surface ventilée différente de Sh sans clé de répartition : divisée par 1', () => {
|
|
150
162
|
// Branche `de.cle_repartition_ventilation || 1` : surface_ventile conservée telle quelle
|
|
151
163
|
const vt = ventilation({ surface_ventile: 50, q4pa_conv_saisi: 1.3 });
|
package/7_inertie.spec.js
CHANGED
|
@@ -54,6 +54,16 @@ describe('Inertie unit tests', () => {
|
|
|
54
54
|
}
|
|
55
55
|
);
|
|
56
56
|
|
|
57
|
+
test('plancher haut non lourd (type 9) isolé par ITE ou non isolé : inertie 0', () => {
|
|
58
|
+
expect(
|
|
59
|
+
inertie.calculateInertiePhLourd({ enum_type_isolation_id: 2, enum_type_plancher_haut_id: 9 })
|
|
60
|
+
).toBe(0);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('type de plancher haut inconnu avec isolation ITE : considéré lourd, inertie 1', () => {
|
|
64
|
+
expect(inertie.calculateInertiePhLourd({ enum_type_isolation_id: 4 })).toBe(1);
|
|
65
|
+
});
|
|
66
|
+
|
|
57
67
|
test.each([
|
|
58
68
|
[1, 1, 0, 2, 0],
|
|
59
69
|
[1, 1, 0, 3, 0],
|
package/9_besoin_ch.spec.js
CHANGED
|
@@ -93,6 +93,10 @@ describe('calc_Fj - fraction des besoins de chauffage couverte par les apports',
|
|
|
93
93
|
expect(lourde).not.toBeCloseTo(legere, 9);
|
|
94
94
|
});
|
|
95
95
|
|
|
96
|
+
test('une inertie inconnue ne définit pas alpha : le résultat n’est pas un nombre', () => {
|
|
97
|
+
expect(calc_Fj(100, 2000, 5000, 1000, 'inconnue')).toBeNaN();
|
|
98
|
+
});
|
|
99
|
+
|
|
96
100
|
test('les inerties "très lourde" et "lourde" partagent le même alpha', () => {
|
|
97
101
|
expect(calc_Fj(100, 2000, 5000, 1000, 'très lourde')).toBeCloseTo(
|
|
98
102
|
calc_Fj(100, 2000, 5000, 1000, 'lourde'),
|
package/9_conso_ch.js
CHANGED
package/9_emetteur_ch.spec.js
CHANGED
|
@@ -23,7 +23,9 @@ vi.mock('./utils.js', () => ({
|
|
|
23
23
|
}));
|
|
24
24
|
|
|
25
25
|
vi.mock('./core/tv/infrastructure/tvs.store.js', () => ({
|
|
26
|
-
TvsStore: vi.fn(()
|
|
26
|
+
TvsStore: vi.fn(function () {
|
|
27
|
+
return mockTvsStore;
|
|
28
|
+
})
|
|
27
29
|
}));
|
|
28
30
|
|
|
29
31
|
const { rendement_emission, calc_emetteur_ch, isIntermittenceCollectiveSaisie } =
|
package/9_generateur_ch.spec.js
CHANGED
|
@@ -342,6 +342,17 @@ describe('checkForGeneratorType - classification du générateur', () => {
|
|
|
342
342
|
expect(de.enum_type_generateur_ch_id).toBe('119');
|
|
343
343
|
});
|
|
344
344
|
|
|
345
|
+
test('cas 119 par défaut avec ligne de rendement sans type de générateur : aucune réécriture', () => {
|
|
346
|
+
vi.mocked(requestInputID).mockReturnValue('119');
|
|
347
|
+
vi.mocked(tv).mockReturnValue({ enum_type_generateur_ch_id: undefined });
|
|
348
|
+
const de = { tv_rendement_generation_id: 3, enum_type_generateur_ch_id: '119' };
|
|
349
|
+
const du = {};
|
|
350
|
+
checkForGeneratorType({}, de, {}, du);
|
|
351
|
+
expect(de.enum_type_generateur_ch_id).toBe('119');
|
|
352
|
+
expect(du.isPacGenerator).toBe(false);
|
|
353
|
+
expect(du.isCombustionGenerator).toBe(false);
|
|
354
|
+
});
|
|
355
|
+
|
|
345
356
|
/**
|
|
346
357
|
* Chaudière fioul par défaut : chaudière standard datant de la construction du bâtiment.
|
|
347
358
|
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §17.2.1.1
|
package/README.md
CHANGED
|
@@ -383,7 +383,7 @@ Ces résultats sont **générés automatiquement** à la fin de `npm run test:co
|
|
|
383
383
|
|
|
384
384
|
<!-- CORPUS:START -->
|
|
385
385
|
|
|
386
|
-
> **Version `1.
|
|
386
|
+
> **Version `1.11.2`** · à publier · branche `main` · généré le 2026-10-30
|
|
387
387
|
> Seuil de tolérance **5%**
|
|
388
388
|
|
|
389
389
|
<table>
|
|
@@ -416,24 +416,24 @@ Ces résultats sont **générés automatiquement** à la fin de `npm run test:co
|
|
|
416
416
|
|
|
417
417
|
<table>
|
|
418
418
|
<tr>
|
|
419
|
-
<td align="center"><strong>5,
|
|
420
|
-
<td align="center"><strong>
|
|
419
|
+
<td align="center"><strong>5,99 ms</strong><br/><sub>moyenne</sub></td>
|
|
420
|
+
<td align="center"><strong>5,23 ms</strong><br/><sub>médiane</sub></td>
|
|
421
421
|
<td align="center"><strong>0,11 ms</strong><br/><sub>min</sub></td>
|
|
422
|
-
<td align="center"><strong>
|
|
422
|
+
<td align="center"><strong>641,5 ms</strong><br/><sub>max</sub></td>
|
|
423
423
|
</tr>
|
|
424
424
|
</table>
|
|
425
425
|
|
|
426
426
|
| Corpus | Moyenne | Médiane | Min | Max | p95 | p99 |
|
|
427
427
|
| :--------------------------------------------- | ------: | ------: | ------: | -------: | ------: | ------: |
|
|
428
|
-
| **Appartement · chauffage individuel (2025)** | 4,
|
|
429
|
-
| **Appartement · chauffage collectif (2025)** | 4,
|
|
430
|
-
| **Logement individuel (2025)** | 5,
|
|
431
|
-
| **Maison individuelle (2025)** | 6,
|
|
432
|
-
| **Maison individuelle (2026)** | 6,
|
|
433
|
-
| **Immeuble · chauffage collectif** | 5,
|
|
434
|
-
| **Immeuble · chauffage individuel** | 7,
|
|
435
|
-
| **Immeuble · chauffage mixte** |
|
|
436
|
-
| **Individuel généré depuis l'immeuble (2026)** | 6,
|
|
428
|
+
| **Appartement · chauffage individuel (2025)** | 4,09 ms | 3,70 ms | 1,56 ms | 47,5 ms | 7,05 ms | 9,52 ms |
|
|
429
|
+
| **Appartement · chauffage collectif (2025)** | 4,98 ms | 4,64 ms | 1,66 ms | 41,1 ms | 8,18 ms | 10,7 ms |
|
|
430
|
+
| **Logement individuel (2025)** | 5,36 ms | 4,70 ms | 1,75 ms | 61,9 ms | 10,0 ms | 14,0 ms |
|
|
431
|
+
| **Maison individuelle (2025)** | 6,37 ms | 5,70 ms | 1,93 ms | 58,0 ms | 11,5 ms | 15,4 ms |
|
|
432
|
+
| **Maison individuelle (2026)** | 6,43 ms | 5,76 ms | 2,16 ms | 48,3 ms | 11,7 ms | 15,7 ms |
|
|
433
|
+
| **Immeuble · chauffage collectif** | 5,75 ms | 4,90 ms | 0,11 ms | 102,7 ms | 10,6 ms | 17,6 ms |
|
|
434
|
+
| **Immeuble · chauffage individuel** | 7,49 ms | 6,40 ms | 0,11 ms | 127,0 ms | 15,3 ms | 23,9 ms |
|
|
435
|
+
| **Immeuble · chauffage mixte** | 6,57 ms | 5,44 ms | 0,19 ms | 641,5 ms | 13,8 ms | 23,4 ms |
|
|
436
|
+
| **Individuel généré depuis l'immeuble (2026)** | 6,88 ms | 5,83 ms | 1,86 ms | 79,6 ms | 12,9 ms | 21,8 ms |
|
|
437
437
|
|
|
438
438
|
<sub>La moyenne est tirée vers le haut par les DPE collectifs, dont le coût atteint plusieurs
|
|
439
439
|
dizaines de fois la médiane : c’est la médiane qui décrit le cas courant, et p95/p99 le cas
|
package/apport_et_besoin.spec.js
CHANGED
|
@@ -22,7 +22,11 @@ vi.mock('./6.2_surface_sud_equivalente.js', () => ({ calc_sse: vi.fn() }));
|
|
|
22
22
|
vi.mock('./10_besoin_fr.js', () => ({ default: vi.fn() }));
|
|
23
23
|
vi.mock('./11_besoin_ecs.js', () => ({ default: vi.fn() }));
|
|
24
24
|
vi.mock('./6.1_apport_gratuit.js', () => ({ calc_ai: vi.fn(), calc_as: vi.fn() }));
|
|
25
|
-
vi.mock('./11_nadeq.js', () => ({
|
|
25
|
+
vi.mock('./11_nadeq.js', () => ({
|
|
26
|
+
Nadeq: vi.fn(function () {
|
|
27
|
+
return { calculateNadeq };
|
|
28
|
+
})
|
|
29
|
+
}));
|
|
26
30
|
|
|
27
31
|
const { default: calc_apport_et_besoin } = await import('./apport_et_besoin.js');
|
|
28
32
|
const { calc_sse } = await import('./6.2_surface_sud_equivalente.js');
|
|
@@ -4,25 +4,6 @@ import { UPB_ADDITIONAL_VALUES } from '../../tv/infrastructure/assets/additional
|
|
|
4
4
|
* Add additional values for ue
|
|
5
5
|
*/
|
|
6
6
|
export class AddAdditionnalUeValuesTables {
|
|
7
|
-
/**
|
|
8
|
-
* @type {FileStore}
|
|
9
|
-
*/
|
|
10
|
-
#fileStore;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @type {ApplicationConfig}
|
|
14
|
-
*/
|
|
15
|
-
#appConfig;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @param fileStore {FileStore}
|
|
19
|
-
* @param appConfig {ApplicationConfig}
|
|
20
|
-
*/
|
|
21
|
-
constructor(fileStore, appConfig) {
|
|
22
|
-
this.#fileStore = fileStore;
|
|
23
|
-
this.#appConfig = appConfig;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
7
|
/**
|
|
27
8
|
* Ajout de valeurs supplémentaires pour le calcul du facteur Ue pour les déperditions plancher_bas
|
|
28
9
|
* @param tableValues
|
|
@@ -3,12 +3,11 @@ import { AddAdditionnalUeValuesTables } from './add-additionnal-ue-values-tables
|
|
|
3
3
|
import { UPB_ADDITIONAL_VALUES } from '../../tv/infrastructure/assets/additional-ue-values.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* dans la table `ue`.
|
|
6
|
+
* Le service n'a aucune dépendance : on vérifie la logique d'aplatissement des
|
|
7
|
+
* valeurs Ue additionnelles dans la table `ue`.
|
|
9
8
|
*/
|
|
10
9
|
describe('AddAdditionnalUeValuesTables', () => {
|
|
11
|
-
const buildService = () => new AddAdditionnalUeValuesTables(
|
|
10
|
+
const buildService = () => new AddAdditionnalUeValuesTables();
|
|
12
11
|
|
|
13
12
|
test('ajoute toutes les valeurs additionnelles dans la table ue', () => {
|
|
14
13
|
const nbAttendu = UPB_ADDITIONAL_VALUES.reduce((acc, entree) => acc + entree.values.length, 0);
|
|
@@ -69,4 +69,29 @@ describe('SynchronizeDpeGesLimitValuesTables unit tests', () => {
|
|
|
69
69
|
});
|
|
70
70
|
});
|
|
71
71
|
});
|
|
72
|
+
test('regroupe plusieurs surfaces sous une même classe d’altitude', () => {
|
|
73
|
+
const fileStore = new FileStore();
|
|
74
|
+
const appConfig = new ApplicationConfig();
|
|
75
|
+
const synchronizeDpeGesLimitValuesTables = new SynchronizeDpeGesLimitValuesTables(
|
|
76
|
+
fileStore,
|
|
77
|
+
appConfig
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
vi.spyOn(fileStore, 'readLocalOdsFileAndConvertToJson').mockResolvedValue({
|
|
81
|
+
dpe_class_limit: [
|
|
82
|
+
{ classe_altitude: '400-800m', surface: '10', A: '124' },
|
|
83
|
+
{ classe_altitude: '400-800m', surface: '20', A: '100' }
|
|
84
|
+
]
|
|
85
|
+
});
|
|
86
|
+
vi.spyOn(ApplicationConfig.prototype, 'solicitationsExtFilePath', 'get').mockReturnValue(
|
|
87
|
+
'src/file.ods'
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
return synchronizeDpeGesLimitValuesTables.execute().then((output) => {
|
|
91
|
+
// Le groupe d'altitude n'est créé qu'une fois : la 1ʳᵉ surface n'est pas écrasée
|
|
92
|
+
expect(output).toStrictEqual({
|
|
93
|
+
dpe_class_limit: { '400-800m': { 10: { A: 124 }, 20: { A: 100 } } }
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
});
|
|
72
97
|
});
|
|
@@ -99,4 +99,47 @@ describe('SynchronizeValeurTables unit tests', () => {
|
|
|
99
99
|
expect(contenu).toContain('valeurs-ue-completees');
|
|
100
100
|
});
|
|
101
101
|
});
|
|
102
|
+
test('force isolation_surfaces à "0" uniquement lorsque la valeur est absente dans q4pa_conv', () => {
|
|
103
|
+
const fileStore = new FileStore();
|
|
104
|
+
const appConfig = new ApplicationConfig();
|
|
105
|
+
const synchronizeC1Tables = new SynchronizeC1Tables(fileStore, appConfig);
|
|
106
|
+
const synchronizeSolicitationTables = new SynchronizeSolicitationsTables(fileStore, appConfig);
|
|
107
|
+
const synchronizeDpeGesLimitValuesTables = new SynchronizeDpeGesLimitValuesTables(
|
|
108
|
+
fileStore,
|
|
109
|
+
appConfig
|
|
110
|
+
);
|
|
111
|
+
const addAdditionnalUeValuesTables = new AddAdditionnalUeValuesTables(fileStore, appConfig);
|
|
112
|
+
const synchronizeValeurTables = new SynchronizeValeurTables(
|
|
113
|
+
fileStore,
|
|
114
|
+
appConfig,
|
|
115
|
+
synchronizeSolicitationTables,
|
|
116
|
+
synchronizeDpeGesLimitValuesTables,
|
|
117
|
+
addAdditionnalUeValuesTables,
|
|
118
|
+
synchronizeC1Tables
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
vi.spyOn(ApplicationConfig.prototype, 'ademeValeurTablesFileUrl', 'get').mockReturnValue(
|
|
122
|
+
'http://localhost/file.xlsx'
|
|
123
|
+
);
|
|
124
|
+
vi.spyOn(ApplicationConfig.prototype, 'assetsOutputFolder', 'get').mockReturnValue(
|
|
125
|
+
'src/assets'
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Le parsing Excel ignore les valeurs à 0 : la 1ʳᵉ ligne n'a pas de isolation_surfaces,
|
|
129
|
+
// la 2ᵉ en possède déjà une qui doit être conservée.
|
|
130
|
+
vi.spyOn(fileStore, 'downloadXlsxFileAndConvertToJson').mockResolvedValue({
|
|
131
|
+
q4pa_conv: [{ q4pa_conv: 'a' }, { q4pa_conv: 'b', isolation_surfaces: 'oui' }]
|
|
132
|
+
});
|
|
133
|
+
vi.spyOn(fileStore, 'writeFileToLocalSystem').mockResolvedValue(null);
|
|
134
|
+
vi.spyOn(synchronizeC1Tables, 'execute').mockResolvedValue({});
|
|
135
|
+
vi.spyOn(synchronizeSolicitationTables, 'execute').mockResolvedValue({});
|
|
136
|
+
vi.spyOn(synchronizeDpeGesLimitValuesTables, 'execute').mockResolvedValue({});
|
|
137
|
+
vi.spyOn(addAdditionnalUeValuesTables, 'execute').mockResolvedValue({});
|
|
138
|
+
|
|
139
|
+
return synchronizeValeurTables.execute().then(() => {
|
|
140
|
+
const contenu = vi.mocked(fileStore.writeFileToLocalSystem).mock.calls[0][1];
|
|
141
|
+
const tvs = JSON.parse(contenu.match(/tvs = (\{[\s\S]*\});\n export/)[1]);
|
|
142
|
+
expect(tvs.q4pa_conv.map((l) => l.isolation_surfaces)).toEqual(['0', 'oui']);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
102
145
|
});
|
|
@@ -58,6 +58,17 @@ describe('Service de logs (log-service)', () => {
|
|
|
58
58
|
expect(console.error).toBe(consoleOrigine.error);
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
+
test('setLoggerOn sans option conserve console.error neutralisé', () => {
|
|
62
|
+
setLoggerOff(true);
|
|
63
|
+
const erreurNeutralisee = console.error;
|
|
64
|
+
setLoggerOn();
|
|
65
|
+
|
|
66
|
+
expect(logger.silent).toBe(false);
|
|
67
|
+
expect(console.log).toBe(consoleOrigine.log);
|
|
68
|
+
expect(console.error).toBe(erreurNeutralisee);
|
|
69
|
+
expect(console.error).not.toBe(consoleOrigine.error);
|
|
70
|
+
});
|
|
71
|
+
|
|
61
72
|
test('les méthodes console neutralisées sont des fonctions no-op', () => {
|
|
62
73
|
setLoggerOff();
|
|
63
74
|
|
|
@@ -77,6 +77,15 @@ describe('DpeSanitizerService.execute - collections manquantes', () => {
|
|
|
77
77
|
});
|
|
78
78
|
});
|
|
79
79
|
|
|
80
|
+
describe('DpeSanitizerService - transformation des clés', () => {
|
|
81
|
+
test('conserve les clés inchangées (transformation identité)', () => {
|
|
82
|
+
vi.mocked(has).mockReturnValue(true);
|
|
83
|
+
service.execute({});
|
|
84
|
+
const keyTransform = vi.mocked(ObjectUtil.deepObjectTransform).mock.calls[0][1];
|
|
85
|
+
expect(keyTransform('generateur_ecs')).toBe('generateur_ecs');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
80
89
|
/**
|
|
81
90
|
* Récupère la fonction de transformation des valeurs passée en 3e argument de
|
|
82
91
|
* `deepObjectTransform` après un appel à `execute`.
|
package/engine.js
CHANGED
|
@@ -34,9 +34,12 @@ import getFicheTechnique from './ficheTechnique.js';
|
|
|
34
34
|
import { ProductionENR, ratioProrataPvLogement } from './16.2_production_enr.js';
|
|
35
35
|
import DpeSanitizerService from './dpe-sanitizer.service.js';
|
|
36
36
|
import { reconstituerRdimDpeGenereImmeuble } from './17.2_rdim_dpe_genere_immeuble.js';
|
|
37
|
-
import {
|
|
37
|
+
import {
|
|
38
|
+
reconstituerRdimEcsIndividuelleUnique,
|
|
39
|
+
reconstituerRdimEcsIndividuellesMultiples
|
|
40
|
+
} from './11_rdim_ecs_individuelle_unique.js';
|
|
38
41
|
|
|
39
|
-
const LIB_VERSION = '1.11.
|
|
42
|
+
const LIB_VERSION = '1.11.3';
|
|
40
43
|
|
|
41
44
|
const dpeSanitizerService = new DpeSanitizerService();
|
|
42
45
|
|
|
@@ -305,6 +308,11 @@ export function calcul_3cl(inputDpe, options) {
|
|
|
305
308
|
* @see https://github.com/Open3CL/engine/issues/288
|
|
306
309
|
*/
|
|
307
310
|
reconstituerRdimEcsIndividuelleUnique(th, ecs, cg.surface_habitable_immeuble);
|
|
311
|
+
/**
|
|
312
|
+
* Immeuble avec plusieurs installations ECS individuelles décrites chacune pour un logement type
|
|
313
|
+
* (Σ Sh_i × nombre_logement_i ≈ SH, sans rdim) : rdim_i = nombre_logement_i.
|
|
314
|
+
*/
|
|
315
|
+
reconstituerRdimEcsIndividuellesMultiples(th, ecs, cg.surface_habitable_immeuble);
|
|
308
316
|
const Nb_lgt = cg.nombre_appartement || 1;
|
|
309
317
|
|
|
310
318
|
/**
|
package/engine.spec.js
CHANGED
|
@@ -75,7 +75,8 @@ vi.mock('./17.2_rdim_dpe_genere_immeuble.js', () => ({
|
|
|
75
75
|
reconstituerRdimDpeGenereImmeuble: vi.fn()
|
|
76
76
|
}));
|
|
77
77
|
vi.mock('./11_rdim_ecs_individuelle_unique.js', () => ({
|
|
78
|
-
reconstituerRdimEcsIndividuelleUnique: vi.fn()
|
|
78
|
+
reconstituerRdimEcsIndividuelleUnique: vi.fn(),
|
|
79
|
+
reconstituerRdimEcsIndividuellesMultiples: vi.fn()
|
|
79
80
|
}));
|
|
80
81
|
|
|
81
82
|
// Les instances Inertie / ProductionENR sont créées au chargement du module.
|
|
@@ -83,9 +84,15 @@ vi.mock('./11_rdim_ecs_individuelle_unique.js', () => ({
|
|
|
83
84
|
const calculateInertie = vi.fn();
|
|
84
85
|
const calculateEnr = vi.fn();
|
|
85
86
|
const ratioProrataPvLogement = vi.fn();
|
|
86
|
-
vi.mock('./7_inertie.js', () => ({
|
|
87
|
+
vi.mock('./7_inertie.js', () => ({
|
|
88
|
+
Inertie: vi.fn(function () {
|
|
89
|
+
return { calculateInertie };
|
|
90
|
+
})
|
|
91
|
+
}));
|
|
87
92
|
vi.mock('./16.2_production_enr.js', () => ({
|
|
88
|
-
ProductionENR: vi.fn(()
|
|
93
|
+
ProductionENR: vi.fn(function () {
|
|
94
|
+
return { calculateEnr };
|
|
95
|
+
}),
|
|
89
96
|
ratioProrataPvLogement: (...args) => ratioProrataPvLogement(...args)
|
|
90
97
|
}));
|
|
91
98
|
|
|
@@ -93,7 +100,9 @@ vi.mock('./16.2_production_enr.js', () => ({
|
|
|
93
100
|
// renvoie l'entrée telle quelle.
|
|
94
101
|
const sanitizeExecute = vi.fn((dpe) => dpe);
|
|
95
102
|
vi.mock('./dpe-sanitizer.service.js', () => ({
|
|
96
|
-
default: vi.fn(()
|
|
103
|
+
default: vi.fn(function () {
|
|
104
|
+
return { execute: sanitizeExecute };
|
|
105
|
+
})
|
|
97
106
|
}));
|
|
98
107
|
|
|
99
108
|
const { getVersion, get_conso_coeff_1_7_2027, get_classe_ges_dpe, calcul_3cl, calcul_3cl_xml } =
|
|
@@ -121,7 +130,7 @@ const calc_confort_ete = (await import('./2021_04_13_confort_ete.js')).default;
|
|
|
121
130
|
const calc_qualite_isolation = (await import('./2021_04_13_qualite_isolation.js')).default;
|
|
122
131
|
const getFicheTechnique = (await import('./ficheTechnique.js')).default;
|
|
123
132
|
const { reconstituerRdimDpeGenereImmeuble } = await import('./17.2_rdim_dpe_genere_immeuble.js');
|
|
124
|
-
const { reconstituerRdimEcsIndividuelleUnique } =
|
|
133
|
+
const { reconstituerRdimEcsIndividuelleUnique, reconstituerRdimEcsIndividuellesMultiples } =
|
|
125
134
|
await import('./11_rdim_ecs_individuelle_unique.js');
|
|
126
135
|
|
|
127
136
|
/** Objet conso renvoyé par le mock de `calc_conso` (mêmes réf pour les 3 appels). */
|
|
@@ -650,6 +659,20 @@ describe('calcul_3cl - collections vidables (poursuite du calcul)', () => {
|
|
|
650
659
|
});
|
|
651
660
|
|
|
652
661
|
describe('calcul_3cl - surfaces immeuble / appartement et prorata', () => {
|
|
662
|
+
test('méthode application inconnue : surface habitable indéterminée transmise à la déperdition', () => {
|
|
663
|
+
enums.methode_application_dpe_log = { 1: 'méthode farfelue' };
|
|
664
|
+
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
665
|
+
const dpe = makeDpe();
|
|
666
|
+
|
|
667
|
+
calcul_3cl(dpe, { sanitize: false });
|
|
668
|
+
|
|
669
|
+
// th null => aucune surface retenue (ni logement ni immeuble)
|
|
670
|
+
expect(calc_deperdition.mock.calls[0][2]).toBeNull();
|
|
671
|
+
expect(calc_deperdition.mock.calls[0][5]).toBeUndefined();
|
|
672
|
+
expect(errSpy).toHaveBeenCalled();
|
|
673
|
+
errSpy.mockRestore();
|
|
674
|
+
});
|
|
675
|
+
|
|
653
676
|
test('immeuble : Sh = surface immeuble ; ECS collective individualisée non divisée', () => {
|
|
654
677
|
enums.methode_application_dpe_log = { 1: 'dpe immeuble collectif' };
|
|
655
678
|
const dpe = makeDpe();
|
|
@@ -1304,6 +1327,26 @@ describe('calcul_3cl - rdim de l’installation ECS individuelle unique d’un i
|
|
|
1304
1327
|
});
|
|
1305
1328
|
});
|
|
1306
1329
|
|
|
1330
|
+
describe('calcul_3cl - rdim des installations ECS individuelles multiples d’un immeuble', () => {
|
|
1331
|
+
test('reconstitution appelée avec le type de bâtiment, les installations ECS et SH, avant le calcul ECS', () => {
|
|
1332
|
+
const dpe = makeDpe();
|
|
1333
|
+
dpe.logement.caracteristique_generale.surface_habitable_immeuble = 566.16;
|
|
1334
|
+
vi.mocked(reconstituerRdimEcsIndividuellesMultiples).mockImplementationOnce(() => {
|
|
1335
|
+
expect(calc_ecs).not.toHaveBeenCalled();
|
|
1336
|
+
return false;
|
|
1337
|
+
});
|
|
1338
|
+
|
|
1339
|
+
calcul_3cl(dpe, { sanitize: false });
|
|
1340
|
+
|
|
1341
|
+
expect(reconstituerRdimEcsIndividuellesMultiples).toHaveBeenCalledWith(
|
|
1342
|
+
'maison',
|
|
1343
|
+
dpe.logement.installation_ecs_collection.installation_ecs,
|
|
1344
|
+
566.16
|
|
1345
|
+
);
|
|
1346
|
+
expect(calc_ecs).toHaveBeenCalled();
|
|
1347
|
+
});
|
|
1348
|
+
});
|
|
1349
|
+
|
|
1307
1350
|
describe('get_conso_coeff_1_7_2027', () => {
|
|
1308
1351
|
test('projette la conso EP 2027 pour une maison', () => {
|
|
1309
1352
|
vi.mocked(classe_bilan_dpe).mockReturnValue('C');
|
|
@@ -1340,4 +1383,22 @@ describe('get_conso_coeff_1_7_2027', () => {
|
|
|
1340
1383
|
expect(res.ep_conso_5_usages_2027).toBeCloseTo(ep, 6);
|
|
1341
1384
|
expect(classe_bilan_dpe).toHaveBeenCalledWith(Math.floor(ep / 200), 'zc1', 'ca1', 200);
|
|
1342
1385
|
});
|
|
1386
|
+
|
|
1387
|
+
test('méthode application inconnue : surface indéterminée, aucune surface ne lui est substituée', () => {
|
|
1388
|
+
enums.methode_application_dpe_log = { 1: 'méthode farfelue' };
|
|
1389
|
+
const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
1390
|
+
vi.mocked(classe_bilan_dpe).mockReturnValue('G');
|
|
1391
|
+
const dpe = makeDpe();
|
|
1392
|
+
dpe.logement.sortie = {
|
|
1393
|
+
ep_conso: { ep_conso_5_usages: 10000 },
|
|
1394
|
+
ef_conso: { conso_5_usages: 6000 }
|
|
1395
|
+
};
|
|
1396
|
+
|
|
1397
|
+
get_conso_coeff_1_7_2027(dpe);
|
|
1398
|
+
|
|
1399
|
+
// th null => ni surface logement ni surface immeuble
|
|
1400
|
+
expect(classe_bilan_dpe.mock.calls[0][3]).toBeUndefined();
|
|
1401
|
+
expect(errSpy).toHaveBeenCalled();
|
|
1402
|
+
errSpy.mockRestore();
|
|
1403
|
+
});
|
|
1343
1404
|
});
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
// Alias des types générés depuis le XSD DPE (voir dpe-xsd.generated.ts). Les extensions propres
|
|
2
|
+
// à Open3CL se déclarent ici (hors fichier généré) pour survivre à la régénération.
|
|
3
|
+
import type {
|
|
4
|
+
XsdLogementEnveloppeEtsBaieEts,
|
|
5
|
+
XsdLogementEnveloppeEtsBaieEtsDonneeEntree
|
|
6
|
+
} from './dpe-xsd.generated';
|
|
2
7
|
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export interface BaieEtsDE extends DE {
|
|
8
|
-
enum_orientation_id?: number;
|
|
9
|
-
enum_inclinaison_vitrage_id?: number; // ENUM inclinaison_vitrage
|
|
10
|
-
surface_totale_baie?: number;
|
|
11
|
-
nb_baie?: number;
|
|
12
|
-
}
|
|
8
|
+
export type BaieEts = XsdLogementEnveloppeEtsBaieEts;
|
|
9
|
+
export type BaieEtsDE = XsdLogementEnveloppeEtsBaieEtsDonneeEntree;
|
|
@@ -1,97 +1,25 @@
|
|
|
1
|
-
|
|
1
|
+
// Alias des types générés depuis le XSD DPE (voir dpe-xsd.generated.ts). Les extensions propres
|
|
2
|
+
// à Open3CL se déclarent ici (hors fichier généré) pour survivre à la régénération.
|
|
3
|
+
import type {
|
|
4
|
+
XsdLogementEnveloppeBaieVitree,
|
|
5
|
+
XsdLogementEnveloppeBaieVitreeBaieVitreeDoubleFenetre,
|
|
6
|
+
XsdLogementEnveloppeBaieVitreeBaieVitreeDoubleFenetreDonneeEntree,
|
|
7
|
+
XsdLogementEnveloppeBaieVitreeBaieVitreeDoubleFenetreDonneeIntermediaire,
|
|
8
|
+
XsdLogementEnveloppeBaieVitreeDonneeEntree,
|
|
9
|
+
XsdLogementEnveloppeBaieVitreeDonneeEntreeMasqueLointainNonHomogene,
|
|
10
|
+
XsdLogementEnveloppeBaieVitreeDonneeIntermediaire
|
|
11
|
+
} from './dpe-xsd.generated';
|
|
2
12
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
enum_type_adjacence_id?: string; // ENUM type_adjacence
|
|
17
|
-
surface_totale_baie?: number;
|
|
18
|
-
nb_baie?: number;
|
|
19
|
-
tv_ug_id?: string; // TV
|
|
20
|
-
enum_type_vitrage_id?: string; // ENUM type_vitrage
|
|
21
|
-
enum_inclinaison_vitrage_id?: string; // ENUM inclinaison_vitrage
|
|
22
|
-
enum_type_gaz_lame_id?: string; // ENUM type_gaz_lame
|
|
23
|
-
epaisseur_lame?: number;
|
|
24
|
-
vitrage_vir?: boolean;
|
|
25
|
-
enum_methode_saisie_perf_vitrage_id?: string; // ENUM methode_saisie_perf_vitrage
|
|
26
|
-
ug_saisi?: number;
|
|
27
|
-
tv_uw_id?: string;
|
|
28
|
-
enum_type_materiaux_menuiserie_id?: string; // ENUM type_materiaux_menuiserie
|
|
29
|
-
enum_type_baie_id?: string; // ENUM type_baie
|
|
30
|
-
uw_saisi?: number;
|
|
31
|
-
double_fenetre?: number;
|
|
32
|
-
uw_1?: number;
|
|
33
|
-
sw_1?: number;
|
|
34
|
-
uw_2?: number;
|
|
35
|
-
sw_2?: number;
|
|
36
|
-
tv_deltar_id?: string;
|
|
37
|
-
tv_ujn_id?: string;
|
|
38
|
-
enum_type_fermeture_id?: string;
|
|
39
|
-
presence_protection_solaire_hors_fermeture?: boolean;
|
|
40
|
-
ujn_saisi?: number;
|
|
41
|
-
presence_retour_isolation?: boolean;
|
|
42
|
-
presence_joint?: boolean;
|
|
43
|
-
largeur_dormant?: number;
|
|
44
|
-
tv_sw_id?: string;
|
|
45
|
-
sw_saisi?: number;
|
|
46
|
-
enum_type_pose_id?: string; // ENUM type_pose
|
|
47
|
-
enum_orientation_id?: string;
|
|
48
|
-
tv_coef_masque_proche_id?: number;
|
|
49
|
-
tv_coef_masque_lointain_homogene_id?: number;
|
|
50
|
-
masque_lointain_non_homogene_collection: {
|
|
51
|
-
masque_lointain_non_homogene: MasqueLointainNonHomogene;
|
|
52
|
-
}[];
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface BaieVitreeDI {
|
|
56
|
-
b: number;
|
|
57
|
-
ug?: number;
|
|
58
|
-
uw: number;
|
|
59
|
-
ujn?: number;
|
|
60
|
-
u_menuiserie: number;
|
|
61
|
-
sw: number;
|
|
62
|
-
fe1: number;
|
|
63
|
-
fe2: number;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface BaieVitreeDoubleFenetre {
|
|
67
|
-
donnee_entree?: BaieVitreeDoubleFenetreDE;
|
|
68
|
-
donnee_intermediaire?: BaieVitreeDoubleFenetreDI;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export interface BaieVitreeDoubleFenetreDE {
|
|
72
|
-
tv_ug_id?: string;
|
|
73
|
-
enum_type_vitrage_id: string;
|
|
74
|
-
enum_inclinaison_vitrage_id: string;
|
|
75
|
-
enum_type_gaz_lame_id?: string;
|
|
76
|
-
epaisseur_lame?: number;
|
|
77
|
-
vitrage_vir?: boolean;
|
|
78
|
-
enum_methode_saisie_perf_vitrage_id: string;
|
|
79
|
-
ug_saisi?: number;
|
|
80
|
-
tv_uw_id?: string;
|
|
81
|
-
enum_type_materiaux_menuiserie_id: string;
|
|
82
|
-
enum_type_baie_id: string;
|
|
83
|
-
uw_saisi?: number;
|
|
84
|
-
tv_sw_id?: string;
|
|
85
|
-
sw_saisi?: number;
|
|
86
|
-
enum_type_pose_id: string;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export interface BaieVitreeDoubleFenetreDI {
|
|
90
|
-
ug?: number;
|
|
91
|
-
uw: number;
|
|
92
|
-
sw: number;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export interface MasqueLointainNonHomogene {
|
|
96
|
-
tv_coef_masque_lointain_non_homogene_id?: string;
|
|
97
|
-
}
|
|
13
|
+
/** + `donnee_utilisateur` Open3CL hors XSD. */
|
|
14
|
+
export type BaieVitree = XsdLogementEnveloppeBaieVitree & {
|
|
15
|
+
donnee_utilisateur?: Record<string, any>;
|
|
16
|
+
};
|
|
17
|
+
export type BaieVitreeDE = XsdLogementEnveloppeBaieVitreeDonneeEntree;
|
|
18
|
+
export type BaieVitreeDI = XsdLogementEnveloppeBaieVitreeDonneeIntermediaire;
|
|
19
|
+
export type BaieVitreeDoubleFenetre = XsdLogementEnveloppeBaieVitreeBaieVitreeDoubleFenetre;
|
|
20
|
+
export type BaieVitreeDoubleFenetreDE =
|
|
21
|
+
XsdLogementEnveloppeBaieVitreeBaieVitreeDoubleFenetreDonneeEntree;
|
|
22
|
+
export type BaieVitreeDoubleFenetreDI =
|
|
23
|
+
XsdLogementEnveloppeBaieVitreeBaieVitreeDoubleFenetreDonneeIntermediaire;
|
|
24
|
+
export type MasqueLointainNonHomogene =
|
|
25
|
+
XsdLogementEnveloppeBaieVitreeDonneeEntreeMasqueLointainNonHomogene;
|