mirror of
https://github.com/actualbudget/actual.git
synced 2026-03-09 03:32:54 -05:00
* fix: Custom Report Total Mode not showing offbudget transactions #3627 * chore: release note * chore: remove debug logging * fix: table grouped report
This commit is contained in:
committed by
GitHub
parent
4d89a9b86a
commit
10ca29e1e9
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
if (hasCategory && !isOffBudget) {
|
||||
return item.uncategorized_id == null;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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 =>
|
||||
|
||||
@@ -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;
|
||||
|
||||
6
upcoming-release-notes/3633.md
Normal file
6
upcoming-release-notes/3633.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Bugfix
|
||||
authors: [UnderKoen]
|
||||
---
|
||||
|
||||
Fix 'show uncategorized' and 'show off budget' for custom reports
|
||||
Reference in New Issue
Block a user