@open3cl/engine 1.0.11 → 1.0.13

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 (25) hide show
  1. package/features/dpe/domain/models/dpe.model.ts +9 -0
  2. package/features/dpe/domain/models/installation-chauffage.model.ts +2 -2
  3. package/features/dpe/domain/models/installation-ecs.model.ts +15 -2
  4. package/features/dpe/domain/models/type-habitation.model.js +1 -0
  5. package/features/dpe/domain/models/type-stockage.model.js +8 -0
  6. package/features/dpe/infrastructure/ecs/ecsTv.store.js +43 -0
  7. package/features/dpe/infrastructure/ecs/ecsTv.store.spec.js +36 -0
  8. package/features/dpe/infrastructure/froid/frTv.store.js +4 -2
  9. package/features/engine/domain/apport_et_besoin/apport-et-besoin.service.js +69 -3
  10. package/features/engine/domain/apport_et_besoin/apport-et-besoin.service.spec.js +123 -1
  11. package/features/engine/domain/apport_et_besoin/apport_gratuit/apport-gratuit.service.js +18 -2
  12. package/features/engine/domain/apport_et_besoin/apport_gratuit/apport-gratuit.service.spec.js +13 -2
  13. package/features/engine/domain/apport_et_besoin/ch/besoin-ch.service.js +150 -0
  14. package/features/engine/domain/apport_et_besoin/ch/besoin-ch.service.spec.js +145 -0
  15. package/features/engine/domain/apport_et_besoin/ch/perte-ch-recup.service.js +168 -0
  16. package/features/engine/domain/apport_et_besoin/ch/perte-ch-recup.service.spec.js +313 -0
  17. package/features/engine/domain/apport_et_besoin/ecs/perte-ecs-recup.service.js +127 -0
  18. package/features/engine/domain/apport_et_besoin/ecs/perte-ecs-recup.service.spec.js +220 -0
  19. package/features/engine/domain/contexte.builder.js +5 -1
  20. package/features/engine/domain/ecs/generateur-ecs.service.js +106 -0
  21. package/features/engine/domain/ecs/generateur-ecs.service.spec.js +136 -0
  22. package/features/engine/domain/ecs/installation-ecs.service.js +156 -0
  23. package/features/engine/domain/ecs/installation-ecs.service.spec.js +284 -0
  24. package/features/engine/domain/engine.service.js +13 -2
  25. package/package.json +1 -1
@@ -159,6 +159,15 @@ export interface Logement {
159
159
  installation_ecs_collection: { installation_ecs: InstallationEcs[] };
160
160
  installation_chauffage_collection: { installation_chauffage: InstallationChauffage[] };
161
161
  sortie: Sortie;
162
+ donnees_de_calcul: LogementDonneesCalcul;
163
+ }
164
+
165
+ export interface LogementDonneesCalcul {
166
+ apportsInterneCh: { [key: string]: number };
167
+ apportsInterneDepensier: { [key: string]: number };
168
+ apportsSolaire: { [key: string]: number };
169
+ besoinChauffageHP: { [key: string]: number };
170
+ besoinChauffageDepensierHP: { [key: string]: number };
162
171
  }
163
172
 
164
173
  export interface Tertiaire extends Logement {}
@@ -71,7 +71,7 @@ export interface GenerateurChauffageDE extends DE {
71
71
  enum_type_generateur_ch_id: number;
72
72
  enum_usage_generateur_id: number;
73
73
  enum_type_energie_id: number;
74
- position_volume_chauffe: boolean;
74
+ position_volume_chauffe: number;
75
75
  tv_rendement_generation_id?: number;
76
76
  tv_scop_id?: number;
77
77
  tv_temp_fonc_100_id?: number;
@@ -81,7 +81,7 @@ export interface GenerateurChauffageDE extends DE {
81
81
  identifiant_reseau_chaleur?: string;
82
82
  date_arrete_reseau_chaleur?: string;
83
83
  priorite_generateur_cascade?: number;
84
- presence_ventouse?: boolean;
84
+ presence_ventouse?: number;
85
85
  presence_regulation_combustion?: boolean;
86
86
  enum_methode_saisie_carac_sys_id: number;
87
87
  enum_lien_generateur_emetteur_id: number;
@@ -3,6 +3,7 @@ import { DE } from './dpe.model';
3
3
  export interface InstallationEcs {
4
4
  donnee_entree?: InstallationEcsDE;
5
5
  donnee_intermediaire?: InstallationEcsDI;
6
+ donnee_utilisateur?: InstallationEcsDU;
6
7
  generateur_ecs_collection?: { generateur_ecs: GenerateurEcs[] };
7
8
  }
8
9
 
@@ -35,8 +36,16 @@ export interface InstallationEcsDI {
35
36
  conso_ecs_depensier: number;
36
37
  }
37
38
 
39
+ export interface InstallationEcsDU {
40
+ QdwIndVc: { conventionnel: number; depensier: number };
41
+ QdwColVc: { conventionnel: number; depensier: number };
42
+ QdwColHVc: { conventionnel: number; depensier: number };
43
+ QgwRecuperable: number;
44
+ }
45
+
38
46
  export interface GenerateurEcs {
39
47
  donnee_entree?: GenerateurEcsDE;
48
+ donnee_utilisateur?: GenerateurEcsDU;
40
49
  donnee_intermediaire?: GenerateurEcsDI;
41
50
  }
42
51
 
@@ -55,8 +64,8 @@ export interface GenerateurEcsDE extends DE {
55
64
  date_arrete_reseau_chaleur?: string;
56
65
  tv_reseau_chaleur_id?: number;
57
66
  enum_type_stockage_ecs_id: number;
58
- position_volume_chauffe: boolean;
59
- position_volume_chauffe_stockage?: boolean;
67
+ position_volume_chauffe: number;
68
+ position_volume_chauffe_stockage?: number;
60
69
  volume_stockage: number;
61
70
  presence_ventouse?: boolean;
62
71
  }
@@ -74,3 +83,7 @@ export interface GenerateurEcsDI {
74
83
  conso_ecs_depensier: number;
75
84
  rendement_stockage?: number;
76
85
  }
86
+
87
+ export interface GenerateurEcsDU {
88
+ Qgw?: number;
89
+ }
@@ -12,5 +12,6 @@ export const TypeHabitation = {
12
12
  export const TypeDpe = {
13
13
  MAISON: 'MAISON',
14
14
  APPARTEMENT: 'APPARTEMENT',
15
+ APPARTEMENT_A_PARTIR_IMMEUBLE: 'APPARTEMENT_A_PARTIR_IMMEUBLE',
15
16
  IMMEUBLE: 'IMMEUBLE'
16
17
  };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @type {{[key: string]: string}}
3
+ */
4
+ export const TypeStockage = {
5
+ INSTANTANE: 'INSTANTANE',
6
+ INTEGRE: 'INTEGRE',
7
+ INDEPENDANT: 'INDEPENDANT'
8
+ };
@@ -1,5 +1,6 @@
1
1
  import { tvs as tv } from '../../../../tv-v2.js';
2
2
  import { TvStore } from './../tv.store.js';
3
+ import { logger } from '../../../../core/util/logger/log-service.js';
3
4
 
4
5
  /**
5
6
  * Accès aux données des tables de valeurs pour le besoin en eau chaude sanitaire
@@ -19,4 +20,46 @@ export class EcsTvStore extends TvStore {
19
20
  getTefs(classeAltitude, zoneClimatique, mois) {
20
21
  return tv['tefs'][classeAltitude][mois][zoneClimatique];
21
22
  }
23
+
24
+ /**
25
+ * Coefficient de perte du ballon de stockage
26
+ * @see 11.6.2 Pertes des ballons électriques
27
+ *
28
+ * @param enumTypeGenerateurEcsId {string}
29
+ * @param volumeStockage {number}
30
+ *
31
+ * @return {number|undefined}
32
+ */
33
+ getPertesStockage(enumTypeGenerateurEcsId, volumeStockage) {
34
+ let volumeBallon;
35
+ if (volumeStockage <= 100) volumeBallon = '≤ 100';
36
+ else if (volumeStockage <= 200) volumeBallon = '100 < ≤ 200';
37
+ else if (volumeStockage <= 300) volumeBallon = '200 < ≤ 300';
38
+ else volumeBallon = '> 300';
39
+
40
+ const cr = tv['pertes_stockage'].find(
41
+ (v) =>
42
+ v.enum_type_generateur_ecs_id.split('|').includes(enumTypeGenerateurEcsId) &&
43
+ v.volume_ballon === volumeBallon
44
+ )?.cr;
45
+
46
+ if (!cr) {
47
+ logger.error(
48
+ `Pas de valeur forfaitaire cr pour enumTypeGenerateurEcsId:${enumTypeGenerateurEcsId}, volumeBallon:${volumeBallon}`
49
+ );
50
+ return;
51
+ }
52
+
53
+ return parseFloat(cr);
54
+ }
55
+
56
+ /**
57
+ * Récupération des ids des générateurs électriques ECS
58
+ * @return {[number]}
59
+ */
60
+ getElectriqueEcsGenerateurs() {
61
+ return tv['pertes_stockage'].flatMap((v) =>
62
+ v.enum_type_generateur_ecs_id.split('|').map(Number)
63
+ );
64
+ }
22
65
  }
@@ -30,4 +30,40 @@ describe('Lecture des tables de valeurs', () => {
30
30
  expect(ecsTvStore.getTefs(classeAltitude, zoneClimatique, mois)).toBe(expected);
31
31
  });
32
32
  });
33
+
34
+ describe('Lecture des valeurs de pertes_stockage', () => {
35
+ test.each([
36
+ {
37
+ label: 'Ballon électrique à accumulation horizontal < 100L',
38
+ enumTypeGenerateurEcsId: '68',
39
+ volumeStockage: 50,
40
+ expected: 0.39
41
+ },
42
+ {
43
+ label: 'Ballon électrique à accumulation horizontal entre 100L et 200L',
44
+ enumTypeGenerateurEcsId: '68',
45
+ volumeStockage: 150,
46
+ expected: 0.33
47
+ },
48
+ {
49
+ label: 'Ballon électrique à accumulation horizontal entre 200L et 300L',
50
+ enumTypeGenerateurEcsId: '68',
51
+ volumeStockage: 250,
52
+ expected: 0.3
53
+ },
54
+ {
55
+ label: 'Ballon électrique à accumulation horizontal > 300L',
56
+ enumTypeGenerateurEcsId: '68',
57
+ volumeStockage: 301,
58
+ expected: 0.3
59
+ }
60
+ ])(`$label`, ({ enumTypeGenerateurEcsId, volumeStockage, expected }) => {
61
+ expect(ecsTvStore.getPertesStockage(enumTypeGenerateurEcsId, volumeStockage)).toBe(expected);
62
+ });
63
+
64
+ test('pas de valeur de pertes_stockage', () => {
65
+ const pertes_stockage = ecsTvStore.getPertesStockage('298', 600);
66
+ expect(pertes_stockage).toBeUndefined();
67
+ });
68
+ });
33
69
  });
@@ -15,8 +15,10 @@ export class FrTvStore extends TvStore {
15
15
  * — e_fr_28 : ensoleillement reçu en période de refroidissement pour un mois donné et une consigne de refroidissement à 28°C (comportement conventionnel).
16
16
  * — textmoy_clim_26 : Température extérieure moyenne pour un mois donné et une consigne de refroidissement à 26°C (comportement conventionnel).
17
17
  * — textmoy_clim_28 : nombre d’heures de chauffage pour un mois donné et une consigne de refroidissement à 28°C (comportement conventionnel).
18
+ * — dh19 : degrés heures de chauffage pour un mois donné à 19°C (comportement conventionnel).
19
+ * — dh21 : degrés heures de chauffage pour un mois donné à 21°C (comportement dépensier).
18
20
  *
19
- * @param type {'e', nref26' | 'nref28' | 'e_fr_26' | 'e_fr_28' | 'textmoy_clim_26' | 'textmoy_clim_28'}
21
+ * @param type {'e', nref26' | 'nref28' | 'e_fr_26' | 'e_fr_28' | 'textmoy_clim_26' | 'textmoy_clim_28'| 'dh19'| 'dh21'}
20
22
  * @param classeAltitude {string}
21
23
  * @param zoneClimatique {string}
22
24
  * @param mois {string}
@@ -31,6 +33,6 @@ export class FrTvStore extends TvStore {
31
33
  values = values[ilpa];
32
34
  }
33
35
 
34
- return values[classeAltitude][mois][zoneClimatique];
36
+ return parseFloat(values[classeAltitude][mois][zoneClimatique]);
35
37
  }
36
38
  }
@@ -3,6 +3,10 @@ import { BesoinEcsService } from './ecs/besoin-ecs.service.js';
3
3
  import { SurfaceSudEquivalenteService } from './surface-sud-equivalente.service.js';
4
4
  import { BesoinFroidService } from './froid/besoin-froid.service.js';
5
5
  import { ApportGratuitService } from './apport_gratuit/apport-gratuit.service.js';
6
+ import { InstallationEcsService } from '../ecs/installation-ecs.service.js';
7
+ import { PerteEcsRecupService } from './ecs/perte-ecs-recup.service.js';
8
+ import { BesoinChService } from './ch/besoin-ch.service.js';
9
+ import { PerteChRecupService } from './ch/perte-ch-recup.service.js';
6
10
 
7
11
  /**
8
12
  * Calcul des déperditions de l’enveloppe GV
@@ -19,11 +23,31 @@ export class ApportEtBesoinService {
19
23
  */
20
24
  #besoinEcsService;
21
25
 
26
+ /**
27
+ * @type {BesoinChService}
28
+ */
29
+ #besoinChService;
30
+
31
+ /**
32
+ * @type {InstallationEcsService}
33
+ */
34
+ #installationEcsService;
35
+
36
+ /**
37
+ * @type {PerteEcsRecupService}
38
+ */
39
+ #perteEcsRecupService;
40
+
22
41
  /**
23
42
  * @type {BesoinFroidService}
24
43
  */
25
44
  #besoinFroidService;
26
45
 
46
+ /**
47
+ * @type {PerteChRecupService}
48
+ */
49
+ #perteChRecupService;
50
+
27
51
  /**
28
52
  * @type {ApportGratuitService}
29
53
  */
@@ -31,19 +55,31 @@ export class ApportEtBesoinService {
31
55
 
32
56
  /**
33
57
  * @param besoinEcsService {BesoinEcsService}
58
+ * @param besoinChService {BesoinChService}
59
+ * @param installationEcsService {InstallationEcsService}
60
+ * @param perteEcsRecupService {PerteEcsRecupService}
34
61
  * @param besoinFroidService {BesoinFroidService}
35
62
  * @param surfaceSudEquivalenteService {SurfaceSudEquivalenteService}
63
+ * @param perteChRecupService {PerteChRecupService}
36
64
  * @param apportGratuitService {ApportGratuitService}
37
65
  */
38
66
  constructor(
39
67
  besoinEcsService = inject(BesoinEcsService),
68
+ besoinChService = inject(BesoinChService),
69
+ installationEcsService = inject(InstallationEcsService),
70
+ perteEcsRecupService = inject(PerteEcsRecupService),
40
71
  besoinFroidService = inject(BesoinFroidService),
41
72
  surfaceSudEquivalenteService = inject(SurfaceSudEquivalenteService),
73
+ perteChRecupService = inject(PerteChRecupService),
42
74
  apportGratuitService = inject(ApportGratuitService)
43
75
  ) {
44
76
  this.#besoinEcsService = besoinEcsService;
77
+ this.#besoinChService = besoinChService;
78
+ this.#installationEcsService = installationEcsService;
79
+ this.#perteEcsRecupService = perteEcsRecupService;
45
80
  this.#besoinFroidService = besoinFroidService;
46
81
  this.#surfaceSudEquivalenteService = surfaceSudEquivalenteService;
82
+ this.#perteChRecupService = perteChRecupService;
47
83
  this.#apportGratuitService = apportGratuitService;
48
84
  }
49
85
 
@@ -55,8 +91,35 @@ export class ApportEtBesoinService {
55
91
  * @return {ApportEtBesoin}
56
92
  */
57
93
  execute(ctx, logement) {
94
+ const besoinEcs = this.#besoinEcsService.execute(ctx);
95
+
96
+ /**
97
+ * Détermination des besoins et pertes des installations ECS
98
+ */
99
+ this.#installationEcsService.execute(ctx, logement, besoinEcs);
100
+
101
+ const apportsInternes = this.#apportGratuitService.apportInterne(ctx, logement);
102
+ const apportsSolaires = this.#apportGratuitService.apportSolaire(ctx, logement);
103
+
104
+ const pertesEcsRecup = this.#perteEcsRecupService.execute(ctx, logement);
105
+ // Besoin de chauffage hors pertes récupérées
106
+ const besoinChHP = this.#besoinChService.execute(ctx, logement);
107
+ const pertesChauffageRecup = this.#perteChRecupService.execute(ctx, logement);
108
+
109
+ const besoinCh = {
110
+ besoin_ch: besoinChHP.besoin_ch_hp,
111
+ besoin_ch_depensier: besoinChHP.besoin_ch_depensier_hp
112
+ };
113
+ besoinCh.besoin_ch -= pertesEcsRecup.pertes_distribution_ecs_recup;
114
+ besoinCh.besoin_ch -= pertesEcsRecup.pertes_stockage_ecs_recup;
115
+ besoinCh.besoin_ch -= pertesChauffageRecup.pertes_generateur_ch_recup;
116
+
117
+ besoinCh.besoin_ch_depensier -= pertesEcsRecup.pertes_distribution_ecs_recup_depensier;
118
+ besoinCh.besoin_ch_depensier -= pertesEcsRecup.pertes_stockage_ecs_recup_depensier;
119
+ besoinCh.besoin_ch_depensier -= pertesChauffageRecup.pertes_generateur_ch_recup_depensier;
120
+
58
121
  return {
59
- ...this.#besoinEcsService.execute(ctx),
122
+ ...besoinEcs,
60
123
  ...{
61
124
  surface_sud_equivalente: this.#surfaceSudEquivalenteService.execute(
62
125
  ctx,
@@ -67,8 +130,11 @@ export class ApportEtBesoinService {
67
130
  v40_ecs_journalier_depensier: ctx.nadeq * 79
68
131
  },
69
132
  ...this.#besoinFroidService.execute(ctx, logement),
70
- ...this.#apportGratuitService.apportInterne(ctx, logement),
71
- ...this.#apportGratuitService.apportSolaire(ctx, logement)
133
+ ...apportsInternes,
134
+ ...apportsSolaires,
135
+ ...pertesEcsRecup,
136
+ ...pertesChauffageRecup,
137
+ ...besoinCh
72
138
  };
73
139
  }
74
140
  }
@@ -5,6 +5,15 @@ import { ApportEtBesoinService } from './apport-et-besoin.service.js';
5
5
  import { BesoinEcsService } from './ecs/besoin-ecs.service.js';
6
6
  import { BesoinFroidService } from './froid/besoin-froid.service.js';
7
7
  import { ApportGratuitService } from './apport_gratuit/apport-gratuit.service.js';
8
+ import { PerteEcsRecupService } from './ecs/perte-ecs-recup.service.js';
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';
8
17
 
9
18
  /** @type {SurfaceSudEquivalenteService} **/
10
19
  let surfaceSudEquivalenteService;
@@ -15,9 +24,27 @@ let besoinEcsService;
15
24
  /** @type {BesoinFroidService} **/
16
25
  let besoinFroidService;
17
26
 
27
+ /** @type {InstallationEcsService} **/
28
+ let installationEcsService;
29
+
30
+ /** @type {PerteEcsRecupService} **/
31
+ let perteEcsRecupService;
32
+
18
33
  /** @type {ApportGratuitService} **/
19
34
  let apportGratuitService;
20
35
 
36
+ /** @type {PerteChRecupService} **/
37
+ let perteChRecupService;
38
+
39
+ /** @type {BesoinChService} **/
40
+ let besoinChService;
41
+
42
+ /** @type {DpeNormalizerService} **/
43
+ let normalizerService;
44
+
45
+ /** @type {ContexteBuilder} **/
46
+ let contexteBuilder;
47
+
21
48
  /** @type {ApportEtBesoinService} **/
22
49
  let service;
23
50
 
@@ -29,14 +56,24 @@ describe('Calcul des apports et besoin du logement', () => {
29
56
  tvStore = new BaieVitreeTvStore();
30
57
  surfaceSudEquivalenteService = new SurfaceSudEquivalenteService(tvStore);
31
58
  besoinEcsService = new BesoinEcsService();
59
+ besoinChService = new BesoinChService();
32
60
  besoinFroidService = new BesoinFroidService();
61
+ installationEcsService = new InstallationEcsService();
62
+ perteEcsRecupService = new PerteEcsRecupService();
33
63
  apportGratuitService = new ApportGratuitService();
64
+ perteChRecupService = new PerteChRecupService();
34
65
  service = new ApportEtBesoinService(
35
66
  besoinEcsService,
67
+ besoinChService,
68
+ installationEcsService,
69
+ perteEcsRecupService,
36
70
  besoinFroidService,
37
71
  surfaceSudEquivalenteService,
72
+ perteChRecupService,
38
73
  apportGratuitService
39
74
  );
75
+ normalizerService = new DpeNormalizerService();
76
+ contexteBuilder = new ContexteBuilder();
40
77
  });
41
78
 
42
79
  test('Determination des apports et besoin du logement', () => {
@@ -45,6 +82,10 @@ describe('Calcul des apports et besoin du logement', () => {
45
82
  besoin_ecs: 1526,
46
83
  besoin_ecs_depensier: 2685.3
47
84
  });
85
+ vi.spyOn(besoinChService, 'execute').mockReturnValue({
86
+ besoin_ch_hp: 2563,
87
+ besoin_ch_depensier_hp: 3258.6
88
+ });
48
89
  vi.spyOn(besoinFroidService, 'execute').mockReturnValue({
49
90
  besoin_fr: 896,
50
91
  besoin_fr_depensier: 1025.3
@@ -57,6 +98,17 @@ describe('Calcul des apports et besoin du logement', () => {
57
98
  apport_interne_ch: 1236.9,
58
99
  apport_interne_fr: 3345.2
59
100
  });
101
+ vi.spyOn(installationEcsService, 'execute').mockReturnThis();
102
+ vi.spyOn(perteEcsRecupService, 'execute').mockReturnValue({
103
+ pertes_distribution_ecs_recup: 354.2,
104
+ pertes_distribution_ecs_recup_depensier: 532.6,
105
+ pertes_stockage_ecs_recup: 25.9,
106
+ pertes_stockage_ecs_recup_depensier: 12.9
107
+ });
108
+ vi.spyOn(perteChRecupService, 'execute').mockReturnValue({
109
+ pertes_generateur_ch_recup: 102.2,
110
+ pertes_generateur_ch_recup_depensier: 153.4
111
+ });
60
112
 
61
113
  /** @type {Contexte} */
62
114
  const ctx = { zoneClimatique: { id: 1 }, nadeq: 2.5 };
@@ -66,6 +118,8 @@ describe('Calcul des apports et besoin du logement', () => {
66
118
  surface_sud_equivalente: 18.5,
67
119
  besoin_ecs: 1526,
68
120
  besoin_ecs_depensier: 2685.3,
121
+ besoin_ch: 2080.7000000000003,
122
+ besoin_ch_depensier: 2559.7,
69
123
  besoin_fr: 896,
70
124
  besoin_fr_depensier: 1025.3,
71
125
  apport_solaire_ch: 5236.9,
@@ -74,7 +128,13 @@ describe('Calcul des apports et besoin du logement', () => {
74
128
  apport_interne_fr: 3345.2,
75
129
  nadeq: 2.5,
76
130
  v40_ecs_journalier: 140,
77
- v40_ecs_journalier_depensier: 197.5
131
+ v40_ecs_journalier_depensier: 197.5,
132
+ pertes_distribution_ecs_recup: 354.2,
133
+ pertes_distribution_ecs_recup_depensier: 532.6,
134
+ pertes_generateur_ch_recup: 102.2,
135
+ pertes_generateur_ch_recup_depensier: 153.4,
136
+ pertes_stockage_ecs_recup: 25.9,
137
+ pertes_stockage_ecs_recup_depensier: 12.9
78
138
  });
79
139
  expect(surfaceSudEquivalenteService.execute).toHaveBeenCalledWith(ctx, logement.enveloppe);
80
140
  expect(besoinEcsService.execute).toHaveBeenCalledWith(ctx);
@@ -82,4 +142,66 @@ describe('Calcul des apports et besoin du logement', () => {
82
142
  expect(apportGratuitService.apportSolaire).toHaveBeenCalledWith(ctx, logement);
83
143
  expect(apportGratuitService.apportInterne).toHaveBeenCalledWith(ctx, logement);
84
144
  });
145
+
146
+ describe("Test d'intégration pour les besoins en chauffage", () => {
147
+ test.each(corpus)(
148
+ 'Vérification de la DI besoin_ch des besoins en chauffage pour dpe %s',
149
+ (ademeId) => {
150
+ /**
151
+ * @type {Dpe}
152
+ */
153
+ let dpeRequest = getAdemeFileJson(ademeId);
154
+ dpeRequest = normalizerService.normalize(dpeRequest);
155
+
156
+ dpeRequest.logement.donnees_de_calcul = {
157
+ apportsInterneDepensier: [],
158
+ apportsInterneCh: [],
159
+ apportsSolaire: [],
160
+ besoinChauffageHP: [],
161
+ besoinChauffageDepensierHP: []
162
+ };
163
+
164
+ /** @type {Contexte} */
165
+ const ctx = contexteBuilder.fromDpe(dpeRequest);
166
+ const apportEtBesoin = service.execute(ctx, dpeRequest.logement);
167
+
168
+ expect(
169
+ Math.abs(
170
+ apportEtBesoin.besoin_ch - dpeRequest.logement.sortie.apport_et_besoin.besoin_ch
171
+ ) / (dpeRequest.logement.sortie.apport_et_besoin.besoin_ch || 1)
172
+ ).toBeLessThan(PRECISION_PERCENT);
173
+ }
174
+ );
175
+
176
+ test.each(corpus)(
177
+ 'Vérification de la DI besoin_ch_depensier des besoins en chauffage pour dpe %s',
178
+ (ademeId) => {
179
+ /**
180
+ * @type {Dpe}
181
+ */
182
+ let dpeRequest = getAdemeFileJson(ademeId);
183
+ dpeRequest = normalizerService.normalize(dpeRequest);
184
+
185
+ dpeRequest.logement.donnees_de_calcul = {
186
+ apportsInterneDepensier: [],
187
+ apportsInterneCh: [],
188
+ apportsSolaire: [],
189
+ besoinChauffageHP: [],
190
+ besoinChauffageDepensierHP: []
191
+ };
192
+
193
+ /** @type {Contexte} */
194
+ const ctx = contexteBuilder.fromDpe(dpeRequest);
195
+
196
+ const apportEtBesoin = service.execute(ctx, dpeRequest.logement);
197
+
198
+ expect(
199
+ Math.abs(
200
+ apportEtBesoin.besoin_ch_depensier -
201
+ dpeRequest.logement.sortie.apport_et_besoin.besoin_ch_depensier
202
+ ) / (dpeRequest.logement.sortie.apport_et_besoin.besoin_ch_depensier || 1)
203
+ ).toBeLessThan(PRECISION_PERCENT);
204
+ }
205
+ );
206
+ });
85
207
  });
@@ -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