@open3cl/engine 1.0.12 → 1.0.14

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 (34) hide show
  1. package/13.2_generateur_combustion_ch.js +1 -1
  2. package/features/dpe/domain/models/dpe.model.ts +9 -0
  3. package/features/dpe/domain/models/installation-chauffage.model.ts +26 -2
  4. package/features/dpe/domain/models/type-generateur.model.js +8 -0
  5. package/features/dpe/infrastructure/ch/chTv.store.js +94 -0
  6. package/features/dpe/infrastructure/ch/chTv.store.spec.js +99 -0
  7. package/features/dpe/infrastructure/ecs/ecsTv.store.js +5 -3
  8. package/features/dpe/infrastructure/ecs/ecsTv.store.spec.js +4 -0
  9. package/features/dpe/infrastructure/froid/frTv.store.js +4 -2
  10. package/features/dpe/infrastructure/tv.store.js +28 -0
  11. package/features/dpe/infrastructure/tv.store.spec.js +12 -0
  12. package/features/engine/domain/apport_et_besoin/apport-et-besoin.service.js +57 -3
  13. package/features/engine/domain/apport_et_besoin/apport-et-besoin.service.spec.js +117 -2
  14. package/features/engine/domain/apport_et_besoin/apport_gratuit/apport-gratuit.service.js +18 -2
  15. package/features/engine/domain/apport_et_besoin/apport_gratuit/apport-gratuit.service.spec.js +13 -2
  16. package/features/engine/domain/apport_et_besoin/ch/besoin-ch.service.js +150 -0
  17. package/features/engine/domain/apport_et_besoin/ch/besoin-ch.service.spec.js +145 -0
  18. package/features/engine/domain/apport_et_besoin/ch/perte-ch-recup.service.js +168 -0
  19. package/features/engine/domain/apport_et_besoin/ch/perte-ch-recup.service.spec.js +313 -0
  20. package/features/engine/domain/apport_et_besoin/ecs/perte-ecs-recup.service.js +12 -8
  21. package/features/engine/domain/apport_et_besoin/ecs/perte-ecs-recup.service.spec.js +28 -13
  22. package/features/engine/domain/ch/emetteur-ch.service.js +100 -0
  23. package/features/engine/domain/ch/emetteur-ch.service.spec.js +78 -0
  24. package/features/engine/domain/ch/generateur-ch.service.js +365 -0
  25. package/features/engine/domain/ch/generateur-ch.service.spec.js +734 -0
  26. package/features/engine/domain/ch/installation-ch.service.js +39 -0
  27. package/features/engine/domain/ch/installation-ch.service.spec.js +41 -0
  28. package/features/engine/domain/contexte.builder.js +1 -0
  29. package/features/engine/domain/contexte.builder.spec.js +2 -0
  30. package/features/engine/domain/ecs/generateur-ecs.service.js +2 -2
  31. package/features/engine/domain/ecs/installation-ecs.service.js +5 -5
  32. package/features/engine/domain/engine.service.js +13 -2
  33. package/features/engine/domain/models/contexte.model.ts +1 -0
  34. package/package.json +1 -1
@@ -7,6 +7,14 @@ import { BesoinFroidService } from './froid/besoin-froid.service.js';
7
7
  import { ApportGratuitService } from './apport_gratuit/apport-gratuit.service.js';
8
8
  import { PerteEcsRecupService } from './ecs/perte-ecs-recup.service.js';
9
9
  import { InstallationEcsService } from '../ecs/installation-ecs.service.js';
10
+ import { PerteChRecupService } from './ch/perte-ch-recup.service.js';
11
+ import { BesoinChService } from './ch/besoin-ch.service.js';
12
+ import corpus from '../../../../../test/corpus-sano.json';
13
+ import { getAdemeFileJson } from '../../../../../test/test-helpers.js';
14
+ import { PRECISION_PERCENT } from '../../../../../test/constant.js';
15
+ import { DpeNormalizerService } from '../../../normalizer/domain/dpe-normalizer.service.js';
16
+ import { ContexteBuilder } from '../contexte.builder.js';
17
+ import { InstallationChService } from '../ch/installation-ch.service.js';
10
18
 
11
19
  /** @type {SurfaceSudEquivalenteService} **/
12
20
  let surfaceSudEquivalenteService;
@@ -20,12 +28,27 @@ let besoinFroidService;
20
28
  /** @type {InstallationEcsService} **/
21
29
  let installationEcsService;
22
30
 
31
+ /** @type {InstallationChService} **/
32
+ let installationChService;
33
+
23
34
  /** @type {PerteEcsRecupService} **/
24
35
  let perteEcsRecupService;
25
36
 
26
37
  /** @type {ApportGratuitService} **/
27
38
  let apportGratuitService;
28
39
 
40
+ /** @type {PerteChRecupService} **/
41
+ let perteChRecupService;
42
+
43
+ /** @type {BesoinChService} **/
44
+ let besoinChService;
45
+
46
+ /** @type {DpeNormalizerService} **/
47
+ let normalizerService;
48
+
49
+ /** @type {ContexteBuilder} **/
50
+ let contexteBuilder;
51
+
29
52
  /** @type {ApportEtBesoinService} **/
30
53
  let service;
31
54
 
@@ -37,18 +60,26 @@ describe('Calcul des apports et besoin du logement', () => {
37
60
  tvStore = new BaieVitreeTvStore();
38
61
  surfaceSudEquivalenteService = new SurfaceSudEquivalenteService(tvStore);
39
62
  besoinEcsService = new BesoinEcsService();
63
+ besoinChService = new BesoinChService();
40
64
  besoinFroidService = new BesoinFroidService();
41
65
  installationEcsService = new InstallationEcsService();
66
+ installationChService = new InstallationChService();
42
67
  perteEcsRecupService = new PerteEcsRecupService();
43
68
  apportGratuitService = new ApportGratuitService();
69
+ perteChRecupService = new PerteChRecupService();
44
70
  service = new ApportEtBesoinService(
45
71
  besoinEcsService,
72
+ besoinChService,
46
73
  installationEcsService,
74
+ installationChService,
47
75
  perteEcsRecupService,
48
76
  besoinFroidService,
49
77
  surfaceSudEquivalenteService,
78
+ perteChRecupService,
50
79
  apportGratuitService
51
80
  );
81
+ normalizerService = new DpeNormalizerService();
82
+ contexteBuilder = new ContexteBuilder();
52
83
  });
53
84
 
54
85
  test('Determination des apports et besoin du logement', () => {
@@ -57,6 +88,10 @@ describe('Calcul des apports et besoin du logement', () => {
57
88
  besoin_ecs: 1526,
58
89
  besoin_ecs_depensier: 2685.3
59
90
  });
91
+ vi.spyOn(besoinChService, 'execute').mockReturnValue({
92
+ besoin_ch_hp: 2563,
93
+ besoin_ch_depensier_hp: 3258.6
94
+ });
60
95
  vi.spyOn(besoinFroidService, 'execute').mockReturnValue({
61
96
  besoin_fr: 896,
62
97
  besoin_fr_depensier: 1025.3
@@ -70,10 +105,16 @@ describe('Calcul des apports et besoin du logement', () => {
70
105
  apport_interne_fr: 3345.2
71
106
  });
72
107
  vi.spyOn(installationEcsService, 'execute').mockReturnThis();
108
+ vi.spyOn(installationChService, 'execute').mockReturnThis();
73
109
  vi.spyOn(perteEcsRecupService, 'execute').mockReturnValue({
74
110
  pertes_distribution_ecs_recup: 354.2,
75
111
  pertes_distribution_ecs_recup_depensier: 532.6,
76
- pertes_stockage_ecs_recup: 25.9
112
+ pertes_stockage_ecs_recup: 25.9,
113
+ pertes_stockage_ecs_recup_depensier: 12.9
114
+ });
115
+ vi.spyOn(perteChRecupService, 'execute').mockReturnValue({
116
+ pertes_generateur_ch_recup: 102.2,
117
+ pertes_generateur_ch_recup_depensier: 153.4
77
118
  });
78
119
 
79
120
  /** @type {Contexte} */
@@ -84,6 +125,8 @@ describe('Calcul des apports et besoin du logement', () => {
84
125
  surface_sud_equivalente: 18.5,
85
126
  besoin_ecs: 1526,
86
127
  besoin_ecs_depensier: 2685.3,
128
+ besoin_ch: 2080.7000000000003,
129
+ besoin_ch_depensier: 2559.7,
87
130
  besoin_fr: 896,
88
131
  besoin_fr_depensier: 1025.3,
89
132
  apport_solaire_ch: 5236.9,
@@ -95,12 +138,84 @@ describe('Calcul des apports et besoin du logement', () => {
95
138
  v40_ecs_journalier_depensier: 197.5,
96
139
  pertes_distribution_ecs_recup: 354.2,
97
140
  pertes_distribution_ecs_recup_depensier: 532.6,
98
- pertes_stockage_ecs_recup: 25.9
141
+ pertes_generateur_ch_recup: 102.2,
142
+ pertes_generateur_ch_recup_depensier: 153.4,
143
+ pertes_stockage_ecs_recup: 25.9,
144
+ pertes_stockage_ecs_recup_depensier: 12.9
99
145
  });
100
146
  expect(surfaceSudEquivalenteService.execute).toHaveBeenCalledWith(ctx, logement.enveloppe);
101
147
  expect(besoinEcsService.execute).toHaveBeenCalledWith(ctx);
102
148
  expect(besoinFroidService.execute).toHaveBeenCalledWith(ctx, logement);
103
149
  expect(apportGratuitService.apportSolaire).toHaveBeenCalledWith(ctx, logement);
104
150
  expect(apportGratuitService.apportInterne).toHaveBeenCalledWith(ctx, logement);
151
+ expect(perteEcsRecupService.execute).toHaveBeenCalledWith(ctx, logement);
152
+ expect(perteChRecupService.execute).toHaveBeenCalledWith(ctx, logement);
153
+ expect(installationEcsService.execute).toHaveBeenCalledWith(ctx, logement, {
154
+ besoin_ecs: 1526,
155
+ besoin_ecs_depensier: 2685.3
156
+ });
157
+ expect(installationChService.execute).toHaveBeenCalledWith(ctx, logement);
158
+ });
159
+
160
+ describe("Test d'intégration pour les besoins en chauffage", () => {
161
+ test.each(corpus)(
162
+ 'Vérification de la DI besoin_ch des besoins en chauffage pour dpe %s',
163
+ (ademeId) => {
164
+ /**
165
+ * @type {Dpe}
166
+ */
167
+ let dpeRequest = getAdemeFileJson(ademeId);
168
+ dpeRequest = normalizerService.normalize(dpeRequest);
169
+
170
+ dpeRequest.logement.donnees_de_calcul = {
171
+ apportsInterneDepensier: [],
172
+ apportsInterneCh: [],
173
+ apportsSolaire: [],
174
+ besoinChauffageHP: [],
175
+ besoinChauffageDepensierHP: []
176
+ };
177
+
178
+ /** @type {Contexte} */
179
+ const ctx = contexteBuilder.fromDpe(dpeRequest);
180
+ const apportEtBesoin = service.execute(ctx, dpeRequest.logement);
181
+
182
+ expect(
183
+ Math.abs(
184
+ apportEtBesoin.besoin_ch - dpeRequest.logement.sortie.apport_et_besoin.besoin_ch
185
+ ) / (dpeRequest.logement.sortie.apport_et_besoin.besoin_ch || 1)
186
+ ).toBeLessThan(PRECISION_PERCENT);
187
+ }
188
+ );
189
+
190
+ test.each(corpus)(
191
+ 'Vérification de la DI besoin_ch_depensier des besoins en chauffage pour dpe %s',
192
+ (ademeId) => {
193
+ /**
194
+ * @type {Dpe}
195
+ */
196
+ let dpeRequest = getAdemeFileJson(ademeId);
197
+ dpeRequest = normalizerService.normalize(dpeRequest);
198
+
199
+ dpeRequest.logement.donnees_de_calcul = {
200
+ apportsInterneDepensier: [],
201
+ apportsInterneCh: [],
202
+ apportsSolaire: [],
203
+ besoinChauffageHP: [],
204
+ besoinChauffageDepensierHP: []
205
+ };
206
+
207
+ /** @type {Contexte} */
208
+ const ctx = contexteBuilder.fromDpe(dpeRequest);
209
+
210
+ const apportEtBesoin = service.execute(ctx, dpeRequest.logement);
211
+
212
+ expect(
213
+ Math.abs(
214
+ apportEtBesoin.besoin_ch_depensier -
215
+ dpeRequest.logement.sortie.apport_et_besoin.besoin_ch_depensier
216
+ ) / (dpeRequest.logement.sortie.apport_et_besoin.besoin_ch_depensier || 1)
217
+ ).toBeLessThan(PRECISION_PERCENT);
218
+ }
219
+ );
105
220
  });
106
221
  });
@@ -46,7 +46,7 @@ export class ApportGratuitService {
46
46
 
47
47
  return mois_liste.reduce(
48
48
  (acc, mois) => {
49
- acc.apport_solaire_ch += this.apportSolaireMois(
49
+ const apportSolaire = this.apportSolaireMois(
50
50
  ctx,
51
51
  logement.enveloppe,
52
52
  mois,
@@ -58,6 +58,9 @@ export class ApportGratuitService {
58
58
  ctx.inertie.ilpa
59
59
  )
60
60
  );
61
+ acc.apport_solaire_ch += apportSolaire;
62
+ logement.donnees_de_calcul.apportsSolaire[mois] = apportSolaire;
63
+
61
64
  if (clim.length > 0) {
62
65
  acc.apport_solaire_fr += this.apportSolaireMois(
63
66
  ctx,
@@ -84,7 +87,7 @@ export class ApportGratuitService {
84
87
 
85
88
  return mois_liste.reduce(
86
89
  (acc, mois) => {
87
- acc.apport_interne_ch += this.apportInterneMois(
90
+ const apportInterne = this.apportInterneMois(
88
91
  ctx,
89
92
  this.#frTvStore.getData(
90
93
  'nref19',
@@ -94,6 +97,19 @@ export class ApportGratuitService {
94
97
  ctx.inertie.ilpa
95
98
  )
96
99
  );
100
+ acc.apport_interne_ch += apportInterne;
101
+ logement.donnees_de_calcul.apportsInterneCh[mois] = apportInterne;
102
+ logement.donnees_de_calcul.apportsInterneDepensier[mois] = this.apportInterneMois(
103
+ ctx,
104
+ this.#frTvStore.getData(
105
+ 'nref21',
106
+ ctx.altitude.value,
107
+ ctx.zoneClimatique.value,
108
+ mois,
109
+ ctx.inertie.ilpa
110
+ )
111
+ );
112
+
97
113
  if (clim.length > 0) {
98
114
  acc.apport_interne_fr += this.apportInterneMois(
99
115
  ctx,
@@ -41,7 +41,10 @@ describe('Calcul des apports gratuits au logement', () => {
41
41
  };
42
42
 
43
43
  /** @type { Logement } **/
44
- const logement = { enveloppe: { porte_collection: {} } };
44
+ const logement = {
45
+ enveloppe: { porte_collection: {} },
46
+ donnees_de_calcul: { apportsSolaire: {} }
47
+ };
45
48
 
46
49
  if (withClimatisation) {
47
50
  logement.climatisation_collection = { climatisation: [{}] };
@@ -81,6 +84,8 @@ describe('Calcul des apports gratuits au logement', () => {
81
84
  mois
82
85
  );
83
86
  }
87
+
88
+ expect(logement.donnees_de_calcul.apportsSolaire[mois]).toBe(185000);
84
89
  }
85
90
  });
86
91
 
@@ -106,7 +111,10 @@ describe('Calcul des apports gratuits au logement', () => {
106
111
  };
107
112
 
108
113
  /** @type { Logement } **/
109
- const logement = { enveloppe: { porte_collection: {} } };
114
+ const logement = {
115
+ enveloppe: { porte_collection: {} },
116
+ donnees_de_calcul: { apportsInterneCh: {}, apportsInterneDepensier: {} }
117
+ };
110
118
 
111
119
  if (withClimatisation) {
112
120
  logement.climatisation_collection = { climatisation: [{}] };
@@ -141,6 +149,9 @@ describe('Calcul des apports gratuits au logement', () => {
141
149
  mois
142
150
  );
143
151
  }
152
+
153
+ expect(logement.donnees_de_calcul.apportsInterneCh[mois]).toBeCloseTo(1306.33, 2);
154
+ expect(logement.donnees_de_calcul.apportsInterneDepensier[mois]).toBeCloseTo(1306.33, 2);
144
155
  }
145
156
  });
146
157
 
@@ -0,0 +1,150 @@
1
+ import { mois_liste } from '../../../../../utils.js';
2
+ import { inject } from 'dioma';
3
+ import { FrTvStore } from '../../../../dpe/infrastructure/froid/frTv.store.js';
4
+
5
+ /**
6
+ * Calcul du besoin en chauffage
7
+ * Chapitre 2 - Expression du besoin de chauffage
8
+ *
9
+ * Methode_de_calcul_3CL_DPE_2021 - Page 7
10
+ * Octobre 2021
11
+ * @see consolide_anne…arrete_du_31_03_2021_relatif_aux_methodes_et_procedures_applicables.pdf
12
+ */
13
+ export class BesoinChService {
14
+ /**
15
+ * @type {FrTvStore}
16
+ */
17
+ #tvStore;
18
+
19
+ /**
20
+ * @param tvStore {FrTvStore}
21
+ */
22
+ constructor(tvStore = inject(FrTvStore)) {
23
+ this.#tvStore = tvStore;
24
+ }
25
+
26
+ /**
27
+ * Besoin de chauffage hors pertes récupérées
28
+ * 9.1.1 - Consommation de chauffage
29
+ *
30
+ * @param ctx {Contexte}
31
+ * @param logement {Logement}
32
+ * @return {{besoin_ch_hp: number, besoin_ch_depensier_hp: number, fraction_apport_gratuit_ch: number, fraction_apport_gratuit_depensier_ch: number}}
33
+ */
34
+ execute(ctx, logement) {
35
+ const besoinCh = mois_liste.reduce(
36
+ (acc, mois) => {
37
+ const dh19 = this.#tvStore.getData(
38
+ 'dh19',
39
+ ctx.altitude.value,
40
+ ctx.zoneClimatique.value,
41
+ mois,
42
+ ctx.inertie.ilpa
43
+ );
44
+ const dh21 = this.#tvStore.getData(
45
+ 'dh21',
46
+ ctx.altitude.value,
47
+ ctx.zoneClimatique.value,
48
+ mois,
49
+ ctx.inertie.ilpa
50
+ );
51
+
52
+ const besoinCh = this.besoinChHorsPertesRecuperees(ctx, logement, mois, dh19);
53
+ acc.besoin_ch_hp += besoinCh.besoinChMoisHP;
54
+ acc.fraction_apport_gratuit_ch += besoinCh.fractionApportGratuitMois;
55
+ logement.donnees_de_calcul.besoinChauffageHP[mois] = besoinCh.besoinChMoisHP;
56
+
57
+ const besoinChDepensier = this.besoinChHorsPertesRecuperees(ctx, logement, mois, dh21);
58
+ acc.besoin_ch_depensier_hp += besoinChDepensier.besoinChMoisHP;
59
+ acc.fraction_apport_gratuit_depensier_ch += besoinChDepensier.fractionApportGratuitMois;
60
+ logement.donnees_de_calcul.besoinChauffageDepensierHP[mois] =
61
+ besoinChDepensier.besoinChMoisHP;
62
+
63
+ acc.dh19 += dh19;
64
+ acc.dh21 += dh21;
65
+ return acc;
66
+ },
67
+ {
68
+ besoin_ch_hp: 0,
69
+ besoin_ch_depensier_hp: 0,
70
+ fraction_apport_gratuit_ch: 0,
71
+ fraction_apport_gratuit_depensier_ch: 0,
72
+ dh19: 0,
73
+ dh21: 0
74
+ }
75
+ );
76
+
77
+ besoinCh.fraction_apport_gratuit_ch /= besoinCh.dh19;
78
+ besoinCh.fraction_apport_gratuit_depensier_ch /= besoinCh.dh21;
79
+
80
+ delete besoinCh.dh19;
81
+ delete besoinCh.dh19;
82
+
83
+ return besoinCh;
84
+ }
85
+
86
+ /**
87
+ * Fraction des besoins de chauffage couverts par les apports gratuits pour un mois donné
88
+ * 6.1 Calcul de F
89
+ *
90
+ * @param ctx {Contexte}
91
+ * @param logement {Logement}
92
+ * @param mois {string}
93
+ * @param dh {number} - degrés-heures de chauffage sur le mois j (°Ch)
94
+ * @returns {number}
95
+ */
96
+ fractionBesoinCh(ctx, logement, mois, dh) {
97
+ // Apports internes dans le logement sur le mois
98
+ const Ai = logement.donnees_de_calcul.apportsInterneCh[mois];
99
+ // Apports solaires dans le logement sur le mois durant la période de chauffe
100
+ const As = logement.donnees_de_calcul.apportsSolaire[mois];
101
+
102
+ if (dh === 0) return 0;
103
+
104
+ let pow;
105
+ switch (ctx.inertie.id) {
106
+ // Inertie très lourde ou lourde
107
+ case 1:
108
+ case 2:
109
+ pow = 3.6;
110
+ break;
111
+ // Inertie moyenne
112
+ case 3:
113
+ pow = 2.9;
114
+ break;
115
+ // Inertie légère
116
+ case 4:
117
+ pow = 2.5;
118
+ break;
119
+ }
120
+
121
+ const Xj = (As + Ai) / (logement.sortie.deperdition.deperdition_enveloppe * dh);
122
+ return (Xj - Xj ** pow) / (1 - Xj ** pow);
123
+ }
124
+
125
+ /**
126
+ * Besoin de chauffage hors pertes récupérées sur le mois (kWh) :
127
+ * 9.1.1 - Consommation de chauffage
128
+ *
129
+ * Fraction des besoins de chauffage couverts par les apports gratuits sur le mois
130
+ * 6.1 - Calcul de F
131
+ *
132
+ * @param ctx {Contexte}
133
+ * @param logement {Logement}
134
+ * @param mois {string}
135
+ * @param dh {number} - degrés-heures de chauffage sur le mois j (°Ch)
136
+ * @returns {{besoinChMoisHP: number, fractionApportGratuitMois: number}}
137
+ */
138
+ besoinChHorsPertesRecuperees(ctx, logement, mois, dh) {
139
+ // Fraction des besoins de chauffage couverts par les apports gratuits pour un mois donné
140
+ const F = this.fractionBesoinCh(ctx, logement, mois, dh);
141
+
142
+ // Besoin de chauffage d’un logement par kelvin sur le mois j (W/K)
143
+ const BV = logement.sortie.deperdition.deperdition_enveloppe * (1 - F);
144
+
145
+ return {
146
+ besoinChMoisHP: (BV * dh) / 1000,
147
+ fractionApportGratuitMois: F * dh
148
+ };
149
+ }
150
+ }
@@ -0,0 +1,145 @@
1
+ import { beforeEach, describe, expect, test, vi } from 'vitest';
2
+ import { FrTvStore } from '../../../../dpe/infrastructure/froid/frTv.store.js';
3
+ import { BesoinChService } from './besoin-ch.service.js';
4
+ import { mois_liste } from '../../../../../utils.js';
5
+
6
+ /** @type {BesoinChService} **/
7
+ let service;
8
+
9
+ /** @type {FrTvStore} **/
10
+ let tvStore;
11
+
12
+ describe('Calcul des besoins en chauffage du logement', () => {
13
+ beforeEach(() => {
14
+ tvStore = new FrTvStore();
15
+ service = new BesoinChService(tvStore);
16
+ });
17
+
18
+ test.each([
19
+ {
20
+ inertie: 1,
21
+ expected: 0.24
22
+ },
23
+ {
24
+ inertie: 2,
25
+ expected: 0.24
26
+ },
27
+ {
28
+ inertie: 3,
29
+ expected: 0.23
30
+ },
31
+ {
32
+ inertie: 4,
33
+ expected: 0.22
34
+ }
35
+ ])(
36
+ 'Determination de la fraction des besoins de chauffage couverts par les apports gratuits pour un mois donné avec inertie = $inertie',
37
+ ({ inertie, expected }) => {
38
+ /** @type {Contexte} */
39
+ const ctx = { inertie: { id: inertie } };
40
+
41
+ /** @type { Logement } **/
42
+ const logement = {
43
+ donnees_de_calcul: {
44
+ apportsInterneCh: { Janvier: 102.5 },
45
+ apportsSolaire: { Janvier: 12.5 }
46
+ },
47
+ sortie: { deperdition: { deperdition_enveloppe: 25.9 } }
48
+ };
49
+ expect(service.fractionBesoinCh(ctx, logement, 'Janvier', 18)).toBeCloseTo(expected, 2);
50
+ }
51
+ );
52
+
53
+ test('Besoin de chauffage hors pertes récupérées et fraction des apports gratuits pour un mois', () => {
54
+ /** @type {Contexte} */
55
+ const ctx = { inertie: { id: 1 } };
56
+
57
+ /** @type { Logement } **/
58
+ const logement = {
59
+ donnees_de_calcul: {
60
+ apportsInterneCh: { Janvier: 102.5 },
61
+ apportsSolaire: { Janvier: 12.5 }
62
+ },
63
+ sortie: { deperdition: { deperdition_enveloppe: 25.9 } }
64
+ };
65
+
66
+ const besoinHP = service.besoinChHorsPertesRecuperees(ctx, logement, 'Janvier', 12.5);
67
+ expect(besoinHP.besoinChMoisHP).toBeCloseTo(0.2139, 4);
68
+ expect(besoinHP.fractionApportGratuitMois).toBeCloseTo(4.2412, 4);
69
+ });
70
+
71
+ test('Besoin total de chauffage hors pertes récupérées et fraction des apports gratuits', () => {
72
+ vi.spyOn(tvStore, 'getData').mockReturnValue(10);
73
+
74
+ /** @type {Contexte} */
75
+ const ctx = {
76
+ inertie: { id: 1, ilpa: 1 },
77
+ altitude: { value: '400-800m' },
78
+ zoneClimatique: { value: 'h3c' }
79
+ };
80
+
81
+ /** @type { Logement } **/
82
+ const logement = {
83
+ donnees_de_calcul: {
84
+ apportsInterneCh: {
85
+ Janvier: 102.5,
86
+ Février: 102.5,
87
+ Mars: 102.5,
88
+ Avril: 102.5,
89
+ Mai: 102.5,
90
+ Juin: 102.5,
91
+ Juillet: 102.5,
92
+ Aout: 102.5,
93
+ Septembre: 102.5,
94
+ Octobre: 102.5,
95
+ Novembre: 102.5,
96
+ Décembre: 102.5
97
+ },
98
+ apportsSolaire: {
99
+ Janvier: 102.5,
100
+ Février: 102.5,
101
+ Mars: 102.5,
102
+ Avril: 102.5,
103
+ Mai: 102.5,
104
+ Juin: 102.5,
105
+ Juillet: 102.5,
106
+ Aout: 102.5,
107
+ Septembre: 102.5,
108
+ Octobre: 102.5,
109
+ Novembre: 102.5,
110
+ Décembre: 102.5
111
+ },
112
+ besoinChauffageHP: {},
113
+ besoinChauffageDepensierHP: {}
114
+ },
115
+ sortie: { deperdition: { deperdition_enveloppe: 25.9 } }
116
+ };
117
+
118
+ const besoinTotalHP = service.execute(ctx, logement);
119
+ expect(besoinTotalHP.besoin_ch_hp).toBeCloseTo(1.1388, 4);
120
+ expect(besoinTotalHP.besoin_ch_depensier_hp).toBeCloseTo(1.1388, 4);
121
+ expect(besoinTotalHP.fraction_apport_gratuit_ch).toBeCloseTo(0.6336, 4);
122
+ expect(besoinTotalHP.fraction_apport_gratuit_depensier_ch).toBeCloseTo(0.6336, 4);
123
+
124
+ for (const mois of mois_liste) {
125
+ expect(tvStore.getData).toHaveBeenCalledWith(
126
+ 'dh19',
127
+ ctx.altitude.value,
128
+ ctx.zoneClimatique.value,
129
+ mois,
130
+ ctx.inertie.ilpa
131
+ );
132
+
133
+ expect(tvStore.getData).toHaveBeenCalledWith(
134
+ 'dh21',
135
+ ctx.altitude.value,
136
+ ctx.zoneClimatique.value,
137
+ mois,
138
+ ctx.inertie.ilpa
139
+ );
140
+
141
+ expect(logement.donnees_de_calcul.besoinChauffageHP[mois]).toBeCloseTo(0.0949, 4);
142
+ expect(logement.donnees_de_calcul.besoinChauffageDepensierHP[mois]).toBeCloseTo(0.0949, 4);
143
+ }
144
+ });
145
+ });