diff --git a/packages/desktop-client/src/components/reports/ReportOptions.ts b/packages/desktop-client/src/components/reports/ReportOptions.ts index e96b14c0f32..1ef7d652e9d 100644 --- a/packages/desktop-client/src/components/reports/ReportOptions.ts +++ b/packages/desktop-client/src/components/reports/ReportOptions.ts @@ -232,49 +232,32 @@ export type QueryDataEntity = { amount: number; }; +type UncategorizedId = 'off_budget' | 'transfer' | 'other' | 'all'; + export type UncategorizedEntity = Pick< CategoryEntity, - 'id' | 'name' | 'hidden' + 'id' | 'name' | 'hidden' | 'cat_group' > & { - /* - When looking at uncategorized and hidden transactions we - need a way to group them. To do this we give them a unique - uncategorized_id. We also need a way to filter the - transctions from our query. For this we use the 3 variables - below. - */ - uncategorized_id?: string; - is_off_budget?: boolean; - is_transfer?: boolean; - has_category?: boolean; + uncategorized_id?: UncategorizedId; }; const uncategorizedCategory: UncategorizedEntity = { id: '', name: 'Uncategorized', - uncategorized_id: '1', + uncategorized_id: 'other', hidden: false, - is_off_budget: false, - is_transfer: false, - has_category: false, }; const transferCategory: UncategorizedEntity = { id: '', name: 'Transfers', - uncategorized_id: '2', + uncategorized_id: 'transfer', hidden: false, - is_off_budget: false, - is_transfer: true, - has_category: false, }; const offBudgetCategory: UncategorizedEntity = { id: '', name: 'Off Budget', - uncategorized_id: '3', + uncategorized_id: 'off_budget', hidden: false, - is_off_budget: true, - is_transfer: false, - has_category: true, }; type UncategorizedGroupEntity = Pick< @@ -282,12 +265,14 @@ type UncategorizedGroupEntity = Pick< 'name' | 'id' | 'hidden' > & { categories?: UncategorizedEntity[]; + uncategorized_id?: UncategorizedId; }; const uncategorizedGroup: UncategorizedGroupEntity = { name: 'Uncategorized & Off Budget', id: 'uncategorized', hidden: false, + uncategorized_id: 'all', categories: [uncategorizedCategory, transferCategory, offBudgetCategory], }; @@ -302,7 +287,7 @@ export const categoryLists = (categories: { const catGroupB = categories.grouped.find(f => f.id === b.cat_group); //initial check that both a and b have a sort_order and category group return a.sort_order && b.sort_order && catGroupA && catGroupB - ? /*sorting by "is_income" because sort_order for this group is + ? /*sorting by "is_income" because sort_order for this group is separate from other groups*/ Number(catGroupA.is_income) - Number(catGroupB.is_income) || //Next, sorting by group sort_order @@ -342,7 +327,12 @@ export const groupBySelections = ( break; case 'Group': groupByList = categoryGroup.map(group => { - return { id: group.id, name: group.name, hidden: group.hidden }; + return { + ...group, + id: group.id, + name: group.name, + hidden: group.hidden, + }; }); groupByLabel = 'categoryGroup'; break; 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 1afe00b3a40..bbfec733876 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts +++ b/packages/desktop-client/src/components/reports/spreadsheets/custom-spreadsheet.ts @@ -145,6 +145,8 @@ export function createCustomSpreadsheet({ let netAssets = 0; let netDebts = 0; + const groupsByCategory = + groupByLabel === 'category' || groupByLabel === 'categoryGroup'; const intervalData = intervals.reduce( (arr: IntervalEntity[], intervalItem, index) => { let perIntervalAssets = 0; @@ -163,11 +165,13 @@ export function createCustomSpreadsheet({ showOffBudget, showHiddenCategories, showUncategorized, + groupsByCategory, ) .filter( asset => asset.date === intervalItem && - asset[groupByLabel] === (item.id ?? null), + (asset[groupByLabel] === (item.id ?? null) || + (item.uncategorized_id && groupsByCategory)), ) .reduce((a, v) => (a = a + v.amount), 0); perIntervalAssets += intervalAssets; @@ -178,11 +182,13 @@ export function createCustomSpreadsheet({ showOffBudget, showHiddenCategories, showUncategorized, + groupsByCategory, ) .filter( debt => debt.date === intervalItem && - debt[groupByLabel] === (item.id ?? null), + (debt[groupByLabel] === (item.id ?? null) || + (item.uncategorized_id && groupsByCategory)), ) .reduce((a, v) => (a = a + v.amount), 0); perIntervalDebts += intervalDebts; diff --git a/packages/desktop-client/src/components/reports/spreadsheets/filterHiddenItems.ts b/packages/desktop-client/src/components/reports/spreadsheets/filterHiddenItems.ts index 538f5c599d1..492e8def099 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/filterHiddenItems.ts +++ b/packages/desktop-client/src/components/reports/spreadsheets/filterHiddenItems.ts @@ -9,43 +9,42 @@ export function filterHiddenItems( showOffBudget?: boolean, showHiddenCategories?: boolean, showUncategorized?: boolean, + groupByCategory?: boolean, ) { const showHide = data - .filter(e => - !showHiddenCategories - ? e.categoryHidden === false && e.categoryGroupHidden === false - : true, + .filter( + e => + showHiddenCategories || + (e.categoryHidden === false && e.categoryGroupHidden === false), ) - .filter(f => - showOffBudget - ? showUncategorized - ? //true,true - true - : //true,false - f.category !== null || - f.accountOffBudget !== false || - f.transferAccount !== null - : showUncategorized - ? //false, true - f.accountOffBudget === false && - (f.transferAccount === null || f.category !== null) - : //false false - f.category !== null && f.accountOffBudget === false, + .filter(e => showOffBudget || e.accountOffBudget === false) + .filter( + e => + showUncategorized || e.category !== null || e.accountOffBudget === true, ); return showHide.filter(query => { - if (!item.uncategorized_id) { - return true; - } + if (!groupByCategory) return true; + + const hasCategory = !!query.category; + const isOffBudget = query.accountOffBudget; + const isTransfer = !!query.transferAccount; - const isTransfer = item.is_transfer - ? query.transferAccount - : !query.transferAccount; - const isHidden = item.has_category ? true : !query.category; - const isOffBudget = item.is_off_budget - ? query.accountOffBudget - : !query.accountOffBudget; + if (hasCategory && !isOffBudget) { + return item.uncategorized_id == null; + } - return isTransfer && isHidden && isOffBudget; + switch (item.uncategorized_id) { + case 'off_budget': + return isOffBudget; + case 'transfer': + return isTransfer && !isOffBudget; + case 'other': + return !isOffBudget && !isTransfer; + case 'all': + return true; + default: + return false; + } }); } 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 26b7e943921..799f0589ba1 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/grouped-spreadsheet.ts +++ b/packages/desktop-client/src/components/reports/spreadsheets/grouped-spreadsheet.ts @@ -2,11 +2,7 @@ import { runQuery } from 'loot-core/src/client/query-helpers'; import { type useSpreadsheet } from 'loot-core/src/client/SpreadsheetProvider'; import { send } from 'loot-core/src/platform/client/fetch'; import * as monthUtils from 'loot-core/src/shared/months'; -import { integerToAmount } from 'loot-core/src/shared/util'; -import { - type IntervalEntity, - type GroupedEntity, -} from 'loot-core/src/types/models/reports'; +import { type GroupedEntity } from 'loot-core/src/types/models/reports'; import { categoryLists, @@ -16,7 +12,6 @@ import { import { type createCustomSpreadsheetProps } from './custom-spreadsheet'; import { filterEmptyRows } from './filterEmptyRows'; -import { filterHiddenItems } from './filterHiddenItems'; import { makeQuery } from './makeQuery'; import { recalculate } from './recalculate'; @@ -98,85 +93,18 @@ export function createGroupedSpreadsheet({ const groupedData: GroupedEntity[] = categoryGroup.map( group => { - let totalAssets = 0; - let totalDebts = 0; - let netAssets = 0; - let netDebts = 0; - - const intervalData = intervals.reduce( - (arr: IntervalEntity[], intervalItem) => { - let groupedAssets = 0; - let groupedDebts = 0; - let groupedNetAssets = 0; - let groupedNetDebts = 0; - let groupedTotals = 0; - - if (!group.categories) { - return []; - } - - group.categories.forEach(item => { - const intervalAssets = filterHiddenItems( - item, - assets, - showOffBudget, - showHiddenCategories, - showUncategorized, - ) - .filter( - asset => - asset.date === intervalItem && - asset.category === (item.id ?? null), - ) - .reduce((a, v) => (a = a + v.amount), 0); - groupedAssets += intervalAssets; - - const intervalDebts = filterHiddenItems( - item, - debts, - showOffBudget, - showHiddenCategories, - showUncategorized, - ) - .filter( - debts => - debts.date === intervalItem && - debts.category === (item.id ?? null), - ) - .reduce((a, v) => (a = a + v.amount), 0); - groupedDebts += intervalDebts; - - const intervalTotals = intervalAssets + intervalDebts; - - groupedNetAssets = - intervalTotals > 0 - ? groupedNetAssets + intervalTotals - : groupedNetAssets; - groupedNetDebts = - intervalTotals < 0 - ? groupedNetDebts + intervalTotals - : groupedNetDebts; - groupedTotals += intervalTotals; - }); - - totalAssets += groupedAssets; - totalDebts += groupedDebts; - netAssets += groupedNetAssets; - netDebts += groupedNetDebts; - - arr.push({ - date: intervalItem, - totalAssets: integerToAmount(groupedAssets), - totalDebts: integerToAmount(groupedDebts), - netAssets: integerToAmount(groupedNetAssets), - netDebts: integerToAmount(groupedNetDebts), - totalTotals: integerToAmount(groupedTotals), - }); - - return arr; - }, - [], - ); + const grouped = recalculate({ + item: group, + intervals, + assets, + debts, + groupByLabel: 'categoryGroup', + showOffBudget, + showHiddenCategories, + showUncategorized, + startDate, + endDate, + }); const stackedCategories = group.categories && @@ -197,14 +125,7 @@ export function createGroupedSpreadsheet({ }); return { - id: group.id || '', - name: group.name, - totalAssets: integerToAmount(totalAssets), - totalDebts: integerToAmount(totalDebts), - netAssets: integerToAmount(netAssets), - netDebts: integerToAmount(netDebts), - totalTotals: integerToAmount(totalAssets + totalDebts), - intervalData, + ...grouped, categories: stackedCategories && stackedCategories.filter(i => diff --git a/packages/desktop-client/src/components/reports/spreadsheets/recalculate.ts b/packages/desktop-client/src/components/reports/spreadsheets/recalculate.ts index d0dc1e1c9f9..c0e21f37fa5 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/recalculate.ts +++ b/packages/desktop-client/src/components/reports/spreadsheets/recalculate.ts @@ -43,17 +43,21 @@ export function recalculate({ (arr: IntervalEntity[], intervalItem, index) => { const last = arr.length === 0 ? null : arr[arr.length - 1]; + const groupsByCategory = + groupByLabel === 'category' || groupByLabel === 'categoryGroup'; const intervalAssets = filterHiddenItems( item, assets, showOffBudget, showHiddenCategories, showUncategorized, + groupsByCategory, ) .filter( asset => asset.date === intervalItem && - asset[groupByLabel] === (item.id ?? null), + (asset[groupByLabel] === (item.id ?? null) || + (item.uncategorized_id && groupsByCategory)), ) .reduce((a, v) => (a = a + v.amount), 0); totalAssets += intervalAssets; @@ -64,11 +68,13 @@ export function recalculate({ showOffBudget, showHiddenCategories, showUncategorized, + groupsByCategory, ) .filter( debt => debt.date === intervalItem && - debt[groupByLabel] === (item.id ?? null), + (debt[groupByLabel] === (item.id ?? null) || + (item.uncategorized_id && groupsByCategory)), ) .reduce((a, v) => (a = a + v.amount), 0); totalDebts += intervalDebts; diff --git a/upcoming-release-notes/3633.md b/upcoming-release-notes/3633.md new file mode 100644 index 00000000000..7b8a748adce --- /dev/null +++ b/upcoming-release-notes/3633.md @@ -0,0 +1,6 @@ +--- +category: Bugfix +authors: [UnderKoen] +--- + +Fix 'show uncategorized' and 'show off budget' for custom reports