mirror of
https://github.com/mayswind/ezbookkeeping.git
synced 2026-05-18 16:54:25 +08:00
reconciliation statement page / dialog supports account balance trends chart (#184)
This commit is contained in:
@@ -0,0 +1,183 @@
|
|||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
|
||||||
|
import {
|
||||||
|
type UnixTimeRange,
|
||||||
|
type YearUnixTime,
|
||||||
|
type YearQuarterUnixTime,
|
||||||
|
type YearMonthUnixTime,
|
||||||
|
YearMonthDayUnixTime,
|
||||||
|
} from '@/core/datetime.ts';
|
||||||
|
import type { FiscalYearUnixTime } from '@/core/fiscalyear.ts';
|
||||||
|
import { ChartDateAggregationType } from '@/core/statistics.ts';
|
||||||
|
import type { TransactionReconciliationStatementResponseItem } from '@/models/transaction.ts';
|
||||||
|
|
||||||
|
import { isDefined, isArray } from '@/lib/common.ts';
|
||||||
|
import {
|
||||||
|
getYearAndMonthFromUnixTime,
|
||||||
|
getYearFirstUnixTimeBySpecifiedUnixTime,
|
||||||
|
getQuarterFirstUnixTimeBySpecifiedUnixTime,
|
||||||
|
getMonthFirstUnixTimeBySpecifiedUnixTime,
|
||||||
|
getDayFirstUnixTimeBySpecifiedUnixTime,
|
||||||
|
getAllDaysStartAndEndUnixTimes,
|
||||||
|
getFiscalYearStartUnixTime
|
||||||
|
} from '@/lib/datetime.ts';
|
||||||
|
import { getAllDateRangesByYearMonthRange } from '@/lib/statistics.ts';
|
||||||
|
|
||||||
|
export interface AccountBalanceUnixTimeAndBalanceRange extends UnixTimeRange {
|
||||||
|
minUnixTimeBalance: number;
|
||||||
|
maxUnixTimeBalance: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AccountBalanceTrendsChartItem {
|
||||||
|
displayDate: string;
|
||||||
|
amount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CommonAccountBalanceTrendsChartProps {
|
||||||
|
items: TransactionReconciliationStatementResponseItem[] | undefined;
|
||||||
|
dateAggregationType?: number;
|
||||||
|
fiscalYearStart: number;
|
||||||
|
accountCurrency: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useAccountBalanceTrendsChartBase(props: CommonAccountBalanceTrendsChartProps) {
|
||||||
|
const { formatUnixTimeToShortDate, formatUnixTimeToShortYear, formatUnixTimeToShortYearMonth, formatUnixTimeToYearQuarter, formatUnixTimeToFiscalYear } = useI18n();
|
||||||
|
|
||||||
|
const dataDateRange = computed<AccountBalanceUnixTimeAndBalanceRange | null>(() => {
|
||||||
|
if (!props.items || props.items.length < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let minUnixTime = Number.MAX_SAFE_INTEGER, maxUnixTime = 0;
|
||||||
|
let minUnixTimeBalance = 0, maxUnixTimeBalance = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < props.items.length; i++) {
|
||||||
|
const item = props.items[i];
|
||||||
|
|
||||||
|
if (item.time < minUnixTime) {
|
||||||
|
minUnixTime = item.time;
|
||||||
|
minUnixTimeBalance = item.accountBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.time > maxUnixTime) {
|
||||||
|
maxUnixTime = item.time;
|
||||||
|
maxUnixTimeBalance = item.accountBalance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minUnixTime >= Number.MAX_SAFE_INTEGER || maxUnixTime <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
minUnixTime: minUnixTime,
|
||||||
|
maxUnixTime: maxUnixTime,
|
||||||
|
minUnixTimeBalance: minUnixTimeBalance,
|
||||||
|
maxUnixTimeBalance: maxUnixTimeBalance
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const allDateRanges = computed<YearUnixTime[] | FiscalYearUnixTime[] | YearQuarterUnixTime[] | YearMonthUnixTime[] | YearMonthDayUnixTime[]>(() => {
|
||||||
|
if (!dataDateRange.value) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isDefined(props.dateAggregationType)) {
|
||||||
|
return getAllDaysStartAndEndUnixTimes(dataDateRange.value.minUnixTime, dataDateRange.value.maxUnixTime);
|
||||||
|
} else {
|
||||||
|
const startYearMonth = getYearAndMonthFromUnixTime(dataDateRange.value.minUnixTime);
|
||||||
|
const endYearMonth = getYearAndMonthFromUnixTime(dataDateRange.value.maxUnixTime);
|
||||||
|
return getAllDateRangesByYearMonthRange(startYearMonth, endYearMonth, props.fiscalYearStart, props.dateAggregationType);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const allDataItems = computed<AccountBalanceTrendsChartItem[]>(() => {
|
||||||
|
const ret: AccountBalanceTrendsChartItem[] = [];
|
||||||
|
|
||||||
|
if (!dataDateRange.value || !allDateRanges.value || allDateRanges.value.length < 1 || !props.items || props.items.length < 1) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dayDataItemsMap: Record<number, TransactionReconciliationStatementResponseItem[]> = {};
|
||||||
|
|
||||||
|
for (let i = 0; i < props.items.length; i++) {
|
||||||
|
const dateItem = props.items[i];
|
||||||
|
let dateRangeMinUnixTime = 0;
|
||||||
|
|
||||||
|
if (props.dateAggregationType === ChartDateAggregationType.Year.type) {
|
||||||
|
dateRangeMinUnixTime = getYearFirstUnixTimeBySpecifiedUnixTime(dateItem.time);
|
||||||
|
} else if (props.dateAggregationType === ChartDateAggregationType.FiscalYear.type) {
|
||||||
|
dateRangeMinUnixTime = getFiscalYearStartUnixTime(dateItem.time, props.fiscalYearStart);
|
||||||
|
} else if (props.dateAggregationType === ChartDateAggregationType.Quarter.type) {
|
||||||
|
dateRangeMinUnixTime = getQuarterFirstUnixTimeBySpecifiedUnixTime(dateItem.time);
|
||||||
|
} else if (props.dateAggregationType === ChartDateAggregationType.Month.type) {
|
||||||
|
dateRangeMinUnixTime = getMonthFirstUnixTimeBySpecifiedUnixTime(dateItem.time);
|
||||||
|
} else {
|
||||||
|
dateRangeMinUnixTime = getDayFirstUnixTimeBySpecifiedUnixTime(dateItem.time);
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataItems: TransactionReconciliationStatementResponseItem[] = dayDataItemsMap[dateRangeMinUnixTime] || [];
|
||||||
|
dataItems.push(dateItem);
|
||||||
|
|
||||||
|
dayDataItemsMap[dateRangeMinUnixTime] = dataItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastAmount = dataDateRange.value.minUnixTimeBalance;
|
||||||
|
|
||||||
|
for (let i = 0; i < allDateRanges.value.length; i++) {
|
||||||
|
const dateRange = allDateRanges.value[i];
|
||||||
|
const dataItems = dayDataItemsMap[dateRange.minUnixTime];
|
||||||
|
|
||||||
|
let displayDate = '';
|
||||||
|
|
||||||
|
if (props.dateAggregationType === ChartDateAggregationType.Year.type) {
|
||||||
|
displayDate = formatUnixTimeToShortYear(dateRange.minUnixTime);
|
||||||
|
} else if (props.dateAggregationType === ChartDateAggregationType.FiscalYear.type) {
|
||||||
|
displayDate = formatUnixTimeToFiscalYear(dateRange.minUnixTime);
|
||||||
|
} else if (props.dateAggregationType === ChartDateAggregationType.Quarter.type) {
|
||||||
|
displayDate = formatUnixTimeToYearQuarter(dateRange.minUnixTime);
|
||||||
|
} else if (props.dateAggregationType === ChartDateAggregationType.Month.type) {
|
||||||
|
displayDate = formatUnixTimeToShortYearMonth(dateRange.minUnixTime);
|
||||||
|
} else {
|
||||||
|
displayDate = formatUnixTimeToShortDate(dateRange.minUnixTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isArray(dataItems)) {
|
||||||
|
let lastUnixTime = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < dataItems.length; i++) {
|
||||||
|
const dataItem = dataItems[i];
|
||||||
|
|
||||||
|
if (dataItem.time >= lastUnixTime) {
|
||||||
|
lastUnixTime = dataItem.time;
|
||||||
|
lastAmount = dataItem.accountBalance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.push({
|
||||||
|
displayDate: displayDate,
|
||||||
|
amount: lastAmount
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
|
||||||
|
const allDisplayDateRanges = computed<string[]>(() => {
|
||||||
|
if (!allDataItems.value || allDataItems.value.length < 1) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return allDataItems.value.map(item => item.displayDate);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
// computed states
|
||||||
|
allDateRanges,
|
||||||
|
allDataItems,
|
||||||
|
allDisplayDateRanges
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
<template>
|
||||||
|
<v-chart autoresize class="account-balance-trends-chart-container" :class="{ 'transition-in': skeleton }" :option="chartOptions"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useTheme } from 'vuetify';
|
||||||
|
import type { CallbackDataParams } from 'echarts/types/dist/shared';
|
||||||
|
|
||||||
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
import { type CommonAccountBalanceTrendsChartProps, useAccountBalanceTrendsChartBase } from '@/components/base/AccountBalanceTrendsChartBase.ts'
|
||||||
|
|
||||||
|
import type { ColorValue } from '@/core/color.ts';
|
||||||
|
import { ThemeType } from '@/core/theme.ts';
|
||||||
|
import { TrendChartType } from '@/core/statistics.ts';
|
||||||
|
import { DEFAULT_CHART_COLORS } from '@/consts/color.ts';
|
||||||
|
|
||||||
|
interface DesktopAccountBalanceTrendsChartProps extends CommonAccountBalanceTrendsChartProps {
|
||||||
|
legendName: string;
|
||||||
|
skeleton?: boolean;
|
||||||
|
type?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AccountBalanceTrendsChartDataItem {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
itemStyle: {
|
||||||
|
color: ColorValue;
|
||||||
|
};
|
||||||
|
selected: boolean;
|
||||||
|
type: string;
|
||||||
|
areaStyle?: object;
|
||||||
|
stack: string;
|
||||||
|
animation: boolean;
|
||||||
|
data: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<DesktopAccountBalanceTrendsChartProps>();
|
||||||
|
|
||||||
|
const theme = useTheme();
|
||||||
|
const { formatAmountWithCurrency } = useI18n();
|
||||||
|
const { allDataItems, allDisplayDateRanges } = useAccountBalanceTrendsChartBase(props);
|
||||||
|
|
||||||
|
const isDarkMode = computed<boolean>(() => theme.global.name.value === ThemeType.Dark);
|
||||||
|
|
||||||
|
const allSeries = computed<AccountBalanceTrendsChartDataItem[]>(() => {
|
||||||
|
const series: AccountBalanceTrendsChartDataItem = {
|
||||||
|
id: 'accountBalance',
|
||||||
|
name: props.legendName,
|
||||||
|
itemStyle: {
|
||||||
|
color: `#${DEFAULT_CHART_COLORS[0]}`
|
||||||
|
},
|
||||||
|
selected: true,
|
||||||
|
type: 'line',
|
||||||
|
stack: 'a',
|
||||||
|
animation: !props.skeleton,
|
||||||
|
data: []
|
||||||
|
};
|
||||||
|
|
||||||
|
if (props.type === TrendChartType.Area.type) {
|
||||||
|
series.areaStyle = {};
|
||||||
|
} else if (props.type === TrendChartType.Column.type) {
|
||||||
|
series.type = 'bar';
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < allDataItems.value.length; i++) {
|
||||||
|
const item = allDataItems.value[i];
|
||||||
|
series.data.push(item.amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [series];
|
||||||
|
});
|
||||||
|
|
||||||
|
const yAxisWidth = computed<number>(() => {
|
||||||
|
let maxValue = Number.MIN_SAFE_INTEGER;
|
||||||
|
let minValue = Number.MAX_SAFE_INTEGER;
|
||||||
|
let width = 90;
|
||||||
|
|
||||||
|
if (!allSeries.value || !allSeries.value.length) {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < allSeries.value.length; i++) {
|
||||||
|
for (let j = 0; j < allSeries.value[i].data.length; j++) {
|
||||||
|
const value = allSeries.value[i].data[j];
|
||||||
|
|
||||||
|
if (value > maxValue) {
|
||||||
|
maxValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value < minValue) {
|
||||||
|
minValue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxValueText = formatAmountWithCurrency(maxValue, props.accountCurrency);
|
||||||
|
const minValueText = formatAmountWithCurrency(minValue, props.accountCurrency);
|
||||||
|
const maxLengthText = maxValueText.length > minValueText.length ? maxValueText : minValueText;
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const context = canvas.getContext('2d');
|
||||||
|
|
||||||
|
if (context) {
|
||||||
|
context.font = '12px Arial';
|
||||||
|
|
||||||
|
const textMetrics = context.measureText(maxLengthText);
|
||||||
|
const actualWidth = Math.round(textMetrics.width) + 20;
|
||||||
|
|
||||||
|
if (actualWidth >= 200) {
|
||||||
|
width = 200;
|
||||||
|
} if (actualWidth > 90) {
|
||||||
|
width = actualWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return width;
|
||||||
|
});
|
||||||
|
|
||||||
|
const chartOptions = computed<object>(() => {
|
||||||
|
return {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'cross',
|
||||||
|
label: {
|
||||||
|
backgroundColor: isDarkMode.value ? '#333' : '#fff',
|
||||||
|
color: isDarkMode.value ? '#eee' : '#333'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
backgroundColor: isDarkMode.value ? '#333' : '#fff',
|
||||||
|
borderColor: isDarkMode.value ? '#333' : '#fff',
|
||||||
|
textStyle: {
|
||||||
|
color: isDarkMode.value ? '#eee' : '#333'
|
||||||
|
},
|
||||||
|
formatter: (params: CallbackDataParams[]) => {
|
||||||
|
const amount = params[0].data as number;
|
||||||
|
const value = formatAmountWithCurrency(amount, props.accountCurrency);
|
||||||
|
|
||||||
|
return `${params[0].name}<br/>`
|
||||||
|
+ '<div><span class="chart-pointer" style="background-color: #' + DEFAULT_CHART_COLORS[0] + '"></span>'
|
||||||
|
+ `<span>${props.legendName}</span><span style="margin-left: 20px; float: right">${value}</span><br/>`
|
||||||
|
+ '</div>';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: yAxisWidth.value,
|
||||||
|
right: 20
|
||||||
|
},
|
||||||
|
xAxis: [
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
data: allDisplayDateRanges.value
|
||||||
|
}
|
||||||
|
],
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
type: 'value',
|
||||||
|
axisLabel: {
|
||||||
|
formatter: (value: string) => {
|
||||||
|
return formatAmountWithCurrency(value, props.accountCurrency);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisPointer: {
|
||||||
|
label: {
|
||||||
|
formatter: (params: CallbackDataParams) => {
|
||||||
|
return formatAmountWithCurrency(Math.floor(params.value as number), props.accountCurrency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
series: allSeries.value
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.account-balance-trends-chart-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 400px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
<template>
|
||||||
|
<f7-list class="skeleton-text margin-top-half" media-list v-if="loading">
|
||||||
|
<f7-list-item class="account-balance-trends-list-item" title="Date Range" after="0.00 USD"
|
||||||
|
:key="itemIdx" v-for="itemIdx in [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]">
|
||||||
|
<template #media>
|
||||||
|
<f7-icon f7="app_fill"></f7-icon>
|
||||||
|
</template>
|
||||||
|
<template #inner>
|
||||||
|
<div class="display-flex padding-top-half">
|
||||||
|
<div class="account-balance-percent-line width-100">
|
||||||
|
<f7-progressbar :progress="0"></f7-progressbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</f7-list-item>
|
||||||
|
</f7-list>
|
||||||
|
|
||||||
|
<f7-list v-else-if="!loading && (!allVirtualListItems || !allVirtualListItems.length)">
|
||||||
|
<f7-list-item :title="tt('No transaction data')"></f7-list-item>
|
||||||
|
</f7-list>
|
||||||
|
|
||||||
|
<f7-list class="margin-top-half" media-list virtual-list :virtual-list-params="{ items: allVirtualListItems, renderExternal, height: 'auto' }"
|
||||||
|
:key="`account-balance-trends-${dateAggregationType}`"
|
||||||
|
v-else-if="!loading && allVirtualListItems && allVirtualListItems.length > 0">
|
||||||
|
<ul>
|
||||||
|
<f7-list-item class="account-balance-trends-list-item"
|
||||||
|
:key="item.index"
|
||||||
|
:style="`top: ${virtualDataItems.topPosition}px`"
|
||||||
|
:virtual-list-index="item.index"
|
||||||
|
:title="item.displayDate"
|
||||||
|
:after="formatAmountWithCurrency(item.amount, accountCurrency)"
|
||||||
|
v-for="item in virtualDataItems.items"
|
||||||
|
>
|
||||||
|
<template #media>
|
||||||
|
<f7-icon f7="calendar"></f7-icon>
|
||||||
|
</template>
|
||||||
|
<template #inner>
|
||||||
|
<div class="display-flex padding-top-half">
|
||||||
|
<div class="account-balance-percent-line" :style="{ 'width': item.percent + '%' }">
|
||||||
|
<f7-progressbar :progress="100" :style="{ '--f7-progressbar-progress-color': (item.color ? item.color : '') } "></f7-progressbar>
|
||||||
|
</div>
|
||||||
|
<div class="account-balance-percent-line" :style="{ 'width': (100.0 - item.percent) + '%' }"
|
||||||
|
v-if="item.percent < 100.0">
|
||||||
|
<f7-progressbar :progress="0"></f7-progressbar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</f7-list-item>
|
||||||
|
</ul>
|
||||||
|
</f7-list>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
|
||||||
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
import {
|
||||||
|
type AccountBalanceTrendsChartItem,
|
||||||
|
type CommonAccountBalanceTrendsChartProps,
|
||||||
|
useAccountBalanceTrendsChartBase
|
||||||
|
} from '@/components/base/AccountBalanceTrendsChartBase.ts'
|
||||||
|
|
||||||
|
import type { ColorValue } from '@/core/color.ts';
|
||||||
|
import { DEFAULT_CHART_COLORS } from '@/consts/color.ts';
|
||||||
|
|
||||||
|
interface MobileAccountBalanceTrendsChartItem extends AccountBalanceTrendsChartItem {
|
||||||
|
index: number;
|
||||||
|
percent: number;
|
||||||
|
color: ColorValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MobileAccountBalanceTrendsChartProps extends CommonAccountBalanceTrendsChartProps {
|
||||||
|
loading?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MobileAccountBalanceTrendsChartVirtualListData {
|
||||||
|
items: MobileAccountBalanceTrendsChartItem[],
|
||||||
|
topPosition: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<MobileAccountBalanceTrendsChartProps>();
|
||||||
|
|
||||||
|
const { tt, formatAmountWithCurrency } = useI18n();
|
||||||
|
const { allDataItems } = useAccountBalanceTrendsChartBase(props);
|
||||||
|
|
||||||
|
const virtualDataItems = ref<MobileAccountBalanceTrendsChartVirtualListData>({
|
||||||
|
items: [],
|
||||||
|
topPosition: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
const allVirtualListItems = computed<MobileAccountBalanceTrendsChartItem[]>(() => {
|
||||||
|
const ret: MobileAccountBalanceTrendsChartItem[] = [];
|
||||||
|
let maxAmount = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < allDataItems.value.length; i++) {
|
||||||
|
const dataItem = allDataItems.value[i];
|
||||||
|
|
||||||
|
if (dataItem.amount > maxAmount) {
|
||||||
|
maxAmount = dataItem.amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalDataItem: MobileAccountBalanceTrendsChartItem = {
|
||||||
|
index: i,
|
||||||
|
displayDate: dataItem.displayDate,
|
||||||
|
amount: dataItem.amount,
|
||||||
|
color: `#${DEFAULT_CHART_COLORS[0]}`,
|
||||||
|
percent: 0.0
|
||||||
|
};
|
||||||
|
|
||||||
|
ret.push(finalDataItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < ret.length; i++) {
|
||||||
|
if (maxAmount > 0 && ret[i].amount > 0) {
|
||||||
|
ret[i].percent = 100.0 * ret[i].amount / maxAmount;
|
||||||
|
} else {
|
||||||
|
ret[i].percent = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
|
||||||
|
function renderExternal(vl: unknown, vlData: MobileAccountBalanceTrendsChartVirtualListData): void {
|
||||||
|
virtualDataItems.value = vlData;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.account-balance-trends-list-item .account-balance-percent-line {
|
||||||
|
--f7-progressbar-bg-color: #f8f8f8;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -20,6 +20,12 @@ export interface YearMonthRange {
|
|||||||
readonly endYearMonth: Year0BasedMonth;
|
readonly endYearMonth: Year0BasedMonth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface YearMonthDay {
|
||||||
|
readonly year: number;
|
||||||
|
readonly month: number; // 1-based (1 = January, 12 = December)
|
||||||
|
readonly day: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface TimeRange {
|
export interface TimeRange {
|
||||||
readonly minTime: number;
|
readonly minTime: number;
|
||||||
readonly maxTime: number;
|
readonly maxTime: number;
|
||||||
@@ -136,6 +142,26 @@ export class YearMonthUnixTime implements Year0BasedMonth, UnixTimeRange {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class YearMonthDayUnixTime implements YearMonthDay, UnixTimeRange {
|
||||||
|
public readonly year: number;
|
||||||
|
public readonly month: number;
|
||||||
|
public readonly day: number;
|
||||||
|
public readonly minUnixTime: number;
|
||||||
|
public readonly maxUnixTime: number;
|
||||||
|
|
||||||
|
private constructor(year: number, month: number, day: number, minUnixTime: number, maxUnixTime: number) {
|
||||||
|
this.year = year;
|
||||||
|
this.month = month;
|
||||||
|
this.day = day
|
||||||
|
this.minUnixTime = minUnixTime;
|
||||||
|
this.maxUnixTime = maxUnixTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static of(yearMonthDay: YearMonthDay, minUnixTime: number, maxUnixTime: number): YearMonthDayUnixTime {
|
||||||
|
return new YearMonthDayUnixTime(yearMonthDay.year, yearMonthDay.month, yearMonthDay.day, minUnixTime, maxUnixTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type MonthValue = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
export type MonthValue = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
||||||
|
|
||||||
export class Month {
|
export class Month {
|
||||||
|
|||||||
+10
-8
@@ -151,23 +151,25 @@ export class ChartSortingType implements TypeAndName {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ChartDateAggregationType implements TypeAndName {
|
export class ChartDateAggregationType {
|
||||||
private static readonly allInstances: ChartDateAggregationType[] = [];
|
private static readonly allInstances: ChartDateAggregationType[] = [];
|
||||||
private static readonly allInstancesByType: Record<number, ChartDateAggregationType> = {};
|
private static readonly allInstancesByType: Record<number, ChartDateAggregationType> = {};
|
||||||
|
|
||||||
public static readonly Month = new ChartDateAggregationType(0, 'Aggregate by Month');
|
public static readonly Month = new ChartDateAggregationType(0, 'Monthly', 'Aggregate by Month');
|
||||||
public static readonly Quarter = new ChartDateAggregationType(1, 'Aggregate by Quarter');
|
public static readonly Quarter = new ChartDateAggregationType(1, 'Quarterly', 'Aggregate by Quarter');
|
||||||
public static readonly Year = new ChartDateAggregationType(2, 'Aggregate by Year');
|
public static readonly Year = new ChartDateAggregationType(2, 'Yearly', 'Aggregate by Year');
|
||||||
public static readonly FiscalYear = new ChartDateAggregationType(3, 'Aggregate by Fiscal Year');
|
public static readonly FiscalYear = new ChartDateAggregationType(3, 'FiscalYearly', 'Aggregate by Fiscal Year');
|
||||||
|
|
||||||
public static readonly Default = ChartDateAggregationType.Month;
|
public static readonly Default = ChartDateAggregationType.Month;
|
||||||
|
|
||||||
public readonly type: number;
|
public readonly type: number;
|
||||||
public readonly name: string;
|
public readonly shortName: string;
|
||||||
|
public readonly fullName: string;
|
||||||
|
|
||||||
private constructor(type: number, name: string) {
|
private constructor(type: number, shortName: string, fullName: string) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.name = name;
|
this.shortName = shortName;
|
||||||
|
this.fullName = fullName;
|
||||||
|
|
||||||
ChartDateAggregationType.allInstances.push(this);
|
ChartDateAggregationType.allInstances.push(this);
|
||||||
ChartDateAggregationType.allInstancesByType[type] = this;
|
ChartDateAggregationType.allInstancesByType[type] = this;
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ import MonthlyTrendsChart from '@/components/desktop/MonthlyTrendsChart.vue';
|
|||||||
import DateRangeSelectionDialog from '@/components/desktop/DateRangeSelectionDialog.vue';
|
import DateRangeSelectionDialog from '@/components/desktop/DateRangeSelectionDialog.vue';
|
||||||
import MonthSelectionDialog from '@/components/desktop/MonthSelectionDialog.vue';
|
import MonthSelectionDialog from '@/components/desktop/MonthSelectionDialog.vue';
|
||||||
import MonthRangeSelectionDialog from '@/components/desktop/MonthRangeSelectionDialog.vue';
|
import MonthRangeSelectionDialog from '@/components/desktop/MonthRangeSelectionDialog.vue';
|
||||||
|
import AccountBalanceTrendsChart from '@/components/desktop/AccountBalanceTrendsChart.vue';
|
||||||
import SwitchToMobileDialog from '@/components/desktop/SwitchToMobileDialog.vue';
|
import SwitchToMobileDialog from '@/components/desktop/SwitchToMobileDialog.vue';
|
||||||
|
|
||||||
import '@/styles/desktop/template/vuetify/index.scss';
|
import '@/styles/desktop/template/vuetify/index.scss';
|
||||||
@@ -525,6 +526,7 @@ app.component('MonthlyTrendsChart', MonthlyTrendsChart);
|
|||||||
app.component('DateRangeSelectionDialog', DateRangeSelectionDialog);
|
app.component('DateRangeSelectionDialog', DateRangeSelectionDialog);
|
||||||
app.component('MonthSelectionDialog', MonthSelectionDialog);
|
app.component('MonthSelectionDialog', MonthSelectionDialog);
|
||||||
app.component('MonthRangeSelectionDialog', MonthRangeSelectionDialog);
|
app.component('MonthRangeSelectionDialog', MonthRangeSelectionDialog);
|
||||||
|
app.component('AccountBalanceTrendsChart', AccountBalanceTrendsChart);
|
||||||
app.component('SwitchToMobileDialog', SwitchToMobileDialog);
|
app.component('SwitchToMobileDialog', SwitchToMobileDialog);
|
||||||
|
|
||||||
app.mount('#app');
|
app.mount('#app');
|
||||||
|
|||||||
+65
-10
@@ -17,6 +17,7 @@ import {
|
|||||||
type TimeFormat,
|
type TimeFormat,
|
||||||
YearQuarterUnixTime,
|
YearQuarterUnixTime,
|
||||||
YearMonthUnixTime,
|
YearMonthUnixTime,
|
||||||
|
YearMonthDayUnixTime,
|
||||||
Month,
|
Month,
|
||||||
WeekDay,
|
WeekDay,
|
||||||
MeridiemIndicator,
|
MeridiemIndicator,
|
||||||
@@ -222,6 +223,10 @@ export function getYear(date: SupportedDate): number {
|
|||||||
return moment(date).year();
|
return moment(date).year();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getQuarter(date: SupportedDate): number {
|
||||||
|
return moment(date).quarter();
|
||||||
|
}
|
||||||
|
|
||||||
export function getMonth(date: SupportedDate): number {
|
export function getMonth(date: SupportedDate): number {
|
||||||
return moment(date).month() + 1;
|
return moment(date).month() + 1;
|
||||||
}
|
}
|
||||||
@@ -323,15 +328,6 @@ export function getThisMonthLastUnixTime(): number {
|
|||||||
return moment.unix(getThisMonthFirstUnixTime()).add(1, 'months').subtract(1, 'seconds').unix();
|
return moment.unix(getThisMonthFirstUnixTime()).add(1, 'months').subtract(1, 'seconds').unix();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMonthFirstUnixTimeBySpecifiedUnixTime(unixTime: number): number {
|
|
||||||
const date = moment.unix(unixTime).set({ hour: 0, minute: 0, second: 0, millisecond: 0 });
|
|
||||||
return date.subtract(date.date() - 1, 'days').unix();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getMonthLastUnixTimeBySpecifiedUnixTime(unixTime: number): number {
|
|
||||||
return moment.unix(getMonthFirstUnixTimeBySpecifiedUnixTime(unixTime)).add(1, 'months').subtract(1, 'seconds').unix();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getThisMonthSpecifiedDayFirstUnixTime(date: number): number {
|
export function getThisMonthSpecifiedDayFirstUnixTime(date: number): number {
|
||||||
return moment().set({ date: date, hour: 0, minute: 0, second: 0, millisecond: 0 }).unix();
|
return moment().set({ date: date, hour: 0, minute: 0, second: 0, millisecond: 0 }).unix();
|
||||||
}
|
}
|
||||||
@@ -349,10 +345,43 @@ export function getThisYearLastUnixTime(): number {
|
|||||||
return moment.unix(getThisYearFirstUnixTime()).add(1, 'years').subtract(1, 'seconds').unix();
|
return moment.unix(getThisYearFirstUnixTime()).add(1, 'years').subtract(1, 'seconds').unix();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSpecifiedDayFirstUnixTime(unixTime: number): number {
|
export function getYearFirstUnixTimeBySpecifiedUnixTime(unixTime: number): number {
|
||||||
|
const date = moment.unix(unixTime).set({ hour: 0, minute: 0, second: 0, millisecond: 0 });
|
||||||
|
return date.subtract(date.dayOfYear() - 1, 'days').unix();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getYearLastUnixTimeBySpecifiedUnixTime(unixTime: number): number {
|
||||||
|
return moment.unix(getYearFirstUnixTimeBySpecifiedUnixTime(unixTime)).add(1, 'years').subtract(1, 'seconds').unix();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getQuarterFirstUnixTimeBySpecifiedUnixTime(unixTime: number): number {
|
||||||
|
const date = moment.unix(unixTime).set({ hour: 0, minute: 0, second: 0, millisecond: 0 });
|
||||||
|
const month = date.month();
|
||||||
|
const quarterStartMonth = Math.floor(month / 3) * 3;
|
||||||
|
return date.set({ month: quarterStartMonth, date: 1 }).unix();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getQuarterLastUnixTimeBySpecifiedUnixTime(unixTime: number): number {
|
||||||
|
return moment.unix(getQuarterFirstUnixTimeBySpecifiedUnixTime(unixTime)).add(3, 'months').subtract(1, 'seconds').unix();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMonthFirstUnixTimeBySpecifiedUnixTime(unixTime: number): number {
|
||||||
|
const date = moment.unix(unixTime).set({ hour: 0, minute: 0, second: 0, millisecond: 0 });
|
||||||
|
return date.subtract(date.date() - 1, 'days').unix();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMonthLastUnixTimeBySpecifiedUnixTime(unixTime: number): number {
|
||||||
|
return moment.unix(getMonthFirstUnixTimeBySpecifiedUnixTime(unixTime)).add(1, 'months').subtract(1, 'seconds').unix();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDayFirstUnixTimeBySpecifiedUnixTime(unixTime: number): number {
|
||||||
return moment.unix(unixTime).set({ hour: 0, minute: 0, second: 0, millisecond: 0 }).unix();
|
return moment.unix(unixTime).set({ hour: 0, minute: 0, second: 0, millisecond: 0 }).unix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getDayLastUnixTimeBySpecifiedUnixTime(unixTime: number): number {
|
||||||
|
return moment.unix(unixTime).set({ hour: 0, minute: 0, second: 0, millisecond: 0 }).add(1, 'days').subtract(1, 'seconds').unix();
|
||||||
|
}
|
||||||
|
|
||||||
export function getYearFirstUnixTime(year: number): number {
|
export function getYearFirstUnixTime(year: number): number {
|
||||||
return moment().set({ year: year, month: 0, date: 1, hour: 0, minute: 0, second: 0, millisecond: 0 }).unix();
|
return moment().set({ year: year, month: 0, date: 1, hour: 0, minute: 0, second: 0, millisecond: 0 }).unix();
|
||||||
}
|
}
|
||||||
@@ -581,6 +610,32 @@ export function getAllMonthsStartAndEndUnixTimes(startYearMonth: Year0BasedMonth
|
|||||||
return allYearMonthTimes;
|
return allYearMonthTimes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getAllDaysStartAndEndUnixTimes(startUnixTime: number, endUnixTime: number): YearMonthDayUnixTime[] {
|
||||||
|
const allYearMonthDayTimes: YearMonthDayUnixTime[] = [];
|
||||||
|
|
||||||
|
if (!startUnixTime || !endUnixTime) {
|
||||||
|
return allYearMonthDayTimes;
|
||||||
|
}
|
||||||
|
|
||||||
|
let unixTime: number = startUnixTime;
|
||||||
|
|
||||||
|
while (unixTime <= endUnixTime) {
|
||||||
|
const currentDay = parseDateFromUnixTime(unixTime);
|
||||||
|
const currentDayMinUnixTime = getDayFirstUnixTimeBySpecifiedUnixTime(unixTime);
|
||||||
|
const currentDayMaxUnixTime = getDayLastUnixTimeBySpecifiedUnixTime(unixTime);
|
||||||
|
|
||||||
|
allYearMonthDayTimes.push(YearMonthDayUnixTime.of({
|
||||||
|
year: currentDay.year(),
|
||||||
|
month: currentDay.month() + 1,
|
||||||
|
day: currentDay.date()
|
||||||
|
}, currentDayMinUnixTime, currentDayMaxUnixTime));
|
||||||
|
|
||||||
|
unixTime = currentDayMaxUnixTime + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return allYearMonthDayTimes;
|
||||||
|
}
|
||||||
|
|
||||||
export function getDateTimeFormatType<T extends DateFormat | TimeFormat>(allFormatMap: Record<string, T>, allFormatArray: T[], formatTypeValue: number, languageDefaultTypeName: string, systemDefaultFormatType: T): T {
|
export function getDateTimeFormatType<T extends DateFormat | TimeFormat>(allFormatMap: Record<string, T>, allFormatArray: T[], formatTypeValue: number, languageDefaultTypeName: string, systemDefaultFormatType: T): T {
|
||||||
if (formatTypeValue > LANGUAGE_DEFAULT_DATE_TIME_FORMAT_VALUE && allFormatArray[formatTypeValue - 1] && allFormatArray[formatTypeValue - 1].key) {
|
if (formatTypeValue > LANGUAGE_DEFAULT_DATE_TIME_FORMAT_VALUE && allFormatArray[formatTypeValue - 1] && allFormatArray[formatTypeValue - 1].key) {
|
||||||
return allFormatArray[formatTypeValue - 1];
|
return allFormatArray[formatTypeValue - 1];
|
||||||
|
|||||||
@@ -74,6 +74,10 @@ export function getAllDateRanges<T extends Year1BasedMonth>(items: YearMonthItem
|
|||||||
endYearMonth = `${maxYear}-${maxMonth}`;
|
endYearMonth = `${maxYear}-${maxMonth}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return getAllDateRangesByYearMonthRange(startYearMonth, endYearMonth, fiscalYearStart, dateAggregationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAllDateRangesByYearMonthRange(startYearMonth: Year1BasedMonth | string, endYearMonth: Year1BasedMonth | string, fiscalYearStart: number, dateAggregationType: number): YearUnixTime[] | FiscalYearUnixTime[] | YearQuarterUnixTime[] | YearMonthUnixTime[] {
|
||||||
if (!startYearMonth || !endYearMonth) {
|
if (!startYearMonth || !endYearMonth) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -251,6 +251,13 @@
|
|||||||
"31": "31."
|
"31": "31."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"granularity": {
|
||||||
|
"FiscalYearly": "Fiscal Yearly",
|
||||||
|
"Yearly": "Yearly",
|
||||||
|
"Quarterly": "Quarterly",
|
||||||
|
"Monthly": "Monthly",
|
||||||
|
"Daily": "Daily"
|
||||||
|
},
|
||||||
"numeral": {
|
"numeral": {
|
||||||
"Dot": "Punkt",
|
"Dot": "Punkt",
|
||||||
"Comma": "Komma",
|
"Comma": "Komma",
|
||||||
@@ -1635,6 +1642,8 @@
|
|||||||
"Unable to delete this account": "Konto kann nicht gelöscht werden",
|
"Unable to delete this account": "Konto kann nicht gelöscht werden",
|
||||||
"Unable to delete this sub-account": "Unable to delete this sub-account",
|
"Unable to delete this sub-account": "Unable to delete this sub-account",
|
||||||
"Reconciliation Statement": "Reconciliation Statement",
|
"Reconciliation Statement": "Reconciliation Statement",
|
||||||
|
"Show Account Balance Trends": "Show Account Balance Trends",
|
||||||
|
"Show Transaction List": "Show Transaction List",
|
||||||
"Update Closing Balance": "Update Closing Balance",
|
"Update Closing Balance": "Update Closing Balance",
|
||||||
"Please enter the new closing balance for the account": "Please enter the new closing balance for the account",
|
"Please enter the new closing balance for the account": "Please enter the new closing balance for the account",
|
||||||
"Transaction": "Transaktion",
|
"Transaction": "Transaktion",
|
||||||
@@ -1873,6 +1882,7 @@
|
|||||||
"Sort by Amount": "Nach Betrag sortieren",
|
"Sort by Amount": "Nach Betrag sortieren",
|
||||||
"Sort by Display Order": "Nach Anzeigereihenfolge sortieren",
|
"Sort by Display Order": "Nach Anzeigereihenfolge sortieren",
|
||||||
"Sort by Name": "Nach Name sortieren",
|
"Sort by Name": "Nach Name sortieren",
|
||||||
|
"Time Granularity": "Time Granularity",
|
||||||
"Aggregate by Month": "Nach Monat aggregieren",
|
"Aggregate by Month": "Nach Monat aggregieren",
|
||||||
"Aggregate by Quarter": "Nach Quartal aggregieren",
|
"Aggregate by Quarter": "Nach Quartal aggregieren",
|
||||||
"Aggregate by Year": "Nach Jahr aggregieren",
|
"Aggregate by Year": "Nach Jahr aggregieren",
|
||||||
|
|||||||
@@ -251,6 +251,13 @@
|
|||||||
"31": "31th"
|
"31": "31th"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"granularity": {
|
||||||
|
"FiscalYearly": "Fiscal Yearly",
|
||||||
|
"Yearly": "Yearly",
|
||||||
|
"Quarterly": "Quarterly",
|
||||||
|
"Monthly": "Monthly",
|
||||||
|
"Daily": "Daily"
|
||||||
|
},
|
||||||
"numeral": {
|
"numeral": {
|
||||||
"Dot": "Dot",
|
"Dot": "Dot",
|
||||||
"Comma": "Comma",
|
"Comma": "Comma",
|
||||||
@@ -1635,6 +1642,8 @@
|
|||||||
"Unable to delete this account": "Unable to delete this account",
|
"Unable to delete this account": "Unable to delete this account",
|
||||||
"Unable to delete this sub-account": "Unable to delete this sub-account",
|
"Unable to delete this sub-account": "Unable to delete this sub-account",
|
||||||
"Reconciliation Statement": "Reconciliation Statement",
|
"Reconciliation Statement": "Reconciliation Statement",
|
||||||
|
"Show Account Balance Trends": "Show Account Balance Trends",
|
||||||
|
"Show Transaction List": "Show Transaction List",
|
||||||
"Update Closing Balance": "Update Closing Balance",
|
"Update Closing Balance": "Update Closing Balance",
|
||||||
"Please enter the new closing balance for the account": "Please enter the new closing balance for the account",
|
"Please enter the new closing balance for the account": "Please enter the new closing balance for the account",
|
||||||
"Transaction": "Transaction",
|
"Transaction": "Transaction",
|
||||||
@@ -1873,6 +1882,7 @@
|
|||||||
"Sort by Amount": "Sort by Amount",
|
"Sort by Amount": "Sort by Amount",
|
||||||
"Sort by Display Order": "Sort by Display Order",
|
"Sort by Display Order": "Sort by Display Order",
|
||||||
"Sort by Name": "Sort by Name",
|
"Sort by Name": "Sort by Name",
|
||||||
|
"Time Granularity": "Time Granularity",
|
||||||
"Aggregate by Month": "Aggregate by Month",
|
"Aggregate by Month": "Aggregate by Month",
|
||||||
"Aggregate by Quarter": "Aggregate by Quarter",
|
"Aggregate by Quarter": "Aggregate by Quarter",
|
||||||
"Aggregate by Year": "Aggregate by Year",
|
"Aggregate by Year": "Aggregate by Year",
|
||||||
|
|||||||
@@ -251,6 +251,13 @@
|
|||||||
"31": "31"
|
"31": "31"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"granularity": {
|
||||||
|
"FiscalYearly": "Fiscal Yearly",
|
||||||
|
"Yearly": "Yearly",
|
||||||
|
"Quarterly": "Quarterly",
|
||||||
|
"Monthly": "Monthly",
|
||||||
|
"Daily": "Daily"
|
||||||
|
},
|
||||||
"numeral": {
|
"numeral": {
|
||||||
"Dot": "Punto",
|
"Dot": "Punto",
|
||||||
"Comma": "Coma",
|
"Comma": "Coma",
|
||||||
@@ -1635,6 +1642,8 @@
|
|||||||
"Unable to delete this account": "No se puede eliminar esta cuenta",
|
"Unable to delete this account": "No se puede eliminar esta cuenta",
|
||||||
"Unable to delete this sub-account": "Unable to delete this sub-account",
|
"Unable to delete this sub-account": "Unable to delete this sub-account",
|
||||||
"Reconciliation Statement": "Reconciliation Statement",
|
"Reconciliation Statement": "Reconciliation Statement",
|
||||||
|
"Show Account Balance Trends": "Show Account Balance Trends",
|
||||||
|
"Show Transaction List": "Show Transaction List",
|
||||||
"Update Closing Balance": "Update Closing Balance",
|
"Update Closing Balance": "Update Closing Balance",
|
||||||
"Please enter the new closing balance for the account": "Please enter the new closing balance for the account",
|
"Please enter the new closing balance for the account": "Please enter the new closing balance for the account",
|
||||||
"Transaction": "Transacción",
|
"Transaction": "Transacción",
|
||||||
@@ -1873,6 +1882,7 @@
|
|||||||
"Sort by Amount": "Ordenar por Importe",
|
"Sort by Amount": "Ordenar por Importe",
|
||||||
"Sort by Display Order": "Ordenar por orden de visualización",
|
"Sort by Display Order": "Ordenar por orden de visualización",
|
||||||
"Sort by Name": "Ordenar por Nombre",
|
"Sort by Name": "Ordenar por Nombre",
|
||||||
|
"Time Granularity": "Time Granularity",
|
||||||
"Aggregate by Month": "Agregado por mes",
|
"Aggregate by Month": "Agregado por mes",
|
||||||
"Aggregate by Quarter": "Agregado por trimestre",
|
"Aggregate by Quarter": "Agregado por trimestre",
|
||||||
"Aggregate by Year": "Agregado por año",
|
"Aggregate by Year": "Agregado por año",
|
||||||
|
|||||||
+27
-1
@@ -147,6 +147,7 @@ import {
|
|||||||
getTimezoneOffset,
|
getTimezoneOffset,
|
||||||
getTimezoneOffsetMinutes,
|
getTimezoneOffsetMinutes,
|
||||||
getYear,
|
getYear,
|
||||||
|
getQuarter,
|
||||||
isDateRangeMatchFullMonths,
|
isDateRangeMatchFullMonths,
|
||||||
isDateRangeMatchFullYears,
|
isDateRangeMatchFullYears,
|
||||||
isPM,
|
isPM,
|
||||||
@@ -496,6 +497,22 @@ export function useI18n() {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLocalizedChartDateAggregationTypeAndDisplayName(fullName: boolean): TypeAndDisplayName[] {
|
||||||
|
const ret: TypeAndDisplayName[] = [];
|
||||||
|
const allTypes: ChartDateAggregationType[] = ChartDateAggregationType.values();
|
||||||
|
|
||||||
|
for (let i = 0; i < allTypes.length; i++) {
|
||||||
|
const type = allTypes[i];
|
||||||
|
|
||||||
|
ret.push({
|
||||||
|
type: type.type,
|
||||||
|
displayName: t(fullName ? type.fullName : `granularity.${type.shortName}`)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
function getAllMonthNames(type: string): string[] {
|
function getAllMonthNames(type: string): string[] {
|
||||||
const ret = [];
|
const ret = [];
|
||||||
const allMonths = Month.values();
|
const allMonths = Month.values();
|
||||||
@@ -1472,6 +1489,13 @@ export function useI18n() {
|
|||||||
return formatMonthDay(monthDay, getLocalizedLongMonthDayFormat());
|
return formatMonthDay(monthDay, getLocalizedLongMonthDayFormat());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatUnixTimeToYearQuarter(unixTime: number): string {
|
||||||
|
const date = parseDateFromUnixTime(unixTime);
|
||||||
|
const year = getYear(date);
|
||||||
|
const quarter = getQuarter(date);
|
||||||
|
return formatYearQuarter(year, quarter);
|
||||||
|
}
|
||||||
|
|
||||||
function formatYearQuarter(year: number, quarter: number): string {
|
function formatYearQuarter(year: number, quarter: number): string {
|
||||||
if (1 <= quarter && quarter <= 4) {
|
if (1 <= quarter && quarter <= 4) {
|
||||||
return t('format.yearQuarter.q' + quarter, {
|
return t('format.yearQuarter.q' + quarter, {
|
||||||
@@ -1912,7 +1936,8 @@ export function useI18n() {
|
|||||||
getAllTrendChartTypes: () => getLocalizedDisplayNameAndType(TrendChartType.values()),
|
getAllTrendChartTypes: () => getLocalizedDisplayNameAndType(TrendChartType.values()),
|
||||||
getAllStatisticsChartDataTypes: (analysisType: StatisticsAnalysisType) => getLocalizedDisplayNameAndType(ChartDataType.values(analysisType)),
|
getAllStatisticsChartDataTypes: (analysisType: StatisticsAnalysisType) => getLocalizedDisplayNameAndType(ChartDataType.values(analysisType)),
|
||||||
getAllStatisticsSortingTypes: () => getLocalizedDisplayNameAndType(ChartSortingType.values()),
|
getAllStatisticsSortingTypes: () => getLocalizedDisplayNameAndType(ChartSortingType.values()),
|
||||||
getAllStatisticsDateAggregationTypes: () => getLocalizedDisplayNameAndType(ChartDateAggregationType.values()),
|
getAllStatisticsDateAggregationTypes: () => getLocalizedChartDateAggregationTypeAndDisplayName(true),
|
||||||
|
getAllStatisticsDateAggregationTypesWithShortName: () => getLocalizedChartDateAggregationTypeAndDisplayName(false),
|
||||||
getAllTransactionEditScopeTypes: () => getLocalizedDisplayNameAndType(TransactionEditScopeType.values()),
|
getAllTransactionEditScopeTypes: () => getLocalizedDisplayNameAndType(TransactionEditScopeType.values()),
|
||||||
getAllTransactionTagFilterTypes: () => getLocalizedDisplayNameAndType(TransactionTagFilterType.values()),
|
getAllTransactionTagFilterTypes: () => getLocalizedDisplayNameAndType(TransactionTagFilterType.values()),
|
||||||
getAllTransactionScheduledFrequencyTypes: () => getLocalizedDisplayNameAndType(ScheduledTemplateFrequencyType.values()),
|
getAllTransactionScheduledFrequencyTypes: () => getLocalizedDisplayNameAndType(ScheduledTemplateFrequencyType.values()),
|
||||||
@@ -1961,6 +1986,7 @@ export function useI18n() {
|
|||||||
formatUnixTimeToShortTime: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedShortTimeFormat(), utcOffset, currentUtcOffset),
|
formatUnixTimeToShortTime: (unixTime: number, utcOffset?: number, currentUtcOffset?: number) => formatUnixTime(unixTime, getLocalizedShortTimeFormat(), utcOffset, currentUtcOffset),
|
||||||
formatDateToLongDate,
|
formatDateToLongDate,
|
||||||
formatMonthDayToLongDay,
|
formatMonthDayToLongDay,
|
||||||
|
formatUnixTimeToYearQuarter,
|
||||||
formatYearQuarter,
|
formatYearQuarter,
|
||||||
formatDateRange,
|
formatDateRange,
|
||||||
formatFiscalYearStartToLongDay,
|
formatFiscalYearStartToLongDay,
|
||||||
|
|||||||
@@ -251,6 +251,13 @@
|
|||||||
"31": "31"
|
"31": "31"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"granularity": {
|
||||||
|
"FiscalYearly": "Fiscal Yearly",
|
||||||
|
"Yearly": "Yearly",
|
||||||
|
"Quarterly": "Quarterly",
|
||||||
|
"Monthly": "Monthly",
|
||||||
|
"Daily": "Daily"
|
||||||
|
},
|
||||||
"numeral": {
|
"numeral": {
|
||||||
"Dot": "Punto",
|
"Dot": "Punto",
|
||||||
"Comma": "Virgola",
|
"Comma": "Virgola",
|
||||||
@@ -1635,6 +1642,8 @@
|
|||||||
"Unable to delete this account": "Impossibile eliminare questo account",
|
"Unable to delete this account": "Impossibile eliminare questo account",
|
||||||
"Unable to delete this sub-account": "Impossibile eliminare questo sotto-account",
|
"Unable to delete this sub-account": "Impossibile eliminare questo sotto-account",
|
||||||
"Reconciliation Statement": "Reconciliation Statement",
|
"Reconciliation Statement": "Reconciliation Statement",
|
||||||
|
"Show Account Balance Trends": "Show Account Balance Trends",
|
||||||
|
"Show Transaction List": "Show Transaction List",
|
||||||
"Update Closing Balance": "Update Closing Balance",
|
"Update Closing Balance": "Update Closing Balance",
|
||||||
"Please enter the new closing balance for the account": "Please enter the new closing balance for the account",
|
"Please enter the new closing balance for the account": "Please enter the new closing balance for the account",
|
||||||
"Transaction": "Transazione",
|
"Transaction": "Transazione",
|
||||||
@@ -1873,6 +1882,7 @@
|
|||||||
"Sort by Amount": "Ordina per importo",
|
"Sort by Amount": "Ordina per importo",
|
||||||
"Sort by Display Order": "Ordina per ordine di visualizzazione",
|
"Sort by Display Order": "Ordina per ordine di visualizzazione",
|
||||||
"Sort by Name": "Ordina per nome",
|
"Sort by Name": "Ordina per nome",
|
||||||
|
"Time Granularity": "Time Granularity",
|
||||||
"Aggregate by Month": "Aggrega per mese",
|
"Aggregate by Month": "Aggrega per mese",
|
||||||
"Aggregate by Quarter": "Aggrega per trimestre",
|
"Aggregate by Quarter": "Aggrega per trimestre",
|
||||||
"Aggregate by Year": "Aggrega per anno",
|
"Aggregate by Year": "Aggrega per anno",
|
||||||
|
|||||||
@@ -251,6 +251,13 @@
|
|||||||
"31": "31"
|
"31": "31"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"granularity": {
|
||||||
|
"FiscalYearly": "Fiscal Yearly",
|
||||||
|
"Yearly": "Yearly",
|
||||||
|
"Quarterly": "Quarterly",
|
||||||
|
"Monthly": "Monthly",
|
||||||
|
"Daily": "Daily"
|
||||||
|
},
|
||||||
"numeral": {
|
"numeral": {
|
||||||
"Dot": "ドット",
|
"Dot": "ドット",
|
||||||
"Comma": "コンマ",
|
"Comma": "コンマ",
|
||||||
@@ -1635,6 +1642,8 @@
|
|||||||
"Unable to delete this account": "この口座を削除できません",
|
"Unable to delete this account": "この口座を削除できません",
|
||||||
"Unable to delete this sub-account": "Unable to delete this sub-account",
|
"Unable to delete this sub-account": "Unable to delete this sub-account",
|
||||||
"Reconciliation Statement": "Reconciliation Statement",
|
"Reconciliation Statement": "Reconciliation Statement",
|
||||||
|
"Show Account Balance Trends": "Show Account Balance Trends",
|
||||||
|
"Show Transaction List": "Show Transaction List",
|
||||||
"Update Closing Balance": "Update Closing Balance",
|
"Update Closing Balance": "Update Closing Balance",
|
||||||
"Please enter the new closing balance for the account": "Please enter the new closing balance for the account",
|
"Please enter the new closing balance for the account": "Please enter the new closing balance for the account",
|
||||||
"Transaction": "取引",
|
"Transaction": "取引",
|
||||||
@@ -1873,6 +1882,7 @@
|
|||||||
"Sort by Amount": "金額で並べ替え",
|
"Sort by Amount": "金額で並べ替え",
|
||||||
"Sort by Display Order": "表示で並べ替え",
|
"Sort by Display Order": "表示で並べ替え",
|
||||||
"Sort by Name": "名前で並べ替え",
|
"Sort by Name": "名前で並べ替え",
|
||||||
|
"Time Granularity": "Time Granularity",
|
||||||
"Aggregate by Month": "月ごとに集計",
|
"Aggregate by Month": "月ごとに集計",
|
||||||
"Aggregate by Quarter": "四半期ごとに集計",
|
"Aggregate by Quarter": "四半期ごとに集計",
|
||||||
"Aggregate by Year": "年ごとに集計",
|
"Aggregate by Year": "年ごとに集計",
|
||||||
|
|||||||
@@ -251,6 +251,13 @@
|
|||||||
"31": "31º"
|
"31": "31º"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"granularity": {
|
||||||
|
"FiscalYearly": "Fiscal Yearly",
|
||||||
|
"Yearly": "Yearly",
|
||||||
|
"Quarterly": "Quarterly",
|
||||||
|
"Monthly": "Monthly",
|
||||||
|
"Daily": "Daily"
|
||||||
|
},
|
||||||
"numeral": {
|
"numeral": {
|
||||||
"Dot": "Ponto",
|
"Dot": "Ponto",
|
||||||
"Comma": "Vírgula",
|
"Comma": "Vírgula",
|
||||||
@@ -1635,6 +1642,8 @@
|
|||||||
"Unable to delete this account": "Não foi possível deletar esta conta",
|
"Unable to delete this account": "Não foi possível deletar esta conta",
|
||||||
"Unable to delete this sub-account": "Não foi possível deletar esta subconta",
|
"Unable to delete this sub-account": "Não foi possível deletar esta subconta",
|
||||||
"Reconciliation Statement": "Reconciliation Statement",
|
"Reconciliation Statement": "Reconciliation Statement",
|
||||||
|
"Show Account Balance Trends": "Show Account Balance Trends",
|
||||||
|
"Show Transaction List": "Show Transaction List",
|
||||||
"Update Closing Balance": "Update Closing Balance",
|
"Update Closing Balance": "Update Closing Balance",
|
||||||
"Please enter the new closing balance for the account": "Please enter the new closing balance for the account",
|
"Please enter the new closing balance for the account": "Please enter the new closing balance for the account",
|
||||||
"Transaction": "Transação",
|
"Transaction": "Transação",
|
||||||
@@ -1873,6 +1882,7 @@
|
|||||||
"Sort by Amount": "Classificar por Montante",
|
"Sort by Amount": "Classificar por Montante",
|
||||||
"Sort by Display Order": "Classificar por Ordem de Exibição",
|
"Sort by Display Order": "Classificar por Ordem de Exibição",
|
||||||
"Sort by Name": "Classificar por Nome",
|
"Sort by Name": "Classificar por Nome",
|
||||||
|
"Time Granularity": "Time Granularity",
|
||||||
"Aggregate by Month": "Agregado por Mês",
|
"Aggregate by Month": "Agregado por Mês",
|
||||||
"Aggregate by Quarter": "Agregado por Trimestre",
|
"Aggregate by Quarter": "Agregado por Trimestre",
|
||||||
"Aggregate by Year": "Agregado por Ano",
|
"Aggregate by Year": "Agregado por Ano",
|
||||||
|
|||||||
@@ -251,6 +251,13 @@
|
|||||||
"31": "31-й"
|
"31": "31-й"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"granularity": {
|
||||||
|
"FiscalYearly": "Fiscal Yearly",
|
||||||
|
"Yearly": "Yearly",
|
||||||
|
"Quarterly": "Quarterly",
|
||||||
|
"Monthly": "Monthly",
|
||||||
|
"Daily": "Daily"
|
||||||
|
},
|
||||||
"numeral": {
|
"numeral": {
|
||||||
"Dot": "Точка",
|
"Dot": "Точка",
|
||||||
"Comma": "Запятая",
|
"Comma": "Запятая",
|
||||||
@@ -1635,6 +1642,8 @@
|
|||||||
"Unable to delete this account": "Не удалось удалить этот счет",
|
"Unable to delete this account": "Не удалось удалить этот счет",
|
||||||
"Unable to delete this sub-account": "Unable to delete this sub-account",
|
"Unable to delete this sub-account": "Unable to delete this sub-account",
|
||||||
"Reconciliation Statement": "Reconciliation Statement",
|
"Reconciliation Statement": "Reconciliation Statement",
|
||||||
|
"Show Account Balance Trends": "Show Account Balance Trends",
|
||||||
|
"Show Transaction List": "Show Transaction List",
|
||||||
"Update Closing Balance": "Update Closing Balance",
|
"Update Closing Balance": "Update Closing Balance",
|
||||||
"Please enter the new closing balance for the account": "Please enter the new closing balance for the account",
|
"Please enter the new closing balance for the account": "Please enter the new closing balance for the account",
|
||||||
"Transaction": "Транзакция",
|
"Transaction": "Транзакция",
|
||||||
@@ -1873,6 +1882,7 @@
|
|||||||
"Sort by Amount": "Сортировать по сумме",
|
"Sort by Amount": "Сортировать по сумме",
|
||||||
"Sort by Display Order": "Сортировать по порядку отображения",
|
"Sort by Display Order": "Сортировать по порядку отображения",
|
||||||
"Sort by Name": "Сортировать по имени",
|
"Sort by Name": "Сортировать по имени",
|
||||||
|
"Time Granularity": "Time Granularity",
|
||||||
"Aggregate by Month": "Агрегировать по месяцам",
|
"Aggregate by Month": "Агрегировать по месяцам",
|
||||||
"Aggregate by Quarter": "Агрегировать по кварталам",
|
"Aggregate by Quarter": "Агрегировать по кварталам",
|
||||||
"Aggregate by Year": "Агрегировать по годам",
|
"Aggregate by Year": "Агрегировать по годам",
|
||||||
|
|||||||
@@ -251,6 +251,13 @@
|
|||||||
"31": "31-й"
|
"31": "31-й"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"granularity": {
|
||||||
|
"FiscalYearly": "Fiscal Yearly",
|
||||||
|
"Yearly": "Yearly",
|
||||||
|
"Quarterly": "Quarterly",
|
||||||
|
"Monthly": "Monthly",
|
||||||
|
"Daily": "Daily"
|
||||||
|
},
|
||||||
"numeral": {
|
"numeral": {
|
||||||
"Dot": "Крапка",
|
"Dot": "Крапка",
|
||||||
"Comma": "Кома",
|
"Comma": "Кома",
|
||||||
@@ -1635,6 +1642,8 @@
|
|||||||
"Unable to delete this account": "Не вдалося видалити цей рахунок",
|
"Unable to delete this account": "Не вдалося видалити цей рахунок",
|
||||||
"Unable to delete this sub-account": "Не вдалося видалити цей субрахунок",
|
"Unable to delete this sub-account": "Не вдалося видалити цей субрахунок",
|
||||||
"Reconciliation Statement": "Reconciliation Statement",
|
"Reconciliation Statement": "Reconciliation Statement",
|
||||||
|
"Show Account Balance Trends": "Show Account Balance Trends",
|
||||||
|
"Show Transaction List": "Show Transaction List",
|
||||||
"Update Closing Balance": "Update Closing Balance",
|
"Update Closing Balance": "Update Closing Balance",
|
||||||
"Please enter the new closing balance for the account": "Please enter the new closing balance for the account",
|
"Please enter the new closing balance for the account": "Please enter the new closing balance for the account",
|
||||||
"Transaction": "Транзакція",
|
"Transaction": "Транзакція",
|
||||||
@@ -1873,6 +1882,7 @@
|
|||||||
"Sort by Amount": "Сортувати за сумою",
|
"Sort by Amount": "Сортувати за сумою",
|
||||||
"Sort by Display Order": "Сортувати за порядком відображення",
|
"Sort by Display Order": "Сортувати за порядком відображення",
|
||||||
"Sort by Name": "Сортувати за назвою",
|
"Sort by Name": "Сортувати за назвою",
|
||||||
|
"Time Granularity": "Time Granularity",
|
||||||
"Aggregate by Month": "Агрегувати за місяцями",
|
"Aggregate by Month": "Агрегувати за місяцями",
|
||||||
"Aggregate by Quarter": "Агрегувати за кварталами",
|
"Aggregate by Quarter": "Агрегувати за кварталами",
|
||||||
"Aggregate by Year": "Агрегувати за роками",
|
"Aggregate by Year": "Агрегувати за роками",
|
||||||
|
|||||||
@@ -251,6 +251,13 @@
|
|||||||
"31": "Ngày 31"
|
"31": "Ngày 31"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"granularity": {
|
||||||
|
"FiscalYearly": "Fiscal Yearly",
|
||||||
|
"Yearly": "Yearly",
|
||||||
|
"Quarterly": "Quarterly",
|
||||||
|
"Monthly": "Monthly",
|
||||||
|
"Daily": "Daily"
|
||||||
|
},
|
||||||
"numeral": {
|
"numeral": {
|
||||||
"Dot": "Dấu chấm",
|
"Dot": "Dấu chấm",
|
||||||
"Comma": "Dấu phẩy",
|
"Comma": "Dấu phẩy",
|
||||||
@@ -1635,6 +1642,8 @@
|
|||||||
"Unable to delete this account": "Không thể xóa tài khoản này",
|
"Unable to delete this account": "Không thể xóa tài khoản này",
|
||||||
"Unable to delete this sub-account": "Unable to delete this sub-account",
|
"Unable to delete this sub-account": "Unable to delete this sub-account",
|
||||||
"Reconciliation Statement": "Reconciliation Statement",
|
"Reconciliation Statement": "Reconciliation Statement",
|
||||||
|
"Show Account Balance Trends": "Show Account Balance Trends",
|
||||||
|
"Show Transaction List": "Show Transaction List",
|
||||||
"Update Closing Balance": "Update Closing Balance",
|
"Update Closing Balance": "Update Closing Balance",
|
||||||
"Please enter the new closing balance for the account": "Please enter the new closing balance for the account",
|
"Please enter the new closing balance for the account": "Please enter the new closing balance for the account",
|
||||||
"Transaction": "Giao dịch",
|
"Transaction": "Giao dịch",
|
||||||
@@ -1873,6 +1882,7 @@
|
|||||||
"Sort by Amount": "Sắp xếp theo số tiền",
|
"Sort by Amount": "Sắp xếp theo số tiền",
|
||||||
"Sort by Display Order": "Sắp xếp theo thứ tự hiển thị",
|
"Sort by Display Order": "Sắp xếp theo thứ tự hiển thị",
|
||||||
"Sort by Name": "Sắp xếp theo tên",
|
"Sort by Name": "Sắp xếp theo tên",
|
||||||
|
"Time Granularity": "Time Granularity",
|
||||||
"Aggregate by Month": "Tổng hợp theo tháng",
|
"Aggregate by Month": "Tổng hợp theo tháng",
|
||||||
"Aggregate by Quarter": "Tổng hợp theo quý",
|
"Aggregate by Quarter": "Tổng hợp theo quý",
|
||||||
"Aggregate by Year": "Tổng hợp theo năm",
|
"Aggregate by Year": "Tổng hợp theo năm",
|
||||||
|
|||||||
@@ -251,6 +251,13 @@
|
|||||||
"31": "31"
|
"31": "31"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"granularity": {
|
||||||
|
"FiscalYearly": "按财年",
|
||||||
|
"Yearly": "按年",
|
||||||
|
"Quarterly": "按季度",
|
||||||
|
"Monthly": "按月",
|
||||||
|
"Daily": "按天"
|
||||||
|
},
|
||||||
"numeral": {
|
"numeral": {
|
||||||
"Dot": "句点",
|
"Dot": "句点",
|
||||||
"Comma": "逗号",
|
"Comma": "逗号",
|
||||||
@@ -1635,6 +1642,8 @@
|
|||||||
"Unable to delete this account": "无法删除该账户",
|
"Unable to delete this account": "无法删除该账户",
|
||||||
"Unable to delete this sub-account": "无法删除该子账户",
|
"Unable to delete this sub-account": "无法删除该子账户",
|
||||||
"Reconciliation Statement": "对账单",
|
"Reconciliation Statement": "对账单",
|
||||||
|
"Show Account Balance Trends": "显示账户余额趋势",
|
||||||
|
"Show Transaction List": "显示交易列表",
|
||||||
"Update Closing Balance": "更新期末余额",
|
"Update Closing Balance": "更新期末余额",
|
||||||
"Please enter the new closing balance for the account": "请输入账户的新期末余额",
|
"Please enter the new closing balance for the account": "请输入账户的新期末余额",
|
||||||
"Transaction": "交易",
|
"Transaction": "交易",
|
||||||
@@ -1873,6 +1882,7 @@
|
|||||||
"Sort by Amount": "按金额排序",
|
"Sort by Amount": "按金额排序",
|
||||||
"Sort by Display Order": "按显示顺序排序",
|
"Sort by Display Order": "按显示顺序排序",
|
||||||
"Sort by Name": "按名称排序",
|
"Sort by Name": "按名称排序",
|
||||||
|
"Time Granularity": "时间粒度",
|
||||||
"Aggregate by Month": "按月聚合",
|
"Aggregate by Month": "按月聚合",
|
||||||
"Aggregate by Quarter": "按季度聚合",
|
"Aggregate by Quarter": "按季度聚合",
|
||||||
"Aggregate by Year": "按年聚合",
|
"Aggregate by Year": "按年聚合",
|
||||||
|
|||||||
@@ -251,6 +251,13 @@
|
|||||||
"31": "31"
|
"31": "31"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"granularity": {
|
||||||
|
"FiscalYearly": "依財政年度",
|
||||||
|
"Yearly": "依年份",
|
||||||
|
"Quarterly": "依季度",
|
||||||
|
"Monthly": "依月份",
|
||||||
|
"Daily": "依日期"
|
||||||
|
},
|
||||||
"numeral": {
|
"numeral": {
|
||||||
"Dot": "句點",
|
"Dot": "句點",
|
||||||
"Comma": "逗號",
|
"Comma": "逗號",
|
||||||
@@ -1635,6 +1642,8 @@
|
|||||||
"Unable to delete this account": "無法刪除此帳戶",
|
"Unable to delete this account": "無法刪除此帳戶",
|
||||||
"Unable to delete this sub-account": "無法刪除此子帳戶",
|
"Unable to delete this sub-account": "無法刪除此子帳戶",
|
||||||
"Reconciliation Statement": "對帳單",
|
"Reconciliation Statement": "對帳單",
|
||||||
|
"Show Account Balance Trends": "顯示帳戶餘額趨勢",
|
||||||
|
"Show Transaction List": "顯示交易清單",
|
||||||
"Update Closing Balance": "更新期末餘額",
|
"Update Closing Balance": "更新期末餘額",
|
||||||
"Please enter the new closing balance for the account": "請輸入帳戶的新期末餘額",
|
"Please enter the new closing balance for the account": "請輸入帳戶的新期末餘額",
|
||||||
"Transaction": "交易",
|
"Transaction": "交易",
|
||||||
@@ -1873,6 +1882,7 @@
|
|||||||
"Sort by Amount": "依金額排序",
|
"Sort by Amount": "依金額排序",
|
||||||
"Sort by Display Order": "依顯示順序排序",
|
"Sort by Display Order": "依顯示順序排序",
|
||||||
"Sort by Name": "依名稱排序",
|
"Sort by Name": "依名稱排序",
|
||||||
|
"Time Granularity": "時間粒度",
|
||||||
"Aggregate by Month": "依月份彙整",
|
"Aggregate by Month": "依月份彙整",
|
||||||
"Aggregate by Quarter": "依季度彙整",
|
"Aggregate by Quarter": "依季度彙整",
|
||||||
"Aggregate by Year": "依年份彙整",
|
"Aggregate by Year": "依年份彙整",
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ import NumberPadSheet from '@/components/mobile/NumberPadSheet.vue';
|
|||||||
import MapSheet from '@/components/mobile/MapSheet.vue';
|
import MapSheet from '@/components/mobile/MapSheet.vue';
|
||||||
import TransactionTagSelectionSheet from '@/components/mobile/TransactionTagSelectionSheet.vue';
|
import TransactionTagSelectionSheet from '@/components/mobile/TransactionTagSelectionSheet.vue';
|
||||||
import ScheduleFrequencySheet from '@/components/mobile/ScheduleFrequencySheet.vue';
|
import ScheduleFrequencySheet from '@/components/mobile/ScheduleFrequencySheet.vue';
|
||||||
|
import AccountBalanceTrendsBarChart from '@/components/mobile/AccountBalanceTrendsBarChart.vue';
|
||||||
|
|
||||||
import TextareaAutoSize from '@/directives/mobile/textareaAutoSize.ts';
|
import TextareaAutoSize from '@/directives/mobile/textareaAutoSize.ts';
|
||||||
|
|
||||||
@@ -197,6 +198,7 @@ app.component('InformationSheet', InformationSheet);
|
|||||||
app.component('NumberPadSheet', NumberPadSheet);
|
app.component('NumberPadSheet', NumberPadSheet);
|
||||||
app.component('MapSheet', MapSheet);
|
app.component('MapSheet', MapSheet);
|
||||||
app.component('TransactionTagSelectionSheet', TransactionTagSelectionSheet);
|
app.component('TransactionTagSelectionSheet', TransactionTagSelectionSheet);
|
||||||
|
app.component('AccountBalanceTrendsBarChart', AccountBalanceTrendsBarChart);
|
||||||
app.component('ScheduleFrequencySheet', ScheduleFrequencySheet);
|
app.component('ScheduleFrequencySheet', ScheduleFrequencySheet);
|
||||||
|
|
||||||
app.directive('TextareaAutoSize', TextareaAutoSize);
|
app.directive('TextareaAutoSize', TextareaAutoSize);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { useUserStore } from '@/stores/user.ts';
|
|||||||
import { useAccountsStore } from '@/stores/account.ts';
|
import { useAccountsStore } from '@/stores/account.ts';
|
||||||
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
||||||
|
|
||||||
|
import type { TypeAndDisplayName } from '@/core/base.ts';
|
||||||
import { type WeekDayValue, KnownDateTimeFormat } from '@/core/datetime.ts';
|
import { type WeekDayValue, KnownDateTimeFormat } from '@/core/datetime.ts';
|
||||||
import { TransactionType } from '@/core/transaction.ts';
|
import { TransactionType } from '@/core/transaction.ts';
|
||||||
import { KnownFileType } from '@/core/file.ts';
|
import { KnownFileType } from '@/core/file.ts';
|
||||||
@@ -33,6 +34,8 @@ import {
|
|||||||
export function useReconciliationStatementPageBase() {
|
export function useReconciliationStatementPageBase() {
|
||||||
const {
|
const {
|
||||||
tt,
|
tt,
|
||||||
|
getAllTrendChartTypes,
|
||||||
|
getAllStatisticsDateAggregationTypesWithShortName,
|
||||||
getCurrentDigitGroupingSymbol,
|
getCurrentDigitGroupingSymbol,
|
||||||
formatUnixTimeToLongDateTime,
|
formatUnixTimeToLongDateTime,
|
||||||
formatUnixTimeToLongDate,
|
formatUnixTimeToLongDate,
|
||||||
@@ -56,6 +59,9 @@ export function useReconciliationStatementPageBase() {
|
|||||||
const currentTimezoneOffsetMinutes = computed<number>(() => getTimezoneOffsetMinutes(settingsStore.appSettings.timeZone));
|
const currentTimezoneOffsetMinutes = computed<number>(() => getTimezoneOffsetMinutes(settingsStore.appSettings.timeZone));
|
||||||
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
|
const defaultCurrency = computed<string>(() => userStore.currentUserDefaultCurrency);
|
||||||
|
|
||||||
|
const allChartTypes = computed<TypeAndDisplayName[]>(() => getAllTrendChartTypes());
|
||||||
|
const allDateAggregationTypes = computed<TypeAndDisplayName[]>(() => getAllStatisticsDateAggregationTypesWithShortName());
|
||||||
|
|
||||||
const currentAccount = computed(() => allAccountsMap.value[accountId.value]);
|
const currentAccount = computed(() => allAccountsMap.value[accountId.value]);
|
||||||
const currentAccountCurrency = computed<string>(() => currentAccount.value?.currency ?? defaultCurrency.value);
|
const currentAccountCurrency = computed<string>(() => currentAccount.value?.currency ?? defaultCurrency.value);
|
||||||
const isCurrentLiabilityAccount = computed<boolean>(() => currentAccount.value?.isLiability ?? false);
|
const isCurrentLiabilityAccount = computed<boolean>(() => currentAccount.value?.isLiability ?? false);
|
||||||
@@ -266,6 +272,8 @@ export function useReconciliationStatementPageBase() {
|
|||||||
fiscalYearStart,
|
fiscalYearStart,
|
||||||
currentTimezoneOffsetMinutes,
|
currentTimezoneOffsetMinutes,
|
||||||
defaultCurrency,
|
defaultCurrency,
|
||||||
|
allChartTypes,
|
||||||
|
allDateAggregationTypes,
|
||||||
currentAccount,
|
currentAccount,
|
||||||
currentAccountCurrency,
|
currentAccountCurrency,
|
||||||
isCurrentLiabilityAccount,
|
isCurrentLiabilityAccount,
|
||||||
|
|||||||
@@ -14,6 +14,34 @@
|
|||||||
<v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip>
|
<v-tooltip activator="parent">{{ tt('Refresh') }}</v-tooltip>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</div>
|
</div>
|
||||||
|
<v-btn density="comfortable" color="default" variant="text" class="ml-2"
|
||||||
|
:icon="true" :disabled="loading"
|
||||||
|
v-if="showAccountBalanceTrendsCharts">
|
||||||
|
<v-icon :icon="mdiTuneVertical" />
|
||||||
|
<v-menu activator="parent">
|
||||||
|
<v-list>
|
||||||
|
<v-list-subheader :title="tt('Chart Type')"/>
|
||||||
|
<v-list-item :key="type.type"
|
||||||
|
:prepend-icon="chartTypeIconMap[type.type]"
|
||||||
|
:append-icon="chartType === type.type ? mdiCheck : undefined"
|
||||||
|
:title="type.displayName"
|
||||||
|
@click="chartType = type.type"
|
||||||
|
v-for="type in allChartTypes"></v-list-item>
|
||||||
|
<v-divider class="my-2"/>
|
||||||
|
<v-list-subheader :title="tt('Time Granularity')"/>
|
||||||
|
<v-list-item :prepend-icon="mdiCalendarTodayOutline"
|
||||||
|
:append-icon="chartDataDateAggregationType === undefined ? mdiCheck : undefined"
|
||||||
|
:title="tt('granularity.Daily')"
|
||||||
|
@click="chartDataDateAggregationType = undefined"></v-list-item>
|
||||||
|
<v-list-item :key="dateAggregationType.type"
|
||||||
|
:prepend-icon="chartDataDateAggregationTypeIconMap[dateAggregationType.type]"
|
||||||
|
:append-icon="chartDataDateAggregationType === dateAggregationType.type ? mdiCheck : undefined"
|
||||||
|
:title="dateAggregationType.displayName"
|
||||||
|
@click="chartDataDateAggregationType = dateAggregationType.type"
|
||||||
|
v-for="dateAggregationType in allDateAggregationTypes"></v-list-item>
|
||||||
|
</v-list>
|
||||||
|
</v-menu>
|
||||||
|
</v-btn>
|
||||||
<v-btn density="comfortable" color="default" variant="text" class="ml-2"
|
<v-btn density="comfortable" color="default" variant="text" class="ml-2"
|
||||||
:icon="true" :disabled="loading">
|
:icon="true" :disabled="loading">
|
||||||
<v-icon :icon="mdiDotsVertical" />
|
<v-icon :icon="mdiDotsVertical" />
|
||||||
@@ -27,6 +55,15 @@
|
|||||||
:title="tt('Update Closing Balance')"
|
:title="tt('Update Closing Balance')"
|
||||||
@click="updateClosingBalance()"></v-list-item>
|
@click="updateClosingBalance()"></v-list-item>
|
||||||
<v-divider class="my-2"/>
|
<v-divider class="my-2"/>
|
||||||
|
<v-list-item :prepend-icon="mdiChartBoxOutline"
|
||||||
|
:title="tt('Show Account Balance Trends')"
|
||||||
|
@click="showAccountBalanceTrendsCharts = true"
|
||||||
|
v-if="!showAccountBalanceTrendsCharts"></v-list-item>
|
||||||
|
<v-list-item :prepend-icon="mdiListBoxOutline"
|
||||||
|
:title="tt('Show Transaction List')"
|
||||||
|
@click="showAccountBalanceTrendsCharts = false"
|
||||||
|
v-if="showAccountBalanceTrendsCharts"></v-list-item>
|
||||||
|
<v-divider class="my-2"/>
|
||||||
<v-list-item :prepend-icon="mdiComma"
|
<v-list-item :prepend-icon="mdiComma"
|
||||||
:disabled="!reconciliationStatements || !reconciliationStatements.transactions || reconciliationStatements.transactions.length < 1"
|
:disabled="!reconciliationStatements || !reconciliationStatements.transactions || reconciliationStatements.transactions.length < 1"
|
||||||
@click="exportReconciliationStatements(KnownFileType.CSV)">
|
@click="exportReconciliationStatements(KnownFileType.CSV)">
|
||||||
@@ -109,6 +146,7 @@
|
|||||||
:no-data-text="loading ? '' : tt('No transaction data')"
|
:no-data-text="loading ? '' : tt('No transaction data')"
|
||||||
v-model:items-per-page="countPerPage"
|
v-model:items-per-page="countPerPage"
|
||||||
v-model:page="currentPage"
|
v-model:page="currentPage"
|
||||||
|
v-if="!showAccountBalanceTrendsCharts"
|
||||||
>
|
>
|
||||||
<template #item.time="{ item }">
|
<template #item.time="{ item }">
|
||||||
<span>{{ getDisplayDateTime(item) }}</span>
|
<span>{{ getDisplayDateTime(item) }}</span>
|
||||||
@@ -187,6 +225,27 @@
|
|||||||
</template>
|
</template>
|
||||||
</v-data-table>
|
</v-data-table>
|
||||||
|
|
||||||
|
<account-balance-trends-chart
|
||||||
|
:type="chartType"
|
||||||
|
:date-aggregation-type="chartDataDateAggregationType"
|
||||||
|
:fiscal-year-start="fiscalYearStart"
|
||||||
|
:items="[]"
|
||||||
|
:legend-name="isCurrentLiabilityAccount ? tt('Account Outstanding Balance') : tt('Account Balance')"
|
||||||
|
:account-currency="currentAccountCurrency"
|
||||||
|
:skeleton="true"
|
||||||
|
v-if="showAccountBalanceTrendsCharts && loading"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<account-balance-trends-chart
|
||||||
|
:type="chartType"
|
||||||
|
:date-aggregation-type="chartDataDateAggregationType"
|
||||||
|
:fiscal-year-start="fiscalYearStart"
|
||||||
|
:items="reconciliationStatements?.transactions"
|
||||||
|
:legend-name="isCurrentLiabilityAccount ? tt('Account Outstanding Balance') : tt('Account Balance')"
|
||||||
|
:account-currency="currentAccountCurrency"
|
||||||
|
v-if="showAccountBalanceTrendsCharts && !loading"
|
||||||
|
/>
|
||||||
|
|
||||||
<v-card-text class="overflow-y-visible">
|
<v-card-text class="overflow-y-visible">
|
||||||
<div class="w-100 d-flex justify-center mt-2 mt-sm-4 mt-md-6 gap-4">
|
<div class="w-100 d-flex justify-center mt-2 mt-sm-4 mt-md-6 gap-4">
|
||||||
<v-btn color="secondary" variant="tonal"
|
<v-btn color="secondary" variant="tonal"
|
||||||
@@ -219,6 +278,7 @@ import { useTransactionCategoriesStore } from '@/stores/transactionCategory.ts';
|
|||||||
import { useTransactionsStore } from '@/stores/transaction.ts';
|
import { useTransactionsStore } from '@/stores/transaction.ts';
|
||||||
|
|
||||||
import { TransactionType } from '@/core/transaction.ts';
|
import { TransactionType } from '@/core/transaction.ts';
|
||||||
|
import { TrendChartType, ChartDateAggregationType } from '@/core/statistics.ts';
|
||||||
import { KnownFileType } from '@/core/file.ts';
|
import { KnownFileType } from '@/core/file.ts';
|
||||||
import { Transaction, type TransactionReconciliationStatementResponseItem } from '@/models/transaction.ts';
|
import { Transaction, type TransactionReconciliationStatementResponseItem } from '@/models/transaction.ts';
|
||||||
|
|
||||||
@@ -229,7 +289,16 @@ import { startDownloadFile } from '@/lib/ui/common.ts';
|
|||||||
import {
|
import {
|
||||||
mdiRefresh,
|
mdiRefresh,
|
||||||
mdiArrowRight,
|
mdiArrowRight,
|
||||||
|
mdiTuneVertical,
|
||||||
mdiDotsVertical,
|
mdiDotsVertical,
|
||||||
|
mdiCheck,
|
||||||
|
mdiChartBoxOutline,
|
||||||
|
mdiListBoxOutline,
|
||||||
|
mdiChartBar,
|
||||||
|
mdiChartAreasplineVariant,
|
||||||
|
mdiCalendarTodayOutline,
|
||||||
|
mdiCalendarMonthOutline,
|
||||||
|
mdiLayersTripleOutline,
|
||||||
mdiInvoiceTextPlusOutline,
|
mdiInvoiceTextPlusOutline,
|
||||||
mdiInvoiceTextEditOutline,
|
mdiInvoiceTextEditOutline,
|
||||||
mdiComma,
|
mdiComma,
|
||||||
@@ -258,10 +327,13 @@ const {
|
|||||||
endTime,
|
endTime,
|
||||||
reconciliationStatements,
|
reconciliationStatements,
|
||||||
currentTimezoneOffsetMinutes,
|
currentTimezoneOffsetMinutes,
|
||||||
allAccountsMap,
|
fiscalYearStart,
|
||||||
allCategoriesMap,
|
allChartTypes,
|
||||||
|
allDateAggregationTypes,
|
||||||
currentAccountCurrency,
|
currentAccountCurrency,
|
||||||
isCurrentLiabilityAccount,
|
isCurrentLiabilityAccount,
|
||||||
|
allAccountsMap,
|
||||||
|
allCategoriesMap,
|
||||||
exportFileName,
|
exportFileName,
|
||||||
displayStartDateTime,
|
displayStartDateTime,
|
||||||
displayEndDateTime,
|
displayEndDateTime,
|
||||||
@@ -283,6 +355,18 @@ const accountsStore = useAccountsStore();
|
|||||||
const transactionCategoriesStore = useTransactionCategoriesStore();
|
const transactionCategoriesStore = useTransactionCategoriesStore();
|
||||||
const transactionsStore = useTransactionsStore();
|
const transactionsStore = useTransactionsStore();
|
||||||
|
|
||||||
|
const chartTypeIconMap = {
|
||||||
|
[TrendChartType.Column.type]: mdiChartBar,
|
||||||
|
[TrendChartType.Area.type]: mdiChartAreasplineVariant,
|
||||||
|
};
|
||||||
|
|
||||||
|
const chartDataDateAggregationTypeIconMap = {
|
||||||
|
[ChartDateAggregationType.Month.type]: mdiCalendarMonthOutline,
|
||||||
|
[ChartDateAggregationType.Quarter.type]: mdiLayersTripleOutline,
|
||||||
|
[ChartDateAggregationType.Year.type]: mdiLayersTripleOutline,
|
||||||
|
[ChartDateAggregationType.FiscalYear.type]: mdiLayersTripleOutline,
|
||||||
|
};
|
||||||
|
|
||||||
const amountInputDialog = useTemplateRef<AmountInputDialogType>('amountInputDialog');
|
const amountInputDialog = useTemplateRef<AmountInputDialogType>('amountInputDialog');
|
||||||
const snackbar = useTemplateRef<SnackBarType>('snackbar');
|
const snackbar = useTemplateRef<SnackBarType>('snackbar');
|
||||||
const editDialog = useTemplateRef<EditDialogType>('editDialog');
|
const editDialog = useTemplateRef<EditDialogType>('editDialog');
|
||||||
@@ -291,6 +375,9 @@ const showState = ref<boolean>(false);
|
|||||||
const loading = ref<boolean>(false);
|
const loading = ref<boolean>(false);
|
||||||
const currentPage = ref<number>(1);
|
const currentPage = ref<number>(1);
|
||||||
const countPerPage = ref<number>(10);
|
const countPerPage = ref<number>(10);
|
||||||
|
const showAccountBalanceTrendsCharts = ref<boolean>(false);
|
||||||
|
const chartType = ref<number>(TrendChartType.Default.type);
|
||||||
|
const chartDataDateAggregationType = ref<number | undefined>(undefined);
|
||||||
|
|
||||||
let rejectFunc: ((reason?: unknown) => void) | null = null;
|
let rejectFunc: ((reason?: unknown) => void) | null = null;
|
||||||
|
|
||||||
@@ -371,6 +458,9 @@ function open(options: { accountId: string, startTime: number, endTime: number }
|
|||||||
reconciliationStatements.value = undefined;
|
reconciliationStatements.value = undefined;
|
||||||
currentPage.value = 1;
|
currentPage.value = 1;
|
||||||
countPerPage.value = 10;
|
countPerPage.value = 10;
|
||||||
|
showAccountBalanceTrendsCharts.value = false;
|
||||||
|
chartType.value = TrendChartType.Default.type;
|
||||||
|
chartDataDateAggregationType.value = undefined;
|
||||||
showState.value = true;
|
showState.value = true;
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
|
||||||
|
|||||||
@@ -716,7 +716,7 @@ import {
|
|||||||
getMonth,
|
getMonth,
|
||||||
getBrowserTimezoneOffsetMinutes,
|
getBrowserTimezoneOffsetMinutes,
|
||||||
getActualUnixTimeForStore,
|
getActualUnixTimeForStore,
|
||||||
getSpecifiedDayFirstUnixTime,
|
getDayFirstUnixTimeBySpecifiedUnixTime,
|
||||||
getYearMonthFirstUnixTime,
|
getYearMonthFirstUnixTime,
|
||||||
getYearMonthLastUnixTime,
|
getYearMonthLastUnixTime,
|
||||||
getShiftedDateRangeAndDateType,
|
getShiftedDateRangeAndDateType,
|
||||||
@@ -1275,7 +1275,7 @@ function changeDateFilter(dateRange: TimeRangeAndDateType | number | null): void
|
|||||||
if (dateRange === DateRange.Custom.type || (isObject(dateRange) && dateRange.dateType === DateRange.Custom.type && !dateRange.minTime && !dateRange.maxTime)) { // Custom
|
if (dateRange === DateRange.Custom.type || (isObject(dateRange) && dateRange.dateType === DateRange.Custom.type && !dateRange.minTime && !dateRange.maxTime)) { // Custom
|
||||||
if (!query.value.minTime || !query.value.maxTime) {
|
if (!query.value.minTime || !query.value.maxTime) {
|
||||||
customMaxDatetime.value = getActualUnixTimeForStore(getCurrentUnixTime(), currentTimezoneOffsetMinutes.value, getBrowserTimezoneOffsetMinutes());
|
customMaxDatetime.value = getActualUnixTimeForStore(getCurrentUnixTime(), currentTimezoneOffsetMinutes.value, getBrowserTimezoneOffsetMinutes());
|
||||||
customMinDatetime.value = getSpecifiedDayFirstUnixTime(customMaxDatetime.value);
|
customMinDatetime.value = getDayFirstUnixTimeBySpecifiedUnixTime(customMaxDatetime.value);
|
||||||
} else {
|
} else {
|
||||||
customMaxDatetime.value = query.value.maxTime;
|
customMaxDatetime.value = query.value.maxTime;
|
||||||
customMinDatetime.value = query.value.minTime;
|
customMinDatetime.value = query.value.minTime;
|
||||||
|
|||||||
@@ -71,7 +71,7 @@
|
|||||||
|
|
||||||
<f7-list strong inset dividers media-list
|
<f7-list strong inset dividers media-list
|
||||||
class="skeleton-text margin-vertical transaction-info-list reconciliation-statement-list"
|
class="skeleton-text margin-vertical transaction-info-list reconciliation-statement-list"
|
||||||
v-if="finishQuery && loading">
|
v-if="finishQuery && !showAccountBalanceTrendsCharts && loading">
|
||||||
<ul>
|
<ul>
|
||||||
<f7-list-item chevron-center
|
<f7-list-item chevron-center
|
||||||
:key="index"
|
:key="index"
|
||||||
@@ -130,14 +130,14 @@
|
|||||||
</f7-list>
|
</f7-list>
|
||||||
|
|
||||||
<f7-list strong inset dividers class="margin-vertical"
|
<f7-list strong inset dividers class="margin-vertical"
|
||||||
v-if="finishQuery && !loading && (!allReconciliationStatementVirtualListItems || !allReconciliationStatementVirtualListItems.length)">
|
v-if="finishQuery && !showAccountBalanceTrendsCharts && !loading && (!allReconciliationStatementVirtualListItems || !allReconciliationStatementVirtualListItems.length)">
|
||||||
<f7-list-item :title="tt('No transaction data')"></f7-list-item>
|
<f7-list-item :title="tt('No transaction data')"></f7-list-item>
|
||||||
</f7-list>
|
</f7-list>
|
||||||
|
|
||||||
<f7-list strong inset dividers media-list virtual-list
|
<f7-list strong inset dividers media-list virtual-list
|
||||||
class="margin-vertical transaction-info-list reconciliation-statement-list"
|
class="margin-vertical transaction-info-list reconciliation-statement-list"
|
||||||
:virtual-list-params="{ items: allReconciliationStatementVirtualListItems, renderExternal, height: 'auto' }"
|
:virtual-list-params="{ items: allReconciliationStatementVirtualListItems, renderExternal, height: 'auto' }"
|
||||||
v-if="finishQuery && !loading && allReconciliationStatementVirtualListItems && allReconciliationStatementVirtualListItems.length">
|
v-if="finishQuery && !showAccountBalanceTrendsCharts && !loading && allReconciliationStatementVirtualListItems && allReconciliationStatementVirtualListItems.length">
|
||||||
<ul>
|
<ul>
|
||||||
<f7-list-item chevron-center
|
<f7-list-item chevron-center
|
||||||
:key="item.index"
|
:key="item.index"
|
||||||
@@ -231,6 +231,50 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</f7-list>
|
</f7-list>
|
||||||
|
|
||||||
|
<f7-card v-if="finishQuery && showAccountBalanceTrendsCharts">
|
||||||
|
<f7-card-header class="no-border display-block">
|
||||||
|
<div class="statistics-chart-header display-flex full-line justify-content-space-between">
|
||||||
|
<div></div>
|
||||||
|
<div class="align-self-flex-end">
|
||||||
|
<span style="margin-right: 4px;">{{ tt('Time Granularity') }}</span>
|
||||||
|
<f7-link :class="{ 'disabled': loading }" href="#" popover-open=".chart-data-date-aggregation-type-popover-menu">{{ chartDataDateAggregationTypeDisplayName }}</f7-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</f7-card-header>
|
||||||
|
<f7-card-content style="margin-top: -14px" :padding="false">
|
||||||
|
<account-balance-trends-bar-chart
|
||||||
|
:loading="loading"
|
||||||
|
:date-aggregation-type="chartDataDateAggregationType"
|
||||||
|
:fiscal-year-start="fiscalYearStart"
|
||||||
|
:items="reconciliationStatements?.transactions"
|
||||||
|
:account-currency="currentAccountCurrency"
|
||||||
|
/>
|
||||||
|
</f7-card-content>
|
||||||
|
</f7-card>
|
||||||
|
|
||||||
|
<f7-popover class="chart-data-date-aggregation-type-popover-menu"
|
||||||
|
v-model:opened="showChartDataDateAggregationTypePopover">
|
||||||
|
<f7-list dividers>
|
||||||
|
<f7-list-item :title="tt('granularity.Daily')"
|
||||||
|
:class="{ 'list-item-selected': chartDataDateAggregationType === undefined }"
|
||||||
|
key="daily"
|
||||||
|
@click="setChartDataDateAggregationType(undefined)">
|
||||||
|
<template #after>
|
||||||
|
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="chartDataDateAggregationType === undefined"></f7-icon>
|
||||||
|
</template>
|
||||||
|
</f7-list-item>
|
||||||
|
<f7-list-item :title="dateAggregationType.displayName"
|
||||||
|
:class="{ 'list-item-selected': chartDataDateAggregationType === dateAggregationType.type }"
|
||||||
|
:key="dateAggregationType.type"
|
||||||
|
v-for="dateAggregationType in allDateAggregationTypes"
|
||||||
|
@click="setChartDataDateAggregationType(dateAggregationType.type)">
|
||||||
|
<template #after>
|
||||||
|
<f7-icon class="list-item-checked-icon" f7="checkmark_alt" v-if="chartDataDateAggregationType === dateAggregationType.type"></f7-icon>
|
||||||
|
</template>
|
||||||
|
</f7-list-item>
|
||||||
|
</f7-list>
|
||||||
|
</f7-popover>
|
||||||
|
|
||||||
<date-range-selection-sheet :title="tt('Custom Date Range')"
|
<date-range-selection-sheet :title="tt('Custom Date Range')"
|
||||||
:min-time="startTime"
|
:min-time="startTime"
|
||||||
:max-time="endTime"
|
:max-time="endTime"
|
||||||
@@ -256,6 +300,8 @@
|
|||||||
</f7-actions-group>
|
</f7-actions-group>
|
||||||
<f7-actions-group>
|
<f7-actions-group>
|
||||||
<f7-actions-button :class="{ 'disabled': loading }" @click="reload(true)">{{ tt('Refresh') }}</f7-actions-button>
|
<f7-actions-button :class="{ 'disabled': loading }" @click="reload(true)">{{ tt('Refresh') }}</f7-actions-button>
|
||||||
|
<f7-actions-button :class="{ 'disabled': loading }" @click="showAccountBalanceTrendsCharts = true" v-if="!showAccountBalanceTrendsCharts">{{ tt('Show Account Balance Trends') }}</f7-actions-button>
|
||||||
|
<f7-actions-button :class="{ 'disabled': loading }" @click="showAccountBalanceTrendsCharts = false" v-if="showAccountBalanceTrendsCharts">{{ tt('Show Transaction List') }}</f7-actions-button>
|
||||||
</f7-actions-group>
|
</f7-actions-group>
|
||||||
<f7-actions-group>
|
<f7-actions-group>
|
||||||
<f7-actions-button bold close>{{ tt('Cancel') }}</f7-actions-button>
|
<f7-actions-button bold close>{{ tt('Cancel') }}</f7-actions-button>
|
||||||
@@ -292,7 +338,7 @@ import { TransactionType } from '@/core/transaction.ts';
|
|||||||
import { TRANSACTION_MIN_AMOUNT, TRANSACTION_MAX_AMOUNT } from '@/consts/transaction.ts';
|
import { TRANSACTION_MIN_AMOUNT, TRANSACTION_MAX_AMOUNT } from '@/consts/transaction.ts';
|
||||||
import { type TransactionReconciliationStatementResponseItem } from '@/models/transaction.ts';
|
import { type TransactionReconciliationStatementResponseItem } from '@/models/transaction.ts';
|
||||||
|
|
||||||
import { isDefined, isEquals } from '@/lib/common.ts';
|
import { isDefined, isEquals, findDisplayNameByType } from '@/lib/common.ts';
|
||||||
import {
|
import {
|
||||||
getCurrentUnixTime,
|
getCurrentUnixTime,
|
||||||
getDateTypeByDateRange,
|
getDateTypeByDateRange,
|
||||||
@@ -330,6 +376,7 @@ const {
|
|||||||
reconciliationStatements,
|
reconciliationStatements,
|
||||||
firstDayOfWeek,
|
firstDayOfWeek,
|
||||||
fiscalYearStart,
|
fiscalYearStart,
|
||||||
|
allDateAggregationTypes,
|
||||||
currentTimezoneOffsetMinutes,
|
currentTimezoneOffsetMinutes,
|
||||||
isCurrentLiabilityAccount,
|
isCurrentLiabilityAccount,
|
||||||
allCategoriesMap,
|
allCategoriesMap,
|
||||||
@@ -358,12 +405,15 @@ const finishQuery = ref<boolean>(false);
|
|||||||
const loading = ref<boolean>(false);
|
const loading = ref<boolean>(false);
|
||||||
const loadingError = ref<unknown | null>(null);
|
const loadingError = ref<unknown | null>(null);
|
||||||
const queryDateRangeType = ref<number>(DateRange.ThisMonth.type);
|
const queryDateRangeType = ref<number>(DateRange.ThisMonth.type);
|
||||||
|
const showAccountBalanceTrendsCharts = ref<boolean>(false);
|
||||||
|
const chartDataDateAggregationType = ref<number | undefined>(undefined);
|
||||||
const transactionToDelete = ref<TransactionReconciliationStatementResponseItem | null>(null);
|
const transactionToDelete = ref<TransactionReconciliationStatementResponseItem | null>(null);
|
||||||
const newClosingBalance = ref<number>(0);
|
const newClosingBalance = ref<number>(0);
|
||||||
const showCustomDateRangeSheet = ref<boolean>(false);
|
const showCustomDateRangeSheet = ref<boolean>(false);
|
||||||
const showNewClosingBalanceSheet = ref<boolean>(false);
|
const showNewClosingBalanceSheet = ref<boolean>(false);
|
||||||
const showMoreActionSheet = ref<boolean>(false);
|
const showMoreActionSheet = ref<boolean>(false);
|
||||||
const showDeleteActionSheet = ref<boolean>(false);
|
const showDeleteActionSheet = ref<boolean>(false);
|
||||||
|
const showChartDataDateAggregationTypePopover = ref<boolean>(false);
|
||||||
const virtualDataItems = ref<ReconciliationStatementVirtualListData>({
|
const virtualDataItems = ref<ReconciliationStatementVirtualListData>({
|
||||||
items: [],
|
items: [],
|
||||||
topPosition: 0
|
topPosition: 0
|
||||||
@@ -407,6 +457,14 @@ const allReconciliationStatementVirtualListItems = computed<ReconciliationStatem
|
|||||||
return ret;
|
return ret;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const chartDataDateAggregationTypeDisplayName = computed<string>(() => {
|
||||||
|
if (chartDataDateAggregationType.value === undefined) {
|
||||||
|
return tt('granularity.Daily');
|
||||||
|
}
|
||||||
|
|
||||||
|
return findDisplayNameByType(allDateAggregationTypes.value, chartDataDateAggregationType.value) || tt('Unknown');
|
||||||
|
});
|
||||||
|
|
||||||
function getTransactionDomId(transaction: TransactionReconciliationStatementResponseItem): string {
|
function getTransactionDomId(transaction: TransactionReconciliationStatementResponseItem): string {
|
||||||
return 'transaction_' + transaction.id;
|
return 'transaction_' + transaction.id;
|
||||||
}
|
}
|
||||||
@@ -592,6 +650,11 @@ function removeTransaction(transaction: TransactionReconciliationStatementRespon
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setChartDataDateAggregationType(type: number | undefined): void {
|
||||||
|
chartDataDateAggregationType.value = type;
|
||||||
|
showChartDataDateAggregationTypePopover.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
function renderExternal(vl: unknown, vlData: ReconciliationStatementVirtualListData): void {
|
function renderExternal(vl: unknown, vlData: ReconciliationStatementVirtualListData): void {
|
||||||
virtualDataItems.value = vlData;
|
virtualDataItems.value = vlData;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -638,7 +638,7 @@ import {
|
|||||||
getActualUnixTimeForStore,
|
getActualUnixTimeForStore,
|
||||||
getYear,
|
getYear,
|
||||||
getMonth,
|
getMonth,
|
||||||
getSpecifiedDayFirstUnixTime,
|
getDayFirstUnixTimeBySpecifiedUnixTime,
|
||||||
getYearMonthFirstUnixTime,
|
getYearMonthFirstUnixTime,
|
||||||
getYearMonthLastUnixTime,
|
getYearMonthLastUnixTime,
|
||||||
getShiftedDateRangeAndDateType,
|
getShiftedDateRangeAndDateType,
|
||||||
@@ -1066,7 +1066,7 @@ function changeDateFilter(dateType: number): void {
|
|||||||
if (dateType === DateRange.Custom.type) { // Custom
|
if (dateType === DateRange.Custom.type) { // Custom
|
||||||
if (!query.value.minTime || !query.value.maxTime) {
|
if (!query.value.minTime || !query.value.maxTime) {
|
||||||
customMaxDatetime.value = getActualUnixTimeForStore(getCurrentUnixTime(), currentTimezoneOffsetMinutes.value, getBrowserTimezoneOffsetMinutes());
|
customMaxDatetime.value = getActualUnixTimeForStore(getCurrentUnixTime(), currentTimezoneOffsetMinutes.value, getBrowserTimezoneOffsetMinutes());
|
||||||
customMinDatetime.value = getSpecifiedDayFirstUnixTime(customMaxDatetime.value);
|
customMinDatetime.value = getDayFirstUnixTimeBySpecifiedUnixTime(customMaxDatetime.value);
|
||||||
} else {
|
} else {
|
||||||
customMaxDatetime.value = query.value.maxTime;
|
customMaxDatetime.value = query.value.maxTime;
|
||||||
customMinDatetime.value = query.value.minTime;
|
customMinDatetime.value = query.value.minTime;
|
||||||
|
|||||||
Reference in New Issue
Block a user