From 822b7bf07abc075e6bf152d2ae02aa9fc4498a16 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20PAILHAREY?= <rpailharey@grandlyon.com>
Date: Tue, 6 Dec 2022 14:16:03 +0000
Subject: [PATCH] fix(export): handle abnormal null dates

---
 src/components/Export/exportLoadingModal.tsx |  3 +-
 src/components/Options/exportOptions.tsx     | 42 +++++++-------------
 src/locales/fr.json                          |  2 +-
 src/services/consumption.service.spec.ts     | 18 +++++++++
 src/services/consumption.service.ts          | 26 +++++++-----
 src/services/queryRunner.service.ts          |  6 +--
 6 files changed, 54 insertions(+), 43 deletions(-)

diff --git a/src/components/Export/exportLoadingModal.tsx b/src/components/Export/exportLoadingModal.tsx
index 37ff12950..2b78dfde6 100644
--- a/src/components/Export/exportLoadingModal.tsx
+++ b/src/components/Export/exportLoadingModal.tsx
@@ -81,7 +81,8 @@ const ExportLoadingModal: React.FC<ExportLoadingModalProps> = ({
         false,
         true
       )
-      if (dataLoad && dataLoad.actualData) {
+
+      if (dataLoad?.actualData) {
         const exportDataFluid: any = {}
         exportDataFluid.fluidName = t(
           'FLUID.' + FluidType[fluidType] + '.LABEL'
diff --git a/src/components/Options/exportOptions.tsx b/src/components/Options/exportOptions.tsx
index 1c4936eb6..bce44da30 100644
--- a/src/components/Options/exportOptions.tsx
+++ b/src/components/Options/exportOptions.tsx
@@ -14,8 +14,8 @@ import { useClient } from 'cozy-client'
 import { useI18n } from 'cozy-ui/transpiled/react/I18n'
 import Icon from 'cozy-ui/transpiled/react/Icon'
 import { FluidType } from 'enum/fluid.enum'
+import { TimeStep } from 'enum/timeStep.enum'
 import { remove } from 'lodash'
-import { DateTime } from 'luxon'
 import React, { useEffect, useMemo, useState } from 'react'
 import ConsumptionDataManager from 'services/consumption.service'
 import './exportOptions.scss'
@@ -33,7 +33,7 @@ const ExportOptions: React.FC = () => {
     useState<boolean>(false)
   const [isExportDoneModal, setIsExportDoneModal] = useState<boolean>(false)
   const [hasError, setHasError] = useState<boolean>(false)
-  const [enabledFluids, setEnabledFluids] = useState<FluidType[]>([])
+  const [exportableFluids, setExportableFluids] = useState<FluidType[]>([])
   const [answer, setAnswer] = useState<FluidType[]>([])
 
   const [active, setActive] = useState<boolean>(false)
@@ -45,9 +45,7 @@ const ExportOptions: React.FC = () => {
   const handleChange = (value: FluidType) => {
     const tempAnswer = [...answer]
     if (tempAnswer.includes(value)) {
-      remove(tempAnswer, function (n) {
-        return n === value
-      })
+      remove(tempAnswer, answer => answer === value)
     } else {
       tempAnswer.push(value)
     }
@@ -56,27 +54,19 @@ const ExportOptions: React.FC = () => {
 
   useEffect(() => {
     let subscribed = true
-    const getEnabledFluids = async () => {
-      const firstDateDatas: (DateTime | null)[] =
-        await consumptionService.fetchAllFirstDateData([
-          FluidType.ELECTRICITY,
-          FluidType.WATER,
-          FluidType.GAS,
-        ])
-
-      const enabledFluidsData: FluidType[] = []
-      firstDateDatas.forEach((date, index) => {
-        if (date) {
-          enabledFluidsData.push(index)
-        }
-      })
-      setEnabledFluids(enabledFluidsData)
-      setAnswer(enabledFluidsData)
+    const getExportableFluids = async () => {
+      const exportableFluidsData: FluidType[] =
+        await consumptionService.getExportableFluids(
+          [FluidType.ELECTRICITY, FluidType.WATER, FluidType.GAS],
+          TimeStep.MONTH
+        )
+      setExportableFluids(exportableFluidsData)
+      setAnswer(exportableFluidsData)
       subscribed = false
     }
 
     if (subscribed) {
-      getEnabledFluids()
+      getExportableFluids()
     }
     return () => {
       subscribed = false
@@ -84,12 +74,11 @@ const ExportOptions: React.FC = () => {
   }, [consumptionService])
 
   const fluidCheckbox = () =>
-    enabledFluids.map((fluidType, key) => (
+    exportableFluids.map((fluidType, key) => (
       <label
         key={key}
         className={classNames('checkbox', {
-          ['answer-checked']:
-            answer.includes(fluidType) && enabledFluids[key] != null,
+          ['answer-checked']: answer.includes(fluidType),
         })}
       >
         <input
@@ -98,7 +87,6 @@ const ExportOptions: React.FC = () => {
           name={t('FLUID.' + FluidType[fluidType] + '.LABEL')}
           onChange={() => handleChange(fluidType)}
           checked={answer.includes(fluidType)}
-          disabled={enabledFluids[key] === null}
         />
         {t('FLUID.' + FluidType[fluidType] + '.LABEL')}
       </label>
@@ -150,7 +138,7 @@ const ExportOptions: React.FC = () => {
                 {t('export.fluid_select')}
               </div>
 
-              {enabledFluids.length === 0 ? (
+              {exportableFluids.length === 0 ? (
                 <div className="text-15-normal content intro">
                   {t('export.no_data')}
                 </div>
diff --git a/src/locales/fr.json b/src/locales/fr.json
index 4e3c7c90c..ea9180d7c 100644
--- a/src/locales/fr.json
+++ b/src/locales/fr.json
@@ -386,7 +386,7 @@
   "ecogesture": {
     "title_tab_0": "Objectifs",
     "title_tab_1": "Je fais déjà",
-    "title_tab_2": "Tous",
+    "title_tab_2": "Toutes",
     "MENU_TITLE": "Filtrer",
     "ALL": "Tous les usages",
     "HEATING": "Chauffage",
diff --git a/src/services/consumption.service.spec.ts b/src/services/consumption.service.spec.ts
index 5542c8073..b8757e23e 100644
--- a/src/services/consumption.service.spec.ts
+++ b/src/services/consumption.service.spec.ts
@@ -403,6 +403,24 @@ describe('Consumption service', () => {
     })
   })
 
+  describe('getExportableFluids method', () => {
+    it('should return the array of fluidtypes that have entries', async () => {
+      const fluidTypes: FluidType[] = [
+        FluidType.ELECTRICITY,
+        FluidType.WATER,
+        FluidType.GAS,
+      ]
+      mockGetEntries.mockResolvedValueOnce({ data: [1] })
+      mockGetEntries.mockResolvedValueOnce({ data: [] })
+      mockGetEntries.mockResolvedValueOnce({ data: [1] })
+      const result = await consumptionDataManager.getExportableFluids(
+        fluidTypes,
+        TimeStep.MONTH
+      )
+      expect(result).toStrictEqual([FluidType.ELECTRICITY, FluidType.GAS])
+    })
+  })
+
   describe('fetchAllFirstDateData method', () => {
     it('should return the latest date data of one fluid', async () => {
       const fluidTypes: FluidType[] = [FluidType.ELECTRICITY]
diff --git a/src/services/consumption.service.ts b/src/services/consumption.service.ts
index 1da49be57..535e8857f 100644
--- a/src/services/consumption.service.ts
+++ b/src/services/consumption.service.ts
@@ -331,28 +331,32 @@ export default class ConsumptionDataManager {
     return result
   }
 
+  public async getExportableFluids(
+    fluidTypes: FluidType[],
+    timeStep: TimeStep
+  ): Promise<FluidType[]> {
+    const exportableFluids: FluidType[] = []
+    for (const fluidType of fluidTypes) {
+      if (await this.checkDoctypeEntries(fluidType, timeStep)) {
+        exportableFluids.push(fluidType)
+      }
+    }
+    return exportableFluids
+  }
+
   public async fetchAllFirstDateData(
     fluidTypes: FluidType[],
     timeStep?: TimeStep
   ): Promise<(DateTime | null)[]> {
     let firstDay = null
     const firstDays = []
-    if (fluidTypes.length === 1) {
+    for (const fluidType of fluidTypes) {
       firstDay =
         (await this._queryRunnerService.getFirstDateData(
-          fluidTypes[0],
+          fluidType,
           timeStep
         )) || null
       firstDays.push(firstDay)
-    } else if (fluidTypes.length > 1) {
-      for (const fluidType of fluidTypes) {
-        firstDay =
-          (await this._queryRunnerService.getFirstDateData(
-            fluidType,
-            timeStep
-          )) || null
-        firstDays.push(firstDay)
-      }
     }
     return firstDays
   }
diff --git a/src/services/queryRunner.service.ts b/src/services/queryRunner.service.ts
index fdc6c671e..f559a5a1c 100644
--- a/src/services/queryRunner.service.ts
+++ b/src/services/queryRunner.service.ts
@@ -76,7 +76,7 @@ export default class QueryRunner {
   ) {
     const doctype = this.getRelevantDoctype(fluidType, timeStep || TimeStep.DAY)
     return Q(doctype)
-      .where({})
+      .where({ year: { $ne: null }, month: { $ne: null }, day: { $ne: null } })
       .indexFields(['year', 'month', 'day'])
       .sortBy([{ year: 'asc' }, { month: 'asc' }, { day: 'asc' }])
       .limitBy(limit)
@@ -89,7 +89,7 @@ export default class QueryRunner {
   ) {
     const doctype = this.getRelevantDoctype(fluidType, timeStep || TimeStep.DAY)
     return Q(doctype)
-      .where({})
+      .where({ year: { $ne: null }, month: { $ne: null }, day: { $ne: null } })
       .indexFields(['year', 'month', 'day'])
       .sortBy([{ year: 'desc' }, { month: 'desc' }, { day: 'desc' }])
       .limitBy(limit)
@@ -449,7 +449,7 @@ export default class QueryRunner {
   public async getEntries(fluidType: FluidType, timeStep: TimeStep) {
     const doctype = this.getRelevantDoctype(fluidType, timeStep)
     try {
-      const query = Q(doctype).where({})
+      const query = Q(doctype).where({}).limitBy(1)
       const result = await this._client.query(query)
       return result
     } catch (error) {
-- 
GitLab