From 6281cc751e86669fbfffc17cc1b82b6260496e7c Mon Sep 17 00:00:00 2001
From: Neil <55785687+carkom@users.noreply.github.com>
Date: Wed, 20 Mar 2024 09:01:44 +0000
Subject: [PATCH] Custom Reports: Intervals Updates (#2479)

* IntervalsUpdates

* notes

* updates

* add Ranges

* calc Leg strict

* ts updates

* review updates
---
 .../src/components/reports/ChooseGraph.tsx    |  10 +-
 .../src/components/reports/Header.jsx         |  88 +++-------
 .../src/components/reports/ReportOptions.ts   |   4 +-
 .../src/components/reports/ReportSidebar.jsx  |  71 ++++----
 .../src/components/reports/ReportSummary.tsx  |  12 +-
 .../components/reports/graphs/AreaGraph.tsx   |  10 +-
 .../components/reports/graphs/BarGraph.tsx    |   2 +-
 .../components/reports/graphs/DonutGraph.tsx  |   2 +-
 .../reports/graphs/StackedBarGraph.tsx        |   6 +-
 .../reports/graphs/tableGraph/ReportTable.tsx |  10 +-
 .../graphs/tableGraph/ReportTableList.tsx     |   6 +-
 .../graphs/tableGraph/ReportTableRow.tsx      |  18 +-
 .../graphs/tableGraph/ReportTableTotals.tsx   |   8 +-
 .../src/components/reports/reportRanges.ts    | 157 ++++++++++++++++++
 .../reports/reports/CustomReport.jsx          |  26 +--
 .../reports/spreadsheets/calculateLegend.ts   |  14 +-
 .../spreadsheets/custom-spreadsheet.ts        |  59 ++++---
 .../spreadsheets/grouped-spreadsheet.ts       |  33 ++--
 .../reports/spreadsheets/makeQuery.ts         |  13 +-
 .../reports/spreadsheets/recalculate.ts       |  40 ++---
 .../loot-core/src/types/models/reports.d.ts   |  12 +-
 upcoming-release-notes/2479.md                |   6 +
 22 files changed, 388 insertions(+), 219 deletions(-)
 create mode 100644 packages/desktop-client/src/components/reports/reportRanges.ts
 create mode 100644 upcoming-release-notes/2479.md

diff --git a/packages/desktop-client/src/components/reports/ChooseGraph.tsx b/packages/desktop-client/src/components/reports/ChooseGraph.tsx
index 1eef9fabc..ab945112d 100644
--- a/packages/desktop-client/src/components/reports/ChooseGraph.tsx
+++ b/packages/desktop-client/src/components/reports/ChooseGraph.tsx
@@ -48,14 +48,14 @@ export function ChooseGraph({
   compact,
   style,
 }: ChooseGraphProps) {
-  const months: string[] = monthUtils.rangeInclusive(startDate, endDate);
+  const intervals: string[] = monthUtils.rangeInclusive(startDate, endDate);
   const graphStyle = compact ? { ...style } : { flexGrow: 1 };
   const balanceTypeOp = ReportOptions.balanceTypeMap.get(balanceType);
   const groupByData =
     groupBy === 'Category'
       ? 'groupedData'
       : groupBy === 'Interval'
-        ? 'monthData'
+        ? 'intervalData'
         : 'data';
 
   const saveScrollWidth = value => {
@@ -144,7 +144,7 @@ export function ChooseGraph({
         <ReportTableHeader
           headerScrollRef={headerScrollRef}
           handleScroll={handleScroll}
-          data={mode === 'time' && data.monthData}
+          data={mode === 'time' && data.intervalData}
           groupBy={groupBy}
           interval={interval}
           balanceType={balanceType}
@@ -160,7 +160,7 @@ export function ChooseGraph({
           groupBy={groupBy}
           data={data[groupByData]}
           mode={mode}
-          monthsCount={months.length}
+          intervalsCount={intervals.length}
           compact={compact}
           style={rowStyle}
           compactStyle={compactStyle}
@@ -171,7 +171,7 @@ export function ChooseGraph({
           data={data}
           mode={mode}
           balanceTypeOp={balanceTypeOp}
-          monthsCount={months.length}
+          intervalsCount={intervals.length}
           compact={compact}
           style={rowStyle}
           compactStyle={compactStyle}
diff --git a/packages/desktop-client/src/components/reports/Header.jsx b/packages/desktop-client/src/components/reports/Header.jsx
index bd4ebe076..2e9fa94a4 100644
--- a/packages/desktop-client/src/components/reports/Header.jsx
+++ b/packages/desktop-client/src/components/reports/Header.jsx
@@ -11,66 +11,12 @@ import { View } from '../common/View';
 import { AppliedFilters } from '../filters/AppliedFilters';
 import { FilterButton } from '../filters/FiltersMenu';
 
-export function validateStart(allMonths, start, end) {
-  const earliest = allMonths[allMonths.length - 1].name;
-  if (end < start) {
-    end = monthUtils.addMonths(start, 6);
-  }
-  return boundedRange(earliest, start, end);
-}
-
-export function validateEnd(allMonths, start, end) {
-  const earliest = allMonths[allMonths.length - 1].name;
-  if (start > end) {
-    start = monthUtils.subMonths(end, 6);
-  }
-  return boundedRange(earliest, start, end);
-}
-
-export function validateRange(allMonths, start, end) {
-  const latest = monthUtils.currentMonth();
-  const earliest = allMonths[allMonths.length - 1].name;
-  if (end > latest) {
-    end = latest;
-  }
-  if (start < earliest) {
-    start = earliest;
-  }
-  return [start, end];
-}
-
-function boundedRange(earliest, start, end) {
-  const latest = monthUtils.currentMonth();
-  if (end > latest) {
-    end = latest;
-  }
-  if (start < earliest) {
-    start = earliest;
-  }
-  return [start, end];
-}
-
-function getLatestRange(offset) {
-  const end = monthUtils.currentMonth();
-  const start = monthUtils.subMonths(end, offset);
-  return [start, end];
-}
-
-export function getSpecificRange(offset, addNumber) {
-  const currMonth = monthUtils.currentMonth();
-  const start = monthUtils.subMonths(currMonth, offset);
-  const end = monthUtils.addMonths(
-    start,
-    addNumber === null ? offset : addNumber,
-  );
-  return [start, end];
-}
-
-export function getFullRange(allMonths) {
-  const start = allMonths[allMonths.length - 1].name;
-  const end = monthUtils.currentMonth();
-  return [start, end];
-}
+import {
+  getFullRange,
+  getLatestRange,
+  validateEnd,
+  validateStart,
+} from './reportRanges';
 
 export function Header({
   title,
@@ -128,7 +74,13 @@ export function Header({
           >
             <Select
               onChange={newValue =>
-                onChangeDates(...validateStart(allMonths, newValue, end))
+                onChangeDates(
+                  ...validateStart(
+                    allMonths[allMonths.length - 1].name,
+                    newValue,
+                    end,
+                  ),
+                )
               }
               value={start}
               defaultLabel={monthUtils.format(start, 'MMMM, yyyy')}
@@ -137,7 +89,13 @@ export function Header({
             <View>to</View>
             <Select
               onChange={newValue =>
-                onChangeDates(...validateEnd(allMonths, start, newValue))
+                onChangeDates(
+                  ...validateEnd(
+                    allMonths[allMonths.length - 1].name,
+                    start,
+                    newValue,
+                  ),
+                )
               }
               value={end}
               options={allMonths.map(({ name, pretty }) => [name, pretty])}
@@ -174,7 +132,11 @@ export function Header({
           </Button>
           <Button
             type="bare"
-            onClick={() => onChangeDates(...getFullRange(allMonths))}
+            onClick={() =>
+              onChangeDates(
+                ...getFullRange(allMonths[allMonths.length - 1].name),
+              )
+            }
           >
             All Time
           </Button>
diff --git a/packages/desktop-client/src/components/reports/ReportOptions.ts b/packages/desktop-client/src/components/reports/ReportOptions.ts
index b62b6c367..7a7c717ca 100644
--- a/packages/desktop-client/src/components/reports/ReportOptions.ts
+++ b/packages/desktop-client/src/components/reports/ReportOptions.ts
@@ -7,8 +7,8 @@ import {
   type PayeeEntity,
 } from 'loot-core/src/types/models';
 
-const startDate = monthUtils.subMonths(monthUtils.currentMonth(), 5);
-const endDate = monthUtils.currentMonth();
+const startDate = monthUtils.subMonths(monthUtils.currentMonth(), 5) + '-01';
+const endDate = monthUtils.currentDay();
 
 export const defaultReport: CustomReportEntity = {
   id: '',
diff --git a/packages/desktop-client/src/components/reports/ReportSidebar.jsx b/packages/desktop-client/src/components/reports/ReportSidebar.jsx
index 2b42b4af2..4e4b3f84f 100644
--- a/packages/desktop-client/src/components/reports/ReportSidebar.jsx
+++ b/packages/desktop-client/src/components/reports/ReportSidebar.jsx
@@ -11,21 +11,20 @@ import { View } from '../common/View';
 import { Tooltip } from '../tooltips';
 
 import { CategorySelector } from './CategorySelector';
+import { ModeButton } from './ModeButton';
+import { ReportOptions } from './ReportOptions';
 import {
-  validateStart,
+  getSpecificRange,
   validateEnd,
-  getFullRange,
   validateRange,
-  getSpecificRange,
-} from './Header';
-import { ModeButton } from './ModeButton';
-import { ReportOptions } from './ReportOptions';
+  validateStart,
+} from './reportRanges';
 
 export function ReportSidebar({
   customReportItems,
   categories,
   dateRangeLine,
-  allMonths,
+  allIntervals,
   setDateRange,
   setGraphType,
   setGroupBy,
@@ -43,44 +42,44 @@ export function ReportSidebar({
   disabledItems,
   defaultItems,
   defaultModeItems,
+  earliestTransaction,
 }) {
   const [menuOpen, setMenuOpen] = useState(false);
   const onSelectRange = cond => {
     onReportChange({ type: 'modify' });
     setDateRange(cond);
+    let dateStart;
+    let dateEnd;
     switch (cond) {
       case 'All time':
-        onChangeDates(...getFullRange(allMonths));
+        onChangeDates(earliestTransaction, monthUtils.currentDay());
         break;
       case 'Year to date':
-        onChangeDates(
-          ...validateRange(
-            allMonths,
-            monthUtils.getYearStart(monthUtils.currentMonth()),
-            monthUtils.currentMonth(),
-          ),
+        [dateStart, dateEnd] = validateRange(
+          earliestTransaction,
+          monthUtils.getYearStart(monthUtils.currentMonth()) + '-01',
+          monthUtils.currentDay(),
         );
+        onChangeDates(dateStart, dateEnd);
         break;
       case 'Last year':
-        onChangeDates(
-          ...validateRange(
-            allMonths,
-            monthUtils.getYearStart(
-              monthUtils.prevYear(monthUtils.currentMonth()),
-            ),
-            monthUtils.getYearEnd(
-              monthUtils.prevYear(monthUtils.currentDate()),
-            ),
-          ),
+        [dateStart, dateEnd] = validateRange(
+          earliestTransaction,
+          monthUtils.getYearStart(
+            monthUtils.prevYear(monthUtils.currentMonth()),
+          ) + '-01',
+          monthUtils.getYearEnd(monthUtils.prevYear(monthUtils.currentDate())) +
+            '-31',
         );
+        onChangeDates(dateStart, dateEnd);
         break;
       default:
-        onChangeDates(
-          ...getSpecificRange(
-            ReportOptions.dateRangeMap.get(cond),
-            cond === 'Last month' ? 0 : null,
-          ),
+        [dateStart, dateEnd] = getSpecificRange(
+          ReportOptions.dateRangeMap.get(cond),
+          cond === 'Last month' ? 0 : null,
+          customReportItems.interval,
         );
+        onChangeDates(dateStart, dateEnd);
     }
   };
 
@@ -378,9 +377,10 @@ export function ReportSidebar({
                 onChange={newValue =>
                   onChangeDates(
                     ...validateStart(
-                      allMonths,
+                      earliestTransaction,
                       newValue,
                       customReportItems.endDate,
+                      customReportItems.interval,
                     ),
                   )
                 }
@@ -389,7 +389,7 @@ export function ReportSidebar({
                   customReportItems.startDate,
                   'MMMM, yyyy',
                 )}
-                options={allMonths.map(({ name, pretty }) => [name, pretty])}
+                options={allIntervals.map(({ name, pretty }) => [name, pretty])}
               />
             </View>
             <View
@@ -406,14 +406,19 @@ export function ReportSidebar({
                 onChange={newValue =>
                   onChangeDates(
                     ...validateEnd(
-                      allMonths,
+                      earliestTransaction,
                       customReportItems.startDate,
                       newValue,
+                      customReportItems.interval,
                     ),
                   )
                 }
                 value={customReportItems.endDate}
-                options={allMonths.map(({ name, pretty }) => [name, pretty])}
+                defaultLabel={monthUtils.format(
+                  customReportItems.endDate,
+                  'MMMM, yyyy',
+                )}
+                options={allIntervals.map(({ name, pretty }) => [name, pretty])}
               />
             </View>
           </>
diff --git a/packages/desktop-client/src/components/reports/ReportSummary.tsx b/packages/desktop-client/src/components/reports/ReportSummary.tsx
index eab6d9a07..5653a19ce 100644
--- a/packages/desktop-client/src/components/reports/ReportSummary.tsx
+++ b/packages/desktop-client/src/components/reports/ReportSummary.tsx
@@ -19,7 +19,8 @@ type ReportSummaryProps = {
   endDate: string;
   data: GroupedEntity;
   balanceTypeOp: string;
-  monthsCount: number;
+  interval: string;
+  intervalsCount: number;
 };
 
 export function ReportSummary({
@@ -27,13 +28,14 @@ export function ReportSummary({
   endDate,
   data,
   balanceTypeOp,
-  monthsCount,
+  interval,
+  intervalsCount,
 }: ReportSummaryProps) {
   const net =
     Math.abs(data.totalDebts) > Math.abs(data.totalAssets)
       ? 'PAYMENT'
       : 'DEPOSIT';
-  const average = amountToInteger(data[balanceTypeOp]) / monthsCount;
+  const average = amountToInteger(data[balanceTypeOp]) / intervalsCount;
   return (
     <View
       style={{
@@ -133,7 +135,9 @@ export function ReportSummary({
             {!isNaN(average) && integerToCurrency(Math.round(average))}
           </PrivacyFilter>
         </Text>
-        <Text style={{ fontWeight: 600 }}>Per month</Text>
+        <Text style={{ fontWeight: 600 }}>
+          Per {interval === 'Monthly' ? 'month' : 'year'}
+        </Text>
       </View>
     </View>
   );
diff --git a/packages/desktop-client/src/components/reports/graphs/AreaGraph.tsx b/packages/desktop-client/src/components/reports/graphs/AreaGraph.tsx
index 276c15a40..4918c92ce 100644
--- a/packages/desktop-client/src/components/reports/graphs/AreaGraph.tsx
+++ b/packages/desktop-client/src/components/reports/graphs/AreaGraph.tsx
@@ -125,8 +125,8 @@ export function AreaGraph({
   viewLabels,
 }: AreaGraphProps) {
   const privacyMode = usePrivacyMode();
-  const dataMax = Math.max(...data.monthData.map(i => i[balanceTypeOp]));
-  const dataMin = Math.min(...data.monthData.map(i => i[balanceTypeOp]));
+  const dataMax = Math.max(...data.intervalData.map(i => i[balanceTypeOp]));
+  const dataMin = Math.min(...data.intervalData.map(i => i[balanceTypeOp]));
 
   const labelsMargin = viewLabels ? 30 : 0;
   const dataDiff = dataMax - dataMin;
@@ -144,7 +144,7 @@ export function AreaGraph({
     dataMax === 0 || Math.abs(dataMax) <= extendRangeAmount
       ? 0
       : Math.ceil((dataMax + extendRangeAmount) / 100) * 100;
-  const lastLabel = data.monthData.length - 1;
+  const lastLabel = data.intervalData.length - 1;
 
   const tickFormatter = tick => {
     if (!privacyMode) return `${amountToCurrencyNoDecimal(tick)}`; // Formats the tick values as strings with commas
@@ -173,14 +173,14 @@ export function AreaGraph({
       }}
     >
       {(width, height) =>
-        data.monthData && (
+        data.intervalData && (
           <ResponsiveContainer>
             <div>
               {!compact && <div style={{ marginTop: '15px' }} />}
               <AreaChart
                 width={width}
                 height={height}
-                data={data.monthData}
+                data={data.intervalData}
                 margin={{
                   top: 0,
                   right: labelsMargin,
diff --git a/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx b/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx
index 16e51f262..f85572d8c 100644
--- a/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx
+++ b/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx
@@ -146,7 +146,7 @@ export function BarGraph({
   const privacyMode = usePrivacyMode();
 
   const yAxis = groupBy === 'Interval' ? 'date' : 'name';
-  const splitData = groupBy === 'Interval' ? 'monthData' : 'data';
+  const splitData = groupBy === 'Interval' ? 'intervalData' : 'data';
   const labelsMargin = viewLabels ? 30 : 0;
 
   const getVal = obj => {
diff --git a/packages/desktop-client/src/components/reports/graphs/DonutGraph.tsx b/packages/desktop-client/src/components/reports/graphs/DonutGraph.tsx
index 40ba708aa..a129f6c30 100644
--- a/packages/desktop-client/src/components/reports/graphs/DonutGraph.tsx
+++ b/packages/desktop-client/src/components/reports/graphs/DonutGraph.tsx
@@ -188,7 +188,7 @@ export function DonutGraph({
   viewLabels,
 }: DonutGraphProps) {
   const yAxis = groupBy === 'Interval' ? 'date' : 'name';
-  const splitData = groupBy === 'Interval' ? 'monthData' : 'data';
+  const splitData = groupBy === 'Interval' ? 'intervalData' : 'data';
 
   const getVal = obj => {
     if (balanceTypeOp === 'totalDebts') {
diff --git a/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx b/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx
index 3f1d5849b..2a3136fe6 100644
--- a/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx
+++ b/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx
@@ -142,7 +142,7 @@ export function StackedBarGraph({
 }: StackedBarGraphProps) {
   const privacyMode = usePrivacyMode();
 
-  const largestValue = data.monthData
+  const largestValue = data.intervalData
     .map(c => c[balanceTypeOp])
     .reduce((acc, cur) => (Math.abs(cur) > Math.abs(acc) ? cur : acc), 0);
 
@@ -155,14 +155,14 @@ export function StackedBarGraph({
       }}
     >
       {(width, height) =>
-        data.monthData && (
+        data.intervalData && (
           <ResponsiveContainer>
             <div>
               {!compact && <div style={{ marginTop: '15px' }} />}
               <BarChart
                 width={width}
                 height={height}
-                data={data.monthData}
+                data={data.intervalData}
                 margin={{ top: 0, right: 0, left: leftMargin, bottom: 0 }}
               >
                 <Tooltip
diff --git a/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTable.tsx b/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTable.tsx
index 284200c6c..45939a4bc 100644
--- a/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTable.tsx
+++ b/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTable.tsx
@@ -24,7 +24,7 @@ type ReportTableProps = {
   balanceTypeOp: 'totalDebts' | 'totalTotals' | 'totalAssets';
   data: DataEntity[];
   mode: string;
-  monthsCount: number;
+  intervalsCount: number;
   compact: boolean;
   style?: CSSProperties;
   compactStyle?: CSSProperties;
@@ -38,7 +38,7 @@ export function ReportTable({
   balanceTypeOp,
   data,
   mode,
-  monthsCount,
+  intervalsCount,
   compact,
   style,
   compactStyle,
@@ -56,7 +56,7 @@ export function ReportTable({
       item,
       groupByItem,
       mode,
-      monthsCount,
+      intervalsCount,
       compact,
       style,
       compactStyle,
@@ -67,7 +67,7 @@ export function ReportTable({
           balanceTypeOp={balanceTypeOp}
           groupByItem={groupByItem}
           mode={mode}
-          monthsCount={monthsCount}
+          intervalsCount={intervalsCount}
           compact={compact}
           style={style}
           compactStyle={compactStyle}
@@ -102,7 +102,7 @@ export function ReportTable({
       >
         <ReportTableList
           data={data}
-          monthsCount={monthsCount}
+          intervalsCount={intervalsCount}
           mode={mode}
           groupBy={groupBy}
           renderItem={renderItem}
diff --git a/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableList.tsx b/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableList.tsx
index b1e56c312..1cdc0e160 100644
--- a/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableList.tsx
+++ b/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableList.tsx
@@ -10,7 +10,7 @@ import { Row } from '../../../table';
 type ReportTableListProps = {
   data: DataEntity[];
   mode?: string;
-  monthsCount?: number;
+  intervalsCount?: number;
   groupBy: string;
   renderItem;
   compact: boolean;
@@ -20,7 +20,7 @@ type ReportTableListProps = {
 
 export function ReportTableList({
   data,
-  monthsCount,
+  intervalsCount,
   mode,
   groupBy,
   renderItem,
@@ -53,7 +53,7 @@ export function ReportTableList({
       item,
       groupByItem,
       mode,
-      monthsCount,
+      intervalsCount,
       compact,
       style,
       compactStyle,
diff --git a/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableRow.tsx b/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableRow.tsx
index ad6ba5c3d..fabe9698f 100644
--- a/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableRow.tsx
+++ b/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableRow.tsx
@@ -15,7 +15,7 @@ type ReportTableRowProps = {
   balanceTypeOp: 'totalAssets' | 'totalDebts' | 'totalTotals';
   groupByItem: 'id' | 'name';
   mode: string;
-  monthsCount: number;
+  intervalsCount: number;
   compact: boolean;
   style?: CSSProperties;
   compactStyle?: CSSProperties;
@@ -27,12 +27,12 @@ export const ReportTableRow = memo(
     balanceTypeOp,
     groupByItem,
     mode,
-    monthsCount,
+    intervalsCount,
     compact,
     style,
     compactStyle,
   }: ReportTableRowProps) => {
-    const average = amountToInteger(item[balanceTypeOp]) / monthsCount;
+    const average = amountToInteger(item[balanceTypeOp]) / intervalsCount;
     return (
       <Row
         key={item.id}
@@ -52,19 +52,19 @@ export const ReportTableRow = memo(
           }}
           valueStyle={compactStyle}
         />
-        {item.monthData && mode === 'time'
-          ? item.monthData.map(month => {
+        {item.intervalData && mode === 'time'
+          ? item.intervalData.map(intervalItem => {
               return (
                 <Cell
-                  key={amountToCurrency(month[balanceTypeOp])}
+                  key={amountToCurrency(intervalItem[balanceTypeOp])}
                   style={{
                     minWidth: compact ? 50 : 85,
                   }}
                   valueStyle={compactStyle}
-                  value={amountToCurrency(month[balanceTypeOp])}
+                  value={amountToCurrency(intervalItem[balanceTypeOp])}
                   title={
-                    Math.abs(month[balanceTypeOp]) > 100000
-                      ? amountToCurrency(month[balanceTypeOp])
+                    Math.abs(intervalItem[balanceTypeOp]) > 100000
+                      ? amountToCurrency(intervalItem[balanceTypeOp])
                       : undefined
                   }
                   width="flex"
diff --git a/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableTotals.tsx b/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableTotals.tsx
index 40228d0d2..d47cf0253 100644
--- a/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableTotals.tsx
+++ b/packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableTotals.tsx
@@ -18,7 +18,7 @@ type ReportTableTotalsProps = {
   data: GroupedEntity;
   balanceTypeOp: string;
   mode: string;
-  monthsCount: number;
+  intervalsCount: number;
   totalScrollRef: RefProp<HTMLDivElement>;
   handleScroll: UIEventHandler<HTMLDivElement>;
   compact: boolean;
@@ -30,7 +30,7 @@ export function ReportTableTotals({
   data,
   balanceTypeOp,
   mode,
-  monthsCount,
+  intervalsCount,
   totalScrollRef,
   handleScroll,
   compact,
@@ -51,7 +51,7 @@ export function ReportTableTotals({
     }
   });
 
-  const average = amountToInteger(data[balanceTypeOp]) / monthsCount;
+  const average = amountToInteger(data[balanceTypeOp]) / intervalsCount;
   return (
     <Row
       collapsed={true}
@@ -85,7 +85,7 @@ export function ReportTableTotals({
           value="Totals"
         />
         {mode === 'time'
-          ? data.monthData.map(item => {
+          ? data.intervalData.map(item => {
               return (
                 <Cell
                   style={{
diff --git a/packages/desktop-client/src/components/reports/reportRanges.ts b/packages/desktop-client/src/components/reports/reportRanges.ts
new file mode 100644
index 000000000..ae0e1a33b
--- /dev/null
+++ b/packages/desktop-client/src/components/reports/reportRanges.ts
@@ -0,0 +1,157 @@
+import * as monthUtils from 'loot-core/src/shared/months';
+
+export function validateStart(
+  earliest: string,
+  start: string,
+  end: string,
+  interval?: string,
+) {
+  let addDays;
+  let dateStart;
+  switch (interval) {
+    case 'Monthly':
+      dateStart = start + '-01';
+      addDays = 180;
+      break;
+    case 'Yearly':
+      dateStart = start + '-01-01';
+      addDays = 1095;
+      break;
+    case 'Daily':
+      dateStart = start;
+      addDays = 6;
+      break;
+    default:
+      dateStart = start;
+      addDays = 180;
+      break;
+  }
+
+  if (end < start) {
+    end = monthUtils.addDays(dateStart, addDays);
+  }
+  return boundedRange(
+    earliest,
+    dateStart,
+    interval ? end : monthUtils.monthFromDate(end),
+    interval,
+  );
+}
+
+export function validateEnd(
+  earliest: string,
+  start: string,
+  end: string,
+  interval?: string,
+) {
+  let subDays;
+  let dateEnd;
+  switch (interval) {
+    case 'Monthly':
+      dateEnd = end + '-31';
+      subDays = 180;
+      break;
+    case 'Yearly':
+      dateEnd = end + '-12-31';
+      subDays = 1095;
+      break;
+    case 'Daily':
+      dateEnd = end;
+      subDays = 6;
+      break;
+    default:
+      dateEnd = end;
+      subDays = 180;
+      break;
+  }
+
+  if (start > end) {
+    start = monthUtils.subDays(dateEnd, subDays);
+  }
+  return boundedRange(
+    earliest,
+    interval ? start : monthUtils.monthFromDate(start),
+    dateEnd,
+    interval,
+  );
+}
+
+export function validateRange(earliest: string, start: string, end: string) {
+  const latest = monthUtils.currentDay();
+  if (end > latest) {
+    end = latest;
+  }
+  if (start < earliest) {
+    start = earliest;
+  }
+  return [start, end];
+}
+
+function boundedRange(
+  earliest: string,
+  start: string,
+  end: string,
+  interval?: string,
+) {
+  let latest;
+  switch (interval) {
+    case 'Monthly':
+      latest = monthUtils.currentMonth() + '-31';
+      break;
+    case 'Yearly':
+      latest = monthUtils.currentDay();
+      break;
+    default:
+      latest = monthUtils.currentMonth();
+      break;
+  }
+
+  if (end > latest) {
+    end = latest;
+  }
+  if (start < earliest) {
+    start = earliest;
+  }
+  return [start, end];
+}
+
+export function getSpecificRange(
+  offset: number,
+  addNumber: number,
+  interval: string,
+) {
+  const currentDay = monthUtils.currentDay();
+  let currInterval;
+  let dateStart;
+  let dateEnd;
+  switch (interval) {
+    case 'Monthly':
+      currInterval = monthUtils.monthFromDate(currentDay);
+      dateStart = monthUtils.subMonths(currInterval, offset);
+      dateEnd = monthUtils.addMonths(
+        dateStart,
+        addNumber === null ? offset : addNumber,
+      );
+      break;
+    default:
+      currInterval = currentDay;
+      dateStart = monthUtils.subDays(currInterval, offset);
+      dateEnd = monthUtils.addDays(
+        dateStart,
+        addNumber === null ? offset : addNumber,
+      );
+      break;
+  }
+  return [dateStart, dateEnd];
+}
+
+export function getFullRange(start: string) {
+  const end = monthUtils.currentMonth();
+  return [start, end];
+}
+
+export function getLatestRange(offset: number) {
+  const end = monthUtils.currentMonth();
+  const start = monthUtils.subMonths(end, offset);
+  return [start, end];
+}
diff --git a/packages/desktop-client/src/components/reports/reports/CustomReport.jsx b/packages/desktop-client/src/components/reports/reports/CustomReport.jsx
index 406c14991..8e7b1605a 100644
--- a/packages/desktop-client/src/components/reports/reports/CustomReport.jsx
+++ b/packages/desktop-client/src/components/reports/reports/CustomReport.jsx
@@ -57,7 +57,7 @@ export function CustomReport() {
     ? location.state.report ?? defaultReport
     : defaultReport;
 
-  const [allMonths, setAllMonths] = useState(null);
+  const [allIntervals, setAllIntervals] = useState(null);
 
   const [selectedCategories, setSelectedCategories] = useState(
     loadReport.selectedCategories,
@@ -83,11 +83,12 @@ export function CustomReport() {
   const [dataCheck, setDataCheck] = useState(false);
   const dateRangeLine = ReportOptions.dateRange.length - 3;
 
+  const [earliestTransaction, setEarliestTransaction] = useState('');
   const [report, setReport] = useState(loadReport);
   const [savedStatus, setSavedStatus] = useState(
     location.state ? (location.state.report ? 'saved' : 'new') : 'new',
   );
-  const months = monthUtils.rangeInclusive(startDate, endDate);
+  const intervals = monthUtils.rangeInclusive(startDate, endDate);
 
   useEffect(() => {
     if (selectedCategories === undefined && categories.list.length !== 0) {
@@ -99,6 +100,7 @@ export function CustomReport() {
     async function run() {
       report.conditions.forEach(condition => onApplyFilter(condition));
       const trans = await send('get-earliest-transaction');
+      setEarliestTransaction(trans ? trans.date : monthUtils.currentDay());
       const currentMonth = monthUtils.currentMonth();
       let earliestMonth = trans
         ? monthUtils.monthFromDate(d.parseISO(fromDateRepr(trans.date)))
@@ -112,7 +114,7 @@ export function CustomReport() {
         earliestMonth = yearAgo;
       }
 
-      const allMonths = monthUtils
+      const allInter = monthUtils
         .rangeInclusive(earliestMonth, monthUtils.currentMonth())
         .map(month => ({
           name: month,
@@ -120,7 +122,7 @@ export function CustomReport() {
         }))
         .reverse();
 
-      setAllMonths(allMonths);
+      setAllIntervals(allInter);
     }
     run();
   }, []);
@@ -133,6 +135,7 @@ export function CustomReport() {
     return createGroupedSpreadsheet({
       startDate,
       endDate,
+      interval,
       categories,
       selectedCategories,
       conditions: filters,
@@ -167,6 +170,7 @@ export function CustomReport() {
     return createCustomSpreadsheet({
       startDate,
       endDate,
+      interval,
       categories,
       selectedCategories,
       conditions: filters,
@@ -226,7 +230,7 @@ export function CustomReport() {
 
   const [scrollWidth, setScrollWidth] = useState(0);
 
-  if (!allMonths || !data) {
+  if (!allIntervals || !data) {
     return null;
   }
 
@@ -289,9 +293,9 @@ export function CustomReport() {
     }
   };
 
-  const onChangeDates = (startDate, endDate) => {
-    setStartDate(startDate);
-    setEndDate(endDate);
+  const onChangeDates = (dateStart, dateEnd) => {
+    setStartDate(dateStart);
+    setEndDate(dateEnd);
     onReportChange({ type: 'modify' });
   };
 
@@ -401,7 +405,7 @@ export function CustomReport() {
           customReportItems={customReportItems}
           categories={categories}
           dateRangeLine={dateRangeLine}
-          allMonths={allMonths}
+          allIntervals={allIntervals}
           setDateRange={setDateRange}
           setGraphType={setGraphType}
           setGroupBy={setGroupBy}
@@ -419,6 +423,7 @@ export function CustomReport() {
           disabledItems={disabledItems}
           defaultItems={defaultItems}
           defaultModeItems={defaultModeItems}
+          earliestTransaction={earliestTransaction}
         />
         <View
           style={{
@@ -546,7 +551,8 @@ export function CustomReport() {
                       endDate={endDate}
                       balanceTypeOp={balanceTypeOp}
                       data={data}
-                      monthsCount={months.length}
+                      interval={interval}
+                      intervalsCount={intervals.length}
                     />
                   )}
                   {viewLegend && (
diff --git a/packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts b/packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts
index 2190b7ea6..db3976bd9 100644
--- a/packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts
+++ b/packages/desktop-client/src/components/reports/spreadsheets/calculateLegend.ts
@@ -1,24 +1,26 @@
-// @ts-strict-ignore
 import {
+  type IntervalData,
   type ItemEntity,
-  type MonthData,
 } from 'loot-core/src/types/models/reports';
 
 import { theme } from '../../../style';
 import { getColorScale } from '../chart-theme';
 
 export function calculateLegend(
-  monthData: MonthData[],
+  intervalData: IntervalData[],
   calcDataFiltered: ItemEntity[],
   groupBy: string,
   graphType: string,
   balanceTypeOp: string,
 ) {
   const colorScale = getColorScale('qualitative');
-  const chooseData = groupBy === 'Interval' ? monthData : calcDataFiltered;
-  return chooseData.map((c, index) => {
+  const chooseData =
+    groupBy === 'Interval'
+      ? intervalData.map(c => c.date)
+      : calcDataFiltered.map(c => c.name);
+  return chooseData.map((name, index) => {
     return {
-      name: groupBy === 'Interval' ? c.date : c.name,
+      name,
       color:
         graphType === 'DonutGraph'
           ? colorScale[index % colorScale.length]
diff --git a/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts b/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts
index e0dc578a5..9d03d9ccf 100644
--- a/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts
+++ b/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts
@@ -24,6 +24,7 @@ import { recalculate } from './recalculate';
 export type createCustomSpreadsheetProps = {
   startDate: string;
   endDate: string;
+  interval: string;
   categories: { list: CategoryEntity[]; grouped: CategoryGroupEntity[] };
   selectedCategories: CategoryEntity[];
   conditions: RuleConditionEntity[];
@@ -43,6 +44,7 @@ export type createCustomSpreadsheetProps = {
 export function createCustomSpreadsheet({
   startDate,
   endDate,
+  interval,
   categories,
   selectedCategories,
   conditions = [],
@@ -92,6 +94,7 @@ export function createCustomSpreadsheet({
           'assets',
           startDate,
           endDate,
+          interval,
           selectedCategories,
           categoryFilter,
           conditionsOpKey,
@@ -103,6 +106,7 @@ export function createCustomSpreadsheet({
           'debts',
           startDate,
           endDate,
+          interval,
           selectedCategories,
           categoryFilter,
           conditionsOpKey,
@@ -111,20 +115,26 @@ export function createCustomSpreadsheet({
       ).then(({ data }) => data),
     ]);
 
-    const months = monthUtils.rangeInclusive(startDate, endDate);
+    const rangeInc =
+      interval === 'Monthly' ? 'rangeInclusive' : 'yearRangeInclusive';
+    const format = interval === 'Monthly' ? 'monthFromDate' : 'yearFromDate';
+    const intervals = monthUtils[rangeInc](
+      monthUtils[format](startDate),
+      monthUtils[format](endDate),
+    );
 
     let totalAssets = 0;
     let totalDebts = 0;
 
-    const monthData = months.reduce((arr, month) => {
-      let perMonthAssets = 0;
-      let perMonthDebts = 0;
+    const intervalData = intervals.reduce((arr, intervalItem) => {
+      let perIntervalAssets = 0;
+      let perIntervalDebts = 0;
       const stacked = {};
 
       groupByList.map(item => {
         let stackAmounts = 0;
 
-        const monthAssets = filterHiddenItems(
+        const intervalAssets = filterHiddenItems(
           item,
           assets,
           showOffBudget,
@@ -133,12 +143,13 @@ export function createCustomSpreadsheet({
         )
           .filter(
             asset =>
-              asset.date === month && asset[groupByLabel] === (item.id ?? null),
+              asset.date === intervalItem &&
+              asset[groupByLabel] === (item.id ?? null),
           )
           .reduce((a, v) => (a = a + v.amount), 0);
-        perMonthAssets += monthAssets;
+        perIntervalAssets += intervalAssets;
 
-        const monthDebts = filterHiddenItems(
+        const intervalDebts = filterHiddenItems(
           item,
           debts,
           showOffBudget,
@@ -147,16 +158,17 @@ export function createCustomSpreadsheet({
         )
           .filter(
             debt =>
-              debt.date === month && debt[groupByLabel] === (item.id ?? null),
+              debt.date === intervalItem &&
+              debt[groupByLabel] === (item.id ?? null),
           )
           .reduce((a, v) => (a = a + v.amount), 0);
-        perMonthDebts += monthDebts;
+        perIntervalDebts += intervalDebts;
 
         if (balanceTypeOp === 'totalAssets') {
-          stackAmounts += monthAssets;
+          stackAmounts += intervalAssets;
         }
         if (balanceTypeOp === 'totalDebts') {
-          stackAmounts += monthDebts;
+          stackAmounts += intervalDebts;
         }
         if (stackAmounts !== 0) {
           stacked[item.name] = integerToAmount(Math.abs(stackAmounts));
@@ -164,16 +176,19 @@ export function createCustomSpreadsheet({
 
         return null;
       });
-      totalAssets += perMonthAssets;
-      totalDebts += perMonthDebts;
+      totalAssets += perIntervalAssets;
+      totalDebts += perIntervalDebts;
 
       arr.push({
-        // eslint-disable-next-line rulesdir/typography
-        date: d.format(d.parseISO(`${month}-01`), "MMM ''yy"),
+        date:
+          interval === 'Monthly'
+            ? // eslint-disable-next-line rulesdir/typography
+              d.format(d.parseISO(`${intervalItem}-01`), "MMM ''yy")
+            : intervalItem,
         ...stacked,
-        totalDebts: integerToAmount(perMonthDebts),
-        totalAssets: integerToAmount(perMonthAssets),
-        totalTotals: integerToAmount(perMonthDebts + perMonthAssets),
+        totalDebts: integerToAmount(perIntervalDebts),
+        totalAssets: integerToAmount(perIntervalAssets),
+        totalTotals: integerToAmount(perIntervalDebts + perIntervalAssets),
       });
 
       return arr;
@@ -182,7 +197,7 @@ export function createCustomSpreadsheet({
     const calcData = groupByList.map(item => {
       const calc = recalculate({
         item,
-        months,
+        intervals,
         assets,
         debts,
         groupByLabel,
@@ -197,7 +212,7 @@ export function createCustomSpreadsheet({
     );
 
     const legend = calculateLegend(
-      monthData,
+      intervalData,
       calcDataFiltered,
       groupBy,
       graphType,
@@ -206,7 +221,7 @@ export function createCustomSpreadsheet({
 
     setData({
       data: calcDataFiltered,
-      monthData,
+      intervalData,
       legend,
       startDate,
       endDate,
diff --git a/packages/desktop-client/src/components/reports/spreadsheets/grouped-spreadsheet.ts b/packages/desktop-client/src/components/reports/spreadsheets/grouped-spreadsheet.ts
index f44cddf8c..57baa6009 100644
--- a/packages/desktop-client/src/components/reports/spreadsheets/grouped-spreadsheet.ts
+++ b/packages/desktop-client/src/components/reports/spreadsheets/grouped-spreadsheet.ts
@@ -16,6 +16,7 @@ import { recalculate } from './recalculate';
 export function createGroupedSpreadsheet({
   startDate,
   endDate,
+  interval,
   categories,
   selectedCategories,
   conditions = [],
@@ -52,6 +53,7 @@ export function createGroupedSpreadsheet({
           'assets',
           startDate,
           endDate,
+          interval,
           selectedCategories,
           categoryFilter,
           conditionsOpKey,
@@ -63,6 +65,7 @@ export function createGroupedSpreadsheet({
           'debts',
           startDate,
           endDate,
+          interval,
           selectedCategories,
           categoryFilter,
           conditionsOpKey,
@@ -71,19 +74,25 @@ export function createGroupedSpreadsheet({
       ).then(({ data }) => data),
     ]);
 
-    const months = monthUtils.rangeInclusive(startDate, endDate);
+    const rangeInc =
+      interval === 'Monthly' ? 'rangeInclusive' : 'yearRangeInclusive';
+    const format = interval === 'Monthly' ? 'monthFromDate' : 'yearFromDate';
+    const intervals = monthUtils[rangeInc](
+      monthUtils[format](startDate),
+      monthUtils[format](endDate),
+    );
 
     const groupedData: GroupedEntity[] = categoryGroup.map(
       group => {
         let totalAssets = 0;
         let totalDebts = 0;
 
-        const monthData = months.reduce((arr, month) => {
+        const intervalData = intervals.reduce((arr, intervalItem) => {
           let groupedAssets = 0;
           let groupedDebts = 0;
 
           group.categories.forEach(item => {
-            const monthAssets = filterHiddenItems(
+            const intervalAssets = filterHiddenItems(
               item,
               assets,
               showOffBudget,
@@ -92,12 +101,13 @@ export function createGroupedSpreadsheet({
             )
               .filter(
                 asset =>
-                  asset.date === month && asset.category === (item.id ?? null),
+                  asset.date === intervalItem &&
+                  asset.category === (item.id ?? null),
               )
               .reduce((a, v) => (a = a + v.amount), 0);
-            groupedAssets += monthAssets;
+            groupedAssets += intervalAssets;
 
-            const monthDebts = filterHiddenItems(
+            const intervalDebts = filterHiddenItems(
               item,
               debts,
               showOffBudget,
@@ -106,17 +116,18 @@ export function createGroupedSpreadsheet({
             )
               .filter(
                 debts =>
-                  debts.date === month && debts.category === (item.id ?? null),
+                  debts.date === intervalItem &&
+                  debts.category === (item.id ?? null),
               )
               .reduce((a, v) => (a = a + v.amount), 0);
-            groupedDebts += monthDebts;
+            groupedDebts += intervalDebts;
           });
 
           totalAssets += groupedAssets;
           totalDebts += groupedDebts;
 
           arr.push({
-            date: month,
+            date: intervalItem,
             totalAssets: integerToAmount(groupedAssets),
             totalDebts: integerToAmount(groupedDebts),
             totalTotals: integerToAmount(groupedDebts + groupedAssets),
@@ -128,7 +139,7 @@ export function createGroupedSpreadsheet({
         const stackedCategories = group.categories.map(item => {
           const calc = recalculate({
             item,
-            months,
+            intervals,
             assets,
             debts,
             groupByLabel: 'category',
@@ -145,7 +156,7 @@ export function createGroupedSpreadsheet({
           totalAssets: integerToAmount(totalAssets),
           totalDebts: integerToAmount(totalDebts),
           totalTotals: integerToAmount(totalAssets + totalDebts),
-          monthData,
+          intervalData,
           categories: stackedCategories.filter(i =>
             filterEmptyRows(showEmpty, i, balanceTypeOp),
           ),
diff --git a/packages/desktop-client/src/components/reports/spreadsheets/makeQuery.ts b/packages/desktop-client/src/components/reports/spreadsheets/makeQuery.ts
index 9598aac02..3b33809d7 100644
--- a/packages/desktop-client/src/components/reports/spreadsheets/makeQuery.ts
+++ b/packages/desktop-client/src/components/reports/spreadsheets/makeQuery.ts
@@ -5,11 +5,16 @@ export function makeQuery(
   name: string,
   startDate: string,
   endDate: string,
+  interval: string,
   selectedCategories: CategoryEntity[],
   categoryFilter: CategoryEntity[],
   conditionsOpKey: string,
   filters: unknown[],
 ) {
+  const intervalGroup =
+    interval === 'Monthly' ? { $month: '$date' } : { $year: '$date' };
+  const intervalFilter = interval === 'Monthly' ? '$month' : '$year';
+
   const query = q('transactions')
     //Apply Category_Selector
     .filter(
@@ -31,8 +36,8 @@ export function makeQuery(
     //Apply month range filters
     .filter({
       $and: [
-        { date: { $transform: '$month', $gte: startDate } },
-        { date: { $transform: '$month', $lte: endDate } },
+        { date: { $transform: intervalFilter, $gte: startDate } },
+        { date: { $transform: intervalFilter, $lte: endDate } },
       ],
     })
     //Show assets or debts
@@ -42,14 +47,14 @@ export function makeQuery(
 
   return query
     .groupBy([
-      { $month: '$date' },
+      intervalGroup,
       { $id: '$account' },
       { $id: '$payee' },
       { $id: '$category' },
       { $id: '$payee.transfer_acct.id' },
     ])
     .select([
-      { date: { $month: '$date' } },
+      { date: intervalGroup },
       { category: { $id: '$category.id' } },
       { categoryHidden: { $id: '$category.hidden' } },
       { categoryGroup: { $id: '$category.group.id' } },
diff --git a/packages/desktop-client/src/components/reports/spreadsheets/recalculate.ts b/packages/desktop-client/src/components/reports/spreadsheets/recalculate.ts
index ee914401c..a19ed5973 100644
--- a/packages/desktop-client/src/components/reports/spreadsheets/recalculate.ts
+++ b/packages/desktop-client/src/components/reports/spreadsheets/recalculate.ts
@@ -1,6 +1,4 @@
 // @ts-strict-ignore
-import * as d from 'date-fns';
-
 import { amountToInteger, integerToAmount } from 'loot-core/src/shared/util';
 
 import { type QueryDataEntity } from '../ReportOptions';
@@ -9,7 +7,7 @@ import { filterHiddenItems } from './filterHiddenItems';
 
 type recalculateProps = {
   item;
-  months: Array<string>;
+  intervals: Array<string>;
   assets: QueryDataEntity[];
   debts: QueryDataEntity[];
   groupByLabel: string;
@@ -20,7 +18,7 @@ type recalculateProps = {
 
 export function recalculate({
   item,
-  months,
+  intervals,
   assets,
   debts,
   groupByLabel,
@@ -30,10 +28,10 @@ export function recalculate({
 }: recalculateProps) {
   let totalAssets = 0;
   let totalDebts = 0;
-  const monthData = months.reduce((arr, month) => {
+  const intervalData = intervals.reduce((arr, intervalItem) => {
     const last = arr.length === 0 ? null : arr[arr.length - 1];
 
-    const monthAssets = filterHiddenItems(
+    const intervalAssets = filterHiddenItems(
       item,
       assets,
       showOffBudget,
@@ -42,12 +40,13 @@ export function recalculate({
     )
       .filter(
         asset =>
-          asset.date === month && asset[groupByLabel] === (item.id ?? null),
+          asset.date === intervalItem &&
+          asset[groupByLabel] === (item.id ?? null),
       )
       .reduce((a, v) => (a = a + v.amount), 0);
-    totalAssets += monthAssets;
+    totalAssets += intervalAssets;
 
-    const monthDebts = filterHiddenItems(
+    const intervalDebts = filterHiddenItems(
       item,
       debts,
       showOffBudget,
@@ -55,26 +54,23 @@ export function recalculate({
       showUncategorized,
     )
       .filter(
-        debt => debt.date === month && debt[groupByLabel] === (item.id ?? null),
+        debt =>
+          debt.date === intervalItem &&
+          debt[groupByLabel] === (item.id ?? null),
       )
       .reduce((a, v) => (a = a + v.amount), 0);
-    totalDebts += monthDebts;
-
-    const dateParse = d.parseISO(`${month}-01`);
+    totalDebts += intervalDebts;
 
     const change = last
-      ? monthAssets + monthDebts - amountToInteger(last.totalTotals)
+      ? intervalAssets + intervalDebts - amountToInteger(last.totalTotals)
       : 0;
 
     arr.push({
-      dateParse,
-      totalAssets: integerToAmount(monthAssets),
-      totalDebts: integerToAmount(monthDebts),
-      totalTotals: integerToAmount(monthAssets + monthDebts),
+      totalAssets: integerToAmount(intervalAssets),
+      totalDebts: integerToAmount(intervalDebts),
+      totalTotals: integerToAmount(intervalAssets + intervalDebts),
       change,
-      // eslint-disable-next-line rulesdir/typography
-      date: d.format(dateParse, "MMM ''yy"),
-      dateLookup: month,
+      dateLookup: intervalItem,
     });
 
     return arr;
@@ -86,6 +82,6 @@ export function recalculate({
     totalAssets: integerToAmount(totalAssets),
     totalDebts: integerToAmount(totalDebts),
     totalTotals: integerToAmount(totalAssets + totalDebts),
-    monthData,
+    intervalData,
   };
 }
diff --git a/packages/loot-core/src/types/models/reports.d.ts b/packages/loot-core/src/types/models/reports.d.ts
index b5cbcad9b..0b1af87a7 100644
--- a/packages/loot-core/src/types/models/reports.d.ts
+++ b/packages/loot-core/src/types/models/reports.d.ts
@@ -26,7 +26,7 @@ export interface CustomReportEntity {
 
 export interface GroupedEntity {
   data?: DataEntity[];
-  monthData: DataEntity[];
+  intervalData: DataEntity[];
   groupedData?: DataEntity[];
   legend?: LegendEntity[];
   startDate?: string;
@@ -44,13 +44,13 @@ type LegendEntity = {
 export type ItemEntity = {
   id: string;
   name: string;
-  monthData: MonthData[];
+  intervalData: IntervalData[];
   totalAssets: number;
   totalDebts: number;
   totalTotals: number;
 };
 
-export type MonthData = {
+export type IntervalData = {
   date: string;
   totalAssets: number;
   totalDebts: number;
@@ -61,15 +61,15 @@ export interface DataEntity {
   id: string;
   name: string;
   date?: string;
-  monthData: MonthData[];
+  intervalData: IntervalData[];
   categories?: ItemEntity[];
   totalAssets: number;
   totalDebts: number;
   totalTotals: number;
 }
 
-export type Month = {
-  month: string;
+export type Interval = {
+  interval: string;
 };
 
 export interface CustomReportData {
diff --git a/upcoming-release-notes/2479.md b/upcoming-release-notes/2479.md
new file mode 100644
index 000000000..4bb65c351
--- /dev/null
+++ b/upcoming-release-notes/2479.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [carkom]
+---
+
+Changing custom reports variable naming from "months" to "interval" so it's less confusing when adding new intervals
-- 
GitLab