@open3cl/engine 1.6.0 → 1.6.2

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 (108) hide show
  1. package/10_besoin_fr.spec.js +122 -0
  2. package/10_clim.spec.js +116 -0
  3. package/11_besoin_ecs.spec.js +69 -0
  4. package/11_ecs.spec.js +282 -0
  5. package/11_nadeq.spec.js +37 -0
  6. package/12.4_pac.spec.js +131 -0
  7. package/13.2_generateur_combustion.js +2 -0
  8. package/13.2_generateur_combustion.spec.js +537 -0
  9. package/13.2_generateur_combustion_bouilleur.js +4 -1
  10. package/13.2_generateur_combustion_bouilleur.spec.js +74 -0
  11. package/13.2_generateur_combustion_ch.spec.js +323 -0
  12. package/13.2_generateur_combustion_chaudiere.spec.js +85 -0
  13. package/13.2_generateur_pac.spec.js +64 -0
  14. package/13_rendement_distribution_ecs.spec.js +38 -0
  15. package/14_generateur_ecs.js +4 -4
  16. package/14_generateur_ecs.spec.js +745 -0
  17. package/15_conso_aux.spec.js +225 -0
  18. package/16.2_production_enr.spec.js +188 -0
  19. package/16_conso_eclairage.spec.js +62 -0
  20. package/2021_04_13_confort_ete.spec.js +159 -0
  21. package/2021_04_13_qualite_isolation.js +1 -1
  22. package/2021_04_13_qualite_isolation.spec.js +286 -0
  23. package/3.1_b.js +1 -1
  24. package/3.1_b.spec.js +367 -0
  25. package/3.2.1_mur.spec.js +812 -39
  26. package/3.2.2_plancher_bas.spec.js +643 -78
  27. package/3.2.3_plancher_haut.spec.js +452 -0
  28. package/3.3.1.4_porte.spec.js +114 -0
  29. package/3.3_baie_vitree.spec.js +449 -295
  30. package/3.4_pont_thermique.spec.js +890 -0
  31. package/3_deperdition.spec.js +397 -0
  32. package/4_ventilation.js +1 -1
  33. package/4_ventilation.spec.js +406 -0
  34. package/5_conso_ventilation.spec.js +186 -0
  35. package/6.2_surface_sud_equivalente.spec.js +138 -0
  36. package/8_intermittence.spec.js +33 -0
  37. package/9_besoin_ch.js +2 -2
  38. package/9_besoin_ch.spec.js +381 -0
  39. package/9_chauffage.js +4 -0
  40. package/9_chauffage.spec.js +478 -92
  41. package/9_conso_ch.spec.js +502 -248
  42. package/9_emetteur_ch.spec.js +339 -0
  43. package/9_generateur_ch.spec.js +467 -81
  44. package/README.md +18 -9
  45. package/apport_et_besoin.spec.js +126 -0
  46. package/conso.js +1 -1
  47. package/conso.spec.js +471 -82
  48. package/core/assets/domain/add-additionnal-ue-values-tables.spec.js +53 -0
  49. package/core/assets/domain/synchronize-solicitations-tables.spec.js +25 -0
  50. package/core/assets/domain/synchronize-valeur-tables.spec.js +48 -0
  51. package/core/conf/infrastructure/application.config.spec.js +35 -0
  52. package/core/file/infrastructure/adapter/csv-parser.store.spec.js +31 -13
  53. package/core/file/infrastructure/adapter/file.store.spec.js +148 -12
  54. package/core/tv/infrastructure/assets/additional-ue-values.spec.js +36 -0
  55. package/core/util/infrastructure/object-util.spec.js +20 -0
  56. package/core/util/logger/log-service.js +16 -7
  57. package/core/util/logger/log-service.spec.js +101 -0
  58. package/dpe-sanitizer.service.spec.js +138 -0
  59. package/engine.js +7 -5
  60. package/engine.spec.js +854 -0
  61. package/enums.spec.js +46 -0
  62. package/features/dpe/domain/models/type-generateur.model.js +1 -2
  63. package/features/dpe/domain/models/type-generateur.model.spec.js +25 -0
  64. package/features/dpe/domain/models/type-habitation.model.spec.js +41 -0
  65. package/features/dpe/domain/models/type-stockage.model.spec.js +18 -0
  66. package/features/dpe/domain/models/type-ventilation.model.spec.js +19 -0
  67. package/features/dpe/infrastructure/ch/chTv.store.spec.js +4 -4
  68. package/features/dpe/infrastructure/enveloppe/baieVitreeTv.store.js +14 -2
  69. package/features/dpe/infrastructure/enveloppe/baieVitreeTv.store.spec.js +16 -0
  70. package/features/dpe/infrastructure/tv.store.js +28 -9
  71. package/features/dpe/infrastructure/tv.store.spec.js +44 -0
  72. package/features/engine/domain/apport_et_besoin/apport-et-besoin.service.spec.js +2 -1
  73. package/features/engine/domain/apport_et_besoin/ch/besoin-ch.service.spec.js +18 -0
  74. package/features/engine/domain/apport_et_besoin/ch/perte-ch-recup.service.spec.js +33 -1
  75. package/features/engine/domain/apport_et_besoin/ecs/besoin-ecs.service.spec.js +21 -1
  76. package/features/engine/domain/apport_et_besoin/ecs/perte-ecs-recup.service.spec.js +41 -1
  77. package/features/engine/domain/apport_et_besoin/froid/besoin-froid.service.spec.js +44 -1
  78. package/features/engine/domain/apport_et_besoin/surface-sud-equivalente.service.spec.js +2 -1
  79. package/features/engine/domain/ch/generateur-ch.service.js +2 -0
  80. package/features/engine/domain/ch/generateur-ch.service.spec.js +117 -2
  81. package/features/engine/domain/ch/installation-ch.service.spec.js +14 -0
  82. package/features/engine/domain/conso/froid/conso-froid.service.spec.js +82 -1
  83. package/features/engine/domain/constants.spec.js +14 -0
  84. package/features/engine/domain/contexte.builder.spec.js +21 -0
  85. package/features/engine/domain/ecs/generateur-ecs.service.spec.js +11 -0
  86. package/features/engine/domain/ecs/installation-ecs.service.spec.js +55 -2
  87. package/features/engine/domain/engine.service.spec.js +227 -4
  88. package/features/engine/domain/enveloppe/baie_vitree/deperdition-baie-vitree.service.spec.js +124 -1
  89. package/features/engine/domain/enveloppe/deperdition-enveloppe.service.spec.js +270 -2
  90. package/features/engine/domain/enveloppe/deperdition.service.spec.js +141 -0
  91. package/features/engine/domain/enveloppe/espace_tampon/espace-tampon.service.spec.js +2 -1
  92. package/features/engine/domain/enveloppe/mur/deperdition-mur.service.spec.js +117 -1
  93. package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.spec.js +134 -1
  94. package/features/engine/domain/enveloppe/plancher_haut/deperdition-plancher-haut.service.spec.js +126 -1
  95. package/features/engine/domain/enveloppe/pont_thermique/deperdition-pont-thermique.service.spec.js +18 -1
  96. package/features/engine/domain/enveloppe/porte/deperdition-porte.service.spec.js +2 -1
  97. package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.spec.js +67 -1
  98. package/features/normalizer/domain/dpe-normalizer.service.js +1 -1
  99. package/features/normalizer/domain/dpe-normalizer.service.spec.js +53 -2
  100. package/ficheTechnique.spec.js +26 -0
  101. package/index.spec.js +54 -0
  102. package/package.json +5 -2
  103. package/preprocessor/index.js +20 -0
  104. package/preprocessor/index.spec.js +15 -0
  105. package/tv.spec.js +38 -0
  106. package/utils.js +43 -1
  107. package/utils.spec.js +518 -2
  108. package/output.js +0 -1
@@ -0,0 +1,339 @@
1
+ import { beforeEach, describe, expect, test, vi } from 'vitest';
2
+
3
+ /**
4
+ * Dépendances mockées pour isoler `9_emetteur_ch.js` :
5
+ * - `tv` : accès générique aux tables (rendements d'émission / de régulation /
6
+ * intermittence). On pilote la ligne retournée selon la table interrogée ;
7
+ * - `bug_for_bug_compat` : désactivé pour rester sur le comportement nominal ;
8
+ * - `TvsStore` : store des rendements de distribution, remplacé par une classe
9
+ * factice dont on contrôle les méthodes via `vi.hoisted`.
10
+ */
11
+ const { getRendementDistributionCh, getRendementDistributionChById, utilState } = vi.hoisted(
12
+ () => ({
13
+ getRendementDistributionCh: vi.fn(),
14
+ getRendementDistributionChById: vi.fn(),
15
+ utilState: { bug: false }
16
+ })
17
+ );
18
+
19
+ vi.mock('./utils.js', () => ({
20
+ tv: vi.fn(),
21
+ get bug_for_bug_compat() {
22
+ return utilState.bug;
23
+ }
24
+ }));
25
+
26
+ vi.mock('./core/tv/infrastructure/tvs.store.js', () => ({
27
+ TvsStore: vi.fn(() => ({
28
+ getRendementDistributionCh,
29
+ getRendementDistributionChById
30
+ }))
31
+ }));
32
+
33
+ const { rendement_emission, calc_emetteur_ch } = await import('./9_emetteur_ch.js');
34
+ const { tv } = await import('./utils.js');
35
+
36
+ beforeEach(() => {
37
+ vi.mocked(tv).mockReset();
38
+ getRendementDistributionCh.mockReset();
39
+ getRendementDistributionChById.mockReset();
40
+ utilState.bug = false;
41
+ });
42
+
43
+ /**
44
+ * 9. Rendements des émetteurs de chauffage
45
+ * @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §9
46
+ */
47
+ describe('rendement_emission - produit des rendements', () => {
48
+ const emetteur = {
49
+ donnee_intermediaire: {
50
+ rendement_emission: 0.95,
51
+ rendement_distribution: 0.9,
52
+ rendement_regulation: 0.9
53
+ }
54
+ };
55
+
56
+ test('rendement global = rg * re * rd * rr (rg = 1 par défaut)', () => {
57
+ expect(rendement_emission(emetteur)).toBeCloseTo(0.7695, 12);
58
+ });
59
+
60
+ test('le rendement de génération (rg) est appliqué en facteur', () => {
61
+ expect(rendement_emission(emetteur, 0.8)).toBeCloseTo(0.6156, 12);
62
+ });
63
+ });
64
+
65
+ describe('calc_emetteur_ch - renseignement des données intermédiaires', () => {
66
+ /** Pilote la fonction `tv` selon la table interrogée. */
67
+ function stubTv() {
68
+ vi.mocked(tv).mockImplementation((table) => {
69
+ if (table === 'rendement_emission') {
70
+ return { re: '0.95', tv_rendement_emission_id: '11' };
71
+ }
72
+ if (table === 'rendement_regulation') {
73
+ return { rr: '0.99', tv_rendement_regulation_id: '22' };
74
+ }
75
+ if (table === 'intermittence') {
76
+ return { i0: '0.85', tv_intermittence_id: '33' };
77
+ }
78
+ return null;
79
+ });
80
+ }
81
+
82
+ test("agrège les rendements (distribution, émission, régulation) et l'intermittence", () => {
83
+ stubTv();
84
+ getRendementDistributionCh.mockReturnValue({
85
+ rd: '0.92',
86
+ tv_rendement_distribution_ch_id: '44'
87
+ });
88
+
89
+ const em_ch = {
90
+ donnee_entree: {
91
+ enum_type_emission_distribution_id: '10',
92
+ reseau_distribution_isole: 1
93
+ }
94
+ };
95
+ calc_emetteur_ch(em_ch, { enum_type_installation_id: '1' }, '2', '3');
96
+
97
+ expect(em_ch.donnee_intermediaire).toEqual({
98
+ rendement_distribution: 0.92,
99
+ rendement_emission: 0.95,
100
+ rendement_regulation: 0.99,
101
+ i0: 0.85
102
+ });
103
+ // les identifiants de table sont réécrits en nombres sur la donnée d'entrée
104
+ expect(em_ch.donnee_entree.tv_rendement_distribution_ch_id).toBe(44);
105
+ expect(em_ch.donnee_entree.tv_rendement_emission_id).toBe(11);
106
+ expect(em_ch.donnee_entree.tv_rendement_regulation_id).toBe(22);
107
+ expect(em_ch.donnee_entree.tv_intermittence_id).toBe(33);
108
+ expect(em_ch.donnee_utilisateur).toEqual({});
109
+ });
110
+
111
+ test('rendement de distribution : repli sur la recherche par identifiant si aucune ligne directe', () => {
112
+ stubTv();
113
+ getRendementDistributionCh.mockReturnValue(undefined);
114
+ getRendementDistributionChById.mockReturnValue({
115
+ rd: '0.88',
116
+ tv_rendement_distribution_ch_id: '55'
117
+ });
118
+
119
+ const em_ch = {
120
+ donnee_entree: {
121
+ enum_type_emission_distribution_id: '10',
122
+ tv_rendement_distribution_ch_id: 55
123
+ }
124
+ };
125
+ calc_emetteur_ch(em_ch, { enum_type_installation_id: '1' }, '2', '3');
126
+
127
+ expect(getRendementDistributionChById).toHaveBeenCalledWith(55);
128
+ expect(em_ch.donnee_intermediaire.rendement_distribution).toBe(0.88);
129
+ });
130
+
131
+ test("intermittence : la fiche technique de comptage force la présence d'un comptage individuel", () => {
132
+ stubTv();
133
+ getRendementDistributionCh.mockReturnValue({
134
+ rd: '0.92',
135
+ tv_rendement_distribution_ch_id: '44'
136
+ });
137
+
138
+ const em_ch = {
139
+ donnee_entree: {
140
+ enum_type_emission_distribution_id: '10',
141
+ enum_type_chauffage_id: '1',
142
+ enum_equipement_intermittence_id: '1',
143
+ enum_type_regulation_id: '1'
144
+ }
145
+ };
146
+ // map_id === '1' => la classe d'inertie entre dans le matcher
147
+ calc_emetteur_ch(
148
+ em_ch,
149
+ {
150
+ enum_type_installation_id: '1',
151
+ ficheTechniqueComptage: { valeur: '1' }
152
+ },
153
+ '1',
154
+ '7'
155
+ );
156
+
157
+ const matcher = vi.mocked(tv).mock.calls.find((c) => c[0] === 'intermittence')[1];
158
+ expect(matcher.comptage_individuel).toBe('Présence');
159
+ expect(matcher.enum_methode_application_dpe_log_id).toBe('1');
160
+ expect(matcher.enum_classe_inertie_id).toBe('7');
161
+ });
162
+
163
+ test('intermittence : absence de comptage individuel hors fiche technique', () => {
164
+ stubTv();
165
+ getRendementDistributionCh.mockReturnValue({
166
+ rd: '0.92',
167
+ tv_rendement_distribution_ch_id: '44'
168
+ });
169
+
170
+ const em_ch = {
171
+ donnee_entree: { enum_type_emission_distribution_id: '10' }
172
+ };
173
+ // map_id !== '1' => pas de classe d'inertie dans le matcher
174
+ calc_emetteur_ch(em_ch, { enum_type_installation_id: '1' }, '2', '3');
175
+
176
+ const matcher = vi.mocked(tv).mock.calls.find((c) => c[0] === 'intermittence')[1];
177
+ expect(matcher.comptage_individuel).toBe('Absence');
178
+ expect(matcher.enum_classe_inertie_id).toBeUndefined();
179
+ });
180
+
181
+ test('aucune ligne trouvée pour l’ensemble des rendements : messages d’erreur', () => {
182
+ const err = vi.spyOn(console, 'error').mockImplementation(() => {});
183
+ // toutes les tables renvoient null et le store ne fournit rien
184
+ vi.mocked(tv).mockReturnValue(null);
185
+ getRendementDistributionCh.mockReturnValue(undefined);
186
+ getRendementDistributionChById.mockReturnValue(undefined);
187
+
188
+ const em_ch = {
189
+ donnee_entree: { enum_type_emission_distribution_id: '10' }
190
+ };
191
+ calc_emetteur_ch(em_ch, { enum_type_installation_id: '1' }, '2', '3');
192
+
193
+ // distribution + émission + régulation + intermittence => 4 erreurs
194
+ expect(err).toHaveBeenCalledTimes(4);
195
+ expect(em_ch.donnee_intermediaire).toEqual({});
196
+ err.mockRestore();
197
+ });
198
+
199
+ test("fiche technique de comptage à 'oui' : présence détectée", () => {
200
+ stubTv();
201
+ getRendementDistributionCh.mockReturnValue({
202
+ rd: '0.92',
203
+ tv_rendement_distribution_ch_id: '44'
204
+ });
205
+
206
+ const em_ch = { donnee_entree: { enum_type_emission_distribution_id: '10' } };
207
+ calc_emetteur_ch(
208
+ em_ch,
209
+ { enum_type_installation_id: '1', ficheTechniqueComptage: { valeur: 'oui' } },
210
+ '2',
211
+ '3'
212
+ );
213
+
214
+ const matcher = vi.mocked(tv).mock.calls.find((c) => c[0] === 'intermittence')[1];
215
+ expect(matcher.comptage_individuel).toBe('Présence');
216
+ });
217
+
218
+ test('fiche technique de comptage vide : absence de comptage', () => {
219
+ stubTv();
220
+ getRendementDistributionCh.mockReturnValue({
221
+ rd: '0.92',
222
+ tv_rendement_distribution_ch_id: '44'
223
+ });
224
+
225
+ const em_ch = { donnee_entree: { enum_type_emission_distribution_id: '10' } };
226
+ calc_emetteur_ch(
227
+ em_ch,
228
+ { enum_type_installation_id: '1', ficheTechniqueComptage: { valeur: '' } },
229
+ '2',
230
+ '3'
231
+ );
232
+
233
+ const matcher = vi.mocked(tv).mock.calls.find((c) => c[0] === 'intermittence')[1];
234
+ expect(matcher.comptage_individuel).toBe('Absence');
235
+ });
236
+
237
+ test('intermittence introuvable : message d’erreur dédié', () => {
238
+ const err = vi.spyOn(console, 'error').mockImplementation(() => {});
239
+ vi.mocked(tv).mockImplementation((table) => {
240
+ if (table === 'rendement_emission') return { re: '0.95', tv_rendement_emission_id: '11' };
241
+ if (table === 'rendement_regulation') return { rr: '0.99', tv_rendement_regulation_id: '22' };
242
+ // intermittence => null
243
+ return null;
244
+ });
245
+ getRendementDistributionCh.mockReturnValue({
246
+ rd: '0.92',
247
+ tv_rendement_distribution_ch_id: '44'
248
+ });
249
+
250
+ const em_ch = { donnee_entree: { enum_type_emission_distribution_id: '10' } };
251
+ calc_emetteur_ch(em_ch, { enum_type_installation_id: '1' }, '2', '3');
252
+
253
+ expect(err).toHaveBeenCalled();
254
+ expect(em_ch.donnee_intermediaire.i0).toBeUndefined();
255
+ err.mockRestore();
256
+ });
257
+ });
258
+
259
+ /**
260
+ * Mode "bug for bug" : en l'absence de fiche technique, la présence d'un
261
+ * comptage individuel est déduite de la ligne d'intermittence de référence.
262
+ */
263
+ describe('calc_emetteur_ch - déduction du comptage en mode "bug for bug"', () => {
264
+ /** Pilote `tv` avec une ligne d'intermittence paramétrable. */
265
+ function stubTvIntermittence(comptageRow) {
266
+ vi.mocked(tv).mockImplementation((table, matcher) => {
267
+ if (table === 'rendement_emission') return { re: '0.95', tv_rendement_emission_id: '11' };
268
+ if (table === 'rendement_regulation') return { rr: '0.99', tv_rendement_regulation_id: '22' };
269
+ if (table === 'intermittence') {
270
+ // 1er appel : matcher basé sur tv_intermittence_id (déduction du comptage)
271
+ if (matcher.tv_intermittence_id !== undefined && !matcher.enum_type_installation_id) {
272
+ return comptageRow;
273
+ }
274
+ return { i0: '0.85', tv_intermittence_id: '33' };
275
+ }
276
+ return null;
277
+ });
278
+ getRendementDistributionCh.mockReturnValue({
279
+ rd: '0.92',
280
+ tv_rendement_distribution_ch_id: '44'
281
+ });
282
+ }
283
+
284
+ const emetteur = () => ({
285
+ donnee_entree: { enum_type_emission_distribution_id: '10', tv_intermittence_id: 5 }
286
+ });
287
+
288
+ test('ligne de référence "Présence" => comptage individuel présent', () => {
289
+ utilState.bug = true;
290
+ stubTvIntermittence({ comptage_individuel: 'Présence d’un comptage' });
291
+
292
+ const em_ch = emetteur();
293
+ calc_emetteur_ch(em_ch, { enum_type_installation_id: '1' }, '2', '3');
294
+
295
+ const matcher = vi
296
+ .mocked(tv)
297
+ .mock.calls.find((c) => c[0] === 'intermittence' && c[1].enum_type_installation_id)[1];
298
+ expect(matcher.comptage_individuel).toBe('Présence');
299
+ });
300
+
301
+ test('ligne de référence sans "Présence" => absence de comptage', () => {
302
+ utilState.bug = true;
303
+ stubTvIntermittence({ comptage_individuel: 'Absence de comptage' });
304
+
305
+ const em_ch = emetteur();
306
+ calc_emetteur_ch(em_ch, { enum_type_installation_id: '1' }, '2', '3');
307
+
308
+ const matcher = vi
309
+ .mocked(tv)
310
+ .mock.calls.find((c) => c[0] === 'intermittence' && c[1].enum_type_installation_id)[1];
311
+ expect(matcher.comptage_individuel).toBe('Absence');
312
+ });
313
+
314
+ test('ligne de référence sans colonne comptage => absence de comptage', () => {
315
+ utilState.bug = true;
316
+ stubTvIntermittence({ i0: '0.5' });
317
+
318
+ const em_ch = emetteur();
319
+ calc_emetteur_ch(em_ch, { enum_type_installation_id: '1' }, '2', '3');
320
+
321
+ const matcher = vi
322
+ .mocked(tv)
323
+ .mock.calls.find((c) => c[0] === 'intermittence' && c[1].enum_type_installation_id)[1];
324
+ expect(matcher.comptage_individuel).toBe('Absence');
325
+ });
326
+
327
+ test('aucune ligne de référence trouvée => absence de comptage', () => {
328
+ utilState.bug = true;
329
+ stubTvIntermittence(null);
330
+
331
+ const em_ch = emetteur();
332
+ calc_emetteur_ch(em_ch, { enum_type_installation_id: '1' }, '2', '3');
333
+
334
+ const matcher = vi
335
+ .mocked(tv)
336
+ .mock.calls.find((c) => c[0] === 'intermittence' && c[1].enum_type_installation_id)[1];
337
+ expect(matcher.comptage_individuel).toBe('Absence');
338
+ });
339
+ });