931 lines
45 KiB
Vue
931 lines
45 KiB
Vue
<template>
|
|
<f7-page @page:afterin="onPageAfterIn">
|
|
<f7-navbar>
|
|
<f7-nav-left :back-link="$t('Back')"></f7-nav-left>
|
|
<f7-nav-title :title="$t(title)"></f7-nav-title>
|
|
<f7-nav-right>
|
|
<f7-link icon-f7="ellipsis" @click="showMoreActionSheet = true" v-if="mode !== 'view'"></f7-link>
|
|
<f7-link :class="{ 'disabled': inputIsEmpty || submitting }" :text="$t(saveButtonTitle)" @click="save" v-if="mode !== 'view'"></f7-link>
|
|
</f7-nav-right>
|
|
|
|
<f7-subnavbar>
|
|
<f7-segmented strong :class="{ 'disabled': mode !== 'add' }">
|
|
<f7-button :text="$t('Expense')" :active="transaction.type === $constants.transaction.allTransactionTypes.Expense" @click="transaction.type = $constants.transaction.allTransactionTypes.Expense"></f7-button>
|
|
<f7-button :text="$t('Income')" :active="transaction.type === $constants.transaction.allTransactionTypes.Income" @click="transaction.type = $constants.transaction.allTransactionTypes.Income"></f7-button>
|
|
<f7-button :text="$t('Transfer')" :active="transaction.type === $constants.transaction.allTransactionTypes.Transfer" @click="transaction.type = $constants.transaction.allTransactionTypes.Transfer"></f7-button>
|
|
</f7-segmented>
|
|
</f7-subnavbar>
|
|
</f7-navbar>
|
|
|
|
<f7-card class="skeleton-text" v-if="loading">
|
|
<f7-card-content class="no-safe-areas" :padding="false">
|
|
<f7-list>
|
|
<f7-list-item
|
|
class="transaction-edit-amount"
|
|
style="font-size: 40px;"
|
|
header="Expense Amount" title="0.00">
|
|
</f7-list-item>
|
|
<f7-list-item class="transaction-edit-category" header="Category" title="Category Names" link="#"></f7-list-item>
|
|
<f7-list-item class="transaction-edit-account" header="Account" title="Account Name" link="#"></f7-list-item>
|
|
<f7-list-input label="Transaction Time" placeholder="YYYY/MM/DD HH:mm"></f7-list-input>
|
|
<f7-list-item class="transaction-edit-timezone" header="Transaction Time Zone" title="(UTC XX:XX) System Default" link="#"></f7-list-item>
|
|
<f7-list-item header="Tags" link="#">
|
|
<f7-block class="margin-top-half no-padding" slot="footer">
|
|
<f7-chip class="transaction-edit-tag" text="None"></f7-chip>
|
|
</f7-block>
|
|
</f7-list-item>
|
|
<f7-list-input type="textarea" label="Description" placeholder="Your transaction description (optional)"></f7-list-input>
|
|
</f7-list>
|
|
</f7-card-content>
|
|
</f7-card>
|
|
|
|
<f7-card v-else-if="!loading">
|
|
<f7-card-content class="no-safe-areas" :padding="false">
|
|
<f7-list form :class="{ 'disabled': mode === 'view' }">
|
|
<f7-list-item
|
|
class="transaction-edit-amount"
|
|
link="#" no-chevron
|
|
:class="{ 'color-theme-teal': transaction.type === $constants.transaction.allTransactionTypes.Expense, 'color-theme-red': transaction.type === $constants.transaction.allTransactionTypes.Income }"
|
|
:style="{ fontSize: sourceAmountFontSize + 'px' }"
|
|
:header="$t(sourceAmountName)"
|
|
:title="transaction.sourceAmount | finalAmount(transaction.hideAmount) | currency"
|
|
@click="showSourceAmountSheet = true"
|
|
>
|
|
<number-pad-sheet :min-value="$constants.transaction.minAmount"
|
|
:max-value="$constants.transaction.maxAmount"
|
|
:show.sync="showSourceAmountSheet"
|
|
v-model="transaction.sourceAmount"
|
|
></number-pad-sheet>
|
|
</f7-list-item>
|
|
|
|
<f7-list-item
|
|
class="transaction-edit-amount"
|
|
link="#" no-chevron
|
|
:style="{ fontSize: destinationAmountFontSize + 'px' }"
|
|
:header="$t('Transfer In Amount')"
|
|
:title="transaction.destinationAmount | finalAmount(transaction.hideAmount) | currency"
|
|
@click="showDestinationAmountSheet = true"
|
|
v-if="transaction.type === $constants.transaction.allTransactionTypes.Transfer"
|
|
>
|
|
<number-pad-sheet :min-value="$constants.transaction.minAmount"
|
|
:max-value="$constants.transaction.maxAmount"
|
|
:show.sync="showDestinationAmountSheet"
|
|
v-model="transaction.destinationAmount"
|
|
></number-pad-sheet>
|
|
</f7-list-item>
|
|
|
|
<f7-list-item
|
|
class="transaction-edit-category"
|
|
key="expenseCategorySelection"
|
|
link="#"
|
|
:class="{ 'disabled': !hasAvailableExpenseCategories }"
|
|
:header="$t('Category')"
|
|
@click="showCategorySheet = true"
|
|
v-if="transaction.type === $constants.transaction.allTransactionTypes.Expense"
|
|
>
|
|
<div slot="title" class="transaction-edit-category-title">
|
|
<span>{{ transaction.expenseCategory | primaryCategoryName(allCategories[$constants.category.allCategoryTypes.Expense]) }}</span>
|
|
<f7-icon class="category-separate-icon" f7="chevron_right"></f7-icon>
|
|
<span>{{ transaction.expenseCategory | secondaryCategoryName(allCategories[$constants.category.allCategoryTypes.Expense]) }}</span>
|
|
</div>
|
|
<tree-view-selection-sheet primary-key-field="id" primary-value-field="id" primary-title-field="name"
|
|
primary-icon-field="icon" primary-icon-type="category" primary-color-field="color"
|
|
primary-sub-items-field="subCategories"
|
|
secondary-key-field="id" secondary-value-field="id" secondary-title-field="name"
|
|
secondary-icon-field="icon" secondary-icon-type="category" secondary-color-field="color"
|
|
:items="allCategories[$constants.category.allCategoryTypes.Expense]"
|
|
:show.sync="showCategorySheet"
|
|
v-model="transaction.expenseCategory">
|
|
</tree-view-selection-sheet>
|
|
</f7-list-item>
|
|
|
|
<f7-list-item
|
|
class="transaction-edit-category"
|
|
key="incomeCategorySelection"
|
|
link="#"
|
|
:class="{ 'disabled': !hasAvailableIncomeCategories }"
|
|
:header="$t('Category')"
|
|
@click="showCategorySheet = true"
|
|
v-if="transaction.type === $constants.transaction.allTransactionTypes.Income"
|
|
>
|
|
<div slot="title" class="transaction-edit-category-title">
|
|
<span>{{ transaction.incomeCategory | primaryCategoryName(allCategories[$constants.category.allCategoryTypes.Income]) }}</span>
|
|
<f7-icon class="category-separate-icon" f7="chevron_right"></f7-icon>
|
|
<span>{{ transaction.incomeCategory | secondaryCategoryName(allCategories[$constants.category.allCategoryTypes.Income]) }}</span>
|
|
</div>
|
|
<tree-view-selection-sheet primary-key-field="id" primary-value-field="id" primary-title-field="name"
|
|
primary-icon-field="icon" primary-icon-type="category" primary-color-field="color"
|
|
primary-sub-items-field="subCategories"
|
|
secondary-key-field="id" secondary-value-field="id" secondary-title-field="name"
|
|
secondary-icon-field="icon" secondary-icon-type="category" secondary-color-field="color"
|
|
:items="allCategories[$constants.category.allCategoryTypes.Income]"
|
|
:show.sync="showCategorySheet"
|
|
v-model="transaction.incomeCategory">
|
|
</tree-view-selection-sheet>
|
|
</f7-list-item>
|
|
|
|
<f7-list-item
|
|
class="transaction-edit-category"
|
|
key="transferCategorySelection"
|
|
link="#"
|
|
:class="{ 'disabled': !hasAvailableTransferCategories }"
|
|
:header="$t('Category')"
|
|
@click="showCategorySheet = true"
|
|
v-if="transaction.type === $constants.transaction.allTransactionTypes.Transfer"
|
|
>
|
|
<div slot="title" class="transaction-edit-category-title">
|
|
<span>{{ transaction.transferCategory | primaryCategoryName(allCategories[$constants.category.allCategoryTypes.Transfer]) }}</span>
|
|
<f7-icon class="category-separate-icon" f7="chevron_right"></f7-icon>
|
|
<span>{{ transaction.transferCategory | secondaryCategoryName(allCategories[$constants.category.allCategoryTypes.Transfer]) }}</span>
|
|
</div>
|
|
<tree-view-selection-sheet primary-key-field="id" primary-value-field="id" primary-title-field="name"
|
|
primary-icon-field="icon" primary-icon-type="category" primary-color-field="color"
|
|
primary-sub-items-field="subCategories"
|
|
secondary-key-field="id" secondary-value-field="id" secondary-title-field="name"
|
|
secondary-icon-field="icon" secondary-icon-type="category" secondary-color-field="color"
|
|
:items="allCategories[$constants.category.allCategoryTypes.Transfer]"
|
|
:show.sync="showCategorySheet"
|
|
v-model="transaction.transferCategory">
|
|
</tree-view-selection-sheet>
|
|
</f7-list-item>
|
|
|
|
<f7-list-item
|
|
class="transaction-edit-account"
|
|
link="#"
|
|
:class="{ 'disabled': !allVisibleAccounts.length }"
|
|
:header="$t(sourceAccountName)"
|
|
:title="transaction.sourceAccountId | accountName(allAccounts)"
|
|
@click="showSourceAccountSheet = true"
|
|
>
|
|
<two-column-list-item-selection-sheet primary-key-field="id" primary-value-field="category"
|
|
primary-title-field="name" primary-footer-field="displayBalance"
|
|
primary-icon-field="icon" primary-icon-type="account"
|
|
primary-sub-items-field="accounts"
|
|
:primary-title-i18n="true"
|
|
secondary-key-field="id" secondary-value-field="id"
|
|
secondary-title-field="name" secondary-footer-field="displayBalance"
|
|
secondary-icon-field="icon" secondary-icon-type="account" secondary-color-field="color"
|
|
:items="categorizedAccounts"
|
|
:show.sync="showSourceAccountSheet"
|
|
v-model="transaction.sourceAccountId">
|
|
</two-column-list-item-selection-sheet>
|
|
</f7-list-item>
|
|
|
|
<f7-list-item
|
|
class="transaction-edit-account"
|
|
link="#"
|
|
:class="{ 'disabled': !allVisibleAccounts.length }"
|
|
:header="$t('Destination Account')"
|
|
:title="transaction.destinationAccountId | accountName(allAccounts)"
|
|
v-if="transaction.type === $constants.transaction.allTransactionTypes.Transfer"
|
|
@click="showDestinationAccountSheet = true"
|
|
>
|
|
<two-column-list-item-selection-sheet primary-key-field="id" primary-value-field="category"
|
|
primary-title-field="name" primary-footer-field="displayBalance"
|
|
primary-icon-field="icon" primary-icon-type="account"
|
|
primary-sub-items-field="accounts"
|
|
:primary-title-i18n="true"
|
|
secondary-key-field="id" secondary-value-field="id"
|
|
secondary-title-field="name" secondary-footer-field="displayBalance"
|
|
secondary-icon-field="icon" secondary-icon-type="account" secondary-color-field="color"
|
|
:items="categorizedAccounts"
|
|
:show.sync="showDestinationAccountSheet"
|
|
v-model="transaction.destinationAccountId">
|
|
</two-column-list-item-selection-sheet>
|
|
</f7-list-item>
|
|
|
|
<f7-list-input
|
|
:label="$t('Transaction Time')"
|
|
type="datetime-local"
|
|
class="transaction-edit-time"
|
|
:value="transaction.time"
|
|
@input="transaction.time = $event.target.value"
|
|
>
|
|
</f7-list-input>
|
|
|
|
<f7-list-item
|
|
class="transaction-edit-timezone list-item-no-item-after"
|
|
:header="$t('Transaction Time Zone')"
|
|
smart-select :smart-select-params="{ openIn: 'popup', pageTitle: $t('Transaction Time Zone'), searchbar: true, searchbarPlaceholder: $t('Timezone'), searchbarDisableText: $t('Cancel'), closeOnSelect: true, popupCloseLinkText: $t('Done'), scrollToSelectedItem: true }">
|
|
<select v-model="transaction.timeZone">
|
|
<option v-for="timezone in allTimezones"
|
|
:key="timezone.name"
|
|
:value="timezone.name">{{ `(UTC${timezone.utcOffset}) ${timezone.displayName}` }}</option>
|
|
</select>
|
|
<f7-block slot="title" class="transaction-edit-timezone-title no-padding">
|
|
<span>{{ transaction.utcOffset | utcOffset }}</span>
|
|
<span class="transaction-edit-timezone-name" v-if="transaction.timeZone || transaction.timeZone === ''">{{ transaction.timeZone | timeZoneName(allTimezones) }}</span>
|
|
</f7-block>
|
|
</f7-list-item>
|
|
|
|
<f7-list-item
|
|
link="#"
|
|
:header="$t('Tags')"
|
|
@click="showTransactionTagSheet = true"
|
|
>
|
|
<transaction-tag-selection-sheet :items="allTags"
|
|
:show.sync="showTransactionTagSheet"
|
|
v-model="transaction.tagIds">
|
|
</transaction-tag-selection-sheet>
|
|
|
|
<f7-block class="margin-top-half no-padding" slot="footer" v-if="transaction.tagIds && transaction.tagIds.length">
|
|
<f7-chip class="transaction-edit-tag" media-bg-color="black"
|
|
v-for="tagId in transaction.tagIds"
|
|
:key="tagId"
|
|
:text="tagId | tagName(allTags)">
|
|
<f7-icon slot="media" f7="number"></f7-icon>
|
|
</f7-chip>
|
|
</f7-block>
|
|
<f7-block class="margin-top-half no-padding" slot="footer" v-else-if="!transaction.tagIds || !transaction.tagIds.length">
|
|
<f7-chip class="transaction-edit-tag" :text="$t('None')">
|
|
</f7-chip>
|
|
</f7-block>
|
|
</f7-list-item>
|
|
|
|
<f7-list-input
|
|
type="textarea"
|
|
class="transaction-edit-comment textarea-auto-size"
|
|
style="height: auto"
|
|
:label="$t('Description')"
|
|
:placeholder="mode !== 'view' ? $t('Your transaction description (optional)') : ''"
|
|
:value="transaction.comment"
|
|
@input="transaction.comment = $event.target.value"
|
|
></f7-list-input>
|
|
</f7-list>
|
|
</f7-card-content>
|
|
</f7-card>
|
|
|
|
<f7-actions close-by-outside-click close-on-escape :opened="showMoreActionSheet" @actions:closed="showMoreActionSheet = false">
|
|
<f7-actions-group>
|
|
<f7-actions-button v-if="transaction.hideAmount" @click="transaction.hideAmount = false">{{ $t('Show Amount') }}</f7-actions-button>
|
|
<f7-actions-button v-if="!transaction.hideAmount" @click="transaction.hideAmount = true">{{ $t('Hide Amount') }}</f7-actions-button>
|
|
</f7-actions-group>
|
|
<f7-actions-group>
|
|
<f7-actions-button bold close>{{ $t('Cancel') }}</f7-actions-button>
|
|
</f7-actions-group>
|
|
</f7-actions>
|
|
|
|
<f7-toolbar tabbar bottom v-if="mode !== 'view'">
|
|
<f7-link :class="{ 'disabled': inputIsEmpty || submitting }" @click="save">
|
|
{{ $t(saveButtonTitle) }}
|
|
</f7-link>
|
|
</f7-toolbar>
|
|
</f7-page>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
data() {
|
|
const self = this;
|
|
const query = self.$f7route.query;
|
|
const now = self.$utilities.getCurrentUnixTime();
|
|
const currentTimezone = self.$locale.getTimezone();
|
|
|
|
let defaultType = self.$constants.transaction.allTransactionTypes.Expense;
|
|
|
|
if (query.type === self.$constants.transaction.allTransactionTypes.Income.toString()) {
|
|
defaultType = self.$constants.transaction.allTransactionTypes.Income;
|
|
} else if (query.type === self.$constants.transaction.allTransactionTypes.Transfer.toString()) {
|
|
defaultType = self.$constants.transaction.allTransactionTypes.Transfer;
|
|
}
|
|
|
|
return {
|
|
mode: 'add',
|
|
editTransactionId: null,
|
|
transaction: {
|
|
type: defaultType,
|
|
unixTime: now,
|
|
time: self.$utilities.formatUnixTime(now, 'YYYY-MM-DDTHH:mm'),
|
|
timeZone: currentTimezone,
|
|
utcOffset: self.$utilities.getTimezoneOffsetMinutes(currentTimezone),
|
|
expenseCategory: '',
|
|
incomeCategory: '',
|
|
transferCategory: '',
|
|
sourceAccountId: '',
|
|
destinationAccountId: '',
|
|
sourceAmount: 0,
|
|
destinationAmount: 0,
|
|
hideAmount: false,
|
|
tagIds: [],
|
|
comment: ''
|
|
},
|
|
loading: true,
|
|
loadingError: null,
|
|
submitting: false,
|
|
showAccountBalance: self.$settings.isShowAccountBalance(),
|
|
showMoreActionSheet: false,
|
|
showSourceAmountSheet: false,
|
|
showDestinationAmountSheet: false,
|
|
showCategorySheet: false,
|
|
showSourceAccountSheet: false,
|
|
showDestinationAccountSheet: false,
|
|
showTransactionTagSheet: false
|
|
};
|
|
},
|
|
computed: {
|
|
title() {
|
|
if (this.mode === 'add') {
|
|
return 'Add Transaction';
|
|
} else if (this.mode === 'edit') {
|
|
return 'Edit Transaction';
|
|
} else {
|
|
return 'Transaction Detail';
|
|
}
|
|
},
|
|
saveButtonTitle() {
|
|
if (this.mode === 'add') {
|
|
return 'Add';
|
|
} else {
|
|
return 'Save';
|
|
}
|
|
},
|
|
sourceAmountName() {
|
|
if (this.transaction.type === this.$constants.transaction.allTransactionTypes.Expense) {
|
|
return 'Expense Amount';
|
|
} else if (this.transaction.type === this.$constants.transaction.allTransactionTypes.Income) {
|
|
return 'Income Amount';
|
|
} else if (this.transaction.type === this.$constants.transaction.allTransactionTypes.Transfer) {
|
|
return 'Transfer Out Amount';
|
|
} else {
|
|
return '';
|
|
}
|
|
},
|
|
sourceAccountName() {
|
|
if (this.transaction.type === this.$constants.transaction.allTransactionTypes.Expense || this.transaction.type === this.$constants.transaction.allTransactionTypes.Income) {
|
|
return 'Account';
|
|
} else if (this.transaction.type === this.$constants.transaction.allTransactionTypes.Transfer) {
|
|
return 'Source Account';
|
|
} else {
|
|
return '';
|
|
}
|
|
},
|
|
defaultCurrency() {
|
|
return this.$store.getters.currentUserDefaultCurrency;
|
|
},
|
|
defaultTimezoneOffset() {
|
|
return this.$locale.defaultTimezoneOffset;
|
|
},
|
|
allTimezones() {
|
|
return this.$locale.getAllTimezones(true);
|
|
},
|
|
allAccounts() {
|
|
return this.$store.getters.allPlainAccounts;
|
|
},
|
|
allVisibleAccounts() {
|
|
return this.$store.getters.allVisiblePlainAccounts;
|
|
},
|
|
allAccountsMap() {
|
|
return this.$store.state.allAccountsMap;
|
|
},
|
|
categorizedAccounts() {
|
|
const categorizedAccounts = this.$utilities.copyObjectTo(this.$utilities.getCategorizedAccounts(this.allVisibleAccounts), {});
|
|
|
|
for (let category in categorizedAccounts) {
|
|
if (!Object.prototype.hasOwnProperty.call(categorizedAccounts, category)) {
|
|
continue;
|
|
}
|
|
|
|
const accountCategory = categorizedAccounts[category];
|
|
|
|
if (accountCategory.accounts) {
|
|
for (let i = 0; i < accountCategory.accounts.length; i++) {
|
|
const account = accountCategory.accounts[i];
|
|
|
|
if (this.showAccountBalance && account.isAsset) {
|
|
account.displayBalance = this.$options.filters.currency(account.balance, account.currency);
|
|
} else if (this.showAccountBalance && account.isLiability) {
|
|
account.displayBalance = this.$options.filters.currency(-account.balance, account.currency);
|
|
} else {
|
|
account.displayBalance = '***';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this.showAccountBalance) {
|
|
const accountsBalance = this.$utilities.getAllFilteredAccountsBalance(categorizedAccounts, account => account.category === accountCategory.category);
|
|
let totalBalance = 0;
|
|
let hasUnCalculatedAmount = false;
|
|
|
|
for (let i = 0; i < accountsBalance.length; i++) {
|
|
if (accountsBalance[i].currency === this.defaultCurrency) {
|
|
if (accountsBalance[i].isAsset) {
|
|
totalBalance += accountsBalance[i].balance;
|
|
} else if (accountsBalance[i].isLiability) {
|
|
totalBalance -= accountsBalance[i].balance;
|
|
}
|
|
} else {
|
|
const balance = this.$store.getters.getExchangedAmount(accountsBalance[i].balance, accountsBalance[i].currency, this.defaultCurrency);
|
|
|
|
if (!this.$utilities.isNumber(balance)) {
|
|
hasUnCalculatedAmount = true;
|
|
continue;
|
|
}
|
|
|
|
if (accountsBalance[i].isAsset) {
|
|
totalBalance += Math.floor(balance);
|
|
} else if (accountsBalance[i].isLiability) {
|
|
totalBalance -= Math.floor(balance);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hasUnCalculatedAmount) {
|
|
totalBalance = totalBalance + '+';
|
|
}
|
|
|
|
accountCategory.displayBalance = this.$options.filters.currency(totalBalance, this.defaultCurrency);
|
|
} else {
|
|
accountCategory.displayBalance = '***';
|
|
}
|
|
}
|
|
|
|
return categorizedAccounts;
|
|
},
|
|
allCategories() {
|
|
return this.$store.state.allTransactionCategories;
|
|
},
|
|
allTags() {
|
|
return this.$store.state.allTransactionTags;
|
|
},
|
|
hasAvailableExpenseCategories() {
|
|
if (!this.allCategories || !this.allCategories[this.$constants.category.allCategoryTypes.Expense] || !this.allCategories[this.$constants.category.allCategoryTypes.Expense].length) {
|
|
return false;
|
|
}
|
|
|
|
const firstAvailableCategoryId = this.getFirstAvailableCategoryId(this.allCategories[this.$constants.category.allCategoryTypes.Expense]);
|
|
return firstAvailableCategoryId !== '';
|
|
},
|
|
hasAvailableIncomeCategories() {
|
|
if (!this.allCategories || !this.allCategories[this.$constants.category.allCategoryTypes.Income] || !this.allCategories[this.$constants.category.allCategoryTypes.Income].length) {
|
|
return false;
|
|
}
|
|
|
|
const firstAvailableCategoryId = this.getFirstAvailableCategoryId(this.allCategories[this.$constants.category.allCategoryTypes.Income]);
|
|
return firstAvailableCategoryId !== '';
|
|
},
|
|
hasAvailableTransferCategories() {
|
|
if (!this.allCategories || !this.allCategories[this.$constants.category.allCategoryTypes.Transfer] || !this.allCategories[this.$constants.category.allCategoryTypes.Transfer].length) {
|
|
return false;
|
|
}
|
|
|
|
const firstAvailableCategoryId = this.getFirstAvailableCategoryId(this.allCategories[this.$constants.category.allCategoryTypes.Transfer]);
|
|
return firstAvailableCategoryId !== '';
|
|
},
|
|
sourceAmountFontSize() {
|
|
return this.getFontSizeByAmount(this.transaction.sourceAmount);
|
|
},
|
|
destinationAmountFontSize() {
|
|
return this.getFontSizeByAmount(this.transaction.destinationAmount);
|
|
},
|
|
inputIsEmpty() {
|
|
return !!this.inputEmptyProblemMessage;
|
|
},
|
|
inputEmptyProblemMessage() {
|
|
return null;
|
|
}
|
|
},
|
|
watch: {
|
|
'transaction.sourceAmount': function (newValue, oldValue) {
|
|
if (this.mode === 'view') {
|
|
return;
|
|
}
|
|
|
|
if (this.transaction.type === this.$constants.transaction.allTransactionTypes.Expense || this.transaction.type === this.$constants.transaction.allTransactionTypes.Income) {
|
|
this.transaction.destinationAmount = newValue;
|
|
} else if (this.transaction.type === this.$constants.transaction.allTransactionTypes.Transfer) {
|
|
const sourceAccount = this.allAccountsMap[this.transaction.sourceAccountId]
|
|
const destinationAccount = this.allAccountsMap[this.transaction.destinationAccountId]
|
|
|
|
if (sourceAccount && destinationAccount && sourceAccount.currency !== destinationAccount.currency) {
|
|
const exchangedOldValue = this.$store.getters.getExchangedAmount(oldValue, sourceAccount.currency, destinationAccount.currency);
|
|
const exchangedNewValue = this.$store.getters.getExchangedAmount(newValue, sourceAccount.currency, destinationAccount.currency);
|
|
|
|
if (this.$utilities.isNumber(exchangedOldValue)) {
|
|
oldValue = Math.floor(exchangedOldValue);
|
|
}
|
|
|
|
if (this.$utilities.isNumber(exchangedNewValue)) {
|
|
newValue = Math.floor(exchangedNewValue);
|
|
}
|
|
}
|
|
|
|
if ((!sourceAccount || !destinationAccount || this.transaction.destinationAmount === oldValue) &&
|
|
(this.$utilities.stringCurrencyToNumeric(this.$constants.transaction.minAmount) <= newValue &&
|
|
newValue <= this.$utilities.stringCurrencyToNumeric(this.$constants.transaction.maxAmount))) {
|
|
this.transaction.destinationAmount = newValue;
|
|
}
|
|
}
|
|
},
|
|
'transaction.destinationAmount': function (newValue) {
|
|
if (this.mode === 'view') {
|
|
return;
|
|
}
|
|
|
|
if (this.transaction.type === this.$constants.transaction.allTransactionTypes.Expense || this.transaction.type === this.$constants.transaction.allTransactionTypes.Income) {
|
|
this.transaction.sourceAmount = newValue;
|
|
}
|
|
},
|
|
'transaction.time': function (newValue) {
|
|
if (!newValue) {
|
|
newValue = this.$utilities.formatUnixTime(this.$utilities.getCurrentUnixTime(), 'YYYY-MM-DDTHH:mm');
|
|
this.transaction.time = newValue;
|
|
}
|
|
|
|
if (this.$utilities.formatUnixTime(this.transaction.unixTime, 'YYYY-MM-DDTHH:mm') !== newValue) {
|
|
this.transaction.unixTime = this.$utilities.getUnixTime(newValue);
|
|
}
|
|
},
|
|
'transaction.timeZone': function (newValue) {
|
|
for (let i = 0; i < this.allTimezones.length; i++) {
|
|
if (this.allTimezones[i].name === newValue) {
|
|
this.transaction.utcOffset = this.allTimezones[i].utcOffsetMinutes;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
created() {
|
|
const self = this;
|
|
const query = self.$f7route.query;
|
|
|
|
if (self.$f7route.path === '/transaction/edit') {
|
|
self.mode = 'edit';
|
|
} else if (self.$f7route.path === '/transaction/detail') {
|
|
self.mode = 'view';
|
|
}
|
|
|
|
self.loading = true;
|
|
|
|
const promises = [
|
|
self.$store.dispatch('loadAllAccounts', { force: false }),
|
|
self.$store.dispatch('loadAllCategories', { force: false }),
|
|
self.$store.dispatch('loadAllTags', { force: false })
|
|
];
|
|
|
|
if (query.id) {
|
|
if (self.mode === 'edit') {
|
|
self.editTransactionId = query.id;
|
|
}
|
|
|
|
promises.push(self.$store.dispatch('getTransaction', { transactionId: query.id }));
|
|
}
|
|
|
|
if (query.type && query.type !== '0' &&
|
|
query.type >= self.$constants.transaction.allTransactionTypes.Income &&
|
|
query.type <= self.$constants.transaction.allTransactionTypes.Transfer) {
|
|
self.transaction.type = parseInt(query.type);
|
|
}
|
|
|
|
Promise.all(promises).then(function (responses) {
|
|
if (query.id && !responses[3]) {
|
|
self.$toast('Unable to get transaction');
|
|
self.loadingError = 'Unable to get transaction';
|
|
return;
|
|
}
|
|
|
|
if (self.allCategories[self.$constants.category.allCategoryTypes.Expense] &&
|
|
self.allCategories[self.$constants.category.allCategoryTypes.Expense].length) {
|
|
if (query.categoryId && query.categoryId !== '0' && self.isCategoryIdAvailable(self.allCategories[self.$constants.category.allCategoryTypes.Expense], query.categoryId)) {
|
|
self.transaction.expenseCategory = query.categoryId;
|
|
}
|
|
|
|
if (!self.transaction.expenseCategory) {
|
|
self.transaction.expenseCategory = self.getFirstAvailableCategoryId(self.allCategories[self.$constants.category.allCategoryTypes.Expense]);
|
|
}
|
|
}
|
|
|
|
if (self.allCategories[self.$constants.category.allCategoryTypes.Income] &&
|
|
self.allCategories[self.$constants.category.allCategoryTypes.Income].length) {
|
|
if (query.categoryId && query.categoryId !== '0' && self.isCategoryIdAvailable(self.allCategories[self.$constants.category.allCategoryTypes.Income], query.categoryId)) {
|
|
self.transaction.incomeCategory = query.categoryId;
|
|
}
|
|
|
|
if (!self.transaction.incomeCategory) {
|
|
self.transaction.incomeCategory = self.getFirstAvailableCategoryId(self.allCategories[self.$constants.category.allCategoryTypes.Income]);
|
|
}
|
|
}
|
|
|
|
if (self.allCategories[self.$constants.category.allCategoryTypes.Transfer] &&
|
|
self.allCategories[self.$constants.category.allCategoryTypes.Transfer].length) {
|
|
if (query.categoryId && query.categoryId !== '0' && self.isCategoryIdAvailable(self.allCategories[self.$constants.category.allCategoryTypes.Transfer], query.categoryId)) {
|
|
self.transaction.transferCategory = query.categoryId;
|
|
}
|
|
|
|
if (!self.transaction.transferCategory) {
|
|
self.transaction.transferCategory = self.getFirstAvailableCategoryId(self.allCategories[self.$constants.category.allCategoryTypes.Transfer]);
|
|
}
|
|
}
|
|
|
|
if (self.allVisibleAccounts.length) {
|
|
if (query.accountId && query.accountId !== '0') {
|
|
for (let i = 0; i < self.allVisibleAccounts.length; i++) {
|
|
if (self.allVisibleAccounts[i].id === query.accountId) {
|
|
self.transaction.sourceAccountId = query.accountId;
|
|
self.transaction.destinationAccountId = query.accountId;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!self.transaction.sourceAccountId) {
|
|
self.transaction.sourceAccountId = self.allVisibleAccounts[0].id;
|
|
}
|
|
|
|
if (!self.transaction.destinationAccountId) {
|
|
self.transaction.destinationAccountId = self.allVisibleAccounts[0].id;
|
|
}
|
|
}
|
|
|
|
if (query.id) {
|
|
const transaction = responses[3];
|
|
|
|
if (self.mode === 'edit') {
|
|
self.transaction.id = transaction.id;
|
|
}
|
|
|
|
self.transaction.type = transaction.type;
|
|
|
|
if (self.transaction.type === self.$constants.transaction.allTransactionTypes.Expense) {
|
|
self.transaction.expenseCategory = transaction.categoryId;
|
|
} else if (self.transaction.type === self.$constants.transaction.allTransactionTypes.Income) {
|
|
self.transaction.incomeCategory = transaction.categoryId;
|
|
} else if (self.transaction.type === self.$constants.transaction.allTransactionTypes.Transfer) {
|
|
self.transaction.transferCategory = transaction.categoryId;
|
|
}
|
|
|
|
if (self.mode === 'edit' || self.mode === 'view') {
|
|
self.transaction.utcOffset = transaction.utcOffset;
|
|
self.transaction.timeZone = null;
|
|
self.transaction.unixTime = self.$utilities.getDummyUnixTimeForLocalUsage(transaction.time, self.transaction.utcOffset, self.$utilities.getTimezoneOffsetMinutes());
|
|
self.transaction.time = self.$utilities.formatUnixTime(self.transaction.unixTime, 'YYYY-MM-DDTHH:mm');
|
|
}
|
|
|
|
self.transaction.sourceAccountId = transaction.sourceAccountId;
|
|
|
|
if (transaction.destinationAccountId) {
|
|
self.transaction.destinationAccountId = transaction.destinationAccountId;
|
|
}
|
|
|
|
self.transaction.sourceAmount = transaction.sourceAmount;
|
|
|
|
if (transaction.destinationAmount) {
|
|
self.transaction.destinationAmount = transaction.destinationAmount;
|
|
}
|
|
|
|
self.transaction.hideAmount = transaction.hideAmount;
|
|
self.transaction.tagIds = transaction.tagIds || [];
|
|
self.transaction.comment = transaction.comment;
|
|
}
|
|
|
|
self.loading = false;
|
|
}).catch(error => {
|
|
self.$logger.error('failed to load essential data for editing transaction', error);
|
|
|
|
if (error.processed) {
|
|
self.loading = false;
|
|
} else {
|
|
self.loadingError = error;
|
|
self.$toast(error.message || error);
|
|
}
|
|
});
|
|
},
|
|
updated: function () {
|
|
this.autoChangeCommentTextareaSize();
|
|
},
|
|
methods: {
|
|
onPageAfterIn() {
|
|
this.$routeBackOnError('loadingError');
|
|
},
|
|
save() {
|
|
const self = this;
|
|
const router = self.$f7router;
|
|
|
|
if (self.mode === 'view') {
|
|
return;
|
|
}
|
|
|
|
const submitTransaction = {
|
|
type: self.transaction.type,
|
|
time: self.$utilities.getActualUnixTimeForStore(self.transaction.unixTime, self.transaction.utcOffset, self.$utilities.getTimezoneOffsetMinutes()),
|
|
sourceAccountId: self.transaction.sourceAccountId,
|
|
sourceAmount: self.transaction.sourceAmount,
|
|
destinationAccountId: '0',
|
|
destinationAmount: 0,
|
|
hideAmount: self.transaction.hideAmount,
|
|
tagIds: self.transaction.tagIds,
|
|
comment: self.transaction.comment,
|
|
utcOffset: self.transaction.utcOffset
|
|
};
|
|
|
|
if (self.transaction.type === self.$constants.transaction.allTransactionTypes.Expense) {
|
|
submitTransaction.categoryId = self.transaction.expenseCategory;
|
|
} else if (self.transaction.type === self.$constants.transaction.allTransactionTypes.Income) {
|
|
submitTransaction.categoryId = self.transaction.incomeCategory;
|
|
} else if (self.transaction.type === self.$constants.transaction.allTransactionTypes.Transfer) {
|
|
submitTransaction.categoryId = self.transaction.transferCategory;
|
|
submitTransaction.destinationAccountId = self.transaction.destinationAccountId;
|
|
submitTransaction.destinationAmount = self.transaction.destinationAmount;
|
|
} else {
|
|
self.$toast('An error has occurred');
|
|
return;
|
|
}
|
|
|
|
if (self.mode === 'edit') {
|
|
submitTransaction.id = self.transaction.id;
|
|
}
|
|
|
|
const doSubmit = function () {
|
|
self.submitting = true;
|
|
self.$showLoading(() => self.submitting);
|
|
|
|
self.$store.dispatch('saveTransaction', {
|
|
transaction: submitTransaction,
|
|
defaultCurrency: self.defaultCurrency
|
|
}).then(() => {
|
|
self.submitting = false;
|
|
self.$hideLoading();
|
|
|
|
if (self.mode === 'add') {
|
|
self.$toast('You have added a new transaction');
|
|
} else if (self.mode === 'edit') {
|
|
self.$toast('You have saved this transaction');
|
|
}
|
|
|
|
router.back();
|
|
}).catch(error => {
|
|
self.submitting = false;
|
|
self.$hideLoading();
|
|
|
|
if (!error.processed) {
|
|
self.$toast(error.message || error);
|
|
}
|
|
});
|
|
};
|
|
|
|
if (submitTransaction.sourceAmount === 0) {
|
|
self.$confirm('Are you sure you want to save this transaction whose amount is 0?', () => {
|
|
doSubmit();
|
|
});
|
|
} else {
|
|
doSubmit();
|
|
}
|
|
},
|
|
autoChangeCommentTextareaSize() {
|
|
const app = this.$f7;
|
|
const $$ = app.$;
|
|
|
|
$$('.textarea-auto-size textarea').each((idx, el) => {
|
|
el.scrollTop = 0;
|
|
el.style.height = '';
|
|
el.style.height = el.scrollHeight + 'px';
|
|
});
|
|
},
|
|
isCategoryIdAvailable(categories, categoryId) {
|
|
if (!categories || !categories.length) {
|
|
return false;
|
|
}
|
|
|
|
for (let i = 0; i < categories.length; i++) {
|
|
for (let j = 0; j < categories[i].subCategories.length; j++) {
|
|
if (categories[i].subCategories[j].id === categoryId) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
},
|
|
getFirstAvailableCategoryId(categories) {
|
|
if (!categories || !categories.length) {
|
|
return '';
|
|
}
|
|
|
|
for (let i = 0; i < categories.length; i++) {
|
|
for (let j = 0; j < categories[i].subCategories.length; j++) {
|
|
return categories[i].subCategories[j].id;
|
|
}
|
|
}
|
|
},
|
|
getFontSizeByAmount(amount) {
|
|
if (amount >= 100000000 || amount <= -100000000) {
|
|
return 32;
|
|
} else if (amount >= 1000000 || amount <= -1000000) {
|
|
return 36;
|
|
} else {
|
|
return 40;
|
|
}
|
|
}
|
|
},
|
|
filters: {
|
|
finalAmount(amount, hideAmount) {
|
|
if (hideAmount) {
|
|
return '***';
|
|
}
|
|
|
|
return amount;
|
|
},
|
|
primaryCategoryName(categoryId, allCategories) {
|
|
for (let i = 0; i < allCategories.length; i++) {
|
|
for (let j = 0; j < allCategories[i].subCategories.length; j++) {
|
|
const subCategory = allCategories[i].subCategories[j];
|
|
if (subCategory.id === categoryId) {
|
|
return allCategories[i].name;
|
|
}
|
|
}
|
|
}
|
|
|
|
return '';
|
|
},
|
|
secondaryCategoryName(categoryId, allCategories) {
|
|
for (let i = 0; i < allCategories.length; i++) {
|
|
for (let j = 0; j < allCategories[i].subCategories.length; j++) {
|
|
const subCategory = allCategories[i].subCategories[j];
|
|
if (subCategory.id === categoryId) {
|
|
return subCategory.name;
|
|
}
|
|
}
|
|
}
|
|
|
|
return '';
|
|
},
|
|
accountName(accountId, allAccounts) {
|
|
for (let i = 0; i < allAccounts.length; i++) {
|
|
if (allAccounts[i].id === accountId) {
|
|
return allAccounts[i].name;
|
|
}
|
|
}
|
|
|
|
return '';
|
|
},
|
|
timeZoneName(timeZoneName, allTimezones) {
|
|
for (let i = 0; i < allTimezones.length; i++) {
|
|
if (allTimezones[i].name === timeZoneName) {
|
|
return allTimezones[i].displayName;
|
|
}
|
|
}
|
|
|
|
return '';
|
|
},
|
|
tagName(tagId, allTags) {
|
|
for (let i = 0; i < allTags.length; i++) {
|
|
if (allTags[i].id === tagId) {
|
|
return allTags[i].name;
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style>
|
|
.category-separate-icon {
|
|
margin-left: 5px;
|
|
margin-right: 5px;
|
|
font-size: 18px;
|
|
line-height: 16px;
|
|
color: var(--f7-color-gray-tint);
|
|
}
|
|
|
|
.transaction-edit-amount {
|
|
line-height: 53px;
|
|
color: var(--f7-theme-color);
|
|
}
|
|
|
|
.transaction-edit-amount .item-title {
|
|
font-weight: bolder;
|
|
}
|
|
|
|
.transaction-edit-amount .item-header {
|
|
padding-top: calc(var(--f7-typography-padding) / 2);
|
|
}
|
|
|
|
.transaction-edit-category .item-header,
|
|
.transaction-edit-account .item-header,
|
|
.transaction-edit-timezone .item-header{
|
|
margin-bottom: 11px;
|
|
}
|
|
|
|
.transaction-edit-category-title,
|
|
.transaction-edit-timezone-title {
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.transaction-edit-time input[type="datetime-local"] {
|
|
max-width: inherit;
|
|
}
|
|
|
|
.transaction-edit-timezone-name {
|
|
padding-left: 4px;
|
|
}
|
|
|
|
.transaction-edit-tag {
|
|
margin-right: 4px;
|
|
max-width: 100%;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
</style>
|