@open3cl/engine 1.6.2 → 1.7.1

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.
Files changed (51) hide show
  1. package/10_besoin_fr.spec.js +1 -0
  2. package/10_clim.spec.js +1 -0
  3. package/11_besoin_ecs.spec.js +1 -0
  4. package/11_ecs.spec.js +1 -0
  5. package/12.4_pac.spec.js +1 -0
  6. package/13.2_generateur_combustion.spec.js +3 -3
  7. package/13.2_generateur_combustion_ch.spec.js +3 -3
  8. package/14_generateur_ecs.spec.js +1 -0
  9. package/15_conso_aux.js +121 -1
  10. package/15_conso_aux.spec.js +329 -4
  11. package/2021_04_13_confort_ete.spec.js +1 -0
  12. package/3.1_b.spec.js +1 -0
  13. package/3.2.1_mur.spec.js +1 -0
  14. package/3.2.2_plancher_bas.spec.js +1 -0
  15. package/3.2.3_plancher_haut.spec.js +1 -0
  16. package/3.3.1.4_porte.spec.js +1 -0
  17. package/3.3_baie_vitree.spec.js +1 -0
  18. package/3.4_pont_thermique.spec.js +1 -0
  19. package/4_ventilation.spec.js +1 -0
  20. package/5_conso_ventilation.spec.js +1 -0
  21. package/6.2_surface_sud_equivalente.js +2 -1
  22. package/6.2_surface_sud_equivalente.spec.js +1 -0
  23. package/9_besoin_ch.spec.js +1 -0
  24. package/9_chauffage.spec.js +3 -3
  25. package/9_conso_ch.spec.js +1 -0
  26. package/9_emetteur_ch.spec.js +1 -0
  27. package/9_generateur_ch.spec.js +1 -0
  28. package/README.md +412 -291
  29. package/conso.js +20 -6
  30. package/conso.spec.js +21 -0
  31. package/engine.js +19 -1
  32. package/engine.spec.js +96 -0
  33. package/package.json +15 -14
  34. package/features/engine/domain/apport_et_besoin/apport-et-besoin.service.spec.js +0 -222
  35. package/features/engine/domain/apport_et_besoin/ch/perte-ch-recup.service.spec.js +0 -345
  36. package/features/engine/domain/apport_et_besoin/ecs/besoin-ecs.service.spec.js +0 -87
  37. package/features/engine/domain/apport_et_besoin/ecs/perte-ecs-recup.service.spec.js +0 -260
  38. package/features/engine/domain/apport_et_besoin/froid/besoin-froid.service.spec.js +0 -272
  39. package/features/engine/domain/apport_et_besoin/surface-sud-equivalente.service.spec.js +0 -231
  40. package/features/engine/domain/ch/generateur-ch.service.spec.js +0 -849
  41. package/features/engine/domain/conso/froid/conso-froid.service.spec.js +0 -233
  42. package/features/engine/domain/ecs/installation-ecs.service.spec.js +0 -337
  43. package/features/engine/domain/enveloppe/baie_vitree/deperdition-baie-vitree.service.spec.js +0 -607
  44. package/features/engine/domain/enveloppe/deperdition-enveloppe.service.spec.js +0 -550
  45. package/features/engine/domain/enveloppe/espace_tampon/espace-tampon.service.spec.js +0 -82
  46. package/features/engine/domain/enveloppe/mur/deperdition-mur.service.spec.js +0 -542
  47. package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.spec.js +0 -430
  48. package/features/engine/domain/enveloppe/plancher_haut/deperdition-plancher-haut.service.spec.js +0 -411
  49. package/features/engine/domain/enveloppe/pont_thermique/deperdition-pont-thermique.service.spec.js +0 -653
  50. package/features/engine/domain/enveloppe/porte/deperdition-porte.service.spec.js +0 -117
  51. package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.spec.js +0 -508
@@ -1,233 +0,0 @@
1
- import { beforeEach, describe, expect, test, vi } from 'vitest';
2
- import { DpeNormalizerService } from '../../../../normalizer/domain/dpe-normalizer.service.js';
3
- import { ContexteBuilder } from '../../contexte.builder.js';
4
- import corpus from '../../../../../../test/corpus-sano.json';
5
- import { getAdemeFileJson } from '../../../../../../test/test-helpers.js';
6
- import { FrTvStore } from '../../../../dpe/infrastructure/froid/frTv.store.js';
7
- import { ConsoFroidService } from './conso-froid.service.js';
8
- import { describeIntegration } from '../../../../../../test/helpers/integration-test.js';
9
-
10
- /** @type {ConsoFroidService} **/
11
- let service;
12
-
13
- /** @type {DpeNormalizerService} **/
14
- let normalizerService;
15
-
16
- /** @type {ContexteBuilder} **/
17
- let contexteBuilder;
18
-
19
- /** @type {FrTvStore} **/
20
- let tvStore;
21
-
22
- describe('Calcul des consos en froid du logement', () => {
23
- beforeEach(() => {
24
- tvStore = new FrTvStore();
25
- service = new ConsoFroidService(tvStore);
26
- normalizerService = new DpeNormalizerService();
27
- contexteBuilder = new ContexteBuilder();
28
- });
29
-
30
- test.each([
31
- {
32
- label: 'Climatisation avec méthode de saisie 6, sans surface clim',
33
- enumMethodeSaisieCaracSysId: 6,
34
- eerInitial: 125,
35
- expected: { conso_fr: 7.2, conso_fr_depensier: 10.8 }
36
- },
37
- {
38
- label: 'Climatisation avec méthode de saisie 7, sans surface clim',
39
- enumMethodeSaisieCaracSysId: 7,
40
- eerInitial: 125,
41
- expected: { conso_fr: 7.2, conso_fr_depensier: 10.8 }
42
- },
43
- {
44
- label: 'Climatisation avec méthode de saisie 8, sans surface clim',
45
- enumMethodeSaisieCaracSysId: 8,
46
- eerInitial: 125,
47
- expected: { conso_fr: 7.2, conso_fr_depensier: 10.8 }
48
- },
49
- {
50
- label: 'Climatisation avec méthode de saisie 6, avec surface clim',
51
- enumMethodeSaisieCaracSysId: 6,
52
- eerInitial: 125,
53
- surfaceClim: 80,
54
- expected: { conso_fr: 5.76, conso_fr_depensier: 8.64 }
55
- },
56
- {
57
- label: 'Climatisation avec méthode de saisie 7, avec surface clim',
58
- enumMethodeSaisieCaracSysId: 7,
59
- eerInitial: 125,
60
- surfaceClim: 80,
61
- expected: { conso_fr: 5.76, conso_fr_depensier: 8.64 }
62
- },
63
- {
64
- label: 'Climatisation avec méthode de saisie 8, avec surface clim',
65
- enumMethodeSaisieCaracSysId: 8,
66
- eerInitial: 125,
67
- surfaceClim: 80,
68
- expected: { conso_fr: 5.76, conso_fr_depensier: 8.64 }
69
- },
70
- {
71
- label: 'Climatisation avec méthode de saisie 5, sans surface clim',
72
- enumMethodeSaisieCaracSysId: 5,
73
- eer: 90,
74
- expected: { conso_fr: 10, conso_fr_depensier: 15 }
75
- },
76
- {
77
- label: 'Climatisation avec méthode de saisie 5, avec surface clim',
78
- enumMethodeSaisieCaracSysId: 5,
79
- eer: 90,
80
- surfaceClim: 80,
81
- expected: { conso_fr: 8, conso_fr_depensier: 12 }
82
- }
83
- ])(
84
- 'Détermination des consommations des systèmes de refroidissement pour $label',
85
- ({
86
- enumMethodeSaisieCaracSysId,
87
- surfaceClim = undefined,
88
- eer = undefined,
89
- eerInitial,
90
- expected
91
- }) => {
92
- vi.spyOn(tvStore, 'getEer').mockReturnValue(eer);
93
-
94
- /** @type {Contexte} */
95
- const contexte = {
96
- zoneClimatique: { id: 1 },
97
- surfaceHabitable: 100
98
- };
99
-
100
- /** @type {Climatisation} */
101
- const climatisation = {
102
- donnee_entree: {
103
- enum_methode_saisie_carac_sys_id: enumMethodeSaisieCaracSysId,
104
- surface_clim: surfaceClim
105
- },
106
- donnee_intermediaire: { eer: eerInitial }
107
- };
108
-
109
- /** @type {ApportEtBesoin} */
110
- const apportEtBesoin = {
111
- besoin_fr: 1000,
112
- besoin_fr_depensier: 1500
113
- };
114
-
115
- const consoFroid = service.consoFroid(contexte, apportEtBesoin, climatisation);
116
- expect(consoFroid.conso_fr).toBeCloseTo(expected.conso_fr, 2);
117
- expect(consoFroid.conso_fr_depensier).toBeCloseTo(expected.conso_fr_depensier, 2);
118
- }
119
- );
120
-
121
- test('consoFroid : données intermédiaires absentes -> EER lu dans la table (repli {})', () => {
122
- // climatisation.donnee_intermediaire absente : repli sur {} puis lecture de l'EER forfaitaire.
123
- vi.spyOn(tvStore, 'getEer').mockReturnValue(90);
124
-
125
- /** @type {Contexte} */
126
- const contexte = { zoneClimatique: { id: 1 }, surfaceHabitable: 100 };
127
-
128
- /** @type {Climatisation} */
129
- const climatisation = {
130
- donnee_entree: { enum_methode_saisie_carac_sys_id: 6, enum_periode_installation_fr_id: 2 }
131
- };
132
-
133
- /** @type {ApportEtBesoin} */
134
- const apportEtBesoin = { besoin_fr: 1000, besoin_fr_depensier: 1500 };
135
-
136
- const consoFroid = service.consoFroid(contexte, apportEtBesoin, climatisation);
137
-
138
- expect(tvStore.getEer).toHaveBeenCalledWith(1, 2);
139
- // conso = 0.9 * besoin / eer
140
- expect(consoFroid.conso_fr).toBeCloseTo((0.9 * 1000) / 90, 5);
141
- expect(consoFroid.conso_fr_depensier).toBeCloseTo((0.9 * 1500) / 90, 5);
142
- });
143
-
144
- test('execute : renseigne les données intermédiaires de chaque climatisation', () => {
145
- // On isole execute du calcul détaillé : consoFroid est doublée pour ne vérifier que
146
- // l'itération sur les climatisations et l'affectation des champs (dont l'initialisation ??=).
147
- vi.spyOn(service, 'consoFroid').mockReturnValue({
148
- besoin_fr: 10,
149
- besoin_fr_depensier: 15,
150
- conso_fr: 2,
151
- conso_fr_depensier: 3
152
- });
153
-
154
- /** @type {Contexte} */
155
- const ctx = { zoneClimatique: { id: 1 }, surfaceHabitable: 100 };
156
-
157
- // Première climatisation sans donnee_intermediaire (branche ??= {}),
158
- // seconde avec un objet préexistant (branche ??= conservée).
159
- const climSansDI = { donnee_entree: {} };
160
- const climAvecDI = { donnee_entree: {}, donnee_intermediaire: { eer: 42 } };
161
-
162
- /** @type {Logement} */
163
- const logement = {
164
- sortie: { apport_et_besoin: { besoin_fr: 1000, besoin_fr_depensier: 1500 } },
165
- climatisation_collection: { climatisation: [climSansDI, climAvecDI] }
166
- };
167
-
168
- service.execute(ctx, logement);
169
-
170
- expect(service.consoFroid).toHaveBeenCalledTimes(2);
171
- expect(service.consoFroid).toHaveBeenCalledWith(
172
- ctx,
173
- logement.sortie.apport_et_besoin,
174
- climSansDI
175
- );
176
-
177
- for (const clim of [climSansDI, climAvecDI]) {
178
- expect(clim.donnee_intermediaire).toMatchObject({
179
- besoin_fr: 10,
180
- besoin_fr_depensier: 15,
181
- conso_fr: 2,
182
- conso_fr_depensier: 3
183
- });
184
- }
185
- // L'objet préexistant a été conservé (eer non écrasé par ??=)
186
- expect(climAvecDI.donnee_intermediaire.eer).toBe(42);
187
- });
188
-
189
- test("execute : aucune conso calculée en l'absence de climatisation", () => {
190
- // Valeur de repli [] quand climatisation_collection est absente.
191
- vi.spyOn(service, 'consoFroid');
192
-
193
- /** @type {Contexte} */
194
- const ctx = { zoneClimatique: { id: 1 }, surfaceHabitable: 100 };
195
-
196
- service.execute(ctx, { sortie: { apport_et_besoin: {} } });
197
-
198
- expect(service.consoFroid).not.toHaveBeenCalled();
199
- });
200
-
201
- describeIntegration("Test d'intégration pour le besoin en froid", () => {
202
- test.each(corpus)('vérification des sorties besoin_fr et conso_fr pour dpe %s', (ademeId) => {
203
- /**
204
- * @type {Dpe}
205
- */
206
- let dpeRequest = getAdemeFileJson(ademeId);
207
- dpeRequest = normalizerService.normalize(dpeRequest);
208
-
209
- /** @type {Contexte} */
210
- const ctx = contexteBuilder.fromDpe(dpeRequest);
211
-
212
- const climatisations = structuredClone(
213
- dpeRequest.logement.climatisation_collection?.climatisation || []
214
- );
215
- service.execute(ctx, dpeRequest.logement);
216
-
217
- climatisations.forEach((climatisation, i) => {
218
- console.log('climatisation.donnee_intermediaire.besoin_fr');
219
- expect(climatisation.donnee_intermediaire.besoin_fr).toBeCloseTo(
220
- dpeRequest.logement.climatisation_collection.climatisation[i].donnee_intermediaire
221
- .besoin_fr,
222
- 2
223
- );
224
- console.log(climatisation.donnee_intermediaire.conso_fr);
225
- expect(climatisation.donnee_intermediaire.conso_fr).toBeCloseTo(
226
- dpeRequest.logement.climatisation_collection.climatisation[i].donnee_intermediaire
227
- .conso_fr,
228
- 2
229
- );
230
- });
231
- });
232
- });
233
- });
@@ -1,337 +0,0 @@
1
- import { beforeEach, describe, expect, test, vi } from 'vitest';
2
- import { GenerateurEcsService } from './generateur-ecs.service.js';
3
- import { InstallationEcsService } from './installation-ecs.service.js';
4
- import corpus from '../../../../../test/corpus-sano.json';
5
- import { expect_or, getAdemeFileJson } from '../../../../../test/test-helpers.js';
6
- import { DpeNormalizerService } from '../../../normalizer/domain/dpe-normalizer.service.js';
7
- import { ContexteBuilder } from '../contexte.builder.js';
8
- import { describeIntegration } from '../../../../../test/helpers/integration-test.js';
9
-
10
- /** @type {InstallationEcsService} **/
11
- let service;
12
-
13
- /** @type {GenerateurEcsService} **/
14
- let generateurEcsService;
15
-
16
- /** @type {DpeNormalizerService} **/
17
- let normalizerService;
18
-
19
- /** @type {ContexteBuilder} **/
20
- let contexteBuilder;
21
-
22
- describe('Calcul des besoins et des pertes des installations ECS', () => {
23
- beforeEach(() => {
24
- generateurEcsService = new GenerateurEcsService();
25
- service = new InstallationEcsService(generateurEcsService);
26
- normalizerService = new DpeNormalizerService();
27
- contexteBuilder = new ContexteBuilder();
28
- });
29
-
30
- test.each([
31
- {
32
- label: "Installation ECS sans surface habitable pour l'installation",
33
- surfaceHabitableLogement: 40,
34
- besoin_ecs: 850,
35
- besoin_ecs_depensier: 1050,
36
- expected: { besoinEcsDepensierInstallation: 1050, besoinEcsInstallation: 850 }
37
- },
38
- {
39
- label: "Installation ECS avec surface habitable pour l'installation",
40
- surfaceHabitableLogement: 40,
41
- surfaceHabitableInstallation: 50,
42
- besoin_ecs: 850,
43
- besoin_ecs_depensier: 1050,
44
- expected: { besoinEcsDepensierInstallation: 1312.5, besoinEcsInstallation: 1062.5 }
45
- }
46
- ])(
47
- 'Calcul des besoins proratisés pour $label',
48
- ({
49
- surfaceHabitableLogement,
50
- surfaceHabitableInstallation = undefined,
51
- besoin_ecs,
52
- besoin_ecs_depensier,
53
- expected
54
- }) => {
55
- /** @type {Contexte} */
56
- const ctx = { surfaceHabitable: surfaceHabitableLogement };
57
-
58
- /** @type {{besoin_ecs: number, besoin_ecs_depensier: number}} */
59
- const besoinEcs = { besoin_ecs, besoin_ecs_depensier };
60
-
61
- /** @type {InstallationEcs} */
62
- const installationEcs = {
63
- donnee_entree: { surface_habitable: surfaceHabitableInstallation }
64
- };
65
-
66
- const besoinEcsInstallation = service.besoinEcsInstallation(ctx, installationEcs, besoinEcs);
67
-
68
- expect(besoinEcsInstallation).toStrictEqual(expected);
69
- }
70
- );
71
-
72
- test.each([
73
- {
74
- label: 'Installation individuelle avec générateur hors volume chauffé',
75
- enumTypeInstallationId: 1,
76
- positionVolumeChauffeStockage1: 0,
77
- positionVolumeChauffeStockage2: 0,
78
- positionVolumeChauffeStockage3: 0,
79
- expected: {
80
- QdwIndVc: { conventionnel: 15, depensier: 27 },
81
- QdwColVc: { conventionnel: 0, depensier: 0 },
82
- QdwColHVc: { conventionnel: 0, depensier: 0 },
83
- QgwRecuperable: 0
84
- }
85
- },
86
- {
87
- label: 'Installation individuelle avec générateur hors volume chauffé',
88
- enumTypeInstallationId: 1,
89
- positionVolumeChauffeStockage2: 0,
90
- positionVolumeChauffe2: 0,
91
- positionVolumeChauffeStockage3: 0,
92
- expected: {
93
- QdwIndVc: { conventionnel: 15, depensier: 27 },
94
- QdwColVc: { conventionnel: 0, depensier: 0 },
95
- QdwColHVc: { conventionnel: 0, depensier: 0 },
96
- QgwRecuperable: 0
97
- }
98
- },
99
- {
100
- label: 'Installation individuelle avec 1 générateur en volume chauffé',
101
- enumTypeInstallationId: 1,
102
- positionVolumeChauffeStockage1: 0,
103
- positionVolumeChauffeStockage2: 1,
104
- positionVolumeChauffeStockage3: 0,
105
- expected: {
106
- QdwIndVc: { conventionnel: 15, depensier: 27 },
107
- QdwColVc: { conventionnel: 0, depensier: 0 },
108
- QdwColHVc: { conventionnel: 0, depensier: 0 },
109
- QgwRecuperable: 120
110
- }
111
- },
112
- {
113
- label: 'Installation individuelle avec 1 générateur en volume chauffé',
114
- enumTypeInstallationId: 1,
115
- positionVolumeChauffeStockage1: 0,
116
- positionVolumeChauffe2: 1,
117
- positionVolumeChauffeStockage3: 0,
118
- expected: {
119
- QdwIndVc: { conventionnel: 15, depensier: 27 },
120
- QdwColVc: { conventionnel: 0, depensier: 0 },
121
- QdwColHVc: { conventionnel: 0, depensier: 0 },
122
- QgwRecuperable: 120
123
- }
124
- },
125
- {
126
- label: 'Installation individuelle avec 2 générateurs en volume chauffé',
127
- enumTypeInstallationId: 1,
128
- positionVolumeChauffeStockage1: 0,
129
- positionVolumeChauffeStockage2: 1,
130
- positionVolumeChauffeStockage3: 1,
131
- expected: {
132
- QdwIndVc: { conventionnel: 15, depensier: 27 },
133
- QdwColVc: { conventionnel: 0, depensier: 0 },
134
- QdwColHVc: { conventionnel: 0, depensier: 0 },
135
- QgwRecuperable: 260
136
- }
137
- },
138
- {
139
- label: 'Installation collective avec générateur hors volume chauffé',
140
- enumTypeInstallationId: 2,
141
- positionVolumeChauffeStockage1: 0,
142
- positionVolumeChauffeStockage2: 0,
143
- positionVolumeChauffeStockage3: 0,
144
- expected: {
145
- QdwIndVc: { conventionnel: 15, depensier: 27 },
146
- QdwColVc: { conventionnel: 16.8, depensier: 30.24 },
147
- QdwColHVc: { conventionnel: 4.2, depensier: 7.56 },
148
- QgwRecuperable: 0
149
- }
150
- },
151
- {
152
- label: 'Installation collective avec 1 générateur en volume chauffé',
153
- enumTypeInstallationId: 2,
154
- positionVolumeChauffeStockage1: 0,
155
- positionVolumeChauffeStockage2: 0,
156
- positionVolumeChauffeStockage3: 1,
157
- expected: {
158
- QdwIndVc: { conventionnel: 15, depensier: 27 },
159
- QdwColVc: { conventionnel: 16.8, depensier: 30.24 },
160
- QdwColHVc: { conventionnel: 4.2, depensier: 7.56 },
161
- QgwRecuperable: 0
162
- }
163
- },
164
- {
165
- label: 'Installation collective avec 2 générateurs en volume chauffé',
166
- enumTypeInstallationId: 2,
167
- positionVolumeChauffeStockage1: 1,
168
- positionVolumeChauffeStockage2: 1,
169
- positionVolumeChauffeStockage3: 0,
170
- expected: {
171
- QdwIndVc: { conventionnel: 15, depensier: 27 },
172
- QdwColVc: { conventionnel: 16.8, depensier: 30.24 },
173
- QdwColHVc: { conventionnel: 4.2, depensier: 7.56 },
174
- QgwRecuperable: 0
175
- }
176
- }
177
- ])(
178
- 'Calcul des pertes de distribution et de stockage $label',
179
- ({
180
- enumTypeInstallationId,
181
- positionVolumeChauffeStockage1,
182
- positionVolumeChauffeStockage2,
183
- positionVolumeChauffe2 = undefined,
184
- positionVolumeChauffeStockage3,
185
- expected
186
- }) => {
187
- /** @type {InstallationEcs} */
188
- const installationEcs = {
189
- donnee_entree: { enum_type_installation_id: enumTypeInstallationId },
190
- generateur_ecs_collection: {
191
- generateur_ecs: [
192
- {
193
- donnee_entree: { position_volume_chauffe_stockage: positionVolumeChauffeStockage1 },
194
- donnee_utilisateur: { Qgw: 100 }
195
- },
196
- {
197
- donnee_entree: {
198
- position_volume_chauffe_stockage: positionVolumeChauffeStockage2,
199
- position_volume_chauffe: positionVolumeChauffe2
200
- },
201
- donnee_utilisateur: { Qgw: 120 }
202
- },
203
- {
204
- donnee_entree: { position_volume_chauffe_stockage: positionVolumeChauffeStockage3 },
205
- donnee_utilisateur: { Qgw: 140 }
206
- }
207
- ]
208
- }
209
- };
210
-
211
- service.pertesDistributionStockageEcsInstallation(installationEcs, 150, 270);
212
-
213
- expect(installationEcs.donnee_utilisateur.QdwIndVc.conventionnel).toBeCloseTo(
214
- expected.QdwIndVc.conventionnel,
215
- 5
216
- );
217
- expect(installationEcs.donnee_utilisateur.QdwIndVc.depensier).toBeCloseTo(
218
- expected.QdwIndVc.depensier,
219
- 5
220
- );
221
- expect(installationEcs.donnee_utilisateur.QdwColVc.conventionnel).toBeCloseTo(
222
- expected.QdwColVc.conventionnel,
223
- 5
224
- );
225
- expect(installationEcs.donnee_utilisateur.QdwColVc.depensier).toBeCloseTo(
226
- expected.QdwColVc.depensier,
227
- 5
228
- );
229
- expect(installationEcs.donnee_utilisateur.QdwColHVc.conventionnel).toBeCloseTo(
230
- expected.QdwColHVc.conventionnel,
231
- 5
232
- );
233
- expect(installationEcs.donnee_utilisateur.QdwColHVc.depensier).toBeCloseTo(
234
- expected.QdwColHVc.depensier,
235
- 5
236
- );
237
- expect(installationEcs.donnee_utilisateur.QgwRecuperable).toBeCloseTo(
238
- expected.QgwRecuperable,
239
- 5
240
- );
241
- }
242
- );
243
-
244
- test("Determination du caractère individuelle de l'installation", () => {
245
- expect(service.isInstallationIndividuelle({ enum_type_installation_id: 1 })).toBeTruthy();
246
- expect(service.isInstallationIndividuelle({ enum_type_installation_id: 2 })).toBeFalsy();
247
- expect(service.isInstallationIndividuelle({ enum_type_installation_id: 3 })).toBeFalsy();
248
- expect(service.isInstallationIndividuelle({ enum_type_installation_id: 4 })).toBeFalsy();
249
- });
250
-
251
- test('execute : parcourt les installations ECS et renseigne besoins et pertes', () => {
252
- // Le calcul des Qgw par générateur est délégué au GenerateurEcsService : on le double
253
- // pour isoler la logique d'orchestration de l'installation.
254
- vi.spyOn(generateurEcsService, 'execute').mockReturnValue(undefined);
255
-
256
- /** @type {Contexte} */
257
- const ctx = { surfaceHabitable: 100 };
258
-
259
- /** @type {InstallationEcs} */
260
- const installationEcs = {
261
- donnee_entree: { enum_type_installation_id: 1, surface_habitable: 100 },
262
- generateur_ecs_collection: {
263
- generateur_ecs: [
264
- {
265
- donnee_entree: { position_volume_chauffe_stockage: 1 },
266
- donnee_utilisateur: { Qgw: 500 }
267
- }
268
- ]
269
- }
270
- };
271
-
272
- /** @type {Logement} */
273
- const logement = { installation_ecs_collection: { installation_ecs: [installationEcs] } };
274
-
275
- service.execute(ctx, logement, { besoin_ecs: 1000, besoin_ecs_depensier: 1500 });
276
-
277
- // Délégation du calcul par générateur
278
- expect(generateurEcsService.execute).toHaveBeenCalledWith(installationEcs);
279
-
280
- // Besoins proratisés (surface installation === surface logement -> ratio 1)
281
- expect(installationEcs.donnee_intermediaire).toStrictEqual({
282
- besoin_ecs: 1000,
283
- besoin_ecs_depensier: 1500
284
- });
285
-
286
- // Pertes de distribution individuelle : 0.1 * besoin (en Wh) ; installation individuelle -> QdwCol* = 0
287
- expect(installationEcs.donnee_utilisateur.QdwIndVc.conventionnel).toBeCloseTo(100000, 5);
288
- expect(installationEcs.donnee_utilisateur.QdwIndVc.depensier).toBeCloseTo(150000, 5);
289
- expect(installationEcs.donnee_utilisateur.QdwColVc.conventionnel).toBe(0);
290
- // Générateur en volume chauffé -> stockage récupérable = Qgw
291
- expect(installationEcs.donnee_utilisateur.QgwRecuperable).toBe(500);
292
- });
293
-
294
- test('execute : aucune installation traitée quand la collection ECS est absente', () => {
295
- // Valeur de repli [] quand installation_ecs_collection est absente.
296
- const generateurSpy = vi.spyOn(generateurEcsService, 'execute');
297
-
298
- service.execute({ surfaceHabitable: 100 }, {}, { besoin_ecs: 0, besoin_ecs_depensier: 0 });
299
-
300
- expect(generateurSpy).not.toHaveBeenCalled();
301
- });
302
-
303
- describeIntegration("Test d'intégration des installations ECS", () => {
304
- test.each(corpus)('vérification des DI des installations ECS pour dpe %s', (ademeId) => {
305
- let dpeRequest = getAdemeFileJson(ademeId);
306
- dpeRequest = normalizerService.normalize(dpeRequest);
307
-
308
- /** @type {Contexte} */
309
- const ctx = contexteBuilder.fromDpe(dpeRequest);
310
-
311
- const installationsECS = structuredClone(
312
- dpeRequest.logement.installation_ecs_collection?.installation_ecs || []
313
- );
314
- service.execute(ctx, dpeRequest.logement, {
315
- besoin_ecs: dpeRequest.logement.sortie.apport_et_besoin.besoin_ecs,
316
- besoin_ecs_depensier: dpeRequest.logement.sortie.apport_et_besoin.besoin_ecs_depensier
317
- });
318
-
319
- installationsECS.forEach((installationECS, i) => {
320
- expect_or(
321
- () =>
322
- expect(installationECS.donnee_intermediaire.besoin_ecs).toBeCloseTo(
323
- dpeRequest.logement.installation_ecs_collection.installation_ecs[i]
324
- .donnee_intermediaire.besoin_ecs,
325
- 2
326
- ),
327
- () =>
328
- expect(installationECS.donnee_intermediaire.besoin_ecs).toBeCloseTo(
329
- dpeRequest.logement.installation_ecs_collection.installation_ecs[i]
330
- .donnee_intermediaire.besoin_ecs * 1000,
331
- 2
332
- )
333
- );
334
- });
335
- });
336
- });
337
- });