@open3cl/engine 1.0.6 → 1.0.8

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 (72) hide show
  1. package/11_nadeq.spec.js +3 -2
  2. package/16.2_production_enr.spec.js +1 -0
  3. package/3.2.1_mur.spec.js +1 -0
  4. package/3.2.2_plancher_bas.spec.js +1 -0
  5. package/3.3_baie_vitree.spec.js +1 -0
  6. package/6.1_apport_gratuit.spec.js +1 -0
  7. package/7_inertie.spec.js +4 -3
  8. package/9_chauffage.spec.js +1 -0
  9. package/9_conso_ch.spec.js +1 -0
  10. package/9_generateur_ch.spec.js +1 -0
  11. package/conso.spec.js +1 -0
  12. package/core/assets/domain/synchronize-assets.spec.js +9 -9
  13. package/core/assets/domain/synchronize-c1-tables.spec.js +4 -4
  14. package/core/assets/domain/synchronize-dpe-ges-limit-values-tables.spec.js +6 -6
  15. package/core/assets/domain/synchronize-enum-tables.spec.js +10 -10
  16. package/core/assets/domain/synchronize-solicitations-tables.spec.js +6 -6
  17. package/core/assets/domain/synchronize-valeur-tables.spec.js +14 -14
  18. package/core/file/infrastructure/adapter/file.store.spec.js +5 -4
  19. package/core/tv/infrastructure/tvs.store.spec.js +3 -2
  20. package/core/util/infrastructure/object-util.spec.js +2 -1
  21. package/core/util/logger/log-service.js +47 -0
  22. package/features/dpe/domain/models/baie-ets.model.ts +12 -0
  23. package/features/dpe/domain/models/baie-vitree.model.ts +97 -0
  24. package/features/dpe/domain/models/climatisation.model.ts +25 -0
  25. package/features/dpe/domain/models/dpe.model.ts +194 -0
  26. package/features/dpe/domain/models/ets.model.ts +19 -0
  27. package/features/dpe/domain/models/installation-chauffage.model.ts +101 -0
  28. package/features/dpe/domain/models/installation-ecs.model.ts +76 -0
  29. package/features/dpe/domain/models/mur.model.ts +37 -0
  30. package/features/dpe/domain/models/plancher-bas.model.ts +38 -0
  31. package/features/dpe/domain/models/plancher-haut.model.ts +33 -0
  32. package/features/dpe/domain/models/pont-thermique.model.ts +21 -0
  33. package/features/dpe/domain/models/porte.model.ts +31 -0
  34. package/features/dpe/domain/models/production-elec-enr.model.ts +27 -0
  35. package/features/dpe/domain/models/sortie.model.ts +178 -0
  36. package/features/dpe/domain/models/type-habitation.model.js +8 -0
  37. package/features/dpe/domain/models/type-ventilation.model.js +8 -0
  38. package/features/dpe/domain/models/ventilation.model.ts +33 -0
  39. package/features/dpe/infrastructure/baieVitreeTv.store.js +292 -0
  40. package/features/dpe/infrastructure/baieVitreeTv.store.spec.js +352 -0
  41. package/features/dpe/infrastructure/tv.store.js +356 -0
  42. package/features/dpe/infrastructure/tv.store.spec.js +607 -0
  43. package/features/engine/domain/constants.js +1 -0
  44. package/features/engine/domain/contexte.builder.js +140 -0
  45. package/features/engine/domain/contexte.builder.spec.js +152 -0
  46. package/features/engine/domain/engine.service.js +139 -0
  47. package/features/engine/domain/engine.service.spec.js +7 -0
  48. package/features/engine/domain/enveloppe/baie_vitree/deperdition-baie-vitree.service.js +292 -0
  49. package/features/engine/domain/enveloppe/baie_vitree/deperdition-baie-vitree.service.spec.js +484 -0
  50. package/features/engine/domain/enveloppe/deperdition-enveloppe.service.js +278 -0
  51. package/features/engine/domain/enveloppe/deperdition-enveloppe.service.spec.js +282 -0
  52. package/features/engine/domain/enveloppe/deperdition.service.js +101 -0
  53. package/features/engine/domain/enveloppe/mur/deperdition-mur.service.js +168 -0
  54. package/features/engine/domain/enveloppe/mur/deperdition-mur.service.spec.js +345 -0
  55. package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.js +169 -0
  56. package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.spec.js +200 -0
  57. package/features/engine/domain/enveloppe/plancher_haut/deperdition-plancher-haut.service.js +136 -0
  58. package/features/engine/domain/enveloppe/plancher_haut/deperdition-plancher-haut.service.spec.js +211 -0
  59. package/features/engine/domain/enveloppe/porte/deperdition-porte.service.js +56 -0
  60. package/features/engine/domain/enveloppe/porte/deperdition-porte.service.spec.js +116 -0
  61. package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.js +338 -0
  62. package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.spec.js +442 -0
  63. package/features/engine/domain/models/contexte.model.ts +10 -0
  64. package/features/engine/domain/models/deperdition.model.ts +8 -0
  65. package/features/normalizer/domain/dpe-normalizer.service.js +24 -0
  66. package/features/normalizer/domain/dpe-normalizer.service.spec.js +3 -0
  67. package/ficheTechnique.spec.js +4 -3
  68. package/output.js +1 -0
  69. package/package.json +9 -8
  70. package/tv-v2.js +79121 -0
  71. package/utils.js +2 -2
  72. package/utils.spec.js +4 -3
@@ -0,0 +1,200 @@
1
+ import corpus from '../../../../../../test/corpus-sano.json';
2
+ import { getAdemeFileJson } from '../../../../../../test/test-helpers.js';
3
+ import { ContexteBuilder } from '../../contexte.builder.js';
4
+ import { DpeNormalizerService } from '../../../../normalizer/domain/dpe-normalizer.service.js';
5
+ import { DeperditionPlancherBasService } from './deperdition-plancher-bas.service.js';
6
+ import { TvStore } from '../../../../dpe/infrastructure/tv.store.js';
7
+ import { beforeEach, describe, expect, test } from 'vitest';
8
+
9
+ /** @type {DeperditionPlancherBasService} **/
10
+ let service;
11
+
12
+ /** @type {DpeNormalizerService} **/
13
+ let normalizerService;
14
+
15
+ /** @type {ContexteBuilder} **/
16
+ let contexteBuilder;
17
+
18
+ describe('Calcul de déperdition des planchers bas', () => {
19
+ beforeEach(() => {
20
+ service = new DeperditionPlancherBasService(new TvStore());
21
+ normalizerService = new DpeNormalizerService();
22
+ contexteBuilder = new ContexteBuilder();
23
+ });
24
+
25
+ describe('Determination de upb, upb0 et upb_final', () => {
26
+ test('Plancher 1 - Dalle béton non isolée donnant sur un terre-plein', () => {
27
+ /** @type {Contexte} */
28
+ const ctx = {
29
+ effetJoule: false,
30
+ enumPeriodeConstructionId: '6',
31
+ zoneClimatiqueId: '3'
32
+ };
33
+ /** @type {PlancherBasDE} */
34
+ const de = {
35
+ enum_type_adjacence_id: '5',
36
+ enum_type_plancher_bas_id: '9',
37
+ enum_methode_saisie_u0_id: '2',
38
+ enum_type_isolation_id: '2',
39
+ enum_methode_saisie_u_id: '1',
40
+ calcul_ue: 1,
41
+ perimetre_ue: 19.2,
42
+ ue: 0.49684211
43
+ };
44
+
45
+ const di = service.execute(ctx, de, []);
46
+ expect(di.upb).toBeCloseTo(2);
47
+ expect(di.upb0).toBeCloseTo(2);
48
+ expect(di.upb_final).toBeCloseTo(0.49684211);
49
+ });
50
+
51
+ test('Plancher 1 - Plancher lourd type entrevous terre-cuite, poutrelles béton donnant sur un sous-sol non chauffé avec isolation intrinsèque ou en sous-face (5 cm)', () => {
52
+ /** @type {Contexte} */
53
+ const ctx = {
54
+ effetJoule: false,
55
+ enumPeriodeConstructionId: '6',
56
+ zoneClimatiqueId: '3'
57
+ };
58
+ /** @type {PlancherBasDE} */
59
+ const de = {
60
+ enum_type_adjacence_id: '6',
61
+ enum_type_plancher_bas_id: '11',
62
+ enum_methode_saisie_u0_id: '2',
63
+ enum_type_isolation_id: '4',
64
+ epaisseur_isolation: 5,
65
+ enum_methode_saisie_u_id: '3',
66
+ calcul_ue: 0,
67
+ perimetre_ue: 27,
68
+ surface_ue: 42.08,
69
+ ue: 0.37117494
70
+ };
71
+
72
+ const di = service.execute(ctx, de, [{ donnee_entree: de }]);
73
+ expect(di.upb).toBeCloseTo(0.59154929577464788);
74
+ expect(di.upb0).toBeCloseTo(2);
75
+ expect(di.upb_final).toBeCloseTo(0.37117494);
76
+ });
77
+
78
+ test('Plancher 2 - Plancher avec ou sans remplissage non isolé donnant sur un garage', () => {
79
+ /** @type {Contexte} */
80
+ const ctx = {
81
+ effetJoule: false,
82
+ enumPeriodeConstructionId: '6',
83
+ zoneClimatiqueId: '3'
84
+ };
85
+ /** @type {PlancherBasDE} */
86
+ const de = {
87
+ enum_type_adjacence_id: '21',
88
+ enum_type_plancher_bas_id: '2',
89
+ enum_methode_saisie_u0_id: '2',
90
+ enum_type_isolation_id: '2',
91
+ enum_methode_saisie_u_id: '1',
92
+ calcul_ue: 0
93
+ };
94
+
95
+ const di = service.execute(ctx, de, []);
96
+ expect(di.upb).toBeCloseTo(1.45);
97
+ expect(di.upb0).toBeCloseTo(1.45);
98
+ expect(di.upb_final).toBeCloseTo(1.45);
99
+ });
100
+
101
+ test('Test de plancher avec upb0 saisie', () => {
102
+ /** @type {Contexte} */
103
+ const ctx = {
104
+ effetJoule: false,
105
+ enumPeriodeConstructionId: '6',
106
+ zoneClimatiqueId: '3'
107
+ };
108
+ /** @type {PlancherBasDE} */
109
+ const de = {
110
+ enum_type_adjacence_id: '21',
111
+ enum_type_plancher_bas_id: '2',
112
+ enum_methode_saisie_u0_id: '9',
113
+ enum_type_isolation_id: '2',
114
+ enum_methode_saisie_u_id: '1',
115
+ calcul_ue: 0,
116
+ upb0_saisi: 3.2
117
+ };
118
+
119
+ const di = service.execute(ctx, de, []);
120
+ expect(di.upb).toBeCloseTo(2);
121
+ expect(di.upb0).toBeCloseTo(3.2);
122
+ expect(di.upb_final).toBeCloseTo(2);
123
+ });
124
+
125
+ test('Test de plancher avec type de paroi inconnue', () => {
126
+ /** @type {Contexte} */
127
+ const ctx = {
128
+ effetJoule: false,
129
+ enumPeriodeConstructionId: '6',
130
+ zoneClimatiqueId: '3'
131
+ };
132
+ /** @type {PlancherBasDE} */
133
+ const de = {
134
+ enum_type_adjacence_id: '21',
135
+ enum_type_plancher_bas_id: '1',
136
+ enum_methode_saisie_u0_id: '1',
137
+ enum_type_isolation_id: '2',
138
+ enum_methode_saisie_u_id: '1',
139
+ calcul_ue: 0
140
+ };
141
+
142
+ const di = service.execute(ctx, de, []);
143
+ expect(di.upb).toBeCloseTo(2);
144
+ expect(di.upb0).toBeCloseTo(2);
145
+ expect(di.upb_final).toBeCloseTo(2);
146
+ });
147
+
148
+ test('Test de plancher avec upb directement saisie', () => {
149
+ /** @type {Contexte} */
150
+ const ctx = {
151
+ effetJoule: false,
152
+ enumPeriodeConstructionId: '6',
153
+ zoneClimatiqueId: '3'
154
+ };
155
+ /** @type {PlancherBasDE} */
156
+ const de = {
157
+ enum_type_adjacence_id: '21',
158
+ enum_type_plancher_bas_id: '1',
159
+ enum_methode_saisie_u0_id: '5',
160
+ enum_type_isolation_id: '2',
161
+ enum_methode_saisie_u_id: '9',
162
+ calcul_ue: 0,
163
+ upb_saisi: 1.25
164
+ };
165
+
166
+ const di = service.execute(ctx, de, []);
167
+ expect(di.upb).toBeCloseTo(1.25);
168
+ expect(di.upb0).toBeUndefined();
169
+ expect(di.upb_final).toBeCloseTo(1.25);
170
+ });
171
+ });
172
+
173
+ describe("Test d'intégration de plancher bas", () => {
174
+ test.each(corpus)('vérification des DI des pb pour dpe %s', (ademeId) => {
175
+ /**
176
+ * @type {Dpe}
177
+ */
178
+ let dpeRequest = getAdemeFileJson(ademeId);
179
+ dpeRequest = normalizerService.normalize(dpeRequest);
180
+
181
+ /** @type {Contexte} */
182
+ const ctx = contexteBuilder.fromDpe(dpeRequest);
183
+
184
+ /** @type {PlancherBas[]} */
185
+ const pbs = dpeRequest.logement.enveloppe.plancher_bas_collection?.plancher_bas || [];
186
+
187
+ pbs.forEach((pb) => {
188
+ const di = service.execute(ctx, pb.donnee_entree, pbs);
189
+
190
+ if (pb.donnee_intermediaire) {
191
+ expect(di.upb0).toBeCloseTo(pb.donnee_intermediaire.upb0, 2);
192
+ } else {
193
+ expect(di.upb0).toBeUndefined();
194
+ }
195
+ expect(di.upb_final).toBeCloseTo(pb.donnee_intermediaire.upb_final, 2);
196
+ expect(di.b).toBeCloseTo(pb.donnee_intermediaire.b, 2);
197
+ });
198
+ });
199
+ });
200
+ });
@@ -0,0 +1,136 @@
1
+ import { inject } from 'dioma';
2
+ import { PRECISION } from '../../constants.js';
3
+ import { DeperditionService } from '../deperdition.service.js';
4
+ import { TvStore } from '../../../../dpe/infrastructure/tv.store.js';
5
+
6
+ /**
7
+ * Calcul des déperditions de l’enveloppe GV
8
+ * Chapitre 3.2.3 Calcul des planchers haut
9
+ *
10
+ * Méthode de calcul 3CL-DPE 2021
11
+ * Octobre 2021
12
+ * @see consolide_anne…arrete_du_31_03_2021_relatif_aux_methodes_et_procedures_applicables.pdf
13
+ */
14
+ export class DeperditionPlancherHautService extends DeperditionService {
15
+ /**
16
+ * @param tvStore {TvStore}
17
+ */
18
+ constructor(tvStore = inject(TvStore)) {
19
+ super(tvStore);
20
+ }
21
+
22
+ /**
23
+ * @param ctx {Contexte}
24
+ * @param phDE {PlancherHautDE}
25
+ * @return {PlancherHautDI}
26
+ */
27
+ execute(ctx, phDE) {
28
+ const uph0 = this.#uph0(phDE);
29
+ const uph = this.#uph(phDE, uph0, ctx);
30
+ const b = this.b({
31
+ enumTypeAdjacenceId: phDE.enum_type_adjacence_id,
32
+ surfaceAiu: phDE.surface_aiu,
33
+ surfaceAue: phDE.surface_aue,
34
+ enumCfgIsolationLncId: phDE.enum_cfg_isolation_lnc_id,
35
+ zoneClimatiqueId: ctx.zoneClimatiqueId
36
+ });
37
+
38
+ /** @type {PlancherHautDI} */
39
+ return { uph0, uph, b };
40
+ }
41
+
42
+ /**
43
+ * @param phDE {PlancherHautDE}
44
+ * @param uph0 {number}
45
+ * @param ctx {Contexte}
46
+ * @return {number|undefined}
47
+ */
48
+ #uph(phDE, uph0, ctx) {
49
+ // On determine uph_nu (soit uph0 soit 2 comme valeur minimale forfaitaire)
50
+ const uphNu = Math.min(uph0, 2.5);
51
+
52
+ const enumPeriodeIsolationId = this.getEnumPeriodeIsolationId(
53
+ phDE.enum_periode_isolation_id,
54
+ ctx
55
+ );
56
+
57
+ // Selon l'isolation, on applique un calcul au uph nu pour simuler son isolation
58
+ let uph;
59
+ switch (phDE.enum_methode_saisie_u_id) {
60
+ case '1':
61
+ // non isolé
62
+ uph = uphNu;
63
+ break;
64
+ case '2': // isolation inconnue (table forfaitaire)
65
+ case '7': // année d'isolation différente de l'année de construction
66
+ case '8': // année de construction saisie
67
+ uph = Math.min(
68
+ uphNu,
69
+ this.tvStore.getUph(
70
+ enumPeriodeIsolationId,
71
+ this.#getTypeAdjacence(phDE),
72
+ ctx.zoneClimatiqueId,
73
+ ctx.effetJoule
74
+ )
75
+ );
76
+ break;
77
+ case '3': // epaisseur isolation saisie justifiée par mesure ou observation
78
+ case '4': // epaisseur isolation saisie (en cm)
79
+ uph = 1 / (1 / uphNu + (phDE.epaisseur_isolation * 0.01) / 0.04);
80
+ break;
81
+ case '5':
82
+ case '6': // resistance isolation saisie
83
+ uph = 1 / (1 / uphNu + phDE.resistance_isolation);
84
+ break;
85
+ default: // saisie direct de la valeur de u
86
+ uph = phDE.uph_saisi;
87
+ break;
88
+ }
89
+
90
+ return Math.round(parseFloat(uph) * PRECISION) / PRECISION;
91
+ }
92
+
93
+ /**
94
+ * @param phDE {PlancherHautDE}
95
+ * @return {number|undefined}
96
+ */
97
+ #uph0(phDE) {
98
+ let uph0;
99
+ switch (phDE.enum_methode_saisie_u0_id) {
100
+ case '1':
101
+ case '2':
102
+ // type de paroi inconnu (valeur par défaut)
103
+ // déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire
104
+ uph0 = this.tvStore.getUph0(phDE.enum_type_plancher_haut_id);
105
+ break;
106
+ case '5':
107
+ // u0 non saisi car le u est saisi connu et justifié
108
+ return;
109
+ default:
110
+ // Valeur saisie
111
+ return phDE.uph0_saisi;
112
+ }
113
+
114
+ return uph0;
115
+ }
116
+
117
+ /**
118
+ * Retourne le type d'adjacence à utiliser combles ou terrasse
119
+ * @see Méthode de calcul 3CL-DPE 2021 (cotobre 2021) chapitre 3.2.3.1
120
+ * "Lorsque le local au-dessus du logement est un local non chauffé, ou un local autre que d’habitation., Uph_tab est pris dans la catégorie « Terrasse »."
121
+ * @param phDE {PlancherHautDE}
122
+ * @return {'combles'|'terasse'}
123
+ */
124
+ #getTypeAdjacence(phDE) {
125
+ switch (phDE.enum_type_adjacence_id) {
126
+ case '1':
127
+ // extérieur - type de ph != 'combles aménagés sous rampant'
128
+ return phDE.enum_type_plancher_haut_id === '12' ? 'combles' : 'terrasse';
129
+ case '7':
130
+ // locaux non chauffés non accessible
131
+ return 'terrasse';
132
+ default:
133
+ return 'combles';
134
+ }
135
+ }
136
+ }
@@ -0,0 +1,211 @@
1
+ import { getAdemeFileJson } from '../../../../../../test/test-helpers.js';
2
+ import { ContexteBuilder } from '../../contexte.builder.js';
3
+ import { DpeNormalizerService } from '../../../../normalizer/domain/dpe-normalizer.service.js';
4
+ import { DeperditionPlancherHautService } from './deperdition-plancher-haut.service.js';
5
+ import corpus from '../../../../../../test/corpus-sano.json';
6
+ import { TvStore } from '../../../../dpe/infrastructure/tv.store.js';
7
+ import { beforeEach, describe, expect, test } from 'vitest';
8
+
9
+ /** @type {DeperditionPlancherHautService} **/
10
+ let service;
11
+
12
+ /** @type {DpeNormalizerService} **/
13
+ let normalizerService;
14
+
15
+ /** @type {ContexteBuilder} **/
16
+ let contexteBuilder;
17
+
18
+ describe('Calcul de déperdition des planchers haut', () => {
19
+ beforeEach(() => {
20
+ service = new DeperditionPlancherHautService(new TvStore());
21
+ normalizerService = new DpeNormalizerService();
22
+ contexteBuilder = new ContexteBuilder();
23
+ });
24
+
25
+ describe('Determination de uph, uph0 et uph_final', () => {
26
+ test('(2187E1039187C) Plafond 2 : plafond bois sous solives bois', () => {
27
+ /** @type {Contexte} */
28
+ const ctx = {
29
+ effetJoule: false,
30
+ enumPeriodeConstructionId: '2',
31
+ zoneClimatiqueId: '3'
32
+ };
33
+ /** @type {PlancherHautDE} */
34
+ const de = {
35
+ enum_type_adjacence_id: '1',
36
+ enum_type_plancher_haut_id: '10',
37
+ enum_methode_saisie_u0_id: '2',
38
+ enum_type_isolation_id: '9',
39
+ enum_periode_isolation_id: '3',
40
+ enum_methode_saisie_u_id: '8'
41
+ };
42
+
43
+ const di = service.execute(ctx, de);
44
+ expect(di.uph0).toBeCloseTo(2.3);
45
+ expect(di.uph).toBeCloseTo(0.75);
46
+ });
47
+
48
+ test("(2287E0224445X) Plafond 2 - Plafond structure inconnu (sous combles perdus) donnant sur l'extérieur (combles aménagés) avec isolation intérieure (réalisée entre 1989 et 2000)", () => {
49
+ /** @type {Contexte} */
50
+ const ctx = {
51
+ effetJoule: false,
52
+ enumPeriodeConstructionId: '1',
53
+ zoneClimatiqueId: '3'
54
+ };
55
+ /** @type {PlancherHautDE} */
56
+ const de = {
57
+ enum_type_adjacence_id: '1', // extérieur
58
+ enum_type_plancher_haut_id: '1', // inconnu
59
+ enum_methode_saisie_u0_id: '2', // déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire
60
+ enum_type_isolation_id: '3', // iti
61
+ enum_periode_isolation_id: '6', // 1989-2000
62
+ enum_methode_saisie_u_id: '7' // année d'isolation différente de l'année de construction saisie justifiée (table forfaitaire)
63
+ };
64
+
65
+ const di = service.execute(ctx, de);
66
+ expect(di.uph0).toBeCloseTo(2.5);
67
+ expect(di.uph).toBeCloseTo(0.4); // Dans le DPE d'origine, uph (0.25) est pris dans la partie "comble" et pas "terrasse" alors que le local donne sur l'extérieur
68
+ expect(di.b).toBeCloseTo(1);
69
+ });
70
+
71
+ test("(2387E0562437Q) Plafond 2 - Plafond structure inconnu (sous combles perdus) donnant sur l'extérieur (combles aménagés) avec isolation intérieure (réalisée entre 1989 et 2000)", () => {
72
+ /** @type {Contexte} */
73
+ const ctx = {
74
+ effetJoule: true,
75
+ enumPeriodeConstructionId: '1',
76
+ zoneClimatiqueId: '3'
77
+ };
78
+ /** @type {PlancherHautDE} */
79
+ const de = {
80
+ enum_type_adjacence_id: '1', // extérieur
81
+ enum_type_plancher_haut_id: '1', // inconnu
82
+ enum_methode_saisie_u0_id: '2', // déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire
83
+ enum_type_isolation_id: '3', // iti
84
+ enum_periode_isolation_id: '8', // 2006-2012
85
+ enum_methode_saisie_u_id: '7' // année d'isolation différente de l'année de construction saisie justifiée (table forfaitaire)
86
+ };
87
+
88
+ const di = service.execute(ctx, de);
89
+ expect(di.uph0).toBeCloseTo(2.5);
90
+ expect(di.uph).toBeCloseTo(0.27); // Dans le DPE d'origine, uph (0.2) est pris dans la partie "comble" et pas "terrasse" alors que le local donne sur l'extérieur
91
+ expect(di.b).toBeCloseTo(1);
92
+ });
93
+
94
+ test("(2287E0272588O) Plafond - Combles aménagés sous rampants donnant sur l'extérieur (combles aménagés) avec isolation intérieure (réalisée entre 1975 et 1977)", () => {
95
+ /** @type {Contexte} */
96
+ const ctx = {
97
+ effetJoule: true,
98
+ enumPeriodeConstructionId: '1',
99
+ zoneClimatiqueId: '3'
100
+ };
101
+ /** @type {PlancherHautDE} */
102
+ const de = {
103
+ enum_type_adjacence_id: '1', // extérieur
104
+ enum_type_plancher_haut_id: '12', // combles aménagés sous rampant
105
+ enum_methode_saisie_u0_id: '2', // déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire
106
+ enum_type_isolation_id: '3', // iti
107
+ enum_periode_isolation_id: '3', // 1975-1977
108
+ enum_methode_saisie_u_id: '7' // année d'isolation différente de l'année de construction saisie justifiée (table forfaitaire)
109
+ };
110
+
111
+ const di = service.execute(ctx, de);
112
+ expect(di.uph0).toBeCloseTo(2.5);
113
+ expect(di.uph).toBeCloseTo(0.5);
114
+ expect(di.b).toBeCloseTo(1);
115
+ });
116
+
117
+ test('Test de plancher avec uph directement saisie', () => {
118
+ /** @type {Contexte} */
119
+ const ctx = {
120
+ effetJoule: false,
121
+ enumPeriodeConstructionId: '6',
122
+ zoneClimatiqueId: '3'
123
+ };
124
+ /** @type {PlancherHautDE} */
125
+ const de = {
126
+ enum_type_adjacence_id: '21',
127
+ enum_type_plancher_haut_id: '1',
128
+ enum_methode_saisie_u0_id: '5',
129
+ enum_type_isolation_id: '2',
130
+ enum_methode_saisie_u_id: '9',
131
+ uph_saisi: 1.25
132
+ };
133
+
134
+ const di = service.execute(ctx, de);
135
+ expect(di.uph).toBeCloseTo(1.25);
136
+ expect(di.uph0).toBeUndefined();
137
+ });
138
+
139
+ test('Test de plancher avec uph0 directement saisie', () => {
140
+ /** @type {Contexte} */
141
+ const ctx = {
142
+ effetJoule: false,
143
+ enumPeriodeConstructionId: '6',
144
+ zoneClimatiqueId: '3'
145
+ };
146
+ /** @type {PlancherHautDE} */
147
+ const de = {
148
+ enum_type_adjacence_id: '21',
149
+ enum_type_plancher_haut_id: '1',
150
+ enum_methode_saisie_u0_id: '3',
151
+ enum_type_isolation_id: '2',
152
+ enum_periode_isolation_id: '3',
153
+ enum_methode_saisie_u_id: '7',
154
+ uph0_saisi: 1.25
155
+ };
156
+
157
+ const di = service.execute(ctx, de);
158
+ expect(di.uph0).toBeCloseTo(1.25);
159
+ expect(di.uph).toBeCloseTo(0.5);
160
+ });
161
+
162
+ test('Test de plancher avec uph0 type de paroi inconnu (valeur par défaut)', () => {
163
+ /** @type {Contexte} */
164
+ const ctx = {
165
+ effetJoule: false,
166
+ enumPeriodeConstructionId: '6',
167
+ zoneClimatiqueId: '3'
168
+ };
169
+ /** @type {PlancherHautDE} */
170
+ const de = {
171
+ enum_type_adjacence_id: '1',
172
+ enum_type_plancher_haut_id: '1',
173
+ enum_methode_saisie_u0_id: '1',
174
+ enum_type_isolation_id: '1',
175
+ enum_methode_saisie_u_id: '7'
176
+ };
177
+
178
+ const di = service.execute(ctx, de);
179
+ expect(di.uph0).toBeCloseTo(2.5);
180
+ expect(di.uph).toBeCloseTo(0.4);
181
+ });
182
+ });
183
+
184
+ describe("Test d'intégration de plancher haut", () => {
185
+ test.each(corpus)('vérification des DI des ph pour dpe %s', (ademeId) => {
186
+ /**
187
+ * @type {Dpe}
188
+ */
189
+ let dpeRequest = getAdemeFileJson(ademeId);
190
+ dpeRequest = normalizerService.normalize(dpeRequest);
191
+
192
+ /** @type {Contexte} */
193
+ const ctx = contexteBuilder.fromDpe(dpeRequest);
194
+
195
+ /** @type {PlancherHaut[]} */
196
+ const phs = dpeRequest.logement.enveloppe.plancher_haut_collection?.plancher_haut || [];
197
+
198
+ phs.forEach((ph) => {
199
+ const di = service.execute(ctx, ph.donnee_entree);
200
+
201
+ if (ph.donnee_intermediaire) {
202
+ expect(di.uph0).toBeCloseTo(ph.donnee_intermediaire.uph0, 2);
203
+ } else {
204
+ expect(di.uph0).toBeUndefined();
205
+ }
206
+ expect(di.uph).toBeCloseTo(ph.donnee_intermediaire.uph, 2);
207
+ expect(di.b).toBeCloseTo(ph.donnee_intermediaire.b, 2);
208
+ });
209
+ });
210
+ });
211
+ });
@@ -0,0 +1,56 @@
1
+ import { inject } from 'dioma';
2
+ import { DeperditionService } from '../deperdition.service.js';
3
+ import { TvStore } from '../../../../dpe/infrastructure/tv.store.js';
4
+
5
+ /**
6
+ * Calcul des déperditions de l’enveloppe GV
7
+ * Chapitre 3.3.4 Coefficients U des portes
8
+ *
9
+ * Méthode de calcul 3CL-DPE 2021
10
+ * Octobre 2021
11
+ * @see consolide_anne…arrete_du_31_03_2021_relatif_aux_methodes_et_procedures_applicables.pdf
12
+ *
13
+ * Si le coefficient U des portes est connu et justifié, le saisir directement. Sinon, prendre les valeurs tabulées
14
+ */
15
+ export class DeperditionPorteService extends DeperditionService {
16
+ /**
17
+ * @param tvStore {TvStore}
18
+ */
19
+ constructor(tvStore = inject(TvStore)) {
20
+ super(tvStore);
21
+ }
22
+
23
+ /**
24
+ * @param ctx {Contexte}
25
+ * @param porteDE {PorteDE}
26
+ * @return {PorteDI}
27
+ */
28
+ execute(ctx, porteDE) {
29
+ /** @type {PorteDI} */
30
+ const di = {
31
+ uporte: undefined,
32
+ b: undefined
33
+ };
34
+
35
+ // @todo : Une porte vitrée avec plus de 60% de vitrage est traitée comme une porte-fenêtre avec soubassement.
36
+
37
+ if (['2', '3'].includes(porteDE.enum_methode_saisie_uporte_id)) {
38
+ // valeur justifiée saisie à partir des documents justificatifs autorisés
39
+ // saisie direct U depuis RSET/RSEE( etude RT2012/RE2020)
40
+ di.uporte = porteDE.uporte_saisi;
41
+ } else {
42
+ // valeur forfaitaire
43
+ di.uporte = this.tvStore.getUPorte(porteDE.enum_type_porte_id);
44
+ }
45
+
46
+ di.b = this.b({
47
+ enumTypeAdjacenceId: porteDE.enum_type_adjacence_id,
48
+ surfaceAiu: porteDE.surface_aiu,
49
+ surfaceAue: porteDE.surface_aue,
50
+ enumCfgIsolationLncId: porteDE.enum_cfg_isolation_lnc_id,
51
+ zoneClimatiqueId: ctx.zoneClimatiqueId
52
+ });
53
+
54
+ return di;
55
+ }
56
+ }